news 2026/10/20 12:03:18

Harness新方向:从DeepSeek Harness到Pi Durable,构建可复用的大模型执行骨架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Harness新方向:从DeepSeek Harness到Pi Durable,构建可复用的大模型执行骨架

1. 三个名字背后其实是同一件事:把模型装进可复用的工作流骨架

第一次看到 DeepSeek Harness、Claude Code Mods、Pi Durable 这三个词摆在一起,我下意识觉得它们分属三个赛道:一个是模型侧的评测框架,一个是编辑器侧的插件扩展,一个是运行时侧的持久化方案。但把三者的设计文档和实际代码翻过一遍之后,我发现它们解决的是同一个问题——如何让大模型从"一次性对话"变成"可反复调用的稳定执行单元"。这个方向,我把它统称为 Harness 新方向。

Harness 这个词原本在测试领域指"测试夹具",就是给被测对象搭一个可控的外壳,让它能在固定条件下跑起来。放到大模型场景里,Harness 的职责变成了:给模型套一层执行骨架,把输入、工具调用、状态管理、错误重试、结果校验这些环节全部固定下来,让模型每次运行的行为可预期、可复现、可调试。DeepSeek Harness 偏重模型能力评测与任务编排,Claude Code Mods 偏重编辑器内的行为定制,Pi Durable 偏重长时间运行的状态保持,三者的切入点不同,但骨架结构高度相似。

为什么这个方向突然集中爆发?我的判断是:单次对话的价值已经被榨干了。过去两年大家比拼的是模型能不能答对一道题、能不能写出一段代码,现在这些能力已经趋于同质化,真正的差距转移到了"能不能稳定地完成一件多步骤的事"上。而多步骤任务天然需要 Harness——你需要记住上一步做了什么、需要调用外部工具、需要在失败时回退、需要在结束时验证结果。没有 Harness,模型再强也只是个会聊天的计算器。

这篇文章适合两类人看:一类是正在做 AI 应用落地、被"模型输出不稳定"折磨过的工程师;另一类是想理解当前 AI 工程化趋势、但被各种新名词绕晕的技术决策者。我会把三个项目的核心机制拆开讲清楚,再给出一个可以自己动手搭的最小 Harness 骨架,最后聊聊我在实际搭建过程中踩过的坑。全文不涉及任何具体平台的部署细节,只讲通用原理和可复现的工程方法。

2. 拆开看三个项目:它们各自解决了 Harness 的哪一块拼图

2.1 DeepSeek Harness:把评测从"打分"变成"可复现的执行"

DeepSeek Harness 最容易被误解成"又一个评测集"。但如果你真的用它跑过任务,会发现它的核心不是题库,而是执行器。它定义了一套任务描述格式,每个任务包含:初始状态、可用工具集、成功判定条件、最大步数限制。模型在每一轮只能做一件事——要么输出一个工具调用,要么输出最终答案。Harness 负责执行工具调用、更新状态、判断是否达到终止条件。

这个设计的关键在于把模型的自由输出约束成了有限动作空间。没有 Harness 的时候,模型可能在一轮里既想调用工具又想给答案,还可能输出格式错误的调用请求,导致整个流程卡死。有了 Harness,模型每轮只能选一个动作,格式由 Harness 校验,错了就重试,重试次数超限就判定任务失败。这样一来,评测结果就不再依赖"模型这次心情好不好",而是变成了一个可统计的通过率。

我实测下来,这种约束带来的最大收益是失败可归因。传统评测里模型答错了,你只知道它错了,不知道错在哪一步。Harness 模式下,你可以看到它在第几步选错了工具、第几步状态更新出错、第几步提前终止。这对于定位模型能力短板极其有用。比如某个模型在"需要连续调用三次工具"的任务上通过率骤降,你就能判断它的多步规划能力有问题,而不是笼统地说"这个模型不行"。

2.2 Claude Code Mods:把编辑器行为变成可插拔的规则集

Claude Code Mods 的定位更贴近日常开发。它做的事情是:允许你修改编辑器内 AI 助手的行为规则,包括系统提示词、工具白名单、上下文注入策略、输出格式约束。你可以把它理解成给编辑器里的 AI 装了一套"行为配置文件",不同项目可以加载不同的 Mods,从而让同一个模型在不同项目里表现出不同的工作方式。

这个设计的精妙之处在于把提示词工程从代码里剥离出来。以前你要改 AI 的行为,得去改源码里的 prompt 字符串,改完还要重新构建。现在你只需要写一个 Mods 配置文件,声明"在这个项目里,AI 只能读取 src 目录、只能执行测试命令、输出必须带文件路径"。编辑器加载配置后,AI 的行为立刻改变,不需要动一行代码。

我试过在一个前端项目里用 Mods 限制 AI 只能操作组件目录,结果它再也不会误改构建配置和依赖锁文件。这个约束看起来简单,但实际收益很大——AI 出错的高发区往往不是它不会写代码,而是它动了不该动的东西。Mods 通过白名单机制把这种风险提前掐掉了。从 Harness 的视角看,Claude Code Mods 解决的是"执行边界"问题,它定义了模型能做什么、不能做什么,这是 Harness 骨架里必不可少的一环。

2.3 Pi Durable:让执行状态在进程重启后还能接上

Pi Durable 关注的是另一个维度:持久化。前面两个项目解决的是"怎么执行"和"执行什么",Pi Durable 解决的是"执行到一半挂了怎么办"。它把 Harness 的每一步状态都落盘,包括当前步数、已调用工具、中间结果、待办事项。进程重启后,Harness 可以从上次落盘的位置继续执行,而不是从头再来。

这个能力在短任务里看不出价值,但在长任务里是刚需。比如一个需要跑二十分钟的数据处理任务,跑到第十五分钟时进程被系统回收,没有持久化就只能重跑,前面十五分钟白费。有了 Pi Durable,重启后从第十五分钟继续,用户几乎无感。从工程角度看,持久化把 Harness 从"一次性脚本"升级成了"可恢复服务",这是它能否进入生产环境的关键门槛。

2.4 三者的共同骨架:输入、动作、状态、校验、恢复

把三个项目放在一起看,Harness 的通用骨架就清晰了:

环节DeepSeek HarnessClaude Code ModsPi Durable
输入定义任务描述格式Mods 配置文件任务初始化参数
动作空间工具调用 + 最终答案白名单工具集步骤定义
状态管理内存状态机编辑器上下文落盘状态
结果校验成功判定条件输出格式约束步骤完成标记
失败恢复重试 + 终止规则拦截断点续跑

这张表说明一件事:Harness 不是某个具体产品,而是一组设计模式的集合。你可以只实现其中一部分,也可以全部实现,但只要你开始把模型输出约束成有限动作、开始管理执行状态、开始做失败恢复,你就在做 Harness 了。三个项目只是从不同角度把这个骨架做深做透。

3. 为什么现在必须做 Harness:单次对话的天花板已经摸到了

3.1 模型能力同质化之后,差距转移到了工程侧

过去一年,主流模型在标准评测集上的分数差距越来越小。同一个编程题,几个模型都能做对;同一段文本总结,几个模型输出质量差不多。这意味着模型本身不再是差异化来源。真正拉开差距的是:谁能把模型稳定地接入业务流程,谁能让模型在无人值守的情况下完成多步骤任务,谁能在模型出错时快速恢复。

我见过太多团队卡在同一个地方:Demo 阶段效果惊艳,一上生产就各种翻车。翻车原因几乎都不是模型能力不够,而是没有 Harness。模型输出格式偶尔不对、工具调用偶尔失败、长任务偶尔中断,这些在 Demo 里被人工兜住了,在生产里没人兜,就变成了故障。Harness 的价值就是把这些"偶尔"变成"可控"。

3.2 多步骤任务的失败率是指数级放大的

假设单步成功率是 95%,看起来很高。但一个需要十步的任务,整体成功率是 0.95 的十次方,约等于 60%。二十步的任务,成功率降到 36%。这就是为什么很多 AI 应用"看起来能用,实际不能用"——步骤一多,失败率就爆炸。

Harness 通过三个机制对抗这种放大:第一,每步校验,错了立刻重试,不让错误累积;第二,状态持久化,中断后能恢复,不浪费已完成步骤;第三,动作空间约束,减少模型选错动作的概率。这三个机制叠加,能把十步任务的成功率从 60% 拉回到 90% 以上。这不是模型变强了,是工程手段把不确定性压下去了。

3.3 生产环境要的不是"聪明",是"可预期"

我在实际项目里最大的体会是:生产环境对可预期性的要求远高于对智能程度的要求。一个每次都能正确完成简单任务的系统,比一个偶尔能完成复杂任务但行为飘忽的系统有价值得多。因为前者可以接入自动化流程,后者只能当玩具。

Harness 的本质就是给模型加约束,让它从"聪明但不可控"变成"能力有限但可预期"。这个取舍在 Demo 阶段看起来是退步,在生产阶段是刚需。DeepSeek Harness 用有限动作空间换可统计性,Claude Code Mods 用白名单换安全性,Pi Durable 用落盘换可恢复性,都是在做这个取舍。

4. 动手搭一个最小 Harness:从零到能跑通多步任务

4.1 先定义任务格式,别急着写执行逻辑

很多人搭 Harness 的第一反应是写执行循环,结果写到一半发现任务描述格式没定好,执行逻辑反复改。我的建议是先把任务格式定死。一个最小任务描述应该包含四个字段:

task = { "task_id": "demo_001", "initial_state": {"files": [], "context": ""}, "tools": ["read_file", "write_file", "run_test"], "success_condition": "all_tests_pass", "max_steps": 10 }

这四个字段分别对应:任务标识、初始状态、可用工具、成功条件、步数上限。定好之后,执行逻辑就围绕这个格式展开,不会再反复改结构。我踩过的坑是:一开始没定max_steps,结果模型陷入死循环,跑了上百步还在原地打转,浪费了大量调用额度。加上步数上限后,超限直接判定失败,问题立刻暴露。

4.2 执行循环的核心:每轮只允许一个动作

执行循环是整个 Harness 的心脏。它的逻辑很简单,但细节很多:

def run_harness(task, model): state = task["initial_state"] for step in range(task["max_steps"]): prompt = build_prompt(state, task["tools"]) response = model.generate(prompt) action = parse_action(response) if action is None: state = record_error(state, "parse_failed") continue if action["type"] == "final_answer": if check_success(state, task["success_condition"]): return {"status": "success", "state": state} else: state = record_error(state, "premature_final") continue result = execute_tool(action, state) state = update_state(state, action, result) save_checkpoint(state) return {"status": "failed", "state": state}

这段代码里有三个关键点。第一,parse_action必须严格,解析失败就记错误并继续,不能让格式错误直接崩溃。第二,final_answer不是无条件接受,必须通过check_success校验,防止模型提前交卷。第三,每步之后save_checkpoint,这是持久化的基础。

我实测下来,parse_action的严格程度直接决定 Harness 的稳定性。早期我用宽松解析,模型输出格式稍微偏一点也能过,结果后面状态更新经常出错。改成严格解析后,格式错误在第一步就被拦住,反而更容易定位问题。

4.3 状态设计:只存必要信息,别把整个上下文塞进去

状态设计是 Harness 里最容易做错的地方。新手常犯的错误是把完整对话历史塞进状态,导致状态越来越大,落盘慢、恢复慢、prompt 也越来越长。正确的做法是只存执行必需的信息:当前步数、已完成动作列表、关键中间结果、待办事项。

state = { "step": 3, "completed_actions": [ {"step": 1, "tool": "read_file", "result": "..."}, {"step": 2, "tool": "write_file", "result": "ok"} ], "key_results": {"file_content": "..."}, "pending": ["run_test"] }

这样设计的好处是状态体积可控,落盘和恢复都快。completed_actions用于回溯和调试,key_results用于后续步骤引用,pending用于恢复时知道下一步该做什么。完整对话历史不存,需要的时候从completed_actions重建即可。

4.4 失败恢复:从 checkpoint 恢复而不是从头重跑

持久化的价值在恢复时体现。恢复逻辑应该读取最近的 checkpoint,重建状态,然后从下一步继续:

def resume_harness(task_id, model): checkpoint = load_latest_checkpoint(task_id) if checkpoint is None: return run_harness(load_task(task_id), model) state = checkpoint["state"] task = load_task(task_id) for step in range(state["step"], task["max_steps"]): # 从 state["step"] 继续执行 ...

这里有个细节:恢复时步数要从 checkpoint 里的step开始,而不是从 0 开始。否则步数上限会被重复计算,长任务永远跑不完。我踩过这个坑,一个二十步的任务恢复三次后步数就超限了,排查了半天才发现是步数没接上。

5. 实际搭建中踩过的五个坑和对应的解法

5.1 坑一:工具返回值太大,把 prompt 撑爆

第一个坑是工具返回值没有做截断。比如read_file读了一个大文件,直接把全文塞进状态,下一步构建 prompt 时上下文超限,模型直接报错。解法是在工具层做返回值截断,只返回关键片段,完整内容存到状态里按需引用。

def read_file(path, max_chars=2000): content = open(path).read() if len(content) > max_chars: return { "truncated": True, "preview": content[:max_chars], "full_ref": path } return {"truncated": False, "content": content}

这样模型看到的是预览,需要完整内容时再通过full_ref二次读取。实测下来,这个改动让长任务的成功率提升明显,因为 prompt 不再被大返回值撑爆。

5.2 坑二:重试没有上限,模型陷入死循环

第二个坑是重试逻辑没有上限。模型某一步选错工具,Harness 重试,模型又选错,再重试,无限循环。解法是给每个动作加重试计数,同一个动作连续失败三次就跳过,记录错误并继续下一步。

retry_count = state.get("retry_count", {}) action_key = f"{action['type']}_{action.get('tool', '')}" retry_count[action_key] = retry_count.get(action_key, 0) + 1 if retry_count[action_key] > 3: state = record_error(state, "max_retry_exceeded") continue

这个机制看起来简单,但实际效果很好。模型在同一个动作上卡住时,跳过它往往能让模型换一条路走,反而绕过了原来的障碍。

5.3 坑三:成功条件写得太松,模型提前交卷

第三个坑是成功条件写得太宽松。比如任务要求"所有测试通过",但成功条件只检查了"测试命令执行完成",结果模型跑完测试没看结果就交卷了。解法是成功条件必须校验实际结果,不能只看动作是否执行。

def check_success(state, condition): if condition == "all_tests_pass": test_result = state["key_results"].get("test_output", "") return "FAILED" not in test_result and "PASSED" in test_result return False

成功条件写得越具体,模型越难钻空子。我现在的习惯是:每个任务的成功条件都要能用一个明确的布尔表达式描述,模糊的条件一律不接受。

5.4 坑四:checkpoint 落盘太频繁,IO 成为瓶颈

第四个坑是持久化做得太激进,每一步都落盘,结果 IO 成为瓶颈,任务执行速度被拖慢。解法是按需落盘:关键步骤落盘,普通步骤攒一批再落。

def save_checkpoint(state, force=False): if force or state["step"] % 5 == 0: write_to_disk(state)

关键步骤包括:工具调用完成后、状态发生重大变化时、任务即将进入长耗时操作前。普通步骤可以攒五步落一次。这样既保证了恢复能力,又不至于被 IO 拖垮。

5.5 坑五:恢复后模型"失忆",不记得之前做了什么

第五个坑是恢复后模型不知道之前发生了什么。因为状态里只存了completed_actions,但构建 prompt 时没有把这些动作重新注入,模型以为自己是第一次执行。解法是恢复时重建上下文,把已完成动作摘要注入 prompt。

def build_prompt(state, tools): history_summary = "\n".join([ f"Step {a['step']}: {a['tool']} -> {a['result'][:100]}" for a in state["completed_actions"] ]) return f"已完成步骤:\n{history_summary}\n\n当前状态:{state['key_results']}\n\n可用工具:{tools}"

这样模型恢复后能看到之前做了什么,不会重复劳动。实测下来,这个改动让恢复后的任务成功率接近从头执行的水平。

6. 从最小 Harness 到生产级 Harness 还差什么

6.1 可观测性:没有日志的 Harness 等于黑盒

最小 Harness 能跑通任务,但一旦出问题就很难排查。生产级 Harness 必须有一套完整的日志体系,记录每一步的输入、输出、耗时、状态变化。我现在的做法是结构化日志,每条日志是一个 JSON,包含step、action、result、duration、state_hash字段。这样出问题时可以直接按state_hash回溯到具体哪一步状态变了。

import json, time def log_step(step, action, result, state): entry = { "step": step, "action": action, "result": str(result)[:500], "duration": time.time() - step_start, "state_hash": hash_state(state) } logger.info(json.dumps(entry))

有了这套日志,排查问题时不用猜,直接看哪一步的state_hash和预期不符,就能定位到具体动作。

6.2 并发控制:多个任务同时跑时的资源隔离

单任务跑通之后,下一步是并发。多个 Harness 实例同时跑,会争抢工具资源、模型调用额度、磁盘 IO。生产级 Harness 需要资源隔离和限流。我的做法是给每个任务分配独立的工具沙箱和状态目录,模型调用走统一队列,避免并发过高被限流。

class TaskSandbox: def __init__(self, task_id): self.task_id = task_id self.work_dir = f"/tmp/harness/{task_id}" self.state_file = f"{self.work_dir}/state.json" os.makedirs(self.work_dir, exist_ok=True)

每个任务有自己的工作目录,工具操作限制在目录内,互不干扰。模型调用通过队列串行化,控制并发数。

6.3 成本控制:步数、token、工具调用都要有预算

生产环境里成本是硬约束。一个失控的 Harness 可能烧掉大量调用额度。生产级 Harness 需要预算机制:每个任务有 token 预算、步数预算、工具调用预算,超预算直接终止。

budget = { "max_tokens": 100000, "max_steps": 20, "max_tool_calls": 50 } def check_budget(state, budget): if state["tokens_used"] > budget["max_tokens"]: return False if state["step"] > budget["max_steps"]: return False if state["tool_calls"] > budget["max_tool_calls"]: return False return True

预算机制让 Harness 的行为可预测,不会因为某个任务失控而影响整体成本。

6.4 版本管理:任务格式和工具集变更时的兼容

Harness 跑久了,任务格式和工具集会变。生产级 Harness 需要版本管理,每个任务记录自己使用的格式版本和工具版本,恢复时按对应版本加载。否则格式一变,老任务的 checkpoint 就恢复不了。

task = { "task_id": "demo_001", "format_version": "1.2", "tool_version": "2.0", ... }

恢复时先检查版本,版本不匹配就走迁移逻辑或拒绝恢复。这个机制在长期运行的系统里必不可少。

7. 我对 Harness 方向后续演进的一些判断

从 DeepSeek Harness、Claude Code Mods、Pi Durable 这三个项目出发,我判断 Harness 方向接下来会往三个方向走。第一是标准化,目前每个项目都有自己的任务格式和状态定义,未来可能会出现通用的 Harness 协议,让任务描述和状态可以在不同实现之间迁移。第二是可视化,现在调试 Harness 主要靠看日志,未来会有更直观的执行轨迹可视化工具,让开发者一眼看出模型在哪一步走偏了。第三是自适应,Harness 会根据任务难度动态调整步数上限、重试策略、工具集,而不是所有任务用同一套配置。

我在实际搭建中的体会是:Harness 的复杂度不在于代码量,而在于边界情况的处理。一个能跑通 Demo 的 Harness 可能只有两百行代码,但要让它稳定跑在生产环境,需要处理的边界情况可能是两千行。这些边界情况包括:工具返回异常、模型输出格式错误、状态落盘失败、恢复时版本不匹配、并发资源争抢、预算超限。每处理一个,Harness 的稳定性就上一个台阶。

最后分享一个小技巧:在 Harness 里加一个"干跑"模式,只构建 prompt 和执行动作解析,不真正调用工具和模型。这个模式用于验证任务格式和工具定义是否正确,能在正式跑任务之前发现大部分配置错误。我现在的习惯是每个新任务先用干跑模式过一遍,确认无误再正式执行,省下了大量调试时间。

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

Qwen2.5-VL-7B LoRA微调实战:从原理到训练全流程指南

开始前先说一句:这篇是 Qwen2.5-VL-7B 模型低秩适配 LoRA 微调方法应用连载的第八篇,前面几篇已经零零散散聊过部署、数据、评估这些东西,这一篇想把这些内容收敛到一条能从零跑通的实战链路上。Qwen2.5-VL-7B 是目前在文档理解、截图问答、智…

作者头像 李华
网站建设 2026/10/20 11:59:37

OpenClaw 在 Windows 报错 1006?从VC++运行库到PowerShell脚本的排错实战

如果安装脚本甩给你一行Error 1006 (Unknown error),在 Windows 上装 OpenClaw 的体验基本就到此为止了。我最近在自己的一台 Windows 11 工作站上跑 OpenClaw 做定时采集时,就撞上了这个问题——进度条走到 73% 戛然而止,没有日志提示、没有…

作者头像 李华
网站建设 2026/10/20 11:58:21

WinForm动态加载界面:从JSON驱动到插件化架构的完整实战

做WinForm开发的朋友,迟早会碰到“Winform动态加载界面”这种需求。我说得直白点,就是你程序里的窗体、控件、用户控件,不再靠设计器里鼠标拖拽、提前画死,而是程序运行起来以后,根据配置、数据、插件,在运…

作者头像 李华
网站建设 2026/10/20 11:57:59

敏捷开发中需求审批与变更管理的轻量落地实践

1. 先搞清楚:敏捷到底在反对什么前不久,一个做金融系统的团队在迭代计划会上吵了起来。产品经理临时提了一个需求,说要加一个“资金流水导出”的按钮,开发组长立刻跳出来反对:“Sprint 都已经排满了,这是加…

作者头像 李华
网站建设 2026/10/20 11:57:44

16|从流程到 Agent:理解 ChatModelAgent 与 ReAct - eino教程

16|从流程到 Agent:理解 ChatModelAgent 与 ReAct 《Eino 实战:用 Go 构建 AI 应用与智能体》 第 16 篇 关键词:ADK、ChatModelAgent、ReAct、Runner、AgentEvent、工具反馈 前置条件:已完成 11|Tool 与 To…

作者头像 李华
网站建设 2026/10/20 11:55:24

堆排序原地建堆原理与实战避坑指南

1. 堆不是“堆”出来的,是“建”出来的:先破除三个常见误解很多人第一次看到“建堆”这个词,下意识觉得就是把一堆数随便摞起来——就像把书本胡乱堆在桌上,高矮不一、重心不稳,风一吹就塌。但计算机里的“堆”&#x…

作者头像 李华