package main import ( "encoding/json" "errors" "flag" "fmt" "io" "os" "time" "data-terminal-cloud/internal/agenteval" ) func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr, time.Now)) } func run(args []string, stdout, stderr io.Writer, now func() time.Time) int { flags := flag.NewFlagSet("agent-eval", flag.ContinueOnError) flags.SetOutput(stderr) suitePath := flags.String("suite", "", "评测集JSON路径") observationsPath := flags.String("observations", "", "脱敏观测结果JSON路径") outputPath := flags.String("out", "", "报告输出路径;省略时输出到stdout") reportRef := flags.String("report-ref", "", "登记到运营配置评测证据中的报告引用") if err := flags.Parse(args); err != nil { return 2 } if *suitePath == "" || *observationsPath == "" || *reportRef == "" || flags.NArg() != 0 { fmt.Fprintln(stderr, "usage: agent-eval -suite -observations -report-ref [-out ]") return 2 } var suite agenteval.Suite if err := decodeFile(*suitePath, &suite); err != nil { fmt.Fprintf(stderr, "读取评测集失败: %v\n", err) return 2 } var observations agenteval.ObservationRun if err := decodeFile(*observationsPath, &observations); err != nil { fmt.Fprintf(stderr, "读取观测结果失败: %v\n", err) return 2 } report, err := agenteval.Evaluate(suite, observations, now()) if err != nil { fmt.Fprintf(stderr, "评测输入无效: %v\n", err) return 2 } artifact, err := agenteval.NewArtifact(report, *reportRef) if err != nil { fmt.Fprintf(stderr, "报告引用无效: %v\n", err) return 2 } encoded, err := json.MarshalIndent(artifact, "", " ") if err != nil { fmt.Fprintf(stderr, "生成评测报告失败: %v\n", err) return 2 } encoded = append(encoded, '\n') if *outputPath == "" { if _, err = stdout.Write(encoded); err != nil { fmt.Fprintf(stderr, "输出评测报告失败: %v\n", err) return 2 } } else if err = os.WriteFile(*outputPath, encoded, 0o600); err != nil { fmt.Fprintf(stderr, "写入评测报告失败: %v\n", err) return 2 } else { fmt.Fprintf(stdout, "%s:%s(%d/%d)\n", report.Result, *outputPath, report.Metrics.CasesPassed, report.Metrics.CasesTotal) } if report.Result != "PASSED" { return 1 } return 0 } func decodeFile(path string, target any) error { file, err := os.Open(path) if err != nil { return err } defer file.Close() decoder := json.NewDecoder(file) decoder.DisallowUnknownFields() if err = decoder.Decode(target); err != nil { return err } var trailing any if err = decoder.Decode(&trailing); !errors.Is(err, io.EOF) { if err == nil { return errors.New("JSON文件包含多个顶层值") } return err } return nil }