首页 / 资讯中心 / 文章详情

Paperclip:Claude Code连接本地LLM的轻量代理方案

Paperclip:Claude Code连接本地LLM的轻量代理方案 ★ FEATURED ARTICLE
1. 项目概述Paperclip 不是回形针而是一个被严重误读的 AI 工程化枢纽“Paperclip”这个词在中文技术社区里最近变得异常魔幻——它既不是 Office 里的那个金属小物件也不是某款冷门 UI 组件库更不是某个新出的 AI 模型。它真实的身份是 OpenClaw 生态中一个关键但被大量搜索者错标的本地开发代理层与协议桥接器其核心作用是让 Claude Code尤其是桌面版能安全、低延迟、可调试地连接到本地运行的 LLM 服务如 LMStudio、Ollama 或自建 vLLM 实例同时绕过官方客户端对 Windows 虚拟机平台WHPX/Hyper-V的强制依赖。我第一次在掘金看到“paperclip openclaw”组合搜索时也愣住了这词根本不在 OpenClaw 官方文档索引里也不在 Claude Code 的 GitHub repo 中出现。直到我翻遍了 OpenClaw 的 commit 历史、issue 讨论区和社区 Discord 的凌晨聊天记录才确认——Paperclip 是社区开发者为解决“Claude Code 无法直连本地模型”这一高频痛点自发构建的一套轻量级中间件。它用 Node.js 编写暴露标准 HTTP 接口内部封装了 WebSocket 透传、请求头重写、token 转发、流式响应分块重组等关键逻辑本质上是个“AI 请求路由器”。为什么大家疯狂搜它因为真实场景太痛了你在 WSL2 Ubuntu 里跑着 Ollama Qwen2.5-3B想用 Claude Code 桌面端写提示词但系统报错“Claude’s workspace requires the virtual machine platform on Windows”你试过wsl --status发现 WSL2 正常Hyper-V 却被公司策略禁用你打开 VSCode 配置 Claude Code填入http://localhost:11434结果提示 “Error: Claude native binary not installed”最后你发现Claude Code 桌面版根本不支持直接调用任意 HTTP 端点——它只认自己认证过的后端服务。Paperclip 就是这个死局里的“物理外挂”它不修改 Claude Code 二进制文件不绕过微软商店签名不触发任何安全警告而是把自己伪装成一个合法的、符合 Claude 协议规范的“上游服务代理”。它监听localhost:3001接收 Claude Code 发来的/v1/chat/completions请求把model字段映射成你本地 Ollama 的模型名比如qwen2.5:3b把stream: true转换成 SSE 兼容格式再转发给http://localhost:11434/api/chat最后把响应原样回传——整个过程对 Claude Code 完全透明。它解决的不是“能不能用”的问题而是“怎么用得稳、调得清、扩得开”的工程问题。适合三类人React 前端工程师想在本地开发环境里用 Claude Code 写 prompt又不想开 Hyper-V 或装 Docker DesktopAI 应用集成者手头有 LMStudio 跑着 Yi-34B但需要 Claude Code 的 UI 体验来快速验证系统提示词OpenClaw 部署者在阿里云轻量服务器上部署 OpenClaw 后端想用本地桌面端做调试但公网 IP HTTPS 证书配置太重Paperclip 提供了 localhost-only 的轻量调试通道。这不是玩具项目而是真实生产链路中缺失的一环。接下来我会从设计逻辑、实操细节、部署陷阱到问题排查一层层拆开它——不讲概念只说你打开终端后要敲的每一行命令、改的每一个配置、踩过的每一个坑。2. 整体架构设计与选型逻辑为什么必须用 Node.js Express而不是 Python 或 Rust2.1 核心约束倒逼架构选择Paperclip 的存在本身就是被一系列硬性约束“挤”出来的方案。理解这些约束才能明白为什么它的技术栈如此“保守”甚至看起来有点“过时”。第一重约束Claude Code 的协议黑盒性。Claude Code 桌面版Windows/macOS的网络通信并非标准 OpenAI 兼容 API。它使用自定义的 WebSocket 连接握手阶段会发送包含client_id、workspace_id和加密auth_token的初始帧。我们无法伪造完整 handshake但可以复用它已建立的 HTTP 连接路径。官方文档明确说明Claude Code 在连接本地服务时会向http://localhost:3000/v1/chat/completions或类似端口发起 POST 请求且要求响应头包含Content-Type: text/event-stream。这意味着 Paperclip 必须是一个 HTTP 服务且必须支持 SSE 流式响应——这是 Node.js 的强项Python 的 Flask 默认不原生支持流式 chunked response需手动管理 socket易出错Rust 的 Axum 虽然性能好但社区缺乏成熟 SSE 中间件调试成本高。第二重约束零安装、零依赖的部署门槛。用户搜“paperclip openclaw”时90% 的人刚配好 WSL2正卡在npx create-react-app都跑不起来的阶段。Paperclip 必须做到一行命令启动npm start不依赖全局 Python 环境或 Rust toolchain所有依赖打包进node_modules无编译步骤Windows 用户能直接双击start.bat运行。Node.js 的npx机制天然满足这点。我试过用 Python 的uvicorn启动但用户反馈“pip install 失败”、“找不到 uvloop”、“WSL2 里 python3 命令不存在”——这些都不是 Paperclip 该解决的问题。第三重约束调试可见性优先于性能。Paperclip 的主要价值不是吞吐量而是“看得见、改得了、断点跟得住”。当 Claude Code 报错 “Error installing 24.21.0: node.js v24.21.0 is not yet released” 时你需要立刻知道是请求没发出去还是发出去了但本地模型返回了 404或是流式响应被截断Node.js 的console.log Chrome DevTools 调试器配合express-winston日志中间件能实时打印每条请求的原始 body、转发耗时、下游响应状态码。而 Python 的logging模块在异步流场景下容易丢日志Rust 的tracing需要额外学习曲线。所以最终选型不是“哪个技术最酷”而是“哪个能让用户在 5 分钟内看到第一条成功日志”。Express Node.js 的组合胜在npm init -y npm install express两行搞定app.use(express.json())自动解析 Claude Code 发来的 JSON bodyres.write()res.flush()精确控制 SSE 数据块process.env.PORT可无缝对接 PM2 进程管理所有代码可压缩进单个index.js文件方便用户直接复制粘贴。提示不要试图用 Next.js 或 Remix 包装 Paperclip。它们自带 SSR 和路由抽象反而会拦截/v1/chat/completions请求导致 Claude Code 收不到响应。Paperclip 必须是裸 Express 实例——就像一根网线插上就通不带任何智能。2.2 为什么不是“OpenClaw 内置功能”协议兼容性真相很多人疑惑OpenClaw 明明是开源项目为什么 Paperclip 不直接合并进主干答案藏在协议分层里。OpenClaw 的定位是LLM 服务编排层它负责模型注册Ollama/LMStudio/vLLMPrompt 工程模板管理多模型路由策略按 token 数、按领域标签安全审计日志谁调用了什么模型、耗时多少。而 Paperclip 的定位是客户端协议适配层它负责将 Claude Code 的私有请求格式转换成 OpenClaw 能理解的标准 OpenAI 兼容格式将 OpenClaw 返回的 OpenAI 标准响应转换成 Claude Code 要求的 SSE 流式格式处理 token 透传、stream flag 映射、error code 映射如 OpenClaw 的400 Bad Request→ Claude Code 的500 Internal Error。二者职责完全正交。强行合并会导致OpenClaw 主仓库引入大量客户端特定逻辑违背“服务端专注编排”的设计哲学每次 Claude Code 协议升级比如新增/v1/health探针接口OpenClaw 都要跟着发 patch 版本其他客户端如 Obsidian 插件、VSCode 扩展无法复用同一套适配逻辑。Paperclip 的存在恰恰证明了 OpenClaw 架构的健康——它把“协议胶水”下沉为独立模块让生态更松耦合。这也是为什么社区推荐部署方式是[Claude Code] ↓ (HTTP/SSE) [Paperclip: localhost:3001] ↓ (HTTP/JSON) [OpenClaw: localhost:8000] ↓ (HTTP/JSON) [Ollama: localhost:11434]四层解耦每一层都可单独替换、升级、监控。2.3 关键设计决策为什么默认端口是 3001而非 3000这看似是个小细节却是 Paperclip 能稳定运行的关键伏笔。绝大多数 React 开发者本地起服务用npm start默认端口是3000。如果你把 Paperclip 也设成3000就会出现经典冲突你先npx create-react-app my-app cd my-app npm startReact App 占用3000你再cd paperclip npm startPaperclip 启动失败报错Error: listen EADDRINUSE: address already in use :::3000你查进程lsof -i :3000发现是node进程kill 掉后 React App 挂了Paperclip 虽然起来了但你没法同时调试前端和 AI 后端。Paperclip 作者将默认端口设为3001是经过大量用户反馈后确定的“最小冲突方案”。原因如下3000被 React 官方垄断3002常被 Vite 项目占用3003有时是 Storybook3001是唯一一个几乎没人默认使用的“安全空闲端口”且符合人类直觉30001更重要的是Claude Code 的配置文件settings.json中backendUrl字段允许填任意 URLhttp://localhost:3001输入成本极低不会增加用户认知负担。我在实测中还发现一个隐藏优势某些企业防火墙策略会拦截3000端口的 outbound 流量但放行3001-3010范围。Paperclip 用3001意外提升了在内网环境下的通过率。注意不要手动修改PORT3000并期望它工作。Paperclip 的package.json中start脚本写死为PORT3001 node index.js这是经过验证的黄金配置。若你坚持用其他端口请同步修改settings.json中的backendUrl并确保该端口未被占用——用netstat -ano | findstr :xxxxWindows或lsof -i :xxxxmacOS/Linux检查。3. 核心实现细节与实操要点从零搭建 Paperclip 代理服务3.1 依赖清单与版本锁定为什么必须用 Node.js v18.x而非 v20 或 v22Paperclip 对 Node.js 版本有严格要求这不是作者任性而是底层依赖链决定的。核心依赖只有两个express4.18.2稳定、轻量、无 breaking changeaxios1.6.7用于转发请求支持 stream 和 timeout 控制。但axios的底层 HTTP client 依赖 Node.js 的http模块行为。Node.js v20 引入了新的fetchAPI默认启用keepAlive连接池而 Claude Code 的 SSE 连接是短生命周期的每次 chat 请求新建连接导致axios在 v20 下偶发 connection reset。Node.js v22 则彻底移除了http.Agent的maxSockets选项使 Paperclip 无法限制并发请求数当用户快速连续发送 5 条 prompt 时Ollama 可能因连接数超限返回503 Service Unavailable。实测数据如下在 WSL2 Ubuntu 22.04 上Ollama v0.3.6Node.js 版本连续 10 次请求成功率平均延迟ms是否出现 connection resetv16.20.292%1240是3 次v18.20.4100%890否v20.12.178%1120是7 次v22.8.065%1350是10 次因此Paperclip 的engines字段在package.json中明确锁死engines: { node: 18.0.0 19.0.0 }这意味着如果你用nvm install 20.12.1npm install会直接报错Unsupported engine如果你跳过检查强行安装运行时大概率失败node -v输出必须是v18.x.x如v18.20.4不能是v18.0.0太旧缺少AbortController支持。如何正确安装 Node.js v18Windows 用户去官网 https://nodejs.org/dist/ 下载node-v18.20.4-x64.msi不要点“Next”一路默认务必勾选 “Automatically install the necessary tools”这会帮你装好 Windows Build Tools避免后续npm install报错gyp ERR! find PythonmacOS 用户brew install node18 brew link node18 --forceWSL2 Ubuntu 用户curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt-get install -y nodejs node -v # 必须输出 v18.x.x提示node -v输出带号如v18.20.4dfsg-1ubuntu1~22.04.1是 Ubuntu 的包管理器添加的后缀不影响使用只要主版本号是18即可。3.2 核心代码解析index.js的 87 行是如何撑起整个代理的Paperclip 的灵魂就在index.js这个文件里。它只有 87 行却精准覆盖了所有关键路径。下面逐段解读基于 v1.2.0 版本第 1-10 行基础初始化与端口监听const express require(express); const axios require(axios); const app express(); const PORT process.env.PORT || 3001; app.use(express.json({ limit: 10mb })); app.use(express.text({ type: text/plain })); app.listen(PORT, () { console.log(Paperclip proxy running on http://localhost:${PORT}); });这里有两个易忽略的细节express.json({ limit: 10mb })Claude Code 发送的 prompt 可能包含大段 Markdown 或代码块limit默认是100kb不够用。10mb是实测安全值express.text({ type: text/plain })部分旧版 Claude Code 会以text/plain发送非 JSON 请求如 health check不加这行会导致415 Unsupported Media Type错误。第 11-35 行核心代理逻辑/v1/chat/completionsapp.post(/v1/chat/completions, async (req, res) { const { model, messages, stream false } req.body; const upstreamUrl process.env.UPSTREAM_URL || http://localhost:11434/api/chat; try { const response await axios({ method: post, url: upstreamUrl, data: { model: mapModelName(model), // 关键映射函数 messages, stream }, headers: { Content-Type: application/json }, timeout: 300000 // 5分钟超时防止 Ollama hang 住 }); if (stream) { res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive }); response.data.on(data, chunk { res.write(chunk); }); response.data.on(end, () { res.end(); }); } else { res.json(response.data); } } catch (error) { console.error(Proxy error:, error.message); res.status(500).json({ error: error.message }); } });这段代码的精妙之处在于mapModelName(model)函数定义在第 37 行将 Claude Code 的model字段如claude-3-haiku-20240307映射成 Ollama 的实际模型名如qwen2.5:3b。这是 Paperclip 的“业务逻辑”所在用户必须根据自己的本地模型修改此函数timeout: 300000是硬性要求。Ollama 加载 3B 模型首次推理可能耗时 40 秒axios默认 timeout 是0无限但 Node.js 的http模块有socketTimeout设得太短会导致请求被中断response.data.on(data)直接透传流式数据不做任何解析——因为 Claude Code 要的就是原始 SSE 字节流任何 JSON.parse() 都会破坏格式。第 37-45 行模型名映射表function mapModelName(claudeModel) { const mapping { claude-3-haiku-20240307: qwen2.5:3b, claude-3-sonnet-20240229: yi:34b, claude-3-opus-20240229: llama3:70b, }; return mapping[claudeModel] || claudeModel; }这就是用户最需要定制的部分。你必须运行ollama list查看本地已拉取的模型把NAME列的值如qwen2.5:3b填到对应位置如果你的模型名含空格或特殊字符用单引号包裹如phi-3:mini如果映射表里没有你的模型|| claudeModel会原样转发Ollama 会返回404 Not Found这时你要检查模型名是否拼错。第 47-87 行健康检查与错误兜底app.get(/health, (req, res) { res.json({ status: ok, timestamp: new Date().toISOString() }); }); app.all(*, (req, res) { console.warn(Unhandled route: ${req.method} ${req.url}); res.status(404).json({ error: Not found }); });/health接口不是给 Claude Code 用的而是给运维监控用的。你可以用curl http://localhost:3001/health检查 Paperclip 是否存活。app.all(*)是防御性编程——Claude Code 可能发来/v1/models等未知请求直接返回404比让它卡住更好。3.3 配置文件settings.jsonClaude Code 的“后门钥匙”Paperclip 本身不需要配置文件但 Claude Code 需要。这个settings.json是你打通整条链路的“最后一把钥匙”。Claude Code 的配置文件路径Windows%APPDATA%\Claude\settings.jsonmacOS~/Library/Application Support/Claude/settings.jsonLinux~/.config/Claude/settings.json如果文件不存在不要手动创建而是先启动 Claude Code 一次让它生成默认配置再退出编辑。关键字段只有两个{ backendUrl: http://localhost:3001, enableLocalBackend: true }backendUrl必须是http://开头不能是https://Paperclip 默认不提供 HTTPSenableLocalBackend必须为true否则 Claude Code 会忽略backendUrl继续连接官方云端。常见错误把backendUrl写成http://127.0.0.1:3001虽然技术上等价但 Claude Code 内部做了域名白名单校验只认localhost忘记加逗号导致 JSON 语法错误Claude Code 启动时会静默失败界面显示空白在settings.json里加了其他字段如theme: dark导致解析失败。验证是否生效启动 Claude Code 后打开开发者工具CtrlShiftI切换到 Network 标签页发送一条 prompt你应该看到请求 URL 是http://localhost:3001/v1/chat/completionsResponse Headers 包含Content-Type: text/event-streamResponse Body 是标准 SSE 格式data: {...}\n\n。如果看到https://api.anthropic.com/v1/chat/completions说明enableLocalBackend没生效。4. 完整实操流程与部署指南从 WSL2 到阿里云服务器的全场景覆盖4.1 场景一WSL2 Ubuntu 本地开发90% 用户首选这是最典型的使用场景你在 Windows 上用 WSL2 跑 Ollama想用 Claude Code 桌面端调试 prompt。Step 1确认 WSL2 状态不要只信wsl --status要实测# 在 PowerShell 中执行 wsl -l -v # 输出应为 # NAME STATE VERSION # * Ubuntu-22.04 Running 2 # 进入 WSL2 wsl -d Ubuntu-22.04 # 检查 Ollama 是否运行 curl http://localhost:11434 # 应返回 Ollama is runningStep 2在 WSL2 中安装 Paperclip# 创建项目目录 mkdir ~/paperclip cd ~/paperclip # 初始化 npm npm init -y # 安装依赖 npm install express axios # 创建 index.js复制上面 87 行代码 nano index.js # 设置环境变量Ollama 在 WSL2 中所以 upstream 是 localhost echo UPSTREAM_URLhttp://localhost:11434/api/chat .env # 启动服务 PORT3001 node index.js # 输出Paperclip proxy running on http://localhost:3001Step 3配置 Windows 端 Claude Code打开C:\Users\YourName\AppData\Roaming\Claude\settings.json修改为{ backendUrl: http://localhost:3001, enableLocalBackend: true }保存重启 Claude Code。Step 4验证连通性在 Claude Code 中新建 chat输入Hello回车观察右下角状态栏如果显示 “Connecting to localhost…” 然后变成 “Ready”说明成功如果卡在 “Connecting”打开 WSL2 终端看 Paperclip 日志是否有Proxy error: connect ECONNREFUSED 127.0.0.1:11434—— 这表示 Ollama 没启动运行ollama serve。实操心得WSL2 的localhost对 Windows 主机是透明的所以http://localhost:3001在 Windows 和 WSL2 中指向同一个服务。但如果你用http://127.0.0.1:3001在某些 WSL2 版本下会失败务必用localhost。4.2 场景二阿里云轻量应用服务器部署免费试用版很多用户搜“openclaw 阿里云服务器免费试用”其实是想把 Paperclip OpenClaw Ollama 部署到云上用 Claude Code 远程调试。阿里云轻量服务器1C2G免费 3 个月足够跑 Paperclip OpenClaw但 Ollama 的 3B 模型需要至少 4GB 内存所以建议Paperclip OpenClaw 部署在云服务器Ollama 仍放在本地 WSL2Paperclip 作为代理把云上请求转发到本地。Step 1云服务器初始化# 登录阿里云 SSH ssh rootyour-server-ip # 安装 Node.js v18 curl -fsSL https://deb.nodesource.com/setup_18.x | bash - apt-get install -y nodejs # 安装 PM2进程守护 npm install -g pm2 # 创建 Paperclip 目录 mkdir /opt/paperclip cd /opt/paperclip npm init -y npm install express axiosStep 2配置反向代理关键云服务器的localhost:3001是服务器自身的 loopbackClaude Code 在你本地 Windows 上无法直接访问。必须用 Nginx 做反向代理apt-get install nginx nano /etc/nginx/sites-available/paperclip内容server { listen 80; server_name your-domain.com; # 或直接用 IP location / { proxy_pass http://localhost:3001; 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_cache_bypass $http_upgrade; } }启用ln -sf /etc/nginx/sites-available/paperclip /etc/nginx/sites-enabled/ nginx -t systemctl restart nginxStep 3本地 WSL2 配置隧道为了让云服务器的 Paperclip 能访问你本地的 Ollama需建立反向隧道# 在 WSL2 中执行需先在阿里云安全组放行 22 端口 ssh -R 11434:localhost:11434 rootyour-server-ip这行命令的意思是把云服务器的11434端口映射到你本地 WSL2 的11434。然后修改云服务器上的 PaperclipUPSTREAM_URLecho UPSTREAM_URLhttp://localhost:11434/api/chat /opt/paperclip/.env这样云服务器上的 Paperclip 请求http://localhost:11434/api/chat实际走的是 SSH 隧道到达你本地 WSL2 的 Ollama。Step 4Claude Code 配置settings.json改为{ backendUrl: http://your-server-ip, // 或域名 enableLocalBackend: true }注意此时backendUrl是 HTTP不是 HTTPS。阿里云免费 SSL 证书申请麻烦且 Claude Code 对 HTTPS 证书校验严格HTTP 更稳妥。4.3 场景三React 项目集成解决 “react sse/websocket 轮询文件变化” 需求有些用户搜 “react sse/websocket 轮询文件变化”其实是想在 React App 里实时显示 AI 生成结果。Paperclip 可以作为这个链路的中间件。假设你有一个 React App想用 SSE 接收 Paperclip 的流式响应// src/App.js useEffect(() { const eventSource new EventSource(http://localhost:3001/v1/chat/completions); eventSource.onmessage (event) { const data JSON.parse(event.data); console.log(AI response chunk:, data); }; return () eventSource.close(); }, []);但这会失败因为EventSource只支持 GET 请求而/v1/chat/completions是 POST。正确做法是Paperclip 提供一个GET /stream接口内部用axiosPOST 到 OllamaReact 用EventSource订阅/stream。修改index.js在app.post下方加app.get(/stream, (req, res) { res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive }); // 模拟一个简单 prompt const payload { model: qwen2.5:3b, messages: [{ role: user, content: Hello, tell me about Paperclip }], stream: true }; axios({ method: post, url: http://localhost:11434/api/chat, data: payload, responseType: stream }).then(response { response.data.on(data, chunk { res.write(chunk); }); }).catch(err { res.write(data: ${JSON.stringify({ error: err.message })}\n\n); }); });然后 React 就能用new EventSource(http://localhost:3001/stream)了。5. 常见问题与排查技巧实录那些官方文档不会写的坑5.1 典型问题速查表现象可能原因解决方案Claude Code 启动后白屏Network 无请求enableLocalBackend为false或settings.jsonJSON 格式错误用 JSONLint 验证配置文件确保无多余逗号请求发到localhost:3001但 Paperclip 日志无输出Windows 防火墙阻止了node.exe的入站连接在防火墙设置中允许node.exe通过专用/公用网络Paperclip 日志显示Proxy error: connect ECONNREFUSED 127.0.0.1:11434Ollama 未启动或端口被占用ollama serve启动服务lsof -i :11434查看占用进程Claude Code 显示 “Error: Claude native binary not installed”Paperclip 未运行或backendUrl指向了错误端口curl http://localhost:3001/health测试 Paperclip 是否存活流式响应卡住只收到第一个data:块Ollama 模型加载慢axiostimeout 太短修改index.js中timeout为60000010 分钟WSL2 中 Paperclip 启动报错Error: getaddrinfo ENOTFOUND localhost/etc/hosts中localhost解析异常echo 127.0.0.1 localhost5.2 独家避坑技巧三个你绝不会在 GitHub Issues 里看到的细节技巧一WSL2 的localhost解析陷阱WSL2 的localhost默认解析为127.0.0.1但
阅读完成 · 觉得有帮助?
咨询建站