|
| 1 | +# codexgo 方案设计文档 |
| 2 | + |
| 3 | +> 版本:对应 codexgo v0.2.2 · 更新日期:2026-06-06 |
| 4 | +> 维护者决策记录;英文技术细节见 `DEVIATIONS.md` 与 `docs/specs/`。 |
| 5 | +
|
| 6 | +--- |
| 7 | + |
| 8 | +## 1. 产品定位 |
| 9 | + |
| 10 | +**codexgo 是一个独立产品**,而非 OpenAI Codex 的替代品: |
| 11 | + |
| 12 | +- 起点:对 OpenAI Codex CLI **0.136.0**(Rust,~935K 行)的逐字节级 Go 重写, |
| 13 | + 通过差分测试(`internal/paritytest/`,对照真实 codex 二进制)验证忠实度; |
| 14 | +- 转折(2026-06-06 维护者决策):codexgo 与原生 codex **在同一台机器上完全 |
| 15 | + 隔离共存**,并走向**多模型后端**(GLM、DeepSeek 等非 OpenAI 厂商), |
| 16 | + 不再追求 drop-in 替代; |
| 17 | +- 兼容性保留在**协议与文件格式层**(请求体、rollout JSONL、SQLite schema、 |
| 18 | + config.toml 格式),便于持续吸收上游改进。 |
| 19 | + |
| 20 | +``` |
| 21 | + ┌─ 本地身份:完全独立(第 2 节) |
| 22 | + codexgo ──────┼─ 引擎内核:逐字节移植 + 差分验证(parity 资产) |
| 23 | + └─ 模型后端:多厂商直连(第 3 节,上游不具备) |
| 24 | +``` |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## 2. 本地隔离方案 |
| 29 | + |
| 30 | +目标:**对原生 codex 的配置、记忆、凭证、环境变量零接触、零冲突。** |
| 31 | + |
| 32 | +| 维度 | 原生 codex | codexgo | |
| 33 | +|------|-----------|---------| |
| 34 | +| 可执行文件 | `/opt/homebrew/bin/codex` | `/opt/homebrew/bin/codexgo` | |
| 35 | +| 配置主目录 | `~/.codex/` | `~/.codexgo/` | |
| 36 | +| 环境变量 | `CODEX_*` | `CODEXGO_*`(读取与导出子进程两个方向全部改名) | |
| 37 | +| macOS 钥匙串 | service `codex` / `Codex MCP Credentials` | `codexgo` / `CodexGo MCP Credentials` | |
| 38 | +| 项目级目录 | `<repo>/.codex/` | `<repo>/.codexgo/`(不读 `.codex/`) | |
| 39 | +| 系统级配置 | `/etc/codex/` | `/etc/codexgo/` | |
| 40 | +| 插件清单 | `.codex-plugin/plugin.json` | `.codexgo-plugin/` 优先,`.codex-plugin/`、`.claude-plugin/` 兼容回退 | |
| 41 | + |
| 42 | +实现集中在 `internal/brand/brand.go`(身份常量单一来源)。 |
| 43 | +**线上身份**(OAuth client id、originator、User-Agent、登录端点)保持与上游 |
| 44 | +一致,保证 ChatGPT 账号登录可用;界面文案品牌化为独立的后续专项。 |
| 45 | + |
| 46 | +登录互不影响:`codexgo login` 写 `~/.codexgo/auth.json`,与原生 |
| 47 | +`~/.codex/auth.json` 完全独立;唯一注意点是两者的 OAuth 回调端口同为 |
| 48 | +1455,不要**同一时刻**执行两边的 login。 |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## 3. 多模型方案(核心差异化能力) |
| 53 | + |
| 54 | +### 3.1 背景:上游的限制 |
| 55 | + |
| 56 | +- codex 0.136 把 `wire_api = "chat"` **整体移除**(配置该值直接报迁移错误, |
| 57 | + 见上游 discussion #7782),自定义 provider 只能讲 OpenAI **Responses API** |
| 58 | + (`/v1/responses`); |
| 59 | +- 实测(2026-06-06):GLM(open.bigmodel.cn/api/paas/v4)与 DeepSeek |
| 60 | + (api.deepseek.com/v1)的 `/responses` 均返回 404,**只提供 |
| 61 | + `/chat/completions`**; |
| 62 | +- 因此原生 codex 直连这类厂商不可行,必须外挂翻译代理(LiteLLM 等)。 |
| 63 | + |
| 64 | +### 3.2 codexgo 的解法:原生 chat 协议层 |
| 65 | + |
| 66 | +上游已无 chat 实现可移植,该层为 codexgo 全新编写(v0.2.0): |
| 67 | + |
| 68 | +``` |
| 69 | + ┌──────────────────────────────────────────┐ |
| 70 | + 引擎回合循环 │ 翻译层(本方案新增) │ 第三方厂商 |
| 71 | + (协议无关) │ │ |
| 72 | + │ 发出:internal/core/client_chat.go │ |
| 73 | + 对话历史 ────────────▶│ · Responses items → chat messages │───▶ POST /chat/completions |
| 74 | + 11 个工具 ───────────▶│ · 工具 JSON-schema → chat function tools│ (GLM / DeepSeek / …) |
| 75 | + │ · developer 角色 → system │ |
| 76 | + │ · 工具结果 → role:"tool" │ |
| 77 | + │ │ |
| 78 | + ResponseEvent ◀──────│ 收回:internal/api/chat_completions.go │◀─── SSE 流 |
| 79 | + (与 Responses │ · content 增量 → OutputTextDelta │ |
| 80 | + 解析器同一事件语言) │ · reasoning_content → 推理增量 │ |
| 81 | + │ · tool_calls 增量聚合 → FunctionCall │ |
| 82 | + │ · finish → Completed{EndTurn} │ |
| 83 | + └──────────────────────────────────────────┘ |
| 84 | +``` |
| 85 | + |
| 86 | +**关键设计**:翻译层把 chat SSE 聚合成与 Responses 解析器**完全相同**的内部 |
| 87 | +事件序列,引擎(回合循环、工具执行、沙箱、TUI)对协议差异零感知——因此 |
| 88 | +第三方模型获得**完整智能体能力**。已实测 GLM 自主发起 `exec` 调用、执行 |
| 89 | +shell 命令、根据结果续轮作答。 |
| 90 | + |
| 91 | +有意不做的部分:推理参数(effort/verbosity/service-tier)无 chat 等价物, |
| 92 | +不发送;reasoning 内容流式显示但不回传历史(chat 协议不接受);多模态图片 |
| 93 | +暂不透传。 |
| 94 | + |
| 95 | +### 3.3 模型 → provider 自动路由 |
| 96 | + |
| 97 | +codexgo 扩展配置字段 `models = [...]`(上游没有;为空时不出现在序列化结果 |
| 98 | +中,保证与上游配置互通): |
| 99 | + |
| 100 | +```toml |
| 101 | +# ~/.codexgo/config.toml |
| 102 | +model = "gpt-5.5" # 默认模型,/model 选择会更新此键 |
| 103 | + |
| 104 | +[model_providers.glm] |
| 105 | +name = "GLM (Zhipu AI)" |
| 106 | +base_url = "https://open.bigmodel.cn/api/paas/v4" |
| 107 | +wire_api = "chat" |
| 108 | +experimental_bearer_token = "<key>" # key 直接放配置(0600),不进环境变量 |
| 109 | +models = ["glm-5.1"] # ← codexgo 路由扩展 |
| 110 | + |
| 111 | +[model_providers.deepseek] |
| 112 | +name = "DeepSeek" |
| 113 | +base_url = "https://api.deepseek.com/v1" |
| 114 | +wire_api = "chat" |
| 115 | +experimental_bearer_token = "<key>" |
| 116 | +models = ["deepseek-v4-pro"] |
| 117 | +``` |
| 118 | + |
| 119 | +装配时构建「模型名 → provider 工厂」路由表 |
| 120 | +(`appserver.NewModelRoutedClientFactory` + `cli.buildModelProviderRoutes`): |
| 121 | +**只改 `model` 一个键即切换后端**,`model_provider` 无需变动;未声明的模型 |
| 122 | +落回默认 provider(ChatGPT 登录 / OPENAI_API_KEY)。 |
| 123 | + |
| 124 | +新增厂商 = 在配置里加一个 `[model_providers.X]` 块,任何 OpenAI 兼容 |
| 125 | +chat API(Qwen、Moonshot、本地 vLLM/Ollama 网关……)均适用。 |
| 126 | + |
| 127 | +### 3.4 /model 选择器(TUI) |
| 128 | + |
| 129 | +- 入口:TUI 内输入 `/model`; |
| 130 | +- 列表 = 内置 OpenAI 目录(gpt-5.5 / 5.4 / 5.4-mini / 5.3-codex / 5.2) |
| 131 | + + 各 provider 的 `models` 声明,支持输入过滤,标记 `(current)`; |
| 132 | +- 选中后三件事原子发生:写回 `config.toml` 的 `model` 键(持久化)、 |
| 133 | + 以模型覆盖开启新会话(`thread/start` 带 `Model` 参数,路由立即生效)、 |
| 134 | + 底部状态栏同步; |
| 135 | +- 实现:`internal/tui/model_picker.go` + `ChatBottomPane` 的 overlay 栈。 |
| 136 | + |
| 137 | +> 教训记录:overlay 回调最初在 Update 调用栈内同步调用 |
| 138 | +> `Program.Send`(无缓冲通道),导致事件循环死锁(选中即整屏冻结)。 |
| 139 | +> 修复为回调延迟到 `tea.Cmd`(bubbletea 在事件循环外执行),由 |
| 140 | +> `TestPickerAcceptDoesNotSendSynchronously` 以真实死锁形态锁定回归。 |
| 141 | +
|
| 142 | +### 3.5 能力对比 |
| 143 | + |
| 144 | +| 能力 | 原生 codex 0.136 | codexgo v0.2.x | |
| 145 | +|------|------|------| |
| 146 | +| OpenAI 模型(ChatGPT 套餐/API key) | ✅ | ✅(同一 OAuth 链路) | |
| 147 | +| Responses API 自定义厂商 | ✅ | ✅ | |
| 148 | +| **chat-only 厂商直连(GLM/DeepSeek/…)** | ❌ 需外挂代理 | ✅ 原生 | |
| 149 | +| 第三方模型的完整智能体能力(工具/沙箱) | —(取决于代理翻译质量) | ✅ 实测 | |
| 150 | +| 模型名自动路由后端 | ❌ | ✅(`models` 扩展) | |
| 151 | +| TUI 内 /model 即时切换+持久化 | 仅 OpenAI 目录 | ✅ 含第三方模型 | |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +## 4. 版本管理与发布流程(2026-06-06 起生效) |
| 156 | + |
| 157 | +``` |
| 158 | +改动 → 升 ./VERSION(semver:功能→次版本,修复→补丁) |
| 159 | + → 本地 commit |
| 160 | + → ./scripts/deploy-local.sh(按 VERSION 构建 → /opt/homebrew/bin/codexgo) |
| 161 | + → 【维护者在本机验证】 |
| 162 | + → 通过后:git push + 打 v<VERSION> 标签 |
| 163 | +``` |
| 164 | + |
| 165 | +- `VERSION` 文件是版本唯一来源,经 ldflags 注入二进制 |
| 166 | + (`codexgo --version` 可见); |
| 167 | +- 未经验证的提交**不出本机**。 |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## 5. 质量保障 |
| 172 | + |
| 173 | +- **单元/集成测试**:协议翻译、SSE 聚合、路由表、选择器交互、配置持久化、 |
| 174 | + 死锁回归均有专项测试;全量 `go test -race ./...` 常绿 |
| 175 | + (已知例外:`internal/uds` 在本机因 sockaddr_un 路径长度限制失败, |
| 176 | + 干净树上同样存在,非回归信号); |
| 177 | +- **差分测试**:`CODEX_PARITY_BIN` 指向真实 codex 0.136 二进制, |
| 178 | + 请求体逐字节对比、TUI 首帧逐格对比(strict 等级含 SGR 属性); |
| 179 | +- **分叉登记**:所有有意偏离上游之处集中登记于 `DEVIATIONS.md` |
| 180 | + (身份隔离、wire_api chat、models 路由、/model 选择器、模拟光标等), |
| 181 | + 目标是零未解释差异。 |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## 6. 已知限制与路线 |
| 186 | + |
| 187 | +| 项 | 状态 | |
| 188 | +|----|------| |
| 189 | +| /model 选择器的推理力度(effort)选择 | 未做(上游选择器具备),按需跟进 | |
| 190 | +| TUI 斜杠命令覆盖 | 已接:/model /new /quit /clear /compact /rename /review;其余显示「not supported yet」提示,按使用优先级接线 | |
| 191 | +| 界面 "codex" 文案品牌化 | 独立专项(涉及 TUI golden 重录) | |
| 192 | +| 第三方模型多模态(图片) | chat 翻译层暂丢弃图片内容 | |
| 193 | +| chat 厂商的用量统计 | 依赖厂商在流中带 usage,缺失时容忍为空 | |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +## 7. 关键文件索引 |
| 198 | + |
| 199 | +| 文件 | 职责 | |
| 200 | +|------|------| |
| 201 | +| `internal/brand/brand.go` | 本地身份常量(目录/环境变量前缀/钥匙串/插件清单) | |
| 202 | +| `internal/api/chat_completions.go` | chat SSE 流客户端与事件聚合 | |
| 203 | +| `internal/core/client_chat.go` | Prompt → chat 请求翻译(消息/工具/角色) | |
| 204 | +| `internal/tools/chat_api.go` | 工具 spec → chat function tools | |
| 205 | +| `internal/appserver/model_client_factory.go` | wire 分支 + 模型路由工厂 | |
| 206 | +| `internal/cli/assembly.go` | 路由表构建、provider 认证链、默认模型解析 | |
| 207 | +| `internal/cli/model_picker_host.go` | 选择器条目构建 + config 持久化 | |
| 208 | +| `internal/tui/model_picker.go` | /model 选择器 UI | |
| 209 | +| `internal/tui/overlay_list.go` | 通用列表 overlay(回调延迟执行) | |
| 210 | +| `VERSION` / `scripts/deploy-local.sh` | 版本与本地部署 | |
| 211 | +| `DEVIATIONS.md` | 有意分叉登记(英文,权威) | |
0 commit comments