<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>CICD on FF的实验笔记</title>
    <link>http://blog.memcd.com/tags/cicd/</link>
    <description>Recent content in CICD on FF的实验笔记</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <copyright>(c) 1998 - 2026</copyright>
    <lastBuildDate>Wed, 14 Jan 2026 22:34:05 +0800</lastBuildDate>
    <atom:link href="http://blog.memcd.com/tags/cicd/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>AI&#43;CICD: 使用本地大模型进行代码审核</title>
      <link>http://blog.memcd.com/posts/ai/2026-0114-cicd-ai-codereview/</link>
      <pubDate>Wed, 14 Jan 2026 22:34:05 +0800</pubDate>
      <guid>http://blog.memcd.com/posts/ai/2026-0114-cicd-ai-codereview/</guid>
      <description>&lt;h1 id=&#34;aicicd-使用本地大模型进行代码审核&#34;&gt;AI+CICD: 使用本地大模型进行代码审核&lt;/h1&gt;&#xA;&lt;h2 id=&#34;需求来源&#34;&gt;需求来源&lt;/h2&gt;&#xA;&lt;p&gt;疫情后对产品研发工作的最大影响，是随着经济下行，公司在不停地裁员，导致研发团队人力资源严重不足。&#xA;事情多得做不完，过去还在定期组织的质量控制手段&amp;ndash;code review，现在也忙得没有时间组织了。&lt;/p&gt;&#xA;&lt;p&gt;早就听说*-coder系列开源大模型在编码方便挺强悍，于是决定尝试使用本地大模型加入到CICD流程中，在团队提交代码的时候，由AI进行code review工作。&lt;/p&gt;&#xA;&lt;h2 id=&#34;计划&#34;&gt;计划&lt;/h2&gt;&#xA;&lt;p&gt;我们的源代码管理环境使用gitea，只要配置workflow工作流就可以了。在工作流中把diff信息交给本地编程大模型进行评估，并把结果输出，根据结果决定后续行为。&lt;/p&gt;&#xA;&lt;!-- 补充架构图 --&gt;&#xA;&lt;p&gt;所以，我们需要：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;一个workflow配置文件&lt;/li&gt;&#xA;&lt;li&gt;一个程序负责提取diff并调用到模型并反馈结果&lt;/li&gt;&#xA;&lt;li&gt;一个本地大模型服务&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;我们倒序进行实现。&lt;/p&gt;&#xA;&lt;h2 id=&#34;本地编程大模型服务&#34;&gt;本地编程大模型服务&lt;/h2&gt;&#xA;&lt;p&gt;参考《LLM：使用Ollama访问本地大模型》，我们使用Ollama搭建本地编程大模型服务。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -fsSL https://ollama.com/install.sh | sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ollama serve&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编程大模型这里选择口碑极好的qwen3-coder:30b&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ollama pull qwen3-coder:30b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;代码检测程序&#34;&gt;代码检测程序&lt;/h2&gt;&#xA;&lt;p&gt;使用Python调用ollama非常容易，但是考虑到gitea的runner是一个docker环境，在同一的镜像中，运行Python和相关的库需要从网络拉取依赖包会导致一些麻烦。所以这里使用go静态编译，直接生成一个bin文件放在本地文件服务器上，runner直接拉取一个bin就可以工作，不需要任何依赖环境，流程更简洁速度也更快。&lt;/p&gt;&#xA;&lt;p&gt;定义本地ollama地址和模型&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;var&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;ollamaURL&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06287e&#34;&gt;getEnv&lt;/span&gt;(&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;OLLAMA_ENDPOINT&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;http://ai.internal:11434&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;model&lt;span style=&#34;color:#bbb&#34;&gt;     &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;qwen3-coder:30b&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;获取diff信息：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;func&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06287e&#34;&gt;getGitDiff&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#902000&#34;&gt;string&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#902000&#34;&gt;error&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;cmd&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;exec.&lt;span style=&#34;color:#06287e&#34;&gt;Command&lt;/span&gt;(&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;git&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;diff&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;HEAD^&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;HEAD&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;output,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;err&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cmd.&lt;span style=&#34;color:#06287e&#34;&gt;Output&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;err&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;!=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;nil&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&#x9;&lt;/span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;fmt.&lt;span style=&#34;color:#06287e&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;获取 git diff 失败: %w&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;err)&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#x9;&lt;/span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#007020&#34;&gt;string&lt;/span&gt;(output),&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;nil&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后传递这样一个提示词&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
