Claude Code 已经很好了。但我决定自己造一遍,原因有两个:
光看 Claude Code 的输入输出,搞不清内部每一层是怎么取舍的。
Agent Loop 看起来就是一个 while 循环加几个条件判断,几十行代码。 但真写下来才发现坑全在边界上:
用户中途按 ESC 打断,模型返回的三个工具调用只执行完一个, 剩下两个不补结果,下一轮调 API 直接报错; 压缩切点恰好落在 tool_use 和 tool_result 之间,历史又不合法了。
这些约束是协议层面的,框架替你兜不住,用框架的人一样会撞上。 自己写完这一遍,Agent 里每一层为什么这么设计,才算真的说清楚。
Claude Code 本质上是闭源的(即使有泄漏的开源快照,那个版本迟早过时)。 自研版本可以审计每一步行为,落地到企业内部更可控。
至于为什么不用 LangChain 之类的现成框架——我认真调研过,最后没用。 LangChain 最值钱的部分在广度上:能接很多模型、连很多数据源、搭 RAG 应用确实省事。 但我的场景很窄很深:只需要 Anthropic 和 OpenAI 两套协议、 几个内置工具加 MCP 扩展。它最强的部分恰好是我用不上的。
后来去看 Claude Code、Codex、OpenCode 这些做得比较好的产品, 发现它们也都没用 Agent 框架,Loop 都是自己写的。 说明这个选择不是我一个人这么想。
这个项目的定位不是替代 Claude Code, 而是把 Coding Agent 的每一层拆开来讲清楚。 从 ReAct 循环到协议约束,从流式解析到上下文压缩, 每一行都能解释为什么这么写。
| # | 特性 | 机制说明 | 代码位置 |
|---|---|---|---|
| 1 | 🧠 ReAct Agent 引擎 | Thought → Action → Observation 循环;批量工具调用、流式中断恢复、紧急压缩重试 | kkcode/agent.py |
| 2 | 🔌 MCP 三策略延迟加载 | EAGER / NATIVE / DISPATCH 按 schema 占窗口比例自动选择,按需展开工具定义,避免上下文窗口被工具描述挤占 |
kkcode/mcp/loading_strategy.py |
| 3 | 🗜️ 两层上下文压缩 | Layer1 幂等落盘大工具结果 + Layer2 LLM 结构化摘要;含熔断器、PTL 重试与恢复段保尾 | kkcode/context/manager.py |
| 4 | 🔒 六层权限裁决 + OS 沙箱 | Layer 0–5 分级裁定 + Linux bwrap / macOS seatbelt 内核级隔离 + Pattern 记忆减少重复确认 |
kkcode/permissions/ · kkcode/sandbox/ |
| 5 | 👥 多 Agent Team 协作 | 网状通信、文件锁邮箱、Git Worktree 隔离、Coordinator 调度并行 worker | kkcode/teams/ · kkcode/worktree/ |
| 6 | 💾 会话持久化与记忆 | JSONL 落盘断点恢复 + 长期记忆 consolidation 自动提炼 | kkcode/conversation.py · kkcode/memory/ |
设计目标是让 Agent 在长时间连续编程会话中保持上下文连贯、工具可用、执行安全 —— 而不是一次性的问答玩具。
LLM-Powered Autonomous Coding Agent Runtime —— 从用户交互层到执行环境层的完整技术栈。核心为 ReAct Agent Loop(Reason → Act → Observe),外围覆盖多 Agent 协作、MCP 动态工具加载、多层记忆、六层权限裁决与 OS 沙箱隔离。
详细机制拆解见 核心机制详解。
需要 Python ≥ 3.11 与
uv(也支持pip)。
git clone https://github.com/wcy12378/KKCode.git
cd KKCode
uv sync # 或使用: pip install -e .复制配置模板并填入你的 API Key:
cp .kkcode/config.yaml.example .kkcode/config.yaml# .kkcode/config.yaml
providers:
- name: deepseek
api_key: "sk-your-key-here" # 或使用环境变量 ${DEEPSEEK_API_KEY}
model: deepseek-chat
base_url: https://api.deepseek.com/v1
mcp_servers:
- name: context7
command: npx
args: ["-y", "@upstash/context7-mcp"]完整配置项(模型、权限模式、MCP 加载策略、沙箱开关等)见 docs/configuration.md。
kkcode # 安装后直接使用 CLI
# 或
python -m kkcode # 等价于上面- 依赖管理:推荐使用
uv(零配置 Python 包管理器),uv sync会自动创建隔离虚拟环境并锁定依赖版本;也支持传统pip install -e .。 - 配置隔离:API Key 通过
.kkcode/config.yaml配置(模板见.kkcode/config.yaml.example,已被.gitignore忽略,不会提交到仓库);也支持通过环境变量注入——优先读取环境变量,缺失时回退配置文件(见下方)。 - 状态保留:会话通过 JSONL 落盘,关闭后重新打开可从断点恢复;恢复时会自动校验对话链合法性,截断不完整的轮次。
每一篇都链接到真实代码位置,可直接 git clone 后对照阅读:
| 机制 | 文档 | 关键代码 |
|---|---|---|
| MCP 三策略延迟加载 | docs/mcp-lazy-loading.md | mcp/loading_strategy.py |
| 两层上下文压缩 | docs/context-compaction.md | context/manager.py |
| 六层权限裁决与 OS 沙箱 | docs/permissions-sandbox.md | permissions/checker.py · sandbox/ |
| 多 Agent Team 协作 | docs/team-collaboration.md | teams/ · worktree/ |
| 系统架构总览 | docs/architecture.md | — |
| 记忆系统 | docs/memory-system.md | memory/ |
KKCode 是一个学习型参考实现——目标是把终端 AI 编程助手的每一层架构走通、讲清楚, 提供一个完全透明、可审计、可修改的 Code Agent 运行时。
| 维度 | KKCode | Claude Code | Codex / OpenCode |
|---|---|---|---|
| 定位 | 学习项目 / 可审计参考实现 | 商业产品(~2000 TS 文件,专职团队) | 商业产品 |
| 架构透明度 | 每一层从零编写,无黑盒依赖 | 闭源(有泄漏快照但会过时) | 闭源 |
| LLM 支持 | Anthropic + OpenAI 兼容(DeepSeek/GLM 等),配置切换 | 硬绑 Anthropic 协议 | 各自有绑定 |
| 上下文管理 | 两层压缩(L1 落盘 / L2 摘要 + 熔断器) | 5-6 层多粒度压缩策略 | 各有方案 |
| 工具生态 | 8 内置核心工具 + MCP 扩展 | 50+ 覆盖垂直场景 | 各有方案 |
| 企业适配 | 可审计、可私有部署、可替换任意 LLM 后端 | 受限于 Anthropic 生态 | 受限于各自厂商 |
| 产品成熟度 | 学习阶段,骨架正确,细节打磨中 | 生产级(LSP/OAuth/语音/IDE Bridge) | 生产级 |
架构完全透明:权限五层怎么短路、压缩怎么分级、 多 Agent 消息怎么投递,每一行都能解释为什么这么写。
多模型原生:LLM Client 从一开始就抽象了两套协议, 实际跑过 DeepSeek、GLM 等兼容端点。Claude Code 硬绑 Anthropic,这方面反而受限。
可控性:不依赖任何闭源运行时,适合需要审计每一步行为的场景。
不在架构,在细节打磨和边界覆盖。 产品级意味着每一个异常路径都要有优雅降级, 每一个用户交互都要有合理反馈。
比如 Claude Code 的 StreamingToolExecutor 里有 sibling abort 机制—— 一个 Bash 工具报错能立刻终止同批次其他子进程; 有 file history 做文件级 checkpoint,改坏了能 undo。 这些都是真实用户踩坑之后才补上的防御层, 不是架构设计阶段能预见的。
KKCode 的架构骨架是对的,但从骨架到产品之间差的是几千个这样的细节。
| 模块 | 一句话说明 | 入口 |
|---|---|---|
| ReAct Agent 引擎 | Thought → Action → Observation 主循环;批量工具调用、流式中断恢复 | kkcode/agent.py |
| MCP 客户端 | stdio / HTTP-SSE 双模式;三策略延迟加载(EAGER/NATIVE/DISPATCH) | kkcode/mcp/ |
| 上下文压缩 | L1 大结果幂等落盘(>50K 自动 spill-to-disk)+ L2 LLM 结构化摘要;熔断器 + PTL 重试 | kkcode/context/ |
| 权限引擎 | 六层分级裁决(Layer 0–5);Pattern 记忆避免重复确认;会话级临时授权 | kkcode/permissions/ |
| OS 沙箱 | Linux bubblewrap / macOS seatbelt 内核级隔离 | kkcode/sandbox/ |
| 多 Agent 团队 | TeamManager 调度 + Worktree 隔离 + 文件锁邮箱 + Coordinator 后端 | kkcode/teams/ |
| Git Worktree | 每个 Agent 独立 checkout + 分支,并行修改互不干扰 | kkcode/worktree/ |
| 记忆系统 | 自动记忆 + consolidation 定期提炼;长期记忆跨会话持久化 | kkcode/memory/ |
| 内置工具 | ReadFile / WriteFile / Grep / Glob / Bash / Edit 等 8 个核心工具 | kkcode/tools/ |
| 会话持久化 | JSONL 落盘 + 断点恢复;对话链校验防孤儿 tool_use | kkcode/conversation.py |
| TUI 交互层 | Textual 终端界面(≥2.1);CLI 单发模式(-p) |
kkcode/app.py · kkcode/remote.py |
| Hooks 引擎 | 生命周期钩子(pre_tool / post_tool / on_compact 等) | kkcode/hooks/ |
examples/ 下每个脚本独立演示一项核心能力,配置好 config.yaml 后可直接 python examples/<name>.py 运行:
| 示例 | 演示内容 |
|---|---|
basic_chat.py |
非交互式对话:Agent 自动读/写文件、执行命令 |
mcp_lazy_loading.py |
MCP 三策略(EAGER/NATIVE/DISPATCH)决策与 Token 开销对比 |
multi_agent_team.py |
多 Agent 团队协作架构(Worktree 隔离 + 文件锁邮箱) |
context_compaction.py |
两层上下文压缩机制(熔断器 + 恢复段快照) |
运行前:
uv sync安装依赖,并在.kkcode/config.yaml配置 LLM API Key。
| 类别 | 选型 |
|---|---|
| 语言 | Python ≥ 3.11(类型注解全量 mypy 检查) |
| TUI | Textual ≥ 2.1 |
| LLM | Anthropic API / OpenAI 兼容端点(DeepSeek、GPT 等) |
| 工具协议 | MCP(Model Context Protocol,stdio + HTTP/SSE) |
| 并发隔离 | Git Worktree · 文件锁邮箱 · asyncio |
| 沙箱 | Linux bwrap (bubblewrap) · macOS seatbelt (sandbox-exec) |
| 质量门禁 | pytest · ruff · mypy · GitHub Actions |
| 指标 | 数值 |
|---|---|
| 核心代码 | 141 个 .py / ~24k 行 |
| 单元测试 | 30 个测试文件 / 670+ 用例通过 |
| 类型检查 | mypy 全量通过 |
| 风格检查 | ruff 全量通过 |
| 持续集成 | Python 3.11 / 3.12 / 3.13 矩阵 |
这不是一个一次性导出的代码快照。以下是开发过程中真实发生的事件:
我把 KKCode 放给一起学的同学和身边几个开发者用。最直接的反馈是 「真能用」带来的意外——大家平时用 Claude Code 当黑盒用, 看到一个自己人写的、能在终端里自主读文件改代码跑命令的 Agent, 第一反应是原来这东西没那么神秘。
具体的改进反馈:
| 反馈 | 来源 | 迭代结果 |
|---|---|---|
| grep 大目录,几万行输出刷爆终端 | 同学 | >50K 字符工具结果自动落盘,对话内只留预览 |
| 权限确认太烦,每个命令都要点 | 同学 | 会话级临时授权,同类操作一次确认 |
| 不知道为什么 Agent 自己停了 | 同学 | 强化可观测性反馈,补使用说明文档 |
| 多 Agent 配置复杂、文档没跟上 | 同学 | 承认是短板,持续补文档(本仓库即响应) |
最大的触动:我觉得理所当然的设计,到别人手里全是问题。 默认大家都懂 Agent Loop 怎么回事,但同学的第一个问题是 「它为什么自己停下来了」。从那以后才意识到可观测性比想象中重要得多。
Function Calling 有一个隐含硬约束:每个 tool_use 必须有且仅有一个 tool_result。 缺了或多了一个,下一轮调 API 直接报错,整个会话崩掉。
这个约束在三种场景下各触发了一次:
- 用户中断:模型返回 3 个工具调用,用户按 ESC,只执行完 1 个。 剩下 2 个孤儿 tool_use 没补结果 → 下一轮炸。
- 会话恢复:进程被 kill,JSONL 日志末尾可能只有 assistant 消息没有 tool_result。 恢复时不校验 → 加载非法历史 → 下一轮炸。
- 上下文压缩:切点恰好落在 tool_use 和 tool_result 之间。 替换后的历史不合法 → 下一轮炸。
最终统一修复:对话链校验逻辑——检测孤儿 tool_use 就截断到最近完整轮次; 压缩按轮分组保证切点永远在完整轮次之间。
用 SWE-bench-Live 数据集做了正式评测(20 道 2026 年最新 GitHub issue), 发现多轮压缩后关键信息保留率仅 1/6。排查根因: 摘要 prompt 把用户指令和工具输出等量齐观,早期用户指令被当成不重要内容丢了。 改为不对称压缩(用户消息原样保留、工具输出激进压缩)后, 信息保留率直接到 100%,代码一行没动。
| 指标 | 数值 |
|---|---|
| 核心代码 | 141 个 .py / ~24k 行 |
| 单元测试 | 30 个文件 / 670+ 用例 |
| CI 矩阵 | Python 3.11 / 3.12 / 3.13 全部通过 |
| 类型检查 | mypy 全量通过 |
| 风格检查 | ruff 全量通过 |
核心原则:设计与实现均由真实代码支撑。 上面每一条都有对应的代码、测试或 commit 可以追溯。
KKCode/
├── kkcode/ # 核心包
│ ├── agent.py # ReAct Agent 主循环
│ ├── context/ # 两层上下文压缩
│ ├── mcp/ # MCP 客户端 + 三策略延迟加载
│ ├── permissions/ # 六层权限裁决
│ ├── sandbox/ # OS 级沙箱 (bwrap/seatbelt)
│ ├── teams/ # 多 Agent 协作
│ ├── worktree/ # Git Worktree 隔离
│ ├── memory/ # 会话记忆与 consolidation
│ ├── tools/ # 内置工具 + MCP 调用
│ ├── conversation.py # 会话持久化 (JSONL 落盘 + 断点恢复)
│ └── hooks/ # 生命周期钩子
├── docs/ # 机制详解文档
├── tests/ # 单元测试
└── .github/ # CI / Issue / PR 模板
欢迎 Issue 与 PR!请先阅读 CONTRIBUTING.md 与 docs/DEVELOPMENT.md。
KKCode 是一个学习型项目——目标是把终端 AI 编程助手的每一层架构走通、讲清楚。 它不是要替代 Claude Code 或任何商业产品,而是提供一个 完全透明、可审计、可修改的参考实现。
如果你正在学习 Agent 工程、准备技术面试、 或需要在企业内部部署可控的 Coding Agent, 这个仓库的设计决策和踩坑记录可能对你有用。
Built as a showcase project · 设计与实现均由真实代码支撑


