尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

20亿Token写C++编译器:AI编程的Token消耗与工程控制

20亿Token写C++编译器:AI编程的Token消耗与工程控制 “I Spent 2B Tokens Writing a C Compiler So You Dont Have To”——这个标题最值得读的不是“20亿token”这个数字而是它背后真正在问的问题如果让AI从零开始写一个C编译器20亿token到底花在哪了成果能不能用下一次再碰这种大型软件项目应该怎么分配预算和任务如果你只是想要一个能编译C的工具那结论很简单直接装GCC、Clang或MSVC不要自己写。但如果你想搞清楚AI编程的边界、Token消耗的规律以及一个真正复杂的工程项目在AI辅助下会变成什么样子这件事就值得仔细拆一遍。下面按我自己的实测和工程复盘习惯把整个过程拆成几个关键部分。1. 先弄清楚一件事20亿Token到底烧在哪里1.1 Token不是“字数”是模型处理和计费的基本单位先解释一个基础概念Token令牌是模型处理文本时的基本单位。它不是严格按“字”来算的。英文里一个常见单词往往对应1到2个token标点和空格也占token中文里一个汉字可能对应1到2个token代码里的大小写符号、特殊字符、缩进换行都会增加token数量。比如下面这段非常简单的C求和代码#include iostream int main() { int sum 0; for (int i 1; i 100; i) { sum i; } std::cout sum std::endl; return 0; }这段代码在主流模型里通常会被切成几十个token而不是按“40多个字符”来算。如果把它翻译成中文注释再补充一些说明token数量还会继续增长。所以“20亿token”不等于“写了20亿行代码”而是指模型生成、读取、反复修改过程中累积消耗的token总量。这个总量会远远大于最终代码的实际体积。1.2 编译器项目为什么天然是Token黑洞编译器不是一个“写几个函数就完事”的程序它是一个完整工程体系词法分析、语法分析、语义分析、中间表示、优化、代码生成、运行时支持每个环节都要单独实现还要互相配合。如果让AI来做这样一个项目典型流程是这样的让AI生成第一批代码。编译报错。把报错信息贴给AI。AI修改再次生成。编译报错或者出现行为不符合预期。继续贴报错继续改。这个过程每循环一次都会消耗大量token。尤其是当一个模块有两三千行的时候每次修改都可能要重新读入整个文件、生成一大段代码再叠加对话历史token消耗会迅速放大。还有一个更隐蔽的问题编译器程序本身对语义正确性要求极高。普通业务代码“看起来能用”可能就够了但编译器必须对语言标准、类型系统、内存布局、ABI调用约定都处理正确。AI生成代码时经常会出现“编译通过但运行结果不对”的情况这种问题比编译报错更难排查因为你得自己去构造用例、打断点、看中间表示然后才能定位是哪一层出了问题。我自己见过类似的案例一个让AI写的表达式解析器四则运算能跑通但一加括号就乱套或者没有考虑运算符优先级乘除和加减完全混在一起。表面看是“AI能力不够”实际是因为整个项目缺少系统性的测试验证所有模块都靠“觉得正确”来推进而不是靠用例来证明正确。2. 先给结论用AI“从零写一个C编译器”到底值不值2.1 如果目标是“拥有一个编译器”不值这个判断可以很直接。GCC、Clang、MSVC都是经过大量开发者长期维护、测试覆盖极其完善的编译器。你自己用AI从零写一个C编译器哪怕真的写出来大概率也只能支持C语法的一个子集还会在模板、异常、标准库、链接等地方不断暴露问题。而且编译器不是“写完就完了”。C标准在演进工具链要适配不同平台还要考虑调试信息、优化选项、二进制兼容。这些工作量远超AI生成的代码范围。一个人用AI辅助在有限时间内很难完整复现一个可用、稳定、能处理真实工程的C编译器。所以如果目的是“我要一个能编译C的工具”这20亿token应该直接省掉。2.2 如果目标是“测试AI编程的极限”值但必须有方法如果目的是搞清楚AI能不能承担大型软件工程那这个实验本身很有价值。你会很快发现几个真实规律模型非常擅长生成“单个模块的草稿”但不擅长维护整个项目的一致性。一旦上下文变长模型会“忘记”前面已经确定的设计约束导致后面生成的代码和前面不匹配。修复bug的循环非常消耗token而且经常“按下葫芦浮起瓢”。没有测试用例时AI生成代码的正确性无法客观判断有测试用例时测试本身的设计又成了新的工作重点。这些经验不是凭空想出来的而是需要在一个足够复杂的项目里压测才能获得。编译器正好是这种压力测试项目——它足够大、足够复杂、有明确的输入输出判断标准非常适合用来观察AI编程的边界。2.3 把“写出一个编译器”改成“跑完一个编译器项目”思路就对了如果一定要做我建议别把目标定为“写出一个能编译C的完整编译器”。更好的目标是设计一个小型语言包含变量、整数运算、函数调用、if/else、while循环。实现它的词法分析和语法分析。生成一个抽象语法树AST。用解释器方式执行AST验证逻辑正确。如果你的兴趣在后端再把AST翻译成C代码交给GCC或Clang编译。这个路径能覆盖编译器的大部分核心概念但复杂度比完整C编译器低一个量级。用AI辅助来跑通可行性高很多token消耗也会明显下降。站在“做实验”的角度这个目标比“复刻一个Clang”更理性。3. 如果你真要试把C编译器按阶段拆开别让AI直接生成整个仓库3.1 一个完整编译器到底包含哪些部分哪怕不写完整C做一个表达式语言编译器也要分清楚这些模块模块职责主要风险词法分析器把源代码字符串切成Token比较容易实现但错误处理要仔细语法分析器根据语法规则生成AST递归下降解析容易漏掉优先级和嵌套语义分析/类型检查检查变量声明、类型匹配、函数调用需要维护符号表状态容易不一致中间表示与执行遍历AST执行或生成低级指令逻辑错误难定位代码生成生成目标代码如果不生成机器码先翻译成C最稳妥每个模块都可以单独让AI生成但生成之前你必须自己把接口定好。3.2 按模块生成而不是按整个文件夹生成我踩过的坑是这样的最开始会让AI“帮我写一个完整的编译器”然后它一次性吐出一个两千行的大文件。看起来结构完整但一旦编译出错信息量太大很难定位。而且后续想扩展功能时整个文件的上下文都在对话里修一个函数要传一遍全文件token消耗巨大。后来我改成按模块生成先让AI写一个lexer.h和lexer.cpp只负责把字符串变成Token列表。再单独写parser.h和parser.cpp负责把Token列表变成AST。每个模块都用独立的小测试验证。全部验证通过后再写AST执行器或者代码生成器。这样做的好处是每个任务的上下文短、目标明确、调试范围小。AI生成的代码如果出错你也能很快判断是词法层的问题还是语法层的问题。3.3 用“测试驱动”来控制质量让AI写编译器最重要的不是“生成代码”而是“验证代码”。我一般会先写测试用例再让AI实现功能。测试用例不能只测正常情况还要包含边界情况空输入单个数字表达式括号嵌套运算符优先级未声明的变量重复声明的变量除数为零超大整数溢出比如如果项目是要做表达式求值器测试至少要有1 2 * 3 - 7 (1 2) * 3 - 9 -5 10 - 5 10 / (2 3) - 2 x 10; y x 5; - y 15AI生成的功能如果过不了这些测试直接告诉它“某个测试失败”和“期望输出是什么”而不是让它自己发挥。这样能少走很多弯路也让token花在实际问题上。3.4 用构建脚本把模块串起来模块拆分之后一定要有自动化构建和测试脚本。不需要多复杂一个简单的build.sh或CMakeLists.txt就够。我推荐先把目录结构固定下来compiler_demo/ ├── CMakeLists.txt ├── include/ │ ├── lexer.h │ ├── parser.h │ └── ast.h ├── src/ │ ├── lexer.cpp │ ├── parser.cpp │ └── main.cpp └── tests/ ├── test_lexer.cpp └── test_parser.cpp这样AI每次只负责一个文件构建脚本全局可见。修改一个模块后可以直接cmake --build看是否编译通过然后跑测试看行为是否正确。这比每次都在对话里贴一堆代码要高效得多也避免AI在修改一个文件时破坏另一个文件的接口。4. Token消耗为什么容易失控三个最常见的原因4.1 上下文窗口被历史内容占满这是最容易被忽略的点。很多对话式AI工具不会主动清理上下文系统提示、历史代码、失败日志、之前的对话片段全都会存在上下文里。假如你已经和模型聊了200轮上下文里可能塞了几十万token。之后每次提问模型都要把这几十万token重新处理一遍你每问一次就消耗一次完整上下文长度的token。对这个问题的处理方式很简单每个任务完成后开新会话。新任务只携带“必要背景信息”不要携带全部历史。长项目的设计文档单独保存而不是一直放在对话里。“20亿token”里有很多可能就是这种重复读取历史产生的。4.2 让AI反复修复而不拆分问题第二种失控场景是只要程序有问题就把完整的编译日志全部贴给AI让它统一处理。编译日志往往非常长几百行。贴一次就是几千token。AI读完日志之后可能同时修改多个函数结果改完一个错误又引入两个新错误。然后你再贴新的报错日志继续循环。正确做法是只看第一条错误。很多编译器会持续报错但真正值得先修的是第一个。把错误信息缩减到最少的复现片段。一次只要求AI修一个函数或一个逻辑问题。修改完成后再编译重复。4.3 重复生成整个文件而不是增量修补第三个常见问题当AI生成的文件不符合预期时用户经常直接说“重新写一个完整版”。这种操作非常烧token。一个两千行的文件重新生成一次就是几千到上万token。如果反复重写十次就是几万token。更糟的是新版本可能和旧版本的接口不兼容导致连锁修改。正确的做法是明确告诉模型“只修改函数xxx”并且只贴出该函数的现有代码。明确修改目标和期望行为。修改完成后保留原有版本用Git或备份文件记录。让AI做“增量修改”而不是“推翻重写”。这样不仅省token错误率也会更低因为模型不需要重新理解所有上下文。5. 如果非要做一个编译器我建议从“小语言”开始而不是直接写C5.1 先做一个“算术表达式求值器”这是编译器项目里最友好的起步点。它不需要处理复杂的类型系统不需要内存布局也不需要考虑指针和引用。你要做的事只有这些把输入字符串拆成Token比如数字、加号、减号、乘号、除号、左括号、右括号。按语法规则解析成AST。遍历AST计算数值。就是这么小一个程序已经能涵盖词法分析、语法分析、AST、求值这几个编译器核心概念。而且它很好验证输入一个表达式输出一个数字对不对一眼就能看出来。AI生成这类小工具的成功率也高很多。5.2 再从“表达式求值器”扩展到“C语言子集”当表达式求值器跑通之后可以继续扩展支持多行语句。支持变量声明和赋值。支持函数定义和调用。支持if/else和while循环。支持整数和布尔值。你会发现这套扩展出来的小型语言已经很像一个“简化版C”。它依然不需要指针不需要结构体不需要标准库但已经足够用来学习编译原理的核心流程。我建议这个阶段用“AST解释器”来运行而不是直接生成机器码。原理是先理解程序要做什么再考虑怎么翻译成低级代码。顺序不要反过来。5.3 最后再考虑“生成机器码”当你对AST、作用域、函数调用这些概念都熟悉之后再往后端走。后端有几个常见方案生成C代码然后调用GCC/Clang编译。生成LLVM IR再交给LLVM后端。生成汇编代码。其中生成C代码是最容易验证的方案。你只要写一个“从AST到C文本”的转换器然后调用系统编译器把生成的C代码转换为可执行文件。这种做法绕开了很多底层细节但能让你理解编译器的输出阶段。如果你真的对生成机器码感兴趣可以先用一个极小的指令集比如只支持加法、减法、跳转然后逐步增加指令。先把逻辑跑通真正理解了之后再决定要不要写复杂后端。6. Token预算怎么管理一些可以复制的经验6.1 把Token当作成本管理而不是凭空概算如果你打算做一个长期项目建议从一开始就记录每个模块的token消耗。不用特别精确但至少要知道每个阶段的量级。我会做这样一个表格任务阶段预计消耗比例常见原因需求与设计5%写清楚模块边界、接口、数据结构模块实现35%生成lexer、parser、AST、执行器测试用例15%生成边界测试和验证脚本错误修复30%编译失败、行为错误、接口不一致重构与维护15%扩展功能、修改设计这个比例不是绝对标准但它能提醒你真正吃掉token的大头往往是“错误修复”而不是最开始生成代码。如果你发现修复比例越来越高说明任务拆分或者上下文管理出了问题。6.2 理解TPM和批量任务的节奏热词里出现过一个概念TPMTokens Per Minute输入token加输出token的总和表示每分钟处理token的速率。这个概念在批量任务里很重要。如果你连续向API发送大量长请求很容易触发频率限制。这时不是“总token不够”而是“某一分钟的token太多”。处理方式有两种降低并发同一时间只发少量请求。给请求之间加暂停比如每完成一个模块停顿几秒再继续。如果是用对话式工具不要一次性塞几十个任务。可以分轮做先做词法分析模块跑通之后再做语法分析模块。这样不会把长对话的上下文塞满也能避开TPM限制。6.3 把中间产物保存下来这是非常关键的一点。我见过很多人和AI聊了一上午某一轮AI突然输出异常把之前的代码覆盖了然后越修越乱。如果中间产物没保存只能靠记忆回顾很容易彻底丢失进度。正确的做法每个模块完成后立刻把代码复制到本地文件。用Git管理每一次修改。每次让AI改代码前先提交一次当前版本。如果AI改乱了直接回滚到上一个提交而不是继续带着错误状态修。用Git保存版本的成本很低但它能防止token消耗在“恢复旧状态”这件事上。编译器项目本身就复杂不值得把精力浪费在丢失代码上。7. 热词里那些C问题很多不是“写编译器”问题7.1 “vscode配置c环境”和“visual c redistributable”这两个热词经常出现但它们其实指向两个完全不同的层面。“vscode配置c环境”通常是说本机还没有配置好编译器比如MinGW、Clang、MSVC。“Visual C Redistributable”则是运行库它解决的是“程序运行时缺少库文件”的问题不是“源代码无法编译”。排查顺序应该是先确认编译器是否安装。再确认命令行工具是否可用。然后检查项目配置里的include路径、库路径。最后才看运行库。不要一上来就重装运行库因为它解决不了源码编译失败的问题。7.2 “arm compiler 5.06”和Keil许可证问题热词里有一堆和Arm Compiler相关的内容比如arm compiler 5.06 update 7keil5 uses arm-compiler default compiler version 5 which is not availablearm compiler许可证错误这类问题在嵌入式开发里很常见。一般原因是工程文件里指定的编译器版本和本机安装的版本不一致。比如Keil工程默认使用Arm Compiler 5但你安装的是Arm Compiler 6于是报错。排查优先级看工程里的编译器选择设置。确认本机是否安装了对应版本的编译器。检查工具链路径是否在Keil安装目录下。检查许可证是否过期或未激活。这类问题不是“AI编程”能直接解决的更多是工具链环境配置。把报错完整贴给AI也有帮助但最好提前把“你用的什么IDE、什么编译器版本、出现错误时的完整日志”准备好这样AI给出的排查方向才会准确。7.3 各种“cannot run compiler”问题热词里还有project error: cannot run compiler aarch64-linux-gnu-gpip setuptools windows cc compilerjava: you arent using a compiler supported by lombok这些都属于环境不匹配。aarch64-linux-gnu-g是交叉编译工具链如果系统里没有安装或者路径没加到PATH里就会出现“cannot run compiler”。解决方法是安装对应工具链或者修改项目配置里的编译器路径。pip setuptools windows cc compiler是Python包安装时找不到C编译器的典型报错常见于Windows环境。处理方式一般是安装Microsoft C Build Tools或者切换到一个自带编译器的Python发行版。Lombok的问题则是JDK版本和Lombok版本不匹配通常升级Lombok或切换到受支持的JDK版本就能解决。说到底这些都可以归为“环境排查”问题。你需要先判断是“编译器不存在”还是“路径找不到”再判断是“版本不兼容”还是“许可证没生效”而不是一上来就重装环境。8. 把20亿Token的价值拆成三笔账8.1 第一笔账编译器本身没有省下来但省了“学习曲线”如果你真的按照上面这套流程去走最终“代码生成”部分很可能没有达到你的预期但你会更清楚编译器各个模块之间的关系。比如你会知道词法分析器输出的Token流会直接决定语法分析器的复杂度。AST的数据结构设计会影响后续求值和代码生成时的遍历难度。符号表需要维护变量类型、作用域和函数签名类型检查一刻都离不开它。如果能尽早把测试自动化整个项目就不会进入“改了这个坏了那个”的失控循环。这些本来就是学习编译原理的关键内容。有人看一个月书也未必能真正打通但用AI辅助实践可能几天就能把整个链路走一遍。从这个角度看tokens换到的是“亲手做一遍”的经验。8.2 第二笔账换取的是“大型软件工程控制能力”让我觉得这个实验真正有价值的不是AI生成出来的代码而是过程中学到的工程控制方式。具体包括如何拆任务让每个任务足够小、可验证。如何控制上下文不在同一个对话里堆太多内容。如何设计测试让AI修改代码之后能客观判断对错。如何保存版本避免AI改乱代码后不可恢复。如何管理token预算记录每个阶段的实际消耗。这些能力可以迁移到任何项目里。不管你是写业务后端、前端、脚本还是做自动化工具都会用到同样的“任务拆解、验证、版本管理”思路。一旦你掌握了这套方法AI对你来说就不再是“写代码的机器”而是一个“需要明确需求和验收标准的外包工程师”。你对它的使用效率会高很多。8.3 第三笔账更大的价值是知道自己不该用AI写什么这句话听起来像废话但它是我认为最值钱的经验。编译器这个项目暴露了很多“AI编程”的边界它适合生成草稿代码但不适合维护全项目的一致性。它适合解决局部问题但需要你提供清晰接口和测试用例。它能在你给出正确方向时快速产出实现但如果你自己都不知道正确行为是什么它很难代替你判断。尤其是在处理语言标准、ABI兼容、二进制兼容、内存布局这类问题时AI生成的代码只能作为起点不能作为终点。真正要用于生产环境的编译器还是需要成熟工具链或者经过大规模人工审查、测试和长期维护。9. 如果不想烧20亿Token更便宜的路是什么如果你只是对编译器感兴趣不想花那么多token和时间我会建议走这条更省力的路径先写一个能求四则运算的表达式解析器。再写一个支持变量、函数、循环的小型解释器。用现成的解析器生成工具比如ANTLR、Bison研究一门小语言。阅读开源编译器的源码理解真实项目的模块和组织方式。如果要动手写后端先翻译成C代码不要直接挑战汇编生成。这个路径花的时间不会比“让AI写一个完整编译器”少但它是在“学习”而不是“测试AI”。两种目标需要不同的投入方式。如果目标不清晰很容易既没学到编译器知识又浪费了大量token。最后我个人的建议是把单任务跑稳再考虑整个大项目。如果你真打算用AI写一个“小神器”先让它在几百行规模内证明能力再决定要不要扩大到几千行。C编译器这个目标太宏大多数情况下它适合作为工程练习而不是日常落地的工具需求。说到底20亿token到底值不值关键看你换到了什么。如果你换到了一套可控、可验证、能复用的AI项目管理方法那它是很贵的“训练课”。如果你只是想让AI帮你拼出一个能跑Hello World的编译器那完全有更便宜的方式。
返回列表