背景与动机
Agent 能完成任务,靠的不是模型单独生成文本,而是模型能选择工具、调用工具、读取结果,再决定下一步。工具系统和事件流,就是这个过程的工程骨架。
SSE 这类事件流适合把执行过程持续推给前端或日志系统,让用户看到 Agent 当前状态。

工具调用的基本结构
一个工具至少要定义:
- 名称。
- 描述。
- 输入 schema。
- 输出结构。
- 错误格式。
- 权限边界。
示例:
{ "name": "read_file", "description": "Read a UTF-8 text file from the workspace", "input": { "path": "src/content/notes/example.md" }, "output": { "content": "..." }}描述要具体。工具描述越模糊,模型越容易在错误场景调用。
事件流应该记录什么
常见事件:
session.createdmessage.receivedmodel.output.deltatool.call.startedtool.call.completedtool.call.failedsession.completed事件的价值是让外部系统知道:
- Agent 是否还在工作。
- 当前调用了什么工具。
- 工具是否失败。
- 失败后是否重试。
- 最终结果从哪里来。
状态反馈要用户可理解
不要只输出底层事件名。面向用户可以转换成:
正在读取项目规则...正在搜索相关文件...正在运行测试...测试失败,正在根据错误调整...任务完成。这样用户能判断是否需要打断或纠偏。
失败恢复
工具失败不应该直接结束任务。可以按错误类型处理:
- 文件不存在:重新搜索路径。
- 权限不足:请求确认或切换只读方案。
- 命令失败:读取错误输出并定位原因。
- 网络失败:重试或降级。
重要的是把失败作为可观察事件记录下来,而不是吞掉。
常见陷阱
- 工具输入没有 schema,模型只能猜参数。
- 工具错误只返回“failed”,缺少可操作信息。
- 事件流只展示最终结果,无法调试中间过程。
- 工具权限过大,所有操作都通过一个通用 shell 完成。
工具设计检查表
名称是否表达具体动作?描述是否说明适用场景和限制?参数是否有明确 schema?输出是否能被模型继续使用?错误信息是否可操作?是否区分只读和写入?是否会把外部提示注入带回系统上下文?一句话总结
工具系统决定 Agent 能做什么,事件流决定人和系统能否看清它正在怎么做。