news 2026/9/27 14:51:26

Harness Engineering 从零理解到动手实践:用 AGENTS.md 与状态机搭一套可验证的 AI Agent 反馈回路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Harness Engineering 从零理解到动手实践:用 AGENTS.md 与状态机搭一套可验证的 AI Agent 反馈回路

1. 为什么你的 AI Agent 总是“嘴上说做完了”

如果你正在用 Claude Code、Cursor 或者自己写的 Agent 跑多步骤任务,大概率遇到过这几个场景:Agent 说“已完成登录模块修复”,你打开文件一看,只改了个注释;长任务跑到一半,它开始重复调用同一个失败命令;你让它自己检查代码,它永远回复“看起来没问题”。

这些问题的根因往往不在模型本身,而在于缺少一套外部的运行控制系统。Harness Engineering 就是解决这个问题的工程方法——它不优化模型参数,而是给 Agent 搭建“缰绳 + 马鞍 + 跑道护栏 + 反馈镜子”。本文会从零带你搭一套最小可用的 Agent 工程:用 AGENTS.md 定义行为边界,用状态机锁定任务流转,用反馈回路做结果校验,最后跑一次端到端验证。

适合谁看:正在做 AI coding 工具链的后端工程师、想让 Agent 稳定跑长任务的团队、以及被“提前宣布胜利”折磨过的开发者。读完你能拿到一份可复制的 AGENTS.md 骨架、一段状态机配置代码,以及一个能立刻跑通的验证动作。

2. 前置准备:TaoToken 统一 Key 与运行环境

在动手之前,先把模型调用通道准备好。我试过在多个项目里分别维护不同厂商的 Key,切换模型时改配置改到崩溃。TaoToken 在这里的作用是提供统一的 Key/API 通道,让你在 Agent 运行层只配置一次,后续换模型不用动业务代码。

官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=

API 地址:https://taotoken.net/api

你需要准备的东西不多:

  • 一个可用的 API Key(在控制台创建,见下方 deep link)
  • Node.js 18+ 或 Python 3.10+ 运行环境
  • 一个测试用的代码仓库(本文用 Next.js + TypeScript 项目举例,其他技术栈同理)

创建 Key 的路径:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite

如果你用的是 Claude Code 这类编码 Agent,接入文档在这里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

注意:Key 只放在环境变量里,不要写进 AGENTS.md 或任何会被 Agent 读取的文件。后面权限边界那一节会专门讲怎么用代码拦住 Agent 碰密钥文件。

3. 可复制配置:AGENTS.md 骨架 + 状态机 + 反馈回路

3.1 AGENTS.md 骨架:让 Agent 读得懂你的项目

AGENTS.md 的本质是给 Agent 看的项目说明书。README 给人看,AGENTS.md 给 AI 看。关键原则是渐进披露——不要把全部文档塞进去,只保留最关键的三类信息:WHAT(项目是什么)、HOW(怎么跑)、RULES(什么不能碰)。

在项目根目录创建AGENTS.md:

# AGENTS.md ## 项目概览 Next.js 14 + TypeScript 全栈项目,使用 App Router。 ## 技术栈 - 框架:Next.js 14(App Router,禁止 Pages Router) - 语言:TypeScript 严格模式,禁止 any - 样式:Tailwind CSS(禁止 CSS Modules) - 数据库:Prisma + PostgreSQL - 测试:Vitest + Testing Library ## 开发命令 - 安装依赖:`pnpm install` - 开发服务器:`pnpm dev` - 运行测试:`pnpm test` - 类型检查:`pnpm typecheck` - 代码检查:`pnpm lint` ## 架构约束 - API 路由放在 `app/api/` 下 - 业务逻辑放在 `lib/` 下 - 环境变量通过 `env.ts` 统一管理,禁止硬编码 - 禁止修改 `.env`、`secrets/`、`config/production/`、`.git/` ## 验证方式 改完代码后必须依次执行: 1. `pnpm typecheck` 2. `pnpm lint` 3. `pnpm test` 三项全部通过才算完成,禁止跳过。

如果是 monorepo,可以在子包目录下再放一份packages/web/AGENTS.md,Agent 在不同目录下读取到的规则更精准。

3.2 状态机配置:把任务流转锁死在轨道里

软约束(写在 Prompt 里的“请先做计划”)不可靠,模型会忘、会跳过。硬约束要写进执行层。下面是一个四阶段状态机,用 Python 实现,你可以直接复制到自己的 Agent 运行层:

from enum import Enum class AgentPhase(Enum): RESEARCH = "research" PLAN = "plan" EXECUTE = "execute" VERIFY = "verify" class PhaseStateMachine: ALLOWED_TRANSITIONS = { AgentPhase.RESEARCH: [AgentPhase.PLAN], AgentPhase.PLAN: [AgentPhase.EXECUTE, AgentPhase.RESEARCH], AgentPhase.EXECUTE: [AgentPhase.VERIFY], AgentPhase.VERIFY: [AgentPhase.EXECUTE, AgentPhase.PLAN], } PHASE_PERMISSIONS = { AgentPhase.RESEARCH: ["read_file", "search_code", "list_files"], AgentPhase.PLAN: ["read_file", "create_plan"], AgentPhase.EXECUTE: ["read_file", "write_file", "run_command"], AgentPhase.VERIFY: ["run_tests", "run_lint", "run_typecheck"], } def __init__(self): self.current = AgentPhase.RESEARCH def can_transition(self, target: AgentPhase) -> bool: return target in self.ALLOWED_TRANSITIONS[self.current] def can_execute(self, action: str) -> bool: return action in self.PHASE_PERMISSIONS[self.current] def transition(self, target: AgentPhase): if not self.can_transition(target): raise PermissionError( f"非法状态迁移:{self.current.value} -> {target.value}" ) self.current = target

这段代码的核心价值:Agent 在 RESEARCH 阶段想直接改代码,会被can_execute("write_file")拦下;想从 RESEARCH 跳到 EXECUTE,会被can_transition拒绝。它必须老老实实先出计划。

3.3 反馈回路:让 Agent 犯错后越来越稳

反馈回路分三层,从低成本到高成本依次叠加。

第一层是自动化验证,改完代码强制跑 typecheck + lint + test:

class FeedbackLoop: def run_verification(self, changed_files: list[str]): results = [] for cmd in ["pnpm typecheck", "pnpm lint", "pnpm test"]: r = self.run_command(cmd) results.append({ "check": cmd, "passed": r.success, "output": r.stdout[-500:], "errors": r.stderr[-500:] if not r.success else None, }) return { "passed": all(r["passed"] for r in results), "details": results, }

第二层是执行与评审分离。让一个 Agent 写代码,另一个独立会话按标准审查,避免“自己写自己夸”:

async def dual_agent_review(task, executor, reviewer): MAX_ROUNDS = 3 for _ in range(MAX_ROUNDS): result = await executor.execute(task) review = await reviewer.review( task_description=task.description, code_diff=result.diff, review_criteria=[ "是否完成所有要求?", "是否有 bug?", "是否遵循 AGENTS.md 规范?", "是否存在安全隐患?", ], ) if review.is_good_enough(): return result task = task.with_feedback(review.comments) return {"status": "escalate_to_human", "result": result, "review": review}

第三层是错误经验持久化。维护一份.harness/lessons-learned.md,每次 Agent 犯错后归因并更新约束:

# .harness/lessons-learned.md ## 2026-03-20: Prisma 迁移必须在测试前执行 - 问题:修改 schema 后没跑 migrate,测试全挂 - 修复:在验证步骤中加入 migrate 检查 - 状态:已纳入硬约束 ## 2026-03-18: 不要在 middleware 中直接 throw - 问题:导致页面白屏 - 修复:补充框架约束说明 - 状态:已写入 AGENTS.md

3.4 权限边界:高风险目录靠代码拦

不要把“别碰生产配置”只写在提示词里。下面这段代码直接拦住 Agent 对敏感路径的访问:

class PermissionBoundary: FORBIDDEN_PATHS = [".env", "secrets/", "config/production/", ".git/"] def check_file_access(self, file_path: str, operation: str): for forbidden in self.FORBIDDEN_PATHS: if file_path.startswith(forbidden): raise PermissionError( f"禁止{operation}文件:{file_path}" )

4. 验证请求:跑一次端到端 Agent 任务

配置写完了,现在跑一次完整流程验证。假设任务是“修复src/login.tsx中的登录 bug”。

4.1 启动会话并加载 AGENTS.md

Agent 启动时,Harness 读取根目录 AGENTS.md,初始化状态机为 RESEARCH 阶段。此时 Agent 只能调用read_file、search_code、list_files。

4.2 观察状态迁移与拦截

Agent 请求读取login.tsx,Harness 检查 RESEARCH 阶段允许read_file,放行。接着 Agent 想直接改代码,Harness 检查write_file不在 RESEARCH 权限列表,拒绝。Agent 转而请求进入 PLAN 阶段,状态机校验RESEARCH -> PLAN合法,允许。

4.3 执行与自动验证

Agent 在 PLAN 阶段生成修复计划,请求进入 EXECUTE。Harness 允许PLAN -> EXECUTE,Agent 修改login.tsx。文件修改事件触发验证守卫,Harness 强制进入 VERIFY 阶段,自动执行:

pnpm typecheck pnpm lint pnpm test

如果三项全部通过,任务标记完成。如果测试失败,错误输出被反馈给 Agent,状态回退到 EXECUTE 重试。

4.4 成功结果长什么样

一次成功的端到端运行,你会看到类似这样的日志:

[Harness] Phase: RESEARCH -> PLAN (allowed) [Harness] Phase: PLAN -> EXECUTE (allowed) [Harness] File write detected: src/login.tsx [Harness] Auto-trigger verification guard [Harness] Phase: EXECUTE -> VERIFY (forced) [Harness] typecheck: PASS [Harness] lint: PASS [Harness] test: PASS (12 passed, 0 failed) [Harness] Task completed with verification

关键点:Agent 不一定知道状态机代码长什么样,但它会真实感受到哪些动作被允许、哪些被拦下、什么才算完成。

5. 本篇常见错排查

5.1 Agent 不读 AGENTS.md

检查文件是否在项目根目录,文件名大小写是否精确匹配。部分工具需要显式配置读取路径,确认你的 Agent 运行层有没有加载逻辑。如果用的是 Claude Code,接入配置参考 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

5.2 状态机迁移报 PermissionError

先打印self.current和target,确认迁移方向在ALLOWED_TRANSITIONS里。常见错误是 VERIFY 失败后想直接回 RESEARCH,但合法路径是VERIFY -> EXECUTE或VERIFY -> PLAN。

5.3 验证命令全部通过但 Agent 仍说没完成

检查 Agent 的输出解析逻辑。有些模型会把pnpm test的输出误判为失败,因为 stderr 里有 warning。建议在 FeedbackLoop 里只以 exit code 为准,不要用文本匹配判断成功。

5.4 长任务跑到后面上下文爆炸

不要让一个会话死扛到底。设置上下文利用率阈值(比如 60%),超过就生成 handoff 文档,开新会话继续:

class ContextManager: MAX_CONTEXT_UTILIZATION = 0.6 async def run_with_reset(self, agent, task): subtasks = self.decompose_task(task) for subtask in subtasks: if agent.context_utilization > self.MAX_CONTEXT_UTILIZATION: handoff = await agent.generate_handoff( prompt="总结:1.已完成 2.当前进度 3.下一步 4.注意事项" ) agent = agent.fresh_session() agent.load_context(handoff) await agent.execute(subtask)

5.5 循环失败检测没生效

确认LoopDetector的record_failure在每次工具调用失败后都被调用。常见遗漏是只在异常捕获里记录,但 Agent 返回success=False时没记录。建议在工具调用统一出口处埋点。

6. 下一步:把反馈回路接进你的编码工作流

最小可用版本跑通后,下一步是把它接进日常编码。如果你主要用 Claude Code 做长期编码任务,可以把状态机和验证守卫配置成 Coding Plan 的一部分,让每次代码修改都自动走一遍验证回路:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite

想先验证模型对话和工具调用是否正常,可以从模型对话入口测试:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite

接入文档和 API Key 管理分别在这里:

  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
  • API Keys:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite

最后留一个我踩过的坑:AGENTS.md 不要一次写太长。我第一版写了 300 多行,结果 Agent 读取后反而忽略了关键约束。后来砍到 60 行以内,只保留技术栈、命令、禁区、验证方式四块,遵守率明显提升。渐进披露比全量灌输有效得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 14:39:38

Claude-code 插件 Hookify 配 TaoToken:settings.json 骨架与 Hooks 验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 14:27:02

AI大模型日报#0626:首款大模型芯片挑战英伟达、面壁智能李大海专访、大模型测试题爆火LeCun点赞——TaoToken统一Key接入实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华