ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从终端到浏览器:构建Web AI编码工作区,用Redis实现Claude Code与Codex会话持久化

从终端到浏览器:构建Web AI编码工作区,用Redis实现Claude Code与Codex会话持久化 1. 项目需求与整体设计思路1.1 为什么需要Web AI编码工作区前一阵子我几乎每天都在用Claude Code和Codex写代码效率确实高但越用越觉得别扭这些工具默认跑在终端里聊天记录滚动起来很快想回看之前的方案得往上翻半天开多个任务就得开多个终端标签页窗口一多就乱。最头疼的是会话状态丢失终端一关刚才聊到一半的上下文就没了重新启动后又要从零开始解释项目背景。Easy Web Vibecoding这个项目本质上就是给Claude Code / Codex这类终端AI编码工具套一层Web外壳把它们的能力搬进浏览器。打开页面左边是文件树和任务列表右边是AI对话窗口中间能看到它改动的diff。所有会话记录、上下文、任务状态都被持久化存储重启电脑、刷新页面、甚至换一台设备都能接续之前的工作。这样既能保留CLI工具原生的强大能力又不用承受终端交互带来的心智负担。这个方案适合谁一类是每天要同时管多个项目的开发者浏览器的多标签天然适合并行处理另一类是刚接触Claude Code / Codex还不习惯命令行操作的新手还有一类是在团队里做技术支撑的人把工作区跑在服务器上大家通过浏览器访问可以方便地进行AI编程协作。1.2 持久化的价值让会话成为项目资产我最初只想解决“终端会话丢失”的问题但真正开始做持久化后才意识到这其实是在把对话数据变成项目资产。默认情况下Claude Code使用一个历史记录文件保存会话索引可以靠claude --resume恢复之前的对话但那是工具自己维护的机制和项目的文件结构、任务清单、环境状态没有关联。Codex类似它有会话记录但你去翻历史记录时很难快速搞清楚当时为什么这么改、改了哪些文件、下一步计划是什么。真正的持久化不只是把聊天记录存下来而是把整个工作区的状态还原出来。包括当前项目的分支、待办任务、上次对话的结论、AI生成的补丁内容、甚至每个任务对应的文件改动范围。我把这些数据按项目为维度组织起来存到Redis里配合文件系统里的结构化目录让每个项目都有一份完整可追溯的“工作档案”。举个实际场景上周我在笔记本上处理一个React项目的中途去开会关了电脑。第二天在台式机上打开Easy Web Vibecoding的页面输入项目名系统把Redis里的会话记录、未完成的任务列表全部加载出来Claude Code重新启动我把历史消息作为上下文重新灌进去它接着昨天的思路继续干活完全没有“我讲到哪了”的断裂感。这就是持久化带给我最直观的收益。1.3 技术选型与架构规划整个项目的架构并不复杂我把它拆成四层前端工作区浏览器里的Web界面负责展示任务、消息、diff和文件状态。后端桥接服务Node.js进程负责接收前端请求通过子进程调用Claude Code / Codex CLI并把输出流转发出去。持久化模块基于Redis实现存储会话消息、任务元数据、上下文索引同时用AOF配置保证数据不丢。CLI工具层底层真正干活的Claude Code和Codex由桥接服务统一调度。为什么选Node.js而不是Python或Go因为Claude Code和Codex本身都是Node.js生态的工具用Node写子进程管理最自然处理标准输入输出流也方便。Express做HTTP接口配合Server-Sent Events或者WebSocket推送AI输出前端实现起来成本很低。选Redis做持久化主要原因有两个一是Redis的RDB和AOF机制可以提供可靠的落盘保证二是会话数据天然适合用Hash、List这样的结构来表示读写都是内存级速度不会拖慢AI输出的流转过程。当然你也可以直接用SQLite但多任务并发状态下Redis的原子操作和过期策略更灵活。架构定了之后我给自己定了三个原则第一不修改Claude Code和Codex本身只用它们的CLI接口第二所有持久化数据都带项目ID隔离干净第三桥接层必须能同时管理多个子进程互不干扰。这三个原则让整个项目的维护成本低了很多。2. 环境准备与基础配置2.1 安装Claude Code和Codex CLI工欲善其事必先利其器。Easy Web Vibecoding底层依赖Claude Code和Codex所以先把这两个CLI工具装好。安装本身不算难但有几个细节容易踩坑。Claude Code的官方推荐方式是通过npm全局安装命令是npm install -g anthropic-ai/claude-code。装完之后在终端执行claude按提示登录授权即可。需要注意的是Node.js版本官方文档要求较新的LTS版本我建议至少Node 18以上最好20 LTS。装之前先执行node -v确认版本不然某些依赖版本会报错。Codex目前提供npm包和桌面应用两种形式。命令行版本核心是codex命令安装方式也是npm全局安装我实际部署时用的包名是openai/codex但这个东西更新频率比较高建议直接查官方文档确认当前推荐的安装命令。桌面版有图形界面但Easy Web Vibecoding的核心场景是后台服务我只用CLI版本桌面版可以作为本地调试的辅助工具。如果你在Windows上安装建议装完Git Bash或者Windows Terminal因为后续子进程的spawn逻辑对PATH环境变量比较敏感。Ubuntu服务器上安装则简单得多但要注意全局npm包的bin目录是否在PATH里我遇到过明明装成功了执行claude却提示command not found的情况后来发现是~/.npm-global/bin没加进PATH。2.2 认证、多供应商与本地模型接入这两个CLI工具装好之后紧接着就是认证。Claude Code默认通过Anthropic账号订阅授权首次运行会跳浏览器。如果你在服务器上跑记得用claude setup之类的命令完成一次性登录然后把凭证信息保存好。Codex也一样首次使用会要求登录OpenAI账号认证通过后会在本地生成token文件。在实际项目中我更推荐把供应商配置做成可切换的而不是绑定死某一个。ccswitch这个工具在社区里很流行它可以在Claude Code和Codex之间切换Provider比如把Claude Code指向DeepSeek、本地LM Studio这类兼容接口。我用ccswitch配置了一个统一的本地代理端点然后让多个CLI共用方便统一管理API key和路由。不过这里就出现了一个热搜里的经典问题cc switch local proxy failed while handling codex endpoint /responses.我后面会专门排查这里先提一句通常是因为本地代理服务没起来或者路由规则里写错了endpoint路径导致请求被转发到了不存在的接口。接入本地模型时还需要设置对应的环境变量比如Claude Code兼容接口通常认ANTHROPIC_BASE_URL和ANTHROPIC_API_KEYCodex兼容接口则可能有自己的环境变量体系。具体变量名以你使用的Provider文档为准。我个人建议如果你是自费使用官方订阅尽量保持默认配置稳定优先。如果你想体验国产模型或者本地模型再单独开一个供应商配置不要影响生产环境。多Provider配置的版本控制也很重要我通常会在项目里维护一份providers.json把不同用途的配置分开。2.3 持久化存储组件Redis部署与配置持久化这块我用的是Redis。虽然可以直接把会话数据写入JSON文件但并发高的时候文件锁会让人抓狂Redis的原子操作省心得多。Redis的安装很简单Ubuntu上apt install redis-serverWindows上可以用官方提供的MSI安装包。装完第一件事是确认持久化配置。Redis默认开启了RDB快照但RDB会有数据丢失窗口如果你希望做到尽量少丢数据必须把AOF也打开。配置文件里把appendonly yes打开appendfsync everysec是一个不错的平衡点既不用每条命令都刷盘拖慢性能也能把崩溃丢失的数据控制在1秒以内。我还给Redis设置了内存上限和淘汰策略。因为会话数据通常不太大但日志和中间结果累积起来也不容忽视。配置里加上maxmemory 512mb和maxmemory-policy allkeys-lru可以有效防止长时间运行后内存被撑爆。如果你是多项目共用同一个Redis实例建议给每个项目设置独立的key前缀比如ewv:proj-demo:session:*这样查数据、清数据都方便。我自己在跑工作区时还会用一个专门的systemd服务托管Redis并开启自动重启。遇到过几次服务器重启后Redis没跟着起来结果Web工作区一直在报连接错误排查了半天才发现是Redis没拉起来。小细节但和生产稳定性强相关。3. Easy Web Vibecoding 实操实现3.1 项目初始化和目录结构整个项目我起名为Easy Web Vibecoding目录结构大概长这样easy-web-vibecoding/ ├── package.json ├── src/ │ ├── index.js # 后端入口Express服务 │ ├── bridge.js # CLI进程桥接层 │ ├── session.js # Redis会话持久化 │ ├── tasks.js # 任务管理 │ └── sse.js # 服务端事件推送 ├── public/ │ ├── index.html # 前端工作区页面 │ ├── app.js # 前端交互逻辑 │ └── style.css └── projects/ └── demo/ # 实际项目目录由CLI操作先用npm init -y初始化项目然后安装Express、ioredis、cors这些依赖。工作区页面我直接放在public目录下Express的express.static托管不用额外弄构建工具保持轻量。项目目录的设计目标很简单projects/下面每个文件夹代表一个被AI操作的代码仓库这个路径会作为子进程的cwd传入确保不同任务不会改错项目。bridge.js负责把前端发来的一条用户消息包装成对应的CLI交互指令然后监听CLI输出把文本回传前端。3.2 后端Bridge进程设计与实现Bridge是整个工作区最关键的一环。Claude Code和Codex虽然是交互式CLI工具但它们支持非交互模式。Claude Code可以使用claude -p 你的指令这样的方式直接执行单轮指令Codex也有类似的headless模式。但Easy Web Vibecoding需要的是多轮对话上下文所以我选择通过spawn启动一个持久的交互式子进程然后手动给它喂输入、读取输出。Node.js里用child_process.spawn来做这件事const { spawn } require(child_process); function launchAgent(projectDir, provider) { const cmd provider claude ? claude : codex; const args provider claude ? [] : []; const child spawn(cmd, args, { cwd: projectDir, env: { ...process.env, ...getProviderEnv(provider) }, shell: false, }); child.stdout.on(data, (chunk) { const text chunk.toString(); // 这里做流式转发通过SSE推给前端 pushToClient(projectDir, text); }); child.stderr.on(data, (chunk) { // CLI工具的日志和报错都走stderr不能忽略 pushToClient(projectDir, chunk.toString(), stderr); }); return child; }有两件事必须处理一是给不同Provider设置不同的环境变量比如指向本地兼容接口时要把ANTHROPIC_BASE_URL这样的变量注入进去否则子进程会用默认的官方配置二是子进程必须按项目目录隔离不能在多个任务之间共享同一个CLI实例否则会出现上下文污染。实际操作中我还会给子进程挂一个简单的状态机idle表示空闲working表示正在处理消息interrupted表示用户主动终止。前端根据状态显示“忙碌中”或“可输入”避免用户连续发消息导致CLI输出交错。3.3 基于Redis的会话存储与恢复会话存储我设计成三层结构任务层每个项目有若干任务用Redis Hash存任务的基础信息。消息层每个任务下面有一条按时间排列的消息列表用Redis List存。上下文层为了让CLI在重启后恢复记忆我会把最近一轮关键对话拼接成新的上下文消息存成一个字符串。写入消息的伪代码如下const key ewv:${projectId}:messages:${taskId}; await redis.rpush(key, JSON.stringify({ role, content, ts })); await redis.ltrim(key, -50, -1); // 最多保留最近50条避免无限膨胀为什么用List因为对话本身是顺序消息List的rpush/lrange天然支持追加和范围查询还能用ltrim做截断。我在实际使用里还存了一份任务的完整信息包括目标、当前分支、相关文件列表这些信息在后续恢复上下文时非常关键。恢复会话的流程分三步。第一步根据项目ID和任务ID从Redis取出历史消息第二步构造一个resume_prompt把历史消息压缩成一段背景说明例如“这是我之前对话的摘要请继续……”第三步通过bridge向CLI子进程发送一条带上下文的指令让它进入到对应的工作状态。这样即使CLI进程因为服务器重启而消失只要Redis数据还在任务就能无缝接续。这里最忌讳的是把所有历史消息原样灌进去。很多AI编码工具的上下文窗口有限早期没有1M上下文版本时一会儿就爆了。我自己的策略是做“摘要压缩”用一个agents调用把长对话归纳为三五个要点再作为新会话的初始上下文。Claude Code现在有1M上下文模型可用但我依然建议对历史消息做压缩因为过长的上下文不仅费token还会稀释模型对最新指令的注意力。3.4 前端工作区页面与服务端推送前端页面我不打算写得多花哨但信息结构要清楚。左边栏列出当前项目的任务列表中间是AI对话窗口右边可以展示文件改动摘要。为了避免引入项目前端框架我直接用原生HTML加少量JavaScript配合Server-Sent Events接收后端推送。SSE的Node端实现很简单app.get(/events/:projectId, (req, res) { res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive, }); // 将bridge推来的消息格式化为SSE帧 const listener (chunk) res.write(data: ${JSON.stringify(chunk)}\n\n); eventBus.on(projectId, listener); req.on(close, () eventBus.off(projectId, listener)); });前端用EventSource连接这个接口一旦后端从CLI拿到新的内容片段马上就推到浏览器渲染。相比WebSocketSSE的实现更简单而且自动重连机制对AI输出这种单向流非常合适。唯一要注意的是Nginx代理SSE时需要关闭缓冲不然内容会卡在缓冲区里迟迟不出来。我在生产环境是用Docker跑这个服务Nginx里加了一行proxy_buffering off;才解决推送延迟问题。页面上的任务操作也很直接新建任务时后端在projects/{projectId}目录下创建一个新的子进程点击恢复任务时后端从Redis读取历史消息重建进程点击停止时后端向子进程发送Ctrl-C信号并保存当前会话状态。这套交互逻辑并不复杂却能让浏览器变成一个功能完整的AI编程控制台。4. 常见问题与排查技巧实录4.1 codex endpoint /responses 调用失败这是我在搜索热词里看到频率非常高的一条错误信息完整描述大概是cc switch local proxy failed while handling codex endpoint /responses.如果你也用了ccswitch这类工具那么这个报错通常不是Codex本身的问题而是本地代理层出了问题。排查顺序我建议从下往上。先确认本地代理服务是否还在运行很多通过nohup方式启动的进程一旦SSH断开就会被杀掉但ccswitch的配置还指着它然后检查本地代理监听的端口默认可能是localhost:8080这类确认没有端口冲突再看ccswitch配置里的endpoint路径Codex的API路径通常是/v1/responses或/responses如果配置里写成了/v1/chat/completions就会导致请求到了本地代理后路由匹配不上返回失败。如果你压根没用ccswitch而是直接改了环境变量指向某个自定义服务同样要检查这个自定义服务是否实现了Codex期望的/responses接口。很多“本地模型兼容层”只实现了OpenAI的/chat/completions没有实现/responses这种新版接口所以对接Codex时就会报错。解决方案要么是升级兼容层版本要么是选择Codex的兼容模式让CLI走旧的chat接口。4.2 认证不可用与组织订阅限制热搜里还有两个问题经常一起出现codex auth token is unavailable和your organization has disabled claude subscription access for claude code。第一个问题绝大多数时候是登录态过期了。Codex和Claude Code的CLI都会在本地缓存token但token有有效期尤其是通过浏览器OAuth登录时。解决办法很简单重新执行一次登录命令比如Codex的codex loginClaude Code第一次启动时也会有登录引导。如果你在server端跑还得检查环境变量里有没有设置正确的API key有时候你设置了key但格式不对也会导致CLI读不到。第二个问题相对微妙它通常意味着你用的Claude账号本身订阅正常但所在的组织不允许在Claude Code场景下使用。我在团队服务器上遇到过个人账号没问题切到公司组织账号就报这个错。这其实是订阅策略限制不是技术故障。解决办法是检查组织管理员是否启用了Claude Code的访问权限或者用一个个人账号来运行Easy Web Vibecoding。无论哪种认证问题我都不建议“绕过”方案。如果账号没有对应权限绕过限制既可能违反服务条款也会让你在使用过程中随时遇到封禁风险。最稳妥的方式是合理配置你已有的官方权限或者联系订阅管理员开通对应功能。4.3 本地模型接入与API路由错位很多人弄Easy Web Vibecoding一个重要动机就是不想订阅付费API想接入本地模型或者更便宜的第三方模型。这个思路没问题但有几个坑。Claude Code接入LM Studio或DeepSeek这类兼容API时核心是设置环境变量。对于兼容Anthropic协议的端点你需要设置ANTHROPIC_BASE_URL指向本地服务并设置ANTHROPIC_API_KEY为任意非空值本地可能不校验。DeepSeek接入Claude Code在社区里有不少教程主要是通过一个中间转换层把Anthropic协议翻译成DeepSeek的OpenAI协议。这种方案可以用但要注意模型名称必须写成DeepSeek支持的模型ID比如deepseek-chat不能写claude-3-5-sonnet。Codex接入DeepSeek或本地模型则复杂一些因为Codex对接口的依赖路径和Claude不太一样。如果用ccswitch配置了一个本地代理这个代理必须同时处理Claude和Codex两种协议。我见过不少人配置完Claude那边能跑通但Codex一调就报错原因往往是代理只转发了Anthropic协议的请求而Codex的请求被原样转发到了不支持/responses的后端上。可以这么说在接入第三方模型时最重要的调试工具是日志。打开本地代理的详细日志看每次请求实际打到哪个URL、返回了什么状态码比盲猜配置有效得多。4.4 Windows与Linux环境差异Easy Web Vibecoding可以跑在Windows上但有几个差异你要提前知道。子进程的shell行为不一样。Windows上使用spawn时如果命令是claudeNode.js不一定能在PATH里找到对应的.cmd文件这时候需要设置shell: true或者直接指定claude.cmd的绝对路径。Linux上则简单得多直接spawn不再需要shell兜底。路径分隔符。projects/目录拼接时Windows用\\Linux用/如果你在代码里写死了路径拼接符Windows上大概率出问题。建议所有路径都使用path.join来构建。还有进程信号。在Windows上向子进程发送SIGINT的行为和Linux不同直接child.kill(SIGINT)可能不会让CLI优雅退出我遇到过进程变成了僵尸进程、一直占着终端输出流的情况。后来改成先往前端发送一个ABORT请求再配合taskkill /pid xxx /T强制清理子进程树才解决。如果你主要部署在Ubuntu那可以省很多心但代码里最好还是做平台兼容判断避免只在一台机器上跑得通。5. 长期运行优化与个人心得5.1 会话持久化的不同层级选择做Easy Web Vibecoding这么久我总结出三层持久化策略你可以按自己的需求取舍。第一层是工具自带的会话记录比如Claude Code的~/.claude/projects目录Codex的会话历史文件。这层基本零成本适合个人偶尔用只要记住用--resume参数就能找回之前的会话。缺点是数据散落跨项目关联性差而且不一定能在Web界面上展示。第二层是项目级的结构化存储就是我现在的方案。用Redis存任务、消息、摘要、上下文和项目代码目录放在一起。这层的价值在上文已经说过了适合所有认真使用AI编程工作区的开发者。第三层是团队级协作存储把所有项目的状态、用户操作记录、审批流都存进数据库支持多人同时访问同一个工作区。这一层需要引入用户系统和权限体系工作量会大不少但后续扩展价值也高。在我看来这可能是Easy Web Vibecoding最值得深入的方向。5.2 工作区进程稳定性与资源控制Web工作区挂在服务器上长期跑稳定性问题会逐步暴露。我最先遇到的是子进程内存占用过大。Codex和Claude Code底层依赖Node.js一个进程大概几百MB如果同时开五六个任务内存压力不小。建议在桥接层做进程池控制限制同时运行的任务数超出部分排队等待。然后是日志问题。CLI进程会输出大量日志如果不做轮转几天就能写满磁盘。我用systemd的journald统一收集日志并设置了按大小轮转避免日志把Redis的持久化文件挤爆。最后是崩溃恢复。CLI毕竟是第三方程序偶尔会因为异常输入直接退出。我在bridge层加了心跳检测超过30秒没有收到CLI的任何输出就认为进程卡死或退出触发自动重启并从Redis恢复最近一次会话状态。这套机制帮我省了不少事至少半夜不会再被“AI挂了”的报警吵醒。5.3 后续扩展方向做完整套工作区后我明显感觉到它已经从“终端套壳”变成了一个可编程的AI编码平台。后续我打算加几个能力一是多Agent并行让同一个项目里可以同时跑多个AI任务各自负责不同模块修改完统一合并二是权限控制让团队成员可以访问共享工作区但只有特定角色能直接改代码三是把任务状态关联到Git提交每次AI完成一次修改自动生成commit并记录到任务时间线里。这些扩展本质上都在依赖已经打好的Redis持久化基础和bridge调度层。5.4 我的一点使用心得说实话我最初只是想解决终端会话丢失的问题没想到做下来之后整个工作流都被改变了。现在我不再关心今天要打开哪个终端、输入哪条命令而是打开浏览器登录工作区直接看昨天的任务进度和AI留下的记录。一个小技巧分享给大家在构造上下文恢复时不要只塞对话历史最好把项目根目录下的README、当前Git分支、最近一次commit message一起读进来作为“现场信息”拼进prompt。这样AI恢复上下文后不仅知道“我们说了什么”还知道“现在代码处于什么状态”连续工作的准确率高很多。还有一个经验是别把所有任务都放在一个持久化进程里。早期我贪方便一个项目只开一个Claude进程对话时间长了上下文越来越长响应越来越慢模型经常把早期需求记混。后来我改成“一个任务一个进程”任务完成就归档新任务重新加载精简后的摘要效率反而高了许多。持久化不是把所有东西都越存越多而是要懂得在不同阶段截取真正有用的状态。
返回列表