内容提要
本文介绍如何用TypeScript构建独立于模型提供商的AI智能体执行框架,以编码智能体为例,核心原则是模型只提出动作、由框架负责执行。文章逐步实现基础执行循环、工具参数校验、工作区文件隔离与命令白名单、轮次与工具调用预算及超时取消、保持工具调用与结果成组的上下文裁剪、事件观测与用量统计,并用脚本化假模型对失败路径做确定性测试,最后说明如何接入真实模型。
延伸解读
模型只提议,框架负责执行
文章的核心架构决策是将模型与执行分离:模型生成工具调用请求,但实际执行由框架完成。这意味着工具参数在模型侧只是“请求”,框架必须将其视为不可信输入,先校验再执行。这种设计让框架能独立于具体模型提供商,也便于在模型之外施加安全与资源限制。
错误分类决定恢复策略
文章区分了工具失败与模型失败:未知工具、参数无效或工具抛异常会作为可恢复的工具错误返回给模型,模型可据此调整;而模型调用本身失败则终止运行并标记为 model_error。这种区分避免框架在模型不可用时假装还能继续,也让测试能确定性地覆盖不同失败路径。
安全边界靠实现而非提示
工作区隔离和命令白名单由工具实现强制执行,而不是依赖模型遵守指令。文件操作拒绝绝对路径、越界路径和符号链接;命令工具只允许开发者预设的命名命令,模型不能提供可执行文件或额外参数。但文章也指出,这仍是教学性限制,并非操作系统级沙箱,运行不可信代码仍需容器或进程隔离。
预算与上下文需分别管理
轮次上限只限制模型响应次数,不限制工具调用数量,因此框架单独统计工具调用预算,并在执行整批调用前检查是否超限,避免部分执行。上下文管理则区分保留的完整记录与发送给模型的消息,按字符数裁剪并保持工具调用与结果成组,防止出现孤立或不完整的对话。
Q&A
用TypeScript构建AI智能体执行框架时,模型和框架各自负责什么?
核心原则是模型只提出动作,由框架负责执行。模型生成包含工具名和参数的结构化请求,但并未实际执行任何操作;框架负责解释该请求、调用对应实现,并将结果返回给模型。
为什么不能直接信任模型返回的工具调用参数?
模型生成的工具参数是不可信输入,工具描述只是对模型的请求,并不能保证模型返回有效参数。因此框架必须在执行前用JSON Schema子集校验参数,校验失败时工具不会运行,模型会收到关联原调用ID的错误结果。
如何防止编码智能体访问工作区之外的文件或执行任意命令?
通过Workspace类将文件操作限制在规范化的根目录内,拒绝绝对路径、逃逸根目录的路径以及符号链接;命令工具只接受开发者预定义的命名命令预设,模型不能提供可执行文件、额外参数或shell语法。
智能体执行框架如何限制运行时间和工具调用次数?
框架分别统计模型轮次(maxSteps)和工具调用次数(maxToolCalls),每个请求的调用都消耗预算,包括未知工具和无效参数。同时通过AbortSignal和timeoutMs设置截止时间,在批次执行前检查整个批次是否超出预算,避免部分执行。
上下文裁剪时为什么不能把工具调用和它的结果分开?
助手消息可能请求多个工具,每个由调用ID标识。如果只发送部分结果,会造成无效或误导性的对话。因此选择器将助手消息及其整批结果视为不可分割的组,要么完整包含,要么整体省略,并拒绝孤立结果和不完整批次。
如何在不依赖真实模型的情况下对智能体框架做确定性测试?
使用脚本化假模型(scriptedModel),它按数组返回预定义响应,脚本耗尽即报错,避免意外的额外模型轮次静默通过。测试可覆盖未知工具、步骤限制、取消、无效参数、超预算批次等失败路径,无需API密钥、网络或token成本。