
前阵子团队里来了个年轻人AI工具用得飞起需求下来半天就能给出能跑的代码Pull Request一个接一个速度比我年轻时快了好几倍。直到有一次线上出了个诡异的内存泄漏他盯着日志看了三个小时最后转过头问我“这个堆栈我见过但为什么会走到这里”那一刻我突然意识到一件事——AI可以替他写代码但没法替他长出工程能力。这不是他一个人的问题。我身边越来越多开发者正在经历同样的分裂产出速度越来越快但面对未知问题的底气越来越薄。代码是AI写的出了问题只能再把问题抛给AIAI再给一个补丁如此循环。你像是代码的快递员而不是创作者。基于这个观察我折腾了大半年开源了一个叫Code to Learn的项目思路和市面上的AI编程工具完全反着来它不帮你写代码它逼你自己把代码搞明白。1. 代码能跑能力没长我看到的断层正在加剧1.1 一个让我坐不住的现象先说个真实的例子。有个实习生用AI写了一个爬虫跑得挺顺。后来目标网站加了反爬Cookies的生成逻辑变了他直接懵了连从哪里开始查都不知道。他把报错往对话框里一贴让AI重写结果AI给的代码还是不对。为什么因为他描述不清楚问题——他不知道失败发生在请求构造阶段、登录会话维持阶段还是解析阶段说不清自己的运行环境也不知道该给AI提供哪些关键信息。这就是我所说的“断层”AI生成代码的能力在飞速增长但开发者自身定位问题、建立假设、验证假设的能力并没有跟上。你写得快不代表你理解得深。ChatGPT时代之前新手读代码、调试代码的过程本质上就是工程能力生长的过程。现在这个环节被跳过了——拿到AI的产出能跑就提交不能跑就换个提示词再来一次。1.2 工程能力到底是什么为什么它不在代码文本里很多人把“工程能力”等同于“写代码的能力”这是个误解。我在一线带项目十几年觉得真正的工程能力是这四样东西定位问题的能力系统出故障时能通过日志、监控、调用链一点点缩小范围而不是瞎猜。建立心智模型的能力拿到一个陌生代码库能快速判断数据怎么流动、模块之间怎么依赖、瓶颈最可能出现在哪。做权衡的能力知道什么时候该抽象什么时候该务实知道性能优化要投在哪里才有回报。评审和质疑的能力看别人的代码能发现隐患看AI生成的代码能识别出它“看起来对但其实不对”的地方。这些能力有一个共同点它们不在代码文本里而在决策过程里。AI生成的是一段静态的代码它没有向你展示为什么要这样设计、为什么选这个API、为什么不走那条备选路径。你拿到的只有结果没有决策树。工程能力恰恰是在复盘那棵决策树的过程中长出来的。1.3 现有的AI工具全都站在“写”这一侧现在最火的工具无外乎几类像GitHub Copilot、Cursor这类在编辑器里做补全和对话像Claude Code这类能直接操作命令行、读写文件、跑测试跑构建再就是各种基于大模型的代码生成插件针对特定语言和框架做增强。它们解决的问题高度一致——降低“把代码写出来”的门槛。这个方向当然有价值我自己的工作流里也离不开它们。但问题在于这些工具默认了一个前提用户知道自己要什么。而工程能力最关键的缺口恰恰是“不知道自己要什么、不知道怎么拆解一个模糊问题”。你让AI写代码前提是你已经有足够清晰的意图意图本身模糊的时候工具越强大产出的垃圾越多。我需要的是一个能帮我把“模糊”变成“清晰”的工具而不是一个把“清晰”变成“代码”的工具。2. Code to Learn 的设计逻辑让AI从一个写手变成陪练2.1 先说清楚这个项目不是什么很多人看到名字会以为这是又一个代码生成工具我正好借此把定位捋清楚。Code to Learn 不是代码生成器它是一个学习编排器。它的目标是把你扔进一个陌生的代码库、一个没接触过的技术概念然后通过结构化的问题、拆解任务和阶段性练习逼你自己把东西弄明白。AI在里面扮演的是陪练和教练不是代笔。这一点从名字就看得出来。不是“Learn to Code”是“Code to Learn”——用代码作为媒介去学习而不是学习了以后去写代码。反向设计的好处是你的目标不再是“让程序跑起来”而是“我真正理解了这里面的机制”。跑起来只是理解之后的副产品。2.2 核心机制四个引擎串起一条学习链路整个项目我拆成了四个模块对应学习路径上的四个关键节点拆解引擎用tree-sitter对代码库做语法级分析提取函数、类、模块之间的调用关系构建出一张“知识地图”。它不是简单的文件列表而是把代码库按依赖关系分层让学习者先看主干再看分支。追问引擎基于知识地图和代码上下文生成一串层层递进的问题。比如读一个缓存模块先问“这个缓存淘汰策略用的什么数据结构”再问“为什么选择这个数据结构而不是另一个”最后问“如果并发量再高一个数量级这个设计哪里会先顶不住”。刻意练习引擎根据当前学习的模块生成不完整的小任务要求你动笔补齐或重写一个核心函数。写完以后AI会给出参考实现并引导你对比差异。关键设计是在你提交自己的版本之前AI不提供答案。这一点和所有对话式工具的体验都不一样。复盘归档器记录你读过哪些文件、卡在哪些问题上、练习中反复出错的地方是哪里。一周以后回头看你能清楚地看到自己的学习轨迹而不是“好像学了又好像没学”。这四个引擎的编排顺序是固定的拆解先行追问跟上练习校验复盘沉淀。跳过任何一步学习效果都会打折扣——你直接看答案就回到了“AI代笔”的老路。2.3 技术栈选型为什么是Python CLI而不是IDE插件技术上我做了两个关键决策可能和不少人的预期相反。第一做成命令行工具而不是IDE插件。原因很简单IDE插件离代码太近了顺手就能让AI补全、跳转、生成代码这对学习是致命的干扰。学习需要的是“够不着答案”的轻度不适感命令行工具天然把这种距离拉开了。你用编辑器还是可以用Copilot写业务代码但进入学习模式时你和代码库之间隔着一层终端界面这层界面就是刻意设计的缓冲带。第二底层做了一层LLM接口抽象不绑定任何单一厂商。你可以用OpenAI的接口也可以用Ollama跑本地开源模型甚至接入国内各家大模型的兼容接口。我的非对称设计思路是提示词与策略是核心资产模型只是执行者。今天最强的模型和三个月后的最强模型不一样但“拆解-追问-练习-复盘”这条方法论可以持续复用。具体技术栈如下组件选型原因语言Python 3.10生态成熟LLM SDK支持最好CLI框架Typer类型友好、自动生成帮助文档代码解析tree-sitter支持几十种语言不依赖运行时环境LLM访问自研抽象层 OpenAI兼容接口支持切换云端模型和本地模型项目配置YAML人类可读可改方便自定义学习策略为什么没有用更强的AST分析工具去“理解”代码因为现阶段的目标不是替代人类做静态分析而是给学习者提供线索。tree-sitter提取的符号和调用关系足够了剩下的空白恰恰需要学习者自己去填补。填补的过程就是建立心智模型的过程。3. 本地部署和第一个学习任务照做就行3.1 环境准备与安装项目依赖不多对环境的要求很克制。实测在macOS、Ubuntu和Windows的WSL环境下都能跑通。需要提前准备好的东西有Python 3.10 或更高版本Git一个大模型接口OpenAI的API Key或者本地跑一个Ollama实例都行安装有两种方式。想快速体验直接通过pip装pip install code-to-learn想改源码或者贡献代码就克隆仓库手动装git clone https://github.com/your-repo/code-to-learn.git cd code-to-learn python -m venv .venv source .venv/bin/activate pip install -e .[dev]装完后命令行里就多了一个ctl命令Code to Learn的首字母缩写。第一次运行ctl --help能看到完整的使用说明。作为一个十年老后端我习惯把项目做成开箱即用所以少踩了不少配置的坑尽量把默认值都调到了比较稳的状态。3.2 配置模型接口关键一步运行ctl init会在当前目录生成一个config.yaml文件里面需要填LLM接口信息。这是我的配置文件示例llm: provider: openai_compatible base_url: http://localhost:11434/v1 api_key: ollama model: qwen2.5-coder:14b temperature: 0.3 learning: language: python depth: deep exercise_count: 3 question_level: intermediate这里有几个参数我特别解释一下。provider用的是openai_compatible因为现在大部分本地模型和云端模型都提供兼容OpenAI的HTTP接口抽象层能少写很多适配代码。temperature我调到了0.3比默认的0.7低不少——生成问题链和拆解报告的时候需要的是稳定和准确而不是发散和创造。exercise_count是每次会话生成的练习数量新手建议从2开始任务太多容易产生挫败感。如果你没有本地模型直接改成OpenAI或兼容服务就行llm: provider: openai base_url: https://api.openai.com/v1 api_key: sk-xxxx model: gpt-4o-mini3.3 初始化第一个学习任务装好依赖、配好模型就可以导入代码库了。假设我想学习一下FastAPI的内部机制把它克隆下来git clone https://github.com/fastapi/fastapi.git cd fastapi ctl import --source . --language pythonctl import会启动拆解引擎对整个仓库做语法分析。这一步是纯本地计算不消耗token速度很快。跑完后会生成一个.ctl/knowledge_graph.json文件里面是模块依赖关系、核心函数列表、调用链摘要。接下来是关键命令ctl plan这条命令会把知识地图发给LLM生成一份定制化的学习计划输出内容包括模块阅读顺序建议从哪个文件开始读优先级是怎么排的每个模块的目标问题读这个模块时需要回答的核心问题关联扩展这个模块和项目内其他部分的逻辑关联点学习计划会输出在终端同时保存到.ctl/learning_plan.md。我建议你打开这个文件仔细看一遍它和网上的框架教程很不一样——教程是按作者的主观顺序写的这份计划是按你本地这份代码的真实依赖关系生成的针对性完全不是一个量级。3.4 追问与练习的实际输出顺着学习计划读代码的过程中随时可以运行ctl question --scope routing系统会根据你当前读到的范围生成3到5个问题。实测下来问题和代码的贴合度很高不是那种泛泛的“什么是路由”式的问题而是“在fastapi/routing.py这个文件里APIRoute对象是在哪个阶段把请求参数绑定到函数参数上的”。这种问题只有真正读过代码才能答上来。当你觉得对某个模块的理解差不多了运行ctl exercise --module routing --count 3系统会给你三个任务比如“不参考源码重写APIRoute的请求参数解析逻辑”。在写完之前AI不会给出参考实现。提交后ctl review --file my_answer.py它会对比你的实现和官方实现输出差异分析包括设计思路的不同、你遗漏的边界条件、以及官方这样写的历史原因。这里我不依赖LLM对代码的凭空理解而是让它结合知识地图里提取的上下文来做对比所以反馈一般都比较具体。4. 实战复盘我用它啃下一个没有文档的遗留系统4.1 场景背景接了个“传家宝”上个月我需要在一个内部老系统里加一个新功能。这是一个Java的定时任务调度服务代码量大概二十万行没有单元测试没有设计文档连注释都写得极其克制。上一任维护者已经离职两年了。放在平时我会打开IDE全局搜索关键类名然后凭借经验硬啃通常需要两三周才能摸得比较透彻。这次我决定拿它当作Code to Learn的第一个真实压力测试。因为项目比较老Java版本还停留在8我开始还担心tree-sitter解析会有问题实测下来对老版本语法的兼容做得不错只是有几个特殊的内部注解需要手工补充到解析规则里。4.2 我是怎么逐步摸清架构的先说结论整体花了一周比预计快了一半而且理解的深度明显强于之前那种“硬啃法”。整个流程分四步。第一步ctl import生成知识地图。因为这个项目跨了三个Maven模块我先对整个仓库做了一次拆解。输出显示核心调度逻辑集中在scheduler-core模块的engine包下其中TaskExecutor.java的出度最高——大量类都依赖它。这是判断代码库核心的第一个信号。第二步ctl plan生成阅读路线。系统给的建议是先读TaskExecutor再读它的两个核心抽象类再读实现类然后是任务的持久化和恢复逻辑。这个顺序和我过去“从入口往内走”的习惯不一样它是按依赖关系的逆向来排的好处是每读一个新文件你都能在已有心智模型上做增量扩展不会出现读到一半发现前面全理解错了的返工。第三步顺着追问引擎的问题链逐个击破。我记得比较清楚的一个问题是“任务执行节点宕机后恢复逻辑是如何保证任务不被重复执行的”这个问题直接带我找到了TaskRecover类的核心方法里面用了数据库行锁配合状态机流转逻辑相当精巧。如果没有这个问题引导我可能在业务代码里瞎转大半天也未必能想到注释这么少的地方藏着整个系统最关键的机制。第四步用练习引擎检验理解。我让它生成一个“手写任务状态流转”的练习结果我的实现和官方实现有一个关键差异我忘了处理“任务在恢复过程中再次被调度器命中”的并发场景。这一步挖出了一个真实的知识盲区比任何代码评审都高效因为评审是人给你挑错这里是你自己用自己的手写实现和二十万行沉淀下来的工业级实现对比差距一目了然。4.3 过程中的避坑心得整体用下来比较稳但也踩了几个坑值得分享。第一个坑是项目太大别整个导入。我第一次尝试导入包含前端代码在内的整个仓库知识地图出现大量与后端调度无关的噪音LLM的分析也被带偏了。后来我把后端代码单独拆出来问题立刻改善。工具本身不限制导入大小但使用上的明智策略是让上下文聚焦。第二个坑是LLM偶尔会一本正经地编造文件和行号。尤其在分析不常见的框架时它会把网络上类似的通用模式套进来。后来我在提示词策略里强制要求“所有引用的符号名必须在知识地图中真实存在否则标注未验证”幻觉率才明显下降。所以如果你想二次开发建议在提示词设计上多花精力。第三个坑和工具无关是学习节奏的问题。一开始我忍不住用Code to Learn把所有问题都问一遍反而陷入了信息过载。后来调整成“读四十分钟代码再用十分钟生成问题和练习”节奏合理很多。工具再强学习这件事还是得遵循基本规律间隔重复才记得住。5. 和主流AI编程工具的区别以及我现在的搭配方案5.1 从四个维度硬碰硬对比我用一张表说清楚 Code to Learn 和主流AI编程工具的差异。不拉踩每种工具都有自己的生态位但定位确实不同。工具核心目标提问方交付物知识归属GitHub Copilot补全代码使用者代码片段AI的上下文Cursor对话式编程使用者文件级修改AI的上下文Claude Code多步任务代理使用者完成的任务AI的上下文Code to Learn引导理解代码工具反问你学习路径和练习你的长期记忆关键差异在最后两列。前三种工具你在对话框里输入需求AI给出结果整个过程中你是需求方AI是执行方知识停留在AI的上下文窗口里关掉窗口就归零。Code to Learn把这个关系翻了过来AI向你提问你来回答AI只做验证和引导知识最后存在你脑子里学完就是你的谁也拿不走。5.2 我个人的搭配方案我现在的工作流是两套并行。日常写业务代码需求明确设计想清楚了我照样用AI辅助生成——这是合理的时间投资没必要和工具对着干。但一旦涉及三类任务我会强制切换到Code to Learn的模式接手没做过的技术栈或遗留系统需要理解一个复杂开源项目的内部机制带新人希望他们建立起独立解决问题的习惯有个细节我说一下我团队里带的新人有一个之前Copilot用得很溜的我说你试用一下Code to Learn写那个Redis工具类他现在能对着源码讲清楚Redis客户端连接池的初始化流程和异常分支处理。这在以前是不可想象的——过去他只会“让AI生成一个连接池工具类”。这就是我坚持做这个项目的根本动力不是让AI成为能力的天花板而是让AI成为能力的垫脚石。6. 开源路线图为什么做开源以及下一步计划6.1 选择开源的核心理由我自己在技术路上的成长很大程度是靠阅读开源项目的源码撑起来的。从早期看Linux内核子系统的实现到后来研究各类中间件每一个难啃的源码项目都让我的工程判断力跨一个台阶。GitHub上开源的不只是代码还有无数前辈踩过的坑和积攒下来的设计智慧。Code to Learn 要做的事本质上就是帮人更高效地吸收这些智慧如果把它做成一个闭源商业工具就有点背离初衷了。所以我选择了MIT协议。你可以随便改、随便用、商用也行只要保留原版权声明。哪怕你只是把它当个代码库学习的工具用我也很欢迎。一个学习工具应该尽可能少地没门槛代码里不应该有墙。6.2 目前已支持的能力和下一步规划当前版本已经支持通用代码库导入与知识地图构建支持Python、Java、JavaScript、Go等主流语言基于LLM的学习计划生成分层追问机制基础概念层、机制原理层、系统设计层练习生成与差异对比学习轨迹记录与复盘报告下一步有三个方向也是社区呼声比较高的IDE只读模式的实验是一个折中方案允许在IDE里查看学习指引但禁止补全和自动生成保持“够不着”的张力项目级学习数据的可视化把学习轨迹渲染成图直观看出哪些模块熟练度提升了、哪些还在徘徊更多学习模板复制“如何读懂一个Web框架”“如何啃下一个消息队列源码”这类场景化的学习策略社区共同维护如果你想参与贡献可以从这些问题入手某个语言的tree-sitter语法规则适配、某个大模型接口兼容性问题、提示词策略的中英文版本优化。GitHub仓库的issue里有标注good first issue的标签适合对照着练手。也可以先做测试、写文档、提bug开源社区的贡献方式从来不只有写代码这一种。6.3 写给看到这里的人最后说点个人的体会吧。在过去的职业经历里我见过最快的成长路径永远是同一个大量读代码、大量写代码、出了故障冷静地排查到底。这个路径在AI时代没有变变的只是其中“写代码”的部分有了替代品。但“读代码”和“排查到底”这两件事到目前为止没有哪个AI能替你完成。Code to Learn 的价值就在于把这个不可替代的部分拆成一套可执行的流程。它不是银弹它不会在你身上发生“装上就变强”的魔法。它给你的是一面墙、一根绳子和一个教练爬还是要你自己爬。但爬过一次以后那座墙就永远是你的了。这也是我把它开源出来的原因——希望更多人能体验到这种“长在自己身上”的感觉。