環境可移植性改善方案 — 架構設計
Author: architect
Date: 2026-02-27
Status: 設計完成,待 CTO 審批
Priority: P0-P3(分階段實施)
一、問題分析
1.1 現狀評估
| 指標 | 我們的系統 | PicoClaw(對照組) |
|---|---|---|
| 首次部署時間 | 30-60 分鐘 | 不到 1 分鐘 |
| 依賴項數量 | Node.js + 三次 npm install + Claude CLI + .env | Single binary + config |
| 硬編碼路徑 | 3 處 WSL 專屬路徑 | 0 |
| 初始化腳本 | 無 | 內建 init |
| 容器化 | 無 | Dockerfile 內建 |
1.2 五大短板驗證結果
經過原始碼全面審查,CTO 的分析結論全部正確,但影響範圍有部分修正:
P0:硬編碼路徑(確認 — 影響範圍精確)
受影響檔案:共 2 個檔案、3 處硬編碼
| 檔案 | 行號 | 硬編碼值 | 用途 |
|---|---|---|---|
| src/agents/worktree-manager.ts | L19 | /mnt/d/gitcode/mybotteam | Git 操作的 PROJECT_ROOT |
| src/agents/worktree-manager.ts | L20 | /home/arc/worktrees | Worktree 基礎目錄 |
| .mcp.json | L19 | /mnt/d/gitcode/mybotteam/blog | Hexo MCP server 目錄 |
正面發現:其餘所有 src/ 模組一致使用 process.cwd() + join() — 這是良好的設計基礎。
P1:無初始化腳本(確認 — 比預期更嚴重)
啟動門檻:soul/genesis.md 不存在會導致 process.exit(1)(src/index.ts L38-45)
需手動建立的結構:
- 核心:genesis.md、identity.json、vitals.json、milestones.json
- Agent configs:21 個 JSON 檔(soul/agents/)
- Skills:28 個 MD 檔(soul/skills/)
- 事件流:narrative.jsonl、reflections.jsonl、diary.jsonl、dreams.jsonl
- 演化系統:evolution/capabilities.json、evolution/goals.json 等
- 任務系統:agent-tasks/queue.json、agent-tasks/history.jsonl
- 20+ 子目錄
自動建立的(無需手動):soul/memory/ 由 initMemoryDir() 啟動時自動建立
P2:三重 npm install(確認)
三個獨立 package.json:
- ./package.json(root — 51 個依賴)
- ./blog/package.json(blog — 15 個依賴)
- ./report/package.json(report — 12 個依賴)
無 npm workspaces、無 postinstall hook。
P2:缺少容器化(確認)
無 Dockerfile、無 docker-compose.yml。
P3:Claude CLI 硬依賴(確認 — 但抽象良好)
所有 LLM 呼叫集中在 askClaudeCode()(src/claude/claude-code.ts),這是單一入口點。用 spawn(‘claude’, args) 方式呼叫。Session 管理通過 session-store.ts。預飛檢查通過 preflight.ts。替換成本可控,因為入口點唯一。
二、方案設計
設計原則
- 向後相容第一 — 現有 WSL 環境必須零中斷
- 最小侵入 — 每個 phase 獨立可交付,不互相依賴
- env var + 智慧預設 — 新機器用 env var 配置,舊機器用 fallback 維持不動
- 不過度設計 — 解決當前問題,不為假想未來設計
Phase 1:P0 硬編碼修復 + P1 初始化腳本(高優先、低風險)
預估工時:1-2 個 agent task
影響範圍:2 個原始碼檔案 + 1 個新腳本
1A. 修復 worktree-manager.ts 硬編碼
變更範圍:src/agents/worktree-manager.ts L19-20
Before:
1 | const PROJECT_ROOT = '/mnt/d/gitcode/mybotteam'; |
After:
1 | import { homedir } from 'node:os'; |
技術細節:
- 引入 homedir() from node:os
- PROJECT_ROOT fallback 到 process.cwd() — 因為 bot 永遠從專案根目錄啟動
- WORKTREE_BASE fallback 到 ~/worktrees — 比硬編碼 /home/arc 更通用
- 保留 _constants export 讓測試可以驗證實際值
- 在 .env.example 新增 PROJECT_ROOT 和 WORKTREE_BASE(有註解說明 optional)
向後相容分析:
- Arc 的 WSL 環境:如果不設 env var,process.cwd() = /mnt/d/gitcode/mybotteam(跟原本一樣)
- WORKTREE_BASE 需要 Arc 在 .env 明確設定 WORKTREE_BASE=/home/arc/worktrees(或接受新預設 ~/worktrees)
- 風險評估:WSL2 的 homedir() 回傳 /home/arc,所以 ~/worktrees = /home/arc/worktrees,完全相容
影響的模組:
- createTaskWorktree() — 使用 PROJECT_ROOT 和 WORKTREE_BASE
- execGit() — 使用 PROJECT_ROOT 作為 cwd
- listActiveWorktrees() — 使用 WORKTREE_BASE
- 所有函數的行為不變,只是常數來源改變
1B. 修復 .mcp.json 硬編碼
變更範圍:.mcp.json L19
Before:
1 | "HEXO_DIR": "/mnt/d/gitcode/mybotteam/blog" |
After(方案 A — 推薦):
1 | "HEXO_DIR": "${HEXO_DIR}" |
配合 .env 新增:
1 | HEXO_DIR=/mnt/d/gitcode/mybotteam/blog |
技術考量:
- .mcp.json 的 env var interpolation 取決於 Claude CLI 是否支援 ${} 語法
- 替代方案 B:改 MCP server 端讀取 process.cwd()。但 hexo MCP server 是外部 npm 套件,不適合改。
- 替代方案 C:將 .mcp.json 改為 .mcp.json.template + 生成腳本。增加複雜度。
- 決策:先測試方案 A。如果 Claude CLI 不支援 env interpolation,fallback 到方案 C(setup script 生成 .mcp.json)。
1C. 建立 scripts/init-soul.ts 初始化腳本
新增檔案:scripts/init-soul.ts
新增 npm script:setup: tsx scripts/init-soul.ts
功能規格:
1 | npm run setup # 交互模式 |
三個模式:
模式 1:全新安裝(soul/ 不存在或為空)
- 建立所有必要目錄(20+ subdirs)
- 生成 genesis.md(預填 Chapter 0 框架,留白讓使用者編輯)
- 生成最小 identity.json(name: “New Bot”, 空 traits)
- 生成空的 vitals.json、milestones.json
- 生成 4 個核心 agent configs(programmer, architect, reviewer, secretary)— 使用內嵌模板
- 建立空的事件流檔案(.jsonl 們)
- 建立 agent-tasks/queue.json(空 queue)
- 執行 Identity Fingerprint 初始化
模式 2:遷移模式(soul/genesis.md 已存在)
- 檢查目錄完整性:列出缺少的子目錄和檔案
- 補建缺失的目錄
- 不覆蓋任何已存在的檔案
- 回報修復了什麼
模式 3:校驗模式(–check)
- 檢查所有必要檔案和目錄是否存在
- 驗證 JSON 檔案格式正確
- 驗證 Identity Fingerprint 一致性
- 輸出健康報告
- Exit code: 0 = 健康, 1 = 有問題
Soul 目錄結構定義(內嵌在腳本中):
1 | const SOUL_DIRS = [ |
核心檔案定義:
1 | const CORE_FILES = { |
空 JSONL 事件流:
1 | const EMPTY_JSONL = [ |
1D. 更新 .env.example
新增以下 env vars(均為 optional):
1 | # Path configuration (optional — defaults use process.cwd()) |
Phase 2:P2 依賴安裝簡化 + 開發體驗改善(中優先)
預估工時:1 個 agent task
影響範圍:package.json + 新增 2 個腳本
2A. 新增 postinstall 腳本
變更範圍:package.json scripts
方案:在 root package.json 新增 postinstall hook,自動安裝子專案依賴。
1 | { |
scripts/install-all.js(純 Node.js,不依賴 tsx — 因為 postinstall 時 tsx 可能還沒裝好):
1 | // scripts/install-all.js — Install dependencies for sub-projects |
為什麼不用 npm workspaces:
- blog/ 和 report/ 是 Hexo 專案,有自己的 Hexo 版本需求
- 依賴樹差異太大(bot 用 grammy/zod,blog 用 hexo 全家桶)
- Workspace hoisting 可能導致 Hexo 插件找不到依賴
- 成本效益不合:改 workspaces 要重寫 blog/report 的 scripts,收益很小
- 結論:postinstall hook 是最小侵入方案
2B. 環境健康檢查工具
新增 npm script:doctor: tsx scripts/doctor.ts
功能示例輸出:
1 | $ npm run doctor |
Phase 3:P2 容器化 + P3 LLM 抽象層(低優先、高複雜度)
預估工時:多個 agent task
影響範圍:新增檔案為主,最小現有程式碼變更
3A. 部分容器化(blog/report 建構環境)
策略:不容器化 bot 本體(Claude CLI OAuth 阻礙),只容器化無狀態的建構任務。
新增檔案:docker/blog-builder.Dockerfile、docker-compose.yml
1 | # docker-compose.yml |
使用場景:
- CI/CD 中的 blog 建構 — 不依賴本地 Node.js 版本
- 新開發者只需 Docker 即可建構 blog,無需安裝 Hexo 全套
不容器化 bot 本體的原因:
- Claude CLI 需要互動式 OAuth 認證(目前無法在 Docker 中完成)
- Bot 需要存取 soul/ 持久化狀態(volume mount 可解決但增加複雜度)
- .env 中的 Telegram token 等秘密需要 Docker secrets 管理
- 結論:等 Anthropic 提供 headless auth 方案後再評估
3B. LLM 呼叫抽象層(長期方向)
目標:讓系統能同時支援 Claude CLI 和 API 直連模式。
現狀分析:
- askClaudeCode() 是唯一入口點 — 非常好的起點
- 用 CLI 模式的原因:工具審批機制、session 管理、MCP 整合
- 用 API 模式的優點:無需 CLI 安裝、Docker 友好、更快的啟動
設計方向(概念層級):
1 | // src/claude/llm-provider.ts — 未來的抽象介面 |
路由邏輯:
- 需要工具使用 → CLI provider
- 純文字生成(日記、摘要、分類)→ SDK provider(更快)
- CLI 不可用 → fallback 到 SDK provider
Phase 3B 不急的原因:
- 現有系統 100% 依賴 Claude CLI 且運作穩定
- Anthropic SDK 的 tool use API 還在快速演進中
- 工具審批機制是安全架構的核心,不宜輕易繞過
- 建議:等 Claude CLI 支援 headless auth 或 API key 認證後再啟動
三、實作路線圖
1 | Week 1 Phase 1A + 1B(硬編碼修復) |
四、風險評估
Phase 1 風險
| 風險 | 機率 | 影響 | 緩解措施 |
|---|---|---|---|
| process.cwd() 在 worktree 內不是專案根目錄 | 低 | 高 | Worktree agent 永遠從根目錄啟動,且有 symlink 到 soul/、node_modules/ |
| .mcp.json 不支援 env var interpolation | 中 | 中 | 實測 Claude CLI 行為;如不支援則改用 setup 腳本生成 |
| init-soul 覆蓋使用者已編輯的 genesis.md | 低 | 極高 | 遷移模式永不覆蓋已存在檔案 |
| homedir() 在某些環境回傳異常值 | 低 | 低 | env var 有明確覆蓋路徑 |
Phase 2 風險
| 風險 | 機率 | 影響 | 緩解措施 |
|---|---|---|---|
| postinstall 在 CI 中重複執行浪費時間 | 中 | 低 | 檢查 node_modules 已存在就跳過 |
| postinstall 在 npm ci 時也觸發 | 中 | 低 | npm ci 本來就該裝全部依賴,行為正確 |
Phase 3 風險
| 風險 | 機率 | 影響 | 緩解措施 |
|---|---|---|---|
| Docker volume mount 造成 soul/ 權限問題 | 中 | 中 | 只容器化無狀態建構,不碰 soul/ |
| LLM 抽象層引入 regression | 中 | 高 | 暫不執行,等 CLI headless auth |
五、改善後的預期效果
首次部署流程(改善後)
1 | # 1. Clone repo |
預估時間:5-10 分鐘(vs 現在 30-60 分鐘)
可移植性改善
| 指標 | Before | After Phase 1 | After Phase 2 |
|---|---|---|---|
| WSL 硬編碼 | 3 處 | 0 處 | 0 處 |
| 初始化步驟 | 手動 20+ 步 | npm run setup | npm run setup |
| npm install 次數 | 3 次手動 | 3 次手動 | 1 次自動 |
| 環境檢查 | 無 | –check 模式 | npm run doctor |
| 預估部署時間 | 30-60 min | 10-15 min | 5-10 min |
六、附錄:Agent Config 最小模板
以下是 init-soul 會生成的 4 個核心 agent 配置:
programmer.json (template):
1 | { |
architect.json (template):
1 | { |
reviewer.json (template):
1 | { |
secretary.json (template):
1 | { |
七、決策記錄
| 決策 | 選擇 | 理由 | 替代方案(已排除) |
|---|---|---|---|
| Worktree 路徑 | env var + process.cwd() fallback | 零中斷遷移 | 讀 git config(過於複雜) |
| .mcp.json | env var interpolation | 最小改動 | template 生成(增加工具鏈) |
| 子專案依賴 | postinstall hook | 最小侵入 | npm workspaces(破壞性太大) |
| 容器化 | 只容器化建構任務 | CLI OAuth 無法容器化 | 全容器化(目前不可行) |
| LLM 抽象 | 暫不實施 | 等 headless auth | 立即抽象(過度設計) |
| Soul template | 內嵌在腳本中 | 無額外目錄管理 | 獨立 template/ 目錄(過度) |
| init-soul 語言 | TypeScript (tsx) | 與專案一致 | Shell script(不跨平台) |