第 11 章 · 思考与推理模式
本章目标:掌握 pi-ai 的思考/推理(Thinking/Reasoning)统一接口,学会按模型能力开启推理、流式接收思考内容并控制 token 预算。
11.1 什么是 Thinking/Reasoning
许多模型支持"思考"能力——在给出最终答案前先生成内部推理过程(如 Claude 的 extended thinking、DeepSeek-R1 的思维链)。pi-ai 把这些差异巨大的 Provider 参数统一成了一套接口。
关键点:
- 通过模型的
reasoning属性可以判断它是否支持推理; - 把推理选项传给不支持的模型时会被静默忽略,不会报错;
- 思考内容以独立 block 类型(
thinking)返回,与正文text分离。
import { createModels } from '@earendil-works/pi-ai';
import { anthropicProvider } from '@earendil-works/pi-ai/providers/anthropic';
const models = createModels();
models.setProvider(anthropicProvider());
// 查询模型是否支持推理
const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
if (model.reasoning) {
console.log('Model supports reasoning/thinking');
}11.2 统一接口:streamSimple / completeSimple
streamSimple 与 completeSimple 是 pi-ai 推荐的高级接口。它们接受统一的推理选项(如 reasoning: 'medium'),由库自动翻译成各 Provider 的原生参数:
// 统一推理等级:off | minimal | low | medium | high | xhigh | max
const response = await models.completeSimple(model, context, {
reasoning: 'medium', // 自动映射为对应 Provider 的参数
});
// 流式版本同样支持
const stream = models.streamSimple(model, context, { reasoning: 'low' });
for await (const event of stream) {
if (event.type === 'message_update') {
console.log(event.assistantMessageEvent.type);
}
}静默降级
如果你给一个不支持推理的模型传了 reasoning: 'high',pi-ai 不会抛错——选项被静默忽略。这让同一份代码可以在不同档位模型间自由切换。
11.3 Provider 特定选项:stream / complete
低级接口 stream / complete 暴露 Provider 特定的原始选项,适合精细控制:
// 低级接口:直接传递 Provider 原生支持的选项
models.stream(m, context, {
thinkingEnabled: true, // 开启思考(Anthropic 扩展思考)
thinkingBudgetTokens: 2048, // 思考 token 预算上限
});两种接口的取舍:
| 接口 | 推理控制方式 | 适用场景 |
|---|---|---|
streamSimple / completeSimple | 统一等级字符串 | 跨提供商通用代码 |
stream / complete | Provider 原生参数 | 需要精确预算等细节 |
11.4 流式接收思考内容
思考内容以独立事件流出。注意:不同内容块的事件不保证连续——Provider 可能在同一段上游数据里交替发出 text、thinking、toolcall 的 delta,必须用 contentIndex 关联事件所属的块:
for await (const event of models.stream(model, context)) {
switch (event.type) {
case 'thinking_start':
// 思考块开始,contentIndex 标记它在 content 数组中的位置
console.log('[Model is thinking...]');
break;
case 'thinking_delta':
// 收到思考片段增量
process.stdout.write(event.delta);
break;
case 'thinking_end':
// 思考块完成,event.content 是完整思考文本
console.log('\n[Thinking complete]');
break;
}
}事件参考:
| 事件 | 含义 | 关键字段 |
|---|---|---|
thinking_start | 思考块开始 | contentIndex |
thinking_delta | 思考片段增量 | delta, contentIndex |
thinking_end | 思考块完成 | content(完整思考), contentIndex |
11.5 在 Agent 中使用思考等级
Agent 类通过 initialState.thinkingLevel 或运行时修改状态来控制思考深度:
import { Agent } from '@earendil-works/pi-agent-core';
const agent = new Agent({
initialState: {
systemPrompt: '你是一个严谨的算法助手。',
model,
thinkingLevel: 'medium', // off/minimal/low/medium/high/xhigh/max
tools: [],
messages: [],
},
streamFn: models.streamSimple.bind(models),
});
// 运行时动态调整难度:简单问题关掉思考省 token
agent.state.thinkingLevel = 'off';
await agent.prompt('1+1 等于几?');
agent.state.thinkingLevel = 'high'; // 复杂问题开高档推理
await agent.prompt('证明:任意大于 2 的偶数都可写成两个素数之和的猜想为何未被证明?');对按 token 计费思考的 Provider,还可以自定义每个等级的预算:
// 为 token 型思考 Provider 自定义各级预算
agent.thinkingBudgets = {
minimal: 128,
low: 512,
medium: 1024,
high: 2048,
};另外,部分模型暴露独有的超高推理档位(如 xhigh、max)。用 getSupportedThinkingLevels(model) 可以查询某个具体模型实际支持哪些等级,避免盲目设置。
本章小结
- 用
model.reasoning判断模型是否支持推理;不支持的模型会静默忽略推理选项; streamSimple/completeSimple提供统一等级字符串,stream/complete暴露原生参数;- 思考事件有
start/delta/end三种,必须用contentIndex关联块,不能假设连续; - Agent 通过
thinkingLevel与thinkingBudgets控制思考深度与预算; getSupportedThinkingLevels()查询具体模型支持的等级集合。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 把 reasoning: "high" 传给一个不支持思考的模型会发生什么?
2. 处理流式思考事件时,为什么必须使用 contentIndex?
3. 想让同一份代码跨 Provider 使用统一的推理控制,应该用哪个接口?
4. 如何查询某个模型实际支持哪些思考等级?
🛠️ 动手实践
- 分别用
reasoning: 'off'、'low'、'high'向同一个数学问题发起请求,对比回答质量与耗时。 - 编写一个终端程序,流式渲染思考过程为灰色文字、正文为白色文字。
- 给你的 Agent 加一个"自适应思考"策略:根据用户输入长度自动选择 thinkingLevel。
下一章我们将让 Agent"看见"图片——第 12 章 · 图片输入与图像生成。