6.3 代码生成与程序合成


6.3 代码生成与程序合成

本节摘要:代码生成任务是 DSPy 家族里最"幸运"的场景——它拥有确定性裁判:代码要么跑得过测试要么跑不过。本节讲如何把这份红利兑换成编译优势:以执行通过率为指标的自举闭环、ProgramOfThought 模块的适用边界、数据合成任务的实操案例,以及把生成代码当不可信输入处理的安全纪律。

代码生成任务的独特难点与独有红利

难点有两重。其一,容错率低:自然语言答案差一个措辞还算对,代码差一个字符就是异常。其二,正确性难以静态判断:代码"看起来对"毫无意义,只有执行结果作数。而红利恰恰长在难点上——因为必须执行,所以有确定性裁判;因为有确定性裁判,指标的模糊地带归零:exact_match 在问答任务里被同义表述困扰的问题,在代码任务里被测试用例彻底解决。这让代码生成成为自举机制效果最好的任务类型:程序自己生成的代码,测试跑过了就是合格的示范,没有任何争议空间。

生成即验证的闭环

用 ProgramOfThought 或普通 Predict 生成代码,外部执行器跑测试,结果回馈给指标——闭环的骨架如下:

import dspy class WriteFunction(dspy.Signature): """根据需求描述写出 Python 函数,只输出函数定义。""" requirement = dspy.InputField(desc="功能需求与边界条件说明") function_code = dspy.OutputField(desc="完整的 Python 函数定义") class CodeSolver(dspy.Module): def __init__(self, runner): super().__init__() self.write = dspy.ChainOfThought(WriteFunction) self.runner = runner # 沙箱执行器:跑测试用例 def forward(self, requirement, test_cases): pred = self.write(requirement=requirement) passed = self.runner.run(pred.function_code, test_cases) if not passed: # 带失败信息重写一次:把报错与用例输出回喂给模型 pred = self.write( requirement=requirement + "\n上次尝试未通过测试,失败信息:" + passed.failure_log ) return dspy.Prediction(code=pred.function_code, passed=passed.ok)

指标直接消费执行结果:

def code_metric(pred, example, trace=None): # 执行器在外部已跑完;这里只比对通过状态 return bool(pred.passed) == bool(example.should_pass)

编译时这个闭环威力翻倍:自举过程中,每个训练样本都是"需求加测试用例",程序生成的代码跑过测试的轨迹回收为示范——模型见到的每条范例都是"真实通过测试的代码",这种示范质量是人工标注无法企及的(人标注答案是快的,人写带测试的完整代码示范反而慢)。实测经验上,五十条带测试的需求样本,一轮自举后通过率常能从小模型的四成上下拉到七成以上;这是所有任务类型里自举收益最陡峭的曲线。

ProgramOfThought 的适用边界

顺带把模块的适用边界讲清。ProgramOfThought 让模型生成代码、由运行时执行取结果,它的正确定位是"确定性计算的外包":日期推算、单位换算、集合运算、统计汇总——凡是模型"心算"容易错而代码"实算"必然对的场景。它的边界同样明确:需要常识判断与语义理解的部分不该塞给代码("这段评论的情感倾向"不是可执行的算式);执行环境必须受控(沙箱、超时、资源限额),因为生成的代码是不可信输入——这一点无论用于推理还是交付都成立。

数据合成是代码生成的一个高价值变体:让模型生成"产生指定特征数据的程序"而不是数据本身。比如需要一万条不同形态的测试数据,与其让模型直接吐数据(重复率高、可控性差),不如让它写生成函数(参数可控、可复现、可审计)。DSPy 化的写法与上面同构:需求签名描述数据特征,执行器跑函数抽样,指标校验样本特征分布。这种"要鱼不如要渔"的思路在测试构造、仿真数据、脱敏样本等场景都有直接应用。

安全纪律:生成的代码是不可信输入

本节的安全纪律值得单独成段。模型生成的代码必须当作不可信输入处理,三条底线:执行环境隔离(容器或进程级沙箱,禁文件系统与网络访问,除非任务明确需要且另行管控);资源限额(超时与内存上限必须设置,防止生成代码里的意外死循环拖垮服务);输出审查(交付给下游的代码过一遍静态检查——禁用危险调用、强制类型注解,这类检查用规则实现,别用模型自查自)。数据合成场景的额外一条:合成样本使用前抽样人工复核,模型生成函数的"特征理解偏差"会批量复制到每个样本,人审是唯一的止损点。

这三条底线与 5.3 节的断言机制衔接自然:把"静态检查通过"做成出口断言,把"执行超时"做成回溯触发,安全约束就融入了程序结构而非依赖人的自觉。还有一个工程细节值得记录:执行器要把测试失败信息做成结构化的回喂材料——报错类型、失败用例、期望与实际的差值,按固定格式拼进重写提示。失败的回喂质量直接决定重写的成功率,模糊的"没通过"三个字与详细的失败报告,在重写通过率上能差出数倍;这一条与 4.4 节的自举逻辑同源——模型进步的原料永远是高质量的反馈信号。

本节要点回顾

  • 红利兑换:测试用例是确定性裁判,指标模糊地带归零;代码生成是自举收益最陡的任务类型,带测试的需求样本五十条即可起步。
  • 闭环结构:生成、执行、失败信息回喂重写;编译时执行通过率直接做示范筛选指标。
  • 模块定位:ProgramOfThought 是确定性计算的外包,常识判断不外包;执行环境永远受控。
  • 数据合成变体:要生成函数不要数据本体,参数可控、可复现、可审计;样本人工抽样复核是止损点。
  • 安全三底线:沙箱隔离、资源限额、静态审查;与断言机制结合,让安全融入程序结构。
  • 通向下一节:代码之外还有一类产物没有硬裁判——文章、文案与报告。主观任务的编译之道,下一节展开。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U