AI 学习经验
用 TypeScript 从零搭一个 AI Agent Harness:校验、隔离、预算与可观测性
多数 Agent 教程只演示工具调用,却回避了工具失败、参数校验、无限循环和上下文爆掉这些真问题。这篇教程用 TypeScript 和 Node.js 24 从最简循环起步,逐层加上参数校验、工作区隔离、执行预算、上下文裁剪和事件观测,并用脚本化的假模型把失败路径也测出来。
今天就能做
- 先写一个 scriptedModel 假模型,把 Agent 主循环和失败路径的测试跑通,再接真实模型。
- 在工具执行前加一层 JSON Schema 校验,并把工具参数类型定为 unknown 而非 any。
- 给 Agent 设 maxSteps 和 AbortSignal,轮数与工具调用次数分开计数,避免无限跑。
核心架构:模型只提议,harness 才执行
- 最重要的设计决定是把「提议」和「执行」分开:模型返回的只是一条带工具名和参数的结构化请求,真正调用实现、把结果回填给模型的是 harness。
- Model 接口刻意不绑定任何厂商,OpenAI、Anthropic 的适配器只需在自家 API 和这套类型之间做转换,harness 本身不用改。
- 工具参数的类型定为 unknown 而不是 any,因为模型生成的数据属于不可信输入,必须在执行前校验。
- 主循环顺序固定:把对话和工具定义发给模型、追加模型回复、若有工具请求就执行并追加结果、直到拿到最终答案或触达步数上限。
工具失败不等于任务终止
- 工具抛错时,harness 不直接结束,而是追加一条带原 callId 的 tool 消息并标记 isError,让模型下一轮自己决定怎么补救,比如换个路径或换条命令。
- 请求了不存在的工具名同样走这条路:回一条 Unknown tool 错误消息,而不是崩掉整个运行。
- 注册表用 Map 建立工具名到实现的映射,并在启动时检查重名,避免同名工具互相覆盖。
- maxSteps 默认 10,且必须是正的安全整数,否则直接抛 RangeError;模型可以一直请求干活,但迭代次数由 harness 说了算。
用假模型把测试变确定性
- 测试里不调真实 API,而是写一个 scriptedModel,按数组顺序返回预设响应:先一条工具请求,再一条最终答案。
- 脚本耗尽即抛错,这样多出来的意外模型轮次不会静默通过,测试能真正卡住循环次数。
- 最小用例覆盖「执行工具并返回最终答案」这条主路径,用 Node.js 内置 test runner 和 assert/strict 跑,依赖极少。
- 项目按 Git tag 分节推进,每节对应一个里程碑,可以 checkout 到 01-model-loop 这样的快照跟着做,而不是直接看最终代码。
后续要补的几层防护
- 参数校验:给每个工具定义显式的 JSON Schema 契约,执行前先验证,并区分「参数不合法」和「执行失败」两类错误,处理方式不同。
- 工作区隔离:把文件操作限制在指定目录内,命令由模型从白名单里选,而不是让它自由拼命令。
- 执行预算:轮数和工具调用次数分开计数,并把取消信号和截止时间一路透传下去。
- 上下文管理:对工具输出和面向模型的历史做长度上限,且不能把一次工具调用和它的结果拆散。
- 可观测性:在执行边界发事件,模型用量只统计真实拿到的数据,不凭空估算。
原文:How to Build an AI Agent Harness from Scratch in TypeScript
来源:freeCodeCamp · 2026-10-20
查看原文本文由 AI 阅读原文后整理,未经人工逐字核对,内容以原文为准。
登录后可以点赞、查看和发表评论