Skip to content

[Feature] Plugin API:允许插件向单次请求注入 prompt 层(system / user / assistant) #172

Description

@Alex2ndrite

Problem / 问题

需求:希望每次发送 Prompt 前,把预先设定好的一段文本插入到本次会话内容的最前/后面,并且可以指定它以 systemuserassistant 的身份出现。典型场景是按"预设"切换开场上下文、人设或前置约束,并希望它以对话轮次(而不是系统提示)的形式被模型看到。

现状:插件体系里没有任何可用的注入点(已在安装版 0.14.2 上逐项核对):

  1. 插件 API 没有写入会话的入口。 plugin-host-process.jsbuildApi() 与会话相关的只有三项:session.getLlmContext()(只读)、agent.complete()(另起一次独立补全,不影响当前会话)、agent.registerTool()(模型在回合内主动调用)。没有 session.appendMessage / prompt.prepend / history.push 之类的接口。

  2. 事件是单向的。 events.on() 注册的监听器返回值被宿主忽略(handleHostEvent 只调用监听器、不消费返回值),bus.publish 只在插件之间传递消息。二者都无法改变发往模型的请求。

  3. 运行时没有"请求前"钩子。 resources/agent-runtime/sidecar.js 里不存在 before_agent_start / beforePrompt / transformPrompt / prependMessage 等扩展点;会话历史只在运行时构造时读一次(historyToEntries()),用户输入由 prompt(input, userMessageId, durableTurnId) 直接送入 agent。独立 CLI 的 ~/.pi/agent/extensions/*.ts 钩子不会被 sidecar 加载,因此在 PI-Desktop 里不会触发。

  4. agent.prompt.inject 权限管不到这里。 它目前只用于放行 contributes.skills:把技能目录插进 baseSystemPrompt(位置在 Project instructions 之前),且技能正文需要模型主动调用 Skill 工具才加载。既不能注入 user / assistant 消息,也无法插入到会话内容最前面。

  5. 存储也够不到。 插件 fs 网关的 root 只能是 workspaceuserSelected,无法访问家目录;会话数据在宿主 SQLite(pi.sqlite)与运行时内存中,即使改写数据库也不会进入正在进行的请求。

  6. 现有近似方案的能力边界:把文本写入 ~/.pi/agent/AGENTS.md 的受管区块,依赖宿主每回合重新解析指令链来生效。但它只能产生 system 级、且固定落在 Project instructions 段落内的追加,无法产生 user/assistant 轮次,也无法位于"会话内容最前面"

Proposed change / 期望改动

建议提供"prompt 层"(prompt layer)机制,两种形态可以只做其一:

方案 A(首选):声明式贡献 + 运行时覆盖

{
  "permissions": ["agent.prompt.inject"],
  "contributes": {
    "promptLayers": [
      {
        "id": "preamble",
        "role": "user",
        "position": "start",
        "text": "",
        "when": { "mode": ["agent"] }
      }
    ]
  }
}
// 动态文本(例如面板里切换预设后即时生效)
await pi.prompt.setLayers("preamble", {
  role: "user",        // system | user | assistant
  position: "start",   // start | end
  text: "…",
  enabled: true,
});

方案 B:仅运行时 RPC,不做 manifest 声明(实现更小,但插件必须在 onLoad 里注册)。

期望的语义(请一并明确,避免歧义):

  • 临时性:层内容只对本次请求有效,不写入 transcript、不渲染成消息气泡、不计入用户轮次。
  • system:按声明顺序合并进 system prompt,位置在 Project instructions 之后(可与现有 pluginSkillsPrompt() 使用同一拼接点)。
  • user / assistant:插入消息数组的头部或尾部,且每回合重新计算,同一层不跨回合累积。
  • 多插件确定性:排序键为 position → 插件 id → 声明顺序。
  • 上限与校验:单层与总量的字节上限;不支持图片/附件;textpath 二选一。
  • provider 兼容性提示:部分 provider 对首条消息角色有要求(例如首条必须是 user),position: "start" + role: "assistant" 可能被拒;希望宿主侧做校验或给出明确错误码,而不是静默失败。
  • 缓存影响:注入内容若落在前缀位置,会影响 prompt cache,建议在文档中说明。
  • 安全:新增独立权限或复用 agent.prompt.inject(高风险);默认关闭;请求含插件注入内容时 UI 上要有可见标记(例如 composer 的提示),并写入审计事件。

建议的实现位置(沿用现有插件内容下发链路):宿主在组装运行时配置时已经把插件内容传给 sidecar(pluginToolspluginSkills,sidecar 侧由 params.pluginSkills 接收),promptLayers 可走同一条路;拼装点在 sidecar 的 composeSystemPrompt()(system 层)与 prompt()(user/assistant 层,请求发出前)。

Alternatives / 其他方案

不做插件级注入,改为内置功能:在设置里提供"Prompt 层"(角色 + 位置 + 文本 + 开关),或支持"每条消息自动附加前缀"。对"按预设切换前置文本"这个具体需求,内置实现风险更低、落地更快;插件化主要对生态有价值。

Additional context / 补充信息

  • 环境:PI-Desktop 0.14.2(Windows 11,安装版);

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions