四个月训练AI写编程语言:长链条任务拆解与验证循环的工程实践

发布时间:2026/10/27 15:13:04

四个月训练AI写编程语言:长链条任务拆解与验证循环的工程实践 1. 四个月训练AI写编程语言这件事到底难在哪先把结论摆在前面让AI写出一门编程语言这件事真正难的不是让模型吐出几段看起来像编译器代码的文本而是让它产出的东西能自洽、能跑通、能通过测试用例。我花了四个月做这件事中间推翻过两次整体方案最后跑通的那一刻说实话没有想象中那么激动更多是终于不用再盯着那堆报错日志了的疲惫感。这个项目的核心目标很明确训练一个AI系统让它能够从零开始设计并实现一门小型编程语言包括词法分析、语法分析、解释执行这条完整链路。关键词里提到的AI编程语言AI agentAI工程实践AI模型部署这些基本覆盖了我这四个月踩过的所有坑。适合谁来参考如果你正在做AI代码生成相关的工程落地或者想搞清楚让大模型干一件需要长链条推理的复杂任务到底该怎么设计流程这篇内容应该能帮你少走一些弯路。为什么选写编程语言作为任务因为它是一个天然的长链条、强约束、可验证的任务。写一个解释器从token定义到AST构建再到求值每一步都有明确的输入输出而且最终可以用测试用例来判定对错。这比让AI写一个网页、写一个爬虫要干净得多——网页好不好看是主观的但12*3算出来是不是7是客观的。这种可验证性是训练AI做复杂任务时最宝贵的东西。但可验证不等于容易。我一开始的想法很天真直接给模型一个prompt让它输出完整的解释器代码跑不通就让它自己改。结果第一周就发现模型生成的代码在超过200行之后变量名开始前后不一致函数调用参数对不上甚至同一个功能定义了两遍。这不是模型笨而是上下文长度和注意力衰减的必然结果。任何做过AI编程的人应该都有体会让模型写一个50行的函数很稳写一个500行的模块就开始飘。所以这个项目的本质不是训练一个会写编译器的模型而是设计一套工程流程把写一门语言这个大任务拆解成模型能够稳定完成的若干小任务并且让这些小任务之间的衔接不出错。这个认知转变是我前两个月最大的收获也是后面所有方案设计的基础。2. 把写语言拆成AI能稳定交付的原子任务2.1 为什么不能一步到位长链条任务的注意力陷阱我先用一组实测数据说明问题。当时我用同一个模型分别让它完成写一个完整的四则运算解释器和写一个解析数字token的函数各跑20次统计首次通过率即生成的代码直接跑通测试用例的比例任务粒度平均代码行数首次通过率主要失败原因完整解释器300-500行5%变量不一致、逻辑遗漏、语法错误词法分析器80-120行45%边界字符处理遗漏单个token解析函数15-30行92%几乎无失败AST节点定义20-40行88%偶有字段命名冲突这张表说明了一个很朴素的道理任务粒度和成功率是强相关的。当任务小到一个函数只干一件事的时候模型的表现相当可靠一旦任务需要它同时维护多个抽象层次失败率就断崖式上升。这背后的机制业内比较公认的解释是模型在生成过程中前面生成的token会作为后面生成的上下文但随着序列变长早期关键信息比如这个变量叫tokens还是token_list在注意力权重上会被稀释。它不是忘了而是权重不够高压不过后面新出现的信息。所以你会看到模型在文件后半段突然用一个新名字指代前面已经定义过的东西。提示判断一个任务是否该拆分有个简单标准——如果模型生成的代码里出现了它应该记得但没记住的信息就说明这个任务超出了它的稳定工作区间必须拆。2.2 拆分方案从一个prompt到一条流水线基于上面的认知我把整个写一门语言拆成了六个阶段每个阶段对应一类原子任务语言规格定义确定这门语言有哪些语法特性、支持哪些数据类型、有哪些关键字。这一步不写代码只产出规格文档。Token定义与词法规则列出所有token类型以及每种token的识别规则。词法分析器实现把源码字符串转成token序列。AST节点定义定义语法树的节点类型和字段。语法分析器实现把token序列转成AST。解释器实现遍历AST并求值。每个阶段内部还可以继续拆。比如词法分析器实现可以拆成数字识别标识符识别运算符识别字符串识别等独立函数。拆到什么程度我的经验是拆到每个子任务的产出可以被单独测试为止。一个函数能单独写测试用例验证就说明粒度够了。这里有个反直觉的点拆分不是越细越好。拆得太细任务之间的接口约定成本会急剧上升而且模型在每个小任务上都要重新理解上下文反而浪费。我试过把词法分析器拆成20个函数结果光是维护这些函数怎么互相调用就花了两天。后来收敛到8个函数整体效率反而更高。拆分的收益递减点大概在每个子任务需要模型理解3-5个前置概念这个位置。2.3 接口契约让AI自己记住变量叫什么拆分之后最大的风险是阶段A产出的东西阶段B用的时候名字对不上。比如词法分析器输出的token对象字段叫type还是token_type如果每个阶段都让模型自由发挥最后拼起来必然是一堆undefined。我的解法是引入一层显式接口契约。在开始写任何代码之前先让模型产出一份数据结构定义清单明确每个跨阶段传递的对象长什么样。这份清单会作为固定上下文注入到后续每一个子任务的prompt里。具体做法是维护一个contract.md文件内容大概是这样## Token对象 - type: string取值集合 {NUMBER, IDENT, PLUS, MINUS, STAR, SLASH, LPAREN, RPAREN, EOF} - value: string原始文本 - pos: number在源码中的起始位置 ## AST节点 - 所有节点必须有 type 字段 - NumberLiteral: { type: NumberLiteral, value: number } - BinaryExpr: { type: BinaryExpr, op: string, left: Node, right: Node }每次调用模型生成某个模块的代码时我会把这份契约的相关部分贴进prompt。实测下来这一招把接口对不上导致的返工减少了大概七成。原因很简单模型不需要记住之前定义了什么它每次都能在上下文里直接看到。注意契约文件本身也要版本管理。我中途改过一次AST节点结构忘了同步更新契约结果语法分析器还在按旧结构生成节点解释器按新结构读取排查了整整一个下午。后来我养成了习惯改契约必须提交一次记录并且重新跑一遍全链路测试。3. 训练流程里的三个关键设计决策3.1 为什么我放弃了微调改用提示词工程加验证循环项目初期我认真考虑过微调路线收集一批高质量的解释器代码对模型做监督微调让它内化写编译器的能力。这个思路听起来很对但我实际评估后放弃了原因有三个。第一数据成本太高。要微调出稳定效果至少需要几千条高质量的任务-代码配对样本而且这些样本得覆盖各种边界情况。我自己写这些样本的时间可能比整个项目还长。第二微调后的模型依然会犯错而且错误模式可能更隐蔽——你很难判断一个错误是它没学会还是这次运气不好。第三也是最关键的微调解决不了长链条问题。就算模型把每个子任务都学得很好把它们串起来的时候该出的接口问题还是会出。所以我最终采用的是提示词工程 自动验证循环的方案。核心思路是不改变模型本身而是通过精心设计的prompt让它在每个子任务上表现最优然后用自动化测试来兜底发现错误就带着错误信息重新请求。这个验证循环的伪代码大概是这样def generate_with_retry(task, contract, test_cases, max_retry5): prompt build_prompt(task, contract) for attempt in range(max_retry): code call_model(prompt) failures run_tests(code, test_cases) if not failures: return code # 把失败信息反馈给模型让它针对性修复 prompt build_fix_prompt(task, contract, code, failures) raise Exception(超过最大重试次数)这里有个细节很关键反馈给模型的失败信息要精准。一开始我把完整的报错堆栈丢给模型结果它经常被无关信息干扰改错地方。后来我只提取哪个测试用例失败了、期望输出是什么、实际输出是什么这三样修复成功率明显提升。3.2 测试用例从哪来让AI自己出题但由人来把关验证循环的前提是有测试用例。手写测试用例很累我一开始想偷懒让模型自己生成。结果发现模型生成的测试用例有个通病它倾向于测试自己会写的功能回避自己不会写的功能。比如它不太会处理嵌套括号那它生成的测试用例里就很少出现多层嵌套。所以我的做法是人机分工让模型生成测试用例的初稿覆盖常规情况然后我人工补充边界用例专门针对我觉得模型可能出错的地方。这些边界用例包括空输入、只有空白字符的输入超长表达式测试是否有栈溢出或性能问题运算符优先级和结合性的边界比如1-2-3应该是(1-2)-3还是1-(2-3)非法输入的错误处理比如12应该报什么错这套组合下来测试覆盖率大概能到85%以上。剩下的15%是那些我没想到、模型也没想到的情况只能靠实际使用中慢慢发现。但至少主干逻辑是被测试保护住的。3.3 错误修复的提示词怎么写才有效这是整个项目里我觉得最值得分享的经验。当模型生成的代码跑不通时怎么让它改对是有讲究的。我试过三种提示词写法效果差异很大写法A最差这段代码有bug请修复。——模型经常改错地方或者把能跑的部分也改坏了。写法B中等测试用例X失败了期望Y实际Z请修复。——比A好但模型有时会为了通过这一个用例而破坏其他用例。写法C最好测试用例X失败了期望Y实际Z。请只修改与这个失败相关的最小代码范围不要改动其他逻辑。修改后需要保证所有已有测试用例仍然通过。写法C的关键在于约束修改范围。模型天然倾向于大改因为它觉得改得多更容易解决问题。但大改会引入新bug。明确告诉它最小修改能显著降低回归率。我统计过用写法C之后修复引入的新失败从平均每次1.8个降到了0.4个。另外一个小技巧如果同一个错误重试三次还没修好不要继续硬刚。这时候往往是模型对问题的理解本身就偏了继续重试只是在同一个错误方向上打转。正确做法是换一个角度重新描述问题或者干脆人工介入把这个模块重写。我在项目里设了个规则任何模块重试超过5次就标记为需要人工介入不再浪费token。4. 四个月里踩过的那些坑4.1 第一个月以为prompt写详细就够了第一个月我基本在跟prompt较劲。我的假设是只要把需求描述得足够详细模型就能一次写对。于是我写了非常长的prompt把每个函数的输入输出、边界条件、异常处理都列得清清楚楚。结果呢模型确实能理解需求但生成的代码依然有大量低级错误。后来我才想明白prompt详细解决的是模型知不知道要做什么的问题但解决不了模型能不能稳定做到的问题。这两个是不同维度的事。前者靠描述后者靠流程设计。这个认知让我从优化prompt转向了优化流程是整个项目的转折点。4.2 第二个月接口不一致的连环坑第二个月我开始搭流水线然后掉进了接口不一致的坑里。最典型的一次词法分析器输出的token位置信息用的是start和end两个字段语法分析器报错时想引用位置用的却是pos。结果所有报错信息里的位置都是undefined我花了半天才定位到是字段名不一致。这类问题的根源是每个模块独立生成时模型对上一个模块产出了什么的理解是模糊的。它可能记得大概但记不住精确的字段名。解决办法就是前面说的契约文件。但契约文件本身也有维护成本我后来用了一个更省事的办法让每个模块的代码里包含一段自检逻辑在模块入口处校验输入数据的结构不符合契约就直接报错。这样问题会在最早的地方暴露而不是等到运行到深处才炸。4.3 第三个月性能问题比正确性更隐蔽到第三个月功能基本跑通了我开始做性能测试。然后发现一个尴尬的问题解释器跑一个稍微复杂的表达式比如100层嵌套的加法要好几秒。这在功能测试阶段完全没暴露因为测试用例都很短。排查后发现是递归求值的问题。AST遍历用的是递归深度一大就慢而且有栈溢出风险。改成显式栈的迭代方式后性能提升了大概20倍。这个坑给我的教训是测试用例要覆盖的不只是功能正确性还有规模维度。如果测试用例全是12这种小输入那性能问题永远不会被发现。后来我在测试集里加了一批压力用例专门测大规模输入下的表现。4.4 第四个月让AI自己review自己的代码最后一个月我在做整体优化尝试了一个有意思的做法让模型review自己之前生成的代码。具体操作是把某个模块的代码和它的测试结果一起给模型问它这段代码有什么潜在问题。效果出乎意料地好。模型确实能发现一些我自己都没注意到的问题比如这个错误处理分支永远不会被执行、这个变量在某个路径下未初始化。我猜测原因是生成时模型是向前看的review时它是向后看的两种视角能看到不同的东西。但也要注意模型review出来的问题不都是真问题有些是它想象出来的。所以review结果必须人工过一遍不能直接采纳。我大概采纳了其中60%的建议剩下40%是误报或者不重要的。5. 最终跑通时这套流程长什么样5.1 完整流水线的六个环节到项目结束时我的流水线是这样的环节一规格生成。输入是我要一门支持四则运算和变量赋值的语言输出是一份规格文档包含语法示例、关键字列表、数据类型说明。这一步人工审核确认规格合理。环节二契约生成。基于规格生成Token结构和AST节点结构的定义。这一步也人工审核因为契约一旦定错后面全错。环节三模块代码生成。按词法分析器、语法分析器、解释器的顺序逐个生成。每个模块生成后立即跑单元测试不通过就进入修复循环。环节四集成测试。所有模块拼起来跑端到端测试。这一步主要暴露接口问题。环节五压力测试。用大规模输入测试性能和稳定性。环节六代码review。让模型review整体代码人工筛选有效建议并应用。整个流程跑一遍从零到能用的解释器大概需要3-4小时取决于重试次数。相比我第一个月手动折腾一周还跑不通效率提升是数量级的。5.2 这套流程能复用到哪些场景虽然我做的是写编程语言但这套流程的本质是用AI完成长链条、强约束的复杂任务所以它可以迁移到很多场景场景可复用的部分需要调整的部分生成数据管道代码拆分策略、契约管理测试用例设计数据校验为主生成API服务验证循环、错误修复提示词接口契约更复杂需要OpenAPI等标准生成测试代码整体流程需要额外的测试有效性验证生成配置文件契约管理拆分粒度可以更粗核心可复用的三点是任务拆分到模型稳定区间、显式接口契约、带精准反馈的验证循环。这三点跟具体任务无关是通用的工程方法。5.3 一些量化数据最后给一些实测数据供参考整个项目四个月实际编码时间约300小时其中约40%花在流程搭建和调试上60%花在具体任务的生成和修复上。最终产出的解释器约1200行代码其中约85%由AI生成15%由我手动修改或重写。全链路测试用例约200个首次全通过率从项目初期的0%提升到最终的100%经过平均2.3次重试。单次完整流水线运行的平均token消耗约15万成本在可接受范围内。6. 如果你也想做类似的事我的几条实在建议第一条先花时间想清楚怎么验证再动手写怎么生成。我见过太多人一上来就研究prompt怎么写结果生成出来的东西没法判断对错只能靠肉眼review效率极低。验证方案是地基地基不稳上面盖什么都是危房。第二条不要追求一次生成就对。这个目标在当前技术条件下不现实而且追求它的过程会让你不断优化prompt陷入局部最优。正确的思路是接受生成-验证-修复这个循环把精力花在让循环跑得更快更稳上。第三条契约要早定、要写下来、要版本管理。口头约定或者模型应该记得是靠不住的。一份明确的契约文件能省掉你大量的排查时间。第四条测试用例要覆盖规模维度。功能正确只是及格线性能和稳定性问题往往在规模上去之后才暴露。早点加压力测试比后期重构便宜得多。第五条知道什么时候该放弃重试。模型在同一个问题上连续失败三次以上基本可以判定是方向错了继续重试是浪费。这时候要么换描述方式要么人工介入。我在项目里设的阈值是5次超过就人工处理。这个项目做完我最大的感受是AI能做的事情比大多数人想象的多但前提是你得给它搭好脚手架。指望它自己搞定一切和指望一个新人第一天就独立负责大项目一样不现实。但如果你把任务拆好、接口定好、验证做好它能交付的东西会让你惊讶。四个月前我也不确定能不能成现在回头看这条路是走得通的只是需要一点耐心和不少工程上的细致活。
延伸阅读

更多相关文章

2026/10/27 15:13:04

Java实现家政智能派单系统架构与算法详解

1. 同城家政行业的痛点与机遇 家政服务行业近年来呈现爆发式增长态势,特别是在一二线城市,随着双职工家庭比例上升和生活节奏加快,保洁、月嫂、保姆等家政服务的需求持续攀升。然而这个看似繁荣的市场背后,却存在着诸多亟待解决的…

2026/10/27 15:13:04

Kadane算法解析:动态规划解决最大子数组和问题

1. 问题背景与核心挑战 最大子数组和问题(Maximum Subarray Problem)是算法领域的一个经典问题,也是技术面试中的高频考点。给定一个整数数组nums,我们需要找到一个具有最大和的连续子数组(子数组最少包含一个元素&…

2026/10/27 15:13:04

从零搭建轻量报表系统:在线填报与报告生成实战

报表工具这个方向,我前前后后做过三套,最早那套是给内部运营团队用的,需求很朴素:让业务方自己填数据,填完能出一份像样的报告。听起来简单,真动手才发现坑不少——填报页面的字段校验、多人同时填一份表的…

2026/10/27 19:04:30

从编译原理到可运行解释器:自制编程语言源码与Windows工具链实践

简介:这份《自制编程语言》配套资料面向希望理解语言底层运行机制的开发者与计算机专业学生,围绕语言设计、实现与使用三条主线展开,帮助读者打通从语法规则到可执行程序的完整认知链路。压缩包内仅有1个PDF文件,体积约2.38MB&…

2026/10/27 19:04:30

从bison报错到可运行语言:Crowbar与Diksam工程拆解

简介:这份资源围绕「自制编程语言」展开,是一份面向编译原理学习者、语言爱好者以及想动手实现解释器与编译器的开发者的PDF资料,适合具备一定C语言与数据结构基础、希望从设计到实现完整走通一遍的中高级学习者。压缩包内共1个PDF文件&#…

2026/10/27 19:04:30

自制编程语言实战:Crowbar/Diksam 解释器与字节码虚拟机

简介:《自制编程语言》相关资料1面向希望从零理解语言设计与实现机制的开发者与学习者,聚焦编译器、解释器构建及跨平台编译工具链的入门实践。压缩包仅含1个PDF文件,大小约2.38MB,轻量便携,适合作为纸书之外的配套阅读…

2026/10/27 19:04:30

基于协同过滤的儿童图书推荐系统:从算法到工程落地

简介:这份资源是基于协同过滤算法实现的儿童图书推荐系统完整源码包,面向计算机相关专业的课程设计、毕业设计学生以及想入门推荐算法的Java开发者。项目采用SpringBoot框架搭建后端,MySQL存储用户与图书数据,通过分析儿童阅读行为…

2026/10/27 19:04:29

离线语音交互智能对话系统:从架构选型到部署调优的完整指南

简介:这是一套基于语音交互的智能对话系统开源实现,聚焦实时语音输入与大型语言模型响应,面向希望摆脱云端依赖、在个人电脑或低算力硬件上离线部署语音助手的开发者和AI研究者,解决低延迟自然对话与本地私有化运行两大痛点。压缩…

2026/10/27 18:59:28

写一个扫雷代码

第一步先将菜单以及选项给呈现出来使用do....while来实现选项&#xff0c;因为只需要执行此选项即可&#xff0c;因此设置返回值为void#include<stdio.h> static void menu() {printf("****************\n");printf("***1.开始游戏***\n");printf(&q…

2026/10/27 14:24:03

全国计算机二级C语言真题PDF高效刷题指南

简介&#xff1a;这份PDF是全国计算机二级C语言考试的试题与答案解析&#xff0c;专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/27 9:34:54

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介&#xff1a;一份以实战为导向的Python爬虫入门资源&#xff0c;面向具备基本编程基础、希望快速掌握网络数据抓取的初学者&#xff0c;可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档&#xff0c;完整演示从网页中提取 标题…

2026/10/27 0:59:21

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介&#xff1a;这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档&#xff0c;压缩包大小七十八KB&#xff0c;内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开&#xff1a;先构造一个极点位于单位圆内…

2026/10/27 0:02:28

WorkBuddy_新建任务与工作模式19_“模式选错”的典型代价

“模式选错”的典型代价 摘要 在软件工程中&#xff0c;设计模式、架构模式、并发模式乃至团队协作模式的选择&#xff0c;往往在项目早期就决定了后续的开发效率和维护成本。本文通过多个真实场景&#xff0c;剖析“模式选错”带来的典型代价——包括返工成本、技术债务累积、…

2026/10/27 0:02:28

WorkBuddy_新建任务与工作模式20_新手推荐路径

新手推荐路径 摘要 在 AI 辅助开发工具快速普及的今天&#xff0c;新手面对最多的困惑往往不是"工具够不够强"&#xff0c;而是"我该从哪一步开始用"。本文提出一条经过实践验证的三阶段学习路径&#xff1a;先从 Ask&#xff08;问答式交互&#xff09;起…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑