2. 模型与 Provider
CLI 不必重启就能切模型、切 provider、调温度。三个命令搞定全部:/model /provider /temperature。
"Provider" 是什么
一个 provider 就是一组 (API_KEY, BASE_URL, MODEL) 三元组 — 一份凭证指向一个端点,并附带一个默认模型。端点默认说 OpenAI Chat Completions,除非这个块用第四个可选变量 XXXX_API_FORMAT(0.5.0)另行说明 — 见下文「线路协议」。Provider 名字是任意的,来自你的 .env 文件。
约定是 XXXX_API_KEY / XXXX_BASE_URL / XXXX_MODEL,其中 XXXX 是 provider 名(大写):
# .env
# 默认 provider(旧的写法只用 OPENAI_*)
OPENAI_API_KEY=sk-...
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-5.4
# 想加多少加多少
GEMINI_API_KEY=...
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
GEMINI_MODEL=gemini-2.5-pro
DEEPSEEK_API_KEY=sk-...
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_MODEL=deepseek-chat
LOCAL_API_KEY=any-string
LOCAL_BASE_URL=http://localhost:8000/v1
LOCAL_MODEL=qwen2.5-72b
# Anthropic 自家 API,原生协议(0.5.0)— base URL 是 API 根,不带 /v1
CLAUDE_API_KEY=sk-ant-...
CLAUDE_BASE_URL=https://api.anthropic.com
CLAUDE_MODEL=claude-sonnet-5
CLAUDE_API_FORMAT=anthropic-messages只有当某个 provider 的三个 env var 都齐全时它才出现在列表里。GEMINI_API_KEY 设了但 GEMINI_BASE_URL 没设,GEMINI 就不会显示。
线路协议
XXXX_API_FORMAT 指明对该块的端点说哪种协议:openai-completions(不设时的默认值)、anthropic-messages(Anthropic 的 Messages API)或 openai-responses(OpenAI 的 Responses API,0.5.3)。它从不推断 — 不看 URL、不看 provider 名、不看模型名 — 所以指向 OpenAI 兼容网关的 CLAUDE_* 在你明说之前仍走 Chat Completions,未知的值会报错而不是回退。原生线路带来的是真正生效、且会上报的提示缓存(LLM_PROMPT_CACHE=anthropic),以及能在工具循环中完整保留的签名 thinking 块。完整说明:配置参考 §2。
/provider — 列出或切换
> /provider列出所有检测到的 provider,当前使用的标 ✓,并打印用法提示。
> /provider GEMINI一次性切换凭证 + base URL + 默认模型 — 目标块的 XXXX_API_FORMAT 不同时,线路协议也一起换(CLI 会打印 Wire protocol: A → B)。对话历史保留 — 只换 LLM 客户端。下一轮就发到新 provider。不会带过去的是被离开的那个模型签发的东西:历史里的 thinking 产物(含签名 thinking 块)会被丢弃,与任何一次换模型相同。LLM_EXTRA_BODY 保留;线路变化时 CLI 会列出它的键,因为它们是为另一种协议写的。
常见错误:
| 提示 | 怎么修 |
|---|---|
No providers found in .env | 至少配一组 XXXX_API_KEY + XXXX_BASE_URL + XXXX_MODEL |
No API key found for provider 'GEMINI' | GEMINI_API_KEY 没设 |
No base URL configured for provider 'GEMINI' | GEMINI_BASE_URL 没设 |
No model configured for provider 'GEMINI' | GEMINI_MODEL 没设 |
Unknown api_format '…' | GEMINI_API_FORMAT 不是 openai-completions、anthropic-messages 或 openai-responses;会话停在原来的 provider 上 |
/model — 在当前 provider 里列 / 切模型
> /model显示当前模型,然后查询 provider 的 /models 端点,分组列出:
Current Model: gpt-5.4
Available Models:
Claude:
• claude-haiku-4-5
• claude-sonnet-4-6
• claude-opus-4-7
OpenAI GPT:
• gpt-5.4 ✓
• gpt-5.4-mini
Other:
• o3
• text-embedding-3-small清单是从 provider 实际拉的,不是硬编码的,所以反映的是你这个端点真实暴露出来的模型。切换:
> /model claude-sonnet-4-6/model 的作用域是当前 provider。要的模型不在这里,先 /provider 切过去再 /model。
跨厂商的模型名
一些 provider 用 OpenAI 兼容 API 转发 Claude 模型。如果你在非 Anthropic provider 上看到 claude-* 也是正常的 — 那是 provider 在做代理。
/temperature — 采样温度
> /temperature # 看当前值
Temperature: provider default (not sent)
> /temperature 0.2 # 设置
Temperature changed from provider default to 0.2范围:0.0 到 2.0。低 = 更确定,高 = 更发散。默认不设值、也不发送,由 provider 自己的默认值决定(聊天典型为 1.0;0.5.7 起 —— 此前会发送 0.2)。/temperature off 保留已设的值但不再发送;on 重新发送,没有值可发时会直接说明。
修改是会话级。重启 CLI 会回到 provider 默认值。要持久化默认温度,写到 .env:
LLM_TEMPERATURE=0.3在 anthropic-messages 线路上,/temperature 和 LLM_TEMPERATURE 不起作用:SDK 的 messages.create 没有 temperature 参数。确实接受它的网关请通过 LLM_EXTRA_BODY 传。
需要 CLI 没有对应命令的请求参数 —— reasoning_effort、top_p、seed、response_format,或某个 provider 专有字段?把 LLM_EXTRA_BODY 设成 JSON 对象;它会被原样转发给 LLM .create()(并被子 agent 继承):
LLM_EXTRA_BODY='{"reasoning_effort":"high"}'解析 / 脱敏细节见 附录 B。
/thinking — 思考深度
> /thinking # 查看当前值
> /thinking high # 设置
> /thinking off # 回到 provider 默认它写什么取决于线路,因为各协议对这个字段的叫法不同:
| 线路 | 写入 extra_body 的字段 | 档位 |
|---|---|---|
openai-completions | reasoning_effort | minimal · low · medium · high(非标准的词会原样透传,由 provider 校验) |
anthropic-messages | output_config.effort | low · medium · high · xhigh · max,或 provider 的 Models API 对当前模型标为支持的那些;其余一律拒绝、不写入 |
openai-responses | reasoning.effort | minimal · low · medium · high(非标准的词原样透传,与 Chat Completions 相同)。你自己设的其它 reasoning 键(如 summary)不动;从别的线路带过来的 reasoning_effort 会被移除,因为这个 API 拒绝它 |
在当前的 Anthropic 模型上,只写 effort 就会打开 adaptive thinking。哪条线路上都没有自动恢复:模型不接受这个字段时,每个请求都会失败,直到 /thinking off。设置是会话级的,子 agent 会继承;要持久化,把同一个字段写进 LLM_EXTRA_BODY。
用 /provider 切换到另一条线路时,/thinking 设置的档位会移到新线路读取的字段里,因为每条线路都拒绝其他线路的字段。新线路不接受的档位(例如 anthropic-messages 上的 minimal)会被丢弃,日志里会写明。只有 /thinking(或 agentao run 的 thinking)写入的档位会移动,而且只在它的字段仍是原值时才移动。如果你已经自己设置了新线路的字段,以你的值为准,移过来的档位会被丢弃。你在 LLM_EXTRA_BODY 里设置的字段归你管:它保持原样,切换到拒绝它的线路后,每个请求都会失败,直到你修改它。
什么时候该切什么
| 情况 | 怎么做 |
|---|---|
| 同任务,想用更聪明的模型 | /model <更大>(不换 provider) |
| 同任务,想省钱 | /model <更小> |
| 换厂商 | /provider <NAME>(历史保留,凭证切换) |
| 输出太随机 / 开始幻觉 | /temperature 0.2 |
| 输出太死板 / 重复 | /temperature 1.2 |
| 问题难,想让它多想一会儿 | /thinking high |
| 会话进行中 cost 在飙升 | 下一轮前 /model 切到小一号 |
容易踩的坑
- 工具调用进行中切换:agent 还在迭代工具时切了模型或 provider,下一次迭代就用新客户端。多数场景没事,但不同厂商之间 tool-call 格式有差异,可能让中间一轮乱掉 — 看到这种情况切完后直接
/clear。 - 历史会跨切换保留:通常你就要这个,但意味着新模型会看到上一个模型留下的 tool-use 痕迹。有些模型对消息格式要求严,会报错。
- "列得出来" 不等于 "调得通":
/model列的是 provider/models上报的全部内容,里面有 embedding 模型、你没权限的 fine-tune 之类,下一轮会失败。/model <name>也接受列表之外的名字,并且不做检查:拼错了要等下一轮失败才会发现。看到错误换一个就行。
接下来读什么
| 想做的事 | 读 |
|---|---|
| 切完模型后保证它别在新模型上跑危险工具 | 3. 权限与模式 |
| 切到小模型后 context 爆了怎么救 | 7. 上下文与状态 |
| 跑本地模型 | 按上面写好 LOCAL_* env,再 /provider LOCAL |
这一章在体系里的位置
CLI 用的是 cli.agent.set_provider(api_key, base_url, model) 和 cli.agent.set_model(name) — 这两个都是 Agentao 实例的公开方法,嵌入式宿主也可以调,效果完全一致(同一份内存里的会话)。嵌入式 API 的等价用法和线程注意事项见 Part 2.5 · 运行时切换 LLM。
真相源头
命令语法:/help 与 agentao/cli/help_text.py。Provider 发现逻辑:agentao/cli/commands/provider.py:_list_providers_from_env。