From 9ad22bf4aea6b64cc4a10369d7b4fc0c4e899607 Mon Sep 17 00:00:00 2001 From: Linghong Date: Mon, 24 Aug 2026 02:50:50 +0000 Subject: [PATCH] =?UTF-8?q?=E5=B9=BF=E5=91=8A=20schema=20=E4=B9=9F?= =?UTF-8?q?=E5=BE=97=E5=85=81=E8=AE=B8=E4=B8=A4=E4=B8=AA=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E4=BC=A0=20null=EF=BC=8C=E5=90=A6=E5=88=99=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=E5=88=86=E6=94=AF=E8=B5=B0=E4=B8=8D=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一提交让 Runtime 在返工/续跑时继承 acceptanceCriteria 与 expectedArtifacts, 但广告给模型的 agent.delegate schema 把它们写死成 `"type": "array"` + minItems 1。模型根本没法传 null,只能继续手抄,继承分支一次都没走到。 无头验证实测:改动前后每条 continuation 都固定先失败 2 次 (verify-farm-2 共 4 次,verify-farm-3 共 6 次),完全没有变化。我上一轮 汇报「0 次」是错的——当时截断了 inspect 输出,没看到那一段就下了结论。 两个字段的 type 改成 ["array", "null"],各自带上说明;工具描述补一句 「返工与澄清续跑一起传 null 由 Runtime 继承」。测试同时钉住描述文案和 schema 的 null 可空性。 Co-Authored-By: Claude Opus 5 --- .../src-tauri/src/agent_native_tools.rs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs b/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs index 1b582897f..9ead2654a 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs @@ -1475,7 +1475,7 @@ fn runtime_tool_description(tool: &str) -> &'static str { "blackboard.write" => "向项目级共享黑板追加稳定结论。", "agent.message" => "向一个目标 Agent 写入定向上下文消息。", "agent.delegate" => { - "用持久验收合同把边界清晰的后台任务委派给另一个 Agent;返工时 repairOfDelegationId 指向原 delivery,且 runId 必须为 null。" + "用持久验收合同把边界清晰的后台任务委派给另一个 Agent;返工时 repairOfDelegationId 指向原 delivery,runId 必须为 null,acceptanceCriteria 与 expectedArtifacts 一起传 null 由 Runtime 从原 delivery 继承。" } "agent.spawn_isolated" => "创建最多三个写范围互不重叠的隔离子 Agent。", "agent.goal_contract" => { @@ -1722,8 +1722,8 @@ fn runtime_tool_input_schema(tool: &str) -> Value { "properties": { "agentId": { "type": "string", "minLength": 1 }, "task": { "type": "string", "minLength": 1, "maxLength": 2400 }, - "acceptanceCriteria": { "type": "array", "minItems": 1, "maxItems": 8, "items": { "type": "string", "minLength": 1, "maxLength": 240 } }, - "expectedArtifacts": { "type": "array", "maxItems": 16, "items": { "type": "string", "minLength": 1, "maxLength": 240 } }, + "acceptanceCriteria": { "type": ["array", "null"], "minItems": 1, "maxItems": 8, "items": { "type": "string", "minLength": 1, "maxLength": 240 }, "description": "初次委派必填。带 repairOfDelegationId 的返工或澄清续跑传 null:Runtime 会从原 delivery 继承权威合同,手抄一遍不增加任何信息,抄错会被直接拒收。" }, + "expectedArtifacts": { "type": ["array", "null"], "maxItems": 16, "items": { "type": "string", "minLength": 1, "maxLength": 240 }, "description": "与 acceptanceCriteria 同进同出:初次委派必填,返工与澄清续跑一起传 null 由 Runtime 继承。" }, "repairOfDelegationId": { "type": ["string", "null"] }, "runId": { "type": ["string", "null"] }, "continuationOfDelegationId": { "type": ["string", "null"] }, @@ -2214,6 +2214,22 @@ mod tests { assert!(description.contains("repairOfDelegationId 指向原 delivery")); assert!(description.contains("runId 必须为 null")); + // 广告给模型的 schema 一度把这两个数组写死成非空数组,模型于是根本无法 + // 传 null,只能手抄——实测每条 continuation 固定先失败 2 次才抄对。 + assert!( + description.contains("acceptanceCriteria 与 expectedArtifacts 一起传 null"), + "{description}" + ); + let schema = runtime_tool_input_schema("agent.delegate"); + for field in ["acceptanceCriteria", "expectedArtifacts"] { + let types = schema["properties"][field]["type"] + .as_array() + .unwrap_or_else(|| panic!("{field} 必须允许 null")); + assert!( + types.iter().any(|value| value == "null"), + "{field} 的广告 schema 必须允许 null,否则 Runtime 侧的继承分支永远走不到" + ); + } } /// plan 根 Supervisor 只广告 7 个原生工具 + 2 个协议控制。全量注册表里其余