你的AI代理不需要臃肿框架:200行TypeScript轻量级执行 harness 实现指南
- 作者

- 姓名
- Nino
- 职业
- Senior Tech Editor
大多数开发者在着手构建AI代理时,往往会陷入使用沉重且具有强烈的观点性框架的陷阱。他们导入庞大的抽象库、配置错综复杂的状态机,并花数天时间排查基于图的工作流为何无法正确路由。然而事实是:你的代理根本不需要框架。它真正需要的是一个循环、一个工具注册表、三个安全限制以及一个审批网关。
这只需要200多行TypeScript代码,其中的每一行都是你可以亲自阅读、测试并在代理行为异常时进行修改的纯粹代码。当你需要持久化运行、多代理路由或不想自己实现的分布式可观测性时,框架才开始体现其价值。但在第一天,当你只有一个代理和五个工具时,框架反而是仓库中最臃肿的负担。
当你通过像n1n.ai这样可靠的高性能API聚合器调用来自DeepSeek-V3或Claude 3.5 Sonnet等顶级模型的服务时,保持架构的极简透明能够确保最高的执行速度与零冗余开销。
代理执行内核的六大核心要素
剥开营销宣传的外衣,一个可靠的代理执行层仅由六个部分组成:
- 执行循环(The loop):一个拥有消息历史记录并决定何时终止的
while循环。 - 工具注册表(The tool registry):从工具名称到描述、JSON模式以及处理函数的映射。系统中其他部分无需了解工具的底层实现。
- 安全限制(The limits):最大轮次限制、最大成本预算以及重复调用防护机制。这是初级代理全部的安全防线。
- 模型接口(The model interface):一个统一的
complete()方法,这意味着切换大模型只需更换适配器,而无需重写代理逻辑。 - 验证器(The verifier):在判定任务结束前对最终输出结果进行的自动化校验检查。
- 审批网关(The approval gate):写入操作和涉及资金的操作必须等待人工确认。读取操作则可直接放行。
仔细观察你会发现缺少的组件:没有复杂的图DSL、没有庞大的代理类继承体系、没有插件系统,也没有状态机框架。
核心TypeScript实现代码
以下是核心的执行引擎实现。它完全与模型提供商无关、零外部依赖,赋予你对执行流程的完全掌控。在生产环境中,你可以通过n1n.ai无缝路由请求,以保持最优延迟。
// harness.ts - 循环、注册表与限制机制。无任何第三方依赖。
export type Message = \{
role: "user" | "assistant" | "tool";
content: string;
toolCallId?: string;
toolCalls?: ToolCall[];
\};
export type ToolCall = \{
id: string;
name: string;
args: unknown;
\};
export type ModelResponse = \{
text: string;
toolCalls: ToolCall[];
costUsd: number;
\};
export interface ModelClient \{
complete(messages: Message[], tools: ToolDef[]): Promise<ModelResponse>;
\}
export type ToolDef = \{
name: string;
description: string;
parameters: Record<string, unknown>;
needsApproval: boolean;
run: (args: unknown) => Promise<unknown>;
\};
export type Limits = \{
maxTurns: number;
maxCostUsd: number;
approve?: (toolName: string, args: unknown) => Promise<boolean>;
verify?: (answer: string) => boolean;
\};
export type AgentResult =
| \{ status: "done"; answer: string; turns: number; costUsd: number \}
| \{ status: "turn_limit" | "cost_limit" | "repeated_call" | "blocked" | "failed_verification"; turns: number; costUsd: number \};
function sameCall(a: ToolCall, b: ToolCall): boolean \{
return a.name === b.name && JSON.stringify(a.args) === JSON.stringify(b.args);
\}
export async function runAgent(
task: string,
model: ModelClient,
tools: ToolDef[],
limits: Limits,
): Promise<AgentResult> \{
const messages: Message[] = [\{ role: "user