Vibe coding 这个概念今年在开发者圈子里讨论度很高,说白了就是让 AI 模型承担主力编码工作,开发者负责描述意图、把控方向和做代码审查。我日常用得最多的两个 AI 编码脚手架是 Claude Code 和 Codex,一个来自 Anthropic,一个走 OpenAI 的 Codex 路线,各有各的强项。但用久了你会发现一个扎心的事实:工具本身很强,状态却特别容易丢。终端里跑的会话一关就没,换个项目要重新交代一遍背景,模型配置和项目记忆文件散落在各处,每次新开一个仓库就像失忆了一样。
为了解决这个问题,我搭了一个叫Easy Web Vibecoding的本地 Web 工作区,专为 Claude Code / Codex 做持久化处理,把会话历史、项目记忆、模型路由、常用命令收拢到一个浏览器页面里统一管理。这篇文章会把我的设计思路、架构选型、接入方式、踩坑记录和最终日常用法完整写出来。如果你也在用这两个 CLI 工具,或者想搭一套"换项目不换脑子"的持久化 AI 编码环境,可以直接照着我这套方案改。
1. 为什么我坚持在终端 CLI 外面再加一层"持久化工作区"
1.1 终端里跑 AI 编码的三个爽点和三个坑
Claude Code 和 Codex 这类工具能在短时间内流行起来,原因很直接。它们直接跑在文件系统之上,能真正读懂项目结构,修改代码、执行命令、跑测试都是真刀真枪地干,不像传统 AI 聊天窗口只能给建议。交互密度也高,你可以一边看 diff 一边让它继续修,整个流程是连续的、agent 式的,而不是一问一答的碎片对话。再加上 MCP、Agent Skills 这些生态能力,工具链能长得非常深。
但这些好处背后有三个绕不开的痛点。
第一个痛点是会话太脆。终端进程一旦被关闭、断网、或者电脑重启,当前上下文基本就丢了。下次打开要重新初始化,CLI 里的/memory或者--resume能帮上一点忙,但只针对它自己记录过的会话,跨设备、跨场景就不灵了。
第二个痛点是项目上下文靠手动粘贴。新拉一个仓库,你要重新说明技术栈、目录结构、编码规范、哪些文件不能动,这些对话成本累积起来相当可观。团队里如果好几个人都在用,每个人都要重复做一遍同样的事。
第三个痛点是模型配置散落。Claude Code 用 Anthropic 的模型,Codex 走 OpenAI 系,想在两者之间切换,或者给 Codex 接 DeepSeek、给 Claude Code 接本地模型,环境变量、配置文件、API 端点到处都要动,很容易出问题。
我的判断是:与其等官方把持久化做到完美,不如自己在外面加一层"外部记忆系统"。CLI 工具负责干活,工作区负责记住一切。
1.2 持久化到底要存些什么东西
很多朋友一听"持久化"就觉得是把聊天记录存下来,其实对于 AI 编码工作区来说,要存的东西远不止对话。我按使用频率和价值排了个序:
- 会话快照(transcript):每次对话的完整输入输出,包括 tool call、命令执行结果、错误信息。这是续接工作时最重要的原材料,没有它,模型无法理解上一个小时你让它做了什么。
- 项目记忆文件:Claude Code 的
CLAUDE.md、Codex 的AGENTS.md,还有各自的技能说明。这些文件是项目级记忆的核心载体,必须纳入版本管理和统一初始化流程。 - 模型路由配置:不同项目用哪个模型、哪个 API 端点、哪个 provider,这是一份需要跨项目复用的配置。
- 常用命令与 MCP 配置:构建命令、测试命令、Lint 规则、团队内部的 MCP server 列表,这些如果只在某个终端配置里出现,换个环境就找不到了。
把这些东西统一管理起来之后,"持久化"才算真正闭环。
1.3 Redis 在持久化层里扮演的角色
这里就涉及到 Redis 了。我选择 Redis 不仅仅是当缓存用,而是把它当作整个工作区的存储底座。Redis 的持久化机制大家应该不陌生,它提供 RDB 快照和 AOF 日志两种方式:RDB 是定期把内存数据整体落盘,恢复快、文件紧凑,适合做缓存和临时状态的备份;AOF 则是追加写入每一条写指令,按everysec策略最多丢一秒数据,适合保存会话这类不能丢的数据。Redis 4.0 之后还支持混合持久化,用 RDB 做基础快照、AOF 记录增量,兼顾重启速度和数据安全。
我的用法是这样的:会话历史和项目记忆这类关键数据走 AOF,everysec就够;临时性的构建输出、终端缓冲走 RDB,加上 TTL 过期,不占长期空间。这样 Redis 既承担了持久化存储的角色,又保留了缓存层的高性能特性,一举两得。
2. Easy Web Vibecoding 的架构设计与关键选型逻辑
2.1 整体分层:浏览器终端、本地网关、CLI 适配器、Redis
整个工作区的架构可以用一张表说清楚:
| 层级 | 组件 | 职责 |
|---|---|---|
| Web 前端 | React + Vite,终端模拟用 xterm.js | 提供浏览器里的终端界面、会话列表面板、配置编辑面板 |
| 本地 API 网关 | Node.js + Express | 接收前端请求,做鉴权、路由、端点适配,读写 Redis |
| CLI 适配器 | child_process 调用claude/codexCLI | 把前端指令翻译成 CLI 调用,把 CLI 输出流式回传 |
| 持久化层 | Redis(AOF + RDB) | 存会话、配置、模板、命令历史,带 TTL 策略 |
选这个结构的原因很实际。Web 前端的好处是终端不再绑定某个桌面会话,浏览器开着就能继续,而且可以随时从任意设备接进来(局域网内)。xterm.js 是模拟终端体验最成熟的开源方案,Claude Code 和 Codex 跑在 Pty 伪终端里输出彩色日志,它能完整还原。本地网关则负责把前端的 HTTP/WebSocket 请求转成 CLI 进程可以理解的操作,并把流式输出推回浏览器。
这里有个设计取舍想特别说一下:为什么用"网关 + CLI 适配器"而不是直接把 API 调用写进后端才算深度集成?因为 CLI 工具本身会维护很多内部状态——比如上下文压缩、tool 调用规划、权限确认——直接用 API 重写这些东西工作量巨大且容易和官方行为产生偏差。包装 CLI 是风险最低、收益最快的方案,等官方 API 稳定了我再考虑深度融合。
2.2 为什么选 Redis 做持久化,而不是简单写文件
我知道会有人说:这不就是个状态管理吗,写 JSON 文件不就行了?我一开始确实是写文件的,把每次会话存成一个.json,放在~/.easy-web-vibecoding/sessions/下。但很快遇到几个实际问题。
首先是并发访问。Claude Code 跑长任务的时候,网关会同时写日志、更新会话状态、记录命令执行结果,多个写操作并发到一个 JSON 文件,要么加锁,要么接受丢数据,非常别扭。Redis 是单线程指令队列,HSET、LPUSH、ZADD这类原子操作天然处理了并发问题。
其次是结构化查询。文件方案想实现"找出上个月所有用过 DeepSeek 模型的会话"这种查询,得自己遍历所有 JSON 再写过滤逻辑。Redis 的 Hash、Sorted Set、Set 结构配合上 TTL,这种查询在毫秒级就能完成。
我实际用的数据结构大概是这样的:
# 会话主体,用 Hash 存,字段包括 transcript、model、project、status HSET claude:session:7f3a9c2 transcript "{}" model "claude-sonnet-4" project "web-app" status "active" # 按项目归档会话,用 Sorted Set 按时间排序 ZADD codex:session:web-app 1752512345 "7f3a9c2" 1752512467 "8b2e91d" # 网关实时日志,用 List 做队列,消费完自动清 RPUSH gateway:log:7f3a9c2 "tool_call: read_file src/main.rs" # 命令模板用 Hash,固化常用操作 HSET command:template frontend_build "npm run build" project "web-app" # 会话缓存数据,设置 TTL,三天后自动过期 SET cache:terminal:7f3a9c2 "..." EX 259200第三个原因是 TTL 过期策略。终端缓冲、临时构建日志这种东西,我不想手动删。Redis 的EXPIRE可以直接给 key 设过期时间,到点自动清理,省心很多。
2.3 会话恢复的核心逻辑:不是"恢复状态",而是"重放上下文"
持久化存储做好之后,最关键的机制就是会话恢复了。很多人以为会话恢复是把终端里的变量、进程状态重新拉起来,这在 CLI 工具层面基本做不到。我用的方法是重放上下文:
- 从 Redis 取出该会话的完整 transcript;
- 把 transcript 整理成一段结构化的上下文描述(包括用户目标、关键文件路径、已执行命令、最近一次报错);
- 调用 CLI 的
--resume或--continue模式,把这个上下文作为起始状态喂回模型; - 模型"回忆"起之前做了什么,再接着干活。
Claude Code 自己有--resume <id>和claude --continue命令,Codex 也有会话追踪机制。工作区要做的就是把 Redis 里的 transcript 和这些官方机制对接起来,相当于给模型递一张"前情提要"。
提示:实测经验是,transcript 里的 tool call 结果(比如
read_file的返回内容)比对话本身更有价值。恢复会话时,我会把这些 tool 结果连同对话一起塞给模型,它对新环境的理解速度会快很多。
3. 把 Claude Code 接进工作区,本地模型联调也能跑
3.1 接入方式:包装 CLI,而不是接管进程
Claude Code 的接入我选择了"包装 CLI"而非"持续托管一个 agent 进程"。原因很实际:Claude Code 本身是一个交互非常重的 agent 工具,它会自己决定调哪些工具、看哪些文件、执行哪些命令,如果我在中间强行插入一层控制,容易破坏它的决策流程。包装 CLI 的方式是:
- 前端发起任务;
- 网关启动
claude -p "任务描述" --output-format json这样的非交互模式,或者claude --resume <id>继续历史会话; - Claude Code 的流式输出通过 WebSocket 推给前端 xterm.js 渲染;
- 命令结束时,网关把整个 transcript 写入 Redis。
这种方式改动最小,升级 Claude Code 几乎不影响我的工作区逻辑,还保留了官方全部能力。
3.2 接入参数与超长上下文的实战用法
接入 Claude Code 时,环境变量是最核心的部分。推荐在工作区里用.env文件管理,而不是全局写死:
ANTHROPIC_MODEL=claude-sonnet-4-20250514 ANTHROPIC_SMALL_FAST_MODEL=claude-haiku-3-5 # 如果走第三方兼容服务,在这里配置本地端点 # ANTHROPIC_BASE_URL=http://127.0.0.1:1234/v1Claude Code 的超长上下文(官方也有最高 1M token 的能力)在会话续接场景里非常有用。我的用法是:对于大型项目,恢复会话时直接把几个核心文件——CLAUDE.md、项目 README、最近变更的模块列表——一起塞进上下文,让模型基于项目全貌而不是碎片化的印象来继续工作。这个用法在"大仓库 + 长会话"的组合里尤其出效果。
3.3 接 LM Studio 本地模型,零成本联调
Claude Code 吸引人的另一个点是可以接本地模型。我机器上装了 LM Studio,它的本地推理服务暴露 OpenAI 兼容的 API,而 Claude Code 支持通过ANTHROPIC_BASE_URL指向任意兼容端点。配置非常简单:
ANTHROPIC_BASE_URL=http://127.0.0.1:1234/v1 ANTHROPIC_MODEL=llama-3.2-3b-instruct把这两行写进工作区的项目配置里,再用claude -p跑一个小重构任务验证。只要 LM Studio 的模型处于加载状态,Claude Code 就会把请求发到本地端口。
注意:本地模型跑 Claude Code 的 tool calling 表现参差不齐。我实测过几个模型,有的能稳定调用
read_file、write_file,有的只敢回答不敢动手。建议先用轻量任务验证——比如让模型改一个函数名、加一条日志,确认它能正确调用工具之后再上大型重构任务。
4. Codex 接入、模型路由与多模型组合玩法
4.1 Codex CLI 接入工作区的两种姿态
Codex 的接入方式和 Claude Code 类似,我试过两条路。
第一条是直接调用codex exec,让 Codex 以非交互模式执行任务。好处是官方支持得最完整,和 Codex 的云上执行、沙箱机制无缝衔接;缺点是输出格式和交互方式相对固定,想定制不容易。
第二条是把 Codex 的请求转到 OpenAI 兼容的网关再转发。Codex CLI 支持通过配置自定义 API 端点,这使得它可以接到任意 OpenAI 兼容服务上,包括本地模型和 DeepSeek 这类第三方。
两条路对比:
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
codex exec直连 | 官方功能完整,集成度高 | 定制空间小 | 日常编码任务、多文件重构 |
| 自定义端点 + 网关 | 可接任意兼容模型,统一路由 | 协议差异要处理 | 模型切换、本地模型、团队统一配置 |
4.2 给 Codex 接 DeepSeek,以及自定义端点配置
Codex 接入 DeepSeek 的热度很高,核心需求就是不想只用一个模型提供商。Codex CLI 的配置写在项目目录的.codex/config.toml里,类似这样:
model = "deepseek-chat" model_provider = "deepseek" [model_providers.deepseek] name = "DeepSeek" base_url = "https://api.deepseek.com/v1" env_key = "DEEPSEEK_API_KEY"这样配置完成之后,Codex 调用时就会自动使用 DeepSeek 的模型。要注意的地方是:不是所有 OpenAI 兼容端点都完整支持 Codex 所需的工具调用协议。DeepSeek 官方 API 对 OpenAI 兼容性做得不错,但社区里一些自建端点经常缺少函数调用能力,导致 Codex 明明拿到了任务却无法操作文件。所以接第三方模型时,我建议先用codex exec "看看当前目录结构并告诉我"这种需要实际调用文件工具的任务验证一下。
在我的工作区里,每个项目都有独立的.codex/config.toml,由网关统一管理和分发。这样换项目时不用手动改 Codex 的全局配置,项目自带一套路由规则。
4.3 工作区路由策略:什么活交给 Claude Code,什么活交给 Codex
两个工具各有擅长,我平时会按任务类型做路由:
- 大型重构、跨模块改动、前端交互复杂的任务→ Claude Code。它对长上下文理解更好,规划能力更强,适合动辄几十个文件的改动。
- 批量脚本、单文件修改、正则替换、日志排查类任务→ Codex。响应更快,执行链更短,适合小步快跑。
- 本地安全敏感操作(比如改配置、处理密钥) → 统一走本地模型端点,不让敏感数据出机器。
工作区的模型选择器只需要在 Redis 里改一个路由配置,前端下拉框选择即可,Claude Code 和 Codex 两条通道互不干扰。
5. 排坑实录:local proxy failed while handling codex endpoint /responses 的完整排查链路
5.1 报错出现的现场
有一天我在工作区里切换 Codex 的模型供应商时,终端里突然弹出一段错误日志,大致内容是:
cc switch local proxy failed while handling codex endpoint /responses. provi...日志后面被截断了,但关键信息已经足够明确:本地 API 网关在转发 Codex 的/responses请求时失败了。这个错误之所以值得记录,是因为它很典型——Codex 的请求走向和 Claude Code 完全不同,一旦网关没有做好端点适配,就会出现这种一眼看不到底的错误。
5.2 逐步排查:从端口、路径到协议头
我当时的排查链路是这样的,每一步都有验证方法,你也可以照着走一遍。
第一步:确认本地服务确实在监听。先用ss -lntp | grep 端口号看网关有没有起来,发现监听正常,排除"服务没启动"这种低级错误。
第二步:直接用 curl 打网关背后的上游模型服务。这一步是为了确认问题究竟出在网关,还是上游模型服务。我执行了:
curl -i http://127.0.0.1:1234/v1/responses \ -H "Content-Type: application/json" \ -d '{"model":"llama-3.2-3b-instruct","input":"ping"}'结果上游服务返回了 404。到这一步已经能确认:不是鉴权问题,是路径问题。上游模型服务(比如 LM Studio)实现的是 OpenAI 的 Chat Completions 协议,路径是/v1/chat/completions,并不存在/v1/responses这个端点。而 Codex CLI 默认请求的是 Responses API 的/responses端点。网关在中间做转发时,原封不动地把/responses路径透传给了上游服务,于是上游直接 404。
第三步:在网关层做端点适配。修复方案是在网关里把/responses请求改写成/chat/completions,同时保留流式参数stream: true。改完再用同样的 curl 验证,上游服务已经能正常响应。
第四步:检查鉴权头是否透传。路径问题解决之后,又冒出一个 401 错误。原因是网关在改写请求时把上游要求的Authorization头弄丢了。修复方式是在网关的转发逻辑里显式把前端请求的鉴权头、模型名透传给上游。
第五步:验证完整链路。在网关日志里能看到一次完整的请求链路记录:前端 → 网关 → 上游模型 → 回传 → 前端渲染,整个流程 2xx 稳定通过。
5.3 "auth token is unavailable" 等其他高频报错速查
排查过程中顺带遇到另一个常见错误:codex auth token is unavailable。这类报错几乎都和 token 注入位置有关,检查顺序很固定:
- 先
env | grep -i OPENAI看环境变量是否加载; - 再看
.codex/config.toml里env_key指向的变量名是否和.env文件一致; - 最后看工作区网关是否在处理请求时误删了鉴权头。
这三点检查完,这类错误基本都能解决。
5.4 这类"本地网关转发失败"的通用排查框架
把这次排坑经验提炼成一个通用框架,以后遇到类似的local proxy failed while handling ...报错,按顺序执行:
| 症状 | 检查点 | 验证命令/方法 |
|---|---|---|
| 转发失败 | 本地服务是否监听 | ss -lntp/netstat -an |
| 404 路径错误 | 上游端点路径是否匹配 | curl -i http://127.0.0.1:端口/路径 |
| 401 鉴权失败 | 鉴权头是否透传 | 网关日志检查请求头 |
| 超时/卡住 | 流式响应是否被正确转发 | WebSocket 实时输出是否逐行到达浏览器 |
| 格式错误 | Responses API 与 Chat Completions 协议差异 | 对比请求体字段结构 |
核心心得:这种报错百分之七八十不是 CLI 工具的问题,而是中间网关的路由、协议、鉴权没有对齐。先确认上下游各自能不能独立工作,再把两端联通,排查速度会快很多。
6. 从能用变好用:项目模板、自动初始化与团队复用
6.1 一套"项目初始化即复活"的目录模板
持久化工作区真正好用起来,靠的不是工具,而是一套标准的项目模板。我的每个项目目录长这样:
my-project/ ├── CLAUDE.md # Claude Code 项目记忆 ├── AGENTS.md # Codex 项目记忆 ├── .codex/ │ └── config.toml # Codex 模型路由配置 ├── .easy-web-vibecoding/ │ ├── workspace.yaml # 工作区配置(端口、路由、TTL) │ ├── commands.yaml # 项目常用命令模板 │ └── mcp_config.json # MCP server 列表 └── src/ └── ...这套模板的价值在于:所有记忆、路由、命令都是项目自带的,而不是散落在全局配置里。新机器上克隆仓库之后,工作区一条命令就能把所有上下文加载进 Redis。
6.2 一条命令生成项目记忆的初始化脚本
为了减少重复劳动,我写了一个初始化脚本,流程是:
- 询问项目技术栈(Node、Python、Go、嵌入式如 STM32 等);
- 根据技术栈生成
CLAUDE.md骨架,自动填入构建命令、测试命令、目录结构说明; - 复制团队统一的
.codex/config.toml模板; - 把项目元信息注册到 Redis,建立会话索引;
- 前端页面刷新即可看到这个项目出现在工作区列表里。
这个脚本是我整个工作区里投入产出比最高的一部分。原来新项目搭建上下文要 20 分钟,现在一条命令搞定,而且所有人拿到的模板是一致的,团队协作时沟通成本低了很多。
6.3 我现在的日常使用方式,以及后续想做的方向
目前我在一台常开的机器上跑这个工作区,浏览器就是我的主操作界面。白天用 Claude Code 做重活,晚上挂着的 Codex 任务会自动把结果写回 Redis,第二天打开工作区就能接着看。Claude Code 桌面版出现之后,我把它当作一个更沉浸的终端入口,但核心的状态管理和持久化仍然走我这套 Redis 工作区,因为桌面版再怎么说也做不到跨会话、跨项目的统一记忆。
后续有几个想做的方向:一是把会话库同步到 Git 仓库,实现多设备之间的记忆漫游;二是给工作区加一个"会话成本统计",每个项目跑了多少 token、花了多少钱都从 Redis 里聚合出来;三是做团队共享模板仓库,不同小组可以维护各自的CLAUDE.md和命令模板,初始化时按团队拉取。
最后说一个我踩了多次才养成的习惯:所有项目记忆文件都纳入 Git 版本管理。每次 Claude Code 或 Codex 跑完一个重要任务,我会在收尾时把会话中新增的关键决策回写到CLAUDE.md。这样做的好处是,即使 Redis 里的会话缓存因为某种原因被清掉,项目的长期记忆仍然在代码仓库里,换人、换机器、换工作区都不会丢。这套方法论本身并不依赖某个特定工具,但它和 Easy Web Vibecoding 的持久化设计搭配起来,才真正做到了"换项目不换脑子"。
如果你也在折腾 Claude Code 和 Codex,建议先从最小闭环开始:安装 Redis,写一个简单的会话存储脚本,把两个 CLI 的历史对话落盘,然后逐步加上项目模板和模型路由。你会发现,一旦外部记忆系统稳定下来,AI 编码助手的实际生产力会上一个台阶。