这个题目我从头到尾做完了一整套源码、论文和演示环境,回头再看,最大的感受是:同类毕设很多,但大部分最后都做成了“给大模型套个聊天框”。我这个项目一开始就定了个规矩——生成出来的代码必须能跑,修复完的 Bug 必须能通过验证,决不搞“看起来对了”的假把式。这篇就把完整的设计思路、模块拆解、提示词细节和踩坑经历都摊开讲,给准备做类似题目的同学一份可以直接照着干的路线图。
整个系统做完之后,可以归纳成两句话:输入需求或 Bug 描述,系统调用大模型生成或修复代码,然后自动执行编译、测试、静态检查三重验证,把真正可用的代码交还给用户。适合正在选毕设题目的计算机相关专业同学,也适合想在本地搭建 AI 编程助手的开发者参考。
1. 为什么这个题目值得做——把“调用 API”包装成“完整系统”
1.1 同样的热门方向,怎么做出差异化
最近几年,“基于大语言模型的 XX 助手”几乎是毕设选题的重灾区。常见版本是前端一个输入框,后端调一次大模型接口,把返回结果直接显示出来,论文写写大模型原理就结束了。这种题目不是不行,而是答辩时老师很容易追问:“你这个系统和直接打开某在线对话工具有什么区别?”
这个项目从一开始就避开了这个致命问题。我把重点放在了闭环验证上:生成代码 → 自动编译 → 自动跑测试用例 → 输出通过结果。这一条链路走完,它就是一个“能自证正确”的系统,而不是一个“看起来在思考”的聊天界面。老师的追问就变成了:“系统的验证机制怎么设计的?”这就有很多内容可以讲,工作量也自然体现出来了。
1.2 核心能力到底覆盖哪些场景
做毕设最怕的就是需求边界不清,做着做着发现什么都要做。这个项目我圈定了三个核心场景,所有工作都围绕它们展开:
- 自然语言生成函数:用户用中文或英文描述“写一个函数,输入一组整数,返回其中第二大的数”,系统生成完整函数代码。
- 代码片段补全:用户在编辑器里写了半截代码,系统根据上下文提示后续内容,类似补全插件但基于大模型实现。
- Bug 识别与修复:用户粘贴报错信息或失败用例,系统定位问题代码,给出修改后的完整函数或补丁,并通过测试验证。
这三个场景对应三个独立模块,但又共用同一套底层大模型调度能力和代码分析工具。模块之间耦合度低,开发顺序可以灵活调整,这对我这种一个人做完整套毕设的节奏非常友好。
1.3 能力边界先想清楚,后面省一半事
我见过太多做类似题目的同学死在了“野心太大”上。一上来就想支持整个 GitHub 仓库的分析、跨文件上下文理解、自动提交 Pull Request,做了三个月连基本的单文件生成都还不稳定。
我的建议是:毕设阶段把边界切在“函数级”和“单文件级”。项目代码规模控制在 500 行以内时,检索和提示词构造的复杂度是可控的;超过这个范围,上下文管理、检索准确率和验证成本都会指数上升。等系统在边界内做到稳定可用,论文里再写“未来可以扩展支持更大规模代码库”,这个扩展是有依据的,而不是空谈。我实际演示的时候就是控制在单文件多函数,效果稳定,答辩演示不会翻车。
2. 系统架构设计——四个模块如何协作变成一份完整源码
2.1 从需求到成品,我把系统拆成了四层
一份能交付的毕设源码,不能只是一个 Python 文件加几个调用大模型的函数。我最终整理出的架构分为以下几层:
| 层级 | 模块名称 | 核心职责 | 技术要点 |
|---|---|---|---|
| 展示层 | Web 操作台 | 交互入口、结果可视化 | 页面表单、代码高亮展示、测试结果面板 |
| 服务层 | 请求调度中心 | 路由分发、任务队列、接口聚合 | 同步处理为主,长任务用异步任务队列 |
| 核心层 | 生成引擎 / 修复引擎 / 验证引擎 | 提示词构造、调用大模型、解析输出、执行测试 | 与具体模型解耦,可切换不同大模型 |
| 基础设施层 | 代码分析器 / 检索器 / 沙箱执行器 | AST 解析、代码切片、向量检索、隔离运行 | 负责“让大模型理解代码”和“验证大模型输出” |
一句话概括数据流向:用户请求进入服务层,核心层根据任务类型构造提示词,调用大模型拿到原始输出,代码分析器对输出做后处理,需要检索时从基础设施层补充项目上下文,最后验证引擎编译并执行测试,把结果返回给展示层。
2.2 技术选型不是堆热门框架,而是看谁最省事
我最终确定的技术栈如下,每项都写了我选它的理由:
- 开发语言:Python 3.11。代码生成和 Bug 修复的生态工具(AST 解析、测试框架、机器学习相关库)几乎全在 Python 里,不需要额外造轮子。
- Web 框架:FastAPI。轻量、异步支持好,同时自带接口文档,调试方便。比 Django 轻,比 Flask 多出类型校验和自动文档。
- 前端:Vue 3 + 页面组件库,或者干脆用 Streamlit 搭原型。前者适合展示完整系统,后者适合三天内跑通演示。我实际交付用的是 Vue,但开发早期用 Streamlit 验证核心逻辑,速度极快。
- 大模型接入:抽象接口层,底层具体接什么模型可以随时换。本地跑用开源代码模型(类似 CodeLlama 系列、CodeQwen 系列,支持代码生成的那种),也可以接入商用闭源 API 服务。毕设想省事的话接闭源 API 效果最好,但要注意调用成本和答辩现场的网络依赖。
- 代码检索:向量数据库存储代码片段向量,本地轻量方案用文件型向量库就够,不需要部署独立的数据库服务。
- 隔离执行:Docker 容器。生成代码不可信,必须在隔离环境里编译和测试。
这套方案的核心是**“模型可替换、验证不可绕过”**。大模型只是一支笔,验证引擎才是裁判。这个设计在答辩时特别好讲清楚。
2.3 为什么要用“三层验证”,而不是让大模型自己判断对错
这是整个项目最关键的架构决策。大模型生成代码之后,如果只问它“你觉得这代码对吗”,它大概率会说“对”,哪怕里面有个变量都没定义。所以我把验证设计成与模型完全无关的三层关卡:
- 语法层:用语言自带的编译器/解释器做语法检查,比如 Python 用
ast.parse或直接py_compile,C++ 用g++ -fsyntax-only。 - 静态分析层:检查未定义变量、未导入模块、函数签名不匹配等基础错误,这部分用代码分析器从 AST 里提取结果。
- 动态执行层:构造测试用例,在沙箱里运行被生成的代码,对比输出和期望值。
只有三层全过,代码才算“生效”。这个思路不新颖,但确实是最可靠的。大模型负责提供候选解,工程手段负责筛掉不对的解,两个角色各司其职。
3. 代码自动生成模块——提示词设计和后处理决定成败
3.1 提示词模板:把“写代码”约束成“按规格填空”
我之前犯过的错误是给模型一句“帮我写个排序函数”就让它自由发挥。结果代码风格飘忽不定,输出里带解释文字,甚至把多个候选函数混在一起。后来我把提示词设计成了固定模板:
你是一名资深软件工程师,请根据以下需求生成Python函数代码。 需求描述:{user_requirement} 函数签名约束:{optional_signature} 依赖导入:{optional_imports} 代码风格:{style_guide} 输出要求: - 只输出代码,不要输出任何解释性文字 - 使用4空格缩进 - 如果需求不明确,先给出你的假设,再写代码 - 代码要包含必要的类型注解这个模板的核心思想是**“责任前置”**。与其等模型乱来再清洗,不如在提示词里把所有边界条件写死。实测下来,加了“只输出代码”这一条之后,后处理的工作量减少了七成。
3.2 多轮对话式生成:把大需求拆成小步骤
用户的需求不会永远是一句话能说完的。比如“写一个带登录功能的 Todo 管理程序”,这种需求直接丢给模型,它往往输出一坨无法运行的“伪完整代码”。我的做法是把需求拆成子任务链:
- 第一轮:让模型把整体需求拆成功能点清单。
- 第二轮:针对每个功能点生成独立的函数。
- 第三轮:生成组装代码,把各函数串起来。
这一步的本质是把大模型当成结对编程的同事,而不是一次性写完整程序的神仙。我实测过同一需求,直接生成的成功率约三成,拆成三步之后成功率能到七成左右。
3.3 生成结果的后处理:从“模型输出”到“可执行代码”
后处理大概是整个模块里最琐碎但最必要的部分。我写了一个专门的处理器,依次做了这几件事:
- 提取代码块:用正则匹配输出里的 Markdown 代码块,优先取第一个。
- 去除多余内容:删掉模型自带的注释说明(保留代码内注释)、多余空行、行号。
- 括号配对检查:快速扫描一遍圆括号、方括号、花括号数量,不配对就回退重生成。
- 缩进修复:Python 对缩进敏感,模型偶尔会用 Tab 和空格混用,这里统一转成 4 空格。
- 未定义符号检查:用 AST 提取代码里的所有 Name 节点,找出未被赋值、未导入的标识符。
后处理不是为了追求完美,而是把明显不合格的输出提前拦下来,不给下游验证引擎增加负担。算下来这一步能把最终成功率提高十到十五个百分点,属于投入产出比极高的一环。
3.4 温度参数调优:生成 vs 修复要区别对待
大模型的采样参数很多人忽略,但这其实是个很实际的调优点。我的经验是分任务类型设置不同参数:
| 任务类型 | 温度参数 | 原因 |
|---|---|---|
| 代码生成 | 0.2~0.3 | 偏低温度让输出更稳定,减少随机性导致的语法错误 |
| Bug 修复 | 0.3~0.5 | 修复需要一点发散空间,温度太低会不断重复同样的错误解法 |
| 需求拆分 | 0.5~0.7 | 偏高的温度让拆解思路更多样,避免套模板 |
不过参数这东西跟具体模型强相关,不同模型的最优区间差异不小,建议做一个参数配置项,用一个小批量测试集去试。
4. Bug 修复助手——从报错信息到验证通过的一整条闭环
4.1 输入模式设计:兼容开发者习惯的三种给法
Bug 修复模块我设计了三种输入模式,覆盖实际开发中最常见的场景:
- 粘贴报错堆栈:模型从堆栈里提取出错文件和行号,再定位代码。
- 粘贴失败用例:给定输入、期望输出、实际输出,让模型推测哪里逻辑不对。
- 直接描述问题现象:“这个函数返回结果偶尔为负数,找不到原因”,这种最考验模型推理。
实际开发中用户根本不会规范地描述 Bug,所以这个模块的核心能力其实是**“从模糊信息里猜出问题范围”**。我的做法是把用户给的原始信息完整丢进提示词,同时附上相关代码片段,让模型自己判断问题到底出在哪。
4.2 代码定位策略:AST 解析 + 行号映射双保险
Bug 修复最难的不是“改”,而是“找到改哪里”。我做了两层定位机制:
第一层,如果用户给了报错堆栈,那就解析堆栈拿到File "xxx.py", line N行号,然后切出该行所在函数;第二层,如果没有报错堆栈,就用代码分析器把文件拆成函数列表,逐个函数做一轮“可疑度预筛选”,把可能涉及用户描述功能的几个函数一起喂给模型。
这里有个很值得说的细节:大模型定位错误的能力其实弱于它修复错误的能力。也就是说,如果问题定位不准,后面修复得再漂亮也没用。所以我把定位环节尽可能用规则和静态分析解决,不依赖模型直觉。
4.3 修复迭代循环:一次不对就跑第二轮,而不是直接放弃
修复部分我设计了一个最大轮数为 3 的循环:
- 模型生成修复后的代码。
- 静态检查 + 执行已有测试用例。
- 如果测试失败,把新的报错信息追加到上下文里,带着失败信息再问一次模型:“上次结果未能通过测试,以下是报错信息,请分析原因并重新修复。”
这个循环机制特别好用的一点是:它把“测试失败”变成了“下一轮修复的提示词输入”,相当于给模型配了一个会反馈的陪练。实测中,第一轮修复成功率大概在百分之四五十,三轮叠加后能到七十多,这个数字在答辩时非常有说服力。
4.4 防止“幻觉修复”:为什么必须要验证闭环
我见过太多大模型修复 Bug 的 Demo 只展示到“模型给出的修复建议”就结束了。问题是,修复建议经常是错的,而且错得一本正经。
比如我曾经测试过一个排序相关的 Bug,模型信誓旦旦地说问题出在“边界条件处理不当”,给出的新代码实际上把原来的正确逻辑都改坏了。如果没有自动执行测试,这种错误建议根本不会暴露。所以我无论宣传什么功能,都坚持一句:所有修复建议必须经过测试用例验证,未通过验证的修复会明确标注“未验证”。这是整个系统信任感的基石。
5. 代码检索与上下文增强——让大模型懂你的项目
5.1 不检索的模型就是个“失忆的程序员”
裸调大模型写代码,它唯一的依据是训练时见过的公开代码。你的项目里自定义的函数名、特定的变量命名风格、内部工具库的调用方式,它一概不知。早期我做过一个很直观的实验:让模型写一个调用了项目自定义工具函数的代码,它凭空编造了一个函数名,看起来很像真的,跑起来直接报错。
解决这个问题就得靠检索增强生成(RAG)。它的作用是把项目里和当前任务最相关的代码片段取出来,塞进提示词上下文,让模型“看得见”你的项目再动手。
5.2 索引怎么建:按函数切块比按固定长度切块好用得多
代码检索和普通文本检索有个关键区别:普通文本按固定字符长度切块没问题,但代码如果从函数中间切开,语义就被破坏了。我最终用的是 AST 辅助切块:
- 用代码分析器解析项目文件,提取所有函数、类、import 语句。
- 以函数和类为基本块,保留其所在文件的路径和行号范围作为元数据。
- 把每个代码块向量化之后存入向量数据库。
- 额外建一个“符号索引”,记录项目里定义了哪些函数名和类名。
这个策略的聪明之处在于:向量检索负责模糊语义匹配,符号索引负责精确映射。比如用户提到“那个处理登录的函数有问题”,向量检索能找到语义相关的候选块;如果用户直接说“login_user 函数”,符号索引能立刻定位。
5.3 混合检索:向量 + 关键词,别让语义搜索唱独角戏
纯向量检索有个问题:对函数名、变量名这类精确字符串不敏感,经常因为拼写差异或罕见命名方式把真正相关的代码漏掉。我最后采用的是混合检索策略:
- 向量检索:用用户的自然语言描述去匹配语义相关的代码块,取 Top 3。
- 关键词检索:提取用户输入里的标识符(正则匹配驼峰/下划线命名的单词),在符号索引里精确匹配,取 Top 2。
- 合并去重:两路结果合并,按综合相关度排序,最多往提示词里注入 5 个代码块。
这样即使某一路检索失效,另一路还能兜底。实测下来混合检索比单用向量检索的命中准确率高出近二十个百分点,代码检索这个领域,精确匹配的价值被很多人低估了。
5.4 上下文窗口管理:塞太多反而让模型“迷路”
另一个我反复调试的问题是:到底给模型塞多少上下文合适?
一开始我贪心,把检索到的 5 个代码块连同整个文件一起塞进去,结果模型反而抓不住重点。后来我把注入策略改为分层:最核心的相关代码块放在提示词最前面作为“项目上下文”,次要相关代码作为附录放在用户问题之后。主次分明之后,模型的表现明显提升。
控制经验总结成一条:单次任务注入的代码总量建议控制在 300 行以内,超出部分宁可不注入也不要一股脑全塞。大模型的注意力是有限的,信息密度比信息数量重要得多。
6. 评测设计与交付物打磨——让答辩老师一眼看到工作量
6.1 不用“感觉好用”交差,用数据证明系统有效
毕设答辩最怕的是老师说“你这个效果不错,但有没有量化数据支持”。所以我花了两周时间专门搭了一套评测流程,最终产出了五组关键数据:
- 编译通过率:生成代码中一次通过语法和编译检查的比例。
- 单测通过率:在测试集上,生成代码通过全部预设用例的比例。
- 修复成功率:用户提交的 Bug 中,系统通过三轮迭代内修复并验证通过的比例。
- 平均响应时长:单次生成/修复请求从提交到返回完整结果的时间。
- 成本统计:一次请求平均消耗的 Token 数量,换算出单次成本。
评测集我用了两部分:一部分来自公开的代码生成基准测试,另一部分是自己在课程实验和开源小项目里挑的 20 个有代表性的编程任务,手动编写了测试用例。这里有个特别重要的经验:不要只报告“生成成功”,一定要报告“测试通过”。生成成功的代码可能充满潜在 Bug,只有通过用例才具备真正的说服力。
6.2 源码包目录结构:老师会按这份地图检查你的项目
源码交付的目录组织,直接影响老师对项目规范性的第一印象。我最终整理成的结构是:
code_assistant/ ├── README.md # 项目简介、环境依赖、快速启动指南 ├── requirements.txt # Python 依赖清单 ├── config/ │ ├── settings.yaml # 模型选择、参数配置、接口密钥 │ └── prompts/ # 提示词模板目录 ├── core/ │ ├── generator.py # 代码生成引擎 │ ├── fixer.py # Bug 修复引擎 │ ├── verifier.py # 三层验证引擎 │ └── router.py # 请求调度 ├── analysis/ │ ├── ast_parser.py # AST 解析与代码切片 │ ├── symbol_index.py # 符号索引维护 │ └── context_builder.py # 提示词上下文构造 ├── retrieval/ │ ├── vector_store.py # 向量索引与检索 │ └── hybrid_search.py # 混合检索 ├── sandbox/ │ ├── docker_executor.py # 容器隔离执行 │ └── test_runner.py # 测试用例运行 ├── web/ │ ├── main.py # FastAPI 入口 │ └── templates/ # 前端页面 ├── tests/ # 系统自身的单元测试和集成测试 ├── eval/ │ ├── dataset.py # 评测数据集加载 │ ├── metrics.py # 指标计算 │ └── run_eval.py # 一键评测脚本 └── docs/ ├── design.md # 详细设计文档 ├── api.md # 接口文档 └── user_manual.md # 使用手册这个目录最大的好处是:核心逻辑、代码分析、检索、验证、Web、评测全都分离,每一块都能单独讲、单独测,答辩时你可以顺着目录讲一整套工程化设计。
6.3 答辩演示脚本:先抛高光,再补细节
演示环节我排练过很多遍,最后固定了一套流程,分享给你参考:
- 开场 1 分钟:展示系统界面,故意输入一个带隐式错误的需求描述,让模型先定位问题再生成代码,展示系统的“需求澄清”能力。
- 高光 2 分钟:在编辑器里提前藏一个 Bug——这个 Bug 最好是逻辑错误而不是语法错误,比如数组边界少减 1——让系统自动定位、修复、跑测试,测试通过后展示前后对比。
- 加分 1 分钟:展示评测报告里的“修复成功率”和“三轮迭代曲线”,证明系统不是碰运气。
- 兜底预案:提前录制好一份完整效果演示视频。现场如果 API 延迟或模型抽风,直接放视频,同时口头补充讲解。这一条一定要做,我实测现场演示因为网络问题翻过车,视频预案救了场。
6.4 论文写作巧思:把重点放在“验证闭环”而非“模型调优”
论文架构我是围绕“生成—修复—验证”这个闭环来组织的。关键词按权重排序是:大语言模型、代码生成、Bug 修复、静态分析、测试验证、检索增强。目录大概分七章:绪论、相关技术、需求分析、系统设计、系统实现、实验与评估、总结与展望。
写论文最难的是“相关工作”部分怎么体现差异化。我的处理方式是:过往研究大多关注“怎么让模型生成的代码更长”,我这篇关注“怎么让模型生成的代码能运行”。这个角度一摆出来,整篇论文的创新点就立住了。
7. 开发与调试中的高频坑位——每一条都是真金白银换来的
7.1 报错行号对不上,排查了两天才发现是编码问题
第一次做 Bug 定位时,模型报错说“第 47 行有语法错误”,我打开文件一看 47 行是个空行,完全没问题。后来发现是文件编码在 Windows 上被转成了带 BOM 的格式,行号偏移了。后来我统一所有代码文件用 UTF-8 无 BOM 保存,并且解析报错堆栈时先做行号校准。这类细节特别浪费时间,但踩过一次就不会再犯。
7.2 模型把测试代码和实现代码混在一起输出
我遇到过一个很头疼的情况:模型生成修复代码时,自己“贴心”地加了一段测试代码放在文件末尾,导致验证引擎把测试代码当成主程序运行,输出完全错乱。解决方案是在后处理阶段用 AST 识别if __name__ == "__main__"和裸调用语句,把与主需求无关的代码剥离,或者在提示词里加一句“不要输出测试代码”。两面夹击之后这个问题基本绝迹。
7.3 沙箱执行的安全边界:不能拿宿主机直接跑
生成代码里可能藏了删除文件、读取环境变量、无限循环之类的操作,这些在毕设阶段虽然大概率是模型生成的“假代码”,但安全习惯必须养好。我的做法是:跑测试一律在 Docker 容器里执行,容器网络关闭,只挂载临时代码目录,并且设置执行超时(Python 默认 10 秒,C++ 编译加运行 15 秒)。这一步不仅为了安全,还能防止模型生成死循环代码把整个系统卡死。
7.4 Token 成本控不住?加缓存和小模型路由
用商用闭源大模型 API 做毕设,最容易翻车的就是账单。我的成本控制方案有三条:一是把用户检索过的代码块和模型生成过的高质量结果缓存到本地,相同请求直接复用;二是简单任务(比如变量重命名、格式化)用更小的模型处理,只有复杂生成和修复才用大模型;三是在提示词里明确要求“优先复用给定的项目上下文,减少无效输出”。三条叠加后,单次请求成本能降一半以上。这个数据放到论文里也挺能说明工程能力的。
7.5 换一个模型,全套参数就得重新调
本地开源代码模型和商用闭源 API 的提示词敏感度、输出格式稳定性完全不一样。我在开发时用本地模型调试逻辑,结果切换到商业 API 后,提示词模板里的“只输出代码”指令被无视了,原因是不同模型的指令遵循能力不同。后来我做了 Prompt 模板版本管理,每个模型配一套专属模板和参数。这个坑提醒我:不要把模型当成可以随意插拔的标准化组件,它们的脾气各不相同。
8. 后续还可以怎么扩展——几个我自己验证过值得做但没时间做的方向
如果时间充裕,我建议在这个底座上扩展三个方向,每个都能写成一篇不错的延伸论文或作为面试项目亮点:
- 历史 Bug 模式学习:把用户提交过的 Bug 和修复方案积累下来,定期微调一个小模型,让修复建议越来越贴合项目的独特写法。这能实现“越用越懂你的项目”的效果。
- 多文件跨模块修复:目前定位范围还局限在函数级和单文件级,扩展的方向是让检索器具备“调用链追踪”能力——从入口函数出发,沿着函数调用关系把相关文件都找出来,一起提供给模型。
- Web 端在线协作模式:把验证结果和修复建议以代码审阅评论的形式展示在编辑器里,人机协作完成修改,而不是整个文件替换。这个方向更贴近真实开发工作流,也更容易讲出工程价值。
我在实际开发中的体会是:这个项目真正的难点不在“接大模型”,而在“让大模型输出能通过验证”。接模型半天就够,但把验证闭环打磨到稳定可靠,我花了两周以上的时间。如果你也打算做类似方向,我建议把时间分配按“模型接入 : 代码处理 : 验证闭环 : 界面与评测 = 1 : 3 : 4 : 2”来规划,不要在前端和 API 接入上消耗太多精力,把资源集中到“验证”这个别人替代不了的核心上。
最后再分享一个很实用的小技巧:调试阶段把每一次大模型的输入输出完整记录到日志文件里,配上时间戳和任务类型。这不仅能帮你回溯问题,写论文做案例分析的时候,这些日志就是最真实的实验数据,比任何编造的例子都有说服力。我论文里的三个失败案例和两个成功案例,全部出自这份日志。