pi 的 Codemode 机制:配置、细节与区别
一句话:Codemode 是 pi 的一个内置工具,不是独立模式。它让模型写一段 JavaScript 脚本,脚本在 QuickJS 沙箱(WASM 运行时)里调用 pi 的其他工具,包括 MCP 工具。只有脚本的输出回到模型上下文。
沙箱限制很明确:无网络、无文件系统、无定时器、内存上限 256MB。脚本只能通过工具和 models 触达外部世界。
和传统方式的区别:
text
传统方式(direct): 模型 → 声明好的工具 → 结果全量进上下文(每轮重复读) Codemode 方式 : 模型 → 写 JS 脚本 → 脚本并行/循环调用工具 → 只回传压缩后的结果
exposure:最关键的概念
每个 MCP server 的工具注册名是 mcp__<server>__<tool>。server 级的 exposure 决定模型怎么够到它。
| exposure | 行为 | 适用场景 |
|---|---|---|
codemode(默认) | 只能从 codemode 脚本调用;不声明给模型,也不列在 codemode 描述里;脚本用 searchTools()、describeTool()、ALL_TOOLS 找 | 通用 MCP,尤其要脚本组合或过滤调用 |
deferred | 默认不声明,直到 tool_search 加载命中,下一次模型调用可直接用 | 大型 server,发现后想直接调 |
direct | 像内置工具一样直接声明给模型,也能从脚本调 | 小、常用的工具集 |
hidden | 注册但不可达 | 明确禁用 |
codemode-deferred 是 codemode 的别名。
触发机制上有两个自动行为:某个 codemode exposure 的 server 连接时,pi 自动开启 codemode;某个 deferred exposure 的 server 连接时,pi 自动开启 tool_search。
想不自动开,在 mcp.json 顶层加 "autoEnableCodemode": false。想没有 MCP 也开着 codemode,在 settings.json 里加 "defaultTools": ["+codemode"]。
配置文件层级
| 文件 | 位置 | 说明 |
|---|---|---|
| 用户级 MCP | ~/.pi/agent/mcp.json | 个人 server,带凭据的放这 |
| 项目级 MCP | .pi/mcp.json | 项目 server,需要项目信任才加载;可覆盖同名用户 server 的 enabled、exposure、toolExposure |
| 用户设置 | ~/.pi/agent/settings.json | defaultTools、codemode.mode、codemode.inlineBudget |
| 项目设置 | .pi/settings.json | 同上,叠加在用户设置之上 |
mcp.json 里的字段
server 级配置长这样:
json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
"exposure": "deferred",
"timeout": 60,
"enabled": true,
"description": "读本地文件"
},
"docs": {
"url": "https://example.com/mcp",
"headers": { "Authorization": "Bearer ${DOCS_TOKEN}" },
"oauth": { "clientId": "...", "clientName": "..." },
"exposure": "direct"
}
},
"autoEnableCodemode": false
}
关键字段:
-
command、args、env、cwd是 stdio server,command是单个可执行文件,不是 shell 字符串。 -
url、headers、oauth是 streamable HTTP server,旧的 SSE 不支持。 -
type可以是stdio、http、streamable-http,缺省时由 command 或 url 推断。 -
timeout单位是秒,默认 60,每次请求超时,进度通知会重置。 -
enabled为 false 时保留条目但不连接。 -
exposure就是上面那四个值。 -
toolExposure是工具级覆盖,后面细说。 -
description是 server 的一句话描述,进系统提示,也影响 tool_search 排序。 -
autoEnableCodemode是顶层字段,阻止自动激活 codemode。
toolExposure:工具级覆盖
json
"exposure": "deferred",
"toolExposure": {
"get_current_weather": "direct",
"get_*": "codemode"
}
规则是精确名优先,然后通配符,多个通配符按顺序第一个命中。可以用来让 hidden server 只放行个别工具。
settings.json 里的字段
json
{
"defaultTools": ["-codemode"],
"codemode": {
"mode": "on",
"inlineBudget": 3000
}
}
defaultTools 默认是 read,bash,edit,write,启动时启用的工具列表。加号是加,减号是减,纯名是整组替换。
codemode.mode 默认是 on。on 表示已声明工具保留,描述里附上如何从脚本调。only 表示隐藏所有已声明工具,模型只能通过 codemode 触达。
codemode.inlineBudget 默认是 3000,是 codemode 描述里工具声明的 token 预算。超了就用 searchTools() 找,设成 0 只列 namespace。
CLI 选项
sh
pi --tools read,codemode,'mcp__radius__*' # 白名单,mcp__ 前缀可筛 MCP pi --tools +codemode,-write # 加减式 pi --exclude-tools codemode # 禁用(MCP 也适用) pi --exclude-tools 'mcp__*' # 去掉全部 MCP pi --no-mcp # 关 MCP(不关 codemode 本身) pi --no-tools / --no-builtin-tools # 全关 / 关内置
交互式命令
/mcp 看连接、工具数、exposure,也可以当场改 exposure、启停、登录/登出/重连。/mcp login <server>、logout、reconnect 是对应操作。/reload 是改完 mcp.json 后重载,defaultTools 新加的工具会启用。pi mcp add/remove/list 是 shell 级的,不需要会话。
四种 exposure 谁看到什么
| exposure | 模型声明 | codemode 脚本 | tool_search | 适用 |
|---|---|---|---|---|
codemode | 不可见 | 可以 | 也可以加载 | 通用/需组合过滤 |
deferred | 搜索后 | 可以 | 主要入口 | 大 server,发现后直调 |
direct | 一直可见 | 可以 | — | 小而常用 |
hidden | 不可见 | 不可达 | 不可达 | 禁用 |
间接触达(codemode 脚本、tool_search)不依赖当前已声明的工具集,所以 /tree、resume、fork 后仍可用。
Codemode vs 传统 MCP
One 团队 2026 年 9 月 30 日那篇博客,同一个任务跑两遍,唯一差别是 exposure。数据如下:
| Codemode | direct | |
|---|---|---|
| 模型轮数 | 4 | 10 |
| 总 tokens | 66,200 | 324,426 |
| 花费 | $0.056 | $0.43 |
| 耗时 | 34s | 142s |
| 首轮负担 | +4,300 tok(带 TS 声明) | 更轻 |
| 大结果 | 脚本过滤,只回传精华 | 全量进上下文,每轮重读 |
| 小任务 | 不划算 | 合适 |
| 出错方式 | 脚本静默漏计(如漏 3 条 dead/deleted) | 逐轮透明 |
结论:Codemode 胜在省 token、省钱、省时间,大约 5 倍、7.7 倍、4 倍。败在起始负担重、小模型易错、会静默出错。适合“大量重复调用加结果巨大需过滤”,不适合“一两次小调用”。
已知局限
Armin Ronacher 2026 年 10 月 6 日原文里的措辞是 "not amazingly well" 和 "not a perfect solution yet"。具体几条:
-
结构化内容缺失。Codemode 要规整 JSON,很多 server 还在吐纯文本,靠
structuredContent和outputSchema补。 -
结果不一致。server 按条数优化 token,5 条探针成、满批次崩。
-
大二进制不支持。图片等只能预签名 URL 绕道。
-
组合式搜索无机制。多 server 并行扇出搜索不缩放。
-
Codemode-in-Codemode 糟糕(Cloudflare 式)。双重 JSON 转义,小模型易懵。
-
小模型不行。原文是 "inability of this pattern to work with smaller models"。
本机实际配置
MCP server
/home/a1/.pi/agent/mcp.json 里有 5 个 server:
| server | 类型 | 说明 |
|---|---|---|
Playwright | stdio (npx) | 浏览器自动化,--user-data-dir /home/a1/.cache/hs-playwright-mcp,--executable-path /usr/bin/google-chrome,--no-sandbox |
context7 | stdio (npx) | 库文档查询 |
tavily-proxy | HTTP (url) | 网页搜索,带 api key |
exa-search | HTTP (url) | 网页搜索 |
parallel-search | HTTP (url) | 并行搜索 |
全部默认 exposure: codemode,也就是没显式设置。
用户设置
/home/a1/.pi/agent/settings.json:
-
defaultProvider和defaultModel都是sensenova-6.8-flash-lite -
theme: dark、tuiMode: fullscreen -
无
defaultTools、无codemode.*字段,所以 codemode 仅因 MCP server 存在而自动激活
踩坑记录
2026 年 11 月 4 日实测踩了两个坑。
第一个是 mcp__exa_search__web_search_exa 首次调用报错 objective: must have required properties objective,漏了必填参数。
第二个是 mcp__tavily_proxy__tavily_extract 首次调用报错 urls: must have required properties urls,误写成了 url。
结论:小模型加默认 codemode 暴露时,工具 schema 字段名和必填项靠脚本内猜,试错成本高。正好符合已知局限第 6 条。
实操建议
针对本机这 5 个搜索和浏览器 server,加上小模型,建议这样配:
| server | 建议 exposure | 理由 |
|---|---|---|
tavily-proxy | deferred | 常用,用到再声明,模型直接调 |
exa-search | deferred | 同上 |
parallel-search | deferred | 同上 |
Playwright | direct 或留 codemode | 用得多就 direct,偶发就 codemode |
context7 | codemode | 文档查询,用得不频繁 |
如果确定不需要 codemode 脚本路由,可以给上面全部设 direct 或 deferred,并在 mcp.json 顶层加 "autoEnableCodemode": false,避免自动激活。
本文基于 pi 官方文档、One 团队实测博客、Armin Ronacher 文章和本机实际配置整理。不同 pi 版本、不同 MCP server 实现下细节可能不同,请以实际环境为准。
更多推荐
所有评论(0)