Java后端转AI工程(一):AI入门篇 —— 从零搭建 Spring AI 对话 API + LoRA 微调实战

本系列共 4 篇,完整覆盖 Java 后端向 AI 工程转型的 6 个月学习路径。
这是第一篇,聚焦第一个月的学习内容:AI 基础认知、Spring AI 框架上手、LoRA 微调实战。
系列导航:Java后端转AI工程:一份6个月全脱产学习计划


总体规划

目标:6个月,从熟练 Java Tech Lead → Java + AI 复合型工程师
设计原则:AI前置主线、架构按需补、全脱产高效冲刺
每天有效学习:6-8h(约每周 35h)

时间线

月份:  1        2        3        4        5        6
       |--AI基础+SpringAI--|  |--AI中台项目--|  |--输出--|
       |--LoRA微调--|       |--DDD+系统设计--|  |--K8s--|
              |---RAG---+---Agent---|
                         |--模型部署+可观测--|
                              |---架构按需补(穿插)---|

阶段概览

月份 主线(AI) 副线(架构/输出) 关键产出
第1月 AI基础 + Spring AI + LoRA微调 命令行AI工具 + Spring AI API + 微调报告
第2月 RAG + Agent 架构按需补 RAG系统 + Agent Demo
第3月 模型部署 + 可观测 + AI中台启动 DDD + 系统设计 私有模型服务 + 可观测面板 + AI中台 v0.5
第4月 AI中台项目(完善+上线) 架构补齐(JVM/JUC/微服务) AI中台 v1.0
第5月 K8s + 容器化部署 架构复习体系化 K8s部署 + 架构知识图谱
第6月 技术输出 + 面试准备 GitHub作品集 + 技术文章 + 分享

并行原则

  • AI 主线不中断。架构补强只在"做 AI 项目遇到具体问题时"回溯查阅工具书,不单独占时段。
  • 微调和 RAG 可以并行——微调是模型侧、RAG 是应用侧,互不冲突。
  • 全脱产的优势:一整天可以深度沉浸一个主题,上午学理论、下午写代码、晚上复盘,学习效率远高于碎片化在职模式。

执行说明:如何直接开始

本计划按"输入 → 动手 → 验收 → 复盘"循环执行。不要先把所有资料看完再写代码;每个学习单元都必须留下可运行代码、实验记录和一条可复述的结论。

开始前检查(第 0 天,约 2 小时)

  • 安装并确认:JDK 17、Maven 3.9+、Git、Docker Desktop、IntelliJ IDEA、curl、jq
  • 执行 java -versionmvn -versiondocker versiongit --version,把输出保存到 docs/environment.md
  • 创建一个独立 Git 仓库,约定 main(稳定版本)、dev(开发版本)、docs/(实验记录)、scripts/(启动和压测脚本)
  • 启动 Docker Compose;执行 docker compose ps,所有必需服务可访问后再开始 Week 1
  • macOS: Docker Desktop 内存至少分配 8GB(Settings → Resources → Memory)
  • 记录本机内存、磁盘和是否有 GPU;内存不足 16 GB 时,Kafka、Ollama、K8s 只按对应阶段按需启动

每日固定节奏(全脱产,约 7h/天,5天/周)

时段 做什么 必须留下的结果
上午 (3h) 阅读官方文档/书籍,整理关键结论 docs/notes/week-N.md
下午 (3h) 完成最小可运行实验 / 项目编码 可启动代码 + README
傍晚 (1h) 制造故障或边界场景并排查 / 补测试 故障记录 + Git commit
晚上 (0.5h) 复述当天知识,更新进度 口述录音/文字总结

周末 (弹性):不做新内容,用于补进度、压测、源码阅读、写周报。如果当周进度正常,休息。

每个单元的完成标准

同时满足以下条件,才进入下一单元:

  1. 能从零启动项目,并在 README 写清依赖、命令、端口和预期结果。
  2. 至少有一个自动化测试(单测、集成测试或可重复的压测脚本)。
  3. 至少记录一次失败实验:现象、日志/指标、定位过程和修复方案。
  4. 能不看资料回答本单元里程碑问题,并画出核心架构图。
  5. 代码已提交 Git,提交信息包含主题和结论,例如 feat(rag): add hybrid retrieval experiment

卡住时的处理规则

  • 同一问题排查 45 分钟仍无进展:保留现场(日志、版本、命令),查官方文档和 issue,再继续。
  • 环境问题与知识问题分开记录;先用最小配置跑通,再逐项加组件。
  • 不为了追新版本频繁升级依赖;以本计划中的主版本为基线,每季度统一升级并重新跑验收。
  • 任何涉及密码、API Key、云账号的配置只放 .env,提交 .env.example,禁止提交真实密钥。

建议的项目目录

learning-lab/
├── docker-compose.yml
├── services/                 # 各阶段可运行项目
├── experiments/              # 独立实验(JVM、SQL、RAG 等)
├── scripts/                  # 启停、初始化、压测、清理脚本
├── docs/
│   ├── environment.md
│   ├── notes/week-N.md
│   ├── decisions/            # ADR:技术选型与权衡
│   └── reports/              # 压测、故障和复盘报告
└── README.md

环境准备:Docker Compose 一键搭建

学习任何模块前,先把环境跑起来。

文件:docker-compose.yml

version: '3.8'
services:
  # ============ 基础中间件 ============
  mysql:
    image: mysql:8.0
    container_name: dev-mysql
    ports: ["3306:3306"]
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: study
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init-sql:/docker-entrypoint-initdb.d
    command: --default-authentication-plugin=mysql_native_password --max_connections=500
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7.2
    container_name: dev-redis
    ports: ["6379:6379"]
    command: redis-server --appendonly yes --requirepass redis123
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "redis123", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  # ============ 微服务组件 ============
  nacos:
    image: nacos/nacos-server:v2.3.2
    container_name: dev-nacos
    ports: ["8848:8848", "9848:9848", "9849:9849"]
    environment:
      MODE: standalone
      PREFER_HOST_MODE: hostname
    depends_on:
      mysql:
        condition: service_healthy

  sentinel-dashboard:
    image: bladex/sentinel-dashboard:1.8.6
    container_name: dev-sentinel
    ports: ["8858:8858"]
    environment:
      SERVER_PORT: 8858

  # ============ MQ ============
  rabbitmq:
    image: rabbitmq:3.13-management
    container_name: dev-rabbitmq
    ports: ["5672:5672", "15672:15672"]
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin123

  # Kafka(按需启动,不用就注释掉)
  zookeeper:
    image: confluentinc/cp-zookeeper:7.6.0
    container_name: dev-zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka:7.6.0
    container_name: dev-kafka
    ports: ["9092:9092"]
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    depends_on: [zookeeper]

  # ============ AI 相关 ============
  postgres:
    image: pgvector/pgvector:pg16
    container_name: dev-postgres
    ports: ["5432:5432"]
    environment:
      POSTGRES_USER: study
      POSTGRES_PASSWORD: study123
      POSTGRES_DB: rag
    volumes:
      - pg-data:/var/lib/postgresql/data
    command: postgres -c max_connections=200
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U study -d rag"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  mysql-data:
  pg-data:

启动与验证

# 1. 启动所有服务
docker compose up -d

# 2. 查看启动状态(所有服务都 healthy 才算就绪)
docker compose ps

# 3. 验证各服务
# Nacos 控制台:     http://localhost:8848/nacos  (账号: nacos/nacos)
# Sentinel 控制台:  http://localhost:8858          (账号: sentinel/sentinel)
# RabbitMQ 管理:    http://localhost:15672         (账号: admin/admin123)

# 4. 安装 Ollama(macOS 建议原生安装,Docker 版在 macOS 上 CPU 推理极慢)
brew install ollama
ollama pull qwen2.5:7b

# 5. 停止环境
docker compose stop    # 保留数据
docker compose down -v # 销毁并清除数据

端口速查表

服务 端口 用途 管理界面
MySQL 3306 数据库 -
Redis 6379 缓存 -
Nacos 8848 注册+配置中心 http://localhost:8848/nacos
Sentinel 8858 熔断限流面板 http://localhost:8858
RabbitMQ 5672 消息队列 http://localhost:15672
Kafka 9092 消息流平台 -
PostgreSQL 5432 向量数据库(PgVector) -
Ollama 11434 本地大模型 http://localhost:11434

macOS 注意

  • Docker Desktop 内存至少分配 8GB
  • Ollama 必须原生安装,Docker 版性能极差
  • 如果内存不够 16GB,注释掉 Kafka+Zookeeper,按需启动

Windows 注意

  • 使用 Docker Desktop WSL2 后端
  • .wslconfig 中内存至少 8GB:[wsl2] memory=8GB

阶段一:AI快速入门(第1月)

核心原则:AI 从第 1 天就启动,不设前置阻塞。Java 主场 + Python 客场,Python 只学微调这一件事。全脱产节奏下,这个阶段密度很高——前两周打基础,后两周上实战。

Week 1:AI 基础速通

学习目标

不需要会数学推导,只需要建立正确的直觉认知:

  • Token 是什么?为什么 LLM 是"预测下一个 Token"的机器?
  • Embedding 的直觉:相似的词,向量在空间中距离近
  • Attention 的直觉:翻译"我爱北京天安门",翻译"天安门"时,模型会"关注"“北京”
  • LLM 的能力与局限:它不会推理,它在"续写"
核心重点
序号 重点 一句话解释
1 Token 是 LLM 的基本单位 中文约 1.5 个字符 = 1 Token,英文约 0.75 个单词 = 1 Token;LLM 本质是一个"给定上文预测下一个 Token"的概率模型
2 Embedding 的几何直觉 高维空间中将语义映射为向量,"猫"和"狗"的向量夹角小(相似),"猫"和"汽车"的夹角大;用余弦相似度衡量
3 Attention = 动态加权 自注意力机制让每个词能"看到"上下文所有词,计算相关性权重后加权求和 → 这是 Transformer 超越 RNN 的关键
4 LLM 的能力边界 擅长:续写/总结/翻译/代码生成;不擅长:精确数学计算、实时信息、需要推理的新问题(需 Function Calling / RAG 补充)
5 API 调用即核心技能 先不用框架,直接用 curl/OkHttp 调 OpenAI 兼容 API,理解 messages 结构(system/user/assistant 角色)+ temperature 参数
5天安排
任务 耗时
Day 1 3Blue1Brown《神经网络》系列 (4集,B站) + 读《图解 Transformer》(jalammar.github.io) 上午3h + 下午2h
Day 2 李宏毅 GPT 课程(B站)+ 理解 Token:tiktoken 工具数各种文本的 Token 数 上午3h + 下午2h
Day 3 用 OpenAI/通义千问 API 写第一个对话程序(Java, OkHttp)+ 理解 Embedding:用 Python 算两个句子的余弦相似度 下午3h + 傍晚2h
Day 4-5 用 curl/PostMan 手动调用 API,熟悉请求格式 + 完成 Java CLI 工具(支持流式输出、多轮对话) 每天 5-6h
第一个 AI 程序(Java)
// 用 OkHttp 直接调 DeepSeek API(不依赖任何 AI 框架,最直观)
public class FirstAiApp {
    private static final String API_KEY = "your-api-key";
    private static final String API_URL = "https://api.deepseek.com/chat/completions";

    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient();
        String requestBody = """
            {
                "model": "deepseek-chat",
                "messages": [
                    {"role": "system", "content": "你是一个有帮助的助手"},
                    {"role": "user", "content": "用一句话解释什么是机器学习"}
                ],
                "stream": false
            }
            """;
        Request request = new Request.Builder()
            .url(API_URL)
            .header("Authorization", "Bearer " + API_KEY)
            .header("Content-Type", "application/json")
            .post(RequestBody.create(requestBody, MediaType.get("application/json")))
            .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println(response.body().string());
        }
    }
}
动手项目:Java 命令行 AI 对话工具

需求:命令行输入问题 → 调用 API → 打印流式输出

验收标准

  1. 能选择模型(gpt-4o / deepseek-chat / qwen-turbo)
  2. 支持多轮对话(保留 messages 历史)
  3. 支持 --stream 参数启用流式输出(逐字打印)
  4. 支持 --temperature 0.7 参数调整创造性
常见报错与排查
报错信息 根因 解决步骤
401 Unauthorized API Key 错误或过期 确认环境变量设置正确、API Key 未过期/余额充足
429 Too Many Requests 请求频率超限 加指数退避重试、升级 API 套餐
context_length_exceeded 对话历史超出模型上下文窗口 截断历史消息(只保留最近 N 轮)、先总结再截断
流式输出乱码 SSE 数据格式解析错误 确认 Accept: text/event-stream、按 data: 前缀逐行解析 JSON
中文 Token 消耗远高于英文 中文字符 Token 化效率低 中文 Prompt 尽量精炼、大段中文文档先压缩再送 LLM
面试实战

Q1: Token 是什么?为什么中文比英文消耗更多?

  • LLM 处理文本的最小单位。英文单词通常就是 1 Token,中文需要分词器切成子词,1 个汉字约 1.5 Token。这是固有的分词器设计问题。

Q2: Temperature 参数的作用?

  • 控制输出的随机性。0 = 确定输出(每次一样,适合代码);0.7-0.9 = 平衡(通用对话);1.0+ = 创意(写诗、故事)。底层是通过调整 softmax 概率分布实现的。

Q3: LLM 为什么会产生幻觉?

  • LLM 是"续写机器"而非"推理机器"。对不确定的内容,它选择概率最高的 Token 续写,这个 Token 可能是错误的。根本原因:没有真实世界知识的验证机制。

Q4: System Prompt 和 User Prompt 的区别?

  • System 设定角色和规则(优先级最高),User 是每次的具体问题。System Prompt 在整个对话中持续生效。

Q5: 为什么 Attention 机制比 RNN 好?

  • RNN 顺序处理,长序列遗忘开头信息(梯度消失);Attention 一次性计算所有位置的关系(全局视野),且可以并行计算(训练更快)。

Q6: Embedding 有什么用?

  • 文字 → 向量,语义相似 → 向量距离近。是 RAG、语义搜索、文本分类的基础。用 cosine similarity 衡量相似度。

Q7: Prompt Engineering 有哪些常用技巧?

  • Few-shot(给示例)、Chain-of-Thought(让 LLM 一步步推理)、角色设定(系统提示词)、格式化输出(指定 JSON/Markdown 格式)、反向提示(告诉它不要做什么)。

Week 2-3:Spring AI 核心

核心重点
序号 重点 一句话解释
1 ChatClient 是统一入口 不管底层是 OpenAI / DeepSeek / Ollama / 通义千问,上层都用同一套 ChatClient API,只需改 yml 配置切换模型
2 Function Calling 机制 @Tool 注解标注 Java 方法 → LLM 自动判断何时调用 → Spring AI 执行方法、拿到结果后注入下文、继续回答
3 ChatMemory 记忆管理 MessageChatMemoryAdvisor 自动将历史消息注入上下文,支持 JDBC 持久化(跨会话记忆),不同 conversationId 互相隔离
4 SSE 流式输出 返回 Flux<String> + MediaType.TEXT_EVENT_STREAM_VALUE,前端用 EventSource 逐字接收(关键体验优化)
5 Prompt Template 用占位符 {name} 动态拼装提示词,@Tool(description="xxx") 描述要精确(LLM 靠描述判断何时调用)
架构师视角

知识点:Function Calling → Agent 的雏形

架构师决策场景:你接到需求"做一个能查订单状态的 AI 客服"。

传统做法:用户问 → NLP 意图识别 → 提取 orderId → 调接口 → 拼接回复
每个业务需要独立开发 NLP 模型和意图路由,维护成本高。

Spring AI Function Calling 做法:
1. 写一个 @Tool 注解的 Java 方法:queryOrder(orderId)
2. LLM 自动判断"用户这句话是不是在查订单",自动提取 orderId
3. Spring AI 执行方法 → 拿到结果 → 注入下文 → LLM 用自然语言回复

架构师决策:
- 工具越多,LLM 选错的概率越大 → 职责单一的工具 + 精确的 @Tool(description)
- Function Calling 本质是给 LLM 一个"菜单",菜单描述不清晰 → LLM 点错菜
- 敏感操作(退款/删数据)必须加人工确认环节,不能全自动
2周安排
任务 产出
Week 2 Spring AI Quickstart + Function Calling(至少2个工具) 对话 API + 工具调用 API
Week 3 ChatMemory + SSE 流式 + Prompt Template + 结构化输出 完整 Spring AI Demo
动手项目:Spring AI 对话 API

验收标准

  1. 能通过 yml 切换模型(OpenAI / DeepSeek / Ollama)
  2. 至少实现 2 个 Function Calling 工具
  3. 支持多轮对话记忆(重启后保留)
  4. 支持 SSE 流式输出
  5. 有单测覆盖核心逻辑
常见报错
报错 根因 解决
401 Unauthorized API Key 未配或过期 检查 application.yml 中的 spring.ai.openai.api-key
Connection refused 连不上 Ollama Ollama 未启动或端口不对 ollama serve 确认端口 11434
Function Calling 不触发 @Tool description 太模糊 description 写清楚"什么情况下该调这个工具"
ChatMemory 对话历史丢失 未配置 JDBC 持久化 JdbcChatMemory 配置
面试实战

Q1: Spring AI 的 ChatClient 和直接调 OpenAI API 有什么区别?

  • ChatClient 屏蔽了不同模型提供商的差异,切换模型只改配置。内置了 Prompt 模板、结构化输出、Function Calling 等能力。直接调 API 更灵活但需要自己管理这些。

Q2: Function Calling 的原理是什么?

  • 注册工具方法 → 调用时将工具定义(名称+描述+参数 schema)发给 LLM → LLM 决定是否调工具、调哪个、参数是什么 → Spring AI 执行方法 → 结果注入消息上下文 → LLM 基于结果给出最终回答。

Q3: 流式输出(SSE)怎么实现?

  • 服务端返回 Flux<String> + Content-Type: text/event-stream。LLM 每生成一个 Token 就推送一次,前端用 EventSource 逐字渲染。相比非流式,用户感知延迟大幅降低。Spring AI 的 chatClient.prompt().stream() 一行搞定。

Week 3-4:LoRA 微调实战

这是整个计划最关键的技能模块——面试硬通货,早攒早变现。

核心重点
序号 重点 一句话解释
1 微调决策树:90% 不需要微调 Prompt 工程 → RAG → 才到微调;只有风格/格式/专业术语类需求且前两者解决不了时才微调
2 LoRA 是性价比之王 不修改原模型参数,只训练低秩矩阵(秩 r=8~64),显存需求降低 3-5 倍,训练速度提升 2-3 倍
3 数据质量 > 数据量 500 条高质量标注数据效果远好于 5000 条低质量数据;每条必须包含 instruction + input + output
4 全量微调 vs LoRA vs QLoRA 全量(效果最好、最贵)→ LoRA(效果好、性价比高)→ QLoRA(4bit 量化 + LoRA,单卡 24GB 可跑 7B 模型)
5 评估防过拟合 训练前预留 10-20% 数据作验证集,训练 Loss 降但验证 Loss 升 → 立即停止(过拟合)
微调前的决策树
你的场景需要微调吗?

1. Prompt 工程能解决 → 不需要微调(90%的场景)
2. RAG 能解决(领域知识缺失)→ 不需要微调
3. 需要特定风格/格式/专业术语 → 考虑微调
4. 通用模型拒绝回答的合规场景 → 考虑微调
5. 需要极低延迟(小模型) → 可能需要微调

微调前必须确认:
□ 有 500-5000 条高质量标注数据
□ 明确评估指标(怎么算微调成功)
□ 有 GPU 资源(至少 16GB 显存)或云 GPU 预算
数据准备(Alpaca 格式)
[
    {
        "instruction": "将以下 Java 代码转换为更简洁的 Stream 写法",
        "input": "List<Integer> result = new ArrayList<>();\nfor (Integer item : list) {\n    if (item > 10) result.add(item * 2);\n}",
        "output": "List<Integer> result = list.stream()\n    .filter(item -> item > 10)\n    .map(item -> item * 2)\n    .collect(Collectors.toList());"
    },
    {
        "instruction": "解释以下 SQL 的性能问题并给出优化建议",
        "input": "SELECT * FROM orders WHERE YEAR(create_time) = 2024",
        "output": "问题:在 create_time 上使用了函数 YEAR(),导致索引失效,会全表扫描。\n优化:将 YEAR(create_time) = 2024 改为 create_time >= '2024-01-01' AND create_time < '2025-01-01',这样可以走 create_time 的索引。"
    }
]
使用 LLaMA-Factory 训练
# 安装
git clone https://github.com/hiyouga/LLaMA-Factory.git
pip install -e ".[torch,metrics]"

# LoRA 微调 Qwen2.5-7B(单卡 24GB 显存可跑)
llamafactory-cli train \
    --model_name_or_path Qwen/Qwen2.5-7B-Instruct \
    --stage sft \
    --do_train \
    --finetuning_type lora \
    --lora_rank 16 \
    --lora_target q_proj,v_proj \
    --dataset your_dataset \
    --template qwen \
    --max_length 2048 \
    --per_device_train_batch_size 2 \
    --gradient_accumulation_steps 4 \
    --lr_scheduler_type cosine \
    --learning_rate 5e-5 \
    --num_train_epochs 3 \
    --output_dir ./output/qwen2.5-7b-lora \
    --bf16 True

# 合并 LoRA 权重
llamafactory-cli export \
    --model_name_or_path Qwen/Qwen2.5-7B-Instruct \
    --adapter_name_or_path ./output/qwen2.5-7b-lora \
    --export_dir ./output/qwen2.5-7b-merged \
    --export_size 4
动手项目:微调一个 Java 代码审查模型

准备 50 条示例数据

[
  {
    "instruction": "请审查以下 Java 代码,指出问题并给出改进建议",
    "input": "public class User { \n  private String name; \n  public void setName(String n) { name = n; } \n}",
    "output": "问题:\n1. 参数名 n 不够语义化,建议改为 name\n2. 缺少 getter 方法\n3. 建议添加构造方法\n改进后的代码:\npublic class User {\n  private String name;\n  public User(String name) { this.name = name; }\n  public String getName() { return name; }\n  public void setName(String name) { this.name = name; }\n}"
  }
]

对比验证

# 微调前:通用模型回答
curl http://localhost:11434/v1/chat/completions -d '{"model":"qwen2.5:7b","messages":[{"role":"user","content":"审查这段代码: public void setName(String n){name=n;}"}]}'

# 微调后:合并 LoRA 权重后用同样问题测试
# 预期:回答风格专业、指出具体问题、带改进建议
常见报错与排查
报错信息 根因 解决步骤
CUDA Out of Memory 显存不足 减小 batch_size=1、增加 gradient_accumulation_steps、用 QLoRA(4bit 量化)
训练 Loss 不下降 学习率过大 / 数据质量差 降低 lr 到 1e-5、检查数据格式、先跑通小数据集
验证 Loss 上升(过拟合) 数据太少 / 训练轮次太多 增加数据、降低 num_train_epochs、加正则化
合并后模型输出乱码 LoRA 合并不完整 / 模板不匹配 确认 --template qwen 与基座模型匹配
面试实战

Q1: 什么是 LoRA?为什么能降低显存?

  • LoRA 冻结原模型参数,只训练两个低秩矩阵 A×B(r=8 时新增参数量仅几十万)。相比全量微调 7B 参数,参数量降低 1000+ 倍,显存需求降低 3-5 倍。

Q2: 微调和 RAG 怎么选择?

  • 领域知识缺失 → RAG;风格/格式调整 + RAG 解决不了 → 微调。微调改变模型行为模式,RAG 补充知识。两者可以结合使用。

Q3: 微调需要多少数据?

  • LoRA 微调 500-5000 条高质量数据。关键不是数量而是质量——每条必须准确、格式规范、覆盖目标场景。数据清洗占微调总时间的 60-70%。

Q4: 全量微调和 LoRA 的效果差多少?

  • 大多数场景下 LoRA(r=8~16)效果接近全量微调(差距 < 2%)。全量微调的优势主要体现在:领域差距极大、需要大幅改变模型行为。
硬件建议
  • 练手阶段:Google Colab 免费 GPU,或 AutoDL 租卡(几块钱/小时)
  • 7B 模型 LoRA 微调:RTX 3090/4090 24GB 单卡足够
  • 不做全量微调的话,不用上 A100

第一篇检查清单

完成以下所有项,再进入第二篇:

  • 能用 OkHttp 直接调 DeepSeek API,理解 messages 结构和 temperature 参数
  • 能解释 Token 是什么、Embedding 的几何直觉、Attention 为什么比 RNN 好
  • 能用 Spring AI 在一小时内搭起一个带 Function Calling 的对话 API
  • 完成过至少一次 LoRA 微调实验(Colab/AutoDL 云 GPU),有训练曲线和前后对比
  • 能解释 LoRA 为什么能降低显存、微调和 RAG 的选择判断

下一篇:Java后端转AI工程(二):AI深化篇 —— RAG + Agent + 模型部署 + 可观测性

第二篇覆盖 RAG 知识库、Agent 多工具协作、Ollama/vLLM 模型部署、AI 应用可观测性,是整个计划最硬核的两个月。

返回系列导航

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐