当"过度工程"成为智能体的进化接口:从 Prompt Engineering 到 Agent 自进化 Harness 的工程实践
从 Prompt Engineering、Context Engineering 到 Harness Engineering,Agent 的竞争正在从"谁的模型更强",转向"谁的系统更会成长"。本文不谈玄学,只谈一个工程问题:我们能不能让 Agent 像维护良好的代码库一样,持续地、可验证地、可回滚地改造自己。

文章目录
- [当"过度工程"成为智能体的进化接口:从 Prompt Engineering 到 Agent 自进化 Harness 的工程实践](#当"过度工程"成为智能体的进化接口:从 Prompt Engineering 到 Agent 自进化 Harness 的工程实践)
-
- [一、Agent 的下一站,不只是更强的模型](#一、Agent 的下一站,不只是更强的模型)
- [二、什么是 Agent 自进化?](#二、什么是 Agent 自进化?)
- [三、自进化真正发生在 Harness 层](#三、自进化真正发生在 Harness 层)
-
- [1. Prompt 层](#1. Prompt 层)
- [2. Context 层](#2. Context 层)
- [3. Workflow 层](#3. Workflow 层)
- [4. Harness 层](#4. Harness 层)
- [5. Optimizer 层](#5. Optimizer 层)
- [四、DeepSeek Harness 为什么看起来"过度工程"?](#四、DeepSeek Harness 为什么看起来"过度工程"?)
- 五、过度工程,也可以是一种期权
- 六、不是所有复杂度都有资格叫"面向进化"
-
- [1. 可观测](#1. 可观测)
- [2. 可归因](#2. 可归因)
- [3. 可回滚](#3. 可回滚)
- [4. 可组合](#4. 可组合)
- [七、一个可落地的自进化 Agent 架构](#七、一个可落地的自进化 Agent 架构)
-
- [1. 执行平面](#1. 执行平面)
- [2. 观测平面](#2. 观测平面)
- [3. 经验平面](#3. 经验平面)
- [4. 演化平面](#4. 演化平面)
- [5. 治理平面](#5. 治理平面)
- 八、警惕"进化"变成复杂度癌变
- 九、过度工程的正确打开方式
- [十、结语:未来的 Agent,竞争的是进化速度](#十、结语:未来的 Agent,竞争的是进化速度)
- 参考资料
如果只用一句话概括过去一年 Agent 领域的范式漂移,我会说:大家的注意力正在从"模型内部"转移到"模型外部"。以前我们关心的是换一个更大的基座、调一调 temperature、写一段更聪明的系统提示词;而现在,越来越多的工作把重点放在了包裹模型的那个"外壳"上------工具怎么暴露、上下文怎么裁剪、失败怎么重试、经验怎么沉淀、权限怎么收敛。这个外壳,在本文里统一称为 Harness(工装 / 承载框架) 。
为什么这个转向会发生?因为工程现场反复验证了一件事:相同模型接入不同 Harness,最终表现可能天差地别。模型提供的是"推理潜力",而 Harness 决定这种潜力能不能在真实环境里被稳定兑现。换句话说,模型是可替换的零件,Harness 才是决定系统上限的工程结构。下面的内容,就是要把这个判断从口号变成可落地的工程方案。
一、Agent 的下一站,不只是更强的模型
过去,我们习惯用模型能力解释 Agent 的表现:任务失败了,就换一个参数更大、推理更强的模型。这个思路在 2023 到 2024 年屡试不爽,因为那段时间模型能力的边际收益确实极高。但到了今天,纯模型增益的性价比正在快速下降,而系统层面的工程增益开始显现出复利效应。
在真实工程里,影响 Agent 表现的远不止模型一个变量。至少还包括:系统提示词如何组织;上下文如何裁剪和压缩;工具如何暴露、调用和校验;任务如何规划、重试与回滚;经验如何沉淀为记忆或技能;权限、沙箱和资源预算如何控制;执行结果如何评估和反馈。这七项里,没有任何一项直接改模型权重,但它们合在一起,往往比"换一个强 10% 的模型"对最终成功率的影响更大。
因此,一个更接近工程现实的公式是:
text
Agent = Model + Harness
模型提供推理能力,Harness 负责把这种能力接入真实环境。这个观点并非空想,近期一系列研究已经把"改 Harness 比改模型更划算"做成了可复现的实验结论。我把其中较有代表性的几篇整理成下表,作为后文讨论的事实底座:
| 方法 | 核心思路 | 评测增益 | 风险可控手段 |
|---|---|---|---|
| Agentic Harness Engineering (AHE) | 可观测性三支柱(组件/经验/决策) | Terminal-Bench 2 pass@1 69.7%→77.0%,超 Codex-CLI 71.9% | 冻结后零成本迁移到其他模型家族 |
| Gated Semantic Quality-Diversity (GSME) | 按 (where×why) 病理学归档补丁 | 7 领域 sealed test +9~+15.5pp,保留训练增益 86%~147% | 门控语义质量多样性抗过拟合 |
| Meta-Harness | 外层循环搜索 harness 代码 | 在 TerminalBench-2 超手搓基线 | 历史经验自动复用 |
| HarnessX | 可组合/自适应/可演化 harness 工厂 | 5 基准平均 +14.5% | AEGIS 引擎约束演化方向 |
| DarwinX | 种群自然选择式演化 | 4 基准平均 +17pp | 选择压淘汰劣势变体 |
| Ouroboros | 受评审的核心自演化 | Terminal-Bench 2.1 达 86.97% | 评审门控防止无意义变更 |
这张表最值得注意的,不是哪个数字更高,而是它们的"风险可控手段"一栏几乎都指向同一条原则:把变化和证明变化的过程拆开。这一点会在第七章变成一套架构原则。
DeepSeek 在发布 Harness 开发者预览时,也给出了非常鲜明的设计立场:模型、工具、技能、会话、存储、沙箱、Agent Loop,甚至调度和 UI,都可以被组织为插件。官方将其概括为"一切皆插件"(Everything is a plugin)。这种设计初看多少有些"过度工程"------一个简单能力为什么要拆成多个包,为什么注册一个服务还要考虑卸载和副作用回收?但它恰好指向 Agent 的下一个重要方向:自进化。所谓"过度工程"在这里不是炫技,而是在提前购买"未来可以安全地修改自己"的权利。
二、什么是 Agent 自进化?
Agent 自进化不是让模型突然拥有意识,也不等于让大模型直接改写自己的权重。从工程角度看,它更接近一个持续运行的闭环:系统先执行任务,记录轨迹与结果,识别稳定失败模式,提出系统修改方案,在隔离环境中验证,灰度启用或回滚,最后把有效改动沉淀为新的 Harness 能力。
#mermaid-svg-jfr6AzeAJNi45bHG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jfr6AzeAJNi45bHG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jfr6AzeAJNi45bHG .error-icon{fill:#552222;}#mermaid-svg-jfr6AzeAJNi45bHG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jfr6AzeAJNi45bHG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jfr6AzeAJNi45bHG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jfr6AzeAJNi45bHG .marker.cross{stroke:#333333;}#mermaid-svg-jfr6AzeAJNi45bHG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jfr6AzeAJNi45bHG p{margin:0;}#mermaid-svg-jfr6AzeAJNi45bHG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jfr6AzeAJNi45bHG .cluster-label text{fill:#333;}#mermaid-svg-jfr6AzeAJNi45bHG .cluster-label span{color:#333;}#mermaid-svg-jfr6AzeAJNi45bHG .cluster-label span p{background-color:transparent;}#mermaid-svg-jfr6AzeAJNi45bHG .label text,#mermaid-svg-jfr6AzeAJNi45bHG span{fill:#333;color:#333;}#mermaid-svg-jfr6AzeAJNi45bHG .node rect,#mermaid-svg-jfr6AzeAJNi45bHG .node circle,#mermaid-svg-jfr6AzeAJNi45bHG .node ellipse,#mermaid-svg-jfr6AzeAJNi45bHG .node polygon,#mermaid-svg-jfr6AzeAJNi45bHG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jfr6AzeAJNi45bHG .rough-node .label text,#mermaid-svg-jfr6AzeAJNi45bHG .node .label text,#mermaid-svg-jfr6AzeAJNi45bHG .image-shape .label,#mermaid-svg-jfr6AzeAJNi45bHG .icon-shape .label{text-anchor:middle;}#mermaid-svg-jfr6AzeAJNi45bHG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jfr6AzeAJNi45bHG .rough-node .label,#mermaid-svg-jfr6AzeAJNi45bHG .node .label,#mermaid-svg-jfr6AzeAJNi45bHG .image-shape .label,#mermaid-svg-jfr6AzeAJNi45bHG .icon-shape .label{text-align:center;}#mermaid-svg-jfr6AzeAJNi45bHG .node.clickable{cursor:pointer;}#mermaid-svg-jfr6AzeAJNi45bHG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jfr6AzeAJNi45bHG .arrowheadPath{fill:#333333;}#mermaid-svg-jfr6AzeAJNi45bHG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jfr6AzeAJNi45bHG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jfr6AzeAJNi45bHG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jfr6AzeAJNi45bHG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jfr6AzeAJNi45bHG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jfr6AzeAJNi45bHG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jfr6AzeAJNi45bHG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jfr6AzeAJNi45bHG .cluster text{fill:#333;}#mermaid-svg-jfr6AzeAJNi45bHG .cluster span{color:#333;}#mermaid-svg-jfr6AzeAJNi45bHG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jfr6AzeAJNi45bHG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jfr6AzeAJNi45bHG rect.text{fill:none;stroke-width:0;}#mermaid-svg-jfr6AzeAJNi45bHG .icon-shape,#mermaid-svg-jfr6AzeAJNi45bHG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jfr6AzeAJNi45bHG .icon-shape p,#mermaid-svg-jfr6AzeAJNi45bHG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jfr6AzeAJNi45bHG .icon-shape .label rect,#mermaid-svg-jfr6AzeAJNi45bHG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jfr6AzeAJNi45bHG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jfr6AzeAJNi45bHG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jfr6AzeAJNi45bHG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
执行任务
记录轨迹与结果
识别稳定失败模式
提出系统修改方案
隔离环境验证
验证通过?
灰度启用或回滚
沉淀为新 Harness 能力

这个图里最容易被忽视的一点是:闭环的起点是"执行任务",终点是"沉淀能力",而能力又会反过来影响下一次执行。它不是一次性的优化脚本,而是一个会不断产生结构化经验的机器。
也就是说,Agent 不只是从一次任务中获得答案,还会把失败转化为下一轮可复用的系统改进。这里的关键词是"稳定失败模式"------偶发的、随机的失败不值得改系统,只有反复出现的、可归类的失败才值得写成能力。
举个具体的代码 Agent 例子。一个代码 Agent 多次出现"修改代码后没有运行测试"的问题。普通方案是在系统提示词里追加一句"完成修改后必须运行测试"。这种写法的问题在于:它依赖模型在每次任务里都"记得"并"愿意"遵守,而模型在长任务、多工具交错的后期,注意力会被稀释,遗忘指令是常态。
而一个具备自进化能力的 Agent 可能进一步采取以下措施:统计哪些任务发生过"未测试即完成";增加任务结束前的验证钩子;根据项目类型自动选择测试命令(前端用 npm test,Python 用 pytest);测试失败时禁止任务直接进入完成状态;评估这一改动是否真正提高成功率、是否增加过多成本;验证有效后,将其注册为可复用插件。
前者是在"劝模型听话",后者是在"改造模型工作的环境"。二者的本质区别是:前者把正确性寄托在模型的意愿上,后者把正确性固化在系统的结构上。
下面这段代码就是"任务完成前强制跑测试"的验证钩子思想的最小实现。它的核心只有一句话:当模型声称任务完成时,必须先经过一道闸门,测试不过就不允许进入完成态。
typescript
// 任务完成前强制跑测试的验证钩子(TypeScript 示意)
type TaskState = "running" | "verifying" | "completed" | "failed";
interface Task {
id: string;
projectRoot: string;
proposedState: TaskState;
blockedBy?: string;
}
class CompletionGate {
constructor(private runner: TestRunner) {}
// 任何试图进入 completed 的请求都必须先过这道闸
async attemptComplete(task: Task): Promise<TaskState> {
if (task.proposedState !== "completed") return task.proposedState;
const result = await this.runner.run(task.projectRoot);
if (!result.passed) {
// 关键:测试失败,禁止进入完成态
task.blockedBy = `tests failed: ${result.summary}`;
return "failed";
}
return "completed";
}
}
注意这里没有用提示词,而是用控制流。模型可以"说"自己完成了,但状态机不认------除非测试通过。这种"用程序而非自然语言来约束模型"的思路,是自进化 Harness 的底层纪律。
三、自进化真正发生在 Harness 层
让模型实时修改自身权重,在成本、训练稳定性、数据质量和安全性方面都非常困难。我们不妨算一笔账:一次有监督微调需要标注数据、算力预算和防灾难性遗忘的护栏;而模型在线上遇到的失败模式是长尾的、快速变化的,为每一个失败都发起一轮微调既不经济也不及时。相比之下,修改 Harness------也就是修改模型工作的外部环境------要现实得多,反馈周期可以从"天"压缩到"分钟"。
Agent 可以逐级修改,风险与收益同步递增。我把这个层次结构画成下图,从最外层的文本修改,一路深入到最内层的"修改修改器本身":
#mermaid-svg-vHrVeFdrl1xZkpbZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vHrVeFdrl1xZkpbZ .error-icon{fill:#552222;}#mermaid-svg-vHrVeFdrl1xZkpbZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vHrVeFdrl1xZkpbZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .marker.cross{stroke:#333333;}#mermaid-svg-vHrVeFdrl1xZkpbZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vHrVeFdrl1xZkpbZ p{margin:0;}#mermaid-svg-vHrVeFdrl1xZkpbZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .cluster-label text{fill:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .cluster-label span{color:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .cluster-label span p{background-color:transparent;}#mermaid-svg-vHrVeFdrl1xZkpbZ .label text,#mermaid-svg-vHrVeFdrl1xZkpbZ span{fill:#333;color:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .node rect,#mermaid-svg-vHrVeFdrl1xZkpbZ .node circle,#mermaid-svg-vHrVeFdrl1xZkpbZ .node ellipse,#mermaid-svg-vHrVeFdrl1xZkpbZ .node polygon,#mermaid-svg-vHrVeFdrl1xZkpbZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .rough-node .label text,#mermaid-svg-vHrVeFdrl1xZkpbZ .node .label text,#mermaid-svg-vHrVeFdrl1xZkpbZ .image-shape .label,#mermaid-svg-vHrVeFdrl1xZkpbZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-vHrVeFdrl1xZkpbZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .rough-node .label,#mermaid-svg-vHrVeFdrl1xZkpbZ .node .label,#mermaid-svg-vHrVeFdrl1xZkpbZ .image-shape .label,#mermaid-svg-vHrVeFdrl1xZkpbZ .icon-shape .label{text-align:center;}#mermaid-svg-vHrVeFdrl1xZkpbZ .node.clickable{cursor:pointer;}#mermaid-svg-vHrVeFdrl1xZkpbZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .arrowheadPath{fill:#333333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vHrVeFdrl1xZkpbZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vHrVeFdrl1xZkpbZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vHrVeFdrl1xZkpbZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vHrVeFdrl1xZkpbZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .cluster text{fill:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ .cluster span{color:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vHrVeFdrl1xZkpbZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vHrVeFdrl1xZkpbZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-vHrVeFdrl1xZkpbZ .icon-shape,#mermaid-svg-vHrVeFdrl1xZkpbZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vHrVeFdrl1xZkpbZ .icon-shape p,#mermaid-svg-vHrVeFdrl1xZkpbZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vHrVeFdrl1xZkpbZ .icon-shape .label rect,#mermaid-svg-vHrVeFdrl1xZkpbZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vHrVeFdrl1xZkpbZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vHrVeFdrl1xZkpbZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vHrVeFdrl1xZkpbZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Prompt 层
修改对象: 角色/规则/说明
风险: 低
Context 层
修改对象: 记忆/排序/注入
风险: 中
Workflow 层
修改对象: 拆分/重试/协作
风险: 中高
Harness 层
修改对象: 中间件/钩子/状态机
风险: 高
Optimizer 层
修改对象: 优化器自身
风险: 极高
每一层的含义和代价不同,我用一个对比表收一下:
| 层级 | 修改对象 | 成本 | 风险 | 代表动作 |
|---|---|---|---|---|
| Prompt 层 | 角色、规则、任务说明 | 低 | 低 | 在系统提示词追加指令 |
| Context 层 | 记忆检索、排序、摘要、Skill 注入 | 低~中 | 中 | 调整上下文注入策略 |
| Workflow 层 | 任务拆分、并行、审查、重试、协作 | 中 | 中高 | 引入多 Agent 流水线 |
| Harness 层 | 工具中间件、权限、钩子、状态机、存储 | 高 | 高 | 注册/卸载一个能力插件 |
| Optimizer 层 | 负责"改进 Agent"的优化器自身 | 极高 | 极高 | 让优化器学会选择写入位置 |
1. Prompt 层
调整角色、规则和任务说明。这是成本最低的改进方式,改一句话就能上线。但问题也最多:提示词膨胀后上下文被稀释;多条规则之间会冲突;模型在长任务里会"遗忘"早期指令。Prompt 层适合做快速实验,不适合做长期承诺。
2. Context 层
改变记忆检索、上下文排序、摘要策略和 Skill 注入方式。它解决的不是"模型不知道",而是"正确的信息没有在正确的时间出现"。这一层常常是性价比最高的------很多失败并不是模型能力不够,而是关键上下文被截断或排在后面。
3. Workflow 层
调整任务拆分、并行执行、审查、重试以及多 Agent 协作流程。Agent 开始改变的不再是一段文本,而是自己的工作方式。例如把"先写全部代码再测试"改成"每完成一个函数就跑一次单元测试",成功率提升往往立竿见影。
4. Harness 层
修改工具中间件、权限策略、生命周期钩子、状态机和 Agent Loop。这一层已经接近真正意义上的系统自修改------Agent 开始决定"自己用什么工具、工具怎么被包装、什么情况下被拦截"。本文第二张图里的 CompletionGate 就属于这一层。
5. Optimizer 层
修改负责"改进 Agent"的优化器自身。例如,原来的优化器只能修改 Prompt;当系统引入 Skill、插件和工作流以后,它还需要学习:某条经验究竟应该写进提示词,还是实现成工具、技能或中间件。第五层才真正触及递归式自进化,但同时也具有最高风险------因为一旦优化器出了问题,它会以指数速度放大错误。
回到工程现实:绝大多数团队应当把自进化的活动范围控制在 Prompt 到 Harness 层,对 Optimizer 层保持人工在环。这一点在第九章的演进路线里会再次强调。
这里补一个落地的例子。经验平面最常见的失败之一,是"Agent 在错误的目录下执行命令"------它以为自己在项目 A,其实 cwd 还停留在项目 B。这属于 Context 层的问题(工作目录状态没有进入短期上下文),修复方式却落在 Harness 层(给 shell 工具加一个中间件,自动注入当前目录)。下面是这个中间件的示意实现:
python
# 执行命令前自动注入当前目录(cwd)的工具中间件(Python 示意)
def with_cwd(tool):
"""包装任意工具,使其在调用前自动携带当前工作目录。"""
original_execute = tool.execute
def wrapped(ctx, args):
args = dict(args) # 不污染原始参数
args["cwd"] = args.get("cwd") or ctx.workspace.current_dir
return original_execute(ctx, args)
return wrapped
# 注册为 shell 工具的全局中间件
register_middleware("shell", with_cwd)
这段代码虽然短,但它示范了自进化的一种典型形态:不写提示词,不改模型,而是给工具套一层"总是做对的事"的中间件。错误目录的问题从此在结构上消失,而不是靠模型"记得切目录"。
四、DeepSeek Harness 为什么看起来"过度工程"?
在传统软件开发中,"一切皆插件"很容易被质疑:一个简单能力为什么要拆成多个包?为什么需要这么多生命周期和上下文?为什么不能直接改核心代码?为什么注册一个服务还要考虑卸载和副作用回收?如果面对的是功能稳定、边界清晰的普通应用,这些质疑完全合理------你不会为了一个静态官网引入一个依赖注入容器。
但 DeepSeek Harness 面对的并不是一个静态应用,而是一个未来可能需要修改自身运行结构的 Agent。根据其公开架构,DeepSeek Harness 底层使用 Cordis 组织插件。Cordis 的内核只负责三件事:插件的挂载、卸载、以及它们之间的依赖解析;它不承载任何具体能力。包括模型适配器、工具注册表、会话日志、Agent Loop 在内的组件,全部由插件提供,通过 Cordis 暴露的共享服务(service)与类型化事件(typed event)相互协作。这意味着开发者无需修改内核源码,就能在配置层把任意能力组合出来。
我把它画成一张"一切皆插件"的架构图,内核居中,外围能力全部可挂载/卸载:
#mermaid-svg-Fxy7xy4ExFPam8D8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fxy7xy4ExFPam8D8 .error-icon{fill:#552222;}#mermaid-svg-Fxy7xy4ExFPam8D8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fxy7xy4ExFPam8D8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .marker.cross{stroke:#333333;}#mermaid-svg-Fxy7xy4ExFPam8D8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fxy7xy4ExFPam8D8 p{margin:0;}#mermaid-svg-Fxy7xy4ExFPam8D8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .cluster-label text{fill:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .cluster-label span{color:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .cluster-label span p{background-color:transparent;}#mermaid-svg-Fxy7xy4ExFPam8D8 .label text,#mermaid-svg-Fxy7xy4ExFPam8D8 span{fill:#333;color:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .node rect,#mermaid-svg-Fxy7xy4ExFPam8D8 .node circle,#mermaid-svg-Fxy7xy4ExFPam8D8 .node ellipse,#mermaid-svg-Fxy7xy4ExFPam8D8 .node polygon,#mermaid-svg-Fxy7xy4ExFPam8D8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .rough-node .label text,#mermaid-svg-Fxy7xy4ExFPam8D8 .node .label text,#mermaid-svg-Fxy7xy4ExFPam8D8 .image-shape .label,#mermaid-svg-Fxy7xy4ExFPam8D8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Fxy7xy4ExFPam8D8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .rough-node .label,#mermaid-svg-Fxy7xy4ExFPam8D8 .node .label,#mermaid-svg-Fxy7xy4ExFPam8D8 .image-shape .label,#mermaid-svg-Fxy7xy4ExFPam8D8 .icon-shape .label{text-align:center;}#mermaid-svg-Fxy7xy4ExFPam8D8 .node.clickable{cursor:pointer;}#mermaid-svg-Fxy7xy4ExFPam8D8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .arrowheadPath{fill:#333333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fxy7xy4ExFPam8D8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Fxy7xy4ExFPam8D8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fxy7xy4ExFPam8D8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Fxy7xy4ExFPam8D8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .cluster text{fill:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 .cluster span{color:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Fxy7xy4ExFPam8D8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Fxy7xy4ExFPam8D8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Fxy7xy4ExFPam8D8 .icon-shape,#mermaid-svg-Fxy7xy4ExFPam8D8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fxy7xy4ExFPam8D8 .icon-shape p,#mermaid-svg-Fxy7xy4ExFPam8D8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Fxy7xy4ExFPam8D8 .icon-shape .label rect,#mermaid-svg-Fxy7xy4ExFPam8D8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fxy7xy4ExFPam8D8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Fxy7xy4ExFPam8D8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Fxy7xy4ExFPam8D8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Cordis 内核
挂载 / 卸载 / 依赖解析
Model 插件
Tools 插件
Skills 插件
Sessions 插件
Sandbox 插件
Storage 插件
Loop 插件
Scheduling 插件
UI 插件
这张图的关键在于:每一条连线都应该是可断开的。插件卸载时,它向共享上下文注册的服务、监听的事件、产生的副作用都应当随之撤销,系统回到挂载前的状态。下面这段 Cordis 风格的插件骨架,展示了"可挂载 / 可卸载"在工程上到底是什么样子:
typescript
// Cordis 风格插件注册骨架:可挂载、可撤销副作用(TypeScript 示意)
import { definePlugin } from "@cordis/plugin";
export default definePlugin({
name: "ds-test-gate",
// provide 返回的函数即为"可撤销副作用"
provide() {
const ctx = this.ctx;
// 1. 向共享上下文注册一个服务
ctx.service("testGate", new CompletionGate(ctx.test.runner));
// 2. 监听任务状态变更事件
const off = ctx.on("task:transition", (e) => {
if (e.to === "completed") gate.intercept(e.task);
});
// 3. 返回清理函数,卸载时调用
return () => {
off();
ctx.service("testGate", null);
};
},
// 插件卸载时,Cordis 会先执行 provide 返回的清理函数,
// 再调用 onUnmount 做最终回收,确保系统恢复原状
onUnmount() {
this.ctx.service("testGate", null);
},
});
注意 provide 的返回值是一个清理闭包。这正是"可撤销副作用"的落点:任何修改都有对应的回收逻辑。没有这层保证,自进化就只是"不断往系统里塞东西",直到再也回不去。
与之配套的,是 DeepSeek Harness 的"每次运行有迹可循"。模型看到的一切都会被写入 append-only 会话日志:系统提示词、思维链、工具调用与结果、子 Agent 调度、上下文注入,一应俱全。这个设计对自进化至关重要------没有完整轨迹,就无法归因,无法归因就无法进化。下面是一个轨迹记录片段的样子:
json
{
"sessionId": "sess_8f2a",
"entries": [
{ "t": 0, "type": "system_prompt", "content": "You are a coding agent..." },
{ "t": 12, "type": "tool_call", "name": "edit_file", "args": {"file": "a.ts"}, "result": "ok" },
{ "t": 13, "type": "context_injection", "source": "skill:test-gate", "content": "remember to run tests" },
{ "t": 40, "type": "subagent", "kind": "reviewer", "prompt": "review diff" },
{ "t": 88, "type": "tool_call", "name": "run_tests", "result": "FAIL 3/40" },
{ "t": 90, "type": "decision", "action": "block_completion", "reason": "tests failed" }
]
}
在 Trajectory 视图里,你可以按来源查看、恢复、分叉、检索、回放任意一次运行。分叉(fork)尤其关键:它允许系统从某一历史节点拉出一条新分支去试一种新 Harness,而不影响主线。
最后,给想动手的读者一组真实可用的命令和配置示例。DeepSeek Harness 提供了极轻量的接入方式,npx 一条命令即可起 Web 界面,也可以直接 clone 仓库研究源码:
bash
# 一行命令启动 DeepSeek Harness Web 控制台
npx @deepseek-ai/dsh web
# 或克隆仓库本地研究其插件与内核实现
git clone https://github.com/deepseek-ai/deepseek-harness
而"在配置层组合能力、无需改源码"则体现在它的 Harness 配置上。下面这段 YAML 描述了一种 Standard 运行模式:模型、工具、技能、沙箱、循环都是独立的插件,按需挂载:
yaml
harness:
mode: standard # 运行模式: standard / code / minimal / creator
plugins:
- model: deepseek-v3.1
- tools: [fs, shell, browser]
- skills: [test-gate, repo-routing]
- sandbox: docker
- loop: react
- storage: append-only-log
把能力写成配置而非硬编码,是自进化 Harness 的基础设施要求之一:只有当"启用一个能力"等于"加一行配置"时,系统才能被自动化地搜索和组合。
这意味着一次系统变更不一定需要直接侵入核心代码,而可以表现为"安装插件 → 注入能力 → 观测效果 → 卸载插件 → 恢复原状"。这正是 Agent 自进化需要的基础设施。
Agent 如果想安全地修改自己,系统至少必须具备以下性质:修改对象可以被准确定位;修改范围具有明确边界;修改能够独立启用;修改效果可以被观测;修改失败后能够回滚;多个修改可以重新组合;实验版本不会污染稳定版本。从这个角度看,所谓"过度工程"并不是为了炫技,而是在提前建设可进化性。
五、过度工程,也可以是一种期权
软件工程经常强调 YAGNI:You Aren't Gonna Need It,不要为尚未出现的需求提前设计。这条原则没有错,但它隐含了一个前提:未来需求大体由人类提出,系统不会高频改造自己。在需求稳定、由人拍板的世界里,提前抽象确实常常是浪费。
自进化 Agent 改变了这个前提。当 Agent 可以持续生成新工具、新技能、新工作流和新策略时,系统变化的频率可能远高于传统软件。此时,模块化、事件化、可撤销和可替换设计,就像购买了一张"未来修改权"。
我们可以把它称为"进化期权"。它的成本和价值可以分别表述为两组式子:
text
过度工程成本
= 当前额外复杂度
+ 学习成本
+ 运行开销
进化期权价值
= 未来修改速度
× 实验数量
× 回滚能力
× 能力复用率
这两组式子不是要你真的去算数值,而是提醒你做一种权衡:一个抽象有没有价值,取决于它服务的系统会"变"多少次。如果系统只运行三个月,而且需求非常稳定,这张期权可能没有价值,额外的抽象就是纯粹的负债。如果系统需要连续运行数年,要适配不同模型、工具和业务环境,甚至允许 Agent 对自身进行实验,那么早期看似多余的接口、事件总线和生命周期管理,可能成为后期最重要的资产。
因此,关键问题不是"有没有过度工程",而是"我们增加的复杂度,是否购买了可以被验证的未来能力"。能验证的,是期权;不能验证的,是债务。
这里有个容易被忽略的时间维度:模型能力正在快速商品化。今天你花大力气适配的旗舰模型,几个月后可能就被替换。但 Harness 里沉淀的"在某类任务上怎么组织上下文、怎么兜底失败、怎么验证结果"是可跨模型复用的。AHE 的研究就报告了一个有启发性的事实:经过冻结的 harness,可以零成本迁移到其他模型家族,而迁移后依然保住在原模型上训练出的增益。换句话说,你在 Harness 上投入的复杂度,比你在某一个模型上投入的微调,半衰期更长。
六、不是所有复杂度都有资格叫"面向进化"
必须强调:给代码增加十层抽象,不会自动获得自进化能力。我见过不少项目,引入插件系统、事件总线、依赖注入,结果只是把"几行代码能解决的事"变成了"五个文件加一个配置文件",系统既没变聪明,也没变灵活。复杂度是进化的必要非充分条件。
有价值的复杂度应当满足四个条件。我把它们当作自进化 Harness 的"准入门槛":
1. 可观测
系统必须知道某次修改影响了哪些任务、成功率、成本、延迟和错误类型。没有观测,自进化就只是随机改代码。可观测性不是"打几行日志"那么简单,它要求轨迹是可结构化查询的------你能回答"过去 100 个任务里,有多少次失败发生在工具调用阶段"这种问题。
2. 可归因
每次变更都应附带明确假设,形如:
text
因为 Agent 经常遗漏测试,
所以增加结束前验证钩子,
预计任务成功率提高,
Token 成本最多增加 8%。
变更结果必须能与这项假设进行比较。假设可证伪,是自进化区别于"玄学调参"的根本。如果一次修改无法在事后被证明"当时预测的收益是否真的出现了",那它就不该被保留。
把这个假设落成一种工程结构,会比纯文本假设更可靠。下面是一个带明确假设的变更提案结构,字段化的好处是它能被程序自动比对:
python
# 带明确假设的变更提案结构(Python 示意)
proposal = ChangeProposal(
hypothesis="Agent 经常在任务结束前遗漏测试,导致未验证代码被标记为完成",
change="在 task lifecycle 的完成态前插入 CompletionGate 钩子",
expected_success_delta="+3pp on coding tasks",
token_cost_ceiling="<= +8% total tokens",
rollback="disable plugin ds-test-gate",
)
# 验证逻辑:跑完灰度后,用实测数据回填 proposal
proposal.observe(
actual_success_delta="+3.4pp",
actual_token_cost="+6.1%",
)
assert proposal.token_cost_ceiling_met()
3. 可回滚
新策略必须经过隔离测试、版本控制和灰度运行。失败时能够恢复到已知稳定状态。不可逆的"自我修改"不是进化,而是线上赌博。回滚能力在技术上的落点,就是第四章反复强调的"可撤销副作用"和 append-only 日志------日志让你知道改了什么,可撤销让你一键退回。
4. 可组合
能力应当能够独立安装、替换和卸载,而不是不断修改一个越来越庞大的核心模块。只有可组合的变化才能接受自动化搜索。这一点和 AHE 的主张完全呼应:让可修改组件显式化、将海量执行轨迹提炼成可用证据、让每次修改携带可以在后续任务中验证的预测。这样,Harness 演化才不会退化为缺乏方向的试错。
这里补一个量化"变更到底值不值"的工具:适应度函数。它把第六章的"可观测 / 可归因"变成可计算的分数。下面这个 Python 实现把成功率收益和各类成本放在同一把尺子上:
python
# 适应度函数 Fitness:成功率收益 - 各类成本(Python 示意)
def fitness(before, after, w=None):
w = w or {"success": 1.0, "token": 0.3, "latency": 0.2,
"complexity": 0.15, "regression": 0.5, "maintain": 0.25}
return (
w["success"] * (after.success - before.success)
- w["token"] * after.token_cost
- w["latency"] * after.latency
- w["complexity"] * after.complexity
- w["regression"] * after.regression_risk
- w["maintain"] * after.maintenance
)
# 只有 fitness(candidate) > fitness(baseline) 才允许进入下一轮灰度
适应度函数是第六章"可归因"在数值上的延伸:它把"这次修改划算吗"从一个模糊判断,变成了一个可比较、可审计的标量。
七、一个可落地的自进化 Agent 架构
企业如果准备探索 Agent 自进化,不必一上来就复制 DeepSeek Harness 的完整插件体系。更稳妥的做法,是先构建五个平面(plane)。平面之间职责分离,尤其是要把"提变化"和"证变化"拆到不同平面,这是整个架构的防自证循环核心。
#mermaid-svg-zkhjZVhyi4qIYHD2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zkhjZVhyi4qIYHD2 .error-icon{fill:#552222;}#mermaid-svg-zkhjZVhyi4qIYHD2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zkhjZVhyi4qIYHD2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .marker.cross{stroke:#333333;}#mermaid-svg-zkhjZVhyi4qIYHD2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zkhjZVhyi4qIYHD2 p{margin:0;}#mermaid-svg-zkhjZVhyi4qIYHD2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .cluster-label text{fill:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .cluster-label span{color:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .cluster-label span p{background-color:transparent;}#mermaid-svg-zkhjZVhyi4qIYHD2 .label text,#mermaid-svg-zkhjZVhyi4qIYHD2 span{fill:#333;color:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .node rect,#mermaid-svg-zkhjZVhyi4qIYHD2 .node circle,#mermaid-svg-zkhjZVhyi4qIYHD2 .node ellipse,#mermaid-svg-zkhjZVhyi4qIYHD2 .node polygon,#mermaid-svg-zkhjZVhyi4qIYHD2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .rough-node .label text,#mermaid-svg-zkhjZVhyi4qIYHD2 .node .label text,#mermaid-svg-zkhjZVhyi4qIYHD2 .image-shape .label,#mermaid-svg-zkhjZVhyi4qIYHD2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-zkhjZVhyi4qIYHD2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .rough-node .label,#mermaid-svg-zkhjZVhyi4qIYHD2 .node .label,#mermaid-svg-zkhjZVhyi4qIYHD2 .image-shape .label,#mermaid-svg-zkhjZVhyi4qIYHD2 .icon-shape .label{text-align:center;}#mermaid-svg-zkhjZVhyi4qIYHD2 .node.clickable{cursor:pointer;}#mermaid-svg-zkhjZVhyi4qIYHD2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .arrowheadPath{fill:#333333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zkhjZVhyi4qIYHD2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zkhjZVhyi4qIYHD2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zkhjZVhyi4qIYHD2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zkhjZVhyi4qIYHD2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .cluster text{fill:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 .cluster span{color:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zkhjZVhyi4qIYHD2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zkhjZVhyi4qIYHD2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-zkhjZVhyi4qIYHD2 .icon-shape,#mermaid-svg-zkhjZVhyi4qIYHD2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zkhjZVhyi4qIYHD2 .icon-shape p,#mermaid-svg-zkhjZVhyi4qIYHD2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zkhjZVhyi4qIYHD2 .icon-shape .label rect,#mermaid-svg-zkhjZVhyi4qIYHD2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zkhjZVhyi4qIYHD2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zkhjZVhyi4qIYHD2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zkhjZVhyi4qIYHD2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 门控评审
发布与回滚
执行平面
模型/工具/沙箱/会话
观测平面
轨迹记录与提取
经验平面
失败模式归纳
演化平面
提出候选方案
治理平面
证变化/批变化

1. 执行平面
负责模型调用、工具执行、任务规划、沙箱和会话管理,是 Agent 完成工作的主体。它应该是"最笨"的平面------只管执行,不做判断。执行平面产出的是任务结果与原始轨迹,不负责解释为什么失败。
2. 观测平面
记录完整执行轨迹,但不只是保存日志,还需要提取可被经验平面消费的结构化字段。我定义一个 Trajectory 接口,把观测平面的数据结构固定下来,这样后续所有分析都建立在一致 schema 上:
typescript
// 观测平面的轨迹数据结构(TypeScript interface 示意)
interface Trajectory {
taskType: string; // 任务类型,如 "refactor" / "debug"
failStage?: "plan" | "tool" | "verify" | "accept";
toolCalls: ToolCall[]; // 工具调用序列
retries: number; // 重试次数
tokenCost: number; // Token 成本
humanTakeover: boolean; // 是否发生人工接管
acceptance: "pass" | "fail"; // 最终验收结果
}
有了这个结构,经验平面才能回答"失败集中在哪个阶段""哪些任务类型最容易走到人工接管"。没有 schema 的轨迹只是文本废墟。
3. 经验平面
把单次失败归纳为跨任务重复出现的模式。它干的是"从个案到病理"的抽象工作。例如:
text
现象:Agent 经常在错误目录执行命令
原因:工作目录状态没有进入短期上下文
候选修复:执行命令前自动注入当前目录
适用范围:多仓库开发任务
经验平面的产出不是补丁,而是"病理报告"。这里要重点提 GSME(门控语义质量多样性,arXiv:2607.13683)的做法:它把"提修改"与"记功劳"严格分离------模型只负责诊断失败、提出补丁,所有采样、测量、显著性检验都由确定性代码负责。补丁按 (where × why) 的病理学进入归档(archive),既避免过拟合,又让同类失败能快速检索到历史补丁。其键设计如下:
python
# GSME 归档的键设计:(where × why) 病理学(Python 示意)
archive_key = (diagnosis.where, diagnosis.why)
# 例如 ("shell-exec", "missing-cwd-in-context")
# 查询:所有针对"错误目录执行命令"的历史补丁
patches = gsme.query(where="shell-exec", why="missing-cwd-in-context")
for p in patches:
print(p.patch, p.sealed_test_gain) # 历史增益可作为本次先验
GSME 在 7 个领域 sealed test 上取得 +9~+15.5pp 的增益,并保留了训练阶段增益的 86%~147%------说明这种"按病理归档、门控去重"的方式,确实比"见一个失败加一条规则"更抗过拟合。
4. 演化平面
根据经验提出修改。修改对象可以是 Prompt、Skill、工作流、工具中间件或插件。这个平面最好只负责"提出候选方案",不要同时掌握最终上线权。让模型既提方案又批准方案,等于让它给自己打分。
5. 治理平面
使用确定性程序完成测试、权限校验、统计评估、版本发布和回滚。换言之:
text
模型负责提出变化,
程序负责证明变化,
策略负责批准变化。
这个边界非常重要。让模型同时提出修改、选择测试数据、解释结果并批准上线,会产生严重的自证循环------它会"证明"自己刚才的修改是对的。治理平面用确定性代码打破这个循环。下面是一段灰度发布的 A/B 评测骨架:候选 Harness 与基线在同一任务集上各跑一遍,用显著性检验决定是否采纳:
python
# 确定性评测 / 灰度发布骨架:A/B 对比候选 Harness(Python 示意)
from scipy import stats
def ab_test(candidate, baseline, tasks, n=200, alpha=0.05):
a = run_harness(candidate, tasks) # 候选
b = run_harness(baseline, tasks) # 基线
# 两比例 z 检验,判断成功率差异是否显著
_, p = stats.proportions_ztest(
[a.passed, b.passed], [n, n]
)
adopt = (p < alpha) and (a.success > b.success)
return {
"adopt": adopt,
"p_value": p,
"delta_success": a.success - b.success,
"token_overhead": a.token_cost - b.token_cost,
}
# 只有 adopt=True 且 token_overhead 在预算内,才允许灰度放量
五个平面的职责边界,我用一张表收口,便于团队对照落地:
| 平面 | 职责 | 关键产出 |
|---|---|---|
| 执行平面 | 模型调用、工具执行、沙箱、会话 | 任务结果 + 原始轨迹 |
| 观测平面 | 轨迹记录与结构化提取 | 可查询的 Trajectory |
| 经验平面 | 失败模式归纳与病理归档 | (where×why) 病理报告 |
| 演化平面 | 提出候选修改方案 | 带假设的变更提案 |
| 治理平面 | 确定性测试、评估、发布、回滚 | 采纳/回滚决策与版本 |
这套架构最妙的地方在于:模型只在执行平面和经验平面(诊断)里活动,演化平面只"提"不"批",治理平面全是确定性程序。把"提出变化的模型"和"证明变化的程序"分到两个平面,系统的自证循环就被物理切断了。
八、警惕"进化"变成复杂度癌变
自进化系统最大的风险,可能不是 Agent 不会成长,而是它太擅长添加东西。每遇到一个失败案例就增加一条规则,听起来很勤奋,最终却可能得到一个肿瘤式的系统:数万字系统提示词;大量相互冲突的 Skill;无法理解的插件依赖;越来越长的执行链路;为极少数案例永久支付的 Token 成本;在评测集上不断提高、在真实业务中反而下降的系统。
这正是 GSME 用"门控"去对抗、AHE 用"冻结迁移"去收敛的原因------不加约束的演化必然走向熵增。因此,Agent 自进化必须同时具备"增"与"减"的能力。第六章给出的适应度函数,在这里要真正用起来:
text
Fitness
= 成功率收益
- Token 成本
- 延迟成本
- 复杂度成本
- 回归风险
- 维护成本
适应度函数的意义不只是"算分",更是给系统一个删除自己的理由。每个新增组件还应该带有"淘汰条件"。淘汰不是惩罚,而是让 Harness 保持年轻的机制。下面是一个淘汰条件的实现骨架:
python
# 组件"淘汰条件"实现(Python 示意)
def review_component(component, history, rounds=20):
# 1. 连续 N 轮未被调用 -> 进入观察名单
if component.calls_in_last(rounds) == 0:
component.move_to_observation()
# 2. 对成功率无显著贡献 -> 自动停用
if not contributes_to_success(component, history, alpha=0.05):
component.auto_disable()
return "disabled: no significant contribution"
# 3. 能被通用组件覆盖 -> 合并
if has_general_equivalent(component):
component.merge_into(general_component())
# 4. 只对单一基准有效 -> 禁止进入全局配置
if only_works_on(component, scope="single-benchmark"):
component.block_from_global_config()
return "kept"
这几条淘汰规则对应了第八章开头列举的每一种"癌变"症状:不被调用的组件占用认知;无贡献的组件虚增复杂度;可被通用能力覆盖的组件制造重复;只在一个基准上有效的组件是过拟合的典型信号。
真正成熟的自进化,不只是学会获得新能力,还要学会遗忘、压缩和重构。一个只会做加法的自进化系统,最终会把自己压垮;而能稳定做减法的系统,才可能是长期复利。
九、过度工程的正确打开方式
我并不建议所有团队立刻复制 DeepSeek Harness 的完整架构。更现实的做法,是根据系统的进化强度逐步投入------不要在没有评测能力时开放自修改,那等于给 Agent 一把扳手,却不告诉它机器什么时候修好了。我把演进路线分成六个阶段,每一阶段都有明确的进入下一阶段的前置条件:
#mermaid-svg-t0T5O2lbRIiMdt0D{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t0T5O2lbRIiMdt0D .error-icon{fill:#552222;}#mermaid-svg-t0T5O2lbRIiMdt0D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t0T5O2lbRIiMdt0D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t0T5O2lbRIiMdt0D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t0T5O2lbRIiMdt0D .marker.cross{stroke:#333333;}#mermaid-svg-t0T5O2lbRIiMdt0D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t0T5O2lbRIiMdt0D p{margin:0;}#mermaid-svg-t0T5O2lbRIiMdt0D .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D .cluster-label text{fill:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D .cluster-label span{color:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D .cluster-label span p{background-color:transparent;}#mermaid-svg-t0T5O2lbRIiMdt0D .label text,#mermaid-svg-t0T5O2lbRIiMdt0D span{fill:#333;color:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D .node rect,#mermaid-svg-t0T5O2lbRIiMdt0D .node circle,#mermaid-svg-t0T5O2lbRIiMdt0D .node ellipse,#mermaid-svg-t0T5O2lbRIiMdt0D .node polygon,#mermaid-svg-t0T5O2lbRIiMdt0D .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t0T5O2lbRIiMdt0D .rough-node .label text,#mermaid-svg-t0T5O2lbRIiMdt0D .node .label text,#mermaid-svg-t0T5O2lbRIiMdt0D .image-shape .label,#mermaid-svg-t0T5O2lbRIiMdt0D .icon-shape .label{text-anchor:middle;}#mermaid-svg-t0T5O2lbRIiMdt0D .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t0T5O2lbRIiMdt0D .rough-node .label,#mermaid-svg-t0T5O2lbRIiMdt0D .node .label,#mermaid-svg-t0T5O2lbRIiMdt0D .image-shape .label,#mermaid-svg-t0T5O2lbRIiMdt0D .icon-shape .label{text-align:center;}#mermaid-svg-t0T5O2lbRIiMdt0D .node.clickable{cursor:pointer;}#mermaid-svg-t0T5O2lbRIiMdt0D .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t0T5O2lbRIiMdt0D .arrowheadPath{fill:#333333;}#mermaid-svg-t0T5O2lbRIiMdt0D .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t0T5O2lbRIiMdt0D .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t0T5O2lbRIiMdt0D .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t0T5O2lbRIiMdt0D .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t0T5O2lbRIiMdt0D .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t0T5O2lbRIiMdt0D .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t0T5O2lbRIiMdt0D .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t0T5O2lbRIiMdt0D .cluster text{fill:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D .cluster span{color:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-t0T5O2lbRIiMdt0D .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t0T5O2lbRIiMdt0D rect.text{fill:none;stroke-width:0;}#mermaid-svg-t0T5O2lbRIiMdt0D .icon-shape,#mermaid-svg-t0T5O2lbRIiMdt0D .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t0T5O2lbRIiMdt0D .icon-shape p,#mermaid-svg-t0T5O2lbRIiMdt0D .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t0T5O2lbRIiMdt0D .icon-shape .label rect,#mermaid-svg-t0T5O2lbRIiMdt0D .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t0T5O2lbRIiMdt0D .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t0T5O2lbRIiMdt0D .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t0T5O2lbRIiMdt0D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 可记录
可复现
可配置
可插拔
可实验
有限自治
下面逐阶段展开,并把前置条件固化成一张表,方便团队自查:
- 第一阶段:可记录。保存任务输入、执行轨迹、工具结果和最终反馈。这是最便宜也最该先做的,没有轨迹就没有后面的一切。
- 第二阶段:可复现。为失败任务建立测试集,确保问题可以稳定复现。不能复现的失败无法被验证修复。
- 第三阶段:可配置。将 Prompt、工具、Skill 和工作流从核心代码中分离,改用配置驱动。这步是把"硬编码的能力"变成"可替换的能力"。
- 第四阶段:可插拔 。让能力能够独立注册、卸载、组合和回滚。对应第四章 Cordis 的
provide/onUnmount机制。 - 第五阶段:可实验。引入候选生成、自动评测、灰度发布和统计判断。对应第七章的 A/B 评测骨架。
- 第六阶段:有限自治。允许 Agent 在明确预算、权限和作用域内修改 Harness,但高风险变更仍需人工批准。
| 阶段 | 目标 | 进入下一阶段的前置条件 |
|---|---|---|
| 可记录 | 留存轨迹与反馈 | 任务输入/轨迹/结果可追溯 |
| 可复现 | 稳定复现失败 | 失败任务可重放且结果稳定 |
| 可配置 | 配置与核心解耦 | 改 Prompt/工具/工作流不碰源码 |
| 可插拔 | 能力独立装卸 | 插件卸载后无残留副作用 |
| 可实验 | 候选自动评测 | 具备显著性判定与灰度通道 |
| 有限自治 | 域内自修改 | 高风险变更保留人工审批闸 |
这个顺序很重要。没有评测系统就开放自修改,相当于给 Agent 一把扳手,却不告诉它机器什么时候修好了。很多团队翻车,不是因为自进化本身不可行,而是跳过了"可复现"和"可实验"直接冲到"有限自治",相当于地基没打就盖楼。
补充一张对比表,帮助团队判断自己到底该投入哪一层。Prompt Engineering 与 Harness Engineering 不是替代关系,而是覆盖范围不同的两件事:
| 维度 | Prompt Engineering | Harness Engineering |
|---|---|---|
| 修改对象 | 一段文本 | 系统结构与运行环境 |
| 生效范围 | 单次对话语义 | 跨任务、跨会话机制 |
| 成本 | 极低 | 较高 |
| 风险 | 低 | 中~高 |
| 跨模型复用 | 弱,换模型常失效 | 强,可冻结迁移 |
| 适合阶段 | 一~三阶段 | 四~六阶段 |
结论很清楚:小团队先吃透 Prompt Engineering 的红利,等到了"可插拔 / 可实验"阶段,再逐步把稳定有效的经验编译进 Harness。不要本末倒置,一上来就搭插件系统。
十、结语:未来的 Agent,竞争的是进化速度
基础模型仍然重要,但模型能力正在快速商品化。今天领先的模型,几个月后可能就会被替换。把护城河全押在模型上,风险越来越高。
真正能够长期积累优势的,可能是模型之外的部分:如何积累真实任务经验;如何把失败转化为结构化知识;如何把知识编译成 Prompt、Skill、工具和工作流;如何验证一次修改确实有效;如何让不同模型复用这些工程经验。这些能力的半衰期,比任何一个具体模型的半衰期都长。
DeepSeek Harness 最值得关注的地方,不是它用了多少插件,也不是 Cordis 的抽象有多复杂,而是它展示了一种可能性:Harness 不再只是承载模型的外壳,而可能成为 Agent 可以持续改造的"可进化身体"。当模型可以被随时替换,而 Harness 里沉淀的"怎么做事"越用越聪明时,系统的竞争力就不再绑定在某张权重表上。
在传统软件里,过度工程通常意味着为不存在的问题提前付费。但在自进化 Agent 中,如果这些抽象能够提供可观测、可替换、可组合和可回滚的能力,那么它们就不再只是冗余设计,而是系统为未来购买的进化空间。
当然,复杂度本身不是智能。无论你搭了多少平面、写了多少插件、画了多少架构图,只有当复杂度能够被测量、验证、淘汰和复用时,所谓"过度工程"才可能真正转化为智能系统的长期复利。否则,它只是另一种更体面的混乱。
最后给一句工程忠告:先让系统"可记录、可复现",再谈"可进化"。进化是结果,不是开关。
参考资料
- DeepSeek Harness:Everything is a plugin(官方介绍)--- https://www.deepseek.com/harness/en/
- DeepSeek Harness GitHub 仓库 --- https://github.com/deepseek-ai/deepseek-harness
- Agentic Harness Engineering (AHE, arXiv:2604.25850,复旦/北大) --- https://arxiv.org/abs/2604.25850
- Self-Evolving Agent Harnesses via Gated Semantic Quality-Diversity (arXiv:2607.13683) --- https://arxiv.org/abs/2607.13683
- Meta-Harness (Stanford, arXiv:2603.28052) --- https://arxiv.org/abs/2603.28052
- HarnessX (arXiv:2606.14249) --- https://arxiv.org/abs/2606.14249
- DarwinX (Salesforce, arXiv:2608.07545) --- https://arxiv.org/abs/2608.07545
- Ouroboros (arXiv:2608.08311) --- https://arxiv.org/abs/2608.08311
- One Recipe, Many Harnesses (arXiv:2608.10178) --- https://arxiv.org/abs/2608.10178