如何构建AIAgentHarness 2.0,让AIAgent比 99% 的开发者更工程化
你的 AI agent 刚刚完成了一个编码任务。
代码可以编译。 测试通过。 PR 看起来不错。
你合并了它。
几个小时后,你注意到一些奇怪的事情。

新的 API 可以正常工作,但这个 agent:
绕过了 repository layer
修改了一个它本不该触碰的文件
跳过了一项重要测试
引入了一个安全问题
忽略了一个既有的架构决策
没有任何东西崩溃。
这个 agent 只是做了它认为正确的事。
而这正是问题所在。
我们一直在尝试让 AI agent 更智能。
更好的模型。 更好的 prompt。 更大的 context window。
但如果模型并不是最大的问题呢?
如果真正的问题是 我们把它放进了什么样的环境中?
这就是 agent harnesses 发挥作用的地方。
什么是 Agent Harness?
把 AI agent 想象成一名能够推理、编写代码、运行命令并使用工具的开发者。
harness 则是围绕这名开发者的工程环境。
┌─────────────────┐
│ AI Agent │
└────────┬────────┘
│
┌──────────────────┼──────────────────┐
↓ ↓ ↓
Context Tools Constraints
↓ ↓ ↓
Memory Execution Verification
↓ ↓ ↓
Rules Feedback Guardrails
└──────────────────┼──────────────────┘
↓
Agent Harness
一个基础 agent 看起来像这样:
Prompt → Model → Code
一个更好的 agent 看起来像这样:
Task
↓
Context
↓
Agent
↓
Tools
↓
Code
↓
Verification
↓
Feedback
↺
这个循环改变了一切。
agent 不只是生成代码。
它会观察、行动、验证并恢复。
让我们来构建一个。
1. 从 Context 开始
假设我们有一个 TypeScript 后端。
开发者提出需求:
Add an endpoint to create a new customer.
如果没有额外 context,agent 就只能猜:
endpoint 应该放在哪里?
应该使用哪个 database layer?
项目使用什么 validation library?
错误响应是什么样的?
测试放在哪里?
项目遵循什么命名约定?
而 agent 非常擅长猜测。
这恰恰是我们不想要的。
所以要给 agent 提供项目特定的指令。
例如:
my-project/
├── AGENTS.md
├── src/
│ ├── controllers/
│ ├── services/
│ ├── repositories/
│ └── models/
├── tests/
└── package.json
我们的 AGENTS.md 可能包含:
# Engineering Rules
- Use TypeScript.
- Controllers must not access the database directly.
- Business logic belongs in services.
- Database access belongs in repositories.
- Every new API endpoint requires tests.
- Use Zod for request validation.
- Run lint, typecheck, and tests before finishing.
- Never modify generated files.
这已经更好了。
但还有一个问题。
这些仍然只是指令。
agent 可以忽略它们。
所以我们需要从 instructions 走向 enforcement。
2. 给 Agent 合适的工具
agent 的有用程度取决于它能够执行哪些动作。
与其给它对所有内容的无限制访问,不如暴露经过设计的工具。
例如:
tools = [
read_file,
search_code,
list_files,
apply_patch,
run_tests,
run_linter,
run_typecheck,
]
现在 agent 可以通过已知能力与 repository 交互。
一个简化的工具定义可能是这样:
def run_tests():
result = subprocess.run(
["npm", "test"],
capture_output=True,
text=True
)
return {
"success": result.returncode == 0,
"stdout": result.stdout,
"stderr": result.stderr
}
以及一个文件搜索工具:
def search_code(query):
result = subprocess.run(
["rg", query, "src"],
capture_output=True,
text=True
)
return result.stdout
现在 agent 可以提出:
Search for existing customer endpoints.
而不是盲目创建一个。
这个区别很重要。
3. Context 应该被检索,而不是倾倒
一个常见错误是把所有东西都交给 agent。
Here are 500 files.
Here are all the logs.
Here is the entire repository.
Good luck.
更多 context 并不自动意味着更好的推理。
相反,应构建一个 retrieval layer。
def get_context(task):
relevant_files = search_code(
extract_keywords(task)
)
architecture = read_file(
"docs/architecture.md"
)
rules = read_file(
"AGENTS.md"
)
return {
"task": task,
"rules": rules,
"architecture": architecture,
"relevant_files": relevant_files
}
目标很简单:
Task
↓
Find relevant context
↓
Give agent only what it needs
这能让 context 保持聚焦,并降低重要信息被淹没的可能性。
4. 给 Agent 记忆
context 和 memory 不是同一回事。
context 回答的是:
“我现在需要知道什么?”
memory 回答的是:
“我们已经学到了什么?”
一个简单项目可能有:
agent-memory/
├── decisions.md
├── progress.md
├── failures.md
└── architecture.md
例如:
# decisions.md
## Customer API
We use repository classes for all database access.
Controllers should never call Prisma directly.
Reason:
Keeps persistence concerns separate from
business logic and makes services easier to test.
现在想象 agent 发现了一次失败。
与其在任务结束后丢失这些知识:
# failures.md
## Customer API
Previous implementation attempted to access
Prisma directly from the controller.
Rejected because this violates the repository pattern.
下一次 agent 运行时就能从中学习。
这比单纯增加 context window 有用得多。
5. 把 Rules 变成 Constraints
这里有一个重要区别。
一条 instruction 会说:
Always write tests.
一条 constraint 会说:
The task cannot be completed unless tests pass.
这是两种截然不同的系统。
我们可以通过一个 verification function 来实现:
def verify():
checks = [
run_tests(),
run_typecheck(),
run_linter(),
]
return all(
check["success"]
for check in checks
)
现在 agent 不能简单地说:
Done!
harness 会问:
Did the code actually pass verification?
6. 构建 Agent Loop
现在我们已经有足够的组件来构建一个基础 loop。
def run_agent(task):
context = get_context(task)
for attempt in range(3):
action = agent.decide(context)
result = execute(action)
context.append(result)
if task_complete(result):
verification = verify()
if verification:
return "Task completed"
context.append(
"Verification failed. Fix the issues."
)
return "Task failed after 3 attempts"
注意发生了什么变化。
我们不再做:
Prompt → Code → Done
而是在做:
Prompt
↓
Reason
↓
Act
↓
Verify
↓
Fix
↓
Verify again
↓
Done
这是真正 harness 的开端。
7. 让 Agent 看到它的失败
这是 agentic system 变得更有意思的地方。
假设 agent 写出了:
const customer = await prisma.customer.create({
data: request.body
});
代码可能可以编译。
但我们的架构规定 controller 不能直接访问 Prisma。
harness 可以通过 linting、architecture checks 或 tests 捕获这一点。
例如:
Agent changes code
↓
Run verification
↓
Architecture check fails
↓
Error returned to agent
↓
Agent fixes code
↓
Run verification again
agent 会收到类似这样的信息:
Verification failed.
Rule violation:
Controllers must not access Prisma directly.
Move database access into the repository layer.
现在 agent 得到了可执行的反馈。
这比下面这样要好得多:
Try again.
8. Hooks 让 Harness 更主动
我们还可以使用 hooks 在某些内容被 commit 之前强制执行规则。
例如:
git diff --check
npm run lint
npm run typecheck
npm test
一个 pre-commit hook 可以是:
#!/bin/sh
npm run lint || exit 1
npm run typecheck || exit 1
npm test || exit 1
现在系统不依赖 agent 记得:
“哦,我大概应该运行测试。”
环境会自动完成这件事。
这就是下面两者的区别:
Please follow the rules.
以及:
You cannot proceed until the rules are satisfied.
9. 添加 Recovery
agent 会失败。
这不是 harness 的 bug。
这是 harness 应该预期到的事情。
一个有用的 recovery loop 看起来像这样:
for attempt in range(MAX_ATTEMPTS):
result = agent.execute(task)
verification = verify()
if verification.success:
return result
feedback = {
"error": verification.error,
"attempt": attempt
}
agent.update_context(feedback)
return "Unable to complete safely"
关键部分是反馈。
agent 不再从头开始,而是得到:
What failed?
Why did it fail?
What changed?
What should be tried next?
这给了 agent 一条恢复路径。
10. 不要让 Agent 无限重试
还有另一个重要的工程问题。
如果 agent 不断犯同样的错误,会发生什么?
Attempt 1 → FAIL
Attempt 2 → FAIL
Attempt 3 → FAIL
Attempt 4 → FAIL
...
最终,你的“autonomous” agent 会变成一个昂贵的无限循环。
所以要给它设置边界。
MAX_ATTEMPTS = 3
if attempt >= MAX_ATTEMPTS:
stop_agent()
你也可以检测重复失败:
if error_hash in previous_errors:
stop_agent()
previous_errors.add(error_hash)
这是一个小实现细节,却会对 production 产生巨大影响。
没有边界的 autonomy 并不等于 reliability。
11. 添加 Git Awareness
coding agent 应该清楚知道自己究竟改了什么。
执行前:
git status
执行后:
git diff
然后 harness 可以检查结果:
changes = get_git_diff()
if touches_forbidden_files(changes):
rollback()
例如:
Allowed:
src/customers/
tests/customers/
Not allowed:
.env
generated/
infrastructure/
现在我们有了另一层安全保障。
agent 可以进行修改。
harness 决定这些修改是否可接受。
12. 将 Planning 与 Execution 分离
另一个有用改进是让 agent 在修改文件之前先制定计划。
不要这样:
Task → Modify files
而是使用:
Task
↓
Plan
↓
Review plan
↓
Execute
↓
Verify
一个 plan 可能看起来像这样:
{
"files": [
"src/customers/customer.controller.ts",
"src/customers/customer.service.ts",
"src/customers/customer.repository.ts",
"tests/customers/customer.test.ts"
],
"changes": [
"Add POST /customers",
"Validate request using Zod",
"Persist through repository",
"Add integration tests"
]
}
harness 可以在 agent 开始修改 repository 之前拒绝一个糟糕的 plan。
这能节省后续大量清理工作。
13. 让 Verification 分层
单一测试套件是不够的。
要按层来思考。
Verification
│
┌──────────────┼──────────────┐
↓ ↓ ↓
Syntax Behavior Architecture
↓ ↓ ↓
Typecheck Tests Rules
↓ ↓ ↓
Lint Integration Security
例如:
checks = [
typecheck(),
lint(),
unit_tests(),
integration_tests(),
security_scan(),
architecture_check()
]
一次成功构建只能告诉你:
“这段代码大概率能运行。”
它并不能告诉你:
“这段代码属于这个架构。”
这就是为什么 harness 需要多种形式的 verification。
14. 完整的 Harness 2.0
现在我们可以把所有东西组合起来。
Developer
│
↓
Task
│
↓
┌─────────────────┐
│ Agent Harness │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
↓ ↓ ↓
Context Memory Rules
↓ ↓ ↓
Retrieval Decisions Guardrails
└────────────────────┼────────────────────┘
↓
Agent
│
↓
Tools
│
↓
Code Changes
│
↓
Verification
│
┌────────┴────────┐
↓ ↓
FAIL PASS
│ │
↓ ↓
Feedback Done
│
└──────→ Agent
这就是我所说的 Harness 2.0。
不是更大的 prompt。
不是另一条 system message。
而是围绕 agent 的完整执行环境。
15. 一个最小 Harness
你不需要在第一天就构建一个庞大的平台。
一个非常有用的初始版本可能是:
class AgentHarness:
def __init__(self, agent):
self.agent = agent
self.memory = []
self.max_attempts = 3
def run(self, task):
context = self.load_context(task)
for attempt in range(self.max_attempts):
action = self.agent.decide(
task=task,
context=context
)
result = self.execute(action)
context.append(result)
verification = self.verify()
if verification.success:
self.save_memory(context)
return "Success"
context.append({
"type": "verification_error",
"message": verification.error
})
return "Failed safely"
这并不是完整的 production implementation。
但架构已经存在:
Context → Action → Execution → Verification → Feedback → Recovery.
在此基础上,你可以继续添加:
tool permissions
sandboxing
persistent memory
planning
rollback
observability
cost controls
security policies
human approval
parallel agents
一次添加一层。
16. 真正的转变
软件工程中正在发生一种微妙但重要的变化。
传统上:
Developer
↓
Code
↓
Tests
↓
Production
在 agentic development 中:
Developer
↓
Harness
↓
Agent
↓
Code
↓
Verification
↓
Feedback
↺
开发者不再只是编写代码。
他们是在设计一个环境,让另一个系统在其中编写代码。
这是一个不同的工程问题。
也需要一种不同的思维方式。
17. 不要为最聪明的 Agent 优化
这可能是最重要的一课。
想象两个系统。
System A
Very powerful model
+
Huge context window
+
Minimal tooling
+
No verification
+
No recovery
System B
Good model
+
Focused context
+
Specialized tools
+
Persistent memory
+
Strong verification
+
Recovery loop
+
Guardrails
如果用于 production,我会选择 System B。
因为 reliability 并不只来自 intelligence。
它来自 围绕 intelligence 的系统。
18. 最好的 Agent 是你能够纠正的 Agent
我们经常问:
“这个 agent 有多聪明?”
更好的问题是:
“当这个 agent 出错时会发生什么?”
你能检测到错误吗?
你能解释它为什么发生吗?
agent 能看到失败吗?
它能恢复吗?
你能防止同样的错误下一次再次发生吗?
你能安全回滚吗?
如果这些问题的答案是 yes,那么你正在构建一个工程系统。
如果答案是 no,那你基本上是在希望模型能够做对。
而希望不是一种 deployment strategy。
结语
AI agent 在编写软件方面已经变得出人意料地出色。
但给 agent 一个 repository 的访问权限,然后让它“build the feature”,还远远不够。
真正的工程挑战在于模型周围的一切:
Context
+
Tools
+
Memory
+
Constraints
+
Verification
+
Recovery
+
Observability
=
Agent Harness
模型提供推理能力。
harness 提供纪律性。
这会改变我们思考 AI-assisted development 的方式。
目标不是构建一个 永远不会犯错 的 agent。
目标是构建一个系统,在这个系统中:
错误能够被快速检测,失败可以恢复,不安全操作受到约束,成功工作会被独立验证。
这就是“能够写代码的 AI”和“你真正可以信任其承担工程工作的 AI system”之间的区别。