工具调用、授权与副作用提交
模型只能提出工具意图,不能直接获得数据库连接、支付权限或文件系统权限。服务端要重新选择白名单工具、校验参数、绑定用户与租户身份、检查资源范围,并为有副作用的操作设置幂等键、确认和审计记录。工具结果回填模型前仍是外部数据,应限制大小并保留来源。
第一个可运行实验:把意图变成受控执行
使用 Python 3.12 保存 tools.py,运行 python tools.py。实验不连接真实服务,只验证授权、参数和幂等顺序。
python
TOOLS = {'lookup_order': {'effect': 'read'}, 'cancel_order': {'effect': 'write'}}
SEEN = set()
def execute(intent, user, confirmed=False):
name = intent['name']
if name not in TOOLS:
raise PermissionError('tool not allowlisted')
if intent.get('tenant_id') != user['tenant_id']:
raise PermissionError('tenant mismatch')
if not isinstance(intent.get('order_id'), str):
raise ValueError('invalid order id')
if TOOLS[name]['effect'] == 'write' and not confirmed:
return {'status': 'needs_confirmation'}
key = (user['tenant_id'], name, intent['order_id'], intent.get('idempotency_key'))
if key in SEEN:
return {'status': 'already_processed'}
SEEN.add(key)
return {'status': 'executed', 'tool': name}
user = {'tenant_id': 't1'}
intent = {'name': 'cancel_order', 'tenant_id': 't1', 'order_id': 'o1', 'idempotency_key': 'k1'}
assert execute(intent, user)['status'] == 'needs_confirmation'
assert execute(intent, user, confirmed=True)['status'] == 'executed'
assert execute(intent, user, confirmed=True)['status'] == 'already_processed'
print('tool boundary passed')call_id 用于关联一次模型工具意图,idempotency_key 用于防止业务副作用重复提交,二者不能混为一谈。生产实现不能只把去重集合放在进程内存;重启、并发和多实例部署都要求持久化约束或下游幂等能力。
变体实验
将工具改成只读查询,确认不需要用户确认但仍要校验租户。再把同一个幂等键用于不同订单,要求实现拒绝,而不是复用第一次结果。最后模拟工具超时,明确区分“未执行”“执行中未知”和“已失败”。
练习
- 为工具结果加入
source、created_at和截断标记,回填模型前限制长度。 - 为删除、付款和发消息分别设计确认内容,确认必须绑定具体资源和参数摘要。
- 设计最多步数、总 deadline、单工具超时和重复调用检测四个停止条件。