omp packages

pi-distill1.10.1

Pi tool-output distillation with file-first configuration

GitHub Actions Published 2 days ago MIT ~1.6K downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

pi-distill

保留事实,把上下文留给决策。

pi-distill 是一个 Pi 扩展:它不替换工具,也不改变命令的执行方式,只在工具已经返回真实结果之后,帮助 Agent 决定哪些内容值得进入下一轮上下文。

解决什么问题

编码 Agent 通常只需要命令、搜索或文件读取结果中的关键信息。把大段日志、生成文件或搜索结果完整塞入下一轮,会增加上下文消耗,也容易让有效信号被噪声淹没。pi-distill 在不替换 Pi 内置工具的前提下,增加一层结果级提炼。

实际上下文节省效果

构建日志、diff 输出和测试报告经常包含重复状态行、未变化上下文、堆栈噪声,以及下一步决策并不需要的细节。这些内容通常很适合高比例压缩。下面这张真实 Pi 会话截图中,结果从 51,215 个字符压缩到 240 个字符:213.40 倍压缩,输出字符减少 99.5%。

pi-distill 上下文节省示例

截图统计的是字符减少比例,不是 tokenizer 得出的精确 token 统计。实际使用时通常会带来同量级的上下文 token 节省,但精确数值取决于语言、内容和模型 tokenizer。对于适合压缩的冗长输出,90% 以上是已经观察到的效果,但不是每个命令的保证;需要完整输出时请使用 RAW。

场景 常见噪声 提炼结果保留
构建 / 编译 重复进度、警告和未变化的环境信息 成功/失败、首个可行动错误、受影响文件和后续步骤
Diff 检查 大量未变化 hunk 和格式化噪声 变更文件、相关 hunk 和评审所需事实
测试 单测逐条输出、snapshot 和框架模板 总数、失败用例、关键断言和有效诊断

Prompt 语言

提炼 prompt 会严格跟随 /config:language 当前选择的语言。持久化语言发生变化后,下一次工具调用会读取新设置,即使语言命令和 pi-distill 来自不同的包实例也可以同步。PI_EXTENSIONS_LOCALE 仍然是显式的环境变量覆盖项。原始用户消息只作为语言上下文传入,不能覆盖已选择的语言。

工作方式

  • 通过 Pi 原生的 tool_call / tool_result 事件监听 bash、read、grep 和 find。
  • 以工具的 outputRequest 作为是否提炼、如何提炼的依据。
  • 当提示词严格只有 RAW 时,视为明确要求返回原始输出。
  • 默认使用当前会话模型,也可以配置独立的 provider/model。
  • 在工具结果 details 中保留状态、字符数、压缩比、耗时和异常等诊断信息。
  • 超长输出不再由 pi-distill 写文件或截断,统一交由 Pi 自身的输出限制机制处理。
  • 当前 Pi 展示中间件可用时显示紧凑审计卡片,否则使用自己的 fallback renderer。展示协议由公共运行库 pi-extensions-tool-display 提供。

它不会注册第二个 bash、read、grep 或 find 工具。

安装

pi install npm:pi-distill

包清单会把共享依赖 pi-extensions-i18n 和 pi-extensions-tool-display 作为扩展入口加载,不需要额外安装这些包。安装 pi-distill 后即可使用 /config:language。

安装后重新加载 Pi:

/reload

交互式配置命令:

/config:distill

不带参数时打开设置面板:左边字段名、右边当前值、选中项下方一行说明。开关按回车或空格原地切换;枚举、数值和提炼模型按回车打开二级列表,列表上方带过滤输入框,模型还能直接手输。改一项立即写盘并对本次会话生效,Esc 关闭时新配置已经生效。模型列表来自 Pi 当前注册的模型。

带参数时不开面板,只做对应动作:

/config:distill status
/config:distill enable
/config:distill disable

核心思想

我们不是想让 Agent 少看信息,而是避免它为了找一句结论,被迫把几千行日志一起带进上下文。

工具执行层需要保留完整事实;Agent 消费层需要控制上下文成本。pi-distill 在两者之间增加一个可选的结果处理层:

  • 工具负责执行并返回事实;
  • Agent 通过 outputRequest 表达自己关心什么;
  • 扩展读取真实输出后,再决定是否调用提炼模型;
  • 模型只压缩消费路径,不改变原工具的业务语义;
  • 诊断信息记录这次处理是否真的节省了上下文。

因此,提炼不是“把所有输出都交给模型总结”,而是一份明确的工具契约:需要什么就提取什么,需要完整内容就保留原文。

为什么需要它

构建、测试和 diff 往往会返回大量重复状态、未变化上下文、框架模板和堆栈噪声。Agent 可能只需要失败原因、变更文件或最终状态,却被迫先消费整段输出。

直接截断会丢失关键事实;新增一个总结工具会增加调用链和决策负担;等 Agent 看完再总结又已经消耗了上下文。pi-distill 选择在结果进入后续推理前处理它,同时保留明确的原文模式和失败回退。

实际效果

下面是一段真实 Pi 会话中的输出:原始结果从 51,215 个字符提炼到 240 个字符,压缩 213.40 倍,输出字符减少 99.5%。

pi-distill 上下文节省示例

这张图统计的是字符减少比例,不是 tokenizer 得出的精确 token 数。实际 token 节省会受到语言、内容和模型 tokenizer 影响;对于适合压缩的构建日志、diff 和测试输出,90% 甚至更高的节省比例是已经观察到的结果,但不是每个命令的保证。

场景 原始输出中的典型噪声 提炼后优先保留
构建 / 编译 重复进度、环境信息、重复警告 成功/失败、首个可行动错误、受影响文件、后续步骤
Diff 检查 大量未变化 hunk、格式化噪声 变更文件、相关 hunk、评审所需事实
测试 逐条单测输出、snapshot、框架模板 总数、失败用例、关键断言、有效诊断

节省比例不是唯一指标。扩展还记录提炼耗时、原始字符数、结果字符数、压缩比和异常;如果总结没有带来真实收益,会暴露 ineffective-compression,而不是静默假装优化成功。

工作原理

一次工具调用的处理链路如下:

Agent 提出处理目标
        ↓ 通过 outputRequest 传给工具
工具执行真实操作,返回 stdout / stderr / 文件内容 / 多媒体结果
        ↓
pi-distill 根据真实结果和配置决定:原样返回,或调用模型提炼
        ↓
Agent 消费更适合当前决策的结果,并获得可审计的处理诊断
  1. 扩展在会话启动时为所有已启用、参数 schema 为 object 的工具增加必填的 outputRequest 参数;edit 和 write 默认关闭,其他未配置工具默认开启。不写死 bash、read、grep 或 find。唯一例外是 codemode,扩展完全跳过它:它使用 Pi 的 grammar 约束采样,只接受一个必填字符串属性,多一个必填属性会让整轮模型请求失败;配置面板也不会给它开关。
  2. tool_call 事件捕获这个参数,并在交给底层工具前移除它,因此原工具不会收到扩展专用字段。
  3. tool_result 事件拿到真实输出后再做判断,不依赖 Agent 对输出长度的预测。
  4. 每次工具调用都必须包含非空的 outputRequest;严格的 RAW 表示明确要求原文;其他非空 prompt 才允许进入提炼流程。
  5. OpenAI-compatible Completions 提炼请求会通过 response_format: { "type": "json_object" } 启用原生 JSON 模式;OpenAI Responses-compatible 请求使用等价的 text.format。单次提炼超时后按照 timeoutRetryCount 重试,其他模型调用异常按照 errorRetryCount 重试(两者默认都重试 1 次);如果模型已经返回文本,但只是 JSON 语法或响应结构校验失败,扩展会把坏响应和校验错误交给一次 JSON-only 修复 prompt,不会再次发送工具输出,也不会重新总结;修复失败、没有可用模型或结果收益过低时,扩展保留原始事实,并通过 details 和审计卡片暴露状态;模型用 Markdown 的 JSON 代码围栏(如 json … )包裹响应时也会兼容解析。
  6. 用户打断上级 Agent 请求时,所有尚未完成的提炼请求会立即取消,并且不会为这次中断继续重试;原始工具结果仍按 fail-open 路径保留。

输出处理契约

outputRequest 行为 适用场景
未提供 工具调用无效;Pi 会在底层工具执行前拒绝该调用 不要省略;未明确要求压缩时使用 RAW
严格为 RAW(大小写不敏感) 不调用提炼模型,保留完整原始文本;超长时由 Pi 自身的输出限制机制处理 逐字核对、复制内容、需要完整日志时
任意非空且非 RAW 输出达到阈值后调用模型,超时与其他异常分别使用独立重试次数,具体保留内容由 prompt 决定 “只保留错误、警告和最终状态”等场景
包含图片、音频或其他非文本内容 原样保留,不发送给提炼模型,不做文本长度截断 图片读取、二进制结果、混合文本与图片结果

RAW 是确定性的完整输出信号。提炼 prompt 会要求总结模型在用户明确要求“不遗漏地完整提取”时直接返回 RAW,尤其适用于语法、参数、SQL、API 调用或其他需要复制的精确文本。工具调用方可以控制参数时,直接传 RAW 仍然是首选方式。

Prompt 语言

提炼 prompt 完全跟随 /config:language 当前选择的语言:

  • 切换语言后,下一次工具调用读取新的持久化语言设置;
  • 即使 /config:language 和 pi-distill 来自不同的包实例,也通过共享 locale 设置同步;
  • PI_EXTENSIONS_LOCALE 可以作为显式环境变量覆盖;
  • 原始用户消息只作为任务上下文传入,不会把中文用户消息误判成中文 prompt。

覆盖范围与边界

  • 自动处理所有当前已启用且参数 schema 为 object 的工具;能否注入 outputRequest 由工具 schema 决定,不维护固定工具名单。codemode 是唯一被跳过的工具:Pi 的 grammar 约束采样要求恰好一个必填字符串属性,它的结果原样返回,面板里也没有它的开关。
  • 不注册替代工具,不改变原工具的执行语义,也不要求额外安装独立的 pi-tool-display 宿主包。
  • 文本提炼是有损操作;完整性要求应使用 RAW。
  • 非文本结果是完整性边界:图片、音频、二进制和混合 content 不进入文本提炼链路。
  • 超长输出不再由 pi-distill 写临时文件或截断,统一交由 Pi 自身的输出限制机制处理,避免上下文无限膨胀。
  • 当前会话没有模型时,提炼会失败并保留原始结果,不阻止 Pi 启动。

配置

默认配置路径:

~/.pi/agent/extensions/pi-distill/config.json

可以从 config.example.json 开始:

{
  "enabled": true,
  "model": "",
  "minChars": 200,
  "maxChars": 100000,
  "maxOutputChars": 10000,
  "timeoutSeconds": 10,
  "timeoutRetryCount": 1,
  "errorRetryCount": 1,
  "missedCompressionRatio": 10,
  "summarizeErrors": true,
  "tools": {},
  "render": {
    "enabled": true,
    "showPrompt": true,
    "showResult": true
  }
}

配置文件字段优先于环境变量。未声明的字段依次回退到 PI_DISTILL_*、旧版 PI_BASH_SUMMARY_* 变量和默认值。

配置项 含义
model 可选的 provider/model;为空时使用当前 Pi 会话模型。
minChars 达到此输出长度后才请求提炼。
maxChars 提炼结果超过此字符数时写入临时文件。
maxOutputChars 最终返回给 Agent 的文本上限。超出后写入临时文件,只返回文件指针。
timeoutSeconds 每次提炼模型尝试的最长等待时间。
timeoutRetryCount 超时后的额外重试次数。默认 1;设为 0 时不重试超时。
errorRetryCount 非超时异常后的额外重试次数。默认 1;设为 0 时不重试其他异常。
missedCompressionRatio 没有提供摘要 prompt 时,用于长输出诊断的倍数阈值。
summarizeErrors 工具返回错误且达到 minChars 时,是否仍发送给提炼模型。
tools.<name>.enabled 按工具开启或关闭 outputRequest 注入和结果提炼。edit 和 write 默认关闭,其他未配置工具默认开启,也可以通过 /config:distill 修改。

/pi-distill 仍作为兼容别名保留。 | render.* | 控制审计卡片、prompt 预览和结果预览。 |

Session 统计

使用 /distill:stats 查看当前 Pi 会话的提炼统计。统计只保存在内存中,在会话开始时重置,不保存原始工具输出。

统计包括工具结果数量、成功/失败/回退次数、模型尝试次数、原始与摘要字符数、压缩比、估算的原文/摘要 Token(启发式:CJK 字符约每字 1 token,其余文本约 4 字符 1 token)、预计节省 Token、提炼实际消耗的 input/output/cache/total Token 和成本。数量达到 1,000 或 1,000,000 时分别使用 k 或 m 紧凑显示;耗时会根据数值显示为 ms、s 或 min。原文/摘要 Token 是估算值;provider 未返回 usage 时,提炼消耗 Token 或成本字段显示为不可用。

主要环境变量包括 PI_DISTILL_MODEL、PI_DISTILL_MIN_CHARS、PI_DISTILL_MAX_CHARS、PI_DISTILL_MAX_OUTPUT_CHARS、PI_DISTILL_TIMEOUT_SECONDS、PI_DISTILL_TIMEOUT_RETRY_COUNT、PI_DISTILL_ERROR_RETRY_COUNT、PI_DISTILL_MISSED_COMPRESSION_RATIO 和 PI_DISTILL_SUMMARIZE_ERRORS。

要求

  • Node.js 22 或更高版本。
  • 当前 Pi 会话需要有可用模型,除非 model 指向一个已配置且可用的模型。

许可证

MIT