打造 AI 驱动的 Git 提交规范助手:基于 React + Express + Ollama+langchain 的全栈实践

在现代软件开发中,高质量的 Git 提交信息不仅是团队协作的基础,更是项目可维护性、可追溯性和工程文化的重要体现。然而,许多开发者(尤其是初学者)常常忽略提交信息的规范性,导致提交日志混乱、难以理解,甚至影响 Code Review 和故障排查效率。

为了解决这一痛点,本文将带你从零构建一个 AI 驱动的 Git 提交规范助手 ------ git-differ 。该项目结合了前端(React + Tailwind CSS + Axios)、后端(Node.js + Express)以及本地大模型(Ollama + DeepSeek-R1:8B),通过分析 git diff 内容,自动生成符合 Conventional Commits 规范的提交信息。

我们将深入剖析项目的代码结构、技术选型与关键知识点,并围绕 跨域处理、LangChain 集成、自定义 React Hook 封装、Express 路由设计 等核心内容展开详细讲解。


一、项目整体架构与技术栈

git-differ 是一个典型的前后端分离全栈应用:

  • 前端 :运行于浏览器(如 http://localhost:5173),使用 React 构建 UI,Tailwind CSS 实现响应式样式,Axios 发起 HTTP 请求。
  • 后端 :运行于 Node.js 环境(http://localhost:3000),基于 Express 框架提供 RESTful API。
  • AI 引擎 :本地部署 Ollama 服务(http://localhost:11434),加载开源大模型 deepseek-r1:8b,通过 LangChain 进行提示工程与输出解析。

整个数据流如下:

bash 复制代码
用户输入 git diff → 前端发送 POST /chat → 后端接收并调用 Ollama → AI 生成 commit message → 返回前端展示

这种架构不仅解耦清晰,还便于后续扩展(如支持多模型、历史记录、配置管理等)。


二、前端实现:React + 自定义 Hook + Axios 模块化

1. 主组件 App.jsx:UI 与逻辑分离

javascript 复制代码
import { useEffect } from "react"
import { chat } from './api/axios'
import { useGitDiff } from "./hooks/useGitDiff"

export default function App(){
  const { loading, content } = useGitDiff('hello')

  return (
    <div className="flex">
      {loading ? 'loading...' : content}
    </div>
  )
}

主组件极其简洁,仅负责渲染状态。真正的业务逻辑被封装在 useGitDiff 自定义 Hook 中,体现了 "组件只负责 UI" 的最佳实践。

"use开头 封装响应式业务 副作用等 从组件里面剥离 组件只负责UI"

这种模式极大提升了代码的可读性与复用性。未来若需在多个页面使用 AI 生成 commit message 功能,只需调用 useGitDiff 即可。


2. API 层:Axios 模块化封装

javascript 复制代码
// src/api/axios.js
import axios from 'axios'

// 创建axios实例 统一进行配置
const service = axios.create({
  baseURL: 'http://localhost:3000',
  headers: {
    'Content-Type': 'application/json'
  },
  timeout: 60000
})

export const chat = (message) => {
  return service.post('/chat', {
    message
  })
}

这里通过 axios.create() 创建了一个 专用的 HTTP 客户端实例,统一配置:

  • baseURL:避免在每个请求中重复写后端地址;
  • headers:确保请求体为 JSON 格式;
  • timeout:设置超时时间,防止请求卡死。

"模块化 在api目录下管理所有的请求"

封装了api请求,在其他组件(如:useGitDiff.js)中只需要模块化导入chat 即可以发起请求,这种组织方式是大型 React 项目的标准做法,便于维护和测试。


3. 自定义 Hook:useGitDiff ------ 封装副作用与状态

scss 复制代码
// src/hooks/useGitDiff.js
import { useState, useEffect } from "react"
import { chat } from "../api/axios"

export const useGitDiff = (diff) => {
  const [content, setContent] = useState('')
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    (async () => {
      if(!diff) return 
      setLoading(true)
      const { data } = await chat(diff)
      setContent(data.reply)
      setLoading(false)
    })()
    // 立即执行异步函数  避免顶层async
  }, [diff])

// 将外部需要的loading状态和content内容 return
  return {
    loading, // 加载状态 用户体验
    content // llm得出的commit message
  }
}

该 Hook 接收 diff 字符串作为依赖,当其变化时自动发起请求。关键点包括:

  • 使用 立即执行异步函数(IIFE)useEffect 中处理异步逻辑;
  • 通过 setLoading 提供加载状态反馈;
  • 依赖数组 [diff] 确保只在 diff 变化时触发请求;
  • 返回结构化对象,便于解构使用。

"通过立即执行函数 执行异步函数"

这种写法规避了 useEffect 的回调函数不能直接使用 async/await 的限制。


三、后端实现:从零构建一个健壮的 Express AI 服务

后端是整个 git-differ 项目的中枢神经。它不仅要接收前端请求、调用本地大模型,还需保证安全性、稳定性、可维护性与协议规范性。以下我们将结合完整代码,逐层剖析其技术内涵。

1. 基础服务初始化:Express 应用骨架

javascript 复制代码
import express from 'express'
import cors from 'cors'

const app = express()
app.use(express.json())
app.use(cors())

app.listen(3000, () => {
  console.log('server is running on port 3000')
})

这段看似简单的代码,实际上完成了现代 Web API 服务的三大基础配置:

express():创建应用实例

  • Express 是 Node.js 生态中最流行的 Web 框架,其核心思想是 "中间件管道"
  • app 是一个可配置、可扩展的 HTTP 服务器容器,后续所有路由、中间件都挂载于此。

app.use(express.json()):请求体解析中间件

  • 默认情况下,Express 不会自动解析请求体req.bodyundefined)。
  • express.json() 是一个内置中间件,用于解析 Content-Type: application/json 的请求体,并将其转换为 JavaScript 对象。
  • 若省略此中间件,req.body 将无法获取前端发送的 { message: "..." } 数据,导致后续逻辑失败。

💡 最佳实践 :应在所有路由定义之前注册全局中间件,确保所有请求都能被正确解析。

app.use(cors()):跨域资源共享(CORS)支持

  • 前端开发服务器(如 Vite,默认端口 5173)与后端(3000)构成跨域请求(协议、域名或端口不同)。

  • 浏览器出于安全考虑,会拦截跨域请求的响应,除非服务器明确允许。

  • cors() 中间件自动处理 OPTIONS 预检请求,并在响应头中添加:

    makefile 复制代码
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
    Access-Control-Allow-Headers: Content-Type
  • 在生产环境中,应限制 origin 为可信域名(如 origin: ['https://your-app.com']),避免开放全站跨域。


2. 路由设计:RESTful API 与 HTTP 语义

dart 复制代码
app.get('/hello', (req, res) => {
  res.send('hello world')
})

app.post('/chat', async (req, res) => {
  // ... 业务逻辑
})

📌 路由方法与资源操作语义

  • GET /hello:用于健康检查或简单测试,无副作用,符合幂等性。

  • POST /chat:用于提交 git diff 内容并获取 AI 生成结果。使用 POST 是因为: 请求包含复杂数据体(diff 文本可能很长);

📌 请求与响应对象(req / res

  • req.body:由 express.json() 解析后的请求体数据;
  • res.status(code).json(data):设置 HTTP 状态码并返回 JSON 响应;
  • res.send():返回纯文本(不推荐用于 API,应统一使用 JSON)。

3. 输入验证:防御性编程的第一道防线

csharp 复制代码
const { message } = req.body
if (!message || typeof message !== 'string') {
  return res.status(400).json({
    error: 'message 必填,必须是字符串'
  })
}
  • 永远不要信任客户端输入,后端稳定性 是第一位。即使前端做了校验,后端也必须二次验证。

  • 此处检查:

    • message 是否存在(防止 nullundefined);
    • 类型是否为字符串(防止传入对象、数组等非法类型)。
  • 返回 400 Bad Request 状态码,明确告知客户端请求格式错误。

  • 错误信息清晰具体,便于前端调试。


4. AI 集成:LangChain 链式调用与错误隔离

javascript 复制代码
import { ChatOllama } from '@langchain/ollama'
import { ChatPromptTemplate } from '@langchain/core/prompts'
import { StringOutputParser } from '@langchain/core/output_parsers'

const model = new ChatOllama({
  baseUrl: 'http://localhost:11434',
  model: 'deepseek-r1:8b',
  temperature: 0.1 // 
})

🔗 LangChain 核心抽象:链(Chain)

LangChain 的核心思想是将 LLM 调用过程模块化、可组合。本项目使用了典型的三段式链:

  1. Prompt Template:结构化输入

    css 复制代码
    const prompt = ChatPromptTemplate.fromMessages([  ['system', `你是一个专业的 Git 提交信息生成器。请根据以下 git diff 内容,生成一条简洁、符合 Conventional Commits 规范的 commit message。不要解释,只输出 commit message`],
      ['human', '{input}']
    ])
    • 将原始 message 注入到预设对话模板中;
    • 支持多轮对话上下文(未来可扩展);
    • 确保提示词格式符合模型预期。
  2. Model:大模型调用

    • ChatOllama 是 LangChain 对 Ollama API 的封装;
    • baseUrl 指向本地 Ollama 服务;
    • temperature: 0.1 降低随机性,使输出更确定(适合生成规范文本)。
  3. Output Parser:标准化输出

    scss 复制代码
    new StringOutputParser()
    • 强制将模型返回的复杂对象(如 { content: "...", role: "assistant" })转换为纯字符串;
    • 避免前端处理不必要的元数据;
    • 保证 API 响应结构稳定:{ reply: "string" }

⚠️ 错误处理:隔离 AI 不稳定性

php 复制代码
try {
  const result = await chain.invoke({ input: message })
  res.json({ reply: result })
} catch (err) {
  res.status(500).json({ error: '调用大模型失败' })
}
  • 大模型调用可能因网络、内存、模型加载失败等原因抛出异常;
  • 使用 try/catch 捕获所有同步/异步错误;
  • 返回 500 Internal Server Error,避免服务崩溃;

5. 工程化考量:可维护性与可观测性

  • 日志记录console.log('正在调用大模型') 提供基本执行追踪;
  • 超时控制:虽未显式设置,但 Ollama 客户端和 Axios 均有默认超时;
  • 依赖解耦:AI 逻辑封装在路由内,未来可提取为独立 service 层;

总结:一个合格的后端 API 应具备什么?

维度 本项目实现
协议合规 正确使用 HTTP 方法、状态码、Content-Type
输入安全 严格校验请求体格式与类型
错误处理 区分客户端错误(4xx)与服务端错误(5xx)
跨域支持 通过 CORS 中间件解决开发跨域问题
AI 集成 使用 LangChain 实现可维护的提示工程
可扩展性 模块化结构,便于未来增加新功能

这才是一个面向生产、面向读者的后端实现应有的深度与广度。


四、AI 部分:Ollama 与本地大模型部署

1. 为什么选择 Ollama?

  • 开源、轻量、支持多种模型(Llama, DeepSeek 等);
  • 提供类 OpenAI 的 API 接口(/api/chat),便于集成;
  • 可在消费级 GPU 或 CPU 上运行(需足够内存)。

2. 模型选择:deepseek-r1:8b

  • r1 表示 Reasoning 版本,推理能力更强;

  • 8b 为 80 亿参数,平衡性能与资源消耗;

  • 需通过命令下载:

    arduino 复制代码
    ollama pull deepseek-r1:8b
    ollama run deepseek-r1:8b  # 测试

"ollama帮助我们像openai一样的api 接口 http://localhost:11434"

3. 性能提示

  • 首次加载模型较慢(需加载到内存);
  • temperature: 0.1 降低随机性,使输出更确定、规范;
  • 若响应慢,可考虑量化版本(如 q4_K_M)。

五、总结

通过 git-differ 项目,我们完整实践了一个 AI 增强型开发者工具 的全栈开发流程:

  • 前端:React 自定义 Hook + Axios 模块化;
  • 后端:Express 路由 + CORS + 错误处理;
  • AI:Ollama + LangChain 提示工程 + 输出解析。

这不仅解决了"如何写好 Git 提交信息"的实际问题,更展示了 本地大模型在开发者工具链中的落地场景。随着开源模型能力不断提升,类似工具将极大提升个人与团队的开发效率与代码质量。

最终目标:让新手也能像高手一样,写出清晰、规范、有价值的 Git 提交记录。


现在,启动你的 AI Git 助手,让每一次提交都成为工程艺术的一部分!

相关推荐
XiaoYu200216 小时前
第11章 LangChain
前端·javascript·langchain
真上帝的左手20 小时前
26. AI-框架工具-LangChain & LangGraph
人工智能·langchain
大模型真好玩20 小时前
LangGraph智能体开发设计模式(四)——LangGraph多智能体设计模式:网络架构
人工智能·langchain·agent
测试游记1 天前
基于 FastGPT 的 LangChain.js + RAG 系统实现
开发语言·前端·javascript·langchain·ecmascript
维度攻城狮1 天前
科研提速!Zotero Awesome GPT 搭配本地 Ollama 模型使用指南
gpt·zotero·ollama·awesome gpt
weixin_462446231 天前
【原创实践】LangChain + Qwen 智能体项目完整解析:构建RPA自动化操作代理
langchain·自动化·rpa
paopao_wu1 天前
LangChainV1.0[09]-中间件(Middleware)
人工智能·python·langchain·ai编程
进击的松鼠2 天前
LangChain 实战 | 快速搭建 Python 开发环境
python·langchain·llm
xinxin本尊2 天前
通过langchain的LCEL创建带历史感知的检索链
langchain