eval

package
v0.7.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package eval 是 ainovel-cli 的离线评测 harness。

设计立足点:评测器(确定性诊断 diag、全书文体 stylestat、七维 rubric)项目里已经 存在,eval 只做薄薄一层——批量驱动 case、采集产出、把 diag Finding 与 case 契约映射 成门禁、聚合报告。一份事实定义,不在评测层重写一遍判断。详见 docs/evaluation-system.md。

当前已覆盖确定性主线:单路门禁、baseline/variant A/B delta、repeat 聚合与 stylestat 回归。 LLM Judge 仍是可选后续层,不能污染确定性门禁。

Index

Constants

View Source
const (
	ArmSingle   = "single"
	ArmBaseline = "baseline"
	ArmVariant  = "variant"
)

Variables

This section is empty.

Functions

func Command

func Command(argv []string) int

Command 是 `ainovel-cli eval` 子命令入口,返回进程退出码: 0=PASS/WARN,1=有 case FAIL,2=用法/配置错误。

清晰流程:加载配置 → 加载 case → 按 single/A-B 编排运行 → 采集 → 评分 → 聚合 → 报告。

func RunCase

func RunCase(cfg bootstrap.Config, bundle assets.Bundle, c Case, opts RunOptions) error

RunCase 驱动一次 case:装配 host → 启动 → 按章数上限推进 → 到点 Abort。 bundle 由调用方做过 variant 覆盖(如有)。返回的 error 即"运行时错误"(hard fail 依据); 正常写完或正常截停都返回 nil。

RunCase 独占并重置 OutputDir:StartPrepared 只重置 progress/checkpoints,不清 chapters/ foundation 等工件,复用旧目录会让残留产物污染 diag 与 novel_context。故运行前清空,保证隔离。

func Summary

func Summary(s Suite) string

Summary 是给 stdout 的精简结论。

func WriteReport

func WriteReport(s Suite, outDir string) error

WriteReport 在 outDir 下写 report.json(机读)与 report.md(人读)。

Types

type Case

type Case struct {
	ID            string   `json:"id"`
	Category      string   `json:"category"`       // 评测层:smoke/workflow/quality/longform/recovery/steering
	Role          string   `json:"role,omitempty"` // 被测角色:writer/architect/editor(与 Category 正交)
	Description   string   `json:"description,omitempty"`
	Prompt        string   `json:"prompt"`                   // 用户创作需求
	Style         string   `json:"style,omitempty"`          // 覆盖配置风格
	MaxChapters   int      `json:"max_chapters"`             // 章数上限;0 表示只跑到规划完成(进入 writing)
	TargetPrompts []string `json:"target_prompts,omitempty"` // 本 case 主要验证的 prompt 文件(信息性)
	Rubric        string   `json:"rubric,omitempty"`         // LLM Judge 评分表(Phase 3 启用)
	Expect        Expect   `json:"expect"`
	Gate          Gate     `json:"gate"`
}

Case 是一个评测样本:一段创作需求 + 一组事实层断言。

func LoadCases

func LoadCases(path string) ([]Case, error)

LoadCases 从单个 .json 文件或目录加载 case。目录下所有 *.json 递归加载,按 id 排序。

func (*Case) Validate

func (c *Case) Validate() error

Validate 校验 case 必填字段。

type CaseResult

type CaseResult struct {
	CaseID   string        `json:"case_id"`
	Category string        `json:"category"`
	Role     string        `json:"role,omitempty"`
	Outcome  Outcome       `json:"outcome"`
	Runs     []RunResult   `json:"runs"`
	Deltas   []Delta       `json:"deltas,omitempty"`
	Summary  RepeatSummary `json:"summary"`
}

func NewABCaseResult

func NewABCaseResult(c Case, runs []RunResult, deltas []Delta) CaseResult

func NewSingleCaseResult

func NewSingleCaseResult(c Case, r Result) CaseResult

func NewSingleRunsCaseResult

func NewSingleRunsCaseResult(c Case, runs []RunResult) CaseResult

type Collected

type Collected struct {
	Dir         string
	Report      diag.Report // diag.Diagnose:工件 + 运行时 Findings + Stats
	Progress    *domain.Progress
	Checkpoints []domain.Checkpoint
	Pending     map[string]bool // 残留信号:pending_commit/pending_steer/last_commit/last_review
	LoadErrors  []string        // 契约依赖工件的真实读取失败(非"不存在");Grade 据此 hard fail
	RuntimeErr  string          // runner 捕获的运行时错误(hard fail),空=无
	Style       StyleCollection
	Usage       UsageMetrics
	ToolCalls   int
}

Collected 是一次运行产出的只读采集结果。全部来自已有评测器与事实层,eval 不自己重算。

func Collect

func Collect(dir string, runtimeErr error) Collected

Collect 对一个已完成的输出目录做离线采集。runtimeErr 是 runner 驱动期间的错误(如有)。 工件读取错误不静默吞:文件不存在视为"无数据",其余(损坏/无权限)记入 LoadErrors, 避免"读不到 pending 文件"被误判成"没有 pending"而 false pass(fail-loud)。

func (Collected) HasCheckpoint

func (c Collected) HasCheckpoint(spec string) (bool, error)

HasCheckpoint 判断采集到的 checkpoint 中是否存在匹配 spec 的记录。 spec 形如 "chapter:1:commit" / "arc:1:1:arc_summary" / "volume:1:volume_summary" / "global:layered_outline"。

type Delta

type Delta struct {
	Outcome   Outcome      `json:"outcome"`
	HardFails []Issue      `json:"hard_fails,omitempty"`
	Warnings  []Issue      `json:"warnings,omitempty"`
	Notes     []Issue      `json:"notes,omitempty"`
	Metrics   DeltaMetrics `json:"metrics"`
}

Delta 描述 variant 相对 baseline 的确定性差异。

func GradeDelta

func GradeDelta(c Case, baseline, variant Result) Delta

GradeDelta 只比较确定性事实:variant 比 baseline 是否更差。

type DeltaMetrics

type DeltaMetrics struct {
	CompletedChapters     int         `json:"completed_chapters"`
	CriticalFindings      int         `json:"critical_findings"`
	WarningFindings       int         `json:"warning_findings"`
	TotalWordsRatio       float64     `json:"total_words_ratio,omitempty"`
	ToolCallDeltaRatio    float64     `json:"tool_call_delta_ratio,omitempty"`
	CostDeltaRatio        float64     `json:"cost_delta_ratio,omitempty"`
	InputTokenDeltaRatio  float64     `json:"input_token_delta_ratio,omitempty"`
	OutputTokenDeltaRatio float64     `json:"output_token_delta_ratio,omitempty"`
	Stylestat             *StyleDelta `json:"stylestat,omitempty"`
}

type Expect

type Expect struct {
	Phase                string   `json:"phase,omitempty"`                  // 期望最终 phase
	MinCompletedChapters int      `json:"min_completed_chapters,omitempty"` // 至少完成的章数
	RequiredCheckpoints  []string `json:"required_checkpoints,omitempty"`   // 形如 "chapter:1:commit" / "arc:1:1:arc_summary" / "global:layered_outline"
	NoPending            []string `json:"no_pending,omitempty"`             // 结束时应清空的信号:pending_commit/pending_steer/last_commit/last_review
}

Expect 是 case 级契约断言——只声明 diag 通用规则覆盖不到、与本 case 强相关的预期。

type Gate

type Gate struct {
	MaxSeverity string `json:"max_severity,omitempty"` // diag Finding 允许的最高严重度(默认 warning):超过即 hard fail

	MaxCostDeltaRatio     *float64 `json:"max_cost_delta_ratio,omitempty"`
	MaxToolCallDeltaRatio *float64 `json:"max_tool_call_delta_ratio,omitempty"`
	StylestatRegression   string   `json:"stylestat_regression,omitempty"`
}

Gate 是本 case 的门禁阈值。本期只用 MaxSeverity;其余字段为 A/B(regression)阶段预留, 解析但不参与门禁——保留是为了 case 文件能按 docs/evaluation-system.md 的完整 schema 书写。

type Issue

type Issue struct {
	Kind     string `json:"kind"`               // hard_fail / warning / passed
	Source   string `json:"source"`             // runtime / finding:<rule> / contract:<name>
	Severity string `json:"severity,omitempty"` // critical / warning / info
	Detail   string `json:"detail"`
}

Issue 是门禁判定中的一条记录。

type Metrics

type Metrics struct {
	CompletedChapters int              `json:"completed_chapters"`
	TotalChapters     int              `json:"total_chapters"`
	TotalWords        int              `json:"total_words"`
	AvgWordsPerChap   int              `json:"avg_words_per_chapter"`
	Phase             string           `json:"phase"`
	Flow              string           `json:"flow"`
	ReviewCount       int              `json:"review_count"`
	RewriteCount      int              `json:"rewrite_count"`
	AvgReviewScore    float64          `json:"avg_review_score"`
	CriticalFindings  int              `json:"critical_findings"`
	WarningFindings   int              `json:"warning_findings"`
	ToolCalls         int              `json:"tool_calls"`
	Usage             UsageMetrics     `json:"usage"`
	StylestatStatus   string           `json:"stylestat_status,omitempty"`
	Stylestat         *stylestat.Stats `json:"stylestat,omitempty"`
}

Metrics 是从 diag.Stats 直接借来的概览指标——eval 不重算。

type Outcome

type Outcome string

Outcome 是单个 case 的门禁结论。

const (
	Pass Outcome = "PASS"
	Warn Outcome = "WARN"
	Fail Outcome = "FAIL"
)

type RangeSummary

type RangeSummary struct {
	Min float64 `json:"min"`
	Avg float64 `json:"avg"`
	Max float64 `json:"max"`
}

type RepeatSummary

type RepeatSummary struct {
	PassRate        float64       `json:"pass_rate"`
	HardFailRuns    int           `json:"hard_fail_runs"`
	WarningRuns     int           `json:"warning_runs"`
	CostUSD         *RangeSummary `json:"cost_usd,omitempty"`
	ToolCalls       *RangeSummary `json:"tool_calls,omitempty"`
	VariantPassRate float64       `json:"variant_pass_rate,omitempty"`
	DeltaPassRate   float64       `json:"delta_pass_rate,omitempty"`
}

type Result

type Result struct {
	CaseID    string  `json:"case_id"`
	Category  string  `json:"category"`
	Role      string  `json:"role,omitempty"`
	Arm       string  `json:"arm,omitempty"`
	Repeat    int     `json:"repeat,omitempty"`
	Outcome   Outcome `json:"outcome"`
	HardFails []Issue `json:"hard_fails"`
	Warnings  []Issue `json:"warnings"`
	Notes     []Issue `json:"notes,omitempty"`
	Passed    []Issue `json:"passed"`
	Metrics   Metrics `json:"metrics"`
	Dir       string  `json:"dir"`
}

Result 是单个 case 的完整评测结果。对齐设计稿三层模型: HardFails(阻塞)/ Warnings(回归,WARN)/ Notes(信息性,不影响门禁)。

func Grade

func Grade(c Case, col Collected) Result

Grade 把采集结果按 case 契约与 diag Finding 严重度映射成门禁结论。这是 MVP 的核心: 确定性证据决定 PASS/WARN/FAIL,不掺主观判断。

type RunOptions

type RunOptions struct {
	OutputDir string        // 隔离输出目录(必填)
	Timeout   time.Duration // 单 case 墙钟上限;0 表示不限
	Progress  io.Writer     // 进度行输出(可选,nil 则不打印)
}

RunOptions 控制单次 case 运行。

type RunResult

type RunResult struct {
	Arm    string `json:"arm"`
	Repeat int    `json:"repeat"`
	Result Result `json:"result"`
}

type StyleCollection

type StyleCollection struct {
	Status string           `json:"status"` // ok / insufficient_sample
	Stats  *stylestat.Stats `json:"stats,omitempty"`
}

StyleCollection 是从章节终稿中采集的全书文体事实。

type StyleDelta

type StyleDelta struct {
	Status               string  `json:"status"` // ok / insufficient_sample
	PatternTopPerChapter float64 `json:"pattern_top_per_chapter_delta,omitempty"`
	EndingShortRatio     float64 `json:"ending_short_ratio_delta,omitempty"`
	RepeatedSentences    int     `json:"repeated_sentences_delta,omitempty"`
	TitleMixedDelta      int     `json:"title_mixed_delta,omitempty"`
}

type Suite

type Suite struct {
	RunID   string       `json:"run_id"`
	Mode    string       `json:"mode"` // single / ab
	Variant string       `json:"variant,omitempty"`
	Repeat  int          `json:"repeat"`
	Gate    Outcome      `json:"gate"`
	Cases   []CaseResult `json:"cases"`
}

Suite 是一次评测运行的聚合结果。

func Aggregate

func Aggregate(runID, mode, variant string, repeat int, cases []CaseResult) Suite

Aggregate 把单 case 结果汇总成 suite,并计算整体门禁:任一 FAIL→FAIL,否则任一 WARN→WARN。

type UsageMetrics

type UsageMetrics struct {
	Input         int     `json:"input,omitempty"`
	Output        int     `json:"output,omitempty"`
	CacheRead     int     `json:"cache_read,omitempty"`
	CacheWrite    int     `json:"cache_write,omitempty"`
	CostUSD       float64 `json:"cost_usd,omitempty"`
	MissingUsage  int     `json:"missing_usage,omitempty"`
	UsageRecorded bool    `json:"usage_recorded"`
}

UsageMetrics 是 meta/usage.json 中已有的可靠成本/token 事实。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL