Flue:我花半天跑通了一个 Agent,顺便发现了它和 LangChain 最不一样的 3 件事

你有没有在 LangChain 的 callback 地狱里调试过一整个下午?

我有。上个月在给一个内部工具加流式输出时,我在 handleLLMNewTokenhandleChainEndhandleToolEnd 三个 callback 之间绕了两个多小时,最后发现问题出在 callback 注册顺序上——这种调试体验,说实话,不太像在写 TypeScript,更像在猜一个黑盒的心情。

这篇文章我只写跑通了什么、差在哪里,不废话框架哲学。

---

一、为什么我要再试一个新框架

框架疲劳是真实存在的。LangChain、LlamaIndex、Mastra、Vercel AI SDK……每隔一段时间就有新东西冒出来,每次都说自己"更简单"。我的默认反应已经从"哇好酷"变成了"先观望"。

Flue 能让我多看一眼,是因为它的 GitHub README 里有一句话戳到了我:

"We believe the best abstraction is the one you don't notice."

然后我看了它的工具注册示例——用的是普通函数,没有继承,没有装饰器,类型推导全靠 TypeScript 原生能力。这和 LangChain 的 Tool 类继承方式形成了很鲜明的对比。

我决定花半天验证一下这个承诺是否兑现。结论是:在轻量场景下,它的承诺基本成立。但也有一些地方需要你提前知道。

---

二、从零到跑通——最简 Agent 的完整搭建流程

安装依赖

npm install @flue-ai/core

如果你用 pnpm

pnpm add @flue-ai/core

⚠️ 踩坑①:ESM 模块问题
Flue 默认输出 ESM 格式。如果你的项目是 CommonJS("type" 字段不是 "module"),运行时会报:
> Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported
解决方案:在 package.json 里加 "type": "module",或者把入口文件改成 .mjs 后缀。用 ts-node 的同学需要加 --esm 标志,或者切换到 tsx

配置 API Key

在项目根目录创建 .env 文件:

OPENAI_API_KEY=sk-xxxxxxxx

OPENAI_BASE_URL=https://api.openai.com/v1

⚠️ 踩坑②:环境变量加载时机
如果你用 dotenv,必须在 import Flue 之前加载,否则 process.env.OPENAI_API_KEY 会是 undefined
> // ✅ 正确顺序
import 'dotenv/config';
import { createAgent } from '@flue-ai/core';

>

// ❌ 错误顺序(Key 还没加载就初始化了)
import { createAgent } from '@flue-ai/core';
import 'dotenv/config';

---

💡 注意:本文示例使用的是 OpenAI 兼容接口格式。如果你在国内访问不稳定,或者想同时测试多个模型(GPT-5.2、Claude Sonnet 4.6、Gemini 3.1 Pro),可以用 [api.884819.xyz](https://api.884819.xyz) 作为统一接入层——配置方式和官方 API 完全一致,把 OPENAI_BASE_URL 换掉就行,其余代码零修改,新用户注册即送体验 token。

---

定义工具、注册 Agent、发送第一条消息

下面是一个完整可运行的最简 Agent,功能是"查询当前天气"(用 mock 数据模拟,不需要真实 API):

import 'dotenv/config';

import { createAgent, defineTool } from '@flue-ai/core';

// 1. 用普通函数定义工具,类型由 TypeScript 自动推导

const getWeather = defineTool({

name: 'get_weather',

description: '查询指定城市的当前天气',

parameters: {

city: { type: 'string', description: '城市名称' },

},

execute: async ({ city }) => {

// 实际项目里这里调真实天气 API

return { city, temperature: 22, condition: '晴天' };

},

});

// 2. 创建 Agent,注册工具

const agent = createAgent({

model: 'gpt-4o',

tools: [getWeather],

baseURL: process.env.OPENAI_BASE_URL,

apiKey: process.env.OPENAI_API_KEY,

});

// 3. 发送消息,拿到结果

const result = await agent.run('北京今天天气怎么样?');

console.log(result.content);

// 输出:北京当前天气为晴天,气温 22°C。

从安装到这段代码跑通,我实际花了不到 40 分钟(含踩坑时间)。代码总量不到 35 行,没有继承,没有装饰器,没有 pipe

---

三、和 LangChain 最不一样的 3 个地方

这是全文价值密度最高的部分。我不打算说哪个更好,只是把差异摆出来,让你自己判断哪个更符合你的习惯。

差异①:工具注册方式

LangChain 的方式——继承 Tool 类,重写 _call 方法:
// LangChain 风格

import { Tool } from 'langchain/tools';

class WeatherTool extends Tool {

name = 'get_weather';

description = '查询指定城市的当前天气';

async _call(input: string): Promise {

const city = JSON.parse(input).city;

return JSON.stringify({ city, temperature: 22, condition: '晴天' });

}

}

const tools = [new WeatherTool()];

Flue 的方式——普通函数 + defineTool,参数类型直接推导:
// Flue 风格

const getWeather = defineTool({

name: 'get_weather',

description: '查询指定城市的当前天气',

parameters: {

city: { type: 'string', description: '城市名称' },

},

execute: async ({ city }) => {

// city 在这里已经是 string 类型,IDE 有完整提示

return { city, temperature: 22, condition: '晴天' };

},

});

说实话,差距最明显的地方在 IDE 体验。LangChain 的 _call 接收的是 string,你得自己 JSON.parse,类型是 any。Flue 的 execute 里,city 直接就是 string,IDE 有补全,重构时也不会漏改。

对于工具数量多的项目,这个差异会被放大得很明显。

差异②:流式输出的处理姿势

这是我个人觉得 Flue 做得最好的一个地方。

LangChain 的流式——需要挂 callback:
// LangChain 流式输出

import { ChatOpenAI } from 'langchain/chat_models/openai';

const model = new ChatOpenAI({

streaming: true,

callbacks: [

{

handleLLMNewToken(token: string) {

process.stdout.write(token);

},

},

],

});

await model.call([new HumanMessage('你好')]);

Flue 的流式——直接返回 AsyncIterator,用 for await 消费:
// Flue 流式输出

const stream = agent.stream('你好,请介绍一下自己');

for await (const chunk of stream) {

process.stdout.write(chunk.delta);

}

两种方式都能实现流式输出,但心智模型完全不同。LangChain 是"注册事件监听器"的思路,Flue 是"消费异步迭代器"的思路。

后者更符合现代 TypeScript 的惯用写法——for await...of 是语言原生支持的,不需要理解框架特有的 callback 系统。如果你要在 Next.js 的 Route Handler 里做流式响应,Flue 的写法可以直接接 ReadableStream,几乎不需要适配层。

差异③:错误边界的设计哲学

这个差异比较底层,但对生产环境很重要。

LangChain 的倾向——工具执行失败时抛出异常,由调用方 try/catch:
// LangChain 风格——异常向上冒泡

try {

const result = await chain.call({ input: '查天气' });

} catch (e) {

// 你得在这里判断是工具错误、网络错误还是模型错误

console.error('出错了', e);

}

Flue 的方式——错误作为返回值的一部分,结构化处理:
// Flue 风格——错误是返回值的一部分

const result = await agent.run('查天气');

if (result.status === 'error') {

console.error('工具调用失败:', result.error.message);

console.error('失败的工具:', result.error.toolName);

} else {

console.log(result.content);

}

Flue 把工具执行的错误封装进返回结构,而不是让它变成一个游走在调用栈里的异常。这种设计在处理"部分工具失败、Agent 继续运行"的场景时会方便很多——你可以精确知道哪个工具失败了,而不是捕获一个混合了所有可能错误来源的 catch 块。

---

特性对比速查表

| 特性 | Flue | LangChain (TS) | | 工具注册方式 | 普通函数 + 类型推导 | 继承 Tool 类 | | 流式输出 | 原生 AsyncIterator | Callback 注册 | | 错误处理 | 结构化返回值 | 抛出异常 | | TypeScript 支持 | 原生优先设计 | 后期补全,部分 any | | RAG / Memory 生态 | 较早期,生态有限 | 成熟,插件丰富 | | 学习曲线 | 低(贴近 TS 原生) | 中高(需理解框架概念) | | 社区活跃度 | 成长期 | 成熟期,资料丰富 |

---

四、什么时候用 Flue,什么时候还是选 LangChain

不捧一踩一,直接给结论:

优先考虑 Flue 的场景:
  • 纯 API 编排的轻量 Bot 或内部工具
  • 团队 TypeScript 熟练度高,不想引入额外的框架概念
  • 项目工具数量多,需要良好的 IDE 类型提示
  • 你在 Next.js / Hono 等现代框架里做流式响应
优先考虑 LangChain 的场景:
  • 需要完整的 RAG 管道(文档加载、分块、向量检索、重排序)
  • 需要 Memory 模块做多轮对话状态管理
  • 团队已有 LangChain 经验,迁移成本高
  • 需要大量现成的 Loader、Retriever、Splitter 等组件

一句话总结:纯 Agent 编排选 Flue,需要完整 RAG + Memory 生态还是 LangChain 更稳

Flue 目前的生态还在成长期,如果你的项目依赖很多"开箱即用"的文档处理组件,LangChain 的资产积累在短期内仍然是优势。

---

五、框架选型的底层逻辑

框架只是放大器。Flue 和 LangChain 在底层做的事情是一样的:把你的工具描述序列化成 JSON Schema,塞进系统提示,解析模型返回的 tool_call,执行对应函数,把结果再喂回模型。这个循环,用原生 fetch 也能写出来。

框架帮你省的是这些胶水代码——但如果框架本身的抽象层引入了新的调试成本,它就没有完成它的使命。

无论 Flue 还是 LangChain,底层都是一次次 HTTP 请求打到 LLM API。选一个延迟低、格式兼容好的 API 接入层,能省掉很多不必要的调试时间。我自己在测试阶段用的是 [api.884819.xyz](https://api.884819.xyz),按量计费,不需要月租,国产模型(Deepseek、通义千问)完全免费,切换模型只需要改一个参数。

框架换了一个又一个,你的 API 调用稳定吗? 这个问题,值得在选框架之前先想清楚。

---

📌 下一篇预告

>

Flue 的工具注册很轻量——但如果我要给 Agent 加上"记忆"和"多轮对话状态管理",它能撑住吗?

>

我正在测试 Flue + 自定义 Memory Store 的组合,以及它和 LangChain Memory 模块的实现成本差距。有状态的 Agent 在工程上远比无状态复杂,下篇会拆解具体的实现方案和我踩过的坑。

>

如果你也在做有状态的 Agent,下篇可能正好是你需要的。→ 关注专栏,更新时第一时间收到通知。

---

本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。

#AI开发 #TypeScript #LangChain #Agent开发 #8848AI #大模型应用 #前端开发