第 3 章 · 'use agent':函数式 Agent
本章目标:吃透
'use agent'指令的语义,建立"Agent 即函数"的心智模型,理解每次渲染重建指令的设计意图。
3.1 指令语义:一个字符串标记整个模块
'use agent' 与 JavaScript 的 'use strict'、React Server Components 的 'use client' 一脉相承——它只是一个写在文件顶部的字符串字面量,本身没有任何运行时行为,是构建工具在扫描时赋予它意义:
// src/agents/triage-agent.ts
'use agent'; // ① 必须是文件第一条语句(注释除外)
import { useModel } from '@flue/runtime';
// ② 构建时:Flue 扫描所有带此指令的文件,
// 把每个导出的大写开头函数注册为一个可寻址 agent
export function TriageAgent() {
useModel('anthropic/claude-sonnet-4-6');
return 'Investigate the reported issue and recommend the next action.';
}
// ③ 一个文件可以导出多个 agent
export function TriageSupervisor() {
useModel('anthropic/claude-opus-4-6');
return 'Review the triage results and escalate critical issues.';
}注册(Registration)让 agent 可以被应用的其他部分按名字寻址:CLI 的 flue run、路由的 createAgentRouter(TriageAgent) 都靠它。
3.2 Agent 即函数:三个关键性质
性质一:返回值就是系统指令
函数返回的字符串会被渲染为 agent 的 system prompt,与用户消息、助手消息一起构成发给 LLM 的完整上下文:
'use agent';
import { useModel } from '@flue/runtime';
export function SupportAgent() {
useModel('anthropic/claude-haiku-4-5');
// 这段文字就是 agent 的"人格 + 行为准则"
return `
你是客服助手。规则:
1. 回答保持简洁专业;
2. 涉及退款时先查询订单状态;
3. 无法解决的问题主动转人工。`;
}性质二:每次模型调用前都会重新渲染
这是与普通函数最大的不同。每一轮对话即将调用模型时,Flue 都重新执行你的函数,从头重建指令。返回的字符串永远反映 agent 的当前状态:
'use agent';
import { useModel, usePersistentState } from '@flue/runtime';
export function AdaptiveAgent() {
const [resolved] = usePersistentState('resolved', false);
useModel('anthropic/claude-haiku-4-5');
// 指令随状态动态变化——resolved 翻转后指令立即不同
return resolved
? '问题已解决。请向用户确认满意度并结束会话。'
: '积极排查用户报告的问题,逐步给出排查步骤。';
}缓存警告
在指令里插入动态数据(如时间戳)会击穿 LLM 的 prompt 缓存, 让推理 token 变贵。生产 agent 应尽量保持指令的静态前缀稳定。
性质三:可以接收 ID 作为参数
agent 函数能拿到初始化参数(如会话 ID),把业务标识直接织入指令:
'use agent';
import { useModel } from '@flue/runtime';
// id 来自 --id 参数或 POST /:id 路由——语义由你定义:
// 可以是用户 ID、工单号、GitHub issue 编号……
export function IssueAgent({ id }: { id: string }) {
useModel('anthropic/claude-sonnet-4-6');
return `调查 GitHub issue #${id},复现问题并给出修复建议。`;
}3.3 与 React 组件的类比
这个设计不是巧合——Flue 明确表示 agent 函数就是仿照 React 渲染函数建模的:
| React | Flue |
|---|---|
| 组件函数每次 render 重新执行 | agent 函数每轮模型调用前重新执行 |
useXxx() Hook 声明状态与副作用 | useModel() 等 Hook 声明能力 |
| JSX 描述 UI | 返回字符串描述指令 |
| props 传入数据 | { id } 与 initial data 传入业务数据 |
'use client' 标记客户端组件 | 'use agent' 标记 agent 模块 |
理解了这一点,后续所有 Hook 的行为规则(调用顺序、条件挂载、每次渲染重跑)都会变得直觉化。
3.4 最小完整示例回顾
// 最小可运行 agent:三行核心代码
'use agent';
import { useModel } from '@flue/runtime';
export function Assistant() {
useModel('anthropic/claude-haiku-4-5'); // ① 声明模型(必需)
return 'You are a helpful assistant.'; // ② 返回指令
}
// ③ 文件顶部 'use agent' 完成注册三条铁律:
useModel()必须调用且每轮恰好一次——没有模型的 agent 无法启动;- 导出的大写函数才会被注册为顶层 agent,非导出函数只是普通函数;
- 返回字符串——返回其他类型没有意义,指令必须是文本。
本章小结
'use agent'是文件级指令,构建时扫描注册所有导出的大写函数;- Agent 函数三大性质:返回值即系统指令、每轮重渲染、可接收 ID 参数;
- 心智模型 = React 组件:Hook 声明能力,渲染产出"UI"(这里是指令);
- 保持指令静态前缀稳定,避免击穿 prompt 缓存。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 'use agent' 指令的本质是什么?
2. agent 函数什么时候会重新执行?
3. 下列哪个函数会被 Flue 注册为顶层 agent?
4. 为什么生产 agent 应避免在指令中插入时间戳这类动态值?
🛠️ 动手实践
- 在一个文件里导出两个 agent(不同模型、不同指令),分别用
flue run与它们对话。 - 写一个带
usePersistentState状态切换指令的 agent,观察状态翻转前后行为差异。 - 故意在指令里加
Date.now()并连续对话,用 Provider 后台的 token 统计对比缓存命中情况。
编程模型已建立。下一章学习如何选择模型与设计高质量指令。