Pi 本地 Coding Harness:方案 A 技术设计


范围:Pi 原生能力上的本地模型增强,不替换 Pi agent loop

1. 目标

将 Pi 配置为面向本地 LM Studio 编程模型的稳定 coding harness,保留 Pi 的原生工具、会话、上下文压缩、AGENTS.md、Skills 和扩展生态。

方案 A 的重点不是复制 little-coder 的完整扩展集合,也不是引入多代理编排。它只补三个本地模型常见薄弱点:工作流程不稳定、多个 Pi 进程同时请求模型、模型服务或工具调用协议不可用。

2. 受管模型

受管模型的唯一来源是 ~/.pi/agent/models.json 中的 lmstudio.models:

模型 ID输入上下文窗口用途
Qwen3.6-35B-A3B-MLX-8bittext, image262144高质量编码任务
ThinkingCap-Qwen3.6-27B-oQ4e-mtptext, image262144默认本地编码模型、快速迭代

当前 settings.json 已由用户设为:

{
  "defaultProvider": "lmstudio",
  "defaultModel": "ThinkingCap-Qwen3.6-27B-oQ4e-mtp"
}

方案 A 不覆盖这个选择。两个模型通过 Pi 原生 --model 或交互式模型选择切换。

3. 范围与非目标

包含

  • 仅对两种受管 LM Studio 模型注入编码工作协议。
  • 在多个 Pi 进程间串行化 LM Studio provider 请求。
  • 提供单模型 /local-doctor 健康与工具调用预检。
  • 复用已有 pi-shepherd 的编辑后测试、配置保护和错误提醒规则。

不包含

  • 不修改 Pi 原生 agent loop、文件工具、上下文压缩或 Skills 加载。
  • 不引入子代理、工作流引擎、自动任务拆分或自动模型路由。
  • 不添加输出修复器、知识注入器、Bash 白名单或自定义权限系统。
  • 不对模型做隐式 fallback。
  • 不管理 LM Studio 的模型加载、卸载、Metal 内存上限或其他客户端。

这些能力留给方案 B,且必须建立在方案 A 的使用数据上。

4. 文件布局

~/.pi/agent/
├── extensions/
│   └── local-coding-harness.ts
└── local-coding-harness/
    ├── core.ts
    └── core.test.ts

extensions/ 中只放 Pi factory 入口。Pi 会将该目录中的 TypeScript 文件当作扩展加载;核心模块和测试必须放在目录外,避免被错误地当作 extension factory。

实现文件:

  • local-coding-harness.ts:Pi 生命周期钩子、锁编排、/local-doctor。
  • core.ts:模型白名单、工作协议、tool-call 探针解析、文件锁。
  • core.test.ts:核心行为回归测试。

5. 架构

用户请求
  |
  v
Pi 原生 agent loop
  |
  +-- before_agent_start
  |     +-- 仅受管 LM Studio 模型:追加 Local Coding Protocol
  |
  +-- before_provider_request
  |     +-- 获取 lmstudio-provider.lock
  |
  v
LM Studio OpenAI-compatible API
  |
  v
turn_end / agent_settled / session_shutdown
  |
  +-- 释放 lmstudio-provider.lock

Pi 内建 read、bash、edit、write、grep、find、ls 保持不变。项目规则仍由 AGENTS.md 管理;按需工作流仍由 Skills 管理。

6. Local Coding Protocol

仅在当前 provider 为 lmstudio 且模型 ID 位于白名单时注入。协议短小,避免长期占用本地模型上下文:

  1. 编辑前读取项目说明和相关代码。
  2. 非平凡任务先给出简短计划。
  3. 只做满足请求的最小改动,保留用户已有改动。
  4. 改动后运行最小相关验证。
  5. 报告前检查 diff。
  6. 验证失败先定位原因。
  7. 未运行验证时必须明确说明。

这复用 Codex 式工作纪律,不模拟或替换 Codex 的执行引擎。

7. 模型串行锁

锁文件路径:

~/.pi/agent/lmstudio-provider.lock

实现使用 Node 的原子 open(path, "wx"):

  • 取得锁时记录当前 PID 和取得时间。
  • 已有锁且 PID 存活时,后续 Pi 请求每 250ms 等待一次。
  • 锁文件的 PID 已不存在时,清理遗留锁后重试。
  • 在 turn_end、agent_settled、session_shutdown 释放锁。
  • /local-doctor 也复用同一锁。

因此,两个 Pi 进程不会同时向受管 LM Studio 模型发送 provider 请求。

限制:锁只协调加载本扩展的 Pi 进程。直接调用 LM Studio API、其他 IDE 或其他 agent 不识别此锁。模型切换也不会自动卸载上一模型;为避免 GPU/统一内存争抢,实际使用仍应遵守单模型、单任务串行原则。

8. /local-doctor

在 Pi 会话中执行:

/local-doctor

命令只检测当前已选受管模型,不会探测两个模型,更不会并行测试。检查顺序:

  1. 确认当前模型为受管 LM Studio 模型。
  2. 读取 Pi 已注册的 LM Studio baseUrl 和认证信息。
  3. 请求 GET /v1/models,确认当前模型在服务端暴露。
  4. 发送一个无副作用的 OpenAI function-calling 请求。
  5. 确认响应中返回指定的 pi_local_probe tool call。
  6. 把结果写为 Pi session 的 local-doctor custom entry,不进入 LLM 上下文。

成功记录格式:

LM Studio ready: <model-id>; /models and tool-call probe passed.

9. 使用方式

默认进入 Pi:

pi

选择 35B:

pi --model lmstudio/Qwen3.6-35B-A3B-MLX-8bit

选择 ThinkingCap:

pi --model lmstudio/ThinkingCap-Qwen3.6-27B-oQ4e-mtp

每次切换模型或 LM Studio 重启后,进入 Pi 后执行:

/local-doctor

运行另一个模型前,等待前一模型任务结束。若 LM Studio 已保留上一模型占用内存,先在 LM Studio 侧释放不需要的模型,再启动下一任务。

10. 现有扩展策略

继续保留:

  • pi-shepherd:编辑后测试提醒、settings 保护、失败提醒等。
  • pi-ate-smart-compact:保留手动使用,不在方案 A 中启用自动接管。
  • pi-memory、context-mode、pi-web-access:维持已有用途。

暂不启用:

  • pi-ate-workflow
  • pi-context-manager
  • 多代理或 crew 类扩展
  • 自动模型路由、自动 fallback、输出修复器

理由:这些机制会增加本地模型的上下文、回合数和故障面,当前没有足够失败数据证明其必要性。

11. 验证记录

已完成:

  • bun test ~/.pi/agent/local-coding-harness/core.test.ts:5 passed,0 failed。
  • bun build:local-coding-harness.ts 编译成功。
  • pi --list-models Qwen3.6:两个受管模型均可发现。
  • Pi RPC:/local-doctor 已注册并成功运行 ThinkingCap 的 /models 与 tool-call 探针。
  • 锁文件检查:命令结束后无残留锁。

35B 当前仅完成 Pi 配置发现验证,未重复执行工具调用探针。该选择避免无必要模型切换和 GPU/统一内存竞争。需要使用 35B 时,以单模型方式启动 Pi 后执行 /local-doctor。

12. 方案 B 的进入条件

方案 B 是长期方向,但不应先行堆叠功能。满足下列任一条件后,再基于日志和基准讨论:

  • 多文件任务持续漏测或错误结束。
  • tool-call 格式失败反复出现。
  • 压缩后关键任务状态遗失。
  • 单模型已稳定运行,但复杂任务规划、审查或并行吞吐成为明确瓶颈。
  • 需要让 Pi 之外的多个客户端共同遵守模型资源调度。

方案 B 应从可观测性和任务状态开始,而不是直接引入多代理编排。

13. 已实施的 B0 基础

已加入 session telemetry、验证账本和 /local-report:

  • 记录模型、provider 请求数、锁等待时间、工具调用与错误数、变更文件、验证命令、context 峰值和 compaction 次数。
  • edit 或 write 成功后标记未验证;成功识别 npm、pnpm、bun、pytest、cargo test、go test、tsc、ruff 等验证命令后清除标记。
  • agent settled 且仍未验证时,显示一次非阻塞提醒;下一次本地模型请求会看到未验证状态。
  • /local-report 将当前快照显示并存为 Pi session custom entry,不进入模型上下文。

这层数据用于判断后续是否需要 read-before-edit guard、循环检测、自动压缩或更复杂的方案 B 编排。

发表回复