企业智能体工程体系v1.1|企业智能体工程卷 · 第4期·Agent-First 工具接口——让 API 为 Agent 可读、可判、可拦

📅 2026/8/7 12:02:11
企业智能体工程体系v1.1|企业智能体工程卷 · 第4期·Agent-First 工具接口——让 API 为 Agent 可读、可判、可拦
企业智能体工程体系v1.1企业智能体工程卷 · 第4期Agent-First 工具接口——让 API 为 Agent 可读、可判、可拦作者技术治理研究组系列企业智能体工程卷发布版 v1.1主案例CASE-CR-0042信用提额申请本集对象ToolSpec · ToolRegistry协议承接P2工具副作用 ⊆ 契约副作用适合读者架构师、技术负责人、AI 产品经理、企业级 Agent 开发者 本文档声明性质本文为企业智能体工程化设计参考框架的第 4 期聚焦 Agent 工具接口的语义化设计提供架构思路与教学级示意代码不构成生产级实现方案或法律合规意见。证据锚定文中案例CASE-CR-0042为教学示意不对应任何真实客户系统。系列定位本篇在第 1 期技能契约的基础上引入ToolSpec工具规格作为 Agent 与外部系统交互的标准化接口并通过 P2 协议确保工具副作用不超出契约边界。摘要在前三期我们建立了第 1 期SkillContract技能契约Agent“能做什么、不能做什么”第 2 期决策四轴 P1单点决策对齐企业价值第 3 期DecisionBoard多环节决策的立场传递与冲突阻断但还有一个关键问题尚未解决Agent 怎么调用外部工具/API在传统的 API 设计中接口是“给人看的”——update(id, data)语义含糊副作用散落在 Wiki 角落调用是否合法靠开发者自觉。当 Agent 成为调用者时这种设计就会成为系统性风险Agent 可能把提额请求写成覆盖整户资料Agent 不知道调用这个接口会产生什么副作用Agent 的信任门槛靠“提示词里约束”而非系统强制本期回答一个核心问题如何把工具/API 设计成 Agent 可读、可判、可拦的一等公民本期引入ToolSpec工具规格——将工具的行为名、副作用、信任等级、允许角色和约束声明为结构化对象并通过ToolRegistry工具注册表实现调用前的权限、契约和副作用三重校验。一句话核心接口要为 Agent 设计——行为名即语义副作用进签名调用前可拦截。1. 问题为什么“给人看的 API”对 Agent 是灾难1.1 CASE-CR-0042 中的工具调用在 CASE-CR-0042 的链路中Agent 需要调用两个核心工具环节需要的工具当前 API 形态问题版数据 Agent查询客户信用信息query(customer_id)—— 查什么返回什么财务 Agent提交提额裁决update(id, data)—— 改了什么是永久生效吗这些 API 的问题问题说明对 Agent 的影响命名含糊update、query、process无法表达业务语义Agent 可能调错接口副作用隐蔽副作用写在 Wiki 里不在签名中Agent 不知道自己会“闯多大祸”权限粗放谁都能调或靠 IAM 粗粒度控制客服 Agent 可能调用财务工具信任门槛靠自觉“提示词里说了别乱调”越狱/注入可绕过1.2 从“给人看”到“给 Agent 看”维度给人看的 API给 Agent 看的 APIToolSpec命名update(id, data)propose_limit_change(case_id, new_limit)副作用文档角落签名中的effects: {limit.write}授权IAM 粗粒度allowed_roles: {finance.limit}约束人工检查constraints: policy_range调用靠自觉调用前三重校验2. ToolSpec工具即声明2.1 什么是 ToolSpecToolSpec ├── name # 行为化命名如 propose_limit_change ├── effects # 副作用集合如 {limit.write, email.send} ├── trust_required # 所需信任等级 ├── allowed_roles # 允许调用的角色 ├── constraints # 调用前约束政策范围、参数校验 └── handler # 实际执行函数核心理念工具不再是一个“可以调用的函数”而是一个带有完整语义声明的可执行规格。2.2 ToolSpec 与 SkillContract 的对扣关系SkillContract技能契约ToolSpec工具规格声明 Agent能做什么声明工具有什么副作用side_effects: {limit.write}effects: {limit.write}P2 裁决Tool 的effects必须被当前 Skill 的side_effects覆盖3. CASE-CR-0042 的两个工具3.1 工具一只读查询属性值nameget_credit_snapshoteffects∅只读无副作用trust_required1低信任allowed_roles{data.credit, support.intake}约束仅 case 绑定客户ToolSpec(nameget_credit_snapshot,effectsfrozenset(),# 只读trust_required1,allowed_rolesfrozenset({data.credit,support.intake}),handlerget_credit_snapshot,)3.2 工具二写操作提额属性值namepropose_limit_changeeffects{limit.write}trust_required2高信任allowed_roles{finance.limit}客服不可调用约束目标额度 ∈ 政策区间须带 case_idToolSpec(namepropose_limit_change,effectsfrozenset({limit.write}),# 写操作副作用显式声明trust_required2,allowed_rolesfrozenset({finance.limit}),handlerpropose_limit_change,)3.3 角色裁剪角色可调用的工具support.intakeget_credit_snapshot只读data.creditget_credit_snapshot只读finance.limitget_credit_snapshotpropose_limit_change客服 Agent 在 SkillContract 层面就没有limit.decide契约在 ToolRegistry 层面也没有propose_limit_change工具的授权。双重保险。4. 最小代码ToolRegistry P2 校验以下为教学级示意代码展示 ToolSpec 的定义与 ToolRegistry 的调用前三重校验from__future__importannotationsfromdataclassesimportdataclassfromtypingimportAny,Callabledataclass(frozenTrue)classToolSpec:工具规格——声明工具的语义、副作用和授权边界。name:streffects:frozenset[str]# 副作用集合trust_required:int# 所需信任等级allowed_roles:frozenset[str]# 允许调用的角色handler:Callable[...,Any]# 实际执行函数classToolRegistry:工具注册表——调用前执行三重校验。def__init__(self)-None:self._tools:dict[str,ToolSpec]{}defregister(self,spec:ToolSpec)-None:注册一个工具。self._tools[spec.name]specdefcall(self,name:str,*,role:str,trust_level:int,skill_effects:set[str],# 当前技能契约的副作用requested_effects:set[str],# 调用方声明的副作用**kwargs:Any,)-Any: 调用工具——三重校验。 校验 1角色授权 校验 2信任等级 校验 3P2——工具副作用 ⊆ 技能契约副作用 toolself._tools[name]# 校验 1角色授权ifrolenotintool.allowed_roles:raisePermissionError(f[ToolRegistry]{name}: 角色{role}未授权)# 校验 2信任等级iftrust_leveltool.trust_required:raisePermissionError(f[ToolRegistry]{name}: 信任不足 (需要{tool.trust_required}, 当前{trust_level}))# 校验 3P2——工具副作用必须 ⊆ 技能契约副作用ifnottool.effects.issubset(skill_effects):raisePermissionError(f[ToolRegistry] P2:{name}的副作用{sorted(tool.effects)}f超出技能契约{sorted(skill_effects)})# 校验 4调用方必须声明所有副作用iftool.effectsandnottool.effects.issubset(requested_effects):raisePermissionError(f[ToolRegistry]{name}: 须声明副作用{sorted(tool.effects)})# 校验 5不能声明超出工具的副作用unknownrequested_effects-tool.effectsifunknown:raisePermissionError(f[ToolRegistry]{name}: 声明了未知副作用{sorted(unknown)})# 全部通过 → 执行returntool.handler(**kwargs)# 工具实现 defget_credit_snapshot(case_id:str,customer_id:str)-dict[str,Any]:获取客户信用快照只读。assertcase_idCASE-CR-0042return{customer_id:customer_id,current_limit:50000,requested:120000,debt_trend:up,score:62,}defpropose_limit_change(case_id:str,new_limit:int)-dict[str,Any]:提议额度变更写操作。# 约束政策区间ifnot(50000new_limit150000):raiseValueError(f额度{new_limit}超出政策区间 (50,000 ~ 150,000))return{case_id:case_id,proposed:new_limit,status:pending_audit,}# 运行演示CASE-CR-0042 if__name____main__:registryToolRegistry()# 注册工具registry.register(ToolSpec(nameget_credit_snapshot,effectsfrozenset(),trust_required1,allowed_rolesfrozenset({data.credit,support.intake}),handlerget_credit_snapshot,))registry.register(ToolSpec(namepropose_limit_change,effectsfrozenset({limit.write}),trust_required2,allowed_rolesfrozenset({finance.limit}),handlerpropose_limit_change,))print( 场景 1数据 Agent 只读查询通过 )resultregistry.call(get_credit_snapshot,roledata.credit,trust_level1,skill_effectsset(),# 只读技能无副作用requested_effectsset(),case_idCASE-CR-0042,customer_id星河零售,)print(f ✅ 结果:{result})print(\n 场景 2财务 Agent 提额通过 )resultregistry.call(propose_limit_change,rolefinance.limit,trust_level2,skill_effects{limit.write},# 技能契约声明了写额度requested_effects{limit.write},case_idCASE-CR-0042,new_limit90000,)print(f ✅ 结果:{result})print(\n 场景 3客服 Agent 尝试提额拦截角色未授权 )try:registry.call(propose_limit_change,rolesupport.intake,trust_level2,skill_effects{limit.write},requested_effects{limit.write},case_idCASE-CR-0042,new_limit120000,)exceptPermissionErrorase:print(f ❌ 拦截:{e})print(\n 场景 4契约未声明副作用拦截P2 )try:registry.call(propose_limit_change,rolefinance.limit,trust_level2,skill_effectsset(),# 技能契约未声明 limit.writerequested_effects{limit.write},case_idCASE-CR-0042,new_limit90000,)exceptPermissionErrorase:print(f ❌ 拦截:{e})print(\n 场景 5声明了工具没有的副作用拦截 )try:registry.call(get_credit_snapshot,roledata.credit,trust_level1,skill_effectsset(),requested_effects{limit.write},# 工具是只读不应声明写case_idCASE-CR-0042,customer_id星河零售,)exceptPermissionErrorase:print(f ❌ 拦截:{e})运行输出 场景 1数据 Agent 只读查询通过 ✅ 结果: {customer_id: 星河零售, current_limit: 50000, requested: 120000, debt_trend: up, score: 62} 场景 2财务 Agent 提额通过 ✅ 结果: {case_id: CASE-CR-0042, proposed: 90000, status: pending_audit} 场景 3客服 Agent 尝试提额拦截角色未授权 ❌ 拦截: [ToolRegistry] propose_limit_change: 角色 support.intake 未授权 场景 4契约未声明副作用拦截P2 ❌ 拦截: [ToolRegistry] P2: propose_limit_change 的副作用 [limit.write] 超出技能契约 [] 场景 5声明了工具没有的副作用拦截 ❌ 拦截: [ToolRegistry] get_credit_snapshot: 声明了未知副作用 [limit.write]5. 三个教训基于 CASE-CR-0042 的 ToolSpec 设计经验教训含义证据命名即行为propose_limit_change优于update——Agent 从名称即可理解工具的业务语义场景 2 vs 传统update副作用是签名的一部分调用前即可检查工具的副作用而非运行时才发现“闯祸了”场景 4P2 拦截契约未声明副作用工具注册表按角色裁剪比“全员可见 API 目录”更安全——客服 Agent 从工具列表中就看不到提额工具场景 3客服调用被拦截6. 思考题以下问题供团队内部讨论帮助将 ToolSpec 概念落地到具体场景行为化命名在 CASE-CR-0042 上还有哪个工具需要行为化命名例如补件通知工具应该叫什么send_document_request还是notify_missing_docs副作用发现若get_credit_snapshot被运维人员“顺手”加上了缓存写副作用cache.writeSkill 与 Tool 谁先改P2 会如何拦截角色裁剪粒度propose_limit_change当前只允许finance.limit调用。如果需要支持“财务实习生”角色可提交但需二审应该如何处理是扩展现有角色还是新增工具7. 下期预告第 5 期数据飞轮 P3误分类工单类型时如何通过数据回流形成改进闭环。引入P3 协议Plan 未过 Assurance 不得进生产。8. 延伸阅读资源说明Agent-First Tool API: A Semantic Interface Paradigm for Enterprise AI AgentsarXiv:2605.10555Agent-First 工具接口设计框架Contractual Skills: A GovernSpec Design Framework for Enterprise AI AgentsarXiv:2605.22634技能契约设计框架本卷第 1 期技能即契约——SkillContract P2能力边界契约化本卷第 2 期决策四轴——四轴 P1单点决策对齐本卷第 3 期无状态决策记忆——DecisionBoard多环节决策传递本文是「企业智能体工程卷」十期专栏的第 4 期。Agent-First 工具接口——让 API 为 Agent 可读、可判、可拦让工具从“给开发者看的”变成“给 Agent 看的一等公民”。欢迎转载请注明出处与原文标题。