耐点
博客

个人Agent:TypeScript 实现路线图参考(MVP 篇)

22 分钟15 次阅读

我个人学习项目(自建 AI Harness)的路线图。 环境快照(2026-08):Node.js 22+、Vercel AI SDK 7、模型 K3、GLM5.2、DSV4FGA、K2.7HS、HY3。依赖版本变化很快,以 npm 最新为准。

一、目标定位

MVP 范围

参考本系列第 5 篇《Harness Engineering 四大支柱》的成熟度评估模型,MVP 目标定位于 Level 1-2 之间:

能力 MVP 必须 v2 可选
Agent Loop(ReAct)
5-7 个核心工具
工具调用 + 参数 schema
基础权限(allow/ask/deny)
上下文管理(observation 屏蔽)
持久化记忆(progress 文件)
Compaction ⚠️ 简化版 完整 4 级
子 Agent
Plan/Act 分离
MCP 集成
Hooks 系统
沙盒
多 Agent 编排

不做的事(避免过度工程)

  • 一上来就多 agent — 先榨干单 agent
  • 一上来就 MCP — 先把内置工具做扎实
  • 一上来就完整压缩管道 — 先做 observation 屏蔽
  • 一上来就 Plan/Act — 先跑通 ReAct

二、技术栈选型

推荐(参考 OpenHarness / coding-agents-from-scratch)

选择 理由
语言 TypeScript 类型安全,生态成熟
Runtime Node.js 22+(或 Bun) Node 稳定;Bun 更快但生态略新(AI SDK 7 与 execa 10 均要求 Node 22+)
LLM 调用 Vercel AI SDK 7 统一接口、流式输出、工具调用
Provider OpenAI-compatible 可配置 base URL,接 GPT/Claude/本地模型
Schema 验证 Zod 4 与 Vercel AI SDK 配套
终端 UI(可选) React + Ink 像 Claude Code 一样;MVP 先用 readline
Shell 执行 execa 10shelljs 跨平台
流式 Async Generator 与 Claude Code 一致
日志 pino 结构化、低开销
测试 Vitest 4 Vite 生态,ESM 友好
评测 Laminar(可选) 结构化评测(https://www.lmnr.ai )

极简依赖树(MVP 起步,版本截至 2026-08)

{
  "dependencies": {
    "ai": "^7.x",            // Vercel AI SDK
    "@ai-sdk/openai": "^4.x",
    "zod": "^4.x",
    "execa": "^10.x"
  },
  "devDependencies": {
    "typescript": "^5.x",
    "vitest": "^4.x",
    "@types/node": "^22.x"
  }
}

三、参考项目

直接参考源码

项目 链接 为什么学
coding-agents-from-scratch TypeScript 版 https://linzzzzzz.github.io/coding-agents-from-scratch/typescript-zh/(仓库:https://github.com/linzzzzzz/coding-agents-from-scratch ) 16 章从零搭一个 CLI coding agent,最贴近你的目标;注意原版 Python/Go 仓库已下架,这是现役的 TS 版
OpenHarness https://open-harness.dev/ MIT 协议、基于 Vercel AI SDK 的 composable harness 原语
puristajs/harness https://github.com/puristajs/harness TypeScript 写的 AI Harness(Apache-2.0,2026-05 创建,很早期的个人项目,仅作参考)
OpenCode https://opencode.ai/ provider 无关的开源终端 agent
Claude Code 文档 https://code.claude.com/docs/en/overview Anthropic 官方
Harness Engineering 完全指南 https://wanlanglin.github.io/-awesome-cc-harness/zh/ Claude Code 源码逆向工程教科书(仓库:https://github.com/WanLanglin/-awesome-cc-harness )

参考的Agent设计

项目 链接 对照点
Cline https://github.com/cline/cline Plan/Act 分离、MCP 集成
Aider https://github.com/Aider-AI/aider git 原生、repo map、编辑格式
mini-SWE-agent https://github.com/SWE-agent/mini-swe-agent 最小参考实现,适合学循环

四、项目结构

参考 coding-agents-from-scratch,适配 harness 命名:

myHarness/
├── docs/                          # 研究资料
├── src/
│   ├── agent/
│   │   ├── run.ts                # 核心 agent loop
│   │   ├── executeTool.ts        # 工具分发器
│   │   ├── tools/
│   │   │   ├── index.ts          # 工具注册表
│   │   │   ├── file.ts           # 文件操作(Read/Write/Edit)
│   │   │   ├── bash.ts           # Shell 命令
│   │   │   ├── glob.ts           # 文件搜索
│   │   │   ├── grep.ts           # 内容搜索
│   │   │   └── webFetch.ts       # 网页抓取(可选)
│   │   ├── context/
│   │   │   ├── index.ts
│   │   │   ├── tokenEstimator.ts
│   │   │   ├── observationMask.ts  # observation 屏蔽
│   │   │   └── compaction.ts       # 简化版压缩
│   │   └── system/
│   │       ├── prompt.ts         # 系统提示
│   │       └── permissionRules.ts # 权限规则
│   ├── memory/
│   │   ├── progressFile.ts       # claude-progress.txt
│   │   ├── featureList.ts        # feature_list.json
│   │   └── agentsMd.ts           # AGENTS.md 读写
│   ├── permission/
│   │   ├── decision.ts           # 权限决策管道
│   │   └── modes.ts             # default/acceptEdits/plan
│   ├── types.ts
│   └── index.ts
├── evals/                        # 评测
├── package.json
└── tsconfig.json

五、分阶段实现步骤

Stage 1:最小 ReAct 循环(目标:能跑工具调用)

步骤

  1. npm init,装 Vercel AI SDK + Zod
  2. 写最简的 run():接收 messages + 输入,调 LLM,流式输出
  3. 定义一个工具(如 read_file),用 Zod schema
  4. 把工具注册到 AI SDK 的 tools 参数
  5. 实现循环:
    • 模型输出 → 检测 tool_calls
    • 有 → 执行 → 结果作为 tool_result 喂回 → 继续循环
    • 没有 → 输出最终文本,退出

验收

  • 模型能调用 read_file 读取本地文件
  • 多轮对话能维持(至少 5 轮不丢)
  • 能流式打印 token

Stage 2:核心工具集(目标:能处理代码库)

步骤

  1. write_file / edit_file 工具
  2. bash 工具(用 execa)
  3. glob 工具(文件名搜索)
  4. grep 工具(内容搜索)
  5. (可选)加 web_fetch(用 fetch)
  6. 工具接口抽象:每个工具有 namedescriptioninputSchemacallisReadOnly

关键决策

  • isReadOnly=true 的工具可并发
  • isReadOnly=false 的工具必须串行
  • 工具结果大小限制(防止几 MB 的 transcript 爆上下文)

验收

  • 让 agent 在你工作目录里读文件、改文件、跑命令
  • 能跑通"找到 README,总结,写到 SUMMARY.md"这种任务

Stage 3:权限与审批(目标:不让 agent 闯祸)

步骤

  1. 实现 3 档权限:allow / ask / deny
  2. 实现 default 模式(敏感操作询问)
  3. 实现 acceptEdits 模式(自动批准文件编辑)
  4. 实现 bypassPermissions 模式(危险,自动批准一切)
  5. .harness/settings.json(类似 Claude Code 的 settings)读写
  6. 加用户审批 prompt(readline 即可)

权限规则示例(类似 Claude Code)

{
  "permissions": {
    "allow": ["Read(*)", "Glob(*)", "Grep(*)", "Bash(git *)"],
    "ask": ["Write(*)", "Edit(*)", "Bash(npm *)"],
    "deny": ["Bash(rm -rf *)", "Bash(sudo *)"]
  }
}

验收

  • 启动 agent,它读文件不问,写文件先问
  • rm -rf 之类命令硬拒绝
  • acceptEdits 模式下写文件不问

Stage 4:上下文管理(目标:长对话不爆上下文)

步骤

  1. 实现 token 估算(用 tiktoken 或粗略的字符数/4)
  2. 实现 observation 屏蔽:N 轮前的工具结果替换为 [Previous: used {tool}]
  3. 实现简化版 compaction:接近上限时,让 LLM 自己总结对话历史
  4. 实现 taskBudgetRemaining 追踪

验收

  • 跑一个会让上下文膨胀的任务(如"读 100 个文件,总结")
  • 不爆上下文,能持续推进
  • agent 不丢失关键信息(架构决策、未完成 todo)

Stage 5:持久化记忆(目标:跨会话连续性)

步骤

  1. 实现 AGENTS.md 读写(项目级持久上下文)
  2. 实现 progress.md 写入(每会话结束更新)
  3. 实现 feature_list.json 读写(JSON 格式,避免被模型乱改)
  4. 实现 Initializer 模式检测:首次会话检查 feature_list.json 是否存在
  5. 实现 Coding 模式启动序列:pwd → 读 git log → 读 progress → 读 feature_list → 选最高优先级未完成

关键文件格式

// feature_list.json
{
  "features": [
    {
      "id": "F001",
      "description": "User can create a new chat",
      "steps": ["...", "..."],
      "passes": false,
      "priority": "high"
    }
  ]
}
# progress.md
## 2026-07-28 Session 3
- Implemented F001 (new chat creation)
- Bug: sidebar not updating after new chat
- Next: F002 (message sending)

验收

  • 跑会话 1(Initializer)→ 生成 feature_list、init.sh、初始 git commit
  • 跑会话 2(Coding)→ 自动接上,实现一个 feature,git commit,更新 progress
  • 跑会话 3(Coding)→ 自动接上,从上次中断处继续

Stage 6:验证循环(目标:agent 能自检)

步骤

  1. 让 agent 在标记 feature passes: true 前先跑测试
  2. 系统提示加强措辞:“It is unacceptable to remove or edit tests”
  3. (可选)加 puppeteer MCP 做浏览器端到端测试

验收

  • agent 不会过早标记完成
  • 失败的测试会被识别并修复

六、最小 ReAct 循环骨架代码

这是 Stage 1 的目标代码,不是生产级。生产级实现要处理大量边界情况(参考 Claude Code 数千行的实现)。

// src/agent/run.ts
import { streamText, Tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
import { readFileSync } from "node:fs";

// 1. 定义工具
const tools = {
  read_file: {
    description: "Read the contents of a file",
    parameters: z.object({
      path: z.string().describe("Absolute file path"),
    }),
    execute: async ({ path }) => readFileSync(path, "utf-8"),
  },
  // ... write_file, bash, glob, grep
} satisfies Record<string, Tool>;

// 2. Agent Loop
type Message = any; // 简化

async function run(
  messages: Message[],
  userInput: string,
): Promise<Message[]> {
  const state = { messages: [...messages, { role: "user", content: userInput }] };

  while (true) {
    const result = await streamText({
      model: openai("gpt-5.4"),
      system: "You are a helpful coding agent. Use tools to gather context and act.",
      messages: state.messages,
      tools,
      maxSteps: 20,  // 防止无限循环
    });

    // 流式打印
    for await (const delta of result.fullStream) {
      if (delta.type === "text-delta") {
        process.stdout.write(delta.textDelta);
      }
    }

    // 检查是否有工具调用
    const toolCalls = await result.toolCalls;
    if (toolCalls.length === 0) {
      // 没有工具调用 = 最终答案
      const finalText = await result.text;
      state.messages.push({ role: "assistant", content: finalText });
      return state.messages;
    }

    // 有工具调用 → 工具结果加入 messages → 继续循环
    const toolResults = await result.toolResults;
    state.messages.push({ role: "assistant", content: toolCalls });
    state.messages.push({ role: "tool", content: toolResults });
  }
}

// 3. 入口
const userInput = process.argv[2] ?? "Hello";
run([], userInput).then(finalMessages => {
  console.log(`\n[Done] ${finalMessages.length} messages`);
});

按 概念 AI SDK 4/5 的 API 编写(手动循环以说明原理,maxSteps 参数在 AI SDK 4/5 的 streamText 中内置了 agent loop)。升级到 AI SDK 7 后,建议改用官方的 ToolLoopAgent(内置 agent 循环、含 sandbox/shell 工具与 toolApproval 工具审批钩子),或继续手动循环并把 maxSteps 换成 stopWhen


七、测试陷阱

1. 不要一上来就多 agent

MVP 阶段:单 agent + 多会话(Anthropic 长跑模式)。多 agent 的复杂度是数量级提升。

2. 不要相信模型会"记得"

任何跨会话的状态都必须落到文件。模型记忆 = 上下文窗口,窗口清空就没了。

3. 不要让上下文越长越好

参考本系列第 1 篇的 Smart Zone 数据:前 40% 是 Smart Zone,超过 40% 进入 Dumb Zone。

4. 不要写完代码就标完成

参考 Anthropic 第四个失败模式。加强措辞系统提示 + 强制跑测试。

5. 不要让 agent 自动跑 rm -rf 之类

硬编码拒绝列表,即使 bypassPermissions 模式也不放过。

6. JSON 比 Markdown 更适合结构化状态

agent 不太容易乱改 JSON,但很容易"优化"Markdown 描述。

7. git commit 是免费的 checkpoint

不要自己实现状态持久化,直接用 git。


八、个人评测

参考 SWE-agent 的思路:

单轮评测

  • 给一个任务,检查 agent 选对了工具
  • 写 golden / secondary / negative 测试用例

多轮评测

  • 用 mock 工具测试完整对话
  • 用 LLM-as-judge 给输出打分
  • 评估工具调用顺序、避免使用 forbidden tool

端到端评测

  • 跑真实任务(例如"修这个 repo 的某个 issue")
  • 看是否产出可工作的代码

也可以从最后开始~

  1. 对照 coding-agents-from-scratch TypeScript 版 开始跟练
  2. 对照 OpenHarness 的 API 设计

附:2026 年 8 月更新与补充阅读

  • Vercel AI SDK 7 已发布(ai 7.0.48,2026-08-02):新增 ToolLoopAgent(内置 agent 循环的类,含 sandbox/shell 工具)、Output.object() 结构化输出、模型字符串直连 Vercel AI Gateway;文档站新增「AI SDK Harnesses」章节与 Terminal UI 支持,并有官方的 toolApproval 工具审批参数——本路线图 Stage 3 的权限设计现在可以借力 SDK,不必全部自研。
  • OpenHarness 是"对照 API 设计"的最佳活参考:MIT 协议、基于 Vercel AI SDK,提供可组合中间件(重试/压缩/持久化)、子 Agent 层级、两阶段上下文压缩、异步工具审批回调、MCP 集成与 AGENTS.md 自动注入。
  • coding-agents-from-scratch TS 版现状:2026-05 创建(17 stars,很新),GitHub Pages 已启用;原版(Python/Go)仓库已下架,引用时以学习参考为主。
  • Claude Code 逆向指南仓库:https://github.com/WanLanglin/-awesome-cc-harness (87 stars),与本文骨架同源。

参考来源