☰
四个月训练AI写编程语言:提示词工程与测试驱动实战
2026/10/14 14:03:45 网站建设 项目流程

1. 四个月训练AI写编程语言,这件事到底难在哪

先说说我看到这个标题时的第一反应。四个月,训练一个AI去写一门编程语言,最后还跑通了。这事听起来像是某个大厂研究院的季度KPI,但实际上它更像是一个人在GitHub上闷头干了小半年之后发出来的项目复盘。我翻了不少类似的案例,也自己动手折腾过用大模型辅助做DSL(领域特定语言)和解释器的活儿,所以对这个话题有一些实打实的体感。

核心问题其实就一个:让AI写编程语言,和让AI写业务代码,完全是两个难度量级的事情。写业务代码,AI有海量的训练数据可以参考,CRUD、API调用、常见算法,这些在GitHub上遍地都是。但编程语言不一样,它涉及到词法分析、语法分析、AST构建、类型系统、代码生成、运行时环境,每一个环节都是高度结构化的系统工程。你让AI去写一个能跑的编译器前端,它很容易给你生成一堆看起来像那么回事但根本跑不通的代码。

这个项目标题里说的“训练AI写一门编程语言”,我理解下来,大概率不是从零训练一个大模型,而是用现有的AI编程能力,通过一套系统化的提示词工程和迭代流程,让AI辅助完成一门编程语言的完整实现。这个区别很关键,因为从零训练一个能写编译器的模型,四个月、个人开发者、没有大规模算力,基本不现实。但如果是用Claude、GPT这类模型,配合精心设计的提示词和工作流,四个月做出一个能跑的小型编程语言,这个是完全可行的。

适合谁来参考这篇内容?我觉得有三类人:一是对编程语言实现感兴趣但一直没动手的开发者;二是想用AI提升自己开发效率但不知道怎么系统化操作的人;三是正在做AI辅助编程工具或流程的产品和技术人员。不管你是哪种,接下来的内容都会围绕一个核心问题展开:怎么把AI变成一个靠谱的编程语言实现助手,而不是一个只会胡说八道的代码生成器。

2. 整体方案设计:为什么不能直接让AI“写一门语言”

2.1 直接让AI写编译器的三个致命问题

我试过最粗暴的方式,就是直接跟AI说“帮我写一个简单的编程语言解释器”。结果很典型:它给你生成一个Python文件,里面有一个Lexer类、一个Parser类、一个Interpreter类,代码看起来结构完整,但一跑就报错。问题出在三个地方。

第一,上下文长度限制导致代码不一致。一个能跑的解释器,少说也要几百行代码,涉及多个模块之间的接口约定。AI在生成后半部分代码时,很容易忘记前半部分定义的Token类型、AST节点结构,导致接口对不上。你让它改,它改了一处又忘了另一处,来回几次就乱了。

第二,AI对编程语言的语义细节理解不够精确。比如运算符优先级、作用域规则、闭包捕获、类型推导,这些在具体实现中都有很多边界情况。AI生成的代码往往能处理happy path,但一遇到嵌套表达式、递归调用、错误处理就崩了。

第三,没有测试驱动,AI不知道自己写错了。你让AI写代码,它写完了你觉得“看起来对”,但实际跑起来才发现问题。没有一套自动化的测试用例,你根本不知道AI生成的代码到底能不能用。

2.2 四个月项目的合理拆解思路

基于上面这些问题,一个可行的方案必须把“写一门编程语言”这个大目标拆成AI能处理的小块,并且每个小块都有明确的验证标准。我梳理了一下,整个项目大概可以分成这么几个阶段:

  • 第一阶段:语言设计。确定这门语言长什么样,支持哪些语法特性,目标运行环境是什么。这个阶段AI可以帮你做调研和方案对比,但最终决策得你自己拍板。
  • 第二阶段:词法分析和语法分析。这是编译器前端,相对标准化,AI比较擅长。关键是你要给它明确的Token定义和文法规则。
  • 第三阶段:AST和解释器核心。这是最容易出问题的部分,需要你把解释器的执行模型讲清楚,比如是树遍历解释还是字节码编译。
  • 第四阶段:标准库和运行时。内置函数、类型系统、错误处理,这些需要逐个模块实现和测试。
  • 第五阶段:测试和迭代。写测试用例,跑,修bug,再跑,再修。这个阶段AI是你的调试助手,不是代码生成器。

每个阶段之间都有明确的交付物和验收标准,这样AI才不会跑偏。四个月的时间,如果每周能投入10到15小时,这个节奏是合理的。

2.3 为什么选择“AI辅助+人工把关”而不是“全自动生成”

这里有一个关键的设计决策:你是把AI当成一个全自动的代码生成器,还是当成一个需要你引导和审核的编程助手?我试过两种模式,结论很明确:全自动生成在编程语言这种复杂系统上基本不可行,因为错误会累积,而且AI没有全局视野。但AI辅助+人工把关的模式,效率提升非常明显。

具体来说,人工负责的是:架构设计、接口定义、关键算法决策、测试用例编写、代码审查。AI负责的是:根据明确的接口和算法描述生成具体实现、补充边界情况处理、生成测试代码、解释报错信息、提供重构建议。这个分工的核心逻辑是:人做判断,AI做执行。编程语言实现中有太多需要权衡的地方,比如性能vs可读性、功能完整vs实现复杂度,这些判断AI做不了,但一旦你做了判断,AI可以很快地把实现写出来。

3. 核心细节解析:提示词工程在编程语言实现中的具体应用

3.1 怎么给AI描述一个词法分析器

词法分析器是编译器的第一环,任务是把源代码字符串转换成Token流。这个任务对AI来说相对友好,因为它的输入输出非常明确。但如果你只说“写一个词法分析器”,AI会给你一个很通用的版本,可能不支持你想要的语法特性。

我的做法是给AI一个非常具体的规格说明。比如我要实现一个支持整数、浮点数、字符串、标识符、运算符和关键字的小型语言,我会这样写提示词:

请用Python实现一个词法分析器,要求如下: Token类型包括: - INTEGER: 整数,如 42 - FLOAT: 浮点数,如 3.14 - STRING: 字符串,双引号包围,支持转义 \" \\ \n \t - IDENTIFIER: 标识符,字母或下划线开头,后跟字母数字下划线 - KEYWORD: 关键字,包括 let, if, else, while, func, return - OPERATOR: 运算符,包括 + - * / = == != < > <= >= - LPAREN, RPAREN, LBRACE, RBRACE, COMMA, SEMICOLON - EOF: 输入结束 要求: 1. 输入是一个字符串,输出是一个Token列表 2. 每个Token包含类型、值、行号、列号 3. 遇到非法字符要抛出异常,异常信息包含行号和列号 4. 跳过空白字符和以 // 开头的单行注释 5. 不要使用正则表达式,手写状态机 请给出完整代码,并附带至少10个测试用例。

这个提示词的关键在于:Token类型枚举清楚、边界条件明确、实现约束具体、要求附带测试。这样AI生成的代码基本可以直接用,即使有小问题,你也能很快定位。

3.2 语法分析器的提示词设计要点

语法分析器比词法分析器复杂得多,因为它涉及到文法规则和递归下降的逻辑。AI在这个环节最容易犯的错误是:生成的解析函数不匹配文法规则,或者处理不了左递归。

我的经验是,在提示词里直接把文法用EBNF写出来,然后要求AI按照递归下降的方式实现。比如:

请根据以下EBNF文法,用Python实现递归下降语法分析器: program := statement* statement := let_stmt | if_stmt | while_stmt | func_def | return_stmt | expr_stmt let_stmt := "let" IDENTIFIER "=" expression ";" if_stmt := "if" "(" expression ")" block ("else" block)? while_stmt := "while" "(" expression ")" block func_def := "func" IDENTIFIER "(" params? ")" block return_stmt := "return" expression? ";" expr_stmt := expression ";" block := "{" statement* "}" params := IDENTIFIER ("," IDENTIFIER)* expression := assignment assignment := logical_or ("=" assignment)? logical_or := logical_and ("||" logical_and)* logical_and := equality ("&&" equality)* equality := comparison (("==" | "!=") comparison)* comparison := term (("<" | ">" | "<=" | ">=") term)* term := factor (("+" | "-") factor)* factor := unary (("*" | "/") unary)* unary := ("!" | "-") unary | primary primary := INTEGER | FLOAT | STRING | IDENTIFIER | "(" expression ")" | func_call func_call := IDENTIFIER "(" args? ")" args := expression ("," expression)* 要求: 1. 每个非终结符对应一个解析函数 2. 解析函数返回AST节点 3. 遇到语法错误时抛出异常,包含行号和期望的Token类型 4. AST节点用Python的dataclass定义 5. 附带AST节点的定义和至少5个解析测试用例

这个提示词的核心是:文法规则完整、优先级层次清晰、AST节点定义明确、错误处理要求具体。有了这些约束,AI生成的解析器代码质量会高很多。

3.3 解释器核心的提示词策略

解释器是整门语言的心脏,也是最容易出bug的地方。AI在写解释器时,最大的问题是对作用域和闭包的处理不够准确。比如它可能会把变量存在一个全局字典里,导致嵌套函数的作用域链断裂。

我的做法是,在提示词里明确指定环境模型。比如:

请实现一个树遍历解释器,要求如下: 环境模型: - 使用Environment类表示作用域,每个Environment有一个parent指针 - 变量查找从当前环境开始,逐级向上查找 - 函数调用时创建新的Environment,parent指向函数定义时的环境(词法作用域) - 支持闭包:函数对象保存定义时的环境引用 AST节点类型: - Program(statements) - LetStatement(name, value) - IfStatement(condition, then_block, else_block) - WhileStatement(condition, body) - FuncDef(name, params, body) - ReturnStatement(value) - ExpressionStatement(expr) - BinaryOp(left, op, right) - UnaryOp(op, operand) - Literal(value) - Identifier(name) - FuncCall(func, args) - Assignment(name, value) 要求: 1. 实现evaluate方法,接收AST节点和环境,返回执行结果 2. 支持整数、浮点数、字符串、布尔值 3. 支持算术运算、比较运算、逻辑运算 4. 支持函数定义和调用,包括递归 5. 支持return语句,用异常实现非局部返回 6. 附带至少10个解释器测试用例,覆盖作用域、闭包、递归

这个提示词的关键在于:环境模型说清楚、AST节点枚举完整、执行语义明确、测试要求具体。特别是闭包和递归这两个点,如果不明确说,AI大概率会写错。

3.4 用测试驱动的方式让AI自我修正

上面说的都是怎么让AI生成正确的代码,但实际过程中,AI第一次生成的代码几乎不可能完全正确。这时候就需要一套测试驱动的迭代流程。

我的做法是:每完成一个模块,先让AI生成测试用例,然后跑测试,把失败的测试和报错信息一起发给AI,让它修复。这个流程的关键是:报错信息要完整,测试用例要最小化。

比如词法分析器跑测试时,如果某个字符串转义的测试失败了,我会把完整的报错栈和输入输出对比发给AI:

测试用例:输入 'hello\\nworld',期望Token值为 'hello\nworld',实际得到 'hello\\nworld' 报错信息: AssertionError: expected 'hello\nworld', got 'hello\\nworld' 当前词法分析器中处理字符串转义的代码: [粘贴相关代码] 请分析问题原因并给出修复方案。

这种方式比直接说“字符串转义有问题”要高效得多,因为AI能看到具体的输入输出差异和当前实现,修复的准确率会高很多。

4. 实操过程:四个月的具体节奏和关键节点

4.1 第一个月:语言设计和基础设施

第一个月的主要任务不是写代码,而是把语言的设计定下来。这包括:语法长什么样、支持哪些数据类型、有哪些内置函数、错误处理机制是什么、目标运行环境是什么。

我建议在这个阶段用AI做几件事:一是调研现有的小型语言实现,比如Lox、Monkey、Crafting Interpreters里的示例语言,让AI帮你对比它们的特性;二是根据你的需求,让AI帮你起草一份语言规范文档;三是让AI帮你搭建项目骨架,包括目录结构、构建脚本、测试框架。

这个阶段最容易犯的错误是设计过度。很多人一上来就想支持泛型、模式匹配、异步,结果四个月连词法分析都没写完。我的建议是:第一版语言只支持最基本的特性——变量、函数、条件、循环、基本类型。把这些做扎实,比什么都强。

4.2 第二个月:编译器前端和AST

第二个月进入实际编码阶段,核心任务是词法分析器、语法分析器和AST定义。这个阶段AI的参与度可以很高,因为前端部分相对标准化。

具体节奏是:第一周搞定词法分析器,包括Token定义、状态机实现、错误处理、测试用例。第二周搞定AST节点定义和语法分析器的框架。第三周实现所有语法规则的解析函数。第四周做集成测试,确保从源代码到AST的转换完全正确。

这个阶段的关键节点是AST的可视化验证。我让AI写了一个简单的AST打印函数,把解析结果以缩进树的形式输出。这样每加一个语法特性,我都能直观地看到AST结构对不对。这个技巧非常实用,比单纯跑测试用例更容易发现结构性问题。

4.3 第三个月:解释器核心和运行时

第三个月是整个项目最硬核的部分。解释器的实现涉及到环境模型、函数调用、闭包、递归、错误处理,每一个都是容易出bug的地方。

我的做法是先实现最简版本,再逐步扩展。第一周只支持字面量和算术运算,确保基本的evaluate流程跑通。第二周加入变量和赋值,实现环境链。第三周加入函数定义和调用,处理作用域和闭包。第四周加入控制流(if/while/return)和错误处理。

这个阶段AI最大的价值是帮我理解和修复bug。比如闭包捕获变量时出现了引用共享的问题,我把测试用例和当前实现发给AI,它很快就指出了问题所在:环境对象在循环中被复用了,导致所有闭包捕获了同一个环境。修复方案是在每次迭代时创建新的环境。这种问题如果没有AI辅助,我可能要调试很久。

4.4 第四个月:标准库、测试和打磨

最后一个月是收尾阶段。主要任务包括:实现标准库函数(字符串处理、数学运算、类型转换)、完善错误处理、写文档、做性能优化、补充测试用例。

这个阶段AI的作用主要是生成测试用例和边界情况。我让AI针对每个模块生成尽可能多的测试用例,特别是边界情况,比如空输入、超长输入、嵌套调用、类型错误。然后跑测试,修bug,再跑,再修。这个过程很枯燥,但AI可以帮你把测试覆盖率提上去。

最后一周做了一件很重要的事:用这门语言写几个示例程序。比如斐波那契数列、快速排序、简单的状态机。这既是对语言功能的验证,也是最好的文档。如果示例程序跑不通,说明语言还有问题。

5. 常见问题与排查技巧实录

5.1 AI生成的代码接口不一致怎么办

这是最常见的问题。AI在生成多个模块的代码时,很容易出现接口对不上的情况。比如词法分析器返回的Token类型是Token(type, value),但语法分析器期望的是Token(kind, lexeme)。

我的解决方案是:在项目根目录维护一个接口定义文件,把所有模块之间的接口约定写清楚。每次让AI生成新模块的代码时,先把接口定义文件的内容贴到提示词里。这样AI就有了统一的接口参考,生成出来的代码一致性会好很多。

如果已经出现了接口不一致,不要手动改,而是把两个模块的代码和接口定义一起发给AI,让它统一修复。手动改容易引入新的不一致。

5.2 解释器跑着跑着就栈溢出了

这个问题通常出现在递归下降解析器或者递归函数调用上。原因可能是:解析函数没有正确消费Token导致无限递归,或者解释器的函数调用没有正确的终止条件。

排查方法是:先加日志,再缩小范围。在解析函数和evaluate函数的入口加日志,打印当前处理的Token和AST节点。跑一个最小的测试用例,看日志输出到哪一步开始异常。通常很快就能定位到问题。

如果是解析器的无限递归,大概率是某个语法规则的解析函数在遇到不匹配的Token时没有报错,而是继续调用下一个解析函数,形成了循环。修复方法是在每个解析函数的入口检查当前Token是否符合预期,不符合就立即抛出异常。

5.3 闭包捕获的变量值不对

这是解释器实现中最经典的bug。表现是:多个闭包捕获了同一个变量,但期望它们各自捕获独立的值。

根本原因是环境对象的复用。比如在循环中创建闭包时,如果所有闭包共享同一个环境对象,那么它们看到的变量值就是最后一次迭代的值。

修复方法是:每次创建闭包时,复制当前环境,而不是引用。具体实现取决于你的环境模型。如果是用字典实现的环境,可以用dict(env)做浅拷贝;如果是用链式环境,需要创建新的Environment对象,parent指向当前环境。

这个问题的排查技巧是:写一个最小的复现用例,比如在循环中创建两个闭包,分别调用它们,看输出是否相同。如果相同,说明捕获了同一个变量。

5.4 常见问题速查表

问题现象可能原因排查方法修复方案
词法分析器报非法字符状态机没有覆盖所有字符打印当前字符和位置补充状态转移分支
语法分析器无限递归解析函数没有消费Token加日志看Token位置是否前进在解析函数入口检查Token类型
解释器结果不对运算符优先级错误用简单表达式测试检查解析器的优先级层次
闭包变量值共享环境对象被复用写最小复现用例创建闭包时复制环境
递归函数栈溢出没有终止条件或条件错误加日志看递归深度检查终止条件和递归调用
类型错误没有报错类型检查缺失用错误类型测试在evaluate中加类型检查
字符串转义不对转义处理逻辑错误用各种转义字符测试重写转义处理函数
函数返回值丢失return语句实现错误测试带返回值的函数用异常实现非局部返回

5.5 几个独家避坑技巧

第一个技巧:每次让AI生成代码时,要求它同时生成对应的测试用例。这样你拿到代码后可以立即验证,不用自己写测试。而且AI生成的测试用例往往能覆盖一些你没想到的边界情况。

第二个技巧:把AI当成一个需要明确指令的初级工程师,而不是一个能猜你心思的专家。提示词越具体,生成结果越好。不要怕提示词长,把接口定义、数据结构、算法要求、错误处理都写清楚。

第三个技巧:遇到bug时,先写一个最小的复现用例,再发给AI。不要直接把整个项目的代码贴过去,那样AI很难定位问题。最小复现用例能让AI聚焦在具体问题上,修复准确率会高很多。

第四个技巧:定期让AI帮你做代码审查。把当前模块的代码发给AI,让它从可读性、性能、边界情况处理等角度提改进建议。AI往往能发现一些你忽略的问题,比如某个函数没有处理空输入、某个循环可以提前终止。

6. 这套方法还能用在哪些地方

这套“AI辅助+人工把关+测试驱动”的方法,不只适用于写编程语言。我后来把它用在了几个其他项目上,效果都不错。

一个是写正则表达式引擎。正则引擎的核心是NFA/DFA的构建和匹配算法,逻辑复杂但边界清晰,非常适合用这套方法。我让AI先生成NFA的构建代码,再生成DFA的转换代码,最后生成匹配函数,每个模块都有测试用例,整体效率比手写高很多。

另一个是写JSON解析器。这个比编程语言简单,但流程是一样的:词法分析、语法分析、AST、序列化和反序列化。我用同样的提示词策略,一个周末就搞定了。

还有一个是写简单的虚拟机。这个更接近编程语言的运行时部分,涉及到指令集设计、栈帧管理、字节码解释。AI在生成指令实现和栈操作代码时表现不错,但指令集的设计还是得自己来。

核心逻辑是一样的:把复杂系统拆成AI能处理的模块,每个模块有明确的接口和测试,人工负责架构和关键决策,AI负责具体实现和调试辅助。这套方法在需要高度结构化的系统开发中特别有效,因为结构化意味着接口明确、边界清晰,AI不容易跑偏。

最后分享一个我在这个项目里体会最深的事:AI不会让你从不会变成会,但它能让会的人做得更快。如果你对编译原理一无所知,AI生成的代码你根本看不懂,出了问题也不知道怎么修。但如果你有基础,知道编译器大概是怎么回事,AI就是一个非常高效的执行助手。四个月能做出一个能跑的编程语言,前提是你知道每一步该做什么,只是把具体的编码工作交给了AI。这个前提,比AI本身重要得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询