Flue 2.0

作者:Fred K. Schott

Flue 2.0 现已正式发布。我们围绕全新的基于 Hook 的 API 重构了 Agent 框架,解锁了一种全新的动态 Agent 能力,可以随时间演进其功能。

Agent Hook 是 Flue 2.0 的全新基础。Hook 让你构建的动态 Agent 能够管理自身状态、监听 Agent 生命周期事件,甚至在运行时动态附加不同的资源和能力来增强自身。

Hook 使用 TypeScript 编写,API 形式非常熟悉:

export function Assistant() {
	const [count, setCount] = usePersistentState('count', 0);
	useAgentStart(() => setCount((n) => n + 1));
	useModel('moonshot/kimi-k2');
	return `You are a helpful assistant. This conversation has ${count} messages.`;
}

借助动态 Agent,能力和指令可以随着对话的发展而增长和演进。一个客服 Agent 可以遵循多步工作流,在客户验证通过后获得新的工具,或者在任务变难时切换到更强的模型。我们构建 Flue 2.0 是因为我们需要在 Agent 中实现非平凡的功能,而现有的 Harness(运行时环境)和框架都没有好的方式来实现它们。

Flue 2.0 包含:

立即将下面的提示词传递给你的编码 Agent 来体验 Flue 2.0,它将帮你搭建第一个 Flue 项目:

Read https://flueframework.cn/start.md then help create my first agent...

更喜欢自己动手?请阅读入门指南。从 Flue 1.0 Beta 升级现有项目?迁移指南将引导你完成整个过程。

Agent Hook

在最初的 Flue 1.0 API 中,我采用了与其他所有 Agent 框架和 SDK 相同的静态方法来构建 Agent 架构。OpenAI SDK、Anthropic SDK、Cursor SDK、Eve、Mastra 等都将“Agent 定义”视为某种静态的、类似配置的东西,预先定义好然后在构建时锁定:

// Flue 1.0 API
export default defineAgent(() => ({
	model: 'moonshot/kimi-k2',
	tools: [replyToIssue],
	skills: [triage, verify],
	sandbox: local(),
	instructions,
}));

在 Flue 1.0 Beta 期间,我们与真实开发者一起深度使用了这个 API。结果出人意料:静态 Agent 方法对简单用例效果很好,但对于更复杂、非平凡的 Agent 和多步工作流,就开始捉襟见肘了。

我们开始思考:如果 Flue 1.0 存在这个问题,那么有多少其他流行的 Agent 框架和 SDK 也同样存在这个问题?

我们认定这是一个值得解决的问题,而且现在正是做出破坏性变更的最佳时机 —— 趁我们还处于早期阶段。我们尝试了很多不同的方法,最终一个熟悉的设计模式逐渐浮现:

// Flue 2.0 API
export function IssueTriageAgent() {
	useModel('moonshot/kimi-k2');
	useTool(replyToIssue);
	useSkill(triage);
	useSkill(verify);
	useSandbox(local());
	return instructions;
}

我上周开玩笑地将其称为“Agent 版的 React?”,灵感来源不言而喻。但我也认为这低估了我们自然而然走到这一步的程度。React Hook 被发明出来解决的同样问题,也适用于 Agent:如何在最低、最根本的层面组合功能?如何使其自然、富有表现力且易于维护?

Agent Hook 为托管 Agent 和工作流开启了全新的可能性。

假设你想让 Agent 能够按需升级其初始模型或 Sandbox。在 Flue 1.0 和其他流行的 Agent Harness 中,这并非易事。而在 Flue 2.0 中,只需几行代码:

export function CompanySlackAgent() {
	// 为每个 Agent 附加持久化数据,存储在你的数据库中。
	const [isEnhanced, setEnhanced] = usePersistentState('isEnhanced', false);
	// 赋予 Agent 自我升级的能力。
	useTool({ name: 'enhance', description: '...', run: () => setEnhanced(true) });
	// 按需附加新能力。
	if (isEnhanced) {
		useModel('anthropic/fable-5-0');
		useSandbox(daytona());
		useTool(/* ... */);
		useSkill(/* ... */);
		useSubagent(/* ... */);
	} else {
		useModel('moonshot/kimi-k2');
	}
	// 返回你的 Agent 指令。Flue 处理其余一切。
	return `You are a helpful assistant.`;
}

或者你想在 Agent 中表示一个类似状态机的工作流,引导 Agent 完成复杂的多步流程。使用静态 Agent,你没有好的方式来跟踪工作流中的当前步骤,或者根据当前步骤选择性地提供工具。

在 Flue 2.0 中,工作流就是持久化状态加上条件工具。为每个步骤赋予其专属的工具和 Skill,让 Agent 自行推进。下面是之前同一个分类 Agent,已扩展为完整的工作流:

export function IssueTriageAgent({ id }) {
	useSandbox(local());
	// 持久化记录 Agent 当前所处的工作流步骤。
	const [step, setStep] = usePersistentState('step', 'reproduce');
	// 每个步骤附加自己的工具和 Skill。
	if (step === 'reproduce') {
		useModel('anthropic/sonnet-5-0');
		useSkill(reproChecklist);
		useTool({ name: 'submit_repro', description: '...', run: () => setStep('diagnose') });
	}
	if (step === 'diagnose') {
		useModel('anthropic/fable-5-0');
		useSkill(debuggingGuide);
		useTool({ name: 'submit_diagnosis', description: '...', run: () => setStep('report') });
	}
	if (step === 'report') {
		useModel('anthropic/sonnet-5-0');
		useTool(postGitHubComment);
	}
	return `Follow the workflow to triage GitHub issue ${id}: reproduce -> diagnose -> report.`;
}

内置 Hook 提供了增强 Agent 新能力和资源的功能,如 useTool()、useSandbox() 和 useMcpConnection()。Hook 还提供持久化状态(usePersistentState())、生命周期事件(useAgentStart())等。

就像 React Hook 一样,它们可以很好地组合在一起。你可以构建自己的自定义 Hook,在你的代码库、公司内或通过 npm 与全世界共享。

export function useLinear(apiKey) {
	useMcpConnection({
		name: 'linear',
		url: 'https://mcp.linear.app/mcp',
		auth: apiKey,
	});
}

export function ProjectAssistant() {
	useLinear(process.env.LINEAR_API_KEY);
	useGitHub(process.env.GITHUB_API_KEY);
	useBrowser();
	return 'Help your team manage their work.';
}

请阅读 Agent Hook 指南开始使用 Hook 构建,或在 API 参考文档中探索所有内置 Hook。

2.0 的其他更新

此版本还包含一系列其他变更和功能,旨在简化使用 Flue 构建 Agent 的体验。亮点如下:

托管 Agent 使用 Vite 构建。 flue dev 和 flue build CLI 命令已移除:Flue 应用现在是一个普通的 Vite 项目,在 vite.config.ts 中配置 flue() 插件,在 app.ts 中通过 Hono 应用显式挂载 Agent 路由。vite dev 运行开发服务器,vite build 生成可部署到 Node.js 或 Cloudflare(通过官方 @cloudflare/vite-plugin)的产物,flue init 搭建整个配置。这是本版本的一个主题:凡是生态系统已有更好方案的地方,Flue 就删除自己的接口 —— Vite 负责构建,Hono 负责路由,Pi 负责 Provider(服务提供商)。

MCP 支持已内置。 新的 useMcpConnection() Hook 可将远程 MCP 服务器的工具直接挂载到你的 Agent 中。连接是无状态的(在 Cloudflare Workers 上运行良好),可以标记为可选,这样当服务器不稳定时会优雅降级而不是导致运行失败,并且 —— 因为连接只是一个 Hook —— 可以有条件地附加或像其他任何东西一样打包成自定义 Hook。

Workflow 和 Action 已简化。 defineWorkflow() 已移除,因为持久性使其变得多余:Agent 接受的每条消息都会精确结算一次,即使经历崩溃、重启和重新部署。工作流现在只是运行 Agent 的任何脚本 —— CI 任务中的 flue run、Node.js 脚本中的 init() 句柄,或平台工作流引擎中的一个步骤(Cloudflare Workflows、Inngest、Temporal)。对于 Agent 内需要检查点的副作用,工具可以启用 durable: true 并通过 step.do() 记录进度。Workflow 指南涵盖了所有模式。

全新的会话级 SDK。 每个对话都有一个 URL,新的 @flue/sdk 客户端精确封装一个对话:send() 发送消息、read() 读取已结算的回复、observe() 实时观察更新、abort() 中止工作。消息现在携带带类型的 purpose 和 display 元数据,客户端无需猜测即可区分用户聊天和内部活动。在前端,@flue/react 的 useFlueAgent({ url }) 将同一个对话流式传输到你的 UI 中。

Cloudflare 零配置追踪。 将 Agent 部署到 Cloudflare,启用 Workers Traces,无需任何埋点代码即可追踪每个对话的内容、元数据、Token 成本等。查看我们的生态系统,接入你已有的任何其他可观测性体系。

立即体验 Flue 2.0

理解 Flue 最好的方式就是用它来构建。请阅读完整的入门指南和迁移指南,或将下面的提示词交给你的编码 Agent 来搭建你的第一个项目:

Read https://flueframework.cn/start.md then help create my first agent...

想要支持这个项目? 在 GitHub 上给我们 Star,这是表达支持和帮助 Flue 成长最简单的方式。

想要了解最新动态? 关注 @flueai 获取更多技巧和开发更新。