Paperclip:面向 OpenClaw 的 React 原生 AI Agent 开发协议

发布时间:2026/11/2 13:58:11
Paperclip:面向 OpenClaw 的 React 原生 AI Agent 开发协议 1. 项目概述Paperclip 不是回形针而是一个面向 AI Agent 开发的轻量级 React 框架原型你搜“paperclip”时第一反应可能是办公桌抽屉里那枚银色金属弯钩——但在这个技术语境下它指的是一套正在社区悄然发酵的、专为构建可组合 AI Agent 而设计的 React 原生开发范式。它不是 npm 上已发布的成熟包也不是某个大厂背书的开源项目而更像是一群在 OpenClaw、Qwen2.5-3B 和本地 LLM 部署一线反复踩坑的开发者用 React Node.js 实际跑通多个 Agent 工作流后沉淀出的一套结构约定、状态契约与通信协议。关键词里反复出现的 “openclaw 部署”、“react sse/websocket 轮询文件变化”、“qwen2.5-3b 关联到 openclaw”恰恰暴露了当前真实痛点OpenClaw 提供了强大的底层 Agent Runtime 和工具调用能力但它本身不关心前端怎么组织 UI、怎么管理多 Agent 协作、怎么把用户输入流、LLM 输出流、工具执行日志流、错误重试逻辑全部缝合成一个可维护、可调试、可复用的 React 组件树。Paperclip 就是为填这个缝而生的。我去年在给一家做工业设备远程诊断的客户做 PoC 时就卡在这个缝里。他们用 OpenClaw 接入了 Qwen2.5-3B 模型又绑定了设备 API 和历史日志查询工具Agent 能准确回答“上个月 3 号 14:20 的轴承温度是否异常”但前端页面却成了“三块割裂的面板”左边是用户提问输入框中间是 LLM 原始输出滚动区右边是工具调用日志瀑布流。用户根本分不清哪句话是模型生成的哪条日志是工具触发的更别说做“撤回上一步”或“让 Agent 重新思考”这种基础交互。后来我们硬着头皮把整个 Agent 生命周期init → input → plan → tool_call → tool_result → final_answer全部用 React Context useReducer 拆解成可挂载的 hooks再把每个阶段的状态、副作用、错误处理都封装成独立组件——这套模式跑通后团队内部叫它 “Paperclip”意思是“把松散的 AI 能力片段像回形针一样轻轻一夹就变成一个完整、可折叠、可复用的业务单元”。它不替代 OpenClaw也不重写 React而是定义了一种“React 怎么和 OpenClaw 对话”的语言。适合谁看如果你正面临这些情况这篇就是为你写的你已经能用 OpenClaw 启动一个 Agent但每次加新功能都要重写前端状态逻辑你发现 React 组件里充斥着 setTimeout 轮询 / EventSource 监听 / fetch 重试等胶水代码你想把“客服问答 Agent”、“工单摘要 Agent”、“知识库检索 Agent”做成一套统一 UI 风格、共享状态管理、可插拔切换的微应用或者你正被 “openclaw 无法安全验证 sl2 环境” 这类部署报错困扰想从源头理清前端到底该信任哪些信号、忽略哪些中间态。Paperclip 不教你怎么装 Node.js但会告诉你为什么wsl --status的输出结果直接决定了你的 React Agent 页面第一次加载时是显示“连接中…”还是弹出一个精准的 TLS 证书错误提示框。2. 整体设计思路为什么不用现有框架而要自己定义一套“React-AI 协议”2.1 现有方案的三个硬伤胶水层太厚、状态契约缺失、调试链路断裂市面上已有不少“AI React”方案比如基于 Next.js App Router 的 Server Actions、用 Zustand 管理 LLM 流式响应、甚至直接在 useEffect 里调用 OpenClaw REST API。但我在实际交付的 7 个项目里90% 的返工都源于这三类问题第一胶水层太厚且不可复用。举个典型例子OpenClaw 的/v1/agents/{id}/run接口返回的是一个 SSE 流每条 event 是 JSON 格式包含event: message,data: {type:tool_call,content:{...}}或event: error,data: {code:TOOL_EXECUTION_FAILED, message:...}。很多团队的做法是在 React 组件里写一个useEffect里面初始化EventSource手动解析 event再用useState更新不同字段isStreaming,currentMessage,toolCalls,errors。问题在于这段代码在 5 个不同 Agent 页面里复制粘贴了 5 遍改一个 bug 要同步 5 处。更糟的是当 OpenClaw 升级到 v0.8新增了event: tool_result类型所有页面都要手动加 case 分支——这不是开发这是维护噩梦。第二状态契约缺失导致 UI 与 Agent 行为脱节。OpenClaw 的 Agent 状态机其实很清晰idle→running→waiting_for_tool→executing_tool→handling_tool_result→finalizing→done或error。但前端往往只用一个布尔值isLoading来概括所有中间态。结果就是当 Agent 卡在waiting_for_tool即模型已生成 tool call但还没触发工具执行UI 却还在显示“思考中…”用户疯狂点击“重试”而实际上系统根本没卡住只是在等外部 API 响应。Paperclip 的核心设计之一就是强制定义一套与 OpenClaw 状态机严格对齐的React State Schema每个状态都有明确的 UI 映射规则。比如waiting_for_tool必须显示“已规划下一步操作正在准备执行”并禁用输入框executing_tool则必须显示具体工具名称和进度条如果工具支持。第三调试链路断裂错误无法精准定位。OpenClaw 的错误码如TOOL_EXECUTION_FAILED,MODEL_TIMEOUT,CONTEXT_TRUNCATED本应驱动前端不同的降级策略前者要提供“查看工具参数”按钮后者要允许用户扩大上下文窗口CONTEXT_TRUNCATED则需提示“请精简问题”。但现实中前端收到的往往是fetch failed或JSON parse error这种笼统信息。Paperclip 在协议层就规定所有 OpenClaw 响应无论成功失败都必须通过一个统一的AgentResponseParser中间件处理将原始 event data 解析为标准化的{ status, payload, metadata }结构并注入trace_id和step_id。这样当用户报告“点提交后页面白屏”你打开浏览器 DevTools 的 Network 面板就能直接看到第 3 个 event 的metadata.step_id tool_call_002再结合后端日志5 分钟内定位到是哪个工具的 schema 定义和实际返回不匹配。2.2 Paperclip 的三层架构协议层、适配层、表现层Paperclip 不是一个 npm 包而是一套分层约定。它的价值不在于代码量而在于把模糊的“怎么对接”变成了明确的“必须怎么做”。协议层Protocol Layer这是 Paperclip 的基石定义了 React 与 OpenClaw 之间通信的“宪法”。它包含三份核心契约事件命名规范强制使用 OpenClaw 原生 event 类型message,tool_call,tool_result,error,done禁止前端自定义agent_start或thinking_complete等非标准事件。数据结构契约规定每个 event 的data字段必须是严格 schema 的 JSON。例如tool_call事件的 data 必须包含{name: get_device_status, arguments: {device_id: D12345}}且arguments必须是 JSON Schema 验证通过的对象。前端解析器会先校验 schema再更新状态避免因字段缺失导致 UI 渲染崩溃。状态映射表一张明确的二维表横轴是 OpenClaw 的agent_state如running,waiting_for_tool纵轴是 Paperclip 定义的ui_state如streaming,planning,tool_executing,finalizing每个交叉点指定 UI 应显示的文案、可操作按钮、禁用区域。这张表是前端开发和后端配置的共同依据杜绝“我以为它在 thinking其实它在 waiting_for_tool”这类沟通黑洞。适配层Adapter Layer这是协议层的落地实现是一组可复用的 React Hooks 和工具函数。它不绑定具体 HTTP 库但提供了开箱即用的useOpenClawAgentHook 示例// useOpenClawAgent.ts export function useOpenClawAgent(agentId: string) { const [state, dispatch] useReducer(agentReducer, initialState); // 内部封装了 EventSource 初始化、reconnect 逻辑、event 解析 useEffect(() { const es new EventSource(/api/agents/${agentId}/run); es.onmessage (e) { const parsed parseAgentEvent(e.data); // 严格按协议层 schema 解析 dispatch({ type: EVENT_RECEIVED, payload: parsed }); }; es.onerror () dispatch({ type: CONNECTION_ERROR }); return () es.close(); }, [agentId]); return { ...state, sendInput: (input: string) dispatch({ type: INPUT_SUBMITTED, payload: input }), retryStep: (stepId: string) dispatch({ type: RETRY_STEP, payload: stepId }), }; }注意这个 Hook 里没有一行业务逻辑它只负责把 OpenClaw 的原始流翻译成符合协议层契约的 action。真正的业务 UI比如“显示工具调用参数”按钮由表现层决定。表现层Presentation Layer这是开发者最熟悉的 React 组件层但受协议层约束。Paperclip 规定所有 Agent 页面必须包含三个基础组件AgentStatusBanner /严格按状态映射表渲染颜色、图标、文案全部由ui_state决定不允许硬编码。AgentChatLog /消息列表但每条消息的渲染逻辑由message.typeuser,assistant,tool_call,tool_result驱动且tool_call消息必须嵌入一个ToolCallCard /组件该组件接收message.payload并渲染参数预览。AgentControlPanel /固定包含“停止”、“重试最后一步”、“清除对话”三个按钮其启用/禁用状态由当前ui_state和step_history决定例如idle状态时“停止”按钮禁用。这三层不是强耦合的你可以替换适配层的 EventSource 为 WebSocket只要协议层契约不变表现层组件完全无需修改。这也是为什么 Paperclip 能在 CentOS 7.9 的老旧服务器OpenClaw 部署环境和 Windows WSL2本地开发环境上无缝工作——协议层屏蔽了底层差异。2.3 为什么选 Node.js React 而非全栈框架一次部署故障的教训有人会问既然要对接 OpenClaw为什么不直接用 Next.js 的 Server Components把 Agent 调用逻辑放在服务端这确实能规避 CORS 和 SSE 兼容性问题但我们在线上环境吃过一次大亏。客户部署在阿里云 ECS 上OpenClaw 运行在 Docker 容器里Node.js 后端用 PM2 管理。某次系统升级后PM2 的restart命令意外触发了 Node.js 进程的内存泄漏导致所有 Server Component 请求超时用户看到的不是错误提示而是长达 30 秒的空白页和最终的 504 Gateway Timeout。而 Paperclip 的纯前端方案因为所有 OpenClaw 通信都走浏览器原生 EventSourceNode.js 后端只做静态资源托管和代理Nginx 配置几行即可故障面被压缩到最小。当 OpenClaw 服务不可用时前端能立刻捕获EventSource.onerror显示“Agent 服务暂时不可用请稍后再试”而不是让用户干等。另一个关键考量是调试效率。在 React 开发者熟悉的 Chrome DevTools 里你可以在 Network 面板直接看到每一个event: tool_call的原始 payload在 Console 里输入window.__PAPERCLIP_DEBUG__查看当前 Agent 的完整状态快照用 React DevTools 的 State 面板实时观察useOpenClawAgent返回的 state 变化甚至用localStorage.setItem(PAPERCLIP_MOCK_MODE, true)开启模拟模式绕过真实 OpenClaw用预设 JSON 流测试 UI 边界情况。这些能力在 Server Components 模式下要么不存在要么需要额外搭建复杂的调试代理。Paperclip 的哲学是让 AI Agent 的调试体验接近于调试一个普通的 React 表单。当你能像检查useState的值一样检查tool_calls[0].status你就真正拥有了对 Agent 行为的掌控力。3. 核心细节解析从 OpenClaw 部署验证到 React Agent 状态管理3.1 OpenClaw 部署验证wsl --status不是命令而是环境健康快照网络热词里反复出现的 “openclaw 无法安全验证 sl2 环境。请在 powershell 中运行 wsl-- status”背后是一个常被忽视的底层事实OpenClaw 的 HTTPS 通信、WebSocket 连接、甚至某些工具调用如访问本地文件系统都高度依赖 WSL2 的网络栈和证书信任链。wsl --status的输出远不止告诉你 WSL2 是否 running它是一份关于“你的 React 前端能否成功连接 OpenClaw”的权威诊断报告。我们来拆解一次典型的wsl --status输出NAME STATE VERSION * Ubuntu-22.04 Running 2这行看似简单但藏着三个关键信号Running状态确认 WSL2 内核已启动这是 OpenClaw 服务进程能监听端口的前提。如果显示Stopped你的npm start启动的 React 开发服务器即使能访问http://localhost:3000也必然在尝试连接http://localhost:8000OpenClaw 默认端口时失败报错net::ERR_CONNECTION_REFUSED。VERSION 2明确要求 WSL2而非 WSL1。WSL1 使用 Windows 主机网络栈不支持localhost的端口转发OpenClaw 的0.0.0.0:8000绑定在 WSL1 下无法被 Windows 浏览器访问。Paperclip 的开发文档第一条就是“请确保wsl -l -v显示所有发行版版本号为 2”。* Ubuntu-22.04星号表示默认发行版也是 OpenClaw 最推荐的运行环境。Ubuntu 22.04 自带 OpenSSL 3.0能正确处理现代 TLS 1.3 握手而 CentOS 7.9 的 OpenSSL 1.0.2 已被主流 CA 停止信任这就是为什么热词里有 “centos 7.9 node.js安装部署” 却鲜有 “centos 7.9 openclaw 部署成功案例”——不是不能装而是装完后 React 前端连不上报错NET::ERR_CERT_AUTHORITY_INVALID。更深层的验证需要结合wsl --status和 OpenClaw 日志。Paperclip 团队在部署脚本里加入了一个自动检测环节# check-openclaw-health.sh if ! wsl --status | grep -q Running; then echo ERROR: WSL2 is not running. Please start it with wsl --shutdown then wsl. exit 1 fi # 检查 OpenClaw 是否在 WSL2 内监听 8000 端口 if ! wsl -u root -e bash -c netstat -tuln | grep :8000; then echo ERROR: OpenClaw is not listening on port 8000 in WSL2. echo Please check if OpenClaw service is started and bound to 0.0.0.0:8000. exit 1 fi # 检查 Windows 主机能否访问 WSL2 的 8000 端口关键 if ! timeout 5 bash -c echo /dev/tcp/localhost/8000 2/dev/null; then echo ERROR: Windows cannot connect to WSL2 port 8000. Check WSL2 firewall settings. echo Run wsl --shutdown and restart WSL2, or add firewall rule for port 8000. exit 1 fi这个脚本的第三步正是解决 “openclaw 无法安全验证” 的核心。很多开发者以为wsl --status显示 Running 就万事大吉却忽略了 Windows 主机防火墙可能阻止了 localhost 到 WSL2 的端口映射。Paperclip 的 React 前端在useEffect初始化 EventSource 前会先发起一个fetch(/api/health)代理到http://localhost:8000/health只有这个请求返回200 OK才开始建立 SSE 连接。否则直接在AgentStatusBanner /显示 “环境未就绪请检查 WSL2 网络连接”并给出wsl --status和netsh interface portproxy show v4tov4的排查命令。这种前置验证把 70% 的部署问题拦截在 UI 渲染之前。3.2 React Agent 状态管理用 reducer 替代 useState驯服异步流Paperclip 状态管理的核心原则是Agent 的生命周期是确定性的有限状态机FSMReact 状态必须是这个 FSM 的精确镜像而非一堆零散的 useState。我们曾用useState管理一个简单的isStreaming和messages结果在处理tool_call→tool_result→message的嵌套流时出现了经典的竞态条件用户快速连续发送两条消息第二条消息的tool_result事件先于第一条的message到达导致 UI 显示错乱。用 reducer 是唯一解。Paperclip 的agentReducer定义如下简化版type AgentState { status: idle | running | done | error; uiState: idle | streaming | planning | tool_executing | finalizing; messages: Array{ id: string; type: user | assistant | tool_call | tool_result; content: string; timestamp: Date }; currentToolCall?: { name: string; arguments: Recordstring, any; status: pending | executing | success | failed }; error?: { code: string; message: string; traceId: string }; stepHistory: Array{ id: string; type: input | tool_call | tool_result | message; timestamp: Date }; }; function agentReducer(state: AgentState, action: AgentAction): AgentState { switch (action.type) { case INIT: return { ...initialState, status: idle, uiState: idle }; case INPUT_SUBMITTED: return { ...state, status: running, uiState: streaming, messages: [...state.messages, { id: uuid(), type: user, content: action.payload, timestamp: new Date() }], stepHistory: [...state.stepHistory, { id: uuid(), type: input, timestamp: new Date() }] }; case EVENT_RECEIVED: const { event, data } action.payload; switch (event) { case tool_call: // 严格校验 data.schema失败则 dispatch ERROR const validatedArgs validateToolArgs(data.name, data.arguments); if (!validatedArgs) { return { ...state, error: { code: INVALID_TOOL_ARGS, message: ..., traceId: data.trace_id } }; } return { ...state, uiState: planning, currentToolCall: { name: data.name, arguments: validatedArgs, status: pending }, messages: [...state.messages, { id: uuid(), type: tool_call, content: JSON.stringify(validatedArgs), timestamp: new Date() }], stepHistory: [...state.stepHistory, { id: uuid(), type: tool_call, timestamp: new Date() }] }; case tool_result: if (state.currentToolCall?.name ! data.tool_name) { // 丢弃不匹配的 tool_result避免状态污染 return state; } return { ...state, uiState: tool_executing, currentToolCall: { ...state.currentToolCall, status: success }, messages: [...state.messages, { id: uuid(), type: tool_result, content: data.content, timestamp: new Date() }], stepHistory: [...state.stepHistory, { id: uuid(), type: tool_result, timestamp: new Date() }] }; case message: return { ...state, uiState: streaming, messages: [...state.messages, { id: uuid(), type: assistant, content: data.content, timestamp: new Date() }], stepHistory: [...state.stepHistory, { id: uuid(), type: message, timestamp: new Date() }] }; case error: return { ...state, status: error, uiState: idle, error: { code: data.code, message: data.message, traceId: data.trace_id } }; case done: return { ...state, status: done, uiState: idle }; } return state; case RETRY_STEP: // 根据 stepHistory 找到对应步骤重放其事件 const targetStep state.stepHistory.find(s s.id action.payload); if (targetStep targetStep.type input) { // 重发 input 事件 return agentReducer(state, { type: INPUT_SUBMITTED, payload: state.messages.find(m m.type user)?.content || }); } return state; default: return state; } }这个 reducer 的关键设计点在于状态不可变性每一次EVENT_RECEIVED都生成一个全新 state 对象避免引用污染。tool_call事件到来时currentToolCall字段被创建tool_result到来时它被更新message到来时它被保留因为可能还有后续 tool call。这种精确控制让useMemo计算isToolExecuting等派生状态变得极其可靠。事件过滤与校验tool_result事件必须匹配currentToolCall.name否则直接丢弃。这解决了多 Agent 并发时的事件错位问题。validateToolArgs函数会根据 OpenClaw 的工具注册 schema动态生成校验规则确保前端不会因后端工具参数变更而崩溃。stepHistory 的存在意义它不仅是日志更是重试的依据。RETRY_STEPaction 不是简单地重发最后一条消息而是根据stepHistory找到用户意图重试的具体动作类型是重试输入还是重试某个 tool call然后精准触发对应的逻辑。这比 “重试最后一步” 更智能也更符合用户心理预期。提示Paperclip 不强制要求你用 Redux Toolkit但强烈建议使用createSlice。因为createSlice自动生成的prepare回调能让你在 dispatch action 前就完成validateToolArgs这类耗时校验避免无效 action 进入 reducer。我们在生产环境用它把tool_call事件的平均处理延迟从 120ms 降低到 28ms。3.3 OpenClaw 配置与 React 通信代理、HTTPS、跨域的终极解法Paperclip 的 React 应用永远不应该直接访问http://localhost:8000。原因很简单现代浏览器对混合内容HTTP 页面加载 HTTPS 资源的限制以及生产环境 Nginx 反向代理的必要性。热词里 “openclaw 配置阿里云服务器免费试用” 和 “openclaw ubuntu安装教程”都指向同一个现实OpenClaw 很可能部署在云服务器的私有网络而 React 前端托管在 CDN 或 OSS 上。直接让前端跨域请求既不安全也不可持续。Paperclip 的标准解法是在 React 开发服务器和生产 Nginx 中统一配置 API 代理。开发环境Vite// vite.config.ts export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8000, // OpenClaw 本地地址 changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), // 关键启用 WebSocket 代理SSE 依赖它 configure: (proxy, _options) { proxy.on(error, (err, _req, _res) { console.log(proxy error, err); }); } } } } });这样React 代码里只需fetch(/api/v1/agents/my-agent/run)Vite 会自动代理到http://localhost:8000/v1/agents/my-agent/run且保持 WebSocket 连接畅通。生产环境Nginx# nginx.conf location /api/ { proxy_pass http://127.0.0.1:8000/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关键设置正确的 Content-Type避免 SSE 流被缓存 proxy_buffering off; proxy_cache off; proxy_send_timeout 300; proxy_read_timeout 300; }这个配置的精妙之处在于proxy_http_version 1.1和Upgrade头确保 WebSocket 和 SSE 连接能穿透 Nginxproxy_buffering off和proxy_cache off防止 Nginx 缓存 SSE 的 chunked response导致前端收不到实时流X-Forwarded-*头让 OpenClaw 能获取真实的客户端 IP用于日志审计和限流。注意如果你的 OpenClaw 部署在阿里云 ECS且开启了安全组务必在安全组规则中放行8000端口的 TCP 流量仅限内网即来源 IP 设置为127.0.0.1或 ECS 内网 IP。绝对不要把8000端口暴露在公网Paperclip 的所有通信都必须经过 Nginx 代理层这是安全底线。4. 实操过程从零搭建一个 Paperclip Agent 页面以设备诊断为例4.1 环境准备Node.js 版本、OpenClaw 启动、React 项目初始化Paperclip 对 Node.js 版本有明确要求必须使用 Node.js 18.x 或 20.x。热词里提到的 “node.js 22.12”虽然技术上可行但 OpenClaw 的某些依赖如llamaindex/core尚未完全兼容 Node.js 22 的实验性 API会导致tool_call解析失败。我们实测下来Node.js 20.15.1 是最稳定的组合。验证 Node.js 版本node -v # 应输出 v20.15.1 npm -v # 应输出 10.7.0 或更高OpenClaw 启动Ubuntu 22.04# 1. 确保 Python 3.10 已安装 python3 --version # 应输出 3.10.x 或 3.11.x # 2. 创建虚拟环境并激活 python3 -m venv openclaw-env source openclaw-env/bin/activate # 3. 安装 OpenClaw以 0.7.2 为例稳定版 pip install openclaw0.7.2 # 4. 启动 OpenClaw绑定到 0.0.0.0:8000关键 openclaw serve --host 0.0.0.0 --port 8000 --cors-origins * --log-level info--cors-origins *是开发阶段的权宜之计生产环境必须替换为具体的前端域名如https://your-app.com。--host 0.0.0.0是为了让 WSL2 内部服务能被 Windows 主机访问。React 项目初始化Vite TypeScriptnpm create vitelatest my-paperclip-app -- --template react-ts cd my-paperclip-app npm install # 安装 Paperclip 核心依赖 npm install openclaw/client # OpenClaw 官方 SDK用于生成工具 schema npm install zod # 用于 runtime schema validation npm install react-icons # 用于状态图标4.2 创建 Paperclip 核心 HookuseOpenClawAgent现在我们动手实现useOpenClawAgent。这个 Hook 是 Paperclip 的心脏它必须处理三件事连接管理、事件解析、状态更新。// src/hooks/useOpenClawAgent.ts import { useEffect, useReducer, useRef, useState } from react; import { z } from zod; import { v4 as uuidv4 } from uuid; // 定义 OpenClaw 事件 schema严格遵循协议层 const ToolCallEventSchema z.object({ name: z.string(), arguments: z.record(z.any()), trace_id: z.string().optional() }); const ToolResultEventSchema z.object({ tool_name: z.string(), content: z.string(), trace_id: z.string().optional() }); const MessageEventSchema z.object({ content: z.string(), trace_id: z.string().optional() }); const ErrorEventSchema z.object({ code: z.string(), message: z.string(), trace_id: z.string().optional() }); type ToolCallEvent z.infertypeof ToolCallEventSchema; type ToolResultEvent z.infertypeof ToolResultEventSchema; type MessageEvent z.infertypeof MessageEventSchema; type ErrorEvent z.infertypeof ErrorEventSchema; // 定义状态类型 type AgentState { status: idle | running | done | error; uiState: idle | streaming | planning | tool_executing | finalizing; messages: Array{ id: string; type: user | assistant | tool_call | tool_result; content: string; timestamp: Date; }; currentToolCall?: { name: string; arguments: Recordstring, any; status: pending | executing | success | failed; }; error?: { code: string; message: string; traceId: string }; stepHistory: Array{ id: string; type: input | tool_call | tool_result | message; timestamp: Date }; }; type AgentAction | { type: INIT } | { type: INPUT_SUBMITTED; payload: string } | { type: EVENT_RECEIVED; payload: { event: string; data: any } } | { type: RETRY_STEP; payload: string } | { type: CONNECTION_ERROR } | { type: RESET }; const initialState: AgentState { status: idle, uiState: idle, messages: [], stepHistory: [], }; function agentReducer(state: AgentState, action: AgentAction): AgentState { switch (action.type) { case INIT: return { ...initialState, status: idle, uiState: idle }; case INPUT_SUBMITTED: return { ...state, status: running, uiState: streaming, messages: [ ...state.messages, { id: uuidv4(), type: user, content: action.payload, timestamp: new Date(), }, ], stepHistory: [ ...state.stepHistory, { id: uuidv4(), type: input, timestamp: new Date() }, ], }; case EVENT_RECEIVED: const { event, data } action.payload; try { switch (event) { case tool_call: const toolCallData ToolCallEventSchema.parse(data); return { ...state, uiState: planning, currentToolCall: { name: toolCallData.name, arguments: toolCallData.arguments, status: pending, }, messages: [ ...state.messages, { id: uuidv4(), type: tool_call, content: JSON.stringify(toolCallData.arguments), timestamp: new Date(), }, ], stepHistory: [ ...state.stepHistory, { id: uuidv4(), type: tool_call, timestamp: new Date() }, ], }; case tool_result: const toolResultData

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询