一个常见误区是把 OpenAPI 文档直接丢给模型,然后期待它自己组合出正确调用。真实情况是:模型会按语义猜,而大多数内部 API 的命名并不是为语义调用设计的。

一、工具名应该表达意图

query_table 不是能力,find_overdue_homework 才是。工具名越接近用户语言,模型选错工具的概率越低。

// 模糊
run_query(sql)

// 清晰
list_pending_assignments(child_id, subject?)
get_assignment_detail(assignment_id)
create_practice_from_photo(child_id, image_url)

工具数量也要控制。不是越多越好:每多一个工具,选择空间和误用概率都会上升。优先合并同质操作,把复杂组合留在代码里。

二、参数是契约,不是建议

参数名要稳定、类型明确、枚举可穷举。不要让模型传自由格式的 JSON 字符串,也不要让“可选参数”实际上决定完全不同的行为。

设计检查

把工具签名给一个不了解系统的人看。他能不能在不看文档的情况下,判断每个参数该填什么?如果不能,模型也不能。

三、错误要能指导下一步

工具返回不只是一个状态码。对 Agent 来说,错误信息是观察结果的一部分。好的错误会告诉它:是继续、换参数、请求用户,还是停止。

// 模型无法行动
{ "error": "400 Bad Request" }

// 模型可以行动
{
  "error": "IMAGE_TOO_SMALL",
  "message": "图片短边小于 800px,题目文字无法识别",
  "next": "请用户重新上传清晰照片,或改用手动录入"
}

把可恢复错误和不可恢复错误分开。可恢复错误可以重试;不可恢复错误应该尽快收敛,避免无限循环。

四、幂等和副作用

Agent 会重试。工具如果不幂等,一次超时就可能创建两份作业、发两条消息。写操作要么带幂等键,要么在服务端做去重。

create_assignment({
  child_id: 3,
  questions: [...],
  idempotency_key: "daily-2026-10-27-child-3"
})

同时区分“查询”和“执行”。读工具可以自由重试,写工具必须有明确的副作用边界,并在轨迹里记录。

五、权限放在代码里,不放在提示词里

“请不要删除用户数据”不是权限控制。真正的权限是服务端校验:这个 Agent 代表谁、能访问哪个家庭、能执行哪些动作。

一个工具定义模板

{
  "name": "create_practice_from_photo",
  "description": "从一张清晰的作业照片生成练习草稿,不直接布置。",
  "when_to_use": "用户上传照片并要求整理成练习时",
  "when_not_to_use": "图片模糊、包含人脸或与学习无关时",
  "parameters": {
    "child_id": "integer, required",
    "image_url": "string, required, 已上传图片地址",
    "subject": "math|chinese|english, optional"
  },
  "returns": "draft_id, question_count, warnings[]",
  "side_effects": "创建草稿,不发布",
  "requires_confirmation": false
}

最后一句很重要:工具不是越自动越好。把“生成草稿”和“正式布置”拆开,系统就多了一个人类检查点。

下一篇:Agent 的记忆 →02 / MEMORY