长任务做到一半失控这件事,我用 DeepSeek Harness 踩过太多次了:上一个小时它还在按需求文档改接口,下一个小时就自己发明了一个新架构,把原本说好不动的地方全给重构了。后来我把版本升到 2026 年 9 月这一版,才真正把 goal、compact、后台 job 这三件套用顺,长任务终于有点"从一开始就知道要干什么、干到哪一步、然后干完"的意思了。
这篇东西不是官方教程,是我用下来之后的经验整理。适合已经装了 DeepSeek Harness、跑过一些简单任务,但一碰到大任务就发现对话越聊越乱、越跑越慢、甚至直接报错的人。读完你会知道:goal 怎么拆才不容易跑偏,compact 到什么程度该按下去、按下去之后报错怎么排查,后台 job 到底在什么场景下才值得用。
1. 长任务失控的三个信号:上下文越跑越窄,问题出在哪
先说个基础判断:agent 类的编程工具卡在长任务上,绝大多数时候不是它"变笨了",而是上下文窗口被历史对话占满了。模型每次生成回复都要把之前的对话重新读一遍,窗口是有限的,前面的内容不会真正消失,但会被挤到注意力范围之外。表现在行为上就是你熟悉的三个信号。
第一个信号是遗忘早期约束。任务开头你明确说过"不要动 database 层的接口",跑了二十分钟之后它开始改 database 层的代码。不是它故意不听话,是那条指令在上下文里已经被几百轮工具调用记录淹没了。第二个信号是重复劳动。同样一个编译错误,它第一次修完,过了几轮又用另一种方式修一遍,因为后续的对话里已经没有"这个问题已经解决过"的记录了。第三个信号是最容易发现的:速度变慢、token 消耗变快。上下文越长,每次请求处理的时间越长,如果你用的是按 token 计费的服务,账单也会跟着涨。
这三件事本质上都是同一个问题:上下文卫生没做好。你不可能指望模型在塞满五万字历史的情况下还保持和开头一样的约束感。所以三件套的设计逻辑就很清楚了——goal 管的是"任务边界",让一段对话有明确的结束点,不无限累积;compact 管的是"上下文压缩",把该记的记下来,把不该占空间的旧对话清掉;后台 job 管的是"执行方式",让长任务不在前台占着你的人,也不被一次断连全部带走。
我见过很多人把这三样当成三个孤立功能:goal 是为了好看,compact 是报错了才用,job 只是"挂起"的意思。实际用下来它们是一套组合拳,单独用哪个都差点意思。后面我会按顺序拆开讲,再给一个完整走位。
2. goal 目标轮次:把大任务拆成"赢一局算一局"的回合制
2.1 goal 到底解决的是什么问题
一句话:goal 是给 agent 一个明确的"当前这一局"的定义。它不只是把任务描述写得更详细,而是把一整段无边界的长对话切成一段段有验收标准的短对话。
我打个比方。不加 goal 的长任务,像把一个人关在房间里说"把这套系统做完",它只能根据自己的想象往前冲,遇到岔路口就自己选一个方向,选错了你也不知道。加了 goal 之后,等于告诉它"这一局只做 X,做完就算赢,不需要考虑 Y 和 Z"。每条任务都有一条明确的终点线,到了终点就停下来汇报,由你来判断要不要开下一局。这样即使它中途跑偏,偏离的距离也只是一局的长度,而不是整个项目的长度。
DeepSeek Harness 里 goal 的核心操作是创建时就带一个"完成标志"。我看到不少人是这么用的:
deepseek-harness goal start "重构 billing 模块的支付回调,并保证现有测试全部通过"这个描述比"继续改项目"强得多,但还不够。更好的写法是把验收标准也塞进去:
deepseek-harness goal start "抽出 BillingService 接口,迁移支付回调到此接口,跑通 pytest tests/billing 下全部用例"目标越能检验,agent 越知道什么时候该停。它不需要自己揣测"这样算不算完",跑完 pytest 看到全绿就是完。
2.2 goal 的常用命令和状态管理
我日常高频使用的是这几个命令:
deepseek-harness goal start "目标描述" deepseek-harness goal status deepseek-harness goal list deepseek-harness goal stopgoal start创建并立即进入一个新目标;goal status查看当前目标进行到哪一步;goal list看这个会话里所有目标的完成情况;goal stop手动终止当前目标。如果你想开下一个目标,直接在对话里说"这个目标已经完成了,接下来我们做下一项",它会自动归档当前 goal 并记录一个完成结论。
一开始我不太理解为什么要维护这个"目标状态",后来发现它最大的价值在于:每次切换目标时,agent 会主动写一段简短的阶段性总结,这段总结在后面对话里非常有用。因为它相当于给上下文打了一个"抽屉隔断",前面那一堆过程细节可以慢慢被遗忘,留下的只有目标和结果。
2.3 拆 goal 的三个原则,按这三个来基本不会跑偏
第一个原则是"一个 goal 的体量不要超过一个上下文窗口"。如果你预感到这个目标要连续改二十个文件,那它就不该是一个 goal,而是三个。我不太纠结具体的 token 数字,一般差不多跑了三分之一任务的时候上下文已经过半,就该考虑这个 goal 是否要收尾或者主动压缩一次。第二个原则是"服务端验证优先"。能跑测试验证的目标,一定写测试验证;不能跑测试的,写明确的 diff 验收点。第三个原则是允许失败停局。goal stop不止在跑偏的时候用,我发现最实用的用法是:任务进行到一个大岔路口时,手动停掉当前 goal,把目前的进展总结成上下文,再开一个新 goal 走另一边。这比让它自己头铁冲到底省心得多。
说实话我刚开始用 goal 的时候,感觉它像多此一举——反正都是同一个对话窗口。但用了一周之后我的体感是:有目标边界的对话,回复质量比无边界的对话高一大截。模型不需要一直扮演"全能开发者"默认完成所有事,它只需要专注当前这一局。
3. compact 上下文压缩:原理、触发时机,以及 remote compact task 报错的完整排查
3.1 压缩的原理,和"直接截断"的差别
compact 做的是把已有的对话历史压缩成一份结构化摘要,然后用这份摘要替代原始对话继续往下走。它和"把旧消息删掉"的本质差别在于:截断之后模型什么都记不得了,而摘要至少保留了决策、文件路径、约束条件、已完成事项这些关键信息。
DeepSeek Harness 的 compact 有个比较实用的设计,就是允许你在压缩的时候手动指定要保留的内容。我经常用这个来"钉住"几条绝对不能忘的约束:
deepseek-harness compact --preserve "billing 模块接口不得改动, 依赖锁定文件不可手动修改, 所有新代码必须兼容 python3.10"用不用--preserve差别很大。不指定时,压缩按优先级把最重要的留在摘要里;指定之后,相当于你给压缩过程画了几个重点,让它把这几条原样保留而不是靠自动摘要去提炼。
3.2 什么时候该压缩,别等到报错才想起来
我见过最多的错误用法是:等到模型开始胡言乱语、或者收到"context too long"之类的提示才去压缩。那个节点的上下文常常已经接近窗口上限,压缩出来的摘要会非常粗糙,因为大量中间信息已经被压得只剩骨架。
我的建议是一旦上下文用量超过窗口的 60% 就主动压缩。尤其在你刚完成一个 goal 的收尾、准备开下一个 goal 的时候,那是压缩的最佳时机——旧的对话正好有了阶段性结论,压缩成本低、收益高。
这里还要区分 local 和 remote 两种模式。本地模型跑 compact 时,压缩是本地完成的,速度取决于你的显存和算力;远端模型跑 compact 时,Harness 会把上下文发给远端服务处理,这个场景比较容易撞上各种 remote compact task 报错。
3.3 remote compact task 错误排查表(这部分真得把你常遇到的全列出来)
热搜里那一串 remote compact task 报错,我基本都撞过一遍。下面这个表是我自己的排查结论,按"错误长什么样 -> 最可能的原因 -> 我推荐的解法"来组织。
| 报错信息 | 常见原因 | 我推荐的解法 |
|---|---|---|
| selected model is at capacity. please try a different model. | 当前模型服务已满载,排队都排不上 | 换一个模型再跑 compact;或者在配置里把 compact 模型和主对话模型分开设置 |
| exceeded retry limit, last status: 429 | 请求频率超过服务端限额,触发限流 | 先停 30 到 60 秒,不要连续重试;检查是不是多个 job 同时触发了 compact |
| connection failed: error sending request | 网络链路不通、服务地址配错、本地模型服务没起来 | 先确认模型服务端口存活;再排查网络出口和防火墙策略是否放行目标域名;本地模型就检查进程启动日志 |
| stream disconnected before completion | 流式响应中途被断开,可能是网络抖动或服务端主动断开 | 重跑一次;如果反复出现,降低上下文规模再 compact,比如先手动精简一部分日志输出 |
| your access token could not be refreshed | 登录凭据过期,刷新失败 | 重新登录一次,更新 token 后再跑 |
| fatal error | 信息太少,一般是环境或配置层面的问题 | 去看~/.deepseek-harness/logs下最新的日志,搜compact关键字,多半有堆栈 |
遇到 429 的时候我最大的教训是:不要写个自动重试脚本去猛冲。限流这种东西本来就是越冲退避时间越长,手动等一分钟再试,成功率反而高。容量报错则要区分是服务端全局满载还是你配置的模型不行,后者换一个模型名立刻就好。
3.4 压缩完了,质量下降怎么办
compact 不是无代价的。压缩本质上是信息有损的,特别是中间态很强的时候——比如某次调试过程里你试了五个方案,最后只留了一个,摘要里就只会有方案五,中间的试错路径全部丢掉。大部分情况下这是好事,但有时候你后面需要知道"为什么排除了方案二",这时候摘要帮不上忙。
我的应对是两层。第一层是在压缩之前,把重要的试错结论手动写进--preserve,相当于给未来的自己留个小纸条。第二层是压缩之后做一个"回读校验":让 agent 复述一下当前任务状态、已完成的改动清单、以及接下来要做的第一步。如果复述得清楚,说明摘要质量可以;如果复述出来和你印象里的偏差很大,立刻重新补充--preserve后再压缩一次。
很多搜索 remote compact task 报错的人,其实是把压缩当成"点一下就行"的魔法按钮。实际它更像搬家打包:打包前先想好哪些东西要随身带,哪些可以放进仓库,这样到了新家才不用到处翻。
4. 后台 job:把长任务"挂"起来之后,如何收尾和交付
4.1 什么任务值得用后台 job,什么任务没必要
后台 job 解决的核心痛点是:长任务不应该占住你的交互循环。比如跑一个一小时的测试回归、批量重构几十个文件的格式化与导入整理、或者让 agent 按模板生成一大批文档,这些任务你不需要实时盯着,适合用 job 挂到后台。
但我要说个反直觉的结论:不是所有长任务都适合后台。凡是需要中途做方向性决策的任务,比如"重构过程中你发现原架构行不通,需要调整方案"这种,挂后台反而危险——它会在没有你确认的情况下自己做决定。后台任务的边界应该是"过程明确、结果可验证"的任务,而不是"探索性任务"。我习惯把 job 当作每一步都确定、只是耗时很长的执行器,而不是另一个可以做主的 agent。
4.2 后台 job 的常用命令
我常用的命令是这几个:
deepseek-harness job start "运行 billing 模块全量回归测试并汇总失败用例" deepseek-harness job list deepseek-harness job attach <job-id> deepseek-harness job stop <job-id>job start会返回一个 job id,job list能看到所有正在运行和已结束的 job,job attach可以重新连回一个在跑的 job 看实时输出,job stop用来终止。这个设计很像你在终端里开了一个tmux,只是背后跑的不是普通脚本,而是一个有上下文理解能力的 agent 任务。
这里有一个容易被忽略的点:job attach之后,你看到的不是历史日志,而是它当前正在执行的步骤、以及它计划下一步做什么。所以你可以中途给它注入新的指令,比如"刚发现 billing 模块的某个 fixture 改了,重跑之前先处理掉"。这比完全放手更可控。我一般会在 attach 进去之后主动确认它的短期计划,然后让它继续跑;偶尔发现计划偏离了就立刻纠正。
4.3 多任务并行时的管理习惯
同时挂两个以上 job 的时候,如果描述写得不够清楚,job list一刷出来根本分不清谁是谁。我的习惯是给 job 描述统一加前缀,比如[billing]、[ci]、[docs],然后在描述里带上验收动词,不要说"处理问题",要说"修正超时问题并重新跑通过率测试"。
另外需要注意资源问题。每个后台 job 都占上下文、都消耗 token,如果你用的是本地模型,多个 job 并行会直接拉满显存,导致每个任务都变慢,产生连锁式的 429 限流。所以并行数量我一般控制在两个以下。如果确实要跑很多独立任务,串行排队加一个 job 一个 goal 反而更稳。
4.4 后台 job 与 goal、compact 的衔接
后台 job 并不是独立于 goal 和 compact 的另一个世界,它完全可以当 goal 的执行载体。比如我在第 5 章会讲的那个重构案例里,补测试这个 goal 就是用 job 挂到后台跑的。任务挂起之后,上下文还是会持续增长,所以我习惯在 job 跑完一轮、准备开始下一阶段之前,先做一次 compact。
有一个很多人问的问题:job 在后台跑的时候,我还能不能做别的事?能做,但要注意你会话里的当前上下文和 job 是分开管理的,回到前台对话时可能会发现模型不知道后台 job 刚才干了什么。我的处理方式是在 attach 结束、回到前台会话时,先发一条简报指令,比如"总结一下刚才 job 完成的内容和遗留问题",然后再继续。这样前台上下文的衔接不会断。
5. 一单中型重构的真实走位:三件套串起来的工作流
理论讲完,给一个我实际做过的例子。任务本身不算复杂:把一个单体服务里的支付模块拆成独立模块,补充测试,顺便把 CI 脚本从只跑单元测试改成同时跑集成测试。整个过程大约横跨一个工作日。
上午开始,第一件事是拆目标。我没有把"拆分支付模块"整个扔给它,而是开了四个 goal:分析依赖并且列出会受影响的文件;完成代码迁移但要求现有单测全绿;补充支付流程的集成测试;更新 CI 配置。第一个 goal 的执行大概花了二十分钟,过程中上下文增长很快,因为 agent 一边读文件一边列依赖关系,输出量很大。我看到上下文差不多过了一半,就直接手动跑了一次 compact,把已确认的依赖清单和影响文件列表放进了--preserve,然后让它在压缩后的上下文中列出一个迁移计划。
下午进入第二个 goal 的时候,我改用后台 job 来跑。因为代码迁移这一步要连续修改十几个文件,而且每改几个就要跑一遍测试确认没坏,预计耗时很长,不值得前台盯着。我用job start把它挂上,然后去干别的了。大约过了一个小时,job list显示任务还在跑,我 attach 进去看了一眼,发现它正卡在一个测试失败上原地重试了三次。我给它补充了一条指令,告诉它先看某个 fixture 的改动,然后重新跑,纠偏之后继续挂后台。等到它跑完,我做的第一件事是再开一个 goal 来收尾,把这个阶段的完成状态固化下来。
这里我踩了一个典型的坑:补集成测试的阶段,agent 需要远程模型做一次较大的 compact,结果恰好撞上了容量报错——selected model is at capacity。当时我并不知道是模型服务满载,还以为是配置问题,反复重启折腾了十几分钟。后来在日志里看到时间点和服务端响应,才确定是容量问题,换了一个模型名后马上就好了。
最后 CI 配置这个 goal 反而是最轻松的,因为前面的上下文经过了两次压缩,保留的全是结论性信息:改动了哪些文件、测试通过的状态、依赖关系图。CI 脚本改动本身不大,但它的基础材料是真实可靠的。
走完这一轮,我自己总结出来的黄金工作流是:goal 拆边界 -> 执行一段 -> 接近半程就 compact -> 需要长时间执行的部分交给 job -> job 完成后 attach 做简报 -> 回到前台对话再开新 goal。每一步之间都有一个清晰的"存档点",整个任务不会因为没有存档点而推到重来。
6. 几个"手感"细节:模型选择、本地配置和效率习惯
你以为三件套知道命令就行了,真正决定体验的是几个细节习惯。第一个是模型选择。如果你同时接了本地模型和远端模型,建议把常规对话跑在响应快的模型上,把 compact 或 batch 类任务放在容量更充裕的模型上。很多 remote compact task 报错本质不是配置问题,而是你把所有压力都压在了同一个模型服务上。我现在的配置是对话模型和压缩模型分开,这样即使某台服务满了,压缩任务还能走另一条路。
第二个细节是配置连接本地模型时,注意"思考模式"这个开关。这个词看着很玄,实际上就是让模型在回答之前先输出一段推理过程。在长任务里这个开关对 goal 的判断质量影响很明显——它会让 agent 先分析当前目标的完成条件,再决定下一步动作,而不是直接凭经验上手改代码。代价就是慢一些、token 多一些,所以我只在关键决策节点开启,不全程开。
第三个细节是日志。DeepSeek Harness 的日志路径在~/.deepseek-harness/logs,排错时第一反应应该是去这里翻日志,而不是反复重试同一个命令。我排查 remote compact task 那几次,真正有用的线索全是日志里给的,报错界面那几行字只是冰山一角。
第四个细节是关于安装和版本。如果你是第一次装这个工具,重点是选对安装方式:Node 环境没问题的话直接装全局包最快,如果网络环境不稳定就下二进制包。不要混着装两套,后面命令行入口会打架。版本升级前把当前会话里还没归档的 goal 收好尾,再执行升级,我吃过一次升级后旧会话上下文对不上新版本解析器的亏。
最后分享一个我一直在用的习惯:每天收工前,把当天的 goal 列表和 job 列表截图或导出成文本,存到项目的docs/agent-notes目录里。这样第二天开新会话时,能直接把这些笔记粘进上下文,让新会话在几秒内恢复"昨天做到哪了、下一步干什么"的全貌。长任务管理说到底就是两件事:保持上下文干净,保持任务边界清楚。goal、compact、后台 job 这三件套已经把这两件事的基建都搭好了,剩下的就看你怎么用了。