97 lines
2.7 KiB
Go
97 lines
2.7 KiB
Go
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 <suite.json> -observations <observations.json> -report-ref <reference> [-out <report.json>]")
|
||
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
|
||
}
|