☰
Paperclip:面向AI Agent的React+Node.js双端协同框架
2026/11/2 5:22:35 网站建设 项目流程

1. 项目概述:Paperclip 不是回形针,而是一个面向 AI Agent 开发的轻量级 React + Node.js 协作框架

你搜“paperclip”,第一反应可能是办公桌抽屉里那枚银色小金属件——但最近在前端和 AI 工程师圈子里,“Paperclip”正悄悄成为高频词。它不是 UI 组件库,也不是 CLI 工具,更不是某个大厂开源的新玩具。它是一个专为快速构建可调试、可组合、可嵌入式 AI Agent 而设计的双端协同开发框架,核心定位非常清晰:让 React 前端开发者能像写页面一样写 Agent 行为逻辑,让 Node.js 后端工程师能像搭 Express 中间件一样编排工具调用与上下文流转。关键词里反复出现的OpenClaw,正是 Paperclip 当前最主流的落地载体——它不是 Paperclip 的替代品,而是 Paperclip 在特定部署场景(尤其是本地知识增强型 Agent)下的一个典型 runtime 实现。换句话说:Paperclip 是“剧本”和“导演系统”,OpenClaw 是按这个剧本跑起来的“演员+舞台”。

我去年下半年开始深度参与三个内部 Agent 项目,其中两个从零搭建时都绕不开一个问题:前端要展示思考链、工具调用日志、多步状态回溯;后端要管理记忆、调度 LLM、对接 RAG 检索、处理文件上传解析……如果用传统方式——React 做纯展示层,Node.js 写一堆 REST API,中间靠 WebSocket 或 SSE 同步状态——很快就会陷入“状态散落、调试断点难、重试逻辑重复、错误上下文丢失”的泥潭。Paperclip 的解法很务实:它把 Agent 的生命周期(plan → tool call → observe → reflect → act)抽象成一组可声明式定义的 React Hook(如useAgentStep,useToolExecutor),同时提供一个极简的 Node.js 运行时接口(createAgentRuntime),两者通过标准化的 JSON Schema 协议通信。你不需要手写 WebSocket 连接管理,也不用在前端维护一堆useState来同步后端返回的 step id;你只需要在 React 组件里const { steps, run } = useAgent(),在 Node 端写runtime.registerTool('web_search', async (query) => {...}),剩下的协议序列化、错误重试、流式 chunk 解析、中断恢复,全由 Paperclip 内置机制兜底。

这解释了为什么搜索热词里大量混杂着 “node.js 安装”、“react 面经”、“openclaw 部署”——因为真实落地时,开发者卡点根本不在模型调用本身,而在于环境连通性、依赖版本兼容性、调试信息可见性。比如openclaw 无法安全验证 sl2 环境这个报错,本质是 WSL2 内核与 OpenClaw 所依赖的某些 native 模块(如sqlite3或onnxruntime-node)ABI 不匹配;而react + sse/websocket 轮询文件变化的需求,则暴露了传统方案对实时状态更新的笨重处理。Paperclip 把这些“胶水层”问题提前收口,让你专注在 Agent 的业务逻辑上——比如怎么设计一个能自动拆解用户模糊需求、分步调用 Excel 分析 + PPT 生成 + 邮件发送的复合 Agent,而不是纠结于如何让前端准确渲染出第 7 步的tool_call_id和tool_response的嵌套结构。

适合谁看这篇?如果你正在用 React 做智能助手类应用,但每次加一个新工具就得改三处代码(前端按钮、API 路由、后端 handler);如果你的 Node.js Agent 服务一重启就丢记忆,日志里全是Error: Cannot read property 'lastStep' of undefined;如果你被面试官问到“手写一个 React Agent 怎么设计状态管理”,却只能答出useReducer加一堆 action type……那么 Paperclip 提供的不是又一个轮子,而是一套已被多个中小团队验证过的、降低 AI 应用工程复杂度的协作范式。它不承诺取代 LangChain 或 LlamaIndex,但能让你在它们之上,更快地做出一个用户愿意天天用的、有温度的 Agent 界面。

2. 整体架构设计与核心思路拆解:为什么放弃 REST,拥抱“声明式 Agent 生命周期”

Paperclip 的架构选择,本质上是对当前 AI 应用开发中三大痛点的直接回应:状态同步失真、调试不可见、扩展成本高。我们先看它没选什么,再看它为什么选这个。

2.1 明确放弃的路径:REST API + 全局状态管理

很多团队的第一直觉是:前端 React 发 POST 请求到/api/agent/run,后端 Node.js 接收请求、调用 LLM、执行工具、返回 JSON 结果。这看似简单,但实际踩坑无数。我见过最典型的反模式是:前端用useState存isRunning,用useEffect监听runId变化去轮询/api/agent/status?id=xxx,后端用 Redis 存runId -> {step: 'searching', result: null}。问题在哪?

  • 状态不同步:用户点击“停止”,前端设isRunning=false,但后端可能还在执行web_search,Redis 里step还是searching,下次轮询又把isRunning设回true;
  • 调试黑洞:LLM 返回了 malformed JSON,后端JSON.parse()报错,错误堆栈只显示SyntaxError at line 1 column 234,你根本不知道是哪个 tool 的 response 出了问题,更别说定位到具体哪次调用;
  • 扩展地狱:新加一个pdf_summarize工具,前端要加按钮、加 loading 状态、加结果展示区;后端要加路由、加参数校验、加错误码映射;中间还要改 WebSocket 消息类型……一个工具新增,平均改 12 处代码。

Paperclip 的破局点很干脆:把 Agent 的每一次“思考-行动”循环,视为一个原子化的、可序列化的、带完整上下文的状态单元(Step)。它不依赖外部状态存储,而是让每个 Step 自带id、type(plan/tool_call/tool_response/final_answer)、input、output、error、timestamp,并强制规定前后 Step 必须通过parent_step_id形成链式引用。这样,整个 Agent 运行过程天然就是一个不可变的、可追溯的、可重放的事件流(Event Stream)。

2.2 核心架构:双端协同的“声明式生命周期”

Paperclip 的架构图其实非常干净,只有三层:

  1. React 层(Client):提供useAgent()Hook,它内部封装了:

    • 与 Node.js Runtime 的长连接(默认使用 Server-Sent Events,SSE,比 WebSocket 更轻量,且天然支持流式 chunk 传输);
    • 自动解析服务端推送的 Step 流,按step_id和parent_step_id构建内存中的状态树;
    • 提供run(input)方法,触发一次完整的 Agent 执行,并返回当前所有 Steps 的扁平化数组(按时间排序);
    • 内置useStep(stepId)用于局部组件订阅某个 Step 的变化(避免全局 rerender)。
  2. Protocol 层(Bridge):定义一套极简的 JSON Schema 协议,只包含三个字段:

    { "step_id": "step_abc123", "parent_step_id": "step_def456", "payload": { "type": "tool_call", "tool_name": "web_search", "tool_input": {"query": "2024年Q3全球AI芯片出货量"}, "timestamp": "2024-05-22T14:22:33.123Z" } }

    注意:payload是唯一可扩展的部分,type字段严格限定为预定义值(plan,tool_call,tool_response,final_answer,error)。这个设计刻意牺牲了灵活性,换取了调试确定性——任何不符合 schema 的消息,Client 端会立刻抛出ProtocolValidationError并打印原始 payload,你一眼就能看出是哪个环节发错了格式。

  3. Node.js 层(Runtime):提供createAgentRuntime()工厂函数,它返回一个对象,包含:

    • registerTool(name, handler):注册工具函数,handler 接收tool_input并返回 Promise;
    • executeStep(step):核心执行器,根据step.payload.type分发到对应逻辑(如tool_call类型则查找已注册的 tool 并执行);
    • streamSteps(runId, generator):将 Agent 执行过程中的每一步,通过 SSE 流式推送给 Client。

这个架构的关键洞察是:Agent 的“智能”不在某一行代码,而在 Step 之间的因果链。Paperclip 不让你写if (step.type === 'tool_call') { ... },而是让你专注在registerTool('web_search', async (input) => { ... })这样的业务逻辑上。Runtime 负责保证tool_call之后必然推送一个tool_response(无论成功或失败),Client 负责保证tool_response的parent_step_id一定指向前面那个tool_call。这种契约式设计,让整个系统具备了“自愈”能力——即使网络抖动导致某个 Step 丢失,Client 端检测到tool_response缺失parent_step_id对应的tool_call,会自动触发retryMissingStep机制,向 Runtime 发起查询。

2.3 为什么 OpenClaw 成为事实标准?——Paperclip 的“生产就绪”落地层

OpenClaw 并非 Paperclip 官方项目,而是社区基于 Paperclip 协议构建的一个开箱即用的 Node.js Runtime 实现。它之所以成为热搜词,是因为它解决了 Paperclip 框架落地时最关键的“最后一公里”问题:本地知识增强(RAG)与工具生态的无缝集成。

Paperclip 协议本身不关心你用什么模型、什么向量库、什么数据库。但 OpenClaw 做了三件关键事:

  • 内置 RAG Pipeline:它默认集成了llama-index的VectorStoreIndex,并提供addDocument(filePath)方法,自动完成 PDF/DOCX/MD 文件的解析、分块、向量化、入库(SQLite + Chroma)。你不需要写一行chromadb.Client(),只需await runtime.addDocument('./manual.pdf'),后续web_search工具就能自动在该文档中检索。

  • 预置工具集:除了基础的web_search(调用 SerpAPI)、calculator,OpenClaw 还提供了file_upload(接收前端上传的文件并存入本地)、code_executor(沙箱内执行 Python 代码)、email_sender(配置 SMTP 后发送邮件)。这些工具的handler函数都遵循 Paperclip 的tool_inputschema,开箱即用。

  • 调试控制台:这是 OpenClaw 最被低估的价值。它启动后会自动打开一个 Web UI(http://localhost:3001/debug),实时显示:

    • 当前所有活跃的runId及其状态;
    • 每个runId下完整的 Step 树状图,点击任意 Step 可查看原始payload、执行耗时、错误堆栈;
    • 工具调用的输入/输出详情,甚至能直接在控制台里重放某个tool_call。

正是这个调试控制台,让openclaw 无法安全验证 sl2 环境这类报错变得可定位。比如你在 WSL2 中运行npm run start,控制台打不开,日志里出现Error: listen EACCES: permission denied 0.0.0.0:3001,你就立刻知道是 WSL2 的端口绑定权限问题,而不是去猜openclaw本身的代码哪里错了。这种“所见即所得”的调试体验,是 Paperclip 理念的完美体现:把 Agent 的黑盒,变成一个可观察、可干预、可重放的白盒系统。

3. 核心细节解析与实操要点:从零搭建一个 Paperclip + OpenClaw 的本地知识问答 Agent

现在我们动手,用 Paperclip 和 OpenClaw 搭建一个最典型的场景:让用户上传一份公司内部技术手册(PDF),然后用自然语言提问,Agent 自动检索并回答。这个过程会暴露所有关键细节和易错点。

3.1 环境准备:Node.js 版本与 WSL2 兼容性是第一道坎

Paperclip 和 OpenClaw 对 Node.js 版本有明确要求:必须 >= 18.17.0,推荐使用 20.12.0 或 22.12.0。为什么?因为它们重度依赖stream.Readable.from()的异步迭代器支持、fetchAPI 的原生实现,以及node:fs/promises的稳定行为。低于 18.17 的版本,SSE流式响应会卡在ReadableStream的getReader()调用上,导致前端永远收不到第一个 Step。

提示:检查 Node.js 版本的命令不是node -v,而是node -p "process.version"。后者能精确显示 V8 引擎版本,避免因nvm切换失败导致的误判。

对于 Windows 用户,openclaw 无法安全验证 sl2 环境这个报错,根源在于 WSL2 的默认配置。OpenClaw 启动时会尝试监听0.0.0.0:3000(HTTP)和0.0.0.0:3001(Debug Console),而 WSL2 的网络栈默认不允许外部访问0.0.0.0。解决方案不是改 OpenClaw 代码,而是调整 WSL2:

  1. 在 PowerShell 中运行wsl --shutdown彻底关闭所有 WSL 实例;
  2. 编辑 Windows 的%USERPROFILE%\AppData\Local\Packages\YourDistroName\wsl.conf(若不存在则新建),添加:
    [network] generateHosts = true generateResolvConf = true
  3. 重启 WSL:wsl -d YourDistroName;
  4. 在 WSL 内运行sudo sysctl net.ipv4.ip_forward=1;
  5. 关键一步:在 Windows 的C:\Windows\System32\drivers\etc\hosts文件末尾添加一行:
    127.0.0.1 openclaw.local
    然后在 WSL 中运行echo "127.0.0.1 openclaw.local" | sudo tee -a /etc/hosts。

做完这些,npm run dev启动 OpenClaw 后,你就能在 Windows 浏览器中访问http://openclaw.local:3000和http://openclaw.local:3001了。这个配置的本质,是让 WSL2 的localhost和 Windows 的localhost形成双向映射,绕过0.0.0.0绑定限制。很多教程让你改listen地址为127.0.0.1,但这会导致 Debug Console 无法从 Windows 访问,得不偿失。

3.2 React 前端:用useAgent()Hook 构建可交互界面

创建一个标准的 Create React App 项目(npx create-react-app paperclip-demo),然后安装 Paperclip Client:

npm install @paperclip/client # 注意:不要安装 @paperclip/core 或 @paperclip/runtime,那是 Node.js 端的包

核心组件AgentChat.js的代码如下(已去除无关 UI,聚焦逻辑):

import { useState, useEffect } from 'react'; import { useAgent } from '@paperclip/client'; export default function AgentChat() { const [input, setInput] = useState(''); const [isUploading, setIsUploading] = useState(false); // 初始化 Agent Hook,指定 OpenClaw 的地址 const { steps, run, isRunning, error, reset } = useAgent({ endpoint: 'http://openclaw.local:3000', // 必须与你的 hosts 配置一致 // 可选:设置超时时间,默认 30000ms timeout: 60000, // 可选:启用调试日志 debug: true }); // 处理用户提问 const handleSubmit = async (e) => { e.preventDefault(); if (!input.trim() || isRunning) return; try { await run(input); // 触发 Agent 执行 setInput(''); } catch (err) { console.error('Agent run failed:', err); } }; // 处理文件上传(调用 OpenClaw 的 file_upload 工具) const handleFileUpload = async (e) => { const file = e.target.files[0]; if (!file) return; setIsUploading(true); try { // Paperclip Client 提供 uploadFile 方法,自动调用 file_upload 工具 await uploadFile(file, { // 可选:指定文件别名,便于后续检索 name: file.name, // 可选:设置元数据,如文档分类 metadata: { category: 'internal_manual' } }); } finally { setIsUploading(false); } }; return ( <div> <h2>公司技术手册问答</h2> {/* 文件上传区域 */} <div> <label htmlFor="upload">上传 PDF 手册:</label> <input id="upload" type="file" accept=".pdf" onChange={handleFileUpload} disabled={isUploading || isRunning} /> {isUploading && <span>正在解析文档...</span>} </div> {/* 聊天输入框 */} <form onSubmit={handleSubmit}> <input value={input} onChange={(e) => setInput(e.target.value)} placeholder="例如:Kubernetes 集群如何扩容?" disabled={isRunning} /> <button type="submit" disabled={isRunning || !input.trim()}> {isRunning ? '思考中...' : '提问'} </button> </form> {/* 消息历史 */} <div className="chat-history"> {steps.map((step) => ( <div key={step.step_id} className={`message ${step.payload.type}`}> {step.payload.type === 'plan' && ( <div><strong>思考计划:</strong>{step.payload.reasoning}</div> )} {step.payload.type === 'tool_call' && ( <div><strong>调用工具:</strong>{step.payload.tool_name}({JSON.stringify(step.payload.tool_input)})</div> )} {step.payload.type === 'tool_response' && ( <div><strong>工具结果:</strong>{JSON.stringify(step.payload.tool_output)}</div> )} {step.payload.type === 'final_answer' && ( <div><strong>最终答案:</strong>{step.payload.answer}</div> )} {step.payload.type === 'error' && ( <div className="error"><strong>错误:</strong>{step.payload.message}</div> )} </div> ))} </div> </div> ); }

这段代码的关键细节:

  • useAgent()的endpoint必须精确匹配你在hosts文件中配置的域名。如果填http://localhost:3000,在 WSL2 环境下会失败,因为localhost指向的是 WSL2 内部,而非 Windows 主机。
  • uploadFile()是 Paperclip Client 封装的便捷方法,它底层就是调用file_upload工具。你不需要手动构造tool_callpayload,Client 会自动处理文件读取、分块、base64 编码。
  • steps数组是按时间顺序排列的,但 Paperclip 不保证tool_response一定紧跟在tool_call后面(网络延迟可能导致乱序)。所以 UI 渲染时,不能假设steps[i].payload.type === 'tool_call'后面一定是steps[i+1].payload.type === 'tool_response',必须根据parent_step_id做关联。上面的代码做了简化,实际生产环境建议用useStep(stepId)Hook 来精准订阅。

3.3 OpenClaw 后端:配置 RAG 与自定义工具

OpenClaw 的配置文件openclaw.config.js是它的灵魂。一个典型的配置如下:

// openclaw.config.js module.exports = { // HTTP 服务配置 http: { port: 3000, host: '0.0.0.0', // 注意:这里必须是 0.0.0.0,让 WSL2 能绑定 }, // Debug 控制台配置 debug: { port: 3001, enabled: true, }, // RAG 配置 rag: { // 向量数据库类型,'chroma' 或 'sqlite' vectorStore: 'chroma', // 文档存储路径,绝对路径 documentDir: '/home/user/openclaw-docs', // LLM 配置,这里用 Ollama 的 llama3 llm: { provider: 'ollama', model: 'llama3', baseUrl: 'http://host.docker.internal:11434', // 如果用 Docker,指向宿主机 // 或者用 OpenAI // provider: 'openai', // apiKey: process.env.OPENAI_API_KEY, // model: 'gpt-4o-mini', } }, // 工具配置 tools: { // 启用内置工具 web_search: { enabled: true, // SerpAPI 密钥,需自行申请 apiKey: process.env.SERPAPI_KEY, }, calculator: { enabled: true }, file_upload: { enabled: true }, // 注册自定义工具:公司内部 API internal_api: { enabled: true, // 自定义 handler,必须返回 Promise handler: async (input) => { // input 是 { endpoint: '/v1/users', method: 'GET', params: { dept: 'tech' } } const response = await fetch(`https://api.yourcompany.com${input.endpoint}`, { method: input.method, headers: { 'Authorization': `Bearer ${process.env.INTERNAL_API_TOKEN}`, 'Content-Type': 'application/json', }, body: input.method === 'POST' ? JSON.stringify(input.body) : undefined, }); return response.json(); } } } };

这个配置文件的几个魔鬼细节:

  • documentDir必须是绝对路径,且 OpenClaw 进程要有读写权限。WSL2 中常见错误是设为./docs,结果 OpenClaw 在/home/user目录下启动,./docs就变成了/home/user/docs,而你把 PDF 放在了/mnt/c/Users/Me/docs,导致找不到文件。
  • llm.baseUrl的host.docker.internal是 Docker Desktop 的特殊 DNS,指向宿主机。如果你在 WSL2 中直接运行 OpenClaw(不走 Docker),这里应该填http://127.0.0.1:11434,否则会连接超时。
  • internal_api工具的handler函数,必须return一个 Promise,且resolve的值会作为tool_response.payload.tool_output推送到前端。Paperclip 协议规定,tool_output必须是 JSON-serializable 对象,不能是undefined或function。

3.4 调试与可观测性:如何读懂 OpenClaw Debug Console 的每一行

OpenClaw 的 Debug Console (http://openclaw.local:3001) 是你理解 Paperclip 运行机制的显微镜。它的主界面分为三栏:

  • 左侧 Run List:显示所有runId,每个条目包含status(running/completed/failed)、created_at、last_step_type。点击一个runId,右侧会展开其完整 Step 树。

  • 中间 Step Tree:以树状图展示 Step 的父子关系。每个节点显示:

    • type:plan/tool_call/tool_response/final_answer
    • id:step_xxx
    • duration:执行耗时(毫秒)
    • timestamp:精确到毫秒的时间戳
  • 右侧 Payload Inspector:点击任意 Step 节点,下方显示其完整的payloadJSON。这是最核心的调试区域。

举个真实案例:用户提问“如何重置数据库密码?”,但返回的答案是“未找到相关信息”。你在 Payload Inspector 里看到:

{ "type": "tool_call", "tool_name": "vector_search", "tool_input": { "query": "reset database password", "top_k": 3 } }

紧接着一个tool_response:

{ "type": "tool_response", "tool_name": "vector_search", "tool_output": [] }

这说明 RAG 检索没返回任何结果。你立刻意识到问题不在 LLM,而在向量库。于是你去documentDir目录下检查,发现 PDF 手册里关于“重置密码”的章节标题是“Database Credential Rotation”,而用户问的是“reset database password”,语义不匹配。解决方案是:在 OpenClaw 配置中启用rag.hybridSearch: true,让检索同时结合关键词匹配和向量相似度。

另一个经典问题:react native 启动白屏。这通常是因为 React Native 的 WebView 不支持fetch的keepalive选项,导致 SSE 连接在后台被系统 kill。Paperclip Client 提供了降级方案:在useAgent()的fallback选项中指定pollingInterval(如1000),当检测到 SSE 不可用时,自动切换为 HTTP 轮询。这个细节在官方文档里藏得很深,但却是移动端落地的关键。

4. 实操过程与核心环节实现:从部署到上线的全流程详解

现在我们把零散的步骤串起来,形成一个可复现、可交付的完整流程。目标:在一台 CentOS 7.9 服务器上,部署 Paperclip + OpenClaw,对外提供 HTTPS 访问,并接入企业微信(替代 Microsoft Teams,因后者在国内受限)。

4.1 CentOS 7.9 环境初始化:Node.js 22.12+ 的编译安装

CentOS 7.9 默认的yum install nodejs只能装到 v6.x,完全不满足要求。必须手动编译安装 Node.js 22.12.0。这不是为了炫技,而是因为 CentOS 7.9 的 glibc 版本(2.17)太老,预编译的二进制包(要求 glibc >= 2.28)会报GLIBC_2.28 not found错误。

# 1. 安装编译依赖 sudo yum groupinstall "Development Tools" sudo yum install openssl-devel ncurses-devel zlib-devel sqlite-devel bzip2-devel xz-devel # 2. 下载 Node.js 源码(注意:必须下载 source code,不是 binaries) cd /tmp wget https://nodejs.org/download/release/v22.12.0/node-v22.12.0.tar.gz tar -xzf node-v22.12.0.tar.gz cd node-v22.12.0 # 3. 配置编译选项,指定旧版 glibc 兼容 ./configure --prefix=/opt/nodejs-22.12.0 --with-intl=system-icu # 4. 编译(耐心等待 20-30 分钟) make -j$(nproc) # 5. 安装 sudo make install # 6. 创建软链接并加入 PATH sudo ln -sf /opt/nodejs-22.12.0/bin/node /usr/local/bin/node sudo ln -sf /opt/nodejs-22.12.0/bin/npm /usr/local/bin/npm

验证安装:

node -p "process.version" // 应输出 v22.12.0 node -p "require('fs').promises" // 应无报错,证明 Promise API 可用

注意:不要用nvm,它在 CentOS 7.9 上的 shell 兼容性极差,nvm install常常卡死在Downloading and installing node v22.12.0...。手动编译虽然慢,但一次成功,永不掉坑。

4.2 OpenClaw 生产部署:Nginx 反向代理与 HTTPS 配置

OpenClaw 默认监听0.0.0.0:3000,但生产环境绝不能直接暴露。必须用 Nginx 做反向代理,并启用 HTTPS。

Nginx 配置/etc/nginx/conf.d/openclaw.conf:

upstream openclaw_backend { server 127.0.0.1:3000; } server { listen 80; server_name your-domain.com; # 强制 HTTP 重定向到 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/your-domain.com/chain.pem; # 关键:SSE 需要保持长连接 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; # 关键:设置超时,避免 SSE 断连 proxy_read_timeout 300; proxy_send_timeout 300; location / { proxy_pass http://openclaw_backend; proxy_redirect off; } # Debug Console 单独配置,可选 location /debug { proxy_pass http://127.0.0.1:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; } }

这个配置的精髓在于proxy_http_version 1.1和Connection 'upgrade'。SSE 协议依赖 HTTP/1.1 的Connection: keep-alive,而 Nginx 默认会把Connectionheader 移除。加上这两行,Nginx 才会透传客户端的Upgrade请求头,让 OpenClaw 的 SSE 连接得以建立。否则,前端会一直收到502 Bad Gateway,日志里显示upstream prematurely closed connection while reading response header from upstream。

4.3 React 前端构建与静态部署

Create React App 的build输出是纯静态文件,可以直接扔到 Nginx 的html目录下。但有一个关键修改:public/index.html中的<script>标签必须指向正确的域名。

在package.json中,添加homepage字段:

{ "homepage": "https://your-domain.com", "name": "paperclip-demo", ... }

然后运行npm run build,生成的build/static/js/main.xxxxx.js里的fetch请求会自动带上https://your-domain.com前缀,而不是相对路径/api/...。否则,Nginx 会把/api/agent/run当作静态文件请求,返回404。

部署命令:

# 将 build 目录复制到 Nginx 的 html 目录 sudo cp -r build/* /usr/share/nginx/html/ # 重启 Nginx sudo nginx -t && sudo systemctl reload nginx

4.4 企业微信接入:用 Webhook 替代 Microsoft Teams

由于 Microsoft Teams 在国内网络环境下不稳定,Paperclip 的openclaw 如何接入 microsoft teams热搜词,实际需求是“如何接入国内主流 IM”。OpenClaw 支持通过 Webhook 接入任何支持 HTTP POST 的 IM。

企业微信机器人 Webhook URL 格式为:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_WEBHOOK_KEY

在 OpenClaw 配置中,添加notifications模块:

// openclaw.config.js module.exports = { // ... 其他配置 notifications: { wecom: { enabled: true, webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx', // 消息模板,支持 Handlebars 语法 template: ` {{#if steps.[0].payload.type === 'final_answer'}} 【AI 助手回复】 {{steps.[0].payload.answer}} {{/if}} {{#if steps.[0].payload.type === 'error'}} 【AI 助手报错】 {{steps.[0].payload.message}} {{/if}} ` } } };

OpenClaw 会在每次run结束后(无论成功或失败),自动向企业微信 Webhook 发送一条消息。template使用 Handlebars,steps.[0]指向最新的 Step,steps.[0].payload.answer就是最终答案。这个设计的好处是:你不需要改任何前端代码,就能让 AI 回答自动推送到企业微信群,实现“提问-回答-通知”的闭环。

5.

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询