test(base): 造只读探针端到端验证工具拒绝,并修掉它暴露的一个死分支

**为什么造探针**:ToolExecutor 的四种拒绝在真实链路上很难安全触发——要么改客服、风控的
生效配置,要么动 RBAC,两条路都会影响正在工作的 Agent。platform_probe 是个只读、无副作用
的探针:它只声明 probe 一个意图(所以意图分类只可能返回它)、没有发布工具白名单
(天然处于"未配置"状态)、工具只回显参数不碰业务数据。

**它立刻查出一个死分支**:探针报的是「工具不在当前意图白名单」,而不是我新加的
「该意图未配置工具白名单」。原因是 governance.resolve 会为每个 supported_intents
**预填条目**(governance.py:55-61),未配置时得到的是**空元组**——所以
intent not in configured_tools 在运行期**永远不成立**,那个分支是死代码。

单元测试没能发现它,因为我在测试里手工构造了 configured={},而真实链路不产生这个形状。
**这正是端到端测试的价值**:单元测试验证的是我设想的形状,端到端验证的是真实形状。

修法:改判"白名单为空"而非"缺键",文案改为"该意图的工具白名单为空",并注明经过 governance
装配后"完全没配"与"配了空列表"无法区分、也不假装能区分(两者运维动作相同)。新增一条按
**真实形状**({"faq": ()})构造的用例把它锁住。

实测:探针调用 → failed / AGENT_PERMISSION_DENIED,stderr 为
ForbiddenAgentError: 该意图未配置工具白名单(tool_executor.py:108)。

ruff / mypy(136 文件) / 611 unit+contract 全绿。
This commit is contained in:
2026-09-11 13:10:04 +08:00
parent cdbd85b27c
commit edc0c43245
4 changed files with 125 additions and 13 deletions
+19 -12
View File
@@ -75,18 +75,25 @@ class ToolExecutor:
# 白名单内容、权限码、角色集属于内部配置,不该出现在客户可见的响应里。
reason = ""
detail = ""
if intent not in configured_tools:
reason = "该意图未配置工具白名单"
detail = (
f"意图 {intent!r} 在发布版本里没有任何工具白名单"
f"(agent_tools / <agent_type>:{intent}),工具失败关闭"
)
elif name not in configured_tools[intent]:
reason = "工具不在当前意图白名单"
detail = (
f"工具 {name!r} 不在意图 {intent!r} 的白名单 "
f"{list(configured_tools[intent])} 内"
)
configured = configured_tools.get(intent, ())
if name not in configured:
if not configured:
# 白名单为空。**注意**:经过 governance 装配后,"意图完全没配"与"配了空列表"
# 在这里无法区分——governance 会为每个 supported_intent 预填条目,所以
# `intent not in configured_tools` 这个判断在真实链路上永远不会成立
# (这一点是端到端跑探针才发现的,单元测试里手工构造空 dict 掩盖了它)。
# 好在两者的运维动作相同:都该去看 agent_tools 里 <agent_type>:<intent>
# 这一项,所以合并成一句准确的话,不假装能区分。
reason = "该意图未配置工具白名单"
detail = (
f"意图 {intent!r} 的工具白名单为空:发布配置里没有为它配置任何工具"
f"(agent_tools / <agent_type>:{intent}),工具失败关闭"
)
else:
reason = "工具不在当前意图白名单"
detail = (
f"工具 {name!r} 不在意图 {intent!r} 的白名单 {list(configured)} 内"
)
elif definition.required_permission not in context.permissions:
reason = "缺少工具权限"
detail = f"缺少权限 {definition.required_permission!r}"