npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jeik/prompt-rewriter

v1.1.0

Published

按 YAML 模板,在**消息进 AI 之前**和**回复发出之前**做结构化包裹,并可让 AI 给回复**打标记**,配合钉钉连接器精准认定最终答案。

Readme

prompt-rewriter (YAML)

按 YAML 模板,在消息进 AI 之前和回复发出之前做结构化包裹,并可让 AI 给回复打标记,配合钉钉连接器精准认定最终答案。

三件事:

  1. 入站包裹 —— 把用户原文塞进你的模板(如先加一层"权限审查"约束),再交给 AI。
  2. 出站包裹 —— 把 AI 的最终回复塞进你的模板(如加免责声明 / 署名后缀)。
  3. 打标记(默认关,安装向导引导开启)—— 注入指令让 AI 给过程段加 [-process-]、最终答案加 [-final-],钉钉卡片据此精准认定最终答案。

安装

方式一:npm(推荐)

插件已发布到 npm。用自带的安装向导,一条命令完成「装插件 + 开启所需 hook 权限」:

npx -y @jeik/prompt-rewriter install

向导会问是否开启打标记功能(默认关);开启时再问注入到系统提示词还是用户指令,并自动写入所需的 allowPromptInjection 权限、在 ~/.openclaw/prompt-rewriter/config.yaml 生成带注释示例的配置(marker 段按选择启用)。最后提示重启。

只想装、之后自己配权限(见下方"必须的配置"):

openclaw plugins install @jeik/prompt-rewriter --force

--force 即为覆盖更新,升级时同样用它,无需先卸载。

方式二:本地构建产物(开发 / 离线)

npm install && npm run build && npm pack   # → jeik-prompt-rewriter-1.0.0.tgz
openclaw plugins install ./jeik-prompt-rewriter-1.0.0.tgz --force

之后

# 写规则配置(可选,不写也能跑——默认只注入打标记指令):~/.openclaw/prompt-rewriter/config.yaml
openclaw gateway restart

必须的配置(否则入站包裹/打标记不生效)

入站钩子 before_prompt_build 属于 prompt-injection 类,必须在 openclaw 主配置里显式放行:

"plugins": {
  "entries": {
    "prompt-rewriter": {
      "enabled": true,
      "hooks": {
        "allowPromptInjection": true   // ← 入站包裹 + 打标记指令,缺它直接被网关拦掉
      }
    }
  }
}

出站包裹用的 reply_payload_sending 钩子不需要额外开关。 排错时若日志出现 typed hook "before_prompt_build" blocked by ... allowPromptInjection=false,就是这里没配对。


热重载

config.yaml 按文件 mtime 缓存:改规则 / markers 后,下一轮消息即生效,无需 gateway restart。

  • 解析失败时保留上一份有效配置(避免写到一半读到坏 YAML 把规则抹掉)。
  • 装插件、改 allowPromptInjection 等主配置后仍需重启网关。

配置文件 ~/.openclaw/prompt-rewriter/config.yaml

# 打标记(默认【关闭】;不写 markers 块或不设 enabled 即关闭)
markers:
  enabled: true                # 唯一开关,默认 false;需显式 true 才注入"打标记"指令(安装向导可引导写入)
  inject: system               # user=用户指令 / system=系统提示(默认 system)
  # instruction: |             # 可选:覆盖下面的默认注入文案(自定义时务必保留固定标记 [-process-] / [-final-])
  #   【输出格式要求 / 必须遵守】
  #   1. 你在思考、查资料、执行中间步骤时输出的每一段过程性内容,【末尾】加标记:[-process-]
  #   2. 你的最终答案(给用户看的正式回复)【末尾】加标记:[-final-]
  #   3. 整轮对话中 [-final-] 只能出现一次,且必须在最终答案的最末尾。
  #   4. 标记只放在末尾、原样输出即可,不要放在开头、不要解释这些标记,也不要把它们包进代码块。

# 包裹规则(可多条)
rules:
  - name: "回复加免责声明"        # 可省,日志里好认
    placeholder: "【agent_reply】" # 必填,模板里要被替换的标记
    where: reply                 # prompt / reply / both,默认 both
    enabled: true                # 可省,默认 true
    inject: user                 # 仅入站生效:user=用户指令(默认) / system=系统提示
    template: |                  # 必填,必须包含 placeholder
      【agent_reply】

      ---该内容由 AI 生成,请注意辨别

字段

| 字段 | 必填 | 说明 | |---|---|---| | placeholder | ✅ | 模板里被替换成实际内容的标记,如 【user_message】、【agent_reply】 | | template | ✅ | 完整模板,必须包含 placeholder;出现几次替换几次 | | where | | prompt(入站)/ reply(出站)/ both(默认) | | inject | | 注入通道,仅入站生效:user=用户指令(默认)/ system=系统提示 | | name | | 备注名,出错/日志里能认出 | | enabled | | 默认 true,设 false 临时停用 |

where 精确含义:

  • prompt —— 网关收到渠道消息后、送进 AI 之前。用 event.prompt(用户整段原文)填模板。
  • reply —— AI 最终回复生成后、发到渠道之前。用回复正文填模板(仅 kind==="final")。
  • both —— 两端都套(默认)。

入站只取第一条命中的 prompt 规则;出站只取第一条命中的 reply 规则。

注入通道:用户指令 vs 系统提示(inject)

入站注入分两条通道,本质都是"进 AI 之前的提示词注入",区别在塞到哪一层:

| inject | 叫法 | 注入到 | 适合 | |---|---|---|---| | user(默认) | 用户指令 | 用户消息层(prependContext) | 跟当前这条用户消息强相关的包装,如"把用户需求包成带约束的指令" | | system | 系统提示 | 系统提示层(prependSystemContext,可被 provider 缓存省 token) | 每轮都一样的静态约束,如打标记格式、全局人设 |

  • prompt 模板默认 user(用户指令);markers 默认 system(系统提示)。
  • 同一通道有多段(如 prompt 模板 + markers 都设成 system),按出现顺序用空行拼接。
  • inject 对出站 reply 规则无意义(出站是改投递文本,不分系统/用户层),会被忽略。

工作原理(重要:回复"两条流")

OpenClaw 把 AI 回复分成两条互不同步的流,这决定了出站包裹的可见范围:

| 流 | 谁显示它 | 本插件怎么处理 | |---|---|---| | 持久化消息 | webchat 控制台、历史记录 | 不碰(避免把模板写进上下文污染后续轮次) | | 投递 payload | 各渠道实际发送的内容 | reply_payload_sending 包裹(不污染历史) |

后果(属正常现象,非 bug):

  • webchat 控制台看不到出站模板——它显示的是持久化原文。这是有意为之:控制台展示模型真实输出。
  • 钉钉等渠道能看到——渠道发的是投递 payload。

钉钉连接器用自定义 dispatcher,核心不会自动跑 reply_payload_sending;配套的钉钉连接器修复版会在投递时显式调用本插件的出站钩子,所以钉钉能正确套上模板。用其它标准渠道(如 Telegram)则由核心自动触发。

钩子一览:

| 时机 | 钩子 | 作用 | 需要的开关 | |---|---|---|---| | 入站 | before_prompt_build | prompt 模板(prependContext)+ 打标记指令(prependSystemContext) | allowPromptInjection: true | | 出站 | reply_payload_sending | reply 模板包裹投递文本(kind==="final") | 无 |


打标记 + 钉钉精准渲染

痛点:AI 常分多段输出(思考过程 + 最终答案),到达顺序会乱,钉钉卡片可能把中间段当成最终、提前定稿。

方案:

  1. 本插件(默认关,开启后)注入指令,让 AI 给过程段加 [-process-]、最终答案加 [-final-]。
  2. 钉钉连接器修复版据此:
    • 带标记 → 只认 [-final-] 后的内容当最终答案,其余当过程;
    • 不带标记 → 兜底用 openclaw 的 isReasoning 标签选"最近一段非思考的正式答案",不再靠时序猜;
    • 标记对用户不可见(渲染前剥离);
    • AI 漏标 [-final-] → 自动退回兜底,绝不卡空白;
    • 选定最终答案后再套上你的 reply 固定模板。

不想用标记:markers.enabled: false,或干脆不把指令喂给 AI(连接器检测不到标记就自动走兜底)。


示例

见 examples/:

| 文件 | 用途 | |---|---| | 01-permission-gate.yaml | 入站包一层权限审查约束 | | 02-reply-wrap.yaml | 出站给回复加前缀/后缀 | | 03-bilingual-prompt.yaml | 同段原文在模板里出现多次(演示全部替换) | | 04-both-sides.yaml | where: both 两端都管 |

cp examples/02-reply-wrap.yaml ~/.openclaw/prompt-rewriter/config.yaml
openclaw gateway restart

测试与验证

插件每个钩子、每个分支都打中文日志(前缀 [prompt-rewriter])。装好发条消息后:

grep prompt-rewriter /tmp/openclaw/openclaw-*.log | tail -30

应能看到:

  • 启动:插件加载完成,共 N 条规则,打标记=开
  • 入站:入站钩子 before_prompt_build 触发… → 规则「…」生效,注入 N 字到 prompt 前 / 注入打标记系统指令(N 字)
  • 出站:出站钩子 reply_payload_sending 触发:kind=final… → 规则「…」生效,回复已套模板

钉钉连接器侧(确认走了哪条认定):

[DingTalk][closeStreaming] 最终答案来源=marker[-final-] / 非reasoning答案 / accumulatedText兜底

排错

| 现象 | 原因 / 处理 | |---|---| | 入站不生效,日志 blocked by ... allowPromptInjection=false | 主配置 plugins.entries.prompt-rewriter.hooks.allowPromptInjection 没设 true | | 改了源码不生效 | 要 npm run build → 重新 install --force → 重启;网关跑的是装进 extensions/ 的构建产物 | | webchat 看不到出站模板 | 正常——webchat 显示持久化原文,模板在投递层。看渠道实际发送内容 | | 钉钉回复没套上模板 | 需安装配套的钉钉连接器修复版(它在投递时显式跑本插件出站钩子) | | AI 不打标记 | 确认 allowPromptInjection: true(指令走入站钩子);连接器会自动走兜底,仍能稳定 | | 回复变成"回显用户原话" | 旧版误用了 before_agent_reply(拦截钩子)。当前版本已改用 reply_payload_sending,升级即可 |


开发

npm install
npm run build      # esbuild → dist/index.js
npm pack           # → jeik-prompt-rewriter-1.0.0.tgz

源码:

  • index.ts —— 钩子注册(入站 / 出站 / 打标记注入)
  • src/config.ts —— 读 / 校验 yaml
  • src/rewriter.ts —— 规则类型、模板渲染、标记常量与默认指令