8.4 KiB
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-retention 的 TextRetainer 负责构建首尾预览。两个不变式塑造了代码:面向模型的替换永远不会超过 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,让错误配置失败在部署阶段,而不是让每次超大调用都失败。
进一步探索
当包级约定不够用时阅读以下页面。
- spill 存储服务——策略替换背后的
saveText约定。 - dsh-spill-local——保存 spill 文本的本地后端。
- dsh-output-retention——策略组合的预览机制(
TextRetainer)。 - 工具输出 spill 决策——能力边界与设计依据。
- 代码 dispatch-log spill 决策——为何持久日志副本同样设界。
模型体验
过大的纯文本结果
模型看到什么
不超过 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 仍然延期。