大模型基础
大模型基础:Java 工程师实战指南
用 SpringBoot 3 + Spring AI + Ollama 跑通第一个本地大模型,再对比 DeepSeek API 调用方式,同时掌握面试必考的 Transformer 原理、KV Cache、主流模型选型。
写在前面
本文你将学到:
- 在本地用 Ollama 运行 Qwen2.5:7B,零成本跑通大模型推理
- 用 SpringBoot 3 + Spring AI 构建一个
/chat接口,5 分钟上手 - 理解 Transformer 架构、Token、Context Window、Temperature 等核心概念
- KV Cache 原理及其对推理性能的影响
- 主流模型(Qwen3、DeepSeek-V3、GPT-4o、Claude 4 Sonnet)横向对比
- 如何一键切换 Ollama 到 DeepSeek 云端 API
- 6 道高频面试题及详细答案
前置条件:
- Java 17+(
java -version确认) - Maven 3.8+(
mvn -version确认) - Docker(可选,用于容器化运行 Ollama)
- 能访问 Ollama 官网(或使用国内镜像)
学习路径
本文是 AI 面试专题的第一篇,聚焦基础概念和第一个能跑的项目。Transformer 深度原理(注意力机制推导)在 transformer-deep.md,RAG 实战在 rag.md。
环境准备
安装 Ollama
Ollama 是一个本地大模型运行工具,类似于"本地的 OpenAI API 服务器"。安装后会在 http://localhost:11434 启动一个兼容 OpenAI 格式的 HTTP 服务。
Linux / macOS 一键安装:
curl -fsSL https://ollama.ai/install.sh | shWindows: 前往 https://ollama.ai/download 下载安装包。
验证安装成功:
ollama --version
# 输出示例:ollama version 0.6.x拉取模型
本文使用 Qwen2.5:7B(阿里通义千问,7B 参数,中英文能力均衡,Q4 量化约 4.7GB):
ollama pull qwen2.5:7b拉取完成后验证:
ollama list预期输出:
NAME ID SIZE MODIFIED
qwen2.5:7b 845dbda0ea48 4.7 GB 2 minutes ago快速测试(命令行对话):
ollama run qwen2.5:7b "用一句话解释什么是 KV Cache"显存要求
- 7B 模型(Q4 量化):约 5GB 显存,或 8GB 内存(CPU 推理)
- 13B 模型:约 8GB 显存
- 没有独立 GPU 也可以跑,CPU 模式速度约 5-15 tokens/s
Docker 方式运行 Ollama(可选)
如果不想直接安装,也可以用 Docker:
# 有 GPU(NVIDIA)
docker run -d --gpus=all -v ollama:/root/.ollama \
-p 11434:11434 --name ollama ollama/ollama
# 纯 CPU
docker run -d -v ollama:/root/.ollama \
-p 11434:11434 --name ollama ollama/ollama
# 进入容器拉模型
docker exec -it ollama ollama pull qwen2.5:7b启动后访问 http://localhost:11434 返回 Ollama is running 即成功。
第一个 Spring AI 项目
创建项目结构
spring-ai-demo/
├── pom.xml
└── src/main/
├── java/com/example/demo/
│ ├── DemoApplication.java
│ └── controller/
│ └── ChatController.java
└── resources/
└── application.ymlpom.xml
Spring AI 1.0.0 使用 BOM 统一管理版本,避免依赖冲突。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0</spring-ai.version>
</properties>
<!-- Spring AI BOM:统一管理 Spring AI 所有子模块版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Ollama 集成:连接本地 Ollama 服务 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>application.yml
这里配置 Ollama 连接地址和默认使用的模型:
spring:
ai:
ollama:
# Ollama 服务地址,本地默认端口 11434
base-url: http://localhost:11434
chat:
options:
# 指定使用的模型,与 ollama list 输出的 NAME 一致
model: qwen2.5:7b
# Temperature:0.7 适合对话场景,代码生成建议用 0.1
temperature: 0.7
# 最大输出 Token 数
num-predict: 2048
server:
port: 8080模型名称要对齐
model: qwen2.5:7b 必须与 ollama list 输出的 NAME 完全一致,否则 Spring AI 会报 404。
ChatController.java
这个 Controller 对接本地 Ollama 服务,使用 qwen2.5:7b 模型响应请求。
package com.example.demo.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.web.bind.annotation.*;
/**
* 对话接口
* 模型:qwen2.5:7b(本地 Ollama,http://localhost:11434)
*/
@RestController
@RequestMapping("/chat")
public class ChatController {
private final ChatClient chatClient;
/**
* Spring AI 自动注入 OllamaChatModel(由 application.yml 配置驱动)
* 无需手动 new,也无需写 @Bean
*/
public ChatController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
// 系统提示:定义模型的角色和行为
.defaultSystem("你是一个专业的 Java 工程师助手,回答简洁准确,代码示例使用 Java。")
.build();
}
/**
* 基础对话接口
* 调用:GET /chat?message=你好
* 模型:本地 Ollama qwen2.5:7b
*/
@GetMapping
public String chat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
/**
* 带参数的对话接口:动态指定 Temperature
* 调用:POST /chat/with-options
* Body:{"message": "写一个快速排序", "temperature": 0.1}
*/
@PostMapping("/with-options")
public String chatWithOptions(@RequestBody ChatRequest request) {
return chatClient.prompt()
.user(request.message())
.options(OllamaOptions.builder()
.temperature(request.temperature())
.build())
.call()
.content();
}
public record ChatRequest(String message, double temperature) {}
}DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}启动和测试
第一步:确认 Ollama 正在运行
curl http://localhost:11434
# 返回:Ollama is running第二步:启动 Spring Boot 应用
mvn spring-boot:run看到以下日志说明启动成功:
Started DemoApplication in 2.3 seconds (process running for 2.6)第三步:发起对话请求
# 基础问答(对接本地 Ollama qwen2.5:7b)
curl "http://localhost:8080/chat?message=用一句话解释KV Cache"预期输出:
KV Cache 是大模型推理时缓存历史 Token 的 Key 和 Value 矩阵的机制,
避免重复计算,将推理速度提升 3-10 倍。# 带参数请求(低 Temperature,代码生成场景)
curl -X POST http://localhost:8080/chat/with-options \
-H "Content-Type: application/json" \
-d '{"message": "写一个 Java 冒泡排序", "temperature": 0.1}'完整项目代码
以上代码可直接 copy 运行,无需修改。如果 Ollama 使用了不同端口或模型,只需修改 application.yml 即可,Java 代码零改动。
理解大模型原理
Transformer 架构:Decoder-Only 为何统治对话场景
2017 年 Google 论文《Attention Is All You Need》提出 Transformer,现代主流对话/代码模型全部基于其 Decoder-Only 变体:
| 架构类型 | 代表模型 | 特点 | 适用任务 |
|---|---|---|---|
| Encoder-Only | BERT、RoBERTa | 双向理解上下文 | 分类、NER、语义匹配 |
| Decoder-Only | GPT-4o、Claude 4、Qwen3、DeepSeek-V3 | 自回归生成,单向注意力 | 对话、代码、推理 |
| Encoder-Decoder | T5、mBART | 编解码分离 | 翻译、摘要 |
为什么 Decoder-Only 成为主流? 自回归训练(预测下一个 Token)可以利用海量无标注文本,数据效率极高;单向注意力 + KV Cache 使推理可以高效缓存;Scaling Law(越大越强)在 Decoder-Only 架构上被充分验证。
Token 与 Tokenization
Token 是模型处理文本的最小单位,不等于字符,也不等于单词。
英文:"Hello, world!" → ["Hello", ",", " world", "!"] → 4 个 Token
中文:"你好世界" → ["你好", "世界"] → 2 个 Token(视分词器)
代码:"System.out.println" → ["System", ".", "out", ".", "println"] → 5 个 Token面试必记规律:
- 英文:1 Token ≈ 4 字符 ≈ 0.75 个单词
- 中文:1 Token ≈ 1.5-2 个汉字(同等语义,中文比英文贵约 1.5 倍)
- 代码:标识符、符号单独计 Token,消耗较高
Context Window(上下文窗口)
Context Window 是模型单次能处理的最大 Token 数,包含输入(System Prompt + 历史对话 + 当前问题)和输出。
| 模型 | 上下文窗口 | 可处理中文量(估算) |
|---|---|---|
| GPT-4o | 128K Token | 约 10 万字 |
| Claude 4 Sonnet | 200K Token | 约 15 万字 |
| DeepSeek-V3 | 128K Token | 约 10 万字 |
| Qwen3-72B | 128K Token | 约 10 万字 |
"Lost in the Middle" 问题
即使模型支持超长上下文,实验表明它对开头和结尾内容的关注度显著高于中间部分。生产中应通过 RAG 检索相关片段,而不是把整个知识库塞进上下文。
Temperature 与 Top-P 参数
Temperature 控制输出的随机性,对 logits 除以该值再做 softmax 归一化:
| Temperature | 输出特征 | 推荐场景 |
|---|---|---|
| 0(贪婪解码) | 确定性,每次基本相同 | SQL 生成、数据提取、JSON 解析 |
| 0.1 - 0.3 | 略有变化,质量稳定 | 代码生成、技术问答 |
| 0.7 - 1.0 | 有创意,多样性强 | 文案写作、对话助手 |
| 1.0+ | 高度随机 | 头脑风暴(不推荐生产) |
在 Spring AI 中配置推理参数:
// 对接本地 Ollama qwen2.5:7b
// 代码生成场景:低 Temperature,确定性输出
OllamaOptions codeOptions = OllamaOptions.builder()
.temperature(0.1) // 低随机性,代码生成更稳定
.topP(0.9) // 只从概率累积到 90% 的词中采样
.numPredict(4096) // 最大输出 Token 数
.build();
String code = chatClient.prompt()
.user("写一个 Java 单例模式")
.options(codeOptions)
.call()
.content();// 创意写作场景:高 Temperature,输出更有变化
OllamaOptions creativeOptions = OllamaOptions.builder()
.temperature(0.9)
.topP(0.95)
.build();Top-P vs Top-K
- Top-P(核采样):从概率累积到 P 的词集合中采样,
topP=0.9表示只考虑累积概率达 90% 的词。动态词表大小,推荐使用。 - Top-K:只从最高概率的 K 个词中采样,词表大小固定。
- 实践建议:两者通常只调一个,优先调 Temperature,Top-P 保持 0.9-0.95。
KV Cache 原理
为什么需要 KV Cache
LLM 是自回归的——生成第 N 个 Token,需要计算前 N-1 个 Token 的注意力。如果每次都从头计算,复杂度是 O(N²),长对话会极慢。
KV Cache 的思路:把已经计算过的 Token 的 Key 和 Value 矩阵缓存起来,新 Token 只需计算自己的 Query,再与缓存的 K/V 做注意力运算。
KV Cache 工作流程
KV Cache 的收益与代价
| 维度 | 无 KV Cache | 有 KV Cache |
|---|---|---|
| 计算复杂度 | O(N²) 每步 | O(N) 每步(仅新 Token) |
| 推理速度提升 | 基准 | 3-10 倍 |
| 显存占用 | 低 | 高(128K 上下文需数十 GB) |
KV Cache 与上下文长度
KV Cache 占用显存随上下文线性增长。一个 70B 模型,128K 上下文的 KV Cache 约需 50-80GB 显存。这是为什么超长上下文推理成本高昂,也是 Flash Attention 等优化技术出现的直接原因。
KV Cache 在 Ollama 中的体现
用 Ollama 本地运行时,可以看到以下日志,体现 KV Cache 的工作状态:
# 查看 Ollama 运行日志
ollama logs
# 输出中可见:
# prompt eval count: 47 token(s) <- Prefill 阶段处理的 Token 数
# prompt eval duration: 1.2s <- Prefill 耗时(第一次较慢)
# eval count: 128 token(s) <- 生成阶段 Token 数
# eval duration: 3.8s <- 生成耗时(KV Cache 加速后较快)主流模型横向对比
| 模型 | 参数量 | 上下文窗口 | 推理能力 | 代码能力 | 中文支持 | 价格(输入/输出 per 1M Token) | 适合场景 |
|---|---|---|---|---|---|---|---|
| Qwen3-72B | 72B | 128K | ★★★★★ | ★★★★ | ★★★★★ | $0.4 / $1.2 | 国内合规,中文最优 |
| DeepSeek-V3 | 671B(MoE,激活 37B) | 128K | ★★★★★ | ★★★★★ | ★★★★★ | $0.27 / $1.1 | 性价比极高,综合强 |
| GPT-4o | 未公开 | 128K | ★★★★★ | ★★★★★ | ★★★★ | $2.5 / $10 | 综合最强,多模态 |
| Claude 4 Sonnet | 未公开 | 200K | ★★★★★ | ★★★★★ | ★★★★ | $3 / $15 | 代码和长文档处理 |
选型决策原则:
- 极强代码能力 → Claude 4 Sonnet / DeepSeek-V3
- 成本敏感 / 高并发 → DeepSeek-V3 / Qwen3
- 国内合规 / 数据不出境 → Qwen3(阿里云) / DeepSeek(私有化部署)
- 私有化本地部署 → Qwen2.5:7B(Ollama) / DeepSeek-R1:8B
- 超长文档处理 → Claude 4 Sonnet(200K 上下文)
DeepSeek-V3 为何这么便宜
DeepSeek-V3 采用 MoE(混合专家)架构:总参数 671B,但每次推理只激活约 37B,计算量和同量级 Dense 模型相比大幅下降,推理成本约为 GPT-4o 的 1/10。
切换到 DeepSeek API
本地 Ollama 适合开发调试,生产环境通常切换到云端 API(更高并发、无需维护硬件)。Spring AI 的优势是只改配置,不改 Java 代码。
第一步:添加 OpenAI 依赖(DeepSeek 兼容 OpenAI 协议)
在 pom.xml 中替换(或新增)依赖:
<!-- 移除或保留 Ollama 依赖,新增 OpenAI 兼容依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>第二步:修改 application.yml(仅改配置)
spring:
ai:
# 注释掉 Ollama 配置
# ollama:
# base-url: http://localhost:11434
# chat:
# options:
# model: qwen2.5:7b
openai:
# DeepSeek 兼容 OpenAI API 格式,只需换 base-url 和 api-key
api-key: ${DEEPSEEK_API_KEY} # 从环境变量读取,不硬编码
base-url: https://api.deepseek.com # DeepSeek API 地址
chat:
options:
model: deepseek-chat # DeepSeek-V3 的模型 ID
temperature: 0.7设置环境变量:
export DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx第三步:Java 代码零改动
ChatController.java 完全不需要修改。Spring AI 会自动注入 OpenAiChatModel 替代之前的 OllamaChatModel,接口行为完全一致。
测试 DeepSeek API:
# 完全相同的 curl 命令,现在对接 DeepSeek 云端 API
curl "http://localhost:8080/chat?message=用一句话解释KV Cache"同时支持多个模型
生产中可以同时配置 Ollama(开发/测试)和 DeepSeek(生产)。通过 Spring Profile(application-dev.yml / application-prod.yml)切换,Java 代码完全无感知。
API Key 安全
永远不要把 API Key 硬编码在代码或 application.yml 中。使用环境变量(${DEEPSEEK_API_KEY})或 Spring Cloud Config / Vault 管理密钥。
RAG 预览
本文的 Spring AI 项目展示了最基础的"直接问答"模式。实际工程中,更常见的是 RAG(检索增强生成) 架构:
RAG 解决的核心问题:让 LLM 回答它训练数据中没有的知识(企业私有文档、最新信息、特定领域数据)。
Spring AI 提供了开箱即用的 RAG 支持,包括:
DocumentReader:读取 PDF、Word、Markdown、网页VectorStore:集成 Chroma、Milvus、PGVector 等向量数据库QuestionAnswerAdvisor:自动化 RAG 流程
详细实战见:RAG 实战指南
高频面试题
Q1:KV Cache 是什么?为什么能加速推理?
LLM 是自回归生成的,生成第 N 个 Token 若无缓存,需要重新计算前 N-1 个 Token 的 Key 和 Value 矩阵,复杂度 O(N²)。KV Cache 将历史 Token 的 K/V 矩阵缓存在 GPU 显存中,每步只计算新 Token 的 Query 再与缓存的 K/V 做注意力运算,将每步复杂度降至 O(N),推理速度提升 3-10 倍。代价是显存占用随上下文长度线性增长,128K 上下文的 KV Cache 可能需要数十 GB 显存。
Q2:Temperature=0 时模型输出是完全固定的吗?
基本固定,但不能 100% 保证。Temperature=0 采用贪婪解码,每次选概率最高的 Token,理论上是确定性的。但 GPU 浮点运算存在并行加法顺序不固定的非确定性,加上服务端负载均衡可能路由到不同硬件,极小概率会产生差异。需要严格复现时,同时设置 seed 参数(OpenAI API 支持)。
Q3:为什么 Decoder-Only 架构主导了现代 LLM?
三个核心原因:第一,自回归训练(预测下一个 Token)可以利用海量无标注文本,数据效率极高;第二,单向注意力 + KV Cache 使推理过程可以高效缓存已计算结果,生成速度快;第三,Scaling Law(模型越大效果越好)在 Decoder-Only 架构上得到了充分验证,从 GPT-3 到 GPT-4o 均如此。Encoder-Only(BERT)适合理解任务但无法生成,Encoder-Decoder(T5)参数利用效率不如纯 Decoder。
Q4:用一句话解释 Flash Attention
Flash Attention 通过分块计算注意力矩阵,将 GPU HBM(高带宽内存)的读写次数从 O(N²) 降至 O(N),在不改变数学结果的前提下,使注意力计算速度提升 2-4 倍并大幅降低显存占用。
Q5:MoE 架构和 Dense 架构的核心区别是什么?
Dense 架构(如 GPT-4o)每次推理激活全部参数;MoE(混合专家,Mixture of Experts)将模型分成若干"专家"子网络,每次推理由门控网络动态选择激活其中少数几个专家(如 DeepSeek-V3 总参数 671B,每次只激活 37B)。MoE 的优势:在保持大参数量(强表达能力)的同时,将推理计算量降低至同量级 Dense 模型的 1/5-1/10,大幅降低推理成本;缺点是所有专家参数都需要加载到显存,对部署硬件要求更高。
Q6:上下文窗口越大越好吗?
不一定。存在三个问题:第一,"Lost in the Middle"——模型对上下文中间部分的关注度显著低于开头和结尾,超长上下文并不代表中间内容能被有效利用;第二,推理成本随上下文长度增加(注意力计算 O(N²),KV Cache 显存线性增长),128K vs 8K 的成本差距可达 10 倍以上;第三,输出质量不稳定,无关上下文会引入噪声干扰。工程上的正确做法是通过 RAG 检索相关片段注入上下文,而不是把整个知识库塞进去。
知识星球深度内容
完整大厂面经(含详细答案、最新更新)、AI 项目源码、1v1 简历修改,扫码加入「AI 工程师加速社区」知识星球获取 👉 立即加入
