7000 行代码手搓了一个终端 Coding Agent,支持视觉分析、Git 回滚、子代理并行
从一个 tkinter 小玩具到 14 个工具、9 层中间件管道的终端编程代理 --- ChCode 开发全记录
起因:一个 tkinter 小玩具
之前我写了一个叫 chat-agent 的 tkinter 桌面应用,用 LangChain 调 API 聊天。功能很基础,但用着用着我发现一个问题:
桌面 GUI 太重了,程序员真正需要的是终端里的 AI 助手。
于是我开始把它改成 CLI 工具,名字也从 chat-agent 变成了 ChCode --- chat-agent 遇上 code。

改着改着,功能越加越多:Git 检查点、子代理并行、视频分析、会话分叉...... 最终变成了一个 7000+ 行 Python 代码、14 个内置工具 的完整项目。
今天就把整个设计思路分享出来,希望能给你一些启发。
功能全景:这玩意能干啥?
先列一下 ChCode 的 12 大功能模块:
| 模块 | 亮点 |
|---|---|
| 模型管理 | 兼容所有 OpenAI 兼容 API,内置 ModelScope(2000次/天免费)、LongCat(5000万+ token/天免费) |
| 视觉多模态 | 支持图片+视频分析,大图自动缩放,支持 PNG/JPG + MP4 |
| 会话持久化 | SQLite 存储,LangGraph 检查点,跨会话保持 |
| Git 集成 | 编辑消息自动回滚工作目录,任意消息创建分支 |
| 人工审核 | Common(逐条审批)/ YOLO(全自动) Tab 一键切换 |
| 子代理系统 | Explore(只读搜索)/ Plan(架构设计)/ General(全能力),支持并行 |
| 技能系统 | 项目级 + 全局技能目录,中间件注入 system prompt |
| 终端 UI | Rich 渲染,流式输出,状态栏显示上下文用量 |
| 跨平台 | Windows 默认 Git Bash,回退 PowerShell;Linux/Mac 原生 bash |
| 可观测性 | LangSmith 追踪,429 限流自动禁用 |
| 上下文压缩 | 手动压缩或接近 token 上限时自动摘要,告别上下文溢出 |
| 环境隔离 | 每个项目独立 .chat/ 目录,互不干扰 |
核心架构:中间件管道
ChCode 用 LangChain 的中间件系统构建了一条 9 层处理管道,每次模型调用都会依次经过这些处理层:
- 工具错误兜底 --- 工具执行异常不会直接崩溃,而是返回错误信息让模型自行修正
- 视觉工具智能屏蔽 --- 如果当前模型本身支持多模态(如 Qwen-VL),自动屏蔽 vision 工具,改为直接把图片/视频嵌入消息
- 工具结果截断与预算控制 --- 过大的工具输出会被自动截断,同时控制每轮总 token 消耗
- 技能动态注入 --- 根据已安装的技能,动态拼装 system prompt,注入技能元数据
- 模型动态加载 --- 支持运行时切换模型,无需重建 agent
- 指数退避重试 + 备用模型切换 --- API 调用失败时按 3/10/30/60 秒间隔重试最多 4 次,全部失败后自动切换到备用模型
- 隐藏消息过滤 --- 压缩标记等内部消息在发送给模型前会被自动过滤
- 上下文裁剪 --- 当 token 数超过 10 万时,自动清理旧的工具调用结果(文件读取结果永不清理),保留最近 3 条
- 自动摘要 --- 当 token 数达到上下文窗口的 90% 时,用当前模型自动摘要历史对话,保留最近 20 条消息
最后还有一层 人工审核中间件(HITL),在 Common 模式下拦截高风险工具调用(bash、edit、write_file),等待用户确认后才执行。
这种分层设计的好处是:每一层只做一件事,新增功能只需要加一层中间件,不用动其他代码。
Git 检查点:编辑消息 = 回滚代码
这是 ChCode 最酷的功能之一。
当你通过 /messages 编辑历史消息时,ChCode 不只是修改聊天记录 ------ 它会用 Git 把工作目录回滚到那个时间点的状态。
举个例子:
你: 帮我写一个用户注册 API
AI: [写了一堆代码,创建了 5 个文件]
你: 帮我加个邮箱验证
AI: [又改了 3 个文件]
这时候你发现方向不对,用 /messages 回到第 1 条消息,编辑成:
你: 帮我写一个用户登录 API(用 JWT)
ChCode 自动:
1. git reset --hard 到第 1 条消息时的提交
2. 删除后面 4 条消息
3. 把你的新消息填入输入框
更狠的是 分叉功能(Fork) :你可以从任意消息创建一个新分支,带着那个时间点的代码状态,去探索另一条实现路径。就像 Git 的 checkout -b,但在聊天维度。
原理很简单:每次 AI 完成一轮操作,ChCode 自动执行 git add . && git commit,把提交 ID 和消息 ID 绑定在一起。编辑/分叉时,根据消息 ID 找到对应的 Git 提交,精准回滚。
子代理系统:隔离上下文,并行执行
ChCode 内置 3 种子代理:
| 代理类型 | 用途 | 权限 |
|---|---|---|
| Explore | 代码库快速搜索 | 只读,禁止创建和修改文件 |
| Plan | 架构设计、方案规划 | 只读,禁止创建和修改文件 |
| General | 全能力编程 | 完整工具集 |
为什么要只读代理?
因为 Explore 和 Plan 只需要读代码。给它们写权限是危险的 ------ 一个搜索代理不应该顺手帮你改代码。ChCode 在 system prompt 里就明确禁止了文件创建和修改操作。
并行执行
你可以同时启动多个子代理处理独立任务。终端会显示实时进度条:
Explore: 搜索 API 路由定义... 120s 剩余 [████████░░░░░░░░]
Plan: 分析数据库模型... 120s 剩余 [████████░░░░░░░░]
子代理运行在隔离上下文中,不会污染主对话。完成后只把最终报告返回给主代理。
自定义代理
你还可以在 .chat/agents/ 目录下定义自己的代理类型,配备专属工具和指令。
统一国产推理模型
国内推理模型(Qwen、DeepSeek、GLM)的 reasoning 输出字段各不相同:
| 模型 | reasoning 字段名 |
|---|---|
| Qwen | reasoning_content |
| DeepSeek | reasoning |
| GLM | thought |
| 通用 | thinking |
| 自定义 | thought_process / reasoning_text / thought_content |
ChCode 通过一个增强版的 ChatOpenAI 类,自动检测并统一这些字段 。上层代码只需要读 reasoning 就能拿到思考过程,不用关心底层是哪个模型。流式输出时也能正确累积 reasoning chunk。
终端 UI:不只是能用,还要好看
ChCode 用 Rich + prompt-toolkit 打造了一套完整的终端 UI 体验。
底部状态栏
实时显示当前模型、上下文用量、Git 检查点数、工作模式:
────────────────────────────────────────────────────────────
Qwen/Qwen3-235B-A22B 45.2K/128K 35% 普通模式 Git (12 cp) cwd: D:\Projects
魔搭今日免费额度剩余: 全局 1856/2000 │ 模型(Qwen3-235B) 98/100
流式输出
逐 token 渲染 AI 回复,推理模型的 thinking 过程用灰色斜体 Panel 展示:
┌─ Thinking ──────────────────────────────────────────────┐
│ 用户需要一个用户注册 API,我应该先检查现有的项目结构... │
│ 看到已经有 models/user.py,可以基于这个来扩展... │
└─────────────────────────────────────────────────────────┘
好的,我来帮你写用户注册 API。先看一下现有的项目结构...
工具审批界面
Common 模式下,每次工具调用都会弹出来让你确认。编辑操作会显示左右对比 diff:
[HITL] edit 修改文件: src/api/user.py
旧代码 新代码
─────────────────────────────────────────────────────────
1 def get_user(user_id: int): 1 + def create_user(username, email, pwd):
2 + """创建新用户"""
3 + hashed = hash_password(pwd)
4 + return db.create(username, email, hashed)
批准 (y) / 拒绝 (n)
左边显示被替换的原始代码,右边红色 - 行对应旧内容,绿色 + 行对应新内容,一目了然。
命令自动补全
输入 / 自动弹出下拉列表,支持模糊搜索:
/new 新会话
/history 历史会话
/model 模型管理
/messages 管理历史消息
/compress 压缩会话
/mode 切换模式
/skill 技能管理
免费模型白嫖指南
ChCode 内置了多个免费平台的快捷配置,零成本上手:
ModelScope(魔搭)
- 每天 2000 次免费模型调用
- 支持 Qwen3-235B-A22B-Thinking 等顶级模型
- 状态栏实时显示剩余额度(全局 + 每模型)
- 自动检测
ModelScopeToken环境变量
LongCat
- 每天最低 5000 万+ 免费 token
- 适合大量代码生成场景
首次运行向导
首次启动时,ChCode 会自动扫描你的环境变量,支持一键配置:
扫描到环境变量: ModelScopeToken
✓ 已自动配置 ModelScope 模型
还需要配置其他模型吗?
> OpenAI
> DeepSeek
> 跳过
跨平台 Shell 抽象
ChCode 做了一层 Shell 抽象,抹平了平台差异:
- Windows:优先使用 Git Bash,不可用时回退到 PowerShell
- Linux/Mac:使用系统 bash/zsh
所有 Shell 命令都在持久化会话中执行,cd 操作会自动同步到上层,确保工具的工作目录始终一致。
项目架构一览
chcode/
├── cli.py # Typer CLI 入口 + LangSmith 429 防护
├── chat.py # REPL 主循环、斜杠命令、HITL 审批
├── agent_setup.py # Agent 构建、中间件管道、模型重试与回退
├── config.py # 模型配置、环境变量检测、首次运行向导
├── display.py # Rich 渲染、流式输出、状态栏、子代理进度
├── prompts.py # 交互式提示(select/confirm/text/checkbox)
├── session.py # 会话管理器(SQLite + LangGraph)
├── skill_manager.py # 技能安装/删除 UI
├── vision_config.py # 视觉模型配置
├── agents/
│ ├── definitions.py # 3 种内置代理定义(Explore/Plan/General)
│ ├── loader.py # 从 .chat/agents/ 加载自定义代理
│ └── runner.py # 子代理执行引擎
└── utils/
├── tools.py # 14 个内置工具
├── shell/ # Shell 抽象层(Bash/PowerShell/CWD 追踪)
├── enhanced_chat_openai.py # 统一国产推理模型
├── git_manager.py # Git 检查点管理(commit/rollback/fork)
├── git_checker.py # Git 可用性检测
├── skill_loader.py # 技能发现与加载
├── modelscope_ratelimit.py # ModelScope API 限额实时监控
├── tool_result_pipeline.py # 工具输出截断与 token 预算
├── multimodal.py # 多模态媒体处理(图片/视频编码)
└── json_utils.py # JSON 配置文件缓存读写
写在最后
ChCode 是我写过的最大的个人 Python 项目之一。从 tkinter 小玩具到 7000+ 行的终端工具,最大的感触是:
好的架构不是设计出来的,是重构出来的。
9 层中间件管道不是一开始就想好的,而是在不断加功能的过程中,发现"这个逻辑应该独立出来"、"这个可以做成中间件",逐步演化而成。
如果你也在做类似的 AI 工具,或者对终端编程代理感兴趣,欢迎来交流:
- GitHub : github-chcode
- 欢迎 Star、Issue、PR
如果这篇文章对你有启发,点个赞再走吧,这对我真的很重要!
