DeepSeek Harness(简称 DSH) 是 DeepSeek AI 于 2026 年开源的智能体运行框架(Agent Harness)。在长周期的多轮工程任务中,会话的上下文预算主要由三层机制协同控制:Spill 负责将单次超大的工具输出落盘,Compaction 负责将较早的历史对话压缩为摘要,而 统计视图(ui-stats) 则用于实时观测上下文体积、KV Cache 命中率与各阶段耗时。
本文基于 0.2.2-rc.1(2026-11-04 发布)官方规范,系统拆解底层模型 API 的接入配置、三大上下文机制的触发阈值与缓存影响,并提供一套遵循 “先量化、再落盘(Spill)、最后压缩(Compact)” 的标准化排查流程。
一、 核心结论:长会话治理不是简单的“一键压缩”
在 0.2.2-rc.1 版本的默认基础配置中,dsh 已经默认挂载了 compaction-basic、command-compact、spill-local 和 spill-policy 四个核心组件。唯一的例外是 统计视图(ui-stats),它作为实验性开发者工具,默认处于未挂载状态。
因此,绝大多数生产或本地部署无需额外寻找第三方“上下文管理插件”,核心工作在于理清内置的 三种主机制、一个前置修剪器与一个只读观测视图 的分工边界:
| 机制组件 | 触发条件 | 核心执行动作 | 是否调用大模型 | 对 KV Cache 的影响 |
|---|---|---|---|---|
| Spill(落盘) | 单条工具返回结果超过 maxInlineTokens |
仅在上下文内保留首尾片段,完整输出写入本地磁盘并返回可检索路径 | 否 | 友好(追加式写入,不破坏前缀缓存) |
| Pruner(修剪器) | 达到压缩阈值后,单条文本超过 8,192 字符 | 保留头部 4,096 字符与尾部 1,024 字符,中间部分直接截断 | 否 | 局部失效(修改历史消息内容) |
| Compaction(压缩) | 触发上下文压力阈值、窗口溢出或手动执行 /compact |
将较早的完整对话轮次替换为一条结构化摘要消息 | 是(发起一次独立摘要请求) | 部分失效(从首个被替换 Token 起缓存失效) |
| 统计视图(ui-stats) | 在 Web 界面开启“统计”页签或显式挂载插件 | 按轮次与步骤只读展示 Token 消耗、缓存命中率与延迟分布 | 否 | 无影响(纯客户端观测,不改变模型输入) |
核心原则: Spill 解决“单次工具输出过大”,Pruner 解决“压缩前的粗粒度瘦身”,Compaction 解决“多轮历史累积过长”,统计视图 解决“动手调参前的瓶颈定位”。
二、 前置基础:DeepSeek Harness 大模型 API 接入配置
上下文预算的计算公式(如窗口上限 $W$、输出预留 $O$)以及 Compaction 摘要请求的响应速度,直接取决于底层接入的大模型服务。在调整预算参数前,需先在配置文件中完成模型端点的挂载。目前主流的接入方式分为 官方订阅直连 与 国内托管中转网关接入 两种路径。
1. 官方 API 订阅直连
如果运行环境具备稳定的国际或专线网络,且工作流仅依赖单一厂商模型,可直接配置官方 API 端点:
models:
- id: primary-agent-model
provider: openai-compatible
config:
baseURL: https://api.deepseek.com/v1
apiKey: ${DEEPSEEK_API_KEY}
model: deepseek-chat
contextWindow: 1000000
maxOutputTokens: 65536
2. 国内托管中转网关接入(以 4SAPI 为例)
在实际工程落地中,Agent 往往需要高频发起工具调用与长文本摘要请求。若团队处于国内网络环境,或希望在主任务模型与低成本摘要模型之间灵活切换,可将 baseURL 替换为支持标准 OpenAI 协议的国内托管中转站(例如 4SAPI):
models:
- id: primary-agent-model
provider: openai-compatible
config:
baseURL: https://4sapi.com/v1
apiKey: ${FOUR_S_API_KEY}
model: deepseek-v4
contextWindow: 1000000
maxOutputTokens: 65536
在 DeepSeek Harness 中采用此类托管网关的核心工程优势主要体现在三点:
- 降低长上下文网络延迟: 国内节点直连可规避跨境网络抖动与连接重置问题,显著缩短几十万 Token 级别上下文传输及 Compaction 摘要生成的首字延迟(TTFT)。
- 多模型统一路由: 仅需维护一套标准的
/v1/chat/completions接口与单一 API Key,即可在配置文件中随时将主执行模型或摘要模型切换为 DeepSeek、Claude 或 GPT 等不同架构,无需改动 Harness 插件逻辑。 - 并发兜底与防中断: 长会话一旦因上游接口触发速率限制(HTTP 429)而报错,往往会导致整个执行链路中断;托管网关层自带的多渠道负载均衡能有效提升长周期任务的连续性。
三、 Compaction 机制:用摘要置换早期历史(非无损操作)
Compaction 的核心逻辑是:将会话中最旧的一段平衡对话范围(Balanced Range)合并替换成一条摘要消息,同时逐字保留近期的对话轮次,使智能体基于“摘要 + 近期原文”继续推进任务。
需要注意的是,Compaction 不会缩减系统提示词(System Prompt)、工具定义(Tool Schema)或固定会话前缀,也无法拆分单条不可分割的超大工具调用。
触发阈值与默认参数计算
Compaction 的默认自动触发阈值遵循以下公式:
$$\text{Trigger Threshold} = \lfloor \min(W \times \text{thresholdRatio}, W - O - \text{headroomTokens}) \rfloor$$
其中:
- $W$ 为模型上下文窗口大小(Context Window)。
- $O$ 为单次响应的最大输出预留(Output Reservation)。
headroomTokens为安全缓冲空间,默认值为65,536。
计算示例: 假设接入模型的上下文窗口 $W = 1,000,000$,单次输出预留 $O = 256,000$,默认触发阈值约为 678,464 Token;而近期逐字保留预算(按 $(W - O) \times 16%$ 计算)约为 119,040 Token。
对于常规代码开发或日常排障任务,等到 67 万 Token 再压缩通常偏晚,不仅单次推理成本高,且响应延迟明显。建议根据实际业务场景主动收紧配置:
- id: compaction-basic
config:
thresholdRatio: 0.7
headroomTokens: 4096
maxTokens: 4096
retainTokens: 2048
配置注意事项:
retainRatio与retainTokens属于互斥参数,不可同时声明。- 保留预算(Retain Budget)必须严格小于触发阈值,否则插件在初始化加载时会直接拒绝启动。
- 若设置
auto: false,系统将关闭自动压缩,仅保留/compact手动触发入口。 - 上述 YAML 会整条覆盖同
id的默认配置;若当前 Profile 中已存在compaction-basic,请直接合并字段,切勿重复声明两个同名块。
Compaction 的三个工程边界
- 细节有损性: 内置摘要模板会优先提取任务目标、已修改文件、报错信息、关键决策与下一步计划,但不会保留每一行代码或日志原文。对于必须严格遵守的全局约束,建议写入交接文档或在近期对话中显式重申。
- 额外请求开销: 生成摘要本身是一次独立的模型推理调用。触发压缩的当前轮次耗时反而会增加,其核心收益在于为后续多轮请求减负。
- KV Cache 局部失效: 历史替换完成后,从第一个被替换的旧 Token 位置起,原有的 KV Cache 复用即告失效;但位于替换点之前的系统提示词与固定前缀依然可以命中缓存。
四、 Spill 与 Pruner:优先拦截单条超大输出
1. Spill:大结果落盘与按需检索
当智能体执行 bash、grep 或网页抓取工具,一次性返回数万行构建日志、整页 HTML 或巨型 JSON 时,直接内联进上下文会瞬间击穿预算。Spill 正是为此类场景设计的无损卸载机制。
在默认基础配置中,spill-policy 的 maxInlineTokens 阈值为 12,500。一旦单次工具输出超标,模型在上下文中看到的将是:按原顺序保留的首尾片段、中间省略提示,以及完整结果在本地磁盘的存储路径。随后,模型可通过 read 工具(配合 offset / limit 参数)或 grep 工具对该文件进行按需精读。
Spill 的四大核心特性:
- 零模型调用: 纯本地 I/O 截断与路径注入,不消耗任何模型 Token 与推理时间。
- 数据完整无损: 全量文本写入本地磁盘,可读可搜;图像等二进制内容则按附件路径记录。
- 优雅降级(Best-Effort): 若磁盘满载或写入权限异常,系统会自动退回保留原始内联结果,绝不会将一次成功的工具调用转换为报错。
- KV Cache 极其友好: 采用追加式替换策略,新生成的文件路径位于已有可复用前缀之后,不会破坏历史 KV Cache。
自定义存储路径与清理策略:
默认情况下,本地后端会将文件写入私有临时目录,并按 sha256(sessionId) 生成子目录进行会话隔离,文件权限锁定为 0600。在生产服务器或云主机(如挂载了独立数据盘的云实例)上运行时,建议通过以下配置将 root 指向独立数据盘,防止 Spill 文件撑爆系统盘:
- id: spill-local
config:
root: /var/lib/dsh/spill
cleanupPeriodDays: 30
- id: spill-policy
config:
maxInlineTokens: 12500
提示:
root强烈建议使用绝对路径(相对路径会基于进程当前工作目录解析)。cleanupPeriodDays默认为 30 天,但过期清理仅在进程启动时扫描一次;对于长期不重启的守护进程,需配合外部定时任务(如 Cron)定期巡检磁盘空间。
2. Pruner:压缩触发后的前置修剪闸门
dsh-compaction-tool-result-pruner 经常被误认为是与 Spill 并列的日常截断工具,但实际上它是 Compaction 的前置步骤。
- 触发时机与规则: 只有在会话达到 Compaction 压力阈值、准备执行压缩时,Pruner 才会介入。它会扫描历史消息,将超过 8,192 字符 的文本结果截断为“头部 4,096 字符 +
[... tool result middle pruned ...]+ 尾部 1,024 字符”。在日常低于压缩阈值时,Pruner 不会触碰任何工具输出。 - 如何快速区分两者: 观察上下文中的占位提示——出现
Full formatted result stored at: ...代表触发了 Spill,完整原文仍在本地磁盘文件中;出现tool result middle pruned则代表触发了 Pruner,中间内容已在上下文中被直接裁切(原始未裁剪数据仅留存于底层会话轨迹日志中)。 - 短路效应: Pruner 不调用大模型。如果经过它的粗粒度修剪后,上下文总体积直接降回了压缩阈值以下,系统会直接跳过后续耗时的模型摘要请求。
五、 统计视图(ui-stats):先量化观测,再动手调参
在盲目修改 maxInlineTokens 或频繁敲击 /compact 之前,最高效的做法是先打开统计视图看清预算账单。该视图按每一轮对话(Turn)和每一个执行步骤(Step),直观呈现上下文体积、输出 Token 数、KV Cache 命中率、首 Token 延迟(TTFT)、模型与工具耗时占比以及生成速率。
启用方式
- Web 界面启用: 进入“插件”管理页面,在“实验性插件”旁点击“更多”,勾选启用 “开发者工具”。
- 配置文件显式挂载:
- id: ui-stats
name: '@deepseek-ai/dsh-experimental-client-ui-stats'
安全与性能提醒: 统计视图属于实验性功能。启用“开发者工具”会同步拉起本地调试后端,并开启未脱敏的 Host fetch 请求采集。因此,不建议在多人共享或生产环境的 Profile 中长期开启,仅在排查性能与预算瓶颈时按需挂载。
核心指标判读指南
- 单步上下文增幅: 观察上下文曲线是随对话平缓爬升,还是在某一步工具调用后出现断崖式暴涨。
- 缓存命中率回升周期: 执行 Compaction 后,紧接着的第一轮请求缓存命中率必然下跌;重点观察后续 2~3 轮对话中命中率是否恢复至高水位。
- 模型耗时 vs. 工具耗时: 若上下文体积很小但整体响应极慢,通过耗时进度条可直接定位究竟是上游模型 API 响应慢,还是本地工具执行卡顿。
- 压缩合成事件行: 勾选开启 “显示事件行(含压缩)”,即可直接查看每次 Compaction 释放的 Token 数量、生成的摘要体积以及摘要请求本身的耗时。
(注:统计页签中的“总用时”为模型耗时与工具耗时的算术累加,并非实际墙钟时间 Wall-clock Time;当存在并行工具调用时,累加耗时条会显得偏长,因此该指标适合做纵向趋势对比,不宜作为严格的 SLA 计时依据。)
六、 场景决策矩阵与标准化排查流程
面对不同表现的会话膨胀或卡顿,可直接参照下表选择干预手段:
| 观测到的现场症状 | 优先处理动作 | 工程原因解析 |
|---|---|---|
| 单次工具返回数万行日志,上下文瞬间跳高 | 依赖 Spill;若仍偏大则调低 maxInlineTokens |
零模型调用开销,既保护上下文窗口,又保留完整文件供模型检索 |
| 对话轮次极多,上下文平缓逼近上限,但当前任务未完结 | 自动 Compaction 或手动执行 /compact |
将早期探索过程浓缩为摘要,腾出空间保障近期上下文的逐字精度 |
| 当前功能开发或排障已彻底结束,准备切换新课题 | 直接开启新会话(New Session) | 成本最低、最彻底的预算清零方式,避免旧任务摘要污染新任务推理 |
| 上下文占用率很低,但每次回复等待时间极长 | 打开 统计视图 拆解模型与工具耗时 | 瓶颈通常在于工具死锁或上游 API 网络延迟,压缩上下文无法提速 |
| 压缩后发现关键细节丢失,需要核对早期原始输出 | 查阅底层 会话日志(Session Logs) 或轨迹文件 | Compaction 仅在上下文内保留摘要,但完整的历史事件仍持久保存在日志中 |
六步标准化排查流程
- 基准测量: 挂载并打开统计视图,记录当前轮次的上下文大小、缓存命中率与工具耗时分布,拒绝凭直觉盲目压缩。
- 定位突变点: 若发现某一步上下文突然暴增,立即定位该步对应的工具调用类型。
- 收紧落盘策略: 若确认为单条大输出所致,检查 Spill 是否正常触发;若默认 12,500 Token 仍显宽裕,可将其下调至
6000~8000让大文本更早落盘。 - 受控手动压缩: 若属于长线任务自然累积,在轮次空闲时输入
/compact执行一次手动压缩,并在统计视图中核对压缩合成行的负增量。 - 验证摘要质量与缓存: 压缩后继续对话 2~3 轮,观察 KV Cache 命中率是否回升,并确认智能体是否遗忘核心约束(如有遗漏,及时在提示词中补充)。
- 避免连续重复压缩: 每次压缩后重新评估趋势,切忌对同一段已摘要的历史连续多次执行
/compact,以免造成信息严重失真与无意义的缓存击穿。
七、 常见技术问答(FAQ)
Q1:Compaction 和 Spill 最本质的区别是什么?
- Spill 针对 “单条超大工具结果”,不调用大模型,直接将完整原文存盘并留下一条检索路径,属于无损卸载。
- Compaction 针对 “多轮累积的历史对话”,会发起一次大模型调用将早期上下文重写为摘要,属于有损压缩。
Q2:为什么在对话框输入 /compact 时提示命令不可用?
当前智能体正处于活跃的推理/工具执行轮次中,或者后台已有一个压缩任务正在运行。/compact 命令本身不会进入队列等待,也不会强行打断当前正在执行的轮次;而在压缩运行期间发送的普通用户提示词会自动排队,待压缩完成后依次执行。
Q3:为什么在插件列表里找不到“统计页签”?
统计页签不属于默认启用的常规插件。需要依次进入 Web 界面的 “插件 → 实验性插件 → 更多” 并开启 “开发者工具”,或在配置文件中显式挂载 @deepseek-ai/dsh-experimental-client-ui-stats。
Q4:Spill 落盘的文件存放在哪里?长期运行会导致磁盘爆满吗?
默认存储在系统私有临时目录下的 session-[hash] 子目录中,也可通过 spill-local 的 root 参数固定到指定磁盘路径。虽然 cleanupPeriodDays 默认配置了 30 天过期清理,但清理扫描仅在 DSH 进程启动时触发一次。因此对于常驻后台的服务,建议固定 root 目录并配合操作系统的定时清理策略。
Q5:执行压缩后,下一轮的 KV Cache 命中率大幅下跌,这属于异常吗? 属于正常现象。因为早期历史 Token 被替换为新的摘要文本,从第一个发生改变的 Token 位置起,后续的缓存前缀签名随之改变(仅替换点之前的系统提示词等前缀仍可复用)。请继续观察后续 2~3 轮对话,随着新对话在摘要之后不断追加,缓存命中率会逐步恢复正常;若持续低迷,再排查是否有动态变化的系统提示词或工具耗时异常。