芥末
发布于 2026-03-23 / 0 阅读
0
0

从上下文工程到 Harness Engineering:让 AI Agent 稳定完成长任务

AI Coding 的瓶颈不在“写代码”

AI Coding 已经能把“写代码”这件事提速很多,但不少研发仍然会觉得更累。原因并不矛盾:代码生成变快以后,后面的验证、测试、排障、沟通和评审没有同步提速,人反而会被更多变更淹没。

软件交付不是只有编码。一个需求从想法到上线,通常会经历这些环节:

flowchart LR
    A[需求理解] --> B[编码实现]
    B --> C[本地验证]
    C --> D[自动化测试]
    D --> E[Code Review]
    E --> F[部署发布]
    F --> G[灰度观察]
    G --> H[线上排障与反馈]

如果 AI 只加速了 B[编码实现],它带来的结果可能是:

  • 代码改动更多;
  • Review 压力更大;
  • 测试范围更难判断;
  • 出错后排障成本更高;
  • 需求方与研发之间的确认次数没有减少。

一个常见的时间分布可以这样理解:

环节典型占比AI Coding 当前常覆盖程度瓶颈
编码30%生成速度已明显变快
验证、QA、测试40%中低需要真实反馈与稳定工具
部署、发布、灰度20%涉及环境、权限、风险控制
排障、沟通、Code Review10%中低需要结构化证据和上下文

这就是局部最优陷阱:AI 在编辑器里飞快地产生代码,但交付链路的总耗时没有按比例下降。真正需要改造的不是“让模型再多写一点代码”,而是让 AI Agent(智能体)能跨过编码阶段,进入测试、验证、排障和交付报告这些环节。

Harness Engineering 要解决的正是这个问题。

Harness Engineering 是什么

Harness 原意是“马具”或“控制装置”。放到 AI Agent 语境里,它指的是一套让模型可控工作的工程系统:给模型稳定的上下文、可调用的工具、清晰的反馈和明确的执行边界。

过去工程师主要写代码;在 AI Agent 参与开发后,工程师还要设计“让 Agent 工作的环境”。

flowchart TB
    U[工程师] --> A[定义目标与边界]
    U --> B[设计上下文]
    U --> C[提供工具]
    U --> D[建立反馈闭环]

    A --> Agent[AI Agent]
    B --> Agent
    C --> Agent
    D --> Agent

    Agent --> Code[修改代码]
    Agent --> Test[运行测试]
    Agent --> Debug[分析错误]
    Agent --> Report[产出交付报告]

Harness Engineering 的核心不是换一个更强的编辑器,也不是堆更多 Prompt,而是把研发环境改造成 Agent 能理解、能操作、能自我纠错的系统。

可以把它拆成四件事:

方向要解决的问题典型手段
上下文工程模型应该知道什么、不该被什么干扰Prompt 布局、AGENTS.md、Compaction、Snapshot
工具工程模型怎样影响真实环境MCP、CLI、API、LSP、测试工具
反馈工程模型怎样知道自己做错了结构化错误、测试报告、行号、日志摘要
执行治理模型什么时候自主,什么时候问人Spec、Plan、AskUser、Rollback

一句话概括:Harness Engineering 是为 Agent 打造“工作室”,而不是只给它一个“大脑”。

大模型的物理限制:慢、笨、幻觉从哪里来

很多 AI Coding 产品表现出“慢、笨、会编”的问题,背后并不全是产品缺陷,而是大语言模型(LLM,Large Language Model)的推理机制决定了它会遇到这些约束。

自回归:为什么模型一个 Token 一个 Token 输出

主流大语言模型基于 Transformer 架构,推理时按自回归方式生成内容。它不是一次性写完整段答案,而是不断预测下一个 Token。

可以用这个公式表示:

P(x1, x2, ..., xT) = ∏ P(xt | x1, x2, ..., xt-1)

也就是说,第 t 个 Token 的生成依赖前面所有 Token。

flowchart LR
    A[已有上下文] --> B[预测下一个 Token]
    B --> C[把新 Token 追加到上下文]
    C --> D[继续预测下一个 Token]
    D --> C

这解释了两个现象:

  1. 输出天然是流式的,因为模型必须一步一步往后生成。
  2. 上下文越长,模型在生成前要处理的历史信息越多。

AI Coding 往往需要把项目说明、文件内容、工具定义、对话历史和错误日志都塞给模型,Prefill 阶段的开销会很明显。

Prefill 与 Decode:首 Token 慢在哪里

一次推理可以粗略分成两个阶段:

阶段做什么主要成本
Prefill处理输入 Prompt,计算已有上下文的中间状态和输入长度强相关
Decode逐个生成新 Token和输出长度强相关

如果 Prompt 很长,模型还没开始输出前就要做大量计算,所以 TTFT(Time To First Token,首 Token 时间)会变长。Coding Agent 把几万 Token 的上下文丢进去时,慢通常不是网络问题,而是 Prefill 本身就很贵。

KV Cache:为什么追加便宜,中间修改昂贵

Transformer 的注意力机制可以简化成三类向量:

  • Q(Query):当前要问什么;
  • K(Key):历史信息的索引或标签;
  • V(Value):历史信息的内容。

标准 Attention 形式是:

Attention(Q, K, V) = softmax(QK^T / √d) V

如果每生成一个 Token 都重新计算所有历史 Token 的 K/V,成本会非常高。KV Cache(Key-Value Cache,键值缓存)会把已经计算过的 K/V 存下来,后续生成时直接复用。

flowchart LR
    A[稳定前缀 Prompt] --> B[计算 KV]
    B --> C[(KV Cache)]
    C --> D[追加新 Token]
    D --> E[只计算新增部分]

KV Cache 带来一条非常重要的工程规律:

操作对缓存的影响成本
在末尾追加内容旧 KV 可复用
修改中间内容修改点后的 KV 失效
频繁重排 Prompt前缀不稳定,缓存难命中

所以,上下文工程的第一原则是:保护前缀稳定性。

稳定前缀越长,缓存复用越多;频繁把重要内容插在 Prompt 中间,或者每轮都重排大段上下文,会让缓存收益大幅下降。

Prompt Cache:上下文治理直接影响成本

Prompt Cache(提示词缓存)可以把重复 Prompt 的 KV 缓存下来。命中缓存后,模型不用重新处理完整前缀,速度和成本都会改善。

以常见商业模型的定价方式来看,Cache Read 的价格可能只有普通输入的一小部分。即使具体数字随模型变化,基本规律不会变:

输入类型说明成本特征
Base Input全量处理输入成本最高
Cache Write写入可复用前缀首次有额外成本
Cache Read读取已缓存前缀成本显著降低

这也是为什么 Agent 产品会强调 Prompt 布局、会话压缩、规则文件和工具定义的稳定性。它们表面上是产品体验设计,本质上是在顺应模型推理的计算规律。

ReAct:Agent 为什么会形成循环

AI Agent 不是简单的一问一答。更常见的执行模式来自 ReAct:Reasoning and Acting,即“推理 + 行动”。

一个 Agent 循环通常长这样:

flowchart TD
    T[Thought<br/>分析当前状态] --> A[Action<br/>调用工具]
    A --> O[Observation<br/>获得结果]
    O --> T

在 Coding 场景里,这个循环可能是:

Thought: 需要查看报错文件
Action: read_file("src/index.ts")
Observation: 返回文件内容

Thought: 发现类型定义不匹配,需要修改
Action: write_file("src/index.ts", ...)
Observation: 写入成功

Thought: 需要运行测试确认
Action: bash("pnpm test")
Observation: 返回测试失败日志

ReAct 让模型能接触真实世界:读文件、写文件、跑命令、查数据库、调 API。问题也随之出现:每一轮 Thought、Action、Observation 都会追加到上下文里。任务跑得越久,上下文越膨胀,模型越容易被中间过程、试错日志和冗余输出干扰。

flowchart LR
    R1[第 1 轮<br/>少量上下文] --> R5[第 5 轮<br/>文件与日志增加]
    R5 --> R20[第 20 轮<br/>大量工具返回]
    R20 --> R40[第 40 轮<br/>注意力被稀释]

长任务失败经常不是模型完全不会,而是上下文里噪音太多,关键事实被埋住了。

Prompt 的五层结构

当用户在 AI Coding 产品里输入一句需求时,真正送进模型的不是这一句话,而是一整套组合后的 Prompt。

典型结构可以分成五层:

flowchart TB
    A[System<br/>系统规则与 Agent 身份]
    B[AGENTS.md<br/>项目约定]
    C[项目快照<br/>目录、选区、相关文件]
    D[会话历史<br/>对话、工具调用、摘要]
    E[工具定义<br/>名称、描述、JSON Schema]
    F[用户当前请求]

    A --> B --> C --> D --> E --> F --> M[模型推理]

每一层的作用不同:

层级内容作用稳定性建议
System模型服务商规则、Agent 身份、行为边界定义最高优先级约束尽量稳定
AGENTS.md技术栈、代码风格、安全要求、团队流程给项目建立“宪法”高稳定
项目快照当前目录、选中文本、相关文件片段提供任务现场按需变化
会话历史对话、工具调用、错误、总结保留推理过程定期压缩
工具定义MCP 工具、CLI、API Schema说明可执行动作保持结构稳定
用户请求当前要做的任务驱动本轮执行每轮变化

上下文不是越多越好,而是要把重要信息放在正确位置。稳定、长期有效、所有任务都要遵守的内容应该靠前;临时信息和工具返回应该可压缩、可丢弃、可恢复。

AGENTS.md:项目给 Agent 的“宪法”

AGENTS.md 适合放那些长期有效、每次任务都应该遵守的规则。它不适合放一次性需求,也不适合放频繁变化的临时计划。

一个可用的 AGENTS.md 可以这样写:

# AGENTS.md

## Tech Stack

- Package manager: pnpm
- Framework: React + TypeScript
- Test runner: Vitest
- E2E: Playwright
- Lint: ESLint + TypeScript strict mode

## Code Style

- Prefer small pure functions.
- Do not introduce new dependencies without asking.
- Keep public API backward compatible unless the spec says otherwise.
- Use existing error handling utilities in `src/shared/errors.ts`.

## Safety Boundaries

- Never modify generated files under `src/generated`.
- Never run destructive database commands.
- Before changing build scripts, explain the reason and ask for approval.

## Verification

After code changes, run:

```bash
pnpm lint
pnpm test

If UI behavior changes, add or update an E2E test.


这类文件有三个价值:

1. Agent 每次进入项目都能读到统一规则;
2. 团队可以 Review 它,避免 Prompt 只存在个人工具里;
3. 规则文件是稳定前缀的一部分,有利于 Prompt Cache 命中。

## Compaction:把长对话压缩成结构化快照

ReAct 长任务会产生大量中间信息。把所有历史都留在上下文里,会让模型越来越慢,也越来越容易被噪音干扰。更好的方式是主动 Compaction:在关键节点把当前状态压缩成结构化文件,然后清理对话历史。

```mermaid
flowchart LR
    A[长对话与工具日志] --> B[阶段性总结]
    B --> C[写入 Snapshot 文件]
    C --> D[清理会话历史]
    D --> E[后续任务 read_file 恢复状态]

例如迁移任务可以沉淀成:

# docs/state/user-module-migration.md

## Goal

Move user profile logic from `legacy/user` to `modules/user`.

## Current Status

- Completed:
  - Created `modules/user/profile-service.ts`
  - Added tests for `getUserProfile`
  - Replaced imports in `src/pages/profile.tsx`
- Not completed:
  - `src/pages/settings.tsx` still imports legacy API
  - E2E coverage is missing for profile editing

## Key Decisions

- Keep the old API wrapper until all pages are migrated.
- Do not change response shape in this phase.

## Verification Commands

```bash
pnpm test user
pnpm lint

Known Errors

  • settings.test.ts fails because mock data still uses legacyUser.

这样做相当于把记忆外包给文件系统。Agent 不需要携带完整聊天记录,只要读取这个快照,就能恢复关键状态。

适合写入 Snapshot 的内容包括:

| 内容 | 是否适合 | 原因 |
|---|---|---|
| 阶段目标 | 适合 | 后续执行需要对齐方向 |
| 已完成变更 | 适合 | 防止重复工作 |
| 关键决策 | 适合 | 防止 Agent 重新争论 |
| 验证命令 | 适合 | 形成闭环 |
| 全量终端日志 | 不适合 | 噪音大,占用上下文 |
| 临时猜测 | 不适合 | 容易误导后续任务 |

## Agent 可读性:基础设施要从“给人看”转向“给 Agent 读”

传统研发基础设施面向人设计,强调 GUI、图表、日志和交互体验。但 Agent 不擅长像人一样从复杂界面里提炼语义。对 Agent 友好的系统应该提供结构化、稳定、低噪音的接口。

| 传统设计 | Agent 友好设计 | 原因 |
|---|---|---|
| 酷炫 GUI | JSON、Markdown、API | Agent 能直接解析 |
| 海量日志 | 分层摘要 + 关键错误 | 降低上下文噪音 |
| 鼠标点击 | CLI 或自然语言动作 | 可复现、可自动化 |
| 截图排查 | 结构化诊断报告 | 减少视觉推理成本 |
| 模糊错误 | 文件、行号、类型、建议 | 支持自动修复 |

工具不是给人用的 UI,而是给 Agent 用的 API。一个好工具要满足三个标准:

1. **快**:秒级返回,避免长时间等待让任务链路变脆。
2. **结构化**:输出 JSON 或 Markdown,不要让 Agent 从杂乱文本里猜。
3. **有痛觉反馈**:错误要精确到文件、行号、类型和修复方向。

一个更适合 Agent 的错误输出示例:

```json
{
  "status": "failed",
  "tool": "typecheck",
  "errors": [
    {
      "file": "src/user/profile.ts",
      "line": 42,
      "column": 18,
      "code": "TS2322",
      "message": "Type 'string | undefined' is not assignable to type 'string'.",
      "suggestion": "Add a fallback value or narrow the type before assignment."
    }
  ]
}

比起一整屏终端输出,这种结构化结果更容易进入 ReAct 闭环。

MCP、Skill、Sub Agent:三种能力分工

MCP(Model Context Protocol,模型上下文协议)、Skill、Sub Agent 经常一起出现,但它们解决的问题不同。

机制定位适合放什么上下文成本典型场景
MCP原子工具GitHub、Slack、数据库、Kubernetes、内部 API工具定义常驻让 Agent 能操作外部系统
Skill可执行 SOP(Standard Operating Procedure,标准作业流程)CI Debug、发布检查、迁移流程按需加载固化成熟流程
Sub Agent独立执行单元长任务子问题、并行探索上下文隔离多模块排查、代码库探索

MCP:让 Agent 获得动作能力

MCP 的核心价值是把外部工具暴露给模型。它提供的最好是小而清晰的原子动作,例如:

{
  "name": "get_pull_request",
  "description": "Get pull request metadata and changed files.",
  "input_schema": {
    "type": "object",
    "properties": {
      "repo": { "type": "string" },
      "pr_number": { "type": "number" }
    },
    "required": ["repo", "pr_number"]
  }
}

工具描述要短、准、无歧义。描述越像“产品说明书”,越容易浪费 Token;描述越像“函数签名 + 使用边界”,越适合 Agent。

Skill:把成熟打法固化下来

Skill 更像一个可执行知识包。它可以包含说明、脚本、示例和检查清单。

skills/
  ci-debug/
    skill.md
    run.sh
    examples/
      typescript-error.md
      flaky-test.md

skill.md 可以写成:

# CI Debug Skill

Use this skill when a CI job fails.

## Steps

1. Fetch the latest CI logs.
2. Classify the failure:
   - typecheck
   - unit test
   - e2e test
   - build
3. Extract the first actionable error.
4. Propose a minimal fix.
5. Run the related verification command.

## Output

Return a Markdown report with:

- failure type
- root cause
- changed files
- verification result

Skill 的意义是减少模型每次从零探索,让团队经验变成 Agent 可执行的流程。

Sub Agent:用上下文隔离解决长任务熵增

单个 Agent 跑长任务时,上下文会越来越乱。Sub Agent 的做法是把任务拆出去,每个子 Agent 在独立上下文里完成一部分,只把摘要返回给主 Agent。

flowchart TB
    O[Orchestrator<br/>主 Agent] --> A[Worker A<br/>探索模块 A]
    O --> B[Worker B<br/>探索模块 B]
    O --> C[Worker C<br/>探索模块 C]

    A --> SA[结构化摘要 A]
    B --> SB[结构化摘要 B]
    C --> SC[结构化摘要 C]

    SA --> O
    SB --> O
    SC --> O
    O --> R[汇总计划与执行]

关键点是“只交换摘要”,不要把所有子任务日志都塞回主会话。结构化通信比堆上下文更重要。

LSP:Agent 编码质量的最低保障线

LSP(Language Server Protocol,语言服务器协议)是 Agent 编码工具链里非常关键的一环。人类开发者在编辑器里能看到红线、类型错误、跳转定义和引用关系;如果 Agent 只能读文件文本,它就感知不到这些 IDE 级反馈。

接入 LSP 后,Agent 可以获得:

  • 类型错误;
  • 语法诊断;
  • 定义跳转;
  • 引用查找;
  • 自动补全;
  • 重命名影响范围。

这能形成自动化闭环:

flowchart LR
    A[Agent 编辑代码] --> B[LSP 诊断]
    B --> C{是否有错误}
    C -- 有 --> D[定位文件和行号]
    D --> E[最小修改]
    E --> B
    C -- 无 --> F[运行测试]
    F --> G{测试通过}
    G -- 否 --> H[读取失败报告]
    H --> E
    G -- 是 --> I[生成交付摘要]

没有 LSP 和测试闭环时,Agent 容易在错误上继续叠加错误,直到上下文爆炸。LSP 的价值不是“让模型更聪明”,而是让模型能及时知道自己错在哪里。

Spec 与 Plan:先约束目标,再执行任务

Agent 最容易浪费轮次的地方,是在需求不明确时自行猜测。长任务应该先写 Spec,再写 Plan。

Spec 回答“做成什么样”,Plan 回答“怎么做”。

文档解决的问题应包含内容
SpecWhat输入输出、功能边界、副作用、成功标准
PlanHow步骤拆解、检查点、回退策略、验证命令

一个简单 Spec 示例:

# Spec: Unified Slash Menu

## Goal

Merge command list and skill list into one `/` triggered menu.

## Behavior

- Typing `/` opens a unified menu.
- Menu items include built-in commands, custom commands, and skills.
- Typing `/skillName` selects the corresponding skill.
- Typing `$` must not open any dropdown.

## Success Criteria

- Unit tests cover filtering and selection.
- TUI interaction test covers:
  - open menu
  - search skill
  - select skill
  - verify input value

Plan 可以这样写:

# Plan

## Step 1: Locate menu data source

Find current command and skill providers.

## Step 2: Introduce unified item model

Create:

```ts
type SlashMenuItem =
  | { type: "builtin"; name: string; description: string }
  | { type: "command"; name: string; description: string }
  | { type: "skill"; name: string; description: string };

Step 3: Update trigger logic

  • / opens unified menu.
  • $ no longer opens dropdown.

Step 4: Add tests

  • filtering
  • selection
  • trigger behavior

Rollback

If TUI behavior regresses, revert menu trigger changes only.


Spec 和 Plan 的价值是减少无目的试错。需求存在多条路径时,Agent 应该在第一轮询问,而不是猜错后跑二十轮。

## 人机协作:什么时候自主,什么时候求助

Harness Engineering 不是让 Agent 永远自主,也不是让它频繁打断人。关键是给它明确边界。

| 情况 | Agent 应该怎么做 |
|---|---|
| Spec 明确、工具齐全、可自动验证 | 自主执行 |
| 失败可回退、影响范围局部 | 自主尝试修复 |
| 需求有多种解释 | 先问人 |
| 缺少环境信息 | 先问人或请求权限 |
| 涉及不可逆操作 | 必须确认 |
| 需要改变公共 API 或架构方向 | 必须确认 |

一个好的 AskUser 问题应该具体到决策点:

```text
当前有两种实现路径:

A. 在现有 SlashMenu 里合并 skill 数据源,改动小,但菜单模型会继续耦合 UI。
B. 新建 SlashMenuItem 模型和 provider,改动稍大,但后续可扩展更多 item 类型。

请选择 A 或 B。若无偏好,将采用 B,因为 Spec 提到后续可能扩展更多触发项。

坏问题通常是泛泛而谈:

你希望我怎么做?

Agent 要问的是“关键分岔点”,不是把任务重新丢回给人。

从 Review 代码转向 Review 交付产物

AI 生成代码后,如果人仍然逐行 Review 所有改动,注意力并没有真正释放。更合理的方式是让 Agent 产出可审计的交付包,人主要 Review 结果、证据和风险。

交付包可以包含:

内容目的
变更范围知道改了哪些模块
行为变化知道用户能感知到什么
测试覆盖知道关键路径是否验证
失败与回退知道风险是否可控
截图或录屏验证 UI/TUI 行为
性能与兼容性说明防止非功能性退化

一个 Agent 交付报告模板:

# Delivery Report

## Requirement

Unify skill list and command list into one `/` dropdown menu.

## Changed Files

- `src/menu/slash-menu.ts`
- `src/menu/providers/skill-provider.ts`
- `src/menu/providers/command-provider.ts`
- `tests/slash-menu.test.ts`

## Behavior Before

- `/` opens command menu.
- Skills use a separate trigger.
- `$` opens a dropdown.

## Behavior After

- `/` opens unified menu.
- Built-in commands, custom commands, and skills are mixed.
- Selecting a skill inserts `/skillName`.
- `$` does not open a dropdown.

## Verification

```bash
pnpm test slash-menu
pnpm lint
pnpm cli-test slash-menu

Result

  • Unit tests: passed
  • TUI test: passed
  • Lint: passed

Risks

  • Menu item ranking may need product review.

人不再只看“代码长什么样”,而是看“需求有没有被正确交付”。当然,架构质量仍然要治理,不能只看测试绿不绿。这个问题可以通过架构规则、公共 API 约束、变更预算和质量指标来补齐。

## 端到端闭环:让 Agent 自己测试自己

如果 Agent 只会改代码,不会验证结果,它仍然需要人当“AI 调试器”。端到端 Harness 应该让 Agent 完成从需求到验收报告的闭环。

```mermaid
sequenceDiagram
    participant H as 人
    participant A as Agent
    participant C as Codebase
    participant S as Sandbox
    participant T as Test Tools
    participant R as Report

    H->>A: 输入需求与验收标准
    A->>C: 定位并修改代码
    A->>S: 启动隔离环境
    A->>T: 运行 lint/unit/e2e/cli-test
    T-->>A: 返回结构化结果
    A->>C: 根据失败信息修复
    A->>T: 再次验证
    A->>R: 生成交付报告
    R-->>H: 人审阅结果与风险

在 CLI(Command Line Interface,命令行界面)或 TUI(Terminal User Interface,终端用户界面)产品里,可以用 tmux 创建沙盒会话,让 Agent 像用户一样操作终端界面,再把关键步骤、截图、输出和测试结果沉淀为报告。

示例流程:

# 1. 启动沙盒
tmux new-session -d -s agent-test "pnpm dev"

# 2. 执行 CLI 测试技能
pnpm cli-test slash-menu

# 3. 导出测试报告
pnpm cli-test report --format markdown > reports/slash-menu.md

重点不是某个命令本身,而是让验证成为 Agent 的默认动作。编码结束不代表任务结束,通过测试、生成证据、说明风险才算交付完成。

AI Native 工具:不要强迫 Agent 模拟人类

Web 自动化里常见做法是让 Agent 获取 DOM(Document Object Model,文档对象模型)、读取截图、计算坐标、模拟点击。这个路径能跑,但 Token 消耗大,稳定性也容易受页面结构影响。

传统路径大致是:

flowchart LR
    A[获取 DOM 树] --> B[解析页面结构]
    B --> C[结合截图判断元素]
    C --> D[计算目标位置]
    D --> E[模拟点击或输入]
    E --> F[观察结果]

AI Native 的思路是把复杂操作封装进工具层,让 Agent 用自然语言表达意图:

点击登录按钮
验证购物车里有 3 件商品
在搜索框输入 headphones

工具负责定位、执行、重试和验证,Agent 不需要把 DOM 和截图全部塞进上下文。

以 Midscene.js 这类工具为例,接口可以更接近 Agent 的自然指令:

await ai.action("点击登录按钮");
await ai.action("在用户名输入框输入 alice@example.com");
await ai.action("在密码输入框输入 password123");
await ai.action("点击提交");

await ai.assert("页面显示欢迎回来");

这种设计的关键收益是:

方式Agent 需要处理什么问题
模拟人类操作DOM、截图、坐标、点击路径上下文重、脆弱
AI Native 工具自然语言目标Token 少,工具层自愈

好的 Harness 会把环境复杂度下沉到工具层,而不是把所有细节都暴露给模型。

维护性不能只靠测试通过

AI 一次生成几十个文件变更时,只看单测是否通过是不够的。测试能证明某些行为没坏,但不能完全证明架构方向正确、性能没有退化、复杂度没有失控。

需要给 Agent 更多工程约束:

风险Harness 约束
文件过大设置文件长度阈值,超过必须拆分或解释
架构漂移在 AGENTS.md 写清模块边界
公共 API 破坏Spec 中列出兼容性要求
性能退化增加 benchmark 或性能检查
内存增长增加资源监控脚本
测试覆盖不足要求交付报告列出覆盖与未覆盖路径

例如可以把架构约束写进规则文件:

## Architecture Boundaries

- `src/core` must not import from `src/ui`.
- `src/shared` must not depend on product-specific modules.
- New public APIs require a compatibility note in the delivery report.
- Files over 500 lines require an explanation and a split plan.

也可以把质量门禁做成脚本:

pnpm lint
pnpm test
pnpm test:e2e
pnpm check:cycles
pnpm check:bundle-size
pnpm benchmark:critical-path

Agent 不是不需要架构治理,而是架构治理要变成它能读取、能执行、能反馈的规则。

一套可落地的 Harness Engineering 清单

搭建 AI Coding Harness 可以从小范围开始,不需要一次性改造全部研发体系。

1. 固化项目规则

  • 添加 AGENTS.md
  • 写清技术栈、代码风格、安全边界;
  • 写清验证命令;
  • 写清架构边界。

2. 治理上下文

  • 长任务使用 Snapshot 文件;
  • 阶段性清理会话历史;
  • 不把全量日志长期留在上下文;
  • 保持稳定前缀,减少 Prompt 重排。

3. 工具结构化

  • CLI 输出 JSON 或 Markdown;
  • 错误包含文件、行号、类型、建议;
  • 避免让 Agent 解析截图和杂乱日志;
  • MCP 工具保持小而清晰。

4. 建立验证闭环

  • 接入 LSP;
  • 默认运行 lint、typecheck、unit test;
  • UI/TUI 变更运行端到端测试;
  • 失败后让 Agent 根据结构化结果修复。

5. 用 Spec 和 Plan 控制执行

  • 不明确的需求先写 Spec;
  • 长任务先产出 Plan;
  • 涉及不可逆操作必须 AskUser;
  • 每个阶段都有 Checkpoint 和 Rollback。

6. 引入 Sub Agent

  • 超长任务拆给多个子 Agent;
  • 每个子 Agent 独立上下文;
  • 只返回结构化摘要;
  • 主 Agent 汇总决策,不合并噪音。

核心结论

AI Coding 的下一阶段不是“更快补全代码”,而是“稳定完成长任务”。要做到这一点,工程师需要从单纯写代码,转向设计 Agent 的工作环境。

几个原则最重要:

  1. 大模型按自回归生成,长上下文会带来真实计算成本。
  2. KV Cache 决定了追加便宜、中间修改昂贵,稳定前缀要尽量保护。
  3. Prompt Cache 让上下文布局直接影响速度和成本。
  4. ReAct 让 Agent 能行动,但也会带来上下文膨胀。
  5. AGENTS.md、Snapshot、Compaction 是上下文治理的基础。
  6. 工具要快、准、结构化,还要能给出明确错误反馈。
  7. MCP 提供原子动作,Skill 固化流程,Sub Agent 隔离长任务。
  8. LSP、测试和交付报告让 Agent 从“写完代码”走向“完成交付”。

Harness Engineering 的目标不是让人退出研发,而是把人的注意力从低层次调试中释放出来,转向目标定义、边界设计、架构判断和结果审计。


评论