From 835a6d7432609b7a5de1c027b1ba39293a99032e Mon Sep 17 00:00:00 2001 From: Blizzard Date: Mon, 29 Jun 2026 17:35:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(harness):=20=E8=AF=84=E6=B5=8B=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E8=AF=AF=E5=88=A4=E9=95=BF=E7=AD=94=E6=A1=88=E3=80=8C?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E3=80=8D=E2=80=94=E2=80=94=20=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E6=88=AA=E6=96=AD=E4=BB=8E=201500=20=E6=8A=AC?= =?UTF-8?q?=E5=88=B0=206000=20+=20=E6=98=8E=E7=A1=AE=E6=A0=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T0.1 校准后 judge 变严,但被评测自己的截断坑了:judge 评分前把模型回答截到 1500 字 (evalTruncate(output,1500)),而报告/多智能体综合等长答案普遍 2000+ 字 → judge 只看到 前半截、误判「回答不完整/截断」,给出假阴性 warn。多智能体一跑就暴露(2278 字答案被 误判 0.55 warn「截断」)。 - evalReviewOutput: 评审长度上限 6000(覆盖绝大多数长答案);确被截断时明确标注 「已被评审系统截断,勿因结尾不完整扣分」,杜绝 judge 因自身截断而扣分。 - llmJudge / llmJudgeGrounded 改用 evalReviewOutput 喂模型回答。 live 验证:同款 2 专家协调任务,修复前 0.55 warn「截断」→ 修复后 0.85 ok,且 judge 挑出真实缺陷(推荐逻辑前后矛盾)而非假截断。既公平又严格。 Co-Authored-By: Claude Opus 4.8 --- sundynix-dispatcher/internal/harness/eval.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/sundynix-dispatcher/internal/harness/eval.go b/sundynix-dispatcher/internal/harness/eval.go index cbde4bd..a7bfdd1 100644 --- a/sundynix-dispatcher/internal/harness/eval.go +++ b/sundynix-dispatcher/internal/harness/eval.go @@ -134,7 +134,7 @@ func normJudge(v float64) float64 { func (e *Evaluator) llmJudge(ctx context.Context, input, output string) (float64, string, bool) { user := fmt.Sprintf("【用户输入】%s\n\n【模型输出】%s\n\n%s\n\n"+ "只输出 JSON:{\"score\":1到5整数,\"reason\":\"先点最主要的缺陷(没有就说无明显缺陷)再给结论,一句话\"},不要多余文字。", - evalTruncate(input, 500), evalTruncate(output, 1500), evalQualityRubric) + evalTruncate(input, 500), evalReviewOutput(output), evalQualityRubric) txt, err := e.chat(ctx, evalQualitySys, user) if err != nil { return 0, "", false @@ -161,7 +161,7 @@ func (e *Evaluator) llmJudgeGrounded(ctx context.Context, input, output string, "faithfulness(忠实度):5=每个关键说法都有资料依据;分数随无依据/编造的说法增多而降;只要有一处明显编造,faithfulness 不得高于 2。\n"+ "unsupported:列出回答中未被检索资料支持的具体说法(无则空数组)。\n"+ "只输出 JSON:{\"quality\":1到5整数,\"faithfulness\":1到5整数,\"unsupported\":[\"...\"],\"reason\":\"一句话\"},不要多余文字。", - evalTruncate(input, 400), src, evalTruncate(output, 1500)) + evalTruncate(input, 400), src, evalReviewOutput(output)) txt, err := e.chat(ctx, sys, user) if err != nil { return 0, 0, nil, "", false @@ -186,6 +186,20 @@ func evalTruncate(s string, n int) string { return string(r[:n]) + "…" } +// evalReviewLimit 是喂给 judge 的「模型回答」最长字数。不能太短:报告/多智能体综合等长答案 +// 普遍 2000+ 字,截太短会让 judge 误判「回答不完整/截断」(而那是评审自己截的)。 +const evalReviewLimit = 6000 + +// evalReviewOutput 把待评审的模型回答按 evalReviewLimit 截断;若确被截断,明确标注是评审所为, +// 让 judge 不要因结尾不完整而扣分(杜绝长答案被误判 warn/poor 的假阴性)。 +func evalReviewOutput(s string) string { + r := []rune(s) + if len(r) <= evalReviewLimit { + return s + } + return string(r[:evalReviewLimit]) + "\n…(注:回答过长,已被评审系统截断,请勿因结尾不完整而扣分)" +} + // evalStripFence 去掉模型可能包裹的 ```json … ``` 围栏。 func evalStripFence(s string) string { s = strings.TrimSpace(s)