Skip to content

第 4 章 · 模型配置与指令设计

本章目标:掌握 useModel() 的声明式用法与模型标识格式,学会用 thinkingLevel 与 compaction 调优,理解动态切换模型的"升级"模式。

4.1 useModel():声明而非客户端

useModel() 是 Flue 唯一必需的 Hook,但它不返回任何东西、也不创建 SDK 客户端——你只负责"点名",连接、认证、流式、重试全部由 Flue 运行时接管:

typescript
'use agent';
import { useModel } from '@flue/runtime';

export function TriageAgent() {
  // 你不需要 new Anthropic(),不需要传 API Key,不需要处理重试
  useModel('anthropic/claude-sonnet-4-6');
  return 'Investigate the reported issue and recommend the next action.';
}

两条硬规则:

  1. 必须调用:没有 useModel() 的 agent 渲染无法启动;
  2. 每轮渲染恰好调用一次:参数可以变,但调用本身不能消失或重复。

例外:子代理

子代理(subagent)的渲染中不能调用 useModel()——它的模型在 useSubagent() 定义上指定;未指定时继承父 agent 的模型。

4.2 模型标识格式

模型标识是 'provider-id/model-id' 格式的普通字符串。第一个 / 之前是 Provider ID,之后全部是 Provider 自己的模型 ID(可以再含斜杠):

typescript
// 标准 provider/model 形式
useModel('anthropic/claude-sonnet-4-6');
useModel('openai/gpt-5.5');

// 模型 ID 自身含斜杠的情况——第一个 / 才是分隔符
useModel('openrouter/moonshotai/kimi-k2.6');      // openrouter 的 moonshotai/kimi-k2.6
useModel('cloudflare/@cf/moonshotai/kimi-k2.6');  // Cloudflare Workers AI,无需 API Key

内置 Provider 来自 Pi 的完整目录:anthropic、openai、google、amazon-bedrock、google-vertex、groq、mistral、xai、deepseek、cerebras、together、fireworks、openrouter 等。目录元数据包含上下文窗口大小、输出上限、费率、推理支持、输入模态——它们决定了压缩触发时机与思考级别是否生效。

未知模型会快速失败:请求发出前就报错指出未解析的 provider 与 model ID。

4.3 thinkingLevel 与 compaction 调优

第二个可选参数提供两个字段:

typescript
'use agent';
import { useModel } from '@flue/runtime';

export function DeepResearcher() {
  useModel('anthropic/claude-opus-4-6', {
    // 推理深度:off | minimal | low | medium(默认) | high | xhigh | max
    thinkingLevel: 'high',
    // 上下文自动压缩配置
    compaction: {
      reserveTokens: 30000,        // 触发阈值余量(默认模型感知,≤20000)
      keepRecentTokens: 16000,     // 最近历史原样保留量(默认 8000)
      model: 'anthropic/claude-haiku-4-5', // 用更便宜的模型做摘要
    },
  });
  return '对给定主题进行深入研究,输出结构化报告。';
}

要点:

  • thinkingLevel 只是默认值——单个操作可覆盖(子代理定义、harness.prompt() 调用);
  • 思考内容只有对标记为 reasoning-capable 的模型才会真正发到线上;
  • compaction: false 可关闭阈值自动压缩(溢出恢复仍会兜底)。

对话接近模型上下文上限时,Flue 自动把旧历史折叠成摘要、最近消息保持原文,会话无缝继续。

4.4 动态切模型:成本升级模式

因为每轮都重新渲染,模型标识可以按状态计算。经典模式是"升级":日常用便宜模型,复杂任务升级到强模型:

typescript
'use agent';
import { useModel, usePersistentState } from '@flue/runtime';

export function Reviewer() {
  // escalated 是跨轮持久的标志位
  const [escalated, setEscalated] = usePersistentState('escalated', false);

  // 状态决定本轮用哪个模型——每次渲染重新评估
  useModel(escalated
    ? 'anthropic/claude-opus-4-6'
    : 'anthropic/claude-haiku-4-5');

  return `
审查用户提交的代码变更。
若发现需要深度推理的复杂缺陷,
先使用 escalate 工具将 escalated 置为 true 再继续分析。`;
}

配合工具(第 6 章)让模型自己决定何时升级,就能实现"便宜模型打杂 + 强模型攻坚"的成本优化组合。

4.5 编写高质量指令

返回的字符串质量直接决定 agent 上限。四条原则:

typescript
'use agent';
import { useModel } from '@flue/runtime';

export function CodeReviewer() {
  useModel('anthropic/claude-sonnet-4-6');
  return `
你是资深代码审查员。审查 PR 时遵循以下流程:
1. 先读变更描述,理解意图;
2. 用 grep 定位受影响的核心逻辑,通读上下文;
3. 按严重程度分类问题:blocker / suggestion / nit;
4. 输出格式:每个问题一段,标注文件与行号,附修复建议。
不要修改任何文件——你只有建议权。`; // ← 明确边界同样重要
}
  1. 角色具体化:"资深代码审查员"优于"AI 助手";
  2. 流程步骤化:编号步骤让模型有章可循;
  3. 输出格式化:规定结构,下游才好消费;
  4. 划清边界:明确告诉它不能做什么。

本章小结

  • useModel() 是唯一必需 Hook:声明式点名,运行时托管连接;
  • 标识格式 provider/model-id,首个 / 分隔 Provider 与模型 ID;
  • thinkingLevel 七档控制推理深度,compaction 三字段调自动压缩;
  • 每轮重渲染使模型可按状态计算——升级模式省成本;
  • 好指令四原则:角色具体、流程步骤化、输出格式化、边界清晰。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 关于 useModel(),下列说法错误的是?

2. 模型标识 'cloudflare/@cf/moonshotai/kimi-k2.6' 中,Provider ID 是哪部分?

3. thinkingLevel 的默认值是什么?

4. "升级模式"(escalation)指什么?

🛠️ 动手实践

  1. 把同一个 agent 分别指向 haiku 和 sonnet 各跑一次相同任务,对比回答质量与耗时。
  2. 写一个根据 usePersistentState 在两个模型间切换的 agent,验证切换即时生效。
  3. 为你的日常某项工作写一个 agent 指令,严格套用 4.5 的四条原则。

模型已就位。下一章解决另一个核心问题:会话如何记住之前聊过什么