From c68a562db103566784d0c14f9ec204c5939193e0 Mon Sep 17 00:00:00 2001 From: 18628386721 <18628386721@163.com> Date: Mon, 27 Jul 2026 04:57:16 +0800 Subject: [PATCH] =?UTF-8?q?loop:=E6=A8=A1=E5=9E=8B=E9=97=AE=E4=B8=8D?= =?UTF-8?q?=E5=87=BA=E7=BB=93=E6=9E=9C=E6=97=B6=E8=BD=AC=E4=BA=BA=E8=A3=81?= =?UTF-8?q?=E6=96=AD,=E8=80=8C=E4=B8=8D=E6=98=AF=E6=8A=9B=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E7=A9=BA=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A3 / A2 起来后每一拍都抛 `Authentication Fails` 并被推进器吞掉: 任务永远 running、预算照烧、负责人工作台上什么也看不到。 模型这一轮给不出行动(端点不可达 / 鉴权失败 / 超时),**不是异常路径,是断点的一种**: 机器当下决断不了就该调人 ——「人在环是一种被调用的工具」。世界一步都没碰, 交回给人是安全且诚实的落点。改在 app 的 agent-runner 收口(引擎保持纯粹), 与 flow 的步级 `failure.fallback` 同构。 缺口作为**依据**摆在卡上,不当表单项让人逐条填。 现状:A3 渠道寻源(林佩萱)装载 21 个计划项、A2 替代选型(王梓名)装载 19 个, 两个 loop 都在等人回卡,不再空转。 gates: tsc / eslint / depcruise / 393 测试 全绿 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m --- apps/api/src/app/agent-runner.ts | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/api/src/app/agent-runner.ts b/apps/api/src/app/agent-runner.ts index 4a71712..7dad669 100644 --- a/apps/api/src/app/agent-runner.ts +++ b/apps/api/src/app/agent-runner.ts @@ -70,15 +70,40 @@ export function createAgentRunner(deps: AgentRunnerDeps): { .join('\n'); const model = await deps.modelOf(taskId); - const result = await deps.gateway.generate({ - model, - prompt, - outputSchema: ACTION_SCHEMA, - taskId, - label: 'loop-turn', - }); - - return normalize(result.object, enabled); + try { + const result = await deps.gateway.generate({ + model, + prompt, + outputSchema: ACTION_SCHEMA, + taskId, + label: 'loop-turn', + }); + return normalize(result.object, enabled); + } catch (error) { + /* + * 模型这一轮问不出结果(端点不可达、鉴权失败、超时…)。 + * + * 这**不是**异常路径,是断点的一种:机器当下决断不了,就该调人 —— + * 「人在环是一种被调用的工具」。抛出去只会让任务挂着空转: + * 推进器每一拍都重试、每一拍都记一行错,预算照烧,负责人却看不到任何东西。 + * 世界一步都没碰,交回给人是安全且诚实的落点。 + */ + const detail = error instanceof Error ? error.message : String(error); + return { + kind: 'human', + itemId: enabled.entries[0]?.id ?? 'breakpoint', + workOrder: 'execution', + card: { + title: '模型这一轮未能决断,转人裁断', + reason: detail, + // 缺口是**依据**不是表单项:摆给人看,别让人逐条填 + gaps: enabled.gaps.join('、') || '(无)', + enabled: enabled.entries.map((e) => `${e.id} ${e.name}`).join('、') || '(空)', + ask: '请判断下一步:接着走哪一项,或补齐缺口后让环继续。', + fields: ['decision'], + }, + }; + } }, }; }