上一篇:AGENTS.md:把项目规则注入 Agent 上下文
English version: How to Build a Mini AI Coding Agent Harness in TypeScript
文章介绍
14 天前,这个项目只有一个问题:Cursor、Codex、Claude Code 这类 coding agent 到底是怎样工作的?
现在我们得到的不是一个完整 IDE,也不是一个通用 autonomous agent,而是一个可运行、可检查的 TypeScript CLI harness。它把模型调用、上下文、工具、执行循环、patch、安全边界、项目规则和可观测性拆成了清晰的模块。
这正是第一阶段的目标:先理解 harness 层,再决定哪些产品能力值得继续投入。

今天要解决什么
Day 14 不再引入一个孤立功能,而是完成四件事:
- 新增
--overview,直接从当前工具注册表展示 harness 能力边界。 - 运行 context、rules、file tools、Git、patch、approval 与 transcript 的完整 CLI demo。
- 复盘 14 天各模块如何组成一次 agent run。
- 明确 Phase 2 的 Skills、MCP、IDE 与 subagents 边界。
先运行能力概览:
npm run dev -- --overview
输出会列出当前 workspace、已注册工具、执行生命周期、安全策略和还没有实现的扩展能力。
最终架构
完整链路可以归纳为:
user task + AGENTS.md + project snippets + tool definitions
-> context builder
-> model provider
-> agent loop
-> tool registry
-> approval gate + workspace sandbox
-> tool result
-> transcript + context report
这条链路里的每一层都有不同职责:
| 层 | 当前实现 | 负责什么 |
|---|---|---|
| Context | system prompt、rules、snippets、tools | 给模型足够且可解释的输入 |
| Model | mock 或 OpenAI-compatible provider | 根据 messages 决定回答或工具调用 |
| Agent loop | bounded observe-decide-act loop | 驱动多步执行、超时和最终答案 |
| Tools | files、Git、patch | 把模型意图转换为受控操作 |
| Safety | approval + sandbox | 决定是否执行、限制可达路径 |
| Observability | JSONL transcript + context report | 回放行为、定位 token 与错误 |
14 天能力地图
Day 01-04:先定义边界与协议
前四天没有急着写复杂功能,而是建立最小运行骨架:
- Day 01:区分 model、agent 和 harness。
- Day 02:建立 TypeScript npm workspace 与 CLI。
- Day 03:定义 messages 和 provider 边界。
- Day 04:建立 schema 驱动的 tool registry。
最关键的判断是:模型不是 harness。模型只生成下一步意图;harness 才负责注入上下文、验证工具输入、执行副作用和记录结果。
Day 05-07:让 agent 观察、循环与回放
Day 05 的 list_files、read_file、search_text 让 agent 能观察 workspace。Day 06 的 bounded loop 把单轮聊天变成:
observe -> decide -> act -> observe -> final
Day 07 的 JSONL transcript 让每一次 model request、tool call、tool result、耗时与错误都可以被回放。
没有 transcript 的 agent 很难调试。你无法判断问题来自模型、上下文、工具参数、超时,还是循环本身。
Day 08-09:把上下文变成工程对象
Day 08 将 system prompt、用户任务、历史摘要、工具定义和文件片段集中到 context builder。Day 09 将这些来源转换成可检查的 token report。
这使上下文不再是散落在字符串拼接里的隐形状态:
Context Explorer
-> source
-> kind
-> estimated tokens
-> budget percentage
Day 10-11:从观察代码到提出并验证修改
Day 10 的 git_status 与 git_diff 让模型可以看见工作区变化。Day 11 使用成熟的 diff 库解析和应用 unified diff,不手写 hunk 匹配。
apply_patch 的重要设计是先准备所有文件内容,再执行写入;解析失败或 hunk 不匹配会返回可恢复错误,而不是让进程崩溃或只写入一半。
Day 12-13:加入真实产品必须面对的约束
Day 12 将真实写入与 dry-run 分开。默认模式拒绝 write-class action,只有显式 --allow-writes 才能执行;workspace guard 会拒绝 ../、绝对路径和 symlink escape。
Day 13 从根目录加载 AGENTS.md,把 durable project guidance 作为独立 rules context part 注入,并纳入 token report。
到这里,agent 具备了一个最小但合理的工程闭环:知道项目约定、能读取和检查 diff、能提出 patch、并且不能绕过安全边界。
Day 14 新增:Harness Overview
文件:apps/mini-harness/src/overview.ts
Day 14 增加的不是新的 agent 能力,而是一个与实现保持同步的运行时概览:
npm run dev -- --overview
它从默认 tool registry 读取实际注册的工具:
Registered tools (7):
echo, list_files, read_file, search_text, git_status, git_diff, apply_patch
同时输出当前生命周期:
context -> model -> tool call -> approval -> sandbox -> tool result -> transcript
这个命令很小,但它避免了文档和代码脱节,也给 demo、教学和后续检查提供了一个稳定入口。
完整 CLI Demo
1. 查看当前能力边界
npm run dev -- --overview
2. 加载项目规则并检查 context
npm run dev -- --workspace demos/day-13/workspace --context-report "load project rules"
输出应包含:
Project rules rules ... source: AGENTS.md
3. 让 agent 读取项目结构
npm run dev -- --context-report "inspect repository structure"
mock provider 会依次调用 list_files 和 read_file。
4. 检查 Git 工作区
npm run dev -- --context-report "inspect git diff"
mock provider 会调用 git_status 和 git_diff,返回原始 unified diff 与解析后的文件统计。
5. 验证 patch,不写入文件
npm run dev -- "apply a small patch to a fixture file"
这个 demo 只运行 dryRun: true,可重复执行。
6. 验证审批门禁
npm run dev -- "show approval sandbox for a write"
模型会请求真实 patch,但默认策略会拒绝:
Tool failed: apply_patch
The harness denied the write because this run did not include explicit approval.
7. 保存一次可回放运行
npm run dev -- --transcript logs/runs/day-14.jsonl "inspect git diff"
transcript 是追加式 JSONL;敏感字段被 redaction,过长字符串会被截断。
当前能力边界
这个项目已经实现:
CLI entrypoint
+ structured context
+ project rules
+ local file tools
+ Git inspection
+ unified patch validation/application
+ bounded agent loop
+ write approval
+ workspace sandbox
+ transcript logging
+ context token reporting
但它仍然是教学 harness,不应该被误认为生产级 coding agent。
还没有实现的部分
| 能力 | 当前状态 | 为什么未在 Phase 1 实现 |
|---|---|---|
| Shell command runner | 未实现 | 需要命令 allowlist、环境隔离、输出流与更严格审批 |
| Native function calling | 未实现 | 当前用文本 TOOL_CALL 协议保持协议最小化 |
| Skills | 未实现 | 需要发现、版本、权限与按需加载设计 |
| MCP | 未实现 | 需要 transport、server 生命周期与授权模型 |
| IDE integration | 未实现 | 需要编辑器状态、诊断、diff UI 与交互确认 |
| Subagents | 未实现 | 需要任务分解、隔离 context、预算与结果合并 |
| Evaluation suite | 未实现 | 需要任务集、断言、回归与成本质量指标 |
关键设计原则
1. 先做边界,再做能力
读取文件、查看 Git diff、应用 patch 和运行命令的风险不同。越早把 tool registry、approval、sandbox 和 transcript 建立起来,后续能力越不容易变成无法审计的脚本集合。
2. 所有副作用都经过 harness
模型永远不直接写磁盘。模型只返回 tool call;实际执行必须经过:
schema validation
-> approval decision
-> workspace path guard
-> tool handler
-> structured tool result
3. Context 是预算,不是垃圾桶
更多上下文不一定更好。项目 snippets、Git diff、rules 和 transcript 都会消耗 token,Context Explorer 的价值就在于使这种成本可见。
4. 错误需要对模型可见
patch conflict、approval denial、timeout 和未知工具都应成为 tool result 或明确错误,而不是隐藏的控制流。下一轮模型才能理解限制、尝试恢复或给用户说明。
5. Demo 必须可重复
默认 patch demo 用 dry-run,默认写入被拒绝,fixture workspace 与主仓库规则隔离。可重复性比“看起来像做了修改”更重要。
Phase 2 建议
下一阶段不建议同时接入所有能力。更合理的顺序是:
- Native function calling 与 command policy:先替换文本 tool-call 协议,并把命令执行纳入 approval/sandbox。
- Evaluation 与 replay:基于 JSONL transcript 建立固定任务集、结果断言、耗时和 token 指标。
- Skills 与 MCP:引入外部能力前,先定义发现、最小权限和生命周期。
- IDE integration:把上下文报告、patch preview、审批确认和 diff 放进用户可见界面。
- Subagents:最后再解决任务拆分、共享规则、并行工具预算与结果收敛。
这个顺序的核心是:先稳住执行协议和评估,再扩大能力表面。
结论
Mini Coding Agent Harness 的第一阶段到这里完成。
它没有试图复刻任何单一产品,而是把现代 coding agent 共同拥有的 harness 骨架拆成可运行的最小模块:
context + tools + loop + patch + safety + rules + observability
理解这条链路后,无论后续接入哪个模型、IDE、MCP server 或 skill,都能清楚判断新能力应该放在哪一层、受什么边界约束、如何被测试和观测。
Comments