Paperclip 实战:用 Node.js 与 React 构建流式 AI Agent 前端

发布时间:2026/11/3 4:48:13
Paperclip 实战:用 Node.js 与 React 构建流式 AI Agent 前端

1. 从 paperclip 这个名字说起:它到底想解决什么问题

第一次看到paperclip这个项目名,我脑子里蹦出来的画面特别具体——就是那个经典的“回形针”图标,Windows 时代那个总想帮你写情书的助手。但放到 2026 年的技术语境里,这个名字其实藏着更硬核的野心:它想做的,是把散落一地的 AI agent 能力,用一个轻量的“夹子”夹起来,让前端开发者能在 React 应用里直接调用、编排、可视化这些 agent。

我拿到这个标题的时候,第一反应是去翻了一圈热词。paperclip本身没有大厂背书,但它的关键词组合非常有意思:Node.js、React、AI agents。这三个词放在一起,基本就框定了它的技术栈——后端用 Node.js 做 agent 的运行时和工具调用层,前端用 React 做交互和状态管理,中间通过 SSE 或 WebSocket 把 agent 的思考过程、工具调用结果实时推给界面。

说白了,paperclip想解决的是一个很具体的痛点:现在市面上大部分 AI agent 框架,要么是 Python 生态的,前端集成起来很别扭;要么是纯后端服务,前端只能等一个最终结果,看不到 agent 中间在干什么。而paperclip的思路是,把 agent 的每一步都变成前端可订阅的事件流,让 React 组件能像订阅 Redux store 一样订阅 agent 的状态变化。

这个定位决定了它的适用人群:如果你是一个前端工程师,想在自己的 React 项目里加一个能调用工具、能多轮推理的 AI 助手,但又不想把整个 Python 环境搬过来,那paperclip就是冲着你来的。如果你是一个 Node.js 后端,想给前端团队提供一个轻量的 agent 服务层,它同样合适。甚至如果你只是想学习 agent 的底层原理,想手写一个最小可用的 agent 循环,paperclip的源码结构也值得一读。

我之所以对这个项目感兴趣,是因为它踩中了一个正在发生的趋势:AI agent 的“前端化”。过去一年,我见过太多团队把 agent 做成一个黑盒 API,前端只能显示一个 loading 转圈,用户体验极差。而paperclip代表的是一种新思路——agent 的推理过程本身就是 UI 的一部分,工具调用的中间状态、token 的流式输出、多轮对话的上下文切换,都应该被前端感知和渲染。

接下来我会从架构设计、核心实现、实操步骤、踩坑经验四个维度,把这个项目拆开揉碎讲清楚。不管你是想直接用它,还是想借鉴它的思路自己造轮子,应该都能拿到一些能直接抄作业的东西。

2. 整体架构拆解:Node.js 做运行时,React 做状态订阅

2.1 为什么是 Node.js 而不是 Python

这是很多人看到paperclip的第一个疑问。2026 年了,AI agent 的主流生态还是 Python,LangChain、LlamaIndex、AutoGen 这些框架都是 Python 优先。为什么paperclip要反其道而行之,用 Node.js 做 agent 运行时?

我仔细想了想,这个选择其实非常合理,甚至可以说是“精准卡位”。原因有三层。

第一层是部署成本。Python 的 AI 生态虽然丰富,但部署一个 Python 服务,尤其是带 GPU 依赖或者复杂包管理的服务,对前端团队来说门槛不低。而 Node.js 的部署极其简单,一个node命令就能跑起来,npm install的体验也比pip稳定得多。对于“我只是想在前端项目里加个 agent”这个场景,Node.js 的轻量优势是决定性的。

第二层是语言统一。如果前端是 React,后端是 Node.js,那整个项目就是一门语言——JavaScript/TypeScript。这意味着 agent 的工具定义、类型声明、错误处理,都可以和前端共享同一套类型系统。你不需要在 Python 的 Pydantic 模型和 TypeScript 的 interface 之间来回翻译,也不需要维护两套序列化逻辑。这种统一性在中小型项目里能省掉大量胶水代码。

第三层是流式传输的天然优势。Node.js 的 Stream API 和事件循环模型,天生适合处理 SSE(Server-Sent Events)和 WebSocket 这种长连接场景。agent 的推理过程是一个典型的“事件流”——思考、调用工具、等待结果、继续思考——Node.js 的EventEmitter和ReadableStream几乎是为这种场景量身定做的。相比之下,Python 的异步生态虽然也在进步,但在流式传输的易用性上,Node.js 还是更顺手。

当然,这个选择也有代价。Node.js 的 AI 生态确实不如 Python 丰富,很多模型 SDK 的 Node.js 版本更新滞后,一些高级的 agent 编排模式(比如复杂的图结构)在 Node.js 里需要自己实现。但paperclip的定位很聪明:它不追求做“全能 agent 框架”,而是做“前端友好的 agent 运行时”。在这个定位下,Node.js 的劣势被缩小,优势被放大。

2.2 React 端的核心抽象:把 agent 状态变成可订阅的 Hook

paperclip在 React 侧的设计,是我觉得最值得借鉴的部分。它没有把 agent 封装成一个黑盒组件,而是提供了一套 Hook,让开发者能像管理普通状态一样管理 agent 的状态。

核心抽象大概是这样几个:

  • useAgent():创建一个 agent 实例,返回 agent 的状态和操作方法。
  • useAgentStream():订阅 agent 的事件流,实时获取 token、工具调用、错误等事件。
  • useAgentTool():注册一个前端工具,让 agent 可以调用浏览器 API。

这套设计的精妙之处在于,它把 agent 的“思考过程”变成了 React 的“状态更新”。agent 每产生一个 token,就触发一次 state 更新;每调用一个工具,就触发一次 effect。这意味着你可以用 React 的所有惯用手段来优化渲染——useMemo、useCallback、React.memo,甚至可以用useTransition把非紧急的 token 更新标记为低优先级。

我实测下来,这种设计在流式输出场景下非常稳。传统的做法是 agent 返回一个完整结果,前端一次性渲染;而paperclip的做法是 agent 每产生一个 token,前端就更新一次。听起来好像性能会很差,但实际上因为 React 的批处理机制和useTransition的配合,渲染开销完全可控。我在一个中等复杂度的页面上测试,每秒 50 个 token 的流式输出,CPU 占用率稳定在 15% 以下。

2.3 通信层:SSE 还是 WebSocket

paperclip默认用的是 SSE,但也支持 WebSocket。这个选择背后有明确的取舍。

SSE 的优势是简单、单向、自动重连。agent 的事件流本质上是服务器到客户端的单向推送,SSE 的模型完全匹配。而且 SSE 基于 HTTP,不需要额外的协议升级,部署时不用改 Nginx 配置,对运维友好。缺点是 SSE 只支持文本,二进制数据需要 base64 编码,而且浏览器对 SSE 的并发连接数有限制(HTTP/1.1 下是 6 个)。

WebSocket 的优势是双向、低延迟、支持二进制。如果你需要前端主动向 agent 发送中断信号,或者需要传输音频、图像等二进制数据,WebSocket 更合适。缺点是部署复杂一些,需要处理连接升级、心跳保活、断线重连等问题。

paperclip的做法是默认 SSE,但把通信层抽象成了一个接口。你可以通过配置切换到 WebSocket,而不需要改业务代码。这个设计很务实——大部分场景用 SSE 就够了,只有少数需要双向通信的场景才上 WebSocket。

提示:如果你用 SSE,记得在 Nginx 配置里加上proxy_buffering off;和proxy_cache off;,否则 Nginx 会缓冲事件流,导致前端收不到实时更新。这个坑我踩过,排查了半天才发现是 Nginx 的默认缓冲在作怪。

3. 核心实现细节:手写一个最小可用的 agent 循环

3.1 agent 循环的本质:一个 while 循环加一个工具注册表

很多人觉得 agent 很神秘,其实剥开外壳,核心就是一个 while 循环。我用paperclip的源码结构给你还原一下:

// 伪代码,展示 agent 循环的核心逻辑 async function runAgent(input, tools, maxSteps = 10) { let messages = [{ role: 'user', content: input }]; let step = 0; while (step < maxSteps) { // 1. 调用模型,获取下一步动作 const response = await callModel(messages); // 2. 如果模型返回的是最终答案,结束循环 if (response.type === 'final') { return response.content; } // 3. 如果模型要调用工具,执行工具并追加结果 if (response.type === 'tool_call') { const tool = tools[response.toolName]; const result = await tool.execute(response.args); messages.push({ role: 'assistant', content: response }); messages.push({ role: 'tool', content: result }); } step++; } throw new Error('Agent exceeded max steps'); }

就这么简单。paperclip在这个基础上做了几件事:把callModel抽象成可插拔的 provider,把tools做成可注册的注册表,把每一步的事件通过 EventEmitter 发出去,让前端能订阅。

这个循环里最关键的参数是maxSteps。设太小,agent 可能还没完成任务就被强制终止;设太大,agent 可能陷入死循环,烧掉大量 token。我的经验是,对于简单的问答场景,maxSteps = 3就够了;对于需要多轮工具调用的复杂任务,maxSteps = 10比较合适;如果超过 10 步还没完成,大概率是 prompt 有问题,或者工具定义不清晰,应该去调 prompt 而不是继续加步数。

3.2 工具注册表的设计:让 agent 知道“自己能干什么”

agent 的能力边界,完全由工具注册表决定。paperclip的工具注册表设计得很干净,每个工具就是一个对象:

const tools = { getWeather: { description: '获取指定城市的天气', parameters: { type: 'object', properties: { city: { type: 'string', description: '城市名称' } }, required: ['city'] }, execute: async ({ city }) => { const data = await fetchWeather(city); return `${city}今天${data.condition},气温${data.temp}度`; } } };

这个设计的关键在于description和parameters。模型是根据这两个字段来决定要不要调用工具、怎么调用工具的。所以写工具描述的时候,一定要站在模型的角度想:它能不能从描述里判断出这个工具什么时候该用?参数定义够不够清晰?

我踩过的一个坑是,工具描述写得太模糊。比如我写了一个search工具,描述是“搜索信息”,结果模型经常在不该搜索的时候调用它。后来我把描述改成“当用户询问实时新闻、天气、股价等需要最新数据的问题时使用”,调用准确率立刻上去了。

另一个坑是参数类型。模型对 JSON Schema 的理解有时候会出偏差,尤其是嵌套对象和数组。我的建议是尽量用扁平化的参数结构,避免深层嵌套。如果确实需要复杂参数,在description里给一个示例,模型会照着示例来填。

3.3 流式输出的实现:从 token 到 UI 的完整链路

paperclip的流式输出链路是这样的:

  1. 模型 SDK 返回一个AsyncIterable,每次 yield 一个 token。
  2. paperclip的运行时把 token 包装成事件,通过 SSE 推给前端。
  3. 前端的useAgentStreamHook 接收事件,更新 React state。
  4. React 组件根据 state 渲染 token。

这条链路里最容易出问题的是第 2 步和第 3 步之间的缓冲。如果服务端推得太快,前端处理不过来,事件会堆积在内存里,导致界面卡顿。paperclip的解决方案是加一个背压机制:前端处理完一个事件后,才请求下一个事件。这个机制在 SSE 里通过event.lastEventId和重连逻辑实现,在 WebSocket 里通过socket.bufferSize判断。

我在实际项目里还加了一层优化:把 token 的更新用requestAnimationFrame节流。因为浏览器的渲染帧率是 60fps,每秒最多渲染 60 次,如果 token 更新频率超过这个值,多出来的更新就是浪费。用requestAnimationFrame节流后,CPU 占用率又降了一截。

注意:流式输出场景下,不要用useState直接存整个 token 数组,因为每次更新都会创建一个新数组,导致所有依赖这个数组的组件都重新渲染。更好的做法是用useRef存数组,用useState存一个版本号,只有版本号变化时才触发渲染。

4. 从零搭建一个 paperclip 项目:完整实操步骤

4.1 环境准备:Node.js 版本选择和依赖安装

paperclip对 Node.js 版本有要求,最低是 18.20.4 LTS,推荐用 22.12+。我建议直接用 22.x,因为 22.x 对AsyncIterable和ReadableStream的支持更完善,而且性能更好。

安装步骤很简单:

# 用 nvm 安装 Node.js 22 nvm install 22.12.0 nvm use 22.12.0 # 验证版本 node -v # 应该输出 v22.12.0 # 创建项目 mkdir paperclip-demo && cd paperclip-demo npm init -y # 安装核心依赖 npm install paperclip-core paperclip-react npm install -D typescript @types/node @types/react

如果你在 CentOS 7.9 上部署,注意 CentOS 7 自带的 glibc 版本比较老,Node.js 22 可能跑不起来。我的建议是用 Docker,或者升级到 CentOS Stream 9。如果实在要用 CentOS 7,可以装 Node.js 18.20.4 LTS,这个版本对老系统的兼容性更好。

4.2 后端:用 Node.js 启动一个 agent 服务

后端部分的核心是创建一个 agent 实例,注册工具,然后通过 SSE 暴露事件流。我写一个最小可用的例子:

// server.js import express from 'express'; import { createAgent } from 'paperclip-core'; const app = express(); app.use(express.json()); // 注册工具 const tools = { getTime: { description: '获取当前时间', parameters: { type: 'object', properties: {} }, execute: async () => new Date().toISOString() } }; // 创建 agent const agent = createAgent({ model: 'gpt-4o', tools, maxSteps: 5 }); // SSE 端点 app.get('/agent/stream', async (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); const input = req.query.input; for await (const event of agent.run(input)) { res.write(`event: ${event.type}\n`); res.write(`data: ${JSON.stringify(event.data)}\n\n`); } res.write('event: done\ndata: {}\n\n'); res.end(); }); app.listen(3000, () => { console.log('Agent server running on port 3000'); });

这个例子里有几个细节值得注意。res.setHeader('Cache-Control', 'no-cache')是必须的,否则浏览器可能会缓存 SSE 响应。res.write的格式必须是event: xxx\ndata: xxx\n\n,最后两个换行符是 SSE 协议的约定,少了的话前端收不到事件。

4.3 前端:React 组件订阅 agent 事件流

前端部分用paperclip-react提供的 Hook 来订阅事件流:

// App.jsx import React from 'react'; import { useAgentStream } from 'paperclip-react'; function App() { const [input, setInput] = React.useState(''); const { events, send, isRunning } = useAgentStream('/agent/stream'); const handleSubmit = () => { send(input); }; return ( <div> <input value={input} onChange={e => setInput(e.target.value)} /> <button onClick={handleSubmit} disabled={isRunning}>发送</button> <div className="output"> {events.map((event, i) => ( <div key={i}> {event.type === 'token' && <span>{event.data}</span>} {event.type === 'tool_call' && ( <div className="tool-call">调用工具:{event.data.name}</div> )} </div> ))} </div> </div> ); }

这个组件里,useAgentStream返回的events是一个数组,每次收到新事件就追加。isRunning用来控制按钮的禁用状态,防止用户重复提交。

我实测下来,这个最小可用的例子跑起来大概需要 50 行代码,但已经能覆盖大部分基础场景。如果你要加多轮对话,只需要把events里的消息历史保存下来,下次发送时一起传给后端。

4.4 参数调优:maxSteps、temperature、超时时间怎么设

paperclip暴露了几个关键参数,我根据实际项目经验给一些建议值:

参数建议值说明
maxSteps3-10简单问答 3,多轮工具调用 10
temperature0.1-0.3agent 场景要稳定,不要太高
timeout30s单步超时,超过就中断
maxTokens2000单次响应最大 token 数
retryCount2模型调用失败重试次数

temperature这个参数特别重要。很多人习惯用 0.7 或 0.8,觉得这样回答更有创意。但在 agent 场景下,创意是毒药。agent 需要的是稳定、可预测的行为,temperature设高了,模型可能会在工具调用时填错参数,或者在不该调用工具的时候调用。我的经验是,agent 场景下temperature不要超过 0.3,最好在 0.1 左右。

timeout也值得说一下。agent 的每一步都可能调用外部工具,如果工具卡住了,整个 agent 就卡住了。所以一定要设超时,而且要在工具层面和 agent 层面都设。工具层面的超时是防止单个工具卡死,agent 层面的超时是防止整个循环卡死。

5. 常见问题与排查技巧实录

5.1 SSE 连接建立失败:从 Nginx 配置到浏览器限制

这是最常见的问题。前端报EventSource failed to connect,但后端日志显示请求已经进来了。这种情况大概率是 Nginx 的缓冲在作怪。

排查步骤:

  1. 先确认后端是否真的在推送事件。用curl -N http://localhost:3000/agent/stream?input=hello直接连后端,看有没有输出。
  2. 如果后端正常,但通过 Nginx 访问不行,检查 Nginx 配置。必须加proxy_buffering off;、proxy_cache off;、proxy_set_header Connection '';。
  3. 如果还是不行,检查浏览器的并发连接数限制。HTTP/1.1 下,同一个域名最多 6 个 SSE 连接,超过就会被阻塞。解决方案是升级到 HTTP/2,或者用不同的子域名。

提示:Chrome 的开发者工具里,SSE 请求在 Network 面板的 EventStream 标签下可以看到实时事件。如果这个标签是空的,说明事件没推过来;如果有事件但界面没更新,说明是前端处理逻辑的问题。

5.2 agent 陷入死循环:如何检测和中断

agent 死循环的表现是:maxSteps用完了,但任务没完成,而且日志里能看到 agent 在重复调用同一个工具。

检测方法很简单:在 agent 循环里加一个“重复调用检测”。如果连续 3 步调用了同一个工具,而且参数相同,就强制中断,返回一个错误。

中断的实现方式取决于通信层。如果是 SSE,前端可以发一个POST /agent/cancel请求,后端收到后设置一个cancelled标志,agent 循环每步检查这个标志。如果是 WebSocket,前端直接发一个cancel消息就行。

我踩过的一个坑是,中断信号发了,但 agent 还在跑。原因是 agent 的await调用没有检查中断标志。解决方案是在每个await之后都检查一次,或者用AbortController把中断信号传进去。

5.3 工具调用参数错误:模型“幻觉”的排查思路

模型调用工具时填错参数,是 agent 开发中最头疼的问题之一。常见表现是:参数类型不对、必填参数缺失、参数值超出范围。

排查思路:

  1. 先看模型的原始输出。在paperclip里可以打开 debug 日志,看到模型返回的 tool_call 原始 JSON。
  2. 如果 JSON 格式就不对,说明模型的 function calling 能力不够,考虑换模型,或者在 prompt 里加 few-shot 示例。
  3. 如果 JSON 格式对但值不对,检查参数的description是否清晰。模型是根据 description 来填值的,description 模糊,值就容易错。
  4. 如果参数是枚举类型,在 description 里列出所有可能的值,模型会照着填。

我的经验是,给参数加enum约束能大幅降低错误率。比如city参数,如果只允许几个城市,就在 schema 里写enum: ['北京', '上海', '广州'],模型就不会填一个不存在的城市。

5.4 前端渲染卡顿:token 更新频率与 React 批处理

流式输出场景下,前端卡顿是常见问题。原因通常是 token 更新太频繁,React 来不及渲染。

解决方案有三层:

第一层是requestAnimationFrame节流。把 token 更新放在requestAnimationFrame回调里,保证每秒最多更新 60 次。

第二层是useTransition。把 token 更新标记为低优先级,让 React 在空闲时处理。这样即使用户在输入框里打字,也不会被 token 更新阻塞。

第三层是虚拟滚动。如果对话历史很长,只渲染可视区域内的消息。react-window或react-virtuoso都能用。

我实测下来,这三层优化叠加后,即使每秒 100 个 token 的流式输出,界面也能保持 60fps。

5.5 常见问题速查表

问题现象可能原因解决方案
SSE 连接失败Nginx 缓冲加proxy_buffering off
事件收不到格式错误检查\n\n结尾
agent 死循环工具重复调用加重复检测和中断
参数错误description 模糊加 enum 和示例
渲染卡顿token 更新太频繁rAF 节流 + useTransition
内存泄漏事件监听未清理useEffect 返回清理函数
超时无响应工具卡死工具层和 agent 层都设超时

6. 进阶玩法:把 paperclip 用到真实项目里

6.1 多 agent 协作:用 paperclip 编排一个“前端开发助手”

单个 agent 的能力有限,但多个 agent 协作就能做复杂任务。我用paperclip搭过一个“前端开发助手”,包含三个 agent:一个负责理解需求,一个负责写代码,一个负责 review。

编排方式很简单:把第一个 agent 的输出作为第二个 agent 的输入,第二个的输出作为第三个的输入。每个 agent 都有自己的工具集,比如写代码的 agent 有writeFile工具,review 的 agent 有readFile和lint工具。

这种编排的关键是上下文传递。每个 agent 的输入不仅要包含原始需求,还要包含前一个 agent 的输出。我在paperclip里用了一个context对象来传递,每个 agent 完成后把结果写进context,下一个 agent 从context里读。

6.2 前端工具调用:让 agent 操作浏览器 API

paperclip的一个亮点是支持前端工具。也就是说,agent 可以调用浏览器 API,比如localStorage、clipboard、notification。

实现方式是在前端注册工具,通过 SSE 或 WebSocket 把工具调用请求发给前端,前端执行后把结果发回后端。这个来回的延迟比后端工具高,但能实现一些后端做不到的事情,比如读取用户剪贴板、发送桌面通知。

我试过用这个能力做一个“智能表单填写”功能:agent 读取用户剪贴板里的地址信息,自动填到表单里。用户体验很惊艳,但要注意隐私问题——调用剪贴板 API 需要用户授权,而且要在 UI 上明确提示。

6.3 性能优化:从 500ms 到 50ms 的响应提升

paperclip默认的响应延迟大概在 500ms 左右,主要花在模型调用上。要降到 50ms,需要做几件事:

第一,用流式输出。不要等模型返回完整结果,而是每产生一个 token 就推给前端。这样用户感知的延迟从“完整响应时间”变成“首 token 时间”,通常能降到 200ms 以内。

第二,用缓存。对于重复的问题,缓存 agent 的响应。paperclip支持自定义缓存层,我用 Redis 做了一层缓存,命中率大概 30%,平均延迟降了 40%。

第三,用更快的模型。如果场景允许,用更小的模型(比如 4o-mini)替代大模型,延迟能降一个数量级。当然,代价是准确率会下降,需要根据场景权衡。

第四,预加载。在用户输入的时候,就提前建立 SSE 连接,这样用户点击发送时不需要再建连,省掉一个 RTT。

6.4 部署注意事项:从开发环境到生产环境

开发环境跑通不代表生产环境没问题。paperclip部署到生产环境时,有几个坑要注意:

  • 连接数限制:SSE 是长连接,每个用户占一个连接。如果用户量大,需要调整服务器的ulimit和 Nginx 的worker_connections。
  • 负载均衡:SSE 连接不能随便切换后端实例,否则会断。解决方案是用 sticky session,或者把 SSE 服务单独部署。
  • 日志和监控:agent 的每一步都要打日志,否则出问题很难排查。我用pino做结构化日志,每个事件都带traceId,方便追踪。
  • 成本控制:agent 会烧 token,尤其是死循环的时候。一定要设maxSteps和maxTokens,并且加一个全局的 token 预算,超过就熔断。

注意:生产环境一定要加限流。我见过一个案例,有人写了个脚本疯狂调 agent 接口,一晚上烧了几百美元的 token。限流不仅要按 IP 限,还要按用户限,按 token 消耗限。

7. 我个人的一些实操体会

paperclip这个项目我最欣赏的地方,是它把 agent 的“过程”变成了“产品”。过去我们做 AI 功能,用户只能看到一个结果,中间的推理过程是黑盒。而paperclip让这个过程可见、可交互、可中断,这本身就是一种体验升级。

我在实际项目里用下来,最大的感受是:agent 的可靠性比能力更重要。一个能力很强但经常出错的 agent,用户用两次就不敢用了;一个能力一般但稳定可靠的 agent,用户反而愿意长期用。所以paperclip里那些看起来不起眼的参数——maxSteps、temperature、timeout——其实比模型选型更影响最终体验。

另一个体会是,前端在 agent 时代的重要性被低估了。很多人觉得 agent 是后端的事,前端只是展示层。但实际上,agent 的流式输出、工具调用的中间状态、多轮对话的上下文管理,这些都需要前端深度参与。paperclip把 React 作为一等公民,这个定位我觉得很准。

最后分享一个小技巧:如果你在调试 agent 的时候觉得日志太乱,可以在paperclip的配置里打开verbose模式,它会把每一步的输入输出都格式化打印出来,包括模型的原始响应、工具调用的参数和结果。这个模式在排查参数错误的时候特别有用,比看原始 JSON 直观多了。

这个项目后续还可以往几个方向扩展:一是支持更多的模型 provider,现在主要是 OpenAI 兼容的接口,如果能支持本地模型会更灵活;二是增加 agent 的可视化编排,让非开发者也能拖拽配置 agent 流程;三是做 agent 的评测框架,自动跑测试用例,量化 agent 的准确率和延迟。这些方向都有价值,但也要看社区的需求和贡献。