feat(harness): 评测闭环 —— 评测结果落库 + 分级 + 告警 + 可查(测温计→恒温器)

此前 eval 只打日志、不闭环。现在:
- 分级:evalLevel 据综合分+忠实度 → ok(≥0.75) / warn(≥0.5 或忠实<0.6) / poor(<0.5);poor 出 slog.Warn 告警。
- 落库:dispatcher 评完经 NATS(SubjectEval) 广播 EvalEvent → 网关订阅写 PG(新表 sundynix_eval,
  按 task_id upsert)。沿用任务状态回写那套(dispatcher 无 DB,经 bus→gateway 落库)。
- 可查:GET /api/v1/tasks/:id/eval 返回 overall/rule/llm/faithful/level/flags/reason/sources。
- 契约 EvalEvent + EvalOK/Warn/Poor;bus PublishEval/SubscribeEval;dispatcher EvalSink(NewOrchestrator 第9参)。

验证:三模块 build+vet+test 全绿;live RAG 任务评测落库,端点返回 overall~1.0 / level=ok / faithful=1 / sources=1。
剩:桌面端质量面板、低分自动重试(P3)。project_analysis 勾掉该项。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-25 15:26:19 +08:00
parent 446b784fc6
commit 10f08ffb14
12 changed files with 173 additions and 10 deletions
@@ -89,6 +89,21 @@ func (h *Handler) TaskStatus(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"task_id": id, "status": status, "detail": detail})
}
// TaskEval: GET /api/v1/tasks/:id/eval —— 取一次任务的自动化评测结果(综合/质量/忠实度/分级/flags)。
func (h *Handler) TaskEval(c *gin.Context) {
e := h.db.GetEval(c.Request.Context(), c.Param("id"))
if e == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "尚无评测结果(任务未完成或评测进行中)"})
return
}
var flags []string
_ = json.Unmarshal([]byte(e.Flags), &flags)
c.JSON(http.StatusOK, gin.H{
"task_id": e.TaskID, "overall": e.Overall, "rule": e.Rule, "llm": e.LLM,
"faithful": e.Faithful, "level": e.Level, "flags": flags, "reason": e.Reason, "sources": e.Sources,
})
}
// ApproveTask: POST /api/v1/tasks/:id/approve {approved, node?, note?} —— 人工审批决定(HITL)。
// 把决定经 NATS 发给 dispatcher,解除审批节点的阻塞(批准放行 / 拒绝中止)。
func (h *Handler) ApproveTask(c *gin.Context) {