写提交信息是开发者天天要做、却很少有人享受的小事。调完一个bug、实现完一个功能,还要把改动描述清楚,这种摩擦感并不必要。GAI 就是冲着这一段流程来的:不依赖外部大模型接口,在本地把提交信息生成出来。
它的做法不是把整份 Git diff 直接丢给模型。GAI 会分析已暂存的改动,从历史提交数据里检索相似的提交,再用本地运行的语言模型生成一条符合仓库既有标签习惯的提交信息。两条路径并行:一条是检索增强生成(RAG),用嵌入向量和 FAISS 找出历史上相似的改动;另一条是聚焦改动的 diff 解析,抽出文件路径、hunk、新增行、删除行和相关上下文,让生成模型更容易看懂改了什么。
工作流按顺序走下来是这样的:
- 运行 gai,校验当前目录是不是 Git 仓库;
- 读取已暂存的 diff,没有暂存改动就报错退出;
- 保留一份原始 diff 快照,同时按文件和 hunk 解析,抽出新增行、删除行与上下文,格式化成结构化 diff;
- 用原始 diff 生成嵌入向量,在 FAISS 索引里检索 top-k 条历史提交;
- 把结构化 diff 和检索到的历史提交一起拼成生成提示词,通过 Ollama 调用本地 Gemma 模型;
- 解析生成结果,校验有效性,展示建议的提交信息。
生成出来的信息不会自动提交。用户可以接受、编辑、重新生成,或者直接取消。选择接受之前,GAI 会再读一次当前的暂存 diff,确认从生成信息到现在改动没有发生变化;如果变了,就中止提交并要求重新运行。这一步是刻意保留的:把无聊的部分自动化,不等于交出最终提交的控制权。
实际用起来是三步。先 git add 暂存改动,用 git diff --cached --stat 看一眼;再运行 gai,它会检索相似历史提交、生成信息,交给用户审阅;最后可选地用 git log -1 --oneline 核对提交结果。GAI 用 Python 实现,也可以通过 Python CLI 调用,例如 python -m src.cli,检索的历史样例数量可以控制,比如 python -m src.cli --top-k 3。
当前实现用到 Ollama、FAISS、EmbeddingGemma 和 Gemma4:e2b。索引流程从带标签、信息和 diff 的历史提交数据开始,用 EmbeddingGemma 为可用的历史 diff 生成嵌入,存进 FAISS 的 IndexFlatL2 索引,对应的提交元数据单独存放,这样检索出的向量能映射回原始信息和标签。运行时,原始 diff 用于检索,解析格式化后的表示则给生成模型一个更清晰的改动视图。
一个待解的难题是较小的本地模型怎么处理大 diff。单纯加大上下文窗口并不总是最优解,给模型结构更好的信息可能更有用。下一步计划是搭一条并行的 LangGraph 工作流,分两条生成路径:一条沿用当前基于历史提交的检索链,另一条先按文件和 hunk 汇总大改动、再生成另一个候选。评估器会从正确性、相关性、具体程度和标签有效性几个维度给两个候选打分,再由归约器选出更强的一个,必要时合成更好的结果。这些 LangGraph 链、评估器和归约器都属于后续改进,尚未进入当前实现。
为什么这件事值得开源来做?GAI 想要生成的是反映作者真实提交习惯的信息,而不是对代码改动的泛泛描述。检索历史提交让 Gemma 拿到仓库标签习惯和写作风格的具体样例;嵌入和生成都走 Ollama 本地运行,Git diff 里可能包含的专有实现细节不必离开本机;生成模型、提示词、检索策略都可以换着试,不用围绕某一家托管 AI 服务商重新设计应用。有意思的地方不在于本地跑了个大模型,而在于把开放模型和仓库自身的历史结合起来,做出贴合开发者工作方式的提交信息生成器。
目标很朴素:把一件重复的事从开发循环里拿掉。不用从写代码切换到琢磨提交信息的措辞,暂存改动、运行 gai、看一眼建议,然后继续。接下来还打算试验并行生成链、分层 diff 摘要,以及评估器与归约器的工作流,来提升生成信息的质量。
热门跟贴