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)