# cluadecodefromscratch **Repository Path**: alienity/cluadecodefromscratch ## Basic Information - **Project Name**: cluadecodefromscratch - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-15 - **Last Updated**: 2026-03-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CluaCodeFromScratch 从零开始实现一个类似 Claude Code 的 CLI Agent。 ## 快速开始 ### 步骤 1: 配置 API Key 运行后会自动创建全局配置文件 `~/.config/claudecodefromscratch/claudecodefromscratch.json` 编辑配置文件,填入 API Key。 ### 步骤 2: 运行 Agent ```bash npm run dev ``` ### 交互式命令 Agent 运行时可使用以下命令: | 命令 | 功能 | |------|------| | `/compact` | 手动压缩对话历史 | | `/tasks` | 列出所有任务 | | `/team` | 列出所有团队成员 | | `/inbox` | 查看收件箱消息 | | `q` / `exit` | 退出程序 | --- ## 核心模式 所有 AI 编程 Agent 共享同一个循环:调用模型、执行工具、回传结果。 ``` THE AGENT PATTERN ================= User --> messages[] --> LLM --> response | stop_reason == "tool_use"? / \ yes no | | execute tools return text append results loop back -----------------> messages[] That's the minimal loop. Every AI coding agent needs this loop. ``` --- ## 已实现功能 | 课程 | 标题 | 格言 | |------|------|------| | s02 | [工具](#s02-工具) | 加一个工具,只加一个 handler | | s03 | [TodoWrite](#s03-todowrite) | 没有计划的 agent 走哪算哪 | | s04 | [子 Agent](#s04-子-agent) | 大任务拆小,每个小任务干净的上下文 | | s05 | [技能](#s05-技能) | 用到什么知识,临时加载什么知识 | | s06 | [上下文压缩](#s06-上下文压缩) | 上下文总会满,要有办法腾地方 | | s07 | [任务系统](#s07-任务系统) | 大目标要拆成小任务,排好序,记在磁盘上 | | s08 | [后台任务](#s08-后台任务) | 慢操作丢后台,agent 继续想下一步 | | s09 | [Agent 团队](#s09-agent-团队) | 任务太大一个人干不完,要能分给队友 | | s10 | [团队协议](#s10-团队协议) | 队友之间要有统一的沟通规矩 | | s11 | [自主 Agent](#s11-自主-agent) | 队友自己看看板,有活就认领 | --- ## 详细实现 ### S02: 工具 > *"加一个工具,只加一个 handler"* -- 循环不用动,新工具注册进 dispatch map 就行。 ``` +--------+ +-------+ +------------------+ | User | ---> | LLM | ---> | Tool Dispatch | | prompt | | | | { | +--------+ +---+---+ | bash: run_bash | ^ | read: run_read | | | write: run_wr | +-----------+ edit: run_edit | tool_result | } | +------------------+ The dispatch map is a dict: {tool_name: handler_function}. One lookup replaces any if/elif chain. ``` **工作原理**: 1. 每个工具有一个处理函数。路径沙箱防止逃逸工作区。 2. dispatch map 将工具名映射到处理函数。 3. 循环中按名称查找处理函数。循环体本身与 s01 完全一致。 ``` 加工具 = 加 handler + 加 schema。循环永远不变。 ``` --- ### S03: TodoWrite > *"没有计划的 agent 走哪算哪"* -- 先列步骤再动手,完成率翻倍。 ``` +--------+ +-------+ +---------+ | User | ---> | LLM | ---> | Tools | | prompt | | | | + todo | +--------+ +---+---+ +----+----+ ^ | | tool_result | +----------------+ | +-----------+-----------+ | TodoManager state | | [ ] task A | | [>] task B <- doing | | [x] task C | +-----------------------+ | if rounds_since_todo >= 3: inject into tool_result ``` **工作原理**: 1. TodoManager 存储带状态的项目。同一时间只允许一个 `in_progress`。 2. `todo` 工具和其他工具一样加入 dispatch map。 3. nag reminder: 模型连续 3 轮以上不调用 `todo` 时注入提醒。 ``` "同时只能有一个 in_progress" 强制顺序聚焦。 nag reminder 制造问责压力 -- 你不更新计划,系统就追着你问。 ``` --- ### S04: 子 Agent > *"大任务拆小,每个小任务干净的上下文"* -- 子智能体用独立 messages[], 不污染主对话。 ``` Parent agent Subagent +------------------+ +------------------+ | messages=[...] | | messages=[] | <-- fresh | | dispatch | | | tool: task | ----------> | while tool_use: | | prompt="..." | | call tools | | | summary | append results | | result = "..." | <---------- | return last text | +------------------+ +------------------+ Parent context stays clean. Subagent context is discarded. ``` **工作原理**: 1. 父智能体有一个 `task` 工具。子智能体拥有除 `task` 外的所有基础工具。 2. 子智能体以 `messages=[]` 启动,运行自己的循环。 3. 只有最终文本返回给父智能体。 ``` 子智能体可能跑了 30+ 次工具调用,但整个消息历史直接丢弃。 父智能体收到的只是一段摘要文本。 ``` --- ### S05: 技能 > *"用到什么知识,临时加载什么知识"* -- 通过 tool_result 注入,不塞 system prompt。 ``` System prompt (Layer 1 -- always present): +--------------------------------------+ | You are a coding agent. | | Skills available: | | - git: Git workflow helpers | ~100 tokens/skill | - test: Testing best practices | +--------------------------------------+ When model calls load_skill("git"): +--------------------------------------+ | tool_result (Layer 2 -- on demand): | | | | Full git workflow instructions... | ~2000 tokens | Step 1: ... | | | +--------------------------------------+ ``` **工作原理**: 1. 每个技能是一个目录,包含 `SKILL.md` 文件和 YAML frontmatter。 2. SkillLoader 递归扫描 `SKILL.md` 文件,用目录名作为技能标识。 3. 第一层写入系统提示。第二层不过是 dispatch map 中的又一个工具。 ``` 模型知道有哪些技能 (便宜), 需要时再加载完整内容 (贵)。 ``` --- ### S06: 上下文压缩 > *"上下文总会满,要有办法腾地方"* -- 三层压缩策略,换来无限会话。 ``` Every turn: +------------------+ | Tool call result | +------------------+ | v [Layer 1: micro_compact] (silent, every turn) Replace tool_result > 3 turns old with "[Previous: used {tool_name}]" | v [Check: tokens > 50000?] | | no yes | | v v continue [Layer 2: auto_compact] Save transcript to .transcripts/ LLM summarizes conversation. Replace all messages with [summary]. | v [Layer 3: compact tool] Model calls compact explicitly. Same summarization as auto_compact. ``` **工作原理**: 1. **第一层 -- micro_compact**: 每次 LLM 调用前,将旧的 tool result 替换为占位符。 2. **第二层 -- auto_compact**: token 超过阈值时,保存完整对话到磁盘,让 LLM 做摘要。 3. **第三层 -- manual compact**: `compact` 工具按需触发同样的摘要机制。 ``` 完整历史通过 transcript 保存在磁盘上。 信息没有真正丢失,只是移出了活跃上下文。 ``` --- ### S07: 任务系统 > *"大目标要拆成小任务,排好序,记在磁盘上"* -- 文件持久化的任务图,为多 agent 协作打基础。 ``` .tasks/ task_1.json {"id":1, "status":"completed"} task_2.json {"id":2, "blockedBy":[1], "status":"pending"} task_3.json {"id":3, "blockedBy":[1], "status":"pending"} task_4.json {"id":4, "blockedBy":[2,3], "status":"pending"} 任务图 (DAG): +----------+ +--> | task 2 | --+ | | pending | | +----------+ +----------+ +--> +----------+ | task 1 | | task 4 | | completed| --> +----------+ +--> | blocked | +----------+ | task 3 | --+ +----------+ | pending | +----------+ 顺序:task 1 必须先完成,才能开始 2 和 3 并行:task 2 和 3 可以同时执行 依赖:task 4 要等 2 和 3 都完成 状态:pending -> in_progress -> completed ``` **工作原理**: 1. **TaskManager**: 每个任务一个 JSON 文件,CRUD + 依赖图。 2. **依赖解除**: 完成任务时,自动将其 ID 从其他任务的 `blockedBy` 中移除。 3. **状态变更 + 依赖关联**: `update` 处理状态转换和依赖边。 ``` 从 s07 起,任务图是多步工作的默认选择。 s03 的 Todo 仍可用于单次会话内的快速清单。 ``` --- ### S08: 后台任务 > *"慢操作丢后台,agent 继续想下一步"* -- 后台线程跑命令,完成后注入通知。 ``` Main thread Background thread +-----------------+ +-----------------+ | agent loop | | subprocess runs | | ... | | ... | | [LLM call] <---+------- | enqueue(result) | | ^drain queue | +-----------------+ +-----------------+ Timeline: Agent --[spawn A]--[spawn B]--[other work]---- | | v v [A runs] [B runs] (parallel) | | +-- results injected before next LLM call --+ ``` **工作原理**: 1. BackgroundManager 用线程安全的通知队列追踪任务。 2. `run()` 启动守护线程,立即返回。 3. 子进程完成后,结果进入通知队列。 4. 每次 LLM 调用前排空通知队列。 ``` 循环保持单线程。只有子进程 I/O 被并行化。 ``` --- ### S09: Agent 团队 > *"任务太大一个人干不完,要能分给队友"* -- 持久化队友 + JSONL 邮箱。 ``` Teammate lifecycle: spawn -> WORKING -> IDLE -> WORKING -> ... -> SHUTDOWN Communication: .team/ config.json <- team roster + statuses inbox/ alice.jsonl <- append-only, drain-on-read bob.jsonl lead.jsonl +--------+ send("alice","bob","...") +--------+ | alice | -----------------------------> | bob | | loop | bob.jsonl << {json_line} | loop | +--------+ +--------+ ^ | | BUS.read_inbox("alice") | +---- alice.jsonl -> read + drain ---------+ ``` **工作原理**: 1. TeammateManager 通过 config.json 维护团队名册。 2. `spawn()` 创建队友并在线程中启动 agent loop。 3. MessageBus: append-only 的 JSONL 收件箱。`send()` 追加一行; `read_inbox()` 读取全部并清空。 4. 每个队友在每次 LLM 调用前检查收件箱,将消息注入上下文。 ``` 三样东西:(1) 能跨多轮对话存活的持久智能体 (2) 身份和生命周期管理 (3) 智能体之间的通信通道 ``` --- ### S10: 团队协议 > *"队友之间要有统一的沟通规矩"* -- 一个 request-response 模式驱动所有协商。 ``` Shutdown Protocol Plan Approval Protocol ================== ====================== Lead Teammate Teammate Lead | | | | |--shutdown_req-->| |--plan_req------>| | {req_id:"abc"} | | {req_id:"xyz"} | | | | | |<--shutdown_resp-| |<--plan_resp-----| | {req_id:"abc", | | {req_id:"xyz", | | approve:true} | | approve:true} | Shared FSM: [pending] --approve--> [approved] [pending] --reject---> [rejected] Trackers: shutdown_requests = {req_id: {target, status}} plan_requests = {req_id: {from, plan, status}} ``` **工作原理**: 1. 领导生成 request_id, 通过收件箱发起关机请求。 2. 队友收到请求后,用 approve/reject 响应。 3. 计划审批遵循完全相同的模式。 ``` 一个 FSM, 两种用途。 同样的 pending -> approved | rejected 状态机可以套用到任何请求 - 响应协议上。 ``` --- ### S11: 自主 Agent > *"队友自己看看板,有活就认领"* -- 不需要领导逐个分配,自组织。 ``` Teammate lifecycle with idle cycle: +-------+ | spawn | +---+---+ | v +-------+ tool_use +-------+ | WORK | <------------- | LLM | +---+---+ +-------+ | | stop_reason != tool_use (or idle tool called) v +--------+ | IDLE | poll every 5s for up to 60s +---+----+ | +---> check inbox --> message? ----------> WORK | +---> scan .tasks/ --> unclaimed? -------> claim -> WORK | +---> 60s timeout ----------------------> SHUTDOWN ``` **工作原理**: 1. 队友循环分两个阶段:WORK 和 IDLE。LLM 停止调用工具 (或调用了 `idle`) 时,进入 IDLE。 2. 空闲阶段循环轮询收件箱和任务看板。 3. 任务看板扫描:找 pending 状态、无 owner、未被阻塞的任务。 4. 身份重注入:上下文过短 (说明发生了压缩) 时,在开头插入身份块。 ``` 真正的自治:队友自己扫描任务看板,认领没人做的任务,做完再找下一个。 ``` --- ## 项目结构 ``` cluadecodefromscratch/ ├── src/ │ └── cli.ts # 主程序入口(单文件实现) ├── .cluadecodefromscratch/ │ ├── tasks/ # 持久化任务文件 │ ├── skills/ # SKILL.md 知识库文件 │ ├── team/ # 团队配置和消息 │ └── transcripts/ # 对话压缩记录 ├── package.json └── tsconfig.json ``` --- ## 配置文件 全局配置文件位置:`~/.config/claudecodefromscratch/claudecodefromscratch.json` 配置文件会在首次运行时自动创建,支持的配置项包括: - 提供商配置(Anthropic, OpenAI, Ollama 等) - API Key 和 baseURL 设置 - 模型配置 --- ## 参考资源 - [Learn Claude Code](https://learn.shareai.run/zh/) - [GitHub 仓库](https://github.com/shareAI-lab/learn-claude-code)