Git Submodule 实战指南:从基础概念到 AI-Native 项目落地

Git Submodule 实战指南:从基础概念到 AI-Native 项目落地

适合对象:需要在多个仓库之间共享代码、组件、契约、Prompt、MCP 服务或基础设施配置的团队。

Git Submodule 的核心价值不是"把仓库放进另一个仓库",而是让主项目精确记录每个子仓库使用的是哪一次提交。它适合需要强版本锁定、独立发布、跨项目复用的场景;如果只是想把代码拆目录,Monorepo 往往更简单。

目录

  1. [为什么企业研发需要 AI-Native 的文档与代码管理](#为什么企业研发需要 AI-Native 的文档与代码管理 "#1-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%81%E4%B8%9A%E7%A0%94%E5%8F%91%E9%9C%80%E8%A6%81-ai-native-%E7%9A%84%E6%96%87%E6%A1%A3%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%AE%A1%E7%90%86")
  2. [为什么选择 Git Submodule](#为什么选择 Git Submodule "#2-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-git-submodule")
  3. [先判断是否该用 Submodule](#先判断是否该用 Submodule "#3-%E5%85%88%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E8%AF%A5%E7%94%A8-submodule")
  4. 核心概念
  5. 快速开始
  6. 常用命令速查
  7. [AI-Native 项目结构](#AI-Native 项目结构 "#7-ai-native-%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84")
  8. [Vue3 前端子模块示例](#Vue3 前端子模块示例 "#8-vue3-%E5%89%8D%E7%AB%AF%E5%AD%90%E6%A8%A1%E5%9D%97%E7%A4%BA%E4%BE%8B")
  9. [Spring Boot 后端子模块示例](#Spring Boot 后端子模块示例 "#9-spring-boot-%E5%90%8E%E7%AB%AF%E5%AD%90%E6%A8%A1%E5%9D%97%E7%A4%BA%E4%BE%8B")
  10. [MCP 服务子模块示例](#MCP 服务子模块示例 "#10-mcp-%E6%9C%8D%E5%8A%A1%E5%AD%90%E6%A8%A1%E5%9D%97%E7%A4%BA%E4%BE%8B")
  11. 团队协作工作流
  12. [CI/CD 配置](#CI/CD 配置 "#12-cicd-%E9%85%8D%E7%BD%AE")
  13. 常见问题与排查
  14. 最佳实践清单

1. 为什么企业研发需要 AI-Native 的文档与代码管理

1.1 传统研发流程的割裂现状

在大多数企业中,产品、研发、测试三条线各自为政,信息散落在不同系统里:

角色 常用工具 产出物 存储位置
产品经理 Axure、Figma、Confluence、飞书文档 PRD、原型、需求变更记录 云文档 / Wiki / 本地文件
研发工程师 IDE、Git、Jira 代码、技术方案、API 文档 Git 仓库 / Confluence
测试工程师 TestRail、Excel、Postman 测试用例、测试报告、自动化脚本 测试平台 / 共享盘 / Git

这种割裂带来几个核心痛点:

信息孤岛:PRD 在飞书,代码在 GitLab,测试用例在 Excel。需求变更时,产品改了文档,研发不知道;研发改了接口,测试用例失效。没有一个地方能看到"当前这个功能,从需求到代码到测试"的完整链路。

版本失控:产品文档的"最终版_v3_确认版_改2"现象屡见不鲜。更严重的是,代码已经迭代到 v2.1,但对应的 PRD 还停留在 v1.0 的描述,没人知道哪个版本的文档对应哪个版本的代码。

协作断层:研发提测时,测试需要手动对照 PRD 和接口文档编写用例。接口变了,测试不知道;PRD 改了,研发不知道。每次迭代都在重复"你那边改了吗?改了哪里?"的沟通成本。

AI 无法介入:当文档散落在各个 SaaS 平台时,AI 工具(无论是 Copilot、Claude 还是内部 Agent)无法获取完整上下文。AI 只能看到代码,看不到需求背景和测试预期,生成的代码和建议缺乏业务语境。

1.2 AI-Native 研发的核心诉求

AI-Native 不是"在现有流程上加个 AI 插件",而是重新设计信息流,让 AI 能贯穿产品→研发→测试的全链路:

text 复制代码
传统模式:
  产品(飞书) ──手动同步──→ 研发(Git) ──口头沟通──→ 测试(Excel)
       ↑                        ↑                        ↑
     各自独立               各自独立                各自独立

AI-Native 模式:
  ┌─────────────────── Git 主仓库 ───────────────────┐
  │                                                    │
  │  product/     frontend/    backend/      qa/       │
  │  (PRD+原型)   (前端代码)   (后端代码)   (测试)    │
  │                                                    │
  │         AI Agent 可访问完整上下文                   │
  │         版本锁定 · 变更可追溯 · 自动联动           │
  └────────────────────────────────────────────────────┘

AI-Native 研发对文档和代码管理的要求:

  1. 统一版本源:产品文档、代码、测试用例在同一个版本体系下,能回答"v2.1 发布时,PRD 是什么样的,测试覆盖了哪些场景"。
  2. AI 可读取:所有产出物以纯文本格式(Markdown、YAML、代码)存储在 Git 中,AI Agent 可以直接读取和理解。
  3. 变更联动:PRD 变更能自动触发相关代码和测试用例的 Review 提醒;接口变更能自动标记受影响的测试。
  4. 全链路追溯:从一个 commit 能追溯到对应的需求、设计决策和测试结果。

1.3 AI 整合后的研发流程

当产品、研发、测试的产出物都纳入 Git 管理后,AI 能在每个环节发挥作用:

环节 AI 能做什么 前提条件
需求分析 基于历史 PRD 和代码,评估需求可行性和影响范围 PRD 在 Git 中,AI 能读取
技术方案 结合 PRD 和现有架构,生成技术方案草稿 需求和代码在同一上下文
代码生成 基于 PRD + API 契约 + 测试预期,生成符合业务语境的代码 三者版本对齐
Code Review 对照 PRD 检查实现是否偏离需求,对照契约检查接口兼容性 PRD 和契约可被 AI 访问
测试生成 基于 PRD 的验收标准和 API 契约,自动生成测试用例 验收标准结构化存储
回归分析 代码变更后,自动识别受影响的测试用例和功能模块 代码和测试在同一版本体系
文档同步 代码变更后,自动更新 API 文档和变更记录 文档在 Git 中可被 AI 修改

2. 为什么选择 Git Submodule

2.1 备选方案对比

要实现"产品+研发+测试统一管理",有几种技术路径:

方案 优势 劣势 适用场景
Monorepo 统一提交、联调简单、CI 一体化 仓库膨胀、权限粗粒度、构建慢 小团队、强耦合模块
Git Submodule 独立权限、独立发布、精确版本锁定 学习成本、操作步骤多 跨团队、独立节奏、需要权限隔离
Git Subtree 无需额外命令、历史合并 历史混杂、难以独立推送 单向引用、不需要回推
包管理器(npm/Maven) 成熟生态、语义化版本 发布流程重、不适合非代码产出物 纯代码库的复用
文档平台 + Git 混合 各取所长 AI 无法统一访问、版本不对齐 过渡阶段

2.2 选择 Git Submodule 的理由

对于企业级 AI-Native 研发管理,Git Submodule 是当前最务实的选择:

权限隔离:产品文档仓库、前端仓库、后端仓库、测试仓库可以设置不同的访问权限。产品经理只需要 product 仓库的写权限,不需要接触代码仓库;实习生可以只访问文档,不能碰生产代码。

独立发布节奏:产品文档的更新频率和代码发布节奏不同。PRD 可能一天改三次,但代码一周才发一个版本。Submodule 允许各模块按自己的节奏演进,主仓库在需要时"快照"一个稳定组合。

精确版本对齐:主仓库的每个 commit 记录了"此刻产品文档是哪个版本、前端是哪个版本、后端是哪个版本、测试是哪个版本"。这就是 AI-Native 研发需要的全链路版本追溯能力。

AI 友好:所有子模块都在本地文件系统中,AI Agent(Claude Code、Cursor、自研 Agent)可以直接读取 product/ 下的 PRD、backend/ 下的代码、qa/ 下的测试用例,获得完整上下文。

渐进式迁移:不需要一次性把所有东西搬进来。可以先把产品文档仓库加为子模块,再逐步加入测试仓库、共享契约仓库。每一步都是可控的。

Git 原生:不依赖额外平台或工具链。任何支持 Git 的 CI/CD、IDE、AI 工具都能直接工作。

2.3 典型的 AI-Native 研发管控结构

text 复制代码
ai-native-project/          ← 主仓库:研发全流程管控
├── .gitmodules
├── product/                 ← 子模块:产品团队维护
│   ├── prd/                    需求文档(Markdown)
│   ├── prototype/              原型说明
│   ├── changelog.md            需求变更记录
│   └── acceptance-criteria/    验收标准(YAML/Markdown)
├── design/                  ← 子模块:设计团队维护
│   ├── ui-specs/
│   └── interaction-flows/
├── frontend/                ← 子模块:前端团队维护
├── backend/                 ← 子模块:后端团队维护
├── shared/                  ← 子模块:API 契约、Prompt、Schema
├── qa/                      ← 子模块:测试团队维护
│   ├── test-cases/             测试用例(Markdown/YAML)
│   ├── automation/             自动化脚本
│   └── reports/                测试报告
├── mcp-servers/             ← 子模块:AI 工具服务
└── infra/                   ← 基础设施配置

这个结构的关键在于:每个团队在自己的仓库里自由工作,主仓库负责"对齐"。当产品经理完成 PRD 评审、研发完成开发、测试完成验证后,主仓库更新各子模块引用,形成一个"经过验证的版本组合"。

2.4 实际场景举例

场景一:需求变更追溯

产品经理修改了 product/prd/user-login.md 中的登录流程。AI Agent 检测到 PRD 变更,自动:

  1. 对比变更内容,识别影响的接口和页面
  2. backend/frontend/ 中标记受影响的代码文件
  3. qa/test-cases/ 中标记需要更新的测试用例
  4. 生成变更影响报告

场景二:接口契约驱动开发

shared/api-contracts/ 中定义了新接口。AI Agent 可以:

  1. 基于契约为 backend/ 生成 Controller 骨架
  2. 基于契约为 frontend/ 生成 API 调用层
  3. 基于契约为 qa/ 生成接口测试用例
  4. 三方代码都引用同一份契约,版本天然对齐

场景三:发布前全链路检查

发布前,CI 在主仓库层面执行:

  1. 检查 product/acceptance-criteria/ 中的验收标准是否都有对应测试
  2. 检查 shared/api-contracts/ 的变更是否前后端都已适配
  3. 检查 qa/automation/ 的测试是否全部通过
  4. 生成"本次发布包含哪些需求、哪些代码变更、测试覆盖情况"的报告

3. 先判断是否该用 Submodule

了解了 AI-Native 研发管理的背景后,再来看具体技术选型。Submodule 不是所有多仓库项目的默认答案。使用前先看这张表:

需求 推荐方案 原因
一个主项目锁定多个独立仓库的精确版本 Git Submodule 主仓库记录子模块 commit,回滚和审计清晰
多包同仓开发,频繁跨包改动 Monorepo 本地联调、统一提交、统一 CI 更顺滑
只想复用第三方源码的一小部分 Vendoring 或包管理器 Submodule 的协作成本可能偏高
共享 SDK、Prompt、API 契约、MCP Server Git Submodule 适合独立演进,同时被多个项目固定引用
子项目需要独立权限和发布节奏 Git Submodule 仓库边界、权限边界和版本边界一致

一句话判断:如果你希望主项目"固定引用某个外部仓库的某次提交",Submodule 很合适;如果你希望所有模块像一个项目一样改、测、发,优先考虑 Monorepo。


4. 核心概念

Git Submodule 允许把一个 Git 仓库挂载到另一个 Git 仓库的子目录中。子模块仍然是独立仓库,拥有自己的分支、提交历史和远程地址。

text 复制代码
parent-repo/
├── src/
├── docs/
├── .gitmodules
└── frontend/            # 子模块目录
    ├── .git             # 通常是指向主仓库 .git/modules 的文件
    └── src/

主仓库不会直接保存子模块里的文件内容,而是保存:

  • .gitmodules:子模块名称、路径、远程地址、可选跟踪分支。
  • 子模块路径对应的 commit hash:主项目当前锁定的子模块版本。
  • .git/config 中的本地子模块配置:初始化后生成,影响本地行为。

4.1 关键特性

特性 说明
独立版本控制 子模块有自己的提交历史、分支和权限模型
精确版本锁定 主仓库记录子模块的具体 commit,不会自动漂移
支持嵌套 子模块内部还可以包含子模块,克隆时需要 --recursive
协作成本更高 更新子模块后,必须回到主仓库提交引用变更

4.2 最容易误解的一点

git pull 只更新主仓库本身,不等于自动更新所有子模块。克隆、切分支、拉代码之后,经常还需要执行:

bash 复制代码
git submodule update --init --recursive

这条命令的意思是:按照主仓库记录的 commit,把所有子模块检出到正确版本。


5. 快速开始

5.1 添加子模块

bash 复制代码
git submodule add https://github.com/myorg/vue3-ai-frontend.git frontend
git submodule add https://github.com/myorg/springboot-ai-backend.git backend
git submodule add https://github.com/myorg/mcp-servers.git mcp-servers
git submodule add https://github.com/myorg/ai-shared-contracts.git shared

git commit -m "chore: add project submodules"

添加后,主仓库通常会出现两类变更:

text 复制代码
.gitmodules
frontend
backend
mcp-servers
shared

这些子模块路径在 Git 中表现为特殊条目,指向子仓库的某个 commit。

5.2 克隆包含子模块的项目

推荐直接递归克隆:

bash 复制代码
git clone --recursive https://github.com/myorg/ai-native-project.git

如果已经普通克隆了主仓库,再补初始化:

bash 复制代码
git clone https://github.com/myorg/ai-native-project.git
cd ai-native-project
git submodule update --init --recursive

5.3 更新子模块

更新到主仓库锁定的版本:

bash 复制代码
git submodule update --init --recursive

更新到 .gitmodules 中配置分支的最新提交:

bash 复制代码
git submodule update --remote --recursive

更新某一个子模块:

bash 复制代码
git submodule update --remote frontend

5.4 提交子模块更新

子模块里提交并推送:

bash 复制代码
cd frontend
git checkout main
git pull origin main

# 修改代码后
git add .
git commit -m "feat: add AI chat component"
git push origin main

回到主仓库提交"子模块指针":

bash 复制代码
cd ..
git status
git add frontend
git commit -m "chore: update frontend submodule"
git push origin main

如果忘记第二步,其他人拉主仓库时看不到你更新后的子模块版本。


6. 常用命令速查

目标 命令
查看子模块状态 git submodule status
初始化子模块 git submodule init
初始化并检出 git submodule update --init --recursive
克隆时递归拉取 git clone --recursive <url>
更新到主仓库锁定版本 git submodule update --recursive
更新到远程分支最新版本 git submodule update --remote --recursive
在所有子模块中执行命令 git submodule foreach '<command>'
同步 .gitmodules URL 到本地配置 git submodule sync --recursive
删除子模块 git submodule deinit -f <path> 后再 git rm -f <path>

6.1 删除子模块

删除要分清"从主仓库移除引用"和"清理本地缓存":

bash 复制代码
git submodule deinit -f frontend
git rm -f frontend
rm -rf .git/modules/frontend
git commit -m "chore: remove frontend submodule"

rm -rf .git/modules/frontend 是本地清理步骤。多人协作时,其他成员只需要拉取主仓库提交即可,不需要跟着删除你的本地缓存目录。

6.2 修改子模块远程地址

先改 .gitmodules

ini 复制代码
[submodule "frontend"]
    path = frontend
    url = git@github.com:myorg/vue3-ai-frontend.git

同步到本地配置:

bash 复制代码
git submodule sync --recursive
git submodule update --init --recursive
git add .gitmodules
git commit -m "chore: update frontend submodule url"

7. AI-Native 项目结构

AI-Native 项目常见特点是模块多、演进快、依赖边界复杂。把前端、后端、MCP 服务、Prompt、评测集、共享契约拆成独立仓库,可以让团队按职责独立迭代,同时由主项目锁定可运行组合。

text 复制代码
ai-native-project/
├── .gitmodules
├── docker-compose.yml
├── Makefile
├── product/                    # 产品文档,可选 Submodule
│   ├── prd/
│   ├── prototype/
│   └── ai-requirements/
├── frontend/                   # Vue3 AI 前端 Submodule
│   └── src/
│       ├── components/
│       ├── prompts/
│       └── mcp/
├── backend/                    # Spring Boot AI 后端 Submodule
│   └── src/main/java/
│       ├── ai/
│       ├── agents/
│       └── mcp/
├── mcp-servers/                # MCP 服务集合 Submodule
│   ├── filesystem-mcp/
│   └── database-mcp/
├── qa/                         # 测试与评测 Submodule
│   ├── automation/
│   └── ai-evaluation/
├── shared/                     # 共享契约 Submodule
│   ├── api-contracts/
│   ├── ai-schemas/
│   └── prompts/
└── infra/
    ├── docker/
    └── k8s/

7.1 .gitmodules 示例

ini 复制代码
[submodule "frontend"]
    path = frontend
    url = https://github.com/myorg/vue3-ai-frontend.git
    branch = main

[submodule "backend"]
    path = backend
    url = https://github.com/myorg/springboot-ai-backend.git
    branch = main

[submodule "mcp-servers"]
    path = mcp-servers
    url = https://github.com/myorg/mcp-servers.git
    branch = main

[submodule "shared"]
    path = shared
    url = https://github.com/myorg/ai-shared-contracts.git
    branch = main

[submodule "qa"]
    path = qa
    url = https://github.com/myorg/ai-qa.git
    branch = main

branch = main 只影响 git submodule update --remote 的目标分支。主仓库实际记录的仍然是具体 commit。

7.2 共享契约示例

typescript 复制代码
// shared/api-contracts/ChatAPI.ts
export interface AIResponse {
  id: string
  content: string
  role: 'assistant'
  metadata: {
    model: string
    tokens: number
    latency: number
  }
}

export const CHAT_ENDPOINTS = {
  CHAT: '/api/chat',
  STREAM: '/api/chat/stream',
  RAG: '/api/chat/rag',
} as const

共享契约适合单独作为子模块,因为它是前端、后端、测试、Agent 评测共同依赖的边界。


8. Vue3 前端子模块示例

8.1 创建前端仓库

bash 复制代码
git init vue3-ai-frontend
cd vue3-ai-frontend
npm create vue@latest .
npm install @ai-sdk/vue ai openai

git add .
git commit -m "init: vue3 ai frontend"
git remote add origin https://github.com/myorg/vue3-ai-frontend.git
git push -u origin main

8.2 AI 聊天组件

vue 复制代码
<!-- src/components/AIChat.vue -->
<template>
  <section class="ai-chat">
    <div class="messages">
      <div
        v-for="msg in messages"
        :key="msg.id"
        :class="['message', msg.role]"
      >
        {{ msg.content }}
      </div>
    </div>

    <form class="input-area" @submit.prevent="handleSubmit">
      <input v-model="input" placeholder="输入消息..." />
      <button type="submit" :disabled="isLoading">
        {{ isLoading ? '思考中...' : '发送' }}
      </button>
    </form>
  </section>
</template>

<script setup lang="ts">
import { useChat } from '@ai-sdk/vue'

const { messages, input, handleSubmit, isLoading } = useChat({
  api: '/api/chat',
})
</script>

8.3 Prompt 管理

typescript 复制代码
// src/prompts/system-prompts.ts
export const SYSTEM_PROMPTS = {
  CODE_ASSISTANT: '你是一个专业的代码助手,擅长 Vue3 和 Spring Boot 开发。',
  CODE_REVIEW: '请审查代码,关注代码质量、性能问题、安全漏洞和最佳实践。',
  DOC_GENERATOR: '请为代码生成文档,包括功能描述、参数说明、返回值和使用示例。',
} as const

export type SystemPromptName = keyof typeof SYSTEM_PROMPTS

export const loadPrompt = (name: SystemPromptName) => SYSTEM_PROMPTS[name]

8.4 Vite 代理配置

typescript 复制代码
// vite.config.ts
import path from 'node:path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@prompts': path.resolve(__dirname, './src/prompts'),
    },
  },
  server: {
    proxy: {
      '/api': { target: 'http://localhost:8080', changeOrigin: true },
      '/mcp': { target: 'http://localhost:3001', changeOrigin: true },
    },
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'ai-vendor': ['@ai-sdk/vue', 'ai'],
        },
      },
    },
  },
})

9. Spring Boot 后端子模块示例

后端子模块适合承载模型调用、RAG、Agent 编排、鉴权和业务 API。实际项目中请以团队当前 Spring Boot、Spring AI 和模型 SDK 版本为准,示例主要展示边界设计。

9.1 Maven 依赖结构

xml 复制代码
<properties>
    <java.version>21</java.version>
    <spring-ai.version>${project.spring-ai.version}</spring-ai.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>${spring-ai.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-pgvector-store</artifactId>
        <version>${spring-ai.version}</version>
    </dependency>
</dependencies>

9.2 ChatService

java 复制代码
@Service
public class ChatService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public ChatService(ChatClient.Builder builder, VectorStore vectorStore) {
        this.chatClient = builder.build();
        this.vectorStore = vectorStore;
    }

    public String chat(String message) {
        return chatClient.prompt()
            .user(message)
            .call()
            .content();
    }

    public Flux<String> chatStream(String message) {
        return chatClient.prompt()
            .user(message)
            .stream()
            .content();
    }

    public String chatWithRag(String message) {
        List<Document> docs = vectorStore.similaritySearch(
            SearchRequest.query(message).withTopK(3)
        );

        String context = docs.stream()
            .map(Document::getContent)
            .collect(Collectors.joining("\n"));

        return chatClient.prompt()
            .system("基于以下上下文回答问题:\n" + context)
            .user(message)
            .call()
            .content();
    }
}

9.3 ChatController

java 复制代码
@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatService chatService;

    public ChatController(ChatService chatService) {
        this.chatService = chatService;
    }

    @PostMapping
    public ResponseEntity<AIResponse> chat(@RequestBody ChatRequest request) {
        String content = chatService.chat(request.getMessage());

        return ResponseEntity.ok(AIResponse.builder()
            .id(UUID.randomUUID().toString())
            .content(content)
            .role("assistant")
            .build());
    }

    @PostMapping("/stream")
    public Flux<ServerSentEvent<String>> chatStream(@RequestBody ChatRequest request) {
        return chatService.chatStream(request.getMessage())
            .map(content -> ServerSentEvent.builder(content).build());
    }

    @PostMapping("/rag")
    public ResponseEntity<AIResponse> chatWithRag(@RequestBody ChatRequest request) {
        String content = chatService.chatWithRag(request.getMessage());
        return ResponseEntity.ok(AIResponse.builder().content(content).build());
    }
}

9.4 Agent 服务

java 复制代码
@Component
public class TaskAgent {

    private final ChatClient chatClient;

    public TaskAgent(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    public String executeWithTools(String message, List<Tool> tools) {
        return chatClient.prompt()
            .user(message)
            .tools(tools)
            .call()
            .content();
    }

    public String reviewCode(String code) {
        return chatClient.prompt()
            .system("你是资深代码审查专家,关注质量、安全性和性能。")
            .user(code)
            .call()
            .content();
    }
}

10. MCP 服务子模块示例

MCP(Model Context Protocol)用于标准化 AI 应用和外部工具之间的交互。把 MCP Server 独立成子模块,有利于多个 AI 应用复用同一套工具服务。

text 复制代码
AI Application
├── MCP Client
├── Agent Runtime
└── Tool Registry
    ├── filesystem-mcp
    ├── database-mcp
    └── custom-tools

10.1 MCP Server(TypeScript)

typescript 复制代码
// mcp-servers/filesystem/src/index.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import * as fs from 'node:fs/promises'

const server = new Server(
  { name: 'filesystem-mcp', version: '1.0.0' },
  { capabilities: { tools: {} } }
)

server.setRequestHandler('tools/list', async () => ({
  tools: [
    {
      name: 'read_file',
      description: '读取文件内容',
      inputSchema: {
        type: 'object',
        properties: {
          path: { type: 'string' },
        },
        required: ['path'],
      },
    },
    {
      name: 'write_file',
      description: '写入文件内容',
      inputSchema: {
        type: 'object',
        properties: {
          path: { type: 'string' },
          content: { type: 'string' },
        },
        required: ['path', 'content'],
      },
    },
  ],
}))

server.setRequestHandler('tools/call', async ({ params }) => {
  const { name, arguments: args } = params

  switch (name) {
    case 'read_file': {
      const content = await fs.readFile(args.path, 'utf-8')
      return { content: [{ type: 'text', text: content }] }
    }
    case 'write_file': {
      await fs.writeFile(args.path, args.content, 'utf-8')
      return { content: [{ type: 'text', text: 'OK' }] }
    }
    default:
      throw new Error(`Unknown tool: ${name}`)
  }
})

await server.connect(new StdioServerTransport())

10.2 MCP Client(Vue3)

typescript 复制代码
// src/mcp/mcp-client.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js'

export const createMcpClient = async () => {
  const client = new Client({
    name: 'vue3-ai-frontend',
    version: '1.0.0',
  })

  await client.connect({
    transport: {
      type: 'stdio',
      command: 'node',
      args: ['../mcp-servers/filesystem/dist/index.js'],
    },
  })

  return client
}

10.3 后端侧 MCP 配置示例

java 复制代码
@Configuration
public class McpConfig {

    @Bean
    public MCPServer mcpServer(FileService fileService, JdbcTemplate jdbcTemplate) {
        return MCPServer.builder()
            .name("springboot-mcp")
            .version("1.0.0")
            .addTool(Tool.builder()
                .name("read_file")
                .description("读取项目文件")
                .inputSchema(Schema.builder()
                    .addProperty("path", Schema.string())
                    .required("path")
                    .build())
                .execute(args -> ToolResult.success(
                    fileService.readFile(args.get("path").asText())
                ))
                .build())
            .addTool(Tool.builder()
                .name("query_database")
                .description("执行数据库查询")
                .inputSchema(Schema.builder()
                    .addProperty("sql", Schema.string())
                    .required("sql")
                    .build())
                .execute(args -> ToolResult.success(
                    jdbcTemplate.queryForList(args.get("sql").asText())
                ))
                .build())
            .build();
    }
}

11. 团队协作工作流

11.1 日常开发

bash 复制代码
# 1. 克隆完整项目
git clone --recursive https://github.com/myorg/ai-native-project.git
cd ai-native-project

# 2. 确保子模块处于主仓库记录的版本
git submodule update --init --recursive

# 3. 进入子模块开发
cd frontend
git checkout main
git pull origin main

# 4. 提交并推送子模块
git add .
git commit -m "feat: add prompt selector"
git push origin main

# 5. 回到主仓库,提交子模块引用
cd ..
git add frontend
git commit -m "chore: update frontend submodule"
git push origin main

11.2 Code Review 时看什么

位置 关注点
子模块仓库 PR 真实代码变更、测试、兼容性
主仓库 PR 子模块 commit 是否指向预期版本
.gitmodules URL、分支、路径是否合理
CI 日志 是否递归拉取子模块,是否存在权限问题

11.3 分支切换后的同步

切换主仓库分支后,子模块目录可能仍停留在旧 commit。建议固定执行:

bash 复制代码
git checkout feature/demo
git submodule update --init --recursive

如果要丢弃子模块里的本地修改,先确认没有未保存工作,再进入子模块处理。不要在不了解状态时批量清理。


12. CI/CD 配置

12.1 GitHub Actions

yaml 复制代码
# .github/workflows/ci.yml
name: AI-Native CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        module: [frontend, backend]

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Build and test ${{ matrix.module }}
        run: |
          cd ${{ matrix.module }}
          if [ "${{ matrix.module }}" = "frontend" ]; then
            npm ci
            npm run build
            npm run test:unit
          else
            ./mvnw clean package
          fi

  ai-integration-test:
    runs-on: ubuntu-latest
    needs: build

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Run AI evaluation tests
        run: |
          cd qa/ai-evaluation
          npm ci
          npm run eval:all

  deploy:
    runs-on: ubuntu-latest
    needs: ai-integration-test
    if: github.ref == 'refs/heads/main'

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Deploy to staging
        run: docker-compose -f infra/docker/production.yml up -d --build

12.2 Makefile

makefile 复制代码
.PHONY: setup update dev test deploy

setup:
	git submodule update --init --recursive
	cd frontend && npm install
	cd backend && ./mvnw clean install -DskipTests
	cd qa && npm install

update:
	git submodule update --remote --recursive

dev:
	docker-compose -f infra/docker/dev.yml up -d
	cd backend && ./mvnw spring-boot:run &
	cd frontend && npm run dev

test:
	cd frontend && npm run test:unit
	cd backend && ./mvnw test
	cd qa/ai-evaluation && npm run eval:all

deploy:
	docker-compose -f infra/docker/production.yml build
	cd qa/ai-test-agents && npm run smoke -- --env production
	docker-compose -f infra/docker/production.yml up -d

13. 常见问题与排查

13.1 Submodule 通用问题

问题 常见原因 处理方式
子模块目录为空 只克隆了主仓库,未初始化子模块 git submodule update --init --recursive
子模块停留在旧版本 主仓库锁定的是旧 commit 确认是否要 git submodule update --remote,再提交主仓库引用
CI 中子模块不存在 checkout 未启用递归子模块 actions/checkout 配置 submodules: recursive
拉取子模块失败 权限、URL、Token 或 SSH Key 配置不正确 检查 .gitmodules、CI Secret、Deploy Key
合并冲突只显示 commit hash 两个分支更新了不同的子模块 commit 进入子模块选择目标 commit,回主仓库重新提交
子模块 URL 改了但本地还用旧地址 本地 .git/config 未同步 git submodule sync --recursive

13.2 AI-Native 特有问题

问题 建议
模型版本不一致 shared/ai-schemas 中统一模型配置,主仓库锁定版本
Prompt 版本混乱 shared/prompts 作为独立子模块,使用语义化版本和变更记录
MCP 服务接口变更 MCP 服务独立仓库管理,主项目只在验证通过后更新引用
RAG 知识库不同步 文档子模块变更后,在 CI 中自动重建向量索引
前后端契约不一致 共享 api-contracts,并在 CI 中跑契约测试

13.3 排查顺序

遇到子模块异常时,按下面顺序看,通常能快速定位:

bash 复制代码
git submodule status
git status
cat .gitmodules
git submodule sync --recursive
git submodule update --init --recursive

如果子模块内还有问题,再进入对应目录执行:

bash 复制代码
cd frontend
git status
git branch -vv
git remote -v

14. 最佳实践清单

  • 主仓库提交信息要说明更新了哪个子模块,以及为什么更新。
  • 子模块仓库先合并、测试、推送,再更新主仓库引用。
  • CI 一律使用递归 checkout,避免环境和本地不一致。
  • .gitmodules 里的 URL 要和团队权限模型一致,统一 HTTPS 或 SSH。
  • 不要把频繁共同修改的强耦合代码拆成 Submodule。
  • 对共享契约、Prompt、MCP 工具服务建立变更记录。
  • 切换主仓库分支后,执行 git submodule update --init --recursive
  • 更新到远程最新版本后,必须回主仓库提交子模块指针。
  • 删除子模块要同时处理 .gitmodules、工作区路径和本地 .git/modules 缓存。

结论

Git Submodule 适合管理"独立演进、精确锁定、跨项目复用"的模块。放在 AI-Native 项目中,它不仅解决代码层面的模块化问题,更重要的是打通了产品→研发→测试的全链路版本管理,让 AI Agent 能获取完整的业务上下文。

传统研发流程中,产品文档在飞书、代码在 Git、测试在 Excel 的割裂状态,本质上是把人类的沟通成本转嫁给了每一次协作。Git Submodule 提供了一种务实的整合路径:不要求所有人改变工具习惯(产品经理仍然可以用 Markdown 写 PRD),但把产出物统一纳入版本管理,让版本对齐和变更追溯成为自动化的事情,而不是靠人肉同步。

真正用好 Submodule 的关键不是命令多熟,而是团队形成一致工作流:子模块先独立验证,主仓库再更新引用,CI 始终递归拉取,问题排查先看 commit 指针和 .gitmodules。当这套机制跑顺之后,AI 工具的价值会被成倍放大------因为它终于能看到全貌了。

相关推荐
用户21991679703915 小时前
基于.Net的NetCoreKevin框架中AgentFramework实现AI智能体Skill和工具动态管理和加载
后端
日月云棠5 小时前
6 高级配置:Spring Boot整合、泛化调用与配置指南
java·后端
SE_NAK5 小时前
go-zero 两个限流器都踩了坑,最后自行实现了一个分布式令牌桶
后端
苏三说技术5 小时前
Durid和HikariCP,哪个连接池更好?
后端
思考着亮5 小时前
1.DDL(数据定义语言)
后端
她的男孩5 小时前
Spring Boot 3 后台框架的自动配置设计:少写配置,多做组合
后端
小黑蛋9125 小时前
Linux核心知识点全解01
后端
日月云棠5 小时前
5 高级配置:多注册中心与异步化编程
java·后端
她的男孩5 小时前
Maven 多模块项目如何避免越写越乱?Forge Admin 的模块边界实践
后端