feat(harness): RAG 忠实度评测 —— judge 拿检索原文评幻觉
补 Harness 已知洞:此前 LLM-judge 只看 input+output,看不到检索来源,幻觉其实没评。 - Evaluator.Score 增 sources 参数;有来源时走 llmJudgeGrounded:一次调用同时评 quality 质量 + faithfulness 忠实度,并列出 unsupported(未被来源支持的说法)→ 进 Flags。 综合分(有来源)=0.3规则+0.35质量+0.35忠实;无来源时维持原 0.4规则+0.6质量。Result 增 Faithful 字段。 - 透传检索来源:runGraph 返回 (answer, refs, err),executeGraph 同步;Handle→evaluate(input,output,refs); refsOf(board)=检索资料+工具产出。compose 路径暂返回 nil refs(不评忠实度)。 - eval 日志增「忠实 X.XX,来源 N」。 测试:单测覆盖 grounded(quality/faithfulness/unsupported 解析 + 加权 + flags)与无来源跳过; 3 处测试 runGraph 三返回值更新。live 实测 RAG 任务忠实 1.00/来源 1,judge 正确判定无编造。 project_analysis Harness 清单勾掉该项。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -158,7 +158,7 @@ func (o *Orchestrator) Handle(ctx context.Context, t *contract.Task) error {
|
||||
tr.info("task", "system", "任务受理", fmt.Sprintf("DSL %d 字节,按图执行", len(t.Graph)))
|
||||
|
||||
// 按 DSL 图执行:compose.Graph(EINO_COMPOSE=1)或自研 graph.go(默认);agent 节点流式回流 token。
|
||||
answer, err := o.executeGraph(tctx, t, tr)
|
||||
answer, refs, err := o.executeGraph(tctx, t, tr)
|
||||
if errors.Is(err, errRejected) {
|
||||
// HITL 拒绝:合法终态,非故障。收尾流 + 置 rejected,不计熔断、不重投。
|
||||
slog.InfoContext(ctx, "task rejected by approval", "task_id", t.ID)
|
||||
@@ -189,21 +189,22 @@ func (o *Orchestrator) Handle(ctx context.Context, t *contract.Task) error {
|
||||
|
||||
// 写回阶段:离开热路径、异步落历史 + (TODO)抽取记忆。
|
||||
go o.memorize(t, answer)
|
||||
// 自动化评测:离开热路径,对本轮输出打分并记录(规则 + LLM-as-judge)。
|
||||
go o.evaluate(t, dsl.Compile(t.Graph).Query, answer)
|
||||
// 自动化评测:离开热路径,对本轮输出打分并记录(规则 + LLM-as-judge + RAG 忠实度)。
|
||||
go o.evaluate(t, dsl.Compile(t.Graph).Query, answer, refs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// evaluate 异步对一次输出做自动化评测并记录评分(off 热路径,不影响响应)。
|
||||
func (o *Orchestrator) evaluate(t *contract.Task, input, output string) {
|
||||
// sources 为本轮检索来源:非空时额外评忠实度(幻觉检测)。
|
||||
func (o *Orchestrator) evaluate(t *contract.Task, input, output string, sources []string) {
|
||||
if o.eval == nil {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
r := o.eval.Score(ctx, input, output)
|
||||
log.Printf("[eval] task %s 综合 %.2f(规则 %.2f / LLM %.2f)flags=%v %s",
|
||||
t.ID, r.Overall, r.Rule, r.LLM, r.Flags, r.Reason)
|
||||
r := o.eval.Score(ctx, input, output, sources)
|
||||
log.Printf("[eval] task %s 综合 %.2f(规则 %.2f / LLM %.2f / 忠实 %.2f,来源 %d)flags=%v %s",
|
||||
t.ID, r.Overall, r.Rule, r.LLM, r.Faithful, len(sources), r.Flags, r.Reason)
|
||||
}
|
||||
|
||||
// fetchMemory 经 MCP memory_get 工具召回用户常驻画像。
|
||||
|
||||
Reference in New Issue
Block a user