Skip to content

第 3 章 · 'use agent':函数式 Agent

本章目标:吃透 'use agent' 指令的语义,建立"Agent 即函数"的心智模型,理解每次渲染重建指令的设计意图。

3.1 指令语义:一个字符串标记整个模块

'use agent' 与 JavaScript 的 'use strict'、React Server Components 的 'use client' 一脉相承——它只是一个写在文件顶部的字符串字面量,本身没有任何运行时行为,是构建工具在扫描时赋予它意义

typescript
// 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 的完整上下文:

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

export function SupportAgent() {
  useModel('anthropic/claude-haiku-4-5');
  // 这段文字就是 agent 的"人格 + 行为准则"
  return `
你是客服助手。规则:
1. 回答保持简洁专业;
2. 涉及退款时先查询订单状态;
3. 无法解决的问题主动转人工。`;
}

性质二:每次模型调用前都会重新渲染

这是与普通函数最大的不同。每一轮对话即将调用模型时,Flue 都重新执行你的函数,从头重建指令。返回的字符串永远反映 agent 的当前状态:

typescript
'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),把业务标识直接织入指令:

typescript
'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 渲染函数建模的:

ReactFlue
组件函数每次 render 重新执行agent 函数每轮模型调用前重新执行
useXxx() Hook 声明状态与副作用useModel() 等 Hook 声明能力
JSX 描述 UI返回字符串描述指令
props 传入数据{ id } 与 initial data 传入业务数据
'use client' 标记客户端组件'use agent' 标记 agent 模块

理解了这一点,后续所有 Hook 的行为规则(调用顺序、条件挂载、每次渲染重跑)都会变得直觉化。

3.4 最小完整示例回顾

typescript
// 最小可运行 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' 完成注册

三条铁律:

  1. useModel() 必须调用且每轮恰好一次——没有模型的 agent 无法启动;
  2. 导出的大写函数才会被注册为顶层 agent,非导出函数只是普通函数;
  3. 返回字符串——返回其他类型没有意义,指令必须是文本。

本章小结

  • 'use agent' 是文件级指令,构建时扫描注册所有导出的大写函数;
  • Agent 函数三大性质:返回值即系统指令、每轮重渲染、可接收 ID 参数;
  • 心智模型 = React 组件:Hook 声明能力,渲染产出"UI"(这里是指令);
  • 保持指令静态前缀稳定,避免击穿 prompt 缓存。

🧪 随堂测验

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

1. 'use agent' 指令的本质是什么?

2. agent 函数什么时候会重新执行?

3. 下列哪个函数会被 Flue 注册为顶层 agent?

4. 为什么生产 agent 应避免在指令中插入时间戳这类动态值?

🛠️ 动手实践

  1. 在一个文件里导出两个 agent(不同模型、不同指令),分别用 flue run 与它们对话。
  2. 写一个带 usePersistentState 状态切换指令的 agent,观察状态翻转前后行为差异。
  3. 故意在指令里加 Date.now() 并连续对话,用 Provider 后台的 token 统计对比缓存命中情况。

编程模型已建立。下一章学习如何选择模型与设计高质量指令