From c9c6505d7110f492f4862926b4691c2edaa260d1 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Sep 2026 22:40:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(agent):=20=E8=B6=85=E9=A2=84=E7=AE=97?= =?UTF-8?q?=E5=BE=85=E5=A4=84=E7=90=86=E4=BA=8B=E9=A1=B9=E5=85=88=E4=BA=8E?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E7=BB=88=E6=80=81=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit e15「超预算」偶发失败(全量 ci 负载下 decision.agent_run_id 仍是上一次运行)是产品竞态,不是测试等待不足: finalize 先把 agent_run 写成 budget_exceeded,之后才 upsert 同根因的超预算待处理事项(和任务行)。 观察者(测试、会话页、待处理中心)在两次写入之间看到终态时,事项还指向上一次运行。 改为先写待处理事项与任务行,最后写会话终态:看到 budget_exceeded 时事项已指向这次运行。 Co-Authored-By: Claude Opus 5.5 --- apps/server/src/modules/agent/service.ts | 49 +++++++++++++----------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/apps/server/src/modules/agent/service.ts b/apps/server/src/modules/agent/service.ts index 43a5bf76..76ac9a08 100644 --- a/apps/server/src/modules/agent/service.ts +++ b/apps/server/src/modules/agent/service.ts @@ -315,18 +315,20 @@ export async function finalize( content_hash: sha(JSON.stringify({ runId, report })), }) .execute(); - await ctx.db - .updateTable("agent_run") - .set({ - status, - summary: summary.slice(0, 4000), - ...(status === "waiting" ? {} : { finished_at: new Date() }), - ...(res.status === "failed" - ? { error: JSON.stringify({ reason: res.reason, budget: res.budget, message: res.message }) } - : {}), - }) - .where("id", "=", runId) - .execute(); + if (status === "budget_exceeded") { + // 同根因(同一待处理事项 / 同一定时巡检 / 同一目标模板)超预算合并到已有事项,不重复新建。 + // 先写事项再写终态:观察者看到 budget_exceeded 时,事项已指向这次运行(之前顺序相反, + // 负载下读到终态时事项还指向上一次运行,e15 偶发失败) + await writeDecision(ctx, { + kind: "other", + title: "智能体运行超出预算", + summary: res.message ?? "超出预算", + subjectId: runId, + dedupeKey: await budgetDedupeKey(ctx, run), + evidence: { runId, runIds: [runId], budget: res.budget, message: res.message, goal: run.goal }, + }); + } + // 任务行同样先于会话终态写:看到会话终态时任务页已是同一结论 if (run.task_id) { const taskStatus = status === "cancelled" @@ -349,17 +351,18 @@ export async function finalize( .where("id", "=", run.task_id) .execute(); } - if (status === "budget_exceeded") { - // 同根因(同一待处理事项 / 同一定时巡检 / 同一目标模板)超预算合并到已有事项,不重复新建 - await writeDecision(ctx, { - kind: "other", - title: "智能体运行超出预算", - summary: res.message ?? "超出预算", - subjectId: runId, - dedupeKey: await budgetDedupeKey(ctx, run), - evidence: { runId, runIds: [runId], budget: res.budget, message: res.message, goal: run.goal }, - }); - } + await ctx.db + .updateTable("agent_run") + .set({ + status, + summary: summary.slice(0, 4000), + ...(status === "waiting" ? {} : { finished_at: new Date() }), + ...(res.status === "failed" + ? { error: JSON.stringify({ reason: res.reason, budget: res.budget, message: res.message }) } + : {}), + }) + .where("id", "=", runId) + .execute(); // 后台调查:结论回写待处理事项,交还人工(仍可被新证据重新唤醒) if (run.dedupe_key?.startsWith("investigate:")) { const decisionId = run.dedupe_key.slice("investigate:".length);