范围: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-8bit | text, image | 262144 | 高质量编码任务 |
ThinkingCap-Qwen3.6-27B-oQ4e-mtp | text, image | 262144 | 默认本地编码模型、快速迭代 |
当前 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 位于白名单时注入。协议短小,避免长期占用本地模型上下文:
- 编辑前读取项目说明和相关代码。
- 非平凡任务先给出简短计划。
- 只做满足请求的最小改动,保留用户已有改动。
- 改动后运行最小相关验证。
- 报告前检查 diff。
- 验证失败先定位原因。
- 未运行验证时必须明确说明。
这复用 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
命令只检测当前已选受管模型,不会探测两个模型,更不会并行测试。检查顺序:
- 确认当前模型为受管 LM Studio 模型。
- 读取 Pi 已注册的 LM Studio
baseUrl和认证信息。 - 请求
GET /v1/models,确认当前模型在服务端暴露。 - 发送一个无副作用的 OpenAI function-calling 请求。
- 确认响应中返回指定的
pi_local_probetool call。 - 把结果写为 Pi session 的
local-doctorcustom 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-workflowpi-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 编排。