最新n1n v2.0.1 正式上线!企业级大模型接口聚合平台 (LLM API Gateway),为您接入 500+ AI Models,价格低至 1 折, 立即尝试

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

作者
  • avatar
    姓名
    Nino
    职业
    Senior Tech Editor

大多数开发者在着手构建AI代理时,往往会陷入使用沉重且具有强烈的观点性框架的陷阱。他们导入庞大的抽象库、配置错综复杂的状态机,并花数天时间排查基于图的工作流为何无法正确路由。然而事实是:你的代理根本不需要框架。它真正需要的是一个循环、一个工具注册表、三个安全限制以及一个审批网关。

这只需要200多行TypeScript代码,其中的每一行都是你可以亲自阅读、测试并在代理行为异常时进行修改的纯粹代码。当你需要持久化运行、多代理路由或不想自己实现的分布式可观测性时,框架才开始体现其价值。但在第一天,当你只有一个代理和五个工具时,框架反而是仓库中最臃肿的负担。

当你通过像n1n.ai这样可靠的高性能API聚合器调用来自DeepSeek-V3或Claude 3.5 Sonnet等顶级模型的服务时,保持架构的极简透明能够确保最高的执行速度与零冗余开销。

代理执行内核的六大核心要素

剥开营销宣传的外衣,一个可靠的代理执行层仅由六个部分组成:

  1. 执行循环(The loop):一个拥有消息历史记录并决定何时终止的while循环。
  2. 工具注册表(The tool registry):从工具名称到描述、JSON模式以及处理函数的映射。系统中其他部分无需了解工具的底层实现。
  3. 安全限制(The limits):最大轮次限制、最大成本预算以及重复调用防护机制。这是初级代理全部的安全防线。
  4. 模型接口(The model interface):一个统一的complete()方法,这意味着切换大模型只需更换适配器,而无需重写代理逻辑。
  5. 验证器(The verifier):在判定任务结束前对最终输出结果进行的自动化校验检查。
  6. 审批网关(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