跳转到内容

输入关键词开始搜索文档。

智能体

最后更新 查看 Markdown 源文件

Flue 是一个用于构建自主智能体的框架。它们是 Flue 存在的核心原因,因此理解它们是什么以及如何工作非常重要。本指南涵盖创建智能体、组合其能力和环境,以及安全地向用户暴露它。

什么是智能体?

智能体由三个关键部分组成,它们协同工作:LLM、运行时和专业上下文。 Flue 旨在帮助你自定义和组合所有三个元素,以构建强大的、真正自主的智能体,为各种产品和内部工作流提供支持。

Flue 附带两个核心原语来帮助你组合强大的智能体:智能体函数和智能体 Hooks。

智能体函数

智能体函数在 Flue 中代表一个智能体。在 Flue 中,智能体是一个 JavaScript 函数,返回智能体的 system 提示指令。这些指令会被渲染,然后与构成某个智能体对话或工作流的用户和助手消息一起传递给 LLM。

src/agents/triage-agent.ts
// 示例:一个用 Flue 编写的简单智能体。
function TriageAgent() {
  return "调查用户的问题并推荐下一步操作。";
}

智能体始终使用 ID 初始化。你可以通过 flue run 的 --id 标志(可选)或托管智能体的 POST /:id 路由(必需)提供一个。ID 的含义由你决定——用户 ID、支持工单、GitHub issue 编号,或只是随机字符串。每个智能体实例都通过 ID 持久化,因此你可以使用智能体 ID 随时间与特定智能体进行消息交互。

src/agents/triage-agent.ts
function TriageAgent({ id }) {
  return `调查 GitHub issue #${id} 并推荐下一步操作。`;
}

还有其他方式将(结构化)数据传递给你的智能体——请参阅智能体 Hooks 指南中的向智能体传递数据。

智能体函数在每个回合都会_重新渲染_。也就是说,每次模型即将被调用时,Flue 会再次运行你的函数并从头开始重建其指令。你返回的字符串始终反映智能体在该时刻的当前状态:

src/agents/assistant-agent.ts
function AssistantAgent() {
  // 警告:将动态数据插入智能体指令可能会破坏 LLM 用于提供更便宜推理 token 的缓存。
  // 在生产智能体中避免这样做通常是最佳实践,特别是为了节省成本。
  return `你是一个有用的助手。现在的时间是 ${Date.now()}。`;
}

如果有助于理解,你可以将智能体函数视为类似于 React 组件的渲染函数。这并非偶然,正如你将在下面看到的:Flue 智能体函数有意模仿 React 进行建模,以帮助解锁更具表现力、更强大的智能体功能。

智能体 Hooks

智能体函数本身用处不大。它返回指令,但一个工作的智能体需要的不仅仅是文字——一个模型、工具、工作空间、记忆。要解锁所有这些,你需要使用 Flue 的第二个核心原语:智能体 hooks。

Hook 是一个普通函数,你在智能体函数体内调用它来为你的智能体赋予一项新能力。所有 hook 都以 use 开头,命名理念是:每个 hook 让你的智能体_钩入_Flue 运行时的不同功能:

  • 模型(useModel)选择驱动智能体的 LLM。
  • 沙盒(useSandbox)提供其文件系统和命令执行环境。
  • 工具(useTool)让它调用应用程序代码并影响外部系统。
  • MCP 服务器(useMcpConnection)从开放的 MCP 生态系统挂载工具。
  • 技能(useSkill)提供它可以在需要时加载的专业知识。
  • 子智能体(useSubagent)让它将专注的工作委托给其他智能体。
  • 持久化状态(usePersistentState)在智能体生命周期内保留自定义数据。
  • 事件 Hooks(useAgentStart、useAgentFinish 等)在不同生命周期事件上触发逻辑。
src/agents/triage.ts
import { useModel, useSandbox, useSkill, useTool } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import { searchIssues } from '../tools/search-issues.ts';
import reviewChecklist from '../skills/review-checklist/SKILL.md';

function Triage() {
  useModel('anthropic/claude-sonnet-4-6');
  useSandbox(local());
  useTool(searchIssues);
  useSkill(reviewChecklist);
  return '调查报告的问题并推荐下一步操作。';
}

要了解更多关于智能体 hooks 的信息,请参阅智能体 Hooks。

“use agent” 指令

上面的示例定义了智能体函数,但你的应用程序还不知道它们。要注册一个智能体,请用 'use agent' 指令标记其模块并导出该函数:

src/agents/triage-agent.ts
'use agent';
import { useModel } from '@flue/runtime';

export function TriageAgent() {
  useModel('anthropic/claude-sonnet-4-6');
  return '调查报告的问题并推荐下一步操作。';
}

就像 JavaScript 中的 'use strict' 或 React 中的 'use client' 一样,该指令是文件顶部的一个普通字符串,在任何导入或其他语句之前。在构建时,Flue 会扫描你的项目以查找标记的文件,并将每个导出的、大写开头的函数注册为智能体。一个文件可以导出多个智能体。

注册使智能体可以被应用程序的其余部分寻址:dispatch(...) 可以向它发送消息,createAgentRouter(...) 可以通过 HTTP 提供服务。导出函数的名称也成为智能体的持久化标识,它在持久化数据库中键控其对话存储。要在不进行数据库迁移的情况下重命名函数,请使用 agentName 静态属性固定标识。设置显式的智能体名称被一些 Flue 开发者视为最佳实践。

src/agents/triage-agent.ts
'use agent';
import { useModel } from '@flue/runtime';

export function TriageAgent() {
  useModel('anthropic/claude-sonnet-4-6');
  return '调查报告的问题并推荐下一步操作。';
}

TriageAgent.agentName = 'triage-agent';

与你的智能体交互

有几种方式可以与智能体交互。所有方式都运行相同的智能体和持久化 API——它们仅在运行时启动方式和是否存在 HTTP 服务器方面有所不同。

CLI

与智能体交互的最简单方式是在本地使用 flue run CLI 命令:

flue run src/agents/triage-agent.ts --message "Triage issue 17307"

这直接运行一个智能体模块——没有服务器,没有应用程序构建。传递 --id 来命名对话,以便你可以在调用之间继续它;没有它,每次运行都会开始一个新的对话并打印其生成的 ID:

flue run src/agents/triage-agent.ts --id issue-17307 --message "Look at issue 17307"
flue run src/agents/triage-agent.ts --id issue-17307 --message "Any update?"

对话在运行之间持久化——在你项目配置的数据库中,或在没有数据库时的本地缓存文件中。请参阅 flue run 参考 了解智能体选择、结构化输出和完整的标志列表。

HTTP

在你的应用程序中挂载的智能体通过 HTTP 提供服务(挂载在路由中介绍)。每个对话都有自己的 URL,以对话 ID 结尾。向它 POST 一条消息:

提示支持智能体对话
POST /agents/support-assistant/ticket-8472 HTTP/1.1
Content-Type: application/json

{
  "kind": "user",
  "body": "你能总结一下我案例中的开放问题吗?"
}

提示是即发即忘的:服务器立即响应 202,智能体的回复从对话中读取——GET 同一个 URL 来跟踪其事件,或使用Flue 智能体 SDK,它围绕一个对话 URL 封装了整个表面(send()、wait()、observe()、history())。

任何能够访问对话 URL 的人都可以与该对话交谈。使用你应用程序的正常中间件来保护挂载:验证调用者,并检查他们是否被允许访问该对话 ID。请参阅路由了解完整模式。

dispatch()

当你的应用程序异步接收到智能体的事件时使用 dispatch(...),例如 webhook、队列消息、聊天事件或通知。例如,应用程序路由可以验证传入的支持系统 webhook 并将评论分派给该工单的智能体:

src/app.ts
import { dispatch } from '@flue/runtime';
import { Hono } from 'hono';
import { SupportAssistant } from './agents/support-assistant.ts';
import { verifySupportWebhook } from './shared/support-webhooks.ts';

const app = new Hono();

app.post('/webhooks/support-comments', async (c) => {
  const event = await verifySupportWebhook(c.req.raw);
  const receipt = await dispatch(SupportAssistant, {
    id: event.ticketId,
    message: {
      kind: 'signal',
      type: 'support.comment.created',
      body: event.text,
      attributes: { commentId: event.commentId },
    },
  });

  return c.json(receipt, 202);
});

export default app;

你的应用程序在分派事件之前选择智能体对话。dispatch(...) 接受它进行异步处理,而不是等待智能体响应。因为注册来自 'use agent' 扫描,仅通过 dispatch(...) 使用的智能体根本不需要挂载。请参阅通道了解经过验证的提供商入口和应用程序拥有的出站行为。

独立脚本

最后,你可以完全在 Flue 应用程序之外运行智能体——没有服务器,没有 app.ts——使用更高级的 start() API。它在你自己的 Node.js 进程中启动 Flue 运行时,这对于 cron 作业、一次性脚本和测试很有用:

scripts/nightly.ts
import { init } from '@flue/runtime';
import { sqlite, start } from '@flue/runtime/node';
import { Reporter } from '../src/agents/reporter.ts';

await using flue = await start({
  agents: [Reporter],
  db: sqlite('./nightly.db'),
});

const reporter = init(Reporter, { id: 'nightly-2026-07-16' });
const receipt = await reporter.dispatch('生成每日报告。');
const reply = await reporter.read(receipt);
console.log(reply.text);

提供商凭据来自进程环境,db 选项决定对话是否比脚本存活更久:省略它以使用内存状态,或传递像 sqlite() 这样的适配器,以便后续运行可以继续同一个对话。在已经运行的 Flue 应用程序内部没有 start()——直接调用 init() 或 dispatch()。工作流指南深入介绍了这个脚本表面,从 CI 流水线到持久化编排。

后续步骤

  • 智能体 Hooks — 组合你的智能体的能力:工具、技能、状态和事件 hooks。
  • 智能体 API — 查找会话操作及其结果。
  • 路由 — 在经过身份验证的应用程序内挂载智能体 HTTP 表面。
  • 调度 — 按调度分派智能体输入。
  • 通道 — 将经过验证的提供商事件传递到智能体对话中。
  • 可观测性 — 检查智能体活动。