Skip to main content

Providers、Models 与安全模型

Pi 在「统一多 provider」这件事上做得非常彻底——30+ 个内置 provider、4 种 key 解析语法、扩展可动态注册新 provider。但最值得记住的不是支持的 provider 多,而是它对「安全」这件事的态度:明确告知无沙箱,把隔离责任推给 OS / 容器 / 虚拟机。本章拆这两个看似无关、实则同源的设计。

统一 Provider 层(pi-ai

@earendil-works/pi-ai 是最底层的 LLM 适配包。它把市面上主流的 LLM API 收敛成一个统一接口——上层(agent、coding-agent)不需要关心你用的是 Anthropic、OpenAI 还是 Google。

两类 Provider

类型认证方式例子
订阅型OAuth via /loginChatGPT Plus/Pro (Codex)、Claude Pro/Max、GitHub Copilot
API Key 型环境变量或 auth.jsonOpenAI、Anthropic、Gemini、Mistral、DeepSeek、Groq、Cerebras

每个 provider 下 Pi 知道其全部可用模型——列表随每次 release 更新,你不需要手动维护 model id

内置 Provider 清单(节选)

ProviderEnvironment Variableauth.json key
AnthropicANTHROPIC_API_KEYanthropic
OpenAIOPENAI_API_KEYopenai
Google GeminiGEMINI_API_KEYgoogle
DeepSeekDEEPSEEK_API_KEYdeepseek
MistralMISTRAL_API_KEYmistral
GroqGROQ_API_KEYgroq
CerebrasCEREBRAS_API_KEYcerebras
xAIXAI_API_KEYxai
OpenRouterOPENROUTER_API_KEYopenrouter
Vercel AI GatewayAI_GATEWAY_API_KEYvercel-ai-gateway
Hugging FaceHF_TOKENhuggingface
FireworksFIREWORKS_API_KEYfireworks
Together AITOGETHER_API_KEYtogether
Kimi For CodingKIMI_API_KEYkimi-coding
OpenCode ZenOPENCODE_API_KEYopencode
NVIDIA NIMNVIDIA_API_KEYnvidia
Cloudflare Workers AICLOUDFLARE_API_KEY + CLOUDFLARE_ACCOUNT_IDcloudflare-workers-ai
Cloudflare AI GatewayCLOUDFLARE_API_KEY + (CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_GATEWAY_ID)cloudflare-ai-gateway
Azure OpenAIAZURE_OPENAI_API_KEY (+ AZURE_OPENAI_BASE_URL / AZURE_OPENAI_RESOURCE_NAME)azure-openai-responses
BedrockAWS profile / IAM keys / bearer token(env-only)
Vertex AIADC 或 service account(env-only)
ZAI Coding PlanZAI_API_KEY / ZAI_CODING_CN_API_KEYzai / zai-coding-cn

完整列表见 packages/ai/src/env-api-keys.tsenvMap 常量。

AuthStorage:key 解析的统一入口

AuthStorage 是 Pi 解析凭据的统一接口。它的优先级:

  1. Runtime overridessetRuntimeApiKey,不持久化)
  2. auth.json 中的存储凭据
  3. 环境变量ANTHROPIC_API_KEY 等)
  4. Fallback resolver

auth.json 形态

{
"anthropic": { "type": "api_key", "key": "sk-ant-..." },
"openai": { "type": "api_key", "key": "sk-..." }
}

文件以 0600 权限创建(仅用户可读写)。Auth file 凭据优先级高于环境变量

对于 Cloudflare AI Gateway 这种需要多个参数(API key + account ID + gateway ID)的 provider,auth.json 条目还能携带 env 对象:

{
"cloudflare-ai-gateway": {
"type": "api_key",
"key": "$CLOUDFLARE_API_KEY",
"env": {
"CLOUDFLARE_API_KEY": "...",
"CLOUDFLARE_ACCOUNT_ID": "account-id",
"CLOUDFLARE_GATEWAY_ID": "gateway-id"
}
}
}

env 适用于「让 pi 使用与项目 shell 环境不同的 provider 设置」的所有场景:credential key、provider/model headers、Cloudflare account ID、Azure OpenAI 配置、Vertex project/location、Bedrock 配置、PI_CACHE_RETENTIONHTTP_PROXY/HTTPS_PROXY

Key 字段的 4 种解析语法

key 字段支持以下形式——这是 Pi 在凭据管理上最精巧的设计:

(a) Shell 命令执行!command 开头,把整个 value 当命令执行,使用 stdout(缓存于进程生命周期):

{ "type": "api_key", "key": "!security find-generic-password -ws 'anthropic'" }
{ "type": "api_key", "key": "!op read 'op://vault/item/credential'" }

可以直接对接 macOS Keychain、1Password CLI、HashiCorp Vault、Bitwarden 等任意外部凭据存储。

(b) 环境变量插值$ENV_VAR${ENV_VAR},插值可嵌在更大的字面量中:

{ "type": "api_key", "key": "$MY_ANTHROPIC_KEY" }
{ "type": "api_key", "key": "${KEY_PREFIX}_${KEY_SUFFIX}" }

$FOO_BAR 表示变量 FOO_BAR;当 BAR 是字面文本时用 ${FOO}_BAR。缺失变量将无法解析。

(c) 转义符

  • "$$" → 字面 $
  • "$!" → 字面 !(不触发命令执行)
{ "type": "api_key", "key": "$$literal-dollar-prefix" }
{ "type": "api_key", "key": "$!literal-bang-prefix" }

(d) 字面量:直接使用。纯大写字符串如 MY_API_KEY 视为字面量——要用环境变量请写 $MY_API_KEY

OAuth 凭据同样存储于该文件,/login 后自动管理。

Cloud Providers

Pi 把几个云厂商单独抽出来,因为它们的配置比 API Key 复杂:

Azure OpenAI

export AZURE_OPENAI_API_KEY=...
export AZURE_OPENAI_BASE_URL=https://your-resource.ai.azure.com
# 也支持 cognitiveservices.azure.com / openai.azure.com
# 根 endpoint 自动归一化为 /openai/v1
# 或用 resource name 代替 base URL
export AZURE_OPENAI_RESOURCE_NAME=your-resource
# 可选
export AZURE_OPENAI_API_VERSION=2024-02-01
export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-4=my-gpt4,gpt-4o=my-gpt4o

Amazon Bedrock

支持三种认证方式 + ECS task roles + IRSA:

# 方式 1: AWS Profile
export AWS_PROFILE=your-profile

# 方式 2: IAM Keys
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...

# 方式 3: Bearer Token
export AWS_BEARER_TOKEN_BEDROCK=...

Prompt caching:对 ID 中含可识别模型名的 Claude 模型自动启用(基础模型与系统定义 inference profiles)。对于 application inference profiles(ARN 不含模型名),需设置 AWS_BEDROCK_FORCE_CACHE=1

代理场景:

export AWS_ENDPOINT_URL_BEDROCK_RUNTIME=https://my.corp.proxy/bedrock
export AWS_BEDROCK_SKIP_AUTH=1 # 代理无需认证时
export AWS_BEDROCK_FORCE_HTTP1=1 # 代理仅支持 HTTP/1.1 时

Cloudflare AI Gateway

CLOUDFLARE_API_KEY 可通过 /login 设置。Account ID 与 gateway slug 既可设为环境变量,也可放入 auth.json 中 API key 凭据的 env 对象。

4 种认证模式:

模式请求认证上游认证
Workers AI仅 Cloudflare tokenCloudflare native
Unified billing仅 Cloudflare tokenCloudflare 处理上游认证并扣 credits
Stored BYOK仅 Cloudflare tokenCloudflare 注入 dashboard 存储的 provider key
Inline BYOKCloudflare token + 上游 Authorization header请求方提供上游 key

正常 pi 使用推荐 unified billing 或 stored BYOK。Inline BYOK 需通过 models.json provider/model override 配置额外的上游 Authorization header。

Cloudflare Workers AI

export CLOUDFLARE_API_KEY=...
export CLOUDFLARE_ACCOUNT_ID=...
pi --provider cloudflare-workers-ai --model "@cf/moonshotai/kimi-k2.6"

Pi 自动设置 x-session-affinity header 以获得 Cloudflare 的 prefix caching 折扣。

Google Vertex AI

gcloud auth application-default login
export GOOGLE_CLOUD_PROJECT=your-project
export GOOGLE_CLOUD_LOCATION=us-central1
# 或
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa-key.json

自定义 Provider:两种姿势

通过 models.json

添加 Ollama、LM Studio、vLLM 或任何支持以下 API 的 provider——OpenAI Completions、OpenAI Responses、Anthropic Messages、Google Generative AI。详见 models.md

通过扩展

对需要自定义 API 实现或 OAuth 流程的 provider,创建扩展。详见 custom-provider.md 和示例 examples/extensions/custom-provider-gitlab-duo/(GitLab Duo 自定义 provider)。

扩展方式更强大——可以在 pi.registerProvider() 运行时注册,无需重启。

Model 选择与思考等级

CLI:

# 切换 provider 和 model
pi --provider openai --model gpt-4o "Help me refactor"

# 带 provider 前缀的 model id
pi --model openai/gpt-4o

# 思考等级简写
pi --model sonnet:high "Solve this complex problem"

# 限制 Ctrl+P 循环的模型集合
pi --models "claude-*,gpt-4o"

SDK:

import { getModel } from "@earendil-works/pi-ai";
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";

const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const opus = getModel("anthropic", "claude-opus-4-5");
const available = await modelRegistry.getAvailable();

const { session } = await createAgentSession({
model: opus,
thinkingLevel: "medium", // off | minimal | low | medium | high | xhigh
scopedModels: [{ model: opus, thinkingLevel: "high" }],
authStorage, modelRegistry,
});

模型解析顺序:1) 从 session 恢复;2) settings 默认;3) 第一个可用模型。

思考等级从 offxhigh 6 档,是 session 内可调的状态——切思考等级会写一条 thinking_level_change entry 进 session 树,分支回放能完整看到。

Pi 的安全模型:明确告知无沙箱

这一节是 Pi 跟其他 coding agent 最显著的差异之一。

核心事实

Pi 不内置沙箱。内置工具可任意读写文件、执行 shell,扩展以同等权限运行。

文档原话:「This is intentional.

Pi 面向本地源码树、调用项目工具链、集成用户现有开发环境。文档明确指出进程内「部分沙箱」会误导用户产生虚假的安全边界感——真正的隔离必须由 OS 或容器/虚拟化层提供。

Project Trust

项目信任是「输入加载守卫」——不是沙箱,也不限制模型在工具内可执行的操作。

触发条件:检测到以下任一即视为「需要信任的资源」:

  • .pi/settings.json
  • .pi/extensionsskillspromptsthemes
  • .pi/SYSTEM.md.pi/APPEND_SYSTEM.md
  • 当前或祖先目录的 .agents/skills

判定流程

  1. 首次进入 → 遵循全局 defaultProjectTrust,默认 "ask"
  2. 已有决策 → 沿路径向上查找 ~/.pi/agent/trust.json 中最近祖先的记录
  3. 决策可由扩展通过 project_trust 事件拦截,第一个返回 yes/no 的扩展即拥有决定权

非交互模式-p--mode json--mode rpc不显示提示,可由 --approve / -a--no-approve / -na 单次覆盖。

Context 文件(AGENTS.md / CLAUDE.md)始终加载,不受 trust 控制——除非显式禁用 context loading。

Trust 的边界

信任 ≠ 安全。文档明确列出无法保护的场景:

  • 不可信代码
  • 不可信提示
  • 不可信模型输出
  • 仓库文件、注释、文档、构建产物中的 prompt injection

项目信任仅防止仓库静默修改 Pi 配置/扩展,无法保护上面那些。

容器化建议

Pi 文档推荐三种容器化方案:

  1. Gondolin — micro-VM(Anthropic 提供的 microVM 方案)
  2. Plain Docker
  3. OpenShell

最佳实践:

  • 整个 pi 进程放入容器,或宿主运行但工具执行路由到 Gondolin micro-VM
  • 仅挂载工作区所需路径;避免挂载 ~/.pi/agent
  • 使用最小/短期凭证;按需限制网络
  • 必要时用只读挂载或沙箱内外文件拷贝代替直接读写

漏洞报告范围

通过仓库 Security Policy 报告。安全敏感报告不得公开 issue

报告范围明确排除

  • 「预期内的本地 agent 行为」
  • 缺乏内置沙箱这一事实
  • 不可信内容的 prompt injection
  • 用户安装的扩展 / skills 行为

仅当报告展示「真实的权限边界绕过」或 Pi 授予了「本地用户本不具有的访问能力」时,才在安全边界内处理

为什么 Pi 选择「无沙箱」

把这一节放在最后是有意的——理解 Pi 的安全模型,才能理解前面所有设计选择的根源。

第一,诚实优于虚假安全感。 进程级「弹窗确认」给用户的「安全」感觉是假的——一旦用户在弹窗里点了「允许」,agent 就能做任何事。Pi 选择不制造这种错觉,强迫用户明确意识到「我跑的是无保护的 agent」。

第二,隔离责任在 OS / 容器 / 虚拟机层更合适。 Docker、Gondolin、Firecracker 这类基础设施已经做得很成熟;让 Pi 自己在进程内做一层弱沙箱,反而跟成熟方案抢位置、还做不好。

第三,配合 minimal core 哲学。 Pi 核心只有几件事,加一层沙箱等于把核心代码量翻倍——但沙箱本身又做不到真正安全。这是个负 ROI 的投入。

第四,安全模型跟「extensions 以完整系统权限运行」一致。 如果 Pi 试图限制 agent 行为,又允许 extensions 完整权限——逻辑上不一致。让 extensions 也跑在宿主权限下,整个 agent(包括 extensions)作为一个单元被容器化,比「限制一部分、放任另一部分」更清晰。

这不是说 Pi 的安全模型「更好」——它只是把 trade-off 摆在了用户面前。用户必须自己选择是否用 Docker / Gondolin 包裹 Pi 进程。愿意做这个动作的用户得到的是「真正隔离的 agent」;不愿意的用户得到的是「明确知道自己没隔离的 agent」。


下一章讲 SDK 与四种运行模式。Pi 把 CLI、SDK、RPC、JSON 四种使用姿势都做了等价支持——同一个 agent session 既可以是 TUI 交互,也可以是 stdin/stdout 上的 JSONL 流、Node.js 嵌入调用。这是 Pi 「自我扩展」哲学的最后一块拼图。