【通俗进阶版】什么是Harness?
一、Harness 的本质再理解
Harness 的核心思想是:AI 模型本身只是一台"裸机"(裸模型),Harness 是围绕它构建的"操作系统"。
就像你的电脑有 CPU(模型能力),但光有 CPU 什么都干不了——你需要操作系统来管理文件、调度程序、连接外设。Harness 就是 AI 的"操作系统层" 。
二、六大设计步骤的详细技术拆解
第一步:基础设施 / 沙箱环境(盖马厩)
小学生版:给马一个专属院子,有马槽、有围栏、有工具房。
技术细节:
| 技术组件 | 作用 | 典型实现 |
|---|---|---|
| 沙箱(Sandbox) | 隔离 Agent 的执行环境,防止它误删系统文件或访问敏感数据 | Docker 容器、Firecracker microVM、E2B 沙箱 |
| 持久化工作目录 | Agent 可以读写文件,状态跨会话保留 | 挂载 Volume,如 /workspace |
| 版本控制集成 | Agent 的每次操作都可追踪、可回滚 | Git 仓库自动初始化,每次修改自动 commit |
| 网络隔离 | 控制 Agent 能访问哪些外部资源 | 白名单域名、代理服务器、API 网关 |
为什么重要:没有沙箱,Agent 就像一匹没有围栏的马——它可能"吃掉"你的整个硬盘。沙箱是 Harness 的"地基" 。
第二步:护栏 / 约束系统(戴眼罩和围栏)
小学生版:马容易乱跑,给它戴上眼罩,再围上栅栏。
技术细节:
| 约束类型 | 技术实现 | 示例 |
|---|---|---|
| 文件系统护栏 | 路径白名单/黑名单 | 禁止写入 ~/.ssh/、/etc/,只允许在 /workspace 操作 |
| 代码质量护栏 | Lint 规则、类型检查 | ESLint、Ruff、MyPy 自动运行,代码不合规就拒绝 |
| 安全护栏 | 内容过滤器、权限最小化 | 检测 SQL 注入、XSS 攻击模式;API Key 不可读 |
| 结构化护栏 | Schema 验证 | 输出必须符合 JSON Schema,否则重试 |
| 预算护栏 | Token/成本限制 | 单次调用不超过 4000 tokens,总成本不超过 $5 |
核心原则:“约束优于指令”(Constraints > Prompts)
不要写 Prompt 说"请不要删除重要文件",而是直接让文件系统权限禁止删除。Prompt 可以被模型"忘记"或"忽略",但系统级约束不可绕过 。
技术实现示例:
# 文件系统护栏:只允许在 /workspace 下操作
ALLOWED_PATHS = ["/workspace"]
def validate_path(path: str) -> bool:
return any(path.startswith(allowed) for allowed in ALLOWED_PATHS)
# 代码护栏:提交前自动运行测试
def pre_commit_hook():
result = run_tests()
if result.failed:
raise RejectCommitError("测试未通过,禁止提交")
第三步:工具集成与上下文工程(工具包和地图)
小学生版:给马挂上工具包,里面有锤子、钉子、地图,需要时自己拿。
技术细节:
| 技术概念 | 说明 | 典型协议/实现 |
|---|---|---|
| MCP(Model Context Protocol) | 标准化的工具调用协议,让 Agent 能统一调用各种外部工具 | Anthropic 提出的开放协议,支持文件系统、数据库、API 等 |
| Skills / 技能定义 | 预定义的工具模板,描述"这个工具是干什么的、需要什么参数、返回什么" | 类似 OpenAI Function Calling 的 functions 定义 |
| AGENTS.md | 项目级的上下文文件,告诉 Agent"这个项目的结构、规范、常用命令" | 放在项目根目录,Agent 自动读取 |
| 动态上下文加载 | 不是一次性塞给模型所有信息,而是按需检索 | RAG(检索增强生成)、向量数据库、文件索引 |
关键技术:MCP 协议
MCP 是 Harness 中工具集成的核心。它定义了一套标准,让任何工具(文件系统、数据库、浏览器、Slack)都能以统一的方式暴露给 Agent:
Agent → MCP Client → MCP Server → 外部工具
好处:
- 可插拔:今天用文件系统工具,明天换成数据库工具,Agent 代码不用改
- 安全:MCP Server 可以独立控制权限
- 标准化:不同厂商的 Agent 可以共享同一套工具
上下文工程的关键原则:
“给地图,别给百科全书”(Index, not dump)
不要一次性把 10 万行代码塞进 Prompt,而是给一个索引(文件树、关键函数列表),让 Agent 按需读取。这解决了上下文窗口的限制问题 。
第四步:规划与任务分解(拆分路程)
小学生版:从北京到上海太远,拆成"北京→天津→济南→南京→上海"。
技术细节:
| 技术概念 | 说明 | 实现方式 |
|---|---|---|
| 任务分解(Task Decomposition) | 将复杂目标拆成原子子任务 | 模型自主规划,或预定义工作流 |
| Sub-agent 调度 | 主 Agent 把子任务派发给专门的子 Agent | 每个子 Agent 有特定角色(如"代码审查员"、“测试工程师”) |
| 规划-执行循环(Plan-Execute Loop) | 先制定计划,再逐步执行,执行中可调整计划 | ReAct 模式、Ralph 循环 |
| 状态机(State Machine) | 用有限状态机管理任务生命周期 | 定义状态:待办→进行中→待验证→完成→失败 |
Ralph 循环(长时程自主执行的核心机制) :
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 规划 │────→│ 执行 │────→│ 验证 │
│ (Planning) │ │ (Execution) │ │ (Validation)│
└─────────────┘ └─────────────┘ └──────┬──────┘
↑ │
└─────────────────────────────────────────┘
(验证失败则重新规划)
这个循环让 Agent 能在长时间运行中保持目标一致性,而不是"做着做着就忘了要干什么"。
技术实现示例:
class TaskPlanner:
def plan(self, goal: str) -> List[SubTask]:
# 让模型生成执行计划
plan = llm.generate(f"将以下目标拆分为子任务:{goal}")
return parse_plan(plan)
def execute(self, tasks: List[SubTask]):
for task in tasks:
result = sub_agent.run(task)
if not validate(result):
# 验证失败,重新规划
return self.replan(task, result)
第五步:验证与回压(检查与拉缰绳)
小学生版:马跑错了,后面的人喊"停!";跑对了,摸摸头鼓励。
技术细节:
| 验证层级 | 检查内容 | 技术实现 |
|---|---|---|
| L0:语法/格式验证 | 输出是否符合预期格式 | JSON Schema 验证、正则表达式、类型检查 |
| L1:单元测试 | 代码逻辑是否正确 | 自动运行 pytest、jest、mocha |
| L2:集成测试 | 模块间协作是否正常 | 端到端测试、API 契约测试 |
| L3:人工审查 | 关键变更需要人确认 | PR Review、审批工作流 |
| L4:安全审计 | 是否存在漏洞或恶意代码 | 静态分析(SAST)、依赖扫描 |
回压机制(Backpressure):
当验证失败时,Harness 不是简单地报错退出,而是将错误信息推回给 Agent,让它自我纠正:
Agent 生成代码 → 运行测试 → 测试失败 → 错误日志返回给 Agent
↓
Agent 分析错误 → 修改代码 → 重新测试 → ...直到通过
这形成了一个自动迭代闭环,类似于人类开发者的"写代码→运行→调试"循环 。
Hook 机制:
Harness 在关键节点插入 Hook,自动触发验证:
@pre_tool_use # 工具调用前
def check_permissions(tool_name: str, args: dict):
if tool_name == "file_delete" and args["path"] not in ALLOWED_PATHS:
raise PermissionDenied()
@post_tool_use # 工具调用后
def validate_output(tool_name: str, result: Any):
if tool_name == "code_generate":
run_linter(result)
run_tests(result)
第六步:记忆与状态管理(记笔记)
小学生版:马跑完一趟,把路线、坑、减速点都记下来,下次直接看。
技术细节:
| 记忆类型 | 类比 | 技术实现 | 持久化方式 |
|---|---|---|---|
| 工作记忆 | 马脑子里现在想的事 | 当前对话上下文(Prompt) | 内存,随会话结束清空 |
| 程序性记忆 | 马学会的技能(怎么钉蹄铁) | Skills、工具定义、代码模板 | 配置文件、数据库 |
| 情景记忆 | 上次跑这条路的经历 | 会话历史、操作日志 | 日志文件、向量数据库 |
| 语义记忆 | 地图、常识知识 | RAG 检索、知识图谱 | 向量数据库、图数据库 |
分层记忆架构:
┌─────────────────────────────────────────┐
│ 工作记忆(Working Memory) │ ← 当前上下文窗口
│ (对话历史、当前任务状态、临时变量) │
├─────────────────────────────────────────┤
│ 程序性记忆(Procedural) │ ← Skills、工具定义
│ ("如何调用 Git"、"如何写单元测试") │
├─────────────────────────────────────────┤
│ 情景记忆(Episodic) │ ← 操作日志、Git 历史
│ ("上次修改这个文件时引入了 Bug") │
├─────────────────────────────────────────┤
│ 语义记忆(Semantic) │ ← 项目文档、代码知识库
│ ("这个函数是做什么的"、"项目的架构") │
└─────────────────────────────────────────┘
关键技术:文件系统作为外部记忆
由于模型的上下文窗口有限(通常 128K-200K tokens),Harness 利用文件系统突破这个限制:
MEMORY.md:记录项目级的重要决策和约束PROGRESS.md:记录当前任务进度,跨会话恢复- Git 历史:记录所有代码变更,Agent 可以
git log查看 - 向量数据库:存储代码片段的语义嵌入,支持语义搜索
# 记忆写入
def save_memory(key: str, content: str):
with open(f"/workspace/.memory/{key}.md", "w") as f:
f.write(content)
# 记忆读取(按需加载)
def load_relevant_memories(query: str) -> List[str]:
# 使用向量数据库检索相关记忆
embeddings = vector_db.search(query, top_k=5)
return [mem.content for mem in embeddings]
三、Harness 的整体架构图
┌─────────────────────────────────────────────────────────────┐
│ 用户 / 开发者 │
└──────────────────────┬────────────────────────────────────┘
│
┌──────────────────────▼────────────────────────────────────┐
│ Harness(驾驭层) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ 沙箱 │ │ 护栏 │ │工具集成 │ │ 规划调度 │ │
│ │ 环境 │ │ 系统 │ │ (MCP) │ │ (Sub-agent) │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────────────────────────┐ │
│ │ 验证 │ │ 记忆 │ │ 状态管理引擎 │ │
│ │ 回压 │ │ 系统 │ │ (Ralph 循环 / 状态机) │ │
│ └─────────┘ └─────────┘ └─────────────────────────────┘ │
└──────────────────────┬────────────────────────────────────┘
│
┌──────────────────────▼────────────────────────────────────┐
│ AI 模型(裸机) │
│ (LLM / VLM / 多模态模型 / 推理引擎) │
└─────────────────────────────────────────────────────────────┘
四、为什么 Harness 比单纯 Prompt Engineering 重要?
| 维度 | Prompt Engineering | Harness Engineering |
|---|---|---|
| 关注点 | 让模型"说对话" | 让模型"做对事" |
| 稳定性 | 依赖模型随机性,结果不稳定 | 通过约束和验证保证确定性 |
| 可扩展性 | 上下文窗口限制 | 通过工具、记忆突破限制 |
| 安全性 | 仅靠"请别做坏事" | 系统级权限控制,不可绕过 |
| 生产力 | 单次对话 | 长时程自主执行,持续迭代 |
核心洞察:Prompt Engineering 是在"调教马的性格",Harness Engineering 是在"给马配装备"。性格调教有上限,但装备可以无限升级 。
五、现实中的 Harness 实例
| 产品/框架 | Harness 特点 |
|---|---|
| Cursor | 文件系统沙箱 + Git 集成 + 代码索引 + 多 Agent 协作 |
| Devin | 完整云环境 + 浏览器工具 + 自主规划循环 + 长期记忆 |
| Claude Code | MCP 工具协议 + 权限控制 + 测试验证 + 会话记忆 |
| OpenAI Codex | 沙箱执行 + 工具调用 + 迭代调试 + 代码审查 |
这些产品之间的竞争,本质上不是"谁的模型更强",而是谁的 Harness 更完善——谁的马具更结实、更灵活、更安全 。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)