Files
deepseek-harness/packages/spill/spill-policy/README.zh.md
T

8.4 KiB
Raw Blame History

description, kind
description kind
工具结果 spill 策略:部署如何用预览和可检索的 spill 文件把过大的纯文本工具结果挡在模型上下文之外。 package-reference

@deepseek-ai/dsh-spill-policy

English | 中文

概述

dsh-spill-policy 把过大的纯文本工具结果挡在模型上下文之外:当最终结果超过 maxInlineBytes 时,它通过 ctx.spillStore 保存完整文本,并把面向模型的结果替换为有界的首尾预览、后端定位信息与取回指引,模型可据此读取或搜索 spill 文件。它不注册任何服务,也不负责存储或预览机制——存储由已挂载的 SpillStore 后端负责,预览来自 dsh-output-retention;它只决定何时 spill 并组合通知。它是可选且尽力而为的:省略 maxInlineBytes 时完全禁用,spill 失败时原始结果仍然可见。第二条分支把同样的上限应用到 run_code 子调用结果的持久日志副本,因此回放与 UI 也不会无限增长。

目录


使用本包

把策略与 spill 后端一起挂载,以限制模型看到的工具纯文本结果大小。上限作用于工具运行后的最终结果;策略放过的结果仍会原样通过。

最小配置

以 UTF-8 字节计的 maxInlineBytes 预算加载策略,并同时挂载 spill 后端:

- name: '@deepseek-ai/dsh-spill-local'
- name: '@deepseek-ai/dsh-spill-policy'
  config:
    maxInlineBytes: 50000
字段 默认值 含义
maxInlineBytes 省略 纯文本结果面向模型的上下文上限(UTF-8 字节);省略时完全禁用该策略

生成的配置目录是每个受支持字段的穷尽式真源。负数或小数上限会让插件加载失败,而不是破坏每次调用的行为。

模型看到什么

过大的纯文本结果会在同一预算内被替换为预览加通知,因此整个替换内容永远不会超过 maxInlineBytes

<retained head/tail preview>

(Omitted N bytes. Full formatted result stored at: /…/session-…/…-web_fetch.txt. Use read with offset/limit, or grep this path to search within it.)

当通知本身已占满预算(上限极小或定位信息很长)时,预览为空,只返回通知;如果连这也会超过上限,策略会保留原始内联结果——上限内的替换内容总比原始结果小。完整文本仍保留在 spill 文件中,成功的替换只改变面向模型的副本,绝不改变规范的程序化结果。

哪些结果会受影响

策略只作用于最终、已接受且纯文本的结果。不超过上限的结果、包含任何非文本块的结果、嵌套复合调用、read 结果、被阻止的决策与已接受的值替换都会原样通过。此前已经发生的提供方级截断(例如 web-fetch-http.maxBodyChars)无法在此恢复——spill 文件保存的是工具实际返回的内容。

尽力而为的故障行为

缺少会话所有者、缺少 ctx.spillStore 后端或 saveText 拒绝时,会记录警告并返回原始结果。spill 失败绝不会把成功的调用变成错误,也绝不会隐藏内联结果。

持久日志副本

同样的上限也约束每个 run_code 子调用结果的会话日志副本:程序仍会收到完整值,只有日志副本被替换为预览与定位信息。过大的 read 子调用结果在此同样设界,因为日志副本不是模型上下文。


理解实现

实现细节——点击展开

本节解释该策略背后的设计决策;可观察行为已在使用本包中完整说明。

设计理念

该策略刻意保持狭窄:它只决定何时 spill,并组合通知。它不注册服务、不负责存储、也不负责预览机制——dsh-output-retentionTextRetainer 负责构建首尾预览。两个不变式塑造了代码:面向模型的替换永远不会超过 maxInlineBytes(先为通知预留字节成本),且 spill 失败永远不会改变工具调用的结果。

两条分支

tools/post-execute waterfall(瀑布式事件)监听器(以 prepend 注册、通过 next() 委托)约束面向模型的结果;tools/code-dispatch-log 监听器约束每个 run_code 子调用的持久日志副本。两者共享同一个替换辅助函数,因此两个投影字节一致。post-execute 分支跳过 read 以避免 read → spill → read 循环;dispatch-log 分支约束 read 子调用,因为日志副本不是模型上下文。

源码地图

文件 职责
src/index.ts 插件入口:Config 校验、两个 waterfall 监听器、共享替换辅助函数
src/types.ts SpillPolicyExec:策略读取所属会话 id 所需的最小结构化工具执行视图
src/invariant.ts 不变式伴生插件(无运行时不变式;约定在 seam 处强制执行)

故障模式

两条分支都适用尽力而为降级:没有会话所有者、没有后端、保存被拒绝或没有上限内的替换时,记录警告并保留原始内容。加载时校验会拒绝负数或小数 maxInlineBytes,让错误配置失败在部署阶段,而不是让每次超大调用都失败。


进一步探索

当包级约定不够用时阅读以下页面。


模型体验

过大的纯文本结果

模型看到什么

不超过 maxInlineBytes 的结果、嵌套结果、read 结果、被阻止的决策与包含非文本块的结果保持不变。过大的纯文本面向模型结果会变成有界的首尾预览,后面附加 (Omitted <bytes> bytes. Full formatted result stored at: <locator>. <retrievalHint>);存储或归属失败时原始结果仍然可见。

Token 影响

成功的替换最多为 maxInlineBytes 个 UTF-8 字节,并保留在历史中直到压缩(compaction);完整 spill 文本不会重新发送给模型。

KV Cache 影响

仅追加;新可见内容位于可重用请求前缀之后,不会使现有 KV Cache 条目失效。

已知限制与延期工作

这些限制说明策略在哪些情况下无法提供帮助。它们是当前的包约束。

  • 只能对最终纯文本结果执行 spill——混合内容结果、阻止反馈与 read 会原样通过;此前已经发生的提供方截断或工具自有保留无法在此恢复。
  • 通知无法容纳时会禁用该次调用的替换——上限极小或定位信息很长时,后端已经保存了无引用的 spill,但过大的原始结果仍留在内联位置。

开发备注

维护者的工作上下文——点击展开

本开发备注是维护者的工作上下文:开放方向。它明确不具权威性。

未来:逐工具配置

逐工具选择退出或逐工具策略声明仍然延期;内置的 read 跳过已覆盖已知循环,第二个真实工具需求才能证明配置的合理性。

未来:更早的 spill

该策略只能看到最终格式化文本,因此已被提供方截断或只以运行时产物形式存在的内容(例如 bash 流或 subagent 展开)仍在触达范围之外;通过 ctx.spillStore 实现的工具自有早期 spill 仍然延期。