当前位置: 首页 > news >正文

Flue:构建下一代代理的 TypeScript 框架,多场景应用与开发全解析

实验阶段 — Flue 正在积极开发中

API 可能会发生变化。若你正在寻找 v0.0.x 版本,请点击[此处](链接待补充)。

Flue:代理管理框架

如果你了解如何使用 Claude Code(或者 Codex、OpenCode、Pi 等),那么你已经掌握了使用 Flue 构建代理的基础知识。Flue 是一个用于构建下一代代理的 TypeScript 框架,围绕内置的代理管理机制设计。它有点像 Claude Code,但完全无需人工干预且可编程,不存在像需要人工操作员才能运行这样的预设条件,没有 TUI(文本用户界面),也没有 GUI(图形用户界面),只有 TypeScript。不过,使用 Flue 的感觉就像使用 Claude Code 一样。你构建的代理能够自主解决问题并完成任务,运行它们所需的代码极少,大部分“逻辑”都存于 Markdown 文件中,包括技能、上下文和 AGENTS.md。

Flue 并非普通的 AI SDK,而是一个与运行时无关的框架,类似于 Astro 或 Next.js,但专门用于代理。你只需编写一次代码,就能构建并将代理部署到任何地方,如 Node.js、Cloudflare、GitHub Actions、GitLab CI/CD 等。

包说明

- `@flue/runtime`:运行时,包含管理机制、会话、工具和沙盒。

- `@flue/cli`:命令行界面(CLI)和构建/开发工具(flue 二进制文件)。

示例

消息驱动的代理

消息驱动的代理可在 `/agents/:name/:id` 处接收直接的 HTTP 或 WebSocket 消息,应用程序拥有的集成可以调用 `dispatch(...)` 方法,将异步输入传递到代理会话中。有关这些方面的详细信息,请参阅[消息驱动的代理](链接待补充)。针对 Node 和 Cloudflare,提供了可运行的 WebSocket 示例。

若要进行外部跟踪、指标监控和错误报告,请参考[可观测性](链接待补充)、官方的 `@flue/opentelemetry` 适配器、基于 `observe(...)` 的公共 Braintrust 跟踪示例以及 Sentry 错误报告示例。

快速开始

最简单的代理无需容器和工具,只需一个提示和一个类型化的结果。除非你选择初始化一个完整的容器沙盒,否则 Flue 会为每个代理默认使用一个由 just - bash 驱动的虚拟沙盒。与为每个代理运行完整的容器相比,虚拟沙盒速度更快、成本更低且可扩展性更强,非常适合构建高流量/大规模的代理。

//.flue/workflows/hello - world.ts import { createAgent, type FlueContext, type WorkflowRouteHandler } from '@flue/runtime'; import * as v from 'valibot'; // 导出的 Hono 中间件可启用公共 HTTP 暴露。 export const route: WorkflowRouteHandler = async (_c, next) => next(); const translator = createAgent(() => ({ model: 'anthropic/claude - sonnet - 4 - 6' })); // 工作流处理程序,工作流的编排逻辑在此处。 export async function run({ init, payload }: FlueContext) { // `harness` 是你初始化的管理机制,包括沙盒、工具、技能等。 const harness = await init(translator); const session = await harness.session(); // prompt() 方法在会话中发送消息,触发操作。 const { data } = await session.prompt( `Translate this to ${payload.language}: "${payload.text}"`, { // 传递 `result` 以从代理获取类型化、经过模式验证的数据。 result: v.object({ translation: v.string(), confidence: v.picklist(['low', 'medium', 'high']), }), }, ); return data; }

支持代理

支持代理也可以在不使用容器的情况下在 Cloudflare 上运行,借助 Flue 的默认虚拟沙盒即可。你可以为其文件系统填充代理所需的上下文,然后它就能使用内置的 grep、glob 和 read 工具搜索这些内容。由于该代理部署在 Cloudflare 上,消息历史记录和会话状态会自动持久化,这样你(或你的客户)可以在数天、数周或数年后重新访问该支持会话,并从上次中断的地方继续。

//.flue/workflows/support.ts import { createAgent, type FlueContext, type WorkflowRouteHandler } from '@flue/runtime'; export const route: WorkflowRouteHandler = async (_c, next) => next(); const support = createAgent(() => ({ model: 'openrouter/moonshotai/kimi - k2.6' })); export async function run({ init, payload }: FlueContext) { const harness = await init(support); const session = await harness.session(); await session.fs.mkdir('/workspace/articles', { recursive: true }); await session.fs.writeFile( '/workspace/articles/reset - password.md', '# Reset your password\n\nUse the account settings page to request a password reset email.', ); return await session.prompt( `You are a support agent. Search the workspace for articles relevant to this request, then write a helpful response.\n\nCustomer: ${payload.message}`, ); }

此示例使用了 Flue 内置的、由 just - bash 驱动的虚拟沙盒,无需连接器或容器。

问题分类(CI)

当 GitHub 上有问题被打开时,分类代理会在 CI 中运行。`local()` 沙盒使代理能够直接访问主机文件系统和 shell,这对于 CI 运行器来说非常合适,因为 `gh`、`git` 和 `npm` 已经在 `$PATH` 中,而运行器本身就是隔离边界。

//.flue/workflows/triage.ts import { createAgent, type FlueContext } from '@flue/runtime'; import { local } from '@flue/runtime/node'; import * as v from 'valibot'; // 由于我们在 CI 中运行此代理,因此无需将其作为 HTTP 端点暴露。 // CLI 可以从命令行运行任何工作流,例如 `flue run triage ...` export async function run({ init, payload }: FlueContext) { // `local()` 使代理能够直接访问主机文件系统和 shell。 // 代理的 bash 工具可以直接运行 `gh`、`git`、`npm`。 // 技能和 AGENTS.md 会从 process.cwd() 中发现。 // 默认情况下,只有一小部分 shell 必需的环境变量(如 PATH、HOME、locale 等)会从 process.env 继承。 // 传递 `env: { GH_TOKEN: process.env.GH_TOKEN }` 可以暴露更多环境变量。 // `model` 为该代理的每个提示/技能调用设置默认模型。 // 可以在 prompt()/skill() 中使用 `{ model: '...' }` 覆盖每个调用的模型。 const agent = createAgent(() => ({ sandbox: local({ env: { GH_TOKEN: process.env.GH_TOKEN } }), model: 'anthropic/claude - opus - 4 - 7', })); const harness = await init(agent); const session = await harness.session(); // 工作区中发现的技能可以通过其前置元数据中的 `name:` 激活。 // 静态导入的打包技能也可以通过传递其引用激活。 const { data } = await session.skill('triage', { // 为任何提示或技能传递参数。 args: { issueNumber: payload.issueNumber }, // 结果模式有助于根据提示或技能调用返回的结构化 `data` 进行操作和编排。 result: v.object({ severity: v.picklist(['low', 'medium', 'high', 'critical']), reproducible: v.boolean(), summary: v.string(), fix_applied: v.boolean(), }), }); return data; }

编码代理(远程沙盒)

上述示例均在轻量级虚拟沙盒上运行,无需容器。但对于完整的编码代理,你需要一个真实的 Linux 环境,其中包含 `git`、Node.js、浏览器和克隆好的仓库。Daytona 的声明式镜像构建器允许你在代码中定义环境。镜像在首次构建后会被缓存,因此后续会话可以立即启动。

使用 `flue add daytona | `(例如 `claude`、`opencode`、`codex`、`cursor - agent`)安装 Daytona 连接器。它会在你的项目中写入一个小的 `connectors/daytona.ts` 适配器,你可以直接导入该适配器。

//.flue/workflows/code.ts import { Type, createAgent, defineTool, type FlueContext, type WorkflowRouteHandler } from '@flue/runtime'; import { Daytona } from '@daytona/sdk'; import { daytona } from '../connectors/daytona'; export const route: WorkflowRouteHandler = async (_c, next) => next(); export async function run({ init, payload, env }: FlueContext) { // 每个代理通过 Daytona 获取一个真实的容器。 // 该容器具有完整的 Linux 环境,包括持久化的文件系统和 shell。 // 为了简单起见,我们总是在这里创建一个新的沙盒。 // 你也可以先检查代理实例 ID 是否存在现有的沙盒,然后重用它,以便最好地从上次对话中断的地方继续。 const client = new Daytona({ apiKey: env.DAYTONA_API_KEY }); const sandbox = await client.create(); const setupAgent = createAgent(() => ({ sandbox: daytona(sandbox), model: 'openai/gpt - 5.5', })); const setupHarness = await init(setupAgent, { name: 'setup' }); const setup = await setupHarness.session(); // 为了简单起见,我们在这里将目标仓库克隆到沙盒中。 // 你也可以将这些内容烘焙到容器镜像快照中,以实现更快或近乎即时的启动。 await setup.shell(`git clone ${payload.repo} /workspace/project`); await setup.shell('npm install', { cwd: '/workspace/project' }); // 在克隆的仓库中启动第二个管理机制。 // 它共享相同的沙盒,但从 /workspace/project 中发现 AGENTS.md 和技能。 const projectAgent = createAgent(() => ({ sandbox: daytona(sandbox), cwd: '/workspace/project', model: 'openai/gpt - 5.5', })); const projectHarness = await init(projectAgent, { name: 'project' }); const session = await projectHarness.session(); // 编码代理不会向用户隐藏代理开发体验,因此无需对用户的提示进行包装。 // 直接将提示发送给代理,然后流式返回进度和最终结果。 return await session.prompt(payload.prompt); }

远程 MCP 工具

MCP 可作为运行时工具适配器使用。在受信任的代码中连接到远程 MCP 服务器,将其工具传递给 `init()`,并将机密信息存储在环境变量中,而不是文件系统上下文或提示中。

//.flue/workflows/assistant.ts import { connectMcpServer, createAgent, type FlueContext, type WorkflowRouteHandler } from '@flue/runtime'; export const route: WorkflowRouteHandler = async (_c, next) => next(); export async function run({ init, payload, env }: FlueContext) { const github = await connectMcpServer('github', { url: 'https://mcp.github.com/mcp', headers: { Authorization: `Bearer ${env.GITHUB_TOKEN}` }, }); try { const agent = createAgent(() => ({ model: 'anthropic/claude - sonnet - 4 - 6', tools: github.tools, })); const harness = await init(agent); const session = await harness.session(); return await session.prompt(payload.prompt); } finally { await github.close(); } }

`connectMcpServer()` 默认使用现代可流式传输的 HTTP。对于旧版 SSE 服务器,请传递 `transport: 'sse'`。在第一个版本中,Flue 不会自动检测传输方式、生成本地标准输入输出 MCP 服务器或处理 OAuth 回调。

代理、管理机制和会话

代理是 `agents/.ts` 中的源文件。对于附加的 HTTP 或 WebSocket 代理,URL 段标识代理实例,即一个客户、仓库、对话空间或其他调用者定义边界的持久运行时范围。

- `POST /agents//`

- `GET /agents//`(升级为 WebSocket)

在代理模块中,导入 `type AgentWebSocketHandler` 并导出 `const websocket: AgentWebSocketHandler = async (_c, next) => next();`,以打开与该稳定实例的长期 SDK 连接。在调用 `next()` 之前,它可以添加身份验证。一个代理套接字可以按顺序发出提示,并为每个提示选择一个会话;工作流套接字每次连接只进行一次调用。

在工作流中,`init(createdAgent)` 创建一个管理机制,它是一个配置好的句柄,用于管理模型默认值、工具、沙盒、文件系统和会话。当一个工作流需要多个隔离的管理机制范围时,可以传递 `init(createdAgent, { name })`。

在代理模块中,当消息到达时,运行时会初始化模块的默认 `createAgent(...)` 导出。默认情况下,`harness.session()` 会在该代理实例的默认管理机制内打开默认会话。使用相同的 URL 可以继续同一个代理实例,使用新的 URL 则可以重新开始。

运行仅属于工作流。直接的 HTTP 和 WebSocket 提示是与代理会话的附加交互。异步代理传递使用 `dispatch(...)`,其 `dispatchId` 用于标识传递,而不是运行。代理交互不会出现在 `/runs` 中,也不会通过 `flue logs` 进行检查。

# 开始一个对话(端口 3583 是 `flue dev` 的默认端口) curl http://localhost:3583/agents/hello/session - abc \ -H "Content - Type: application/json" \ -d '{"message": "Hello, Alice"}' # 继续该对话 curl http://localhost:3583/agents/hello/session - abc \ -H "Content - Type: application/json" \ -d '{"message": "What did I just say?"}' # 开始一个新的对话 curl http://localhost:3583/agents/hello/session - xyz \ -H "Content - Type: application/json" \ -d '{"message": "Hello from another conversation"}'

代理实例拥有沙盒状态,例如在提示和调度输入交互过程中写入的文件。管理机制将实例内相关的会话状态分组。会话在管理机制内持久保存消息历史记录和对话元数据。在 Cloudflare 上,会话数据由 Durable Objects 支持,可在请求之间持久保存。在 Node.js 上,会话默认存储在内存中,除非你提供自定义存储。在生产环境中,为你想要保留的代理实例生成一个稳定的 URL。当你需要在同一个管理机制内进行多个对话时,可以使用 `harness.session(threadName)`。

任务

使用 `session.task()` 在分离的会话中运行一个专注的、一次性的子代理。任务共享相同的沙盒/文件系统,但有自己的消息历史记录,并从其工作目录中发现 AGENTS.md 和 `.agents/skills/`。在 `prompt()` 和 `skill()` 调用期间,LLM 也可以使用相同的任务工具,因此代理可以自行委派并行研究或探索工作。

const session = await harness.session(); const research = await session.task('Research the auth flow and summarize the key files.', { cwd: '/workspace/project', agent: 'researcher', }); const answer = await session.prompt( `Use this research to draft the implementation plan:\n\n${research.text}`, );

使用 `defineAgentProfile()` 声明可重用的子代理配置文件,在 `createAgent(...)` 中进行配置,并使用 `task({ agent })` 选择它们进行分离委派。

const researcher = defineAgentProfile({ name: 'researcher', instructions: 'Research carefully.', }); const workflowAgent = createAgent(() => ({ model: 'anthropic/claude - sonnet - 4 - 6', subagents: [researcher], })); const harness = await init(workflowAgent); const session = await harness.session('review - thread'); await session.prompt('Review the latest changes.'); await session.task('Research related issues.', { agent: 'researcher' });

提供商设置

当模型流量需要特定于提供商的运行时设置时,例如企业 API 网关、与提供商兼容的代理、自定义端点或网关特定的凭证,可以使用 `configureProvider(...)`。这在管理凭证、审计日志、流量路由或自托管的 OpenAI 兼容提供商中很常见。在 `app.ts` 中使用模型值中的提供商 ID 配置这些设置,它们将应用于通过该提供商解析模型的每个管理机制和会话。

//.flue/app.ts import { configureProvider } from '@flue/runtime'; import { flue } from '@flue/runtime/routing'; export default { fetch(req, env, ctx) { configureProvider('anthropic', { baseUrl: env.ANTHROPIC_BASE_URL, headers: { 'X - Custom - Auth': env.GATEWAY_KEY }, // 当代理期望合成或网关特定的密钥时使用。 apiKey: 'dummy', }); return flue().fetch(req, env, ctx); }, };

导入的代理技能

工作区技能位于 `/.agents/skills//SKILL.md`,在运行时会被发现,并可以通过 `session.skill('name')` 按名称激活。静态技能导入是打包的构建依赖项,例如 `import review from '../skills/review/SKILL.md' with { type: 'skill' }`。

const agent = createAgent(() => ({ model: 'anthropic/claude - sonnet - 4 - 6', skills: [review], })); const harness = await init(agent); const session = await harness.session(); await session.skill('review'); // 或者直接激活导入的引用 await session.skill(review);

静态导入会暴露一个轻量级的 `SkillReference`,而不是技能内容。Vite 会验证符合规范的 `SKILL.md`,并将技能目录中每个允许的支持文件打包,不仅包括 `scripts/`、`references/` 和 `assets/`。打包的文件在直接引用激活和注册了该引用的代理操作中可读,但未注册的导入本身不会将其内容暴露给普通提示。可能包含机密信息或私钥的文件,如 `.env*`、`.dev.vars*`、凭证文件、密钥文件、`.aws/`、`.ssh/` 和 `.gnupg/`,会拒绝构建,而不是被部署。请将凭证信息放在技能目录之外。

自定义虚拟沙盒

对于大多数代理,建议使用内置的虚拟沙盒或 `sandbox: local()`(仅适用于 Node 目标)。生成的默认沙盒由 `@flue/runtime` 提供,应用程序无需声明 just - bash,除非应用程序代码直接导入它。如果你需要直接自定义 just - bash,可以将 just - bash 作为应用程序依赖项添加,并传递一个 Bash 工厂。该工厂每次必须返回一个全新的类似 Bash 的运行时,在闭包中共享文件系统对象,以便在会话和提示之间持久保存文件。

import { Bash, InMemoryFs } from 'just - bash'; const fs = new InMemoryFs(); const agent = createAgent(() => ({ sandbox: () => new Bash({ fs, cwd: '/workspace', python: true }), model: 'anthropic/claude - sonnet - 4 - 6', })); const harness = await init(agent); const session = await harness.session();

连接器

连接器可以将第三方服务(如沙盒提供商等)集成到 Flue 中。它们不是 npm 包,而是托管在 [https://flueframework.com/cli/connectors/](https://flueframework.com/cli/connectors/) 的 Markdown 安装说明,由你的 AI 编码代理应用到你的项目中。

flue add # 列出可用的连接器 flue add daytona | # 将其传递给你的编码代理(如 claude、opencode、codex、cursor - agent 等) flue add https://e2b.dev --category sandbox | # 从头开始构建一个连接器,将提供商的文档 URL 作为代理的起点

CLI 会获取指定连接器的 Markdown 文件,并在由代理运行(或使用 `--print`)时将其打印到标准输出,或者在终端中由人类运行时显示一个简短的可复制的 `flue add ... | ` 配方。你的代理会读取 Markdown 文件,并在选定的源目录(`.flue/`,然后是 `src/`,最后是项目根目录)下编写一个小的 TypeScript 适配器。

运行和连接

本地开发(flue dev)

这是一个长期运行的监视模式开发服务器,会在文件更改时重新构建和重新加载。你可以编辑代理代码,重新运行 `curl` 命令,即可看到更改效果。

flue dev --target node # Node.js 开发服务器 flue dev --target cloudflare # Cloudflare Vite/workerd 开发服务器

默认端口为 3583(在电话键盘上对应“FLUE”),可以使用 `--port` 覆盖。

import { createFlueClient } from '@flue/sdk'; const client = createFlueClient({ baseUrl: 'http://localhost:3583' }); const chat = client.agents.connect('chat', 'customer - 123'); await chat.ready; await chat.prompt('Hello', { session: 'support' }); chat.close(); const job = client.workflows.connect('summarize'); await job.ready; await job.invoke({ text: 'Summarize me' });

导出的 WebSocket 中间件可以对其自己的代理或工作流套接字端点进行身份验证。可以使用自定义的 `app.ts` 进行集中身份验证或挂载前缀,在 `app.route('/api', flue())` 之前应用普通的 Hono 中间件,相同的路由模型在 Node 和 Cloudflare 上都适用。在 `baseUrl` 中包含自定义挂载,例如 `'https://example.com/api'`,并使用 `websocketUrl: (url) => { url.searchParams.set('token', socketToken); return url; }` 进行 URL 携带或签名握手身份验证。HTTP 令牌和头选项不会自动应用于 WebSocket 升级,浏览器应使用 cookie 或应用程序设计的 URL 身份验证,而需要特定实现头的 Node 客户端可以提供自定义的 WebSocket 工厂。避免在 WebSocket 升级路由周围使用修改头的中间件。

`flue dev --target cloudflare` 需要 `wrangler` 作为项目的对等依赖项(`npm install --save - dev wrangler`)。

加载环境变量

`flue build`、`flue dev`、`flue run` 和 `flue connect` 在配置之前会加载项目根目录下的 `.env` 文件(如果存在)。可以使用 `--env ` 选择另一个文件,shell 设置的值优先级更高。`flue build` 在评估配置时可能会使用加载的值,但发布的工件在部署运行时不会加载 `.env` 文件。对于 Cloudflare 开发,Worker 运行时绑定继续使用官方的 `.dev.vars` 或 `.env` 文件以及 `CLOUDFLARE_ENV` 约定。

从 CLI 触发(flue run)

在本地构建并运行任何工作流,非常适合 CI 或一次性脚本调用。CLI 通过私有子进程通信调用构建的 Node 工件,与公共工作流路由和中间件无关。

flue run hello --target node \ --payload '{"text": "Hello world", "language": "French"}'
连接到代理实例(flue connect)

与代理实例打开一个交互式本地会话。构建的 Node 子进程会在连接期间保持活动状态,以便重复的提示可以共享内存中的会话状态。

flue connect chat customer - 123 --target node --session support
从 HTTP 端点触发(flue build)

将你的代理构建并部署为 Web 服务器,非常适合托管代理。`flue build` 会构建到 `./dist` 目录,你可以将其部署。目前支持 Cloudflare 和任何 Node.js 主机,未来还会支持更多平台。

flue build --target node # Node.js 服务器(单个捆绑的 .mjs 文件) flue build --target cloudflare # Cloudflare Workers + Durable Objects

对于 Cloudflare,`flue build` 和 `flue dev` 通过官方的 Cloudflare Vite/workerd 集成运行。生产构建会生成可由 `wrangler deploy` 部署的输出。那么,Flue 在未来还会有哪些新的发展和应用呢?

http://www.cnnetsun.cn/news/2798091.html

相关文章:

  • 高性能异步打印架构解析:PDFtoPrinter实现原理与安全优化方案
  • 零成本解锁WeMod Pro:开源增强工具全面指南
  • 效率提升秘籍:用快马生成自动化脚本,十分钟搞定claude code本地部署与监控
  • TPFanCtrl2技术深度解析:ThinkPad双风扇嵌入式控制与智能散热优化方案
  • 苹果平方字体PingFangSC免费使用终极指南:3分钟掌握专业中文字体
  • OpenProject开源项目管理软件:从入门到精通的完整指南
  • 模拟灰度传感器原理与实战:从循迹小车到简易颜色识别
  • CSDN AI数字营销链接配置实战:手把手教你为5类专栏定制专属引流链路(含平台API权限避坑指南)
  • 如何用OpenRocket在电脑上设计并仿真你的第一枚火箭模型
  • 天辛大师浅谈人机争霸赛,AI时代全人类大脑进化方向指南
  • CSDN原创检测算法逆向分析(2024最新版V3.7.2内核曝光):AI生成内容的“安全阈值”首次公开
  • 别再死记硬背了!用HBase 2.1.1 + Hadoop 2.7 搭建伪分布式环境,我踩过的坑都帮你填好了
  • 本地实现Overleaf般LaTeX编辑体验
  • 「ECG信号处理——(34)基于PSO优化ELM的睡眠分期研究」2026年06月05日
  • Linux玩转硬件:除了cutecom,还有哪些好用的串口调试工具?CH340驱动搞定后的选择指南
  • 别再傻傻分不清!一张图看懂SATA、M.2、NVMe硬盘怎么选(附避坑指南)
  • 别再纠结了!实测Colmap 3.6 vs OpenMVG:手把手教你为不同3D重建项目选对SFM工具
  • 明日方舟终极解放指南:如何用MAA助手一键完成全部日常任务
  • 嵌入式MCU开发实战:IAR环境下的RAM使用分析与栈溢出检测
  • 戴尔G15散热控制终极指南:开源替代AWCC的高效解决方案
  • 食品伙伴网实验室信息管理系统(LIMS)如何定制自己的管理系统
  • 终极指南:使用bandcamp-dl轻松下载Bandcamp高品质音乐
  • 三极管搭建RS232电平转换电路:从原理到实战的深度解析
  • 5分钟搭建智能安防系统:Frigate本地AI监控终极指南
  • 实战指南:WrenAI容器化部署与性能优化终极方案
  • 嵌入式LED情景调光:从PWM控制到低功耗设计的完整实践
  • 电源设计全链路解析:从需求评估到PCB布局与调试实战
  • C++写的Faiss向量检索服务:支持每日重建索引、GPU加速搜索、按日期过滤结果
  • 【愚公系列】《移动端AI应用开发》013-DeepSeek API开发与集成(深度集成与中间件架构)
  • 如何在本地安全对话?PrivateGPT隐私优先的AI解决方案指南