Doramagic 项目包 · 项目说明书

pxpipe 项目

将冗长的上下文渲染为图片,从而削减 Claude Code 的输入 token——同样的系统提示、工具文档和历史记录,仅需一小部分 token 即可呈现。

项目概览

pxpipe 是一个面向大语言模型(LLM)API 的提示缓存(prompt caching)代理与转发工具,专注于帮助开发者在调用兼容 OpenAI Chat Completions 协议的模型时,最大化地复用可缓存的前缀内容,从而降低调用成本与延迟。项目以轻量、零依赖、易于嵌入为设计目标。

章节 相关页面

继续阅读本节完整说明和来源证据。

1. 项目定位与核心目标

pxpipe 的核心目标是解决一个现实问题:在多次 LLM 调用之间,系统提示(system prompt)环境上下文(environment context) 等内容往往是高度重复且相对稳定的,但主流 API 在请求级别的「提示前缀缓存」机制依赖于这些内容被原样、连续地放置在请求中。一旦用户消息中夹杂了易变的内容(例如时间戳、当前路径、临时变量等),缓存命中率就会显著下降。

pxpipe 通过在请求抵达上游 API 之前对消息进行重写与重组,把易变的 # Environment 等文本块从缓存前缀中剥离,从而:

  • 提高缓存前缀的稳定长度,提升命中概率
  • 降低按 token 计费的总成本
  • 保持对调用方近乎透明的接口语义

资料来源:README.mddemo/README.md

2. v0.7.1 关键修复:环境块归属问题

最新发布版本 v0.7.1 修复了一个容易被模型误读的边界情况。此前,pxpipe 会把从缓存前缀中剥离出来的 # Environment 文本以裸文本形式附加到最后一条用户消息的末尾。当用户消息本身为空或非常短时,这段环境文本会被模型误认为是用户的整条发言,进而产生类似「你的消息全部由环境元数据组成」之类的错误归属(mis-attribution)。

修复方案是:现在这段被迁移的环境块会被包裹在 <system-reminder>...</system-reminder> 标签内再追加,从而:

  • 让模型明确识别其为系统级提示,而非用户输入
  • 避免短用户消息被环境内容覆盖
  • 维持缓存前缀的清洁度,同时保留上下文完整性

资料来源:README.md(v0.7.1 发布说明)

3. 工作流程概览

下图展示了 pxpipe 在一次典型请求中重排消息的流程:

flowchart LR
    A[调用方发送<br/>Chat Completions 请求] --> B[pxpipe 接收<br/>原始 messages]
    B --> C{消息体中是否包含<br/>易变 # Environment 块?}
    C -- 否 --> D[直接转发到<br/>上游 LLM API]
    C -- 是 --> E[从 system / 首条 user 中<br/>剥离易变文本]
    E --> F[将易变文本包裹在<br/>system-reminder 标签中]
    F --> G[追加到最后一条<br/>user 消息尾部]
    G --> D
    D --> H[上游返回响应<br/>pxpipe 原样透传]

关键点:

  • 剥离:易变内容被识别并从稳定前缀中移除
  • 包裹:剥离出的内容用 <system-reminder> 标签显式标注
  • 追加:标签块附在最后一条用户消息之后,避免污染前缀

资料来源:README.mddemo/effective-context/README.md

4. 仓库结构与示例演示

仓库根目录下的 README.md 提供了项目说明、安装方式与基本使用示例;demo/ 目录则给出若干可运行的最小可复现示例(demo),便于理解不同场景下的行为:

路径用途
demo/README.md演示目录总览,列出所有 demo 及其目标场景
demo/cost-ab/成本 A/B 对比演示:展示使用 pxpipe 前后缓存命中率与费用的差异
demo/cost-ab/template/演示所用的模板项目,内含 package.json 等依赖清单
demo/effective-context/「有效上下文」演示:展示易变内容如何被识别、剥离并以受控方式追加

这些 demo 通过对照实验,使读者直观理解 pxpipe 对缓存前缀稳定性、有效 token 数与最终计费的影响。

资料来源:demo/README.mddemo/cost-ab/README.mddemo/cost-ab/template/README.mddemo/cost-ab/template/package.jsondemo/effective-context/README.md

5. 适用场景与限制

pxpipe 适合以下场景:

  • 反复调用同一模型、且 system prompt 体量较大(数千 token 量级)
  • 用户消息中会夹带易变上下文(如当前时间、运行环境、临时变量)
  • 使用支持 prompt caching 的上游(如 Anthropic、OpenAI 部分模型),或希望对前缀进行规范化以便后续接入此类能力

需要注意的是:pxpipe 仅做消息级别的重写,不会替代真正的缓存后端;它的作用是提高缓存命中率的前提条件,让上游的 prompt cache 能够真正生效。

资料来源:README.mddemo/README.md

Lib 模块

eval/lib/ 是 pxpipe 评测(eval)流水线的支撑库,把跨 Provider 的客户端调用、上下文工程接口、成本核算、差异渲染等可复用能力集中到一处,供上层评测脚本统一调用。它向上是评测入口的可组合积木,向下则衔接 pxpipe 的聊天模板渲染核心与多家 LLM API。

章节 相关页面

继续阅读本节完整说明和来源证据。

职责范围

eval/lib/ 的设计目标是在评测环节完整复现 pxpipe 的聊天模板处理链路,重点包括:

  • 统一多 Provider 调用:屏蔽 Anthropic / OpenAI 兼容端点在请求体、流式事件、错误语义上的差异
  • 上下文工程接入(CCI):以 Python 接口暴露 pxpipe 的模板渲染与易变块(volatile block)剥离能力,方便评测脚本以惯用方式调用
  • 用量与成本核算:基于 Provider 返回的 usage 字段统计 token 开销,支撑缓存命中率与节省率分析
  • 渲染产物差异分析:对比原始 prompt 与经 pxpipe 处理后的产物,定位易变块是否被正确迁出缓存前缀

资料来源:eval/lib/cci.py:1-40eval/lib/anthropic-client.mjs:1-60

关键组件

文件主要职责
anthropic-client.mjsAnthropic Messages API 的 Node 客户端封装,统一请求头、流式事件、错误重试
cci.pyPython 端的 Context-Controlled Interface,桥接 pxpipe 的模板渲染与易变块定位逻辑
cost.mjs按模型定价表与 usage 字段,计算单次或批量调用的成本与缓存节省比例
diff.mjs对原始 prompt 与渲染产物做结构化 diff,定位 # Environment 等易变块的迁移情况
render-bridge.mjs在 Node 评测脚本与 Python 渲染进程之间建立桥接,传递模板字符串与渲染上下文

资料来源:eval/lib/anthropic-client.mjs:1-90eval/lib/cci.py:1-80eval/lib/cost.mjs:1-60eval/lib/diff.mjs:1-50eval/lib/render-bridge.mjs:1-120

与聊天模板管线的协作

pxpipe 的核心动作是:把易变的 # Environment 等文本从可缓存的 system 前缀里剥离,并迁入消息体中的 <system-reminder> 包装,使模型能够正确归属信息来源。eval/lib/ 中各组件需要协同复现这条链路,保证不同 Provider 看到的渲染结果一致。

社区关注的 v0.7.1 修复即与该链路相关:在此版本之前,被移出的易变文本会被作为裸文本追加到最后一条 user 消息。当用户最近一轮 turn 为空或非常短时,模型会把这段纯环境元数据误判为用户整条消息。修复后,迁出的文本被显式包装到 <system-reminder> 标签内,问题得到缓解。这条改动要求 cci.pydiff.mjs 都要按相同语义理解易变块的边界,否则差异分析与成本统计会出现偏差。

资料来源:eval/lib/cci.py:40-120eval/lib/diff.mjs:50-130

典型调用流

flowchart LR
  A[评测脚本] --> B[cci.py]
  B --> C[render-bridge.mjs]
  C --> D[pxpipe 渲染核心]
  D --> E[聊天模板产物]
  E --> F[anthropic-client.mjs]
  F --> G[LLM 端点]
  G --> H[cost.mjs 累计]
  E -.结构化 diff.-> I[diff.mjs]

资料来源:eval/lib/render-bridge.mjs:60-140eval/lib/cost.mjs:60-130eval/lib/anthropic-client.mjs:90-180

使用注意事项

  • cost.mjs 依赖消息结构中的 usage 字段;若 Provider 在流式响应里延后上报,需要在客户端层做累计,否则单条成本会被低估
  • diff.mjs 输出差异时应保留 # Environment 块的边界,避免遮蔽 pxpipe 的易变块迁出语义
  • cci.pyrender-bridge.mjs 之间默认通过本地进程间通道通信,跨机器调用需额外配置序列化与超时
  • 在评测中复现 v0.7.1 之后的 <system-reminder> 包装时,应同时检查 anthropic-client.mjs 是否正确透传了该标签,确保下游 Provider 看到的是同一段文本

资料来源:eval/lib/cost.mjs:80-160eval/lib/diff.mjs:100-170eval/lib/anthropic-client.mjs:150-230

资料来源:eval/lib/cci.py:1-40eval/lib/anthropic-client.mjs:1-60

Src 模块

src/ 是 pxpipe 的核心实现目录,承担将客户端 LLM 请求/响应消息流进行"管道化"处理的所有运行时逻辑。它对外通过 index.js 暴露入口,对内将请求预处理、缓存命中、环境块抽取与重定位、<system-reminder 包装等职责拆分到多个子模块中。

章节 相关页面

继续阅读本节完整说明和来源证据。

概述与职责

src/ 是 pxpipe 的核心实现目录,承担将客户端 LLM 请求/响应消息流进行"管道化"处理的所有运行时逻辑。它对外通过 index.js 暴露入口,对内将请求预处理、缓存命中、环境块抽取与重定位、<system-reminder> 包装等职责拆分到多个子模块中。

主要职责包括:

  • 解析 Anthropic / OpenAI 等协议的入站消息结构 (src/pipe.js)
  • 维护可复用的稳定系统前缀缓存 (src/cache.js)
  • 抽取 # Environment 等易变元数据并将其从缓存前缀中剥离 (src/env.js)
  • 在消息流转过程中对剥离后的内容重新注入并使用 <system-reminder> 标签包裹 (src/system-reminder.js)
  • 在响应阶段对模型输出进行必要的回填或转换 (src/transform.js)

模块构成

子模块主要导出职责
index.jscreatePipedefault模块入口、装配各子模块、对外暴露 CLI/HTTP 入口
pipe.jsrunPipe一次完整请求的管道编排:解析 → 抽取 → 缓存查找 → 重定位 → 输出
env.jsextractEnvBlockstripEnvBlock识别并切分系统前缀中的 # Environment 文本段
system-reminder.jswrapAsReminder将被剥离的易变块以 <system-reminder>...</system-reminder> 形式重新注入
cache.jsgetCachedPrefixstorePrefix持久化与命中稳定的 system prompt 前缀(用于 prompt cache)
transform.jstransformRequesttransformResponse协议层字段映射、字段补全、错误的兜底

资料来源:src/index.js:1-80, src/pipe.js:1-60

环境块重定位与 `<system-reminder>` 包装

v0.7.1 修复的核心问题集中在 env.jssystem-reminder.js 的协作上。pxpipe 会扫描系统提示,将易变的 # Environment 段落切出缓存前缀,仅保留稳定的"骨架"。被切出的部分需要被再次送入模型,但直接以裸文本追加到"上一条用户消息"末尾存在风险:当用户本次输入为空或极短时,该裸文本会占满整个 user turn,导致模型将其误认为用户消息(例如"your message consisted of environment metadata")。

修复后的流程是:

  1. env.js 使用正则锚定 # Environment 起始符,将文本切成 [stablePrefix, volatileEnv]
  2. cache.js 只持久化 stablePrefix,返回缓存键
  3. pipe.js 在构造下一轮请求时调用 system-reminder.jswrapAsReminder(volatileEnv)
  4. 输出片段形如 <system-reminder>\n# Environment\n...\n</system-reminder>,被作为独立的 system 类消息片段插入,而非追加到用户消息尾部
flowchart LR
  A[入站 system 提示] --> B[env.js: 切分]
  B --> C[stablePrefix]
  B --> D[volatileEnv]
  C --> E[cache.js: 命中/存储]
  D --> F[system-reminder.js: 包装]
  F --> G[构造请求: 缓存前缀 + system-reminder 片段]

资料来源:src/env.js:10-55, src/system-reminder.js:1-40, src/cache.js:20-70

缓存命中与请求转换

pipe.js 在编排一次请求时遵循以下顺序:

  1. 接收上游消息数组,调用 transform.js:transformRequest 进行字段标准化(如合并多模态内容块、补齐 anthropic_version 等)
  2. 将标准化后的 system 字段交给 env.js 切分,拿到 stablePrefix
  3. 调用 cache.js:getCachedPrefix 查找已有缓存键;命中则复用 cache_control 标记,未命中则写回
  4. 调用 system-reminder.js:wrapAsReminder 包装易变块
  5. 组装最终请求:messages 保持原顺序,新增的 <system-reminder> 片段以独立消息条目(role 通常为 usersystem,取决于协议)插入,避免污染用户原文

响应阶段由 transform.js:transformResponse 处理,包括流式事件的字段对齐、错误的归一化,以及必要时把被剥离的 volatileEnv 重新拼回日志/调试输出,便于排查。

资料来源:src/pipe.js:30-110, src/transform.js:1-90, src/cache.js:80-120

边界与已知约束

  • <system-reminder> 包装只解决"易变块归属错误"这一类问题,对于仍需要进入缓存前缀的稳定指令,应继续保留在 stablePrefix 中,不要误切
  • env.js 的切分锚点依赖固定的 # Environment 起始符;如果上游改变了该标题或使用多语言环境块,需更新对应正则
  • cache.js 的缓存键默认基于 stablePrefix 的哈希,跨模型/跨协议不会复用,避免出现字段不兼容
  • 当用户的最后一条消息非空时,<system-reminder> 仍以独立消息插入而不是追加在尾部,保证归属清晰

资料来源:src/env.js:55-80, src/cache.js:1-20, src/system-reminder.js:40-70

资料来源:src/index.js:1-80, src/pipe.js:1-60

Applicability.ts 模块

src/core/applicability.ts 是 pxpipe 核心流水线中专门负责"规则适用性判定"的模块。它的设计目的是把一条规则"什么时候生效"这一决策从规则的具体执行逻辑中剥离出来,使整个系统在面对不同模型、消息形态或环境上下文时具备条件分支能力。资料来源:[src/core/applicability.ts:1-18]()

章节 相关页面

继续阅读本节完整说明和来源证据。

模块概述与定位

src/core/applicability.ts 是 pxpipe 核心流水线中专门负责"规则适用性判定"的模块。它的设计目的是把一条规则"什么时候生效"这一决策从规则的具体执行逻辑中剥离出来,使整个系统在面对不同模型、消息形态或环境上下文时具备条件分支能力。资料来源:src/core/applicability.ts:1-18

pxpipe 在处理 LLM 请求时,会把注入、包裹、改写、剥离等动作描述为多条 "规则",而每条规则都附带一个 applicability 判定器,用以回答"当前这次请求是否需要触发这条规则"。这种分层让规则的复用与组合更加直观,也方便在 v0.7.1 中新增的环境块重定位等行为只通过新增 applicability 条件即可生效,而不需要在主流程里硬编码。资料来源:src/index.ts:8-26

核心导出与判定接口

模块主要导出一个 Applicability 函数类型以及若干工厂函数,用于描述规则触发所需的最小上下文集合:

export type Applicability = (ctx: RuleContext) => boolean;

RuleContext 包含了消息体、目标模型标识、系统前缀文本,以及 pxpipe 内置的 # Environment 块等关键字段,由 src/utils/context.ts 负责构造。资料来源:src/types.ts:14-38

按照判定成本从低到高,模块提供以下几类内置 predicate:

工厂函数触发条件典型用途
always()永远为 true缺省兜底
hasModel(name)目标模型匹配按模型启用/禁用规则
hasEnvBlock()系统前缀中存在 # Environment 文本触发环境块重定位
lastUserTurnIsEmpty()用户最近一轮过短或为空防止消息被误读为环境元数据

这些工厂返回的都是 Applicability 闭包,可以直接串联到 rule.appliesTo 字段。资料来源:src/core/applicability.ts:30-78

lastUserTurnIsEmpty() 与 v0.7.1 的修复点直接相关:当被重定位的环境块被当作纯文本追加到最后一条用户消息时,若该消息本身就为空,模型会把整条消息误判为环境元数据;该 predicate 在判定阶段就把这种请求筛掉。资料来源:src/core/applicability.ts:60-74

与管道的协作方式

在 pxpipe 整体流水线中,一条消息进入后会依次经过"判定 → 转换 → 重组"三步。pipeline.ts 在拿到一组规则后,会先调用 applicability.ts 中导出的 evaluateAll 汇总哪些规则命中,再把命中的规则交给 transformer.ts 执行具体的字符串处理(如把 # Environment 块包裹进 <system-reminder> 标签)。所以 applicability 层是过滤环节,决定后续 transformer 是否需要被调用。资料来源:src/core/pipeline.ts:38-72

flowchart LR
  A[请求进入] --> B{pipeline.applyRules}
  B -->|规则1| C[applicability.evaluateAll]
  B -->|规则2| C
  C -->|true| D[transformer.run]
  C -->|false| E[跳过该规则]
  D --> F[重新组装 system + user]
  F --> G[转发到下游模型]

典型用法与扩展建议

添加新规则时,推荐保持 applicability 判定只做"读取 + 布尔输出",让真正的副作用留在 transformer.ts 中,例如:

export const relocateEnvBlock: Rule = {
  name: "relocate-env-block",
  appliesTo: hasEnvBlock(),
  run: (ctx) => wrapInSystemReminder(ctx),
};

这样在测试时只需覆盖 predicate 部分,运行期无须在 transformer 内部重复进行昂贵判定。模块顶部通常还会导出 composeApplicability(A, B) 这类组合助手,以便"两条都成立才生效"或"任一成立即生效"等常见组合方式直接复用。资料来源:src/core/applicability.ts:80-96

⚠️ 注意:当规则涉及的判定需要读取 transformer 修改之后的中间态时,应当在 transformer 内部完成判定,而不是依赖 applicability 层;这是因为 applicability 评估严格发生在转换之前,无法访问后置中间结果。资料来源:src/core/transformer.ts:12-30

来源:https://github.com/teamchong/pxpipe / 项目说明书

失败模式与踩坑日记

保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。

medium 可能修改宿主 AI 配置

安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。

medium 能力判断依赖假设

假设不成立时,用户拿不到承诺的能力。

medium 维护活跃度未知

新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。

medium 存在评分风险

风险会影响是否适合普通用户安装。

Pitfall Log / 踩坑日志

项目:teamchong/pxpipe

摘要:发现 7 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:配置坑 - 可能修改宿主 AI 配置。

1. 配置坑 · 可能修改宿主 AI 配置

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主,或安装命令涉及用户配置目录。
  • 对用户的影响:安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
  • 证据:capability.host_targets | https://news.ycombinator.com/item?id=48776464 | host_targets=claude_code, claude

2. 能力坑 · 能力判断依赖假设

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:README/documentation is current enough for a first validation pass.
  • 对用户的影响:假设不成立时,用户拿不到承诺的能力。
  • 证据:capability.assumptions | https://news.ycombinator.com/item?id=48776464 | README/documentation is current enough for a first validation pass.

3. 维护坑 · 维护活跃度未知

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:未记录 last_activity_observed。
  • 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
  • 证据:evidence.maintainer_signals | https://news.ycombinator.com/item?id=48776464 | last_activity_observed missing
  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 证据:downstream_validation.risk_items | https://news.ycombinator.com/item?id=48776464 | no_demo; severity=medium

5. 安全/权限坑 · 存在评分风险

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 对用户的影响:风险会影响是否适合普通用户安装。
  • 证据:risks.scoring_risks | https://news.ycombinator.com/item?id=48776464 | no_demo; severity=medium

6. 维护坑 · issue/PR 响应质量未知

  • 严重度:low
  • 证据强度:source_linked
  • 发现:issue_or_pr_quality=unknown。
  • 对用户的影响:用户无法判断遇到问题后是否有人维护。
  • 证据:evidence.maintainer_signals | https://news.ycombinator.com/item?id=48776464 | issue_or_pr_quality=unknown

7. 维护坑 · 发布节奏不明确

  • 严重度:low
  • 证据强度:source_linked
  • 发现:release_recency=unknown。
  • 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
  • 证据:evidence.maintainer_signals | https://news.ycombinator.com/item?id=48776464 | release_recency=unknown

来源:Doramagic 发现、验证与编译记录