前言
在使用 Spring AI 开发大模型应用时,你是否遇到过这些问题:
- 为什么同一个 Prompt 每次输出结果不一样?
- 上下文窗口 128K 到底能放多少内容?
- Temperature 设成多少才能稳定输出 JSON?
- RAG 检索效果不好该怎么调优?
这些问题的答案,都藏在大模型的核心概念里。理解这些概念,是从"会用 API"进阶到"能调优、能排查问题"的关键一步。
一、大模型到底在做什么
1.1 Token 的本质
Token 是模型处理文本的基本单位,通常由子词(subword)切分算法(如 BPE/Unigram)产生。它不是"一个字"或"一个词"的严格等价物:
- 英文可能一个单词被拆成多个 Token
- 中文可能一个词被拆成多个 Token,也可能多个字合并成一个 Token
经验估算(仅用于粗略规划):
| 语言 | 估算方式 |
|---|---|
| 英文 | 1 Token ≈ 3~4 个字符 |
| 中文 | 1 Token ≈ 1~2 个汉字 |
成本趋势提示:Token 成本与 Tokenizer 版本强相关。新一代模型(如 GPT-4o, Qwen2.5)词表扩容至 200k+,中文常用词已被收录为单个 Token,压缩率显著提升。
Token 化过程示例:
|
|
注意:不同供应商对相同文本可能产生不同的 Token 序列。生产环境中应使用对应供应商的 Tokenizer 工具进行精确计数。
1.2 上下文窗口详解
上下文窗口(或称"上下文长度")是 LLM 的**“工作记忆”(Working Memory)**。它决定了模型在任何时刻可以处理或"记住"的文本量。
两大关键能力:
- 对话连续性:决定模型能进行多长的多轮对话而不遗忘早期细节
- 单次处理能力:决定模型一次性能够处理的最大文档、代码库或数据样本的大小
上下文窗口的"隐形占用":
|
|
有效业务内容往往远小于标称上限,通常需要预留 10%~20% 安全边际。
1.3 上下文窗口为什么有上限
上下文窗口并非越大越好,它受限于 Transformer 架构的自注意力机制(Self-Attention):
| 限制因素 | 影响 |
|---|---|
| 计算成本平方级增长 | O(N²),输入 Token 翻倍,需求可能变 4 倍 |
| 推理延迟增加 | 每个新 Token 需要关注所有历史 Token |
| 安全风险增加 | 更长上下文意味着更大的攻击面 |
1.4 上下文溢出的真实表现
当上下文接近上限或内容过长时,常见现象包括:
- 模型忽略早期约束:System Prompt 里要求"必须输出 JSON",但因距离生成点太远被忽略
- “中间丢失"现象(Lost in the Middle):模型对开头和结尾信息最敏感,对中间部分召回率显著下降
- 回答漂移:前半段还围绕问题,后半段开始总结/扩写/跑题
- RAG 失效:检索文档过多,关键信息被稀释
- 成本与延迟激增:首字延迟(TTFT)显著增加
1.5 Token 预算怎么做
把"上下文窗口"当成一个固定容量的桶:
|
|
最实用的预算方式:
|
|
超预算时的处理策略(可解释的"减输入”):
- 优先减少 RAG 的 Top-K 或做片段去重
- 对长字段做摘要/截断(如简历、长回答)
- 多段任务拆成多次调用(分批评估、两阶段生成)
二、解码参数详解
2.1 解码的本质
模型本体输出的是 logits(未归一化分数)。解码阶段把 logits 转成概率分布,再决定"下一步选哪个 Token"。
2.2 Temperature 详解
Temperature 的实现是对 logits 做缩放:
|
|
| Temperature 值 | 效果 |
|---|---|
| T ≈ 1 | 保持原始分布 |
| T < 1 | 分布更尖锐,更倾向选择高概率 Token(更"稳") |
| T > 1 | 分布更平坦,低概率 Token 更容易被采样到(更"灵感") |
工程建议(经验值):
| 场景 | Temperature | 说明 |
|---|---|---|
| 结构化提取 / JSON 输出 | 0~0.3 | 低温,配合严格 schema/重试策略 |
| 评估/分析类文本 | 0.4~0.8 | 中温,平衡稳定性和多样性 |
| 创作类内容 | 更高 | 可增加多样性,但承担一致性风险 |
追求确定性? 若需单元测试幂等或结果复现,仅设
Temperature=0不够(GPU 浮点误差仍可能导致非确定性)。建议同时配置seed参数。固定 seed + 低温可最大程度减少波动。
2.3 Top-p 与 Top-k
它们都是"截断候选集合"的方法,目的是减少离谱 Token 被采样到的概率:
| 参数 | 原理 |
|---|---|
| Top-k | 只保留概率最高的 k 个 Token,在这 k 个里采样 |
| Top-p | 按概率从高到低累加,保留累计概率达到 p 的最小集合 |
常见组合:
- 低温 + Top-p(如 0.9):“相对稳定但允许措辞多样”
- 纯贪婪:尽量可复现的结构化输出(但也可能更容易陷入重复)
2.4 Max Tokens 与 Stop Sequences
| 参数 | 特性 |
|---|---|
| Max Tokens | 硬上限,到上限会被强制截断(常见 JSON 缺括号) |
| Stop Sequences | 软切断,设计不当可能提前截断关键字段 |
2.5 其他参数
| 参数 | 说明 |
|---|---|
| Presence Penalty | 惩罚已出现的 Token,鼓励话题扩展 |
| Frequency Penalty | 惩罚频繁出现的 Token,鼓励多样性 |
三、Prompt 工程
3.1 Prompt 的核心要素
| 要素 | 说明 | 示例 |
|---|---|---|
| Role(角色) | 定义 AI 扮演的身份 | “你是一位资深 Java 后端工程师” |
| Task(任务) | 明确要完成的具体工作 | “帮我审查这段代码的性能问题” |
| Context(上下文) | 提供必要的背景信息 | “项目使用 Spring Boot 3.0,数据库是 PostgreSQL” |
| Format(格式) | 指定输出的结构 | “请用 JSON 格式返回,包含 code 和 reason 字段” |
3.2 System Prompt vs User Prompt
| 类型 | 作用 | 特点 |
|---|---|---|
| System Prompt | 定义行为约束,模型优先遵守 | 通常对用户隐藏,占用上下文 |
| User Prompt | 具体任务输入 | 用户直接提供 |
3.3 CoT 思维链
为什么需要思维链?
- LLM 的推理能力需要显式激发
- 复杂问题直接回答错误率高
- 分步推理可以减少幻觉
CoT 变体:
| 范式 | 适用场景 | 说明 |
|---|---|---|
| 普通 CoT | 数学推导、逻辑分析 | 一步步展示推理过程 |
| 自治 CoT | 模型自我验证推理路径 | 模型自我检查中间步骤 |
| 工具增强 CoT | 需要调用工具获取中间结果 | Tool Calling 协同 |
| 多模态 CoT | 图文混合推理 | 结合图像理解 |
3.4 Few-Shot 学习
为什么 Few-Shot 有效?
- 提供 1~3 个示例,让模型理解输入输出模式
- 比纯文字描述更有效
- 特别适合结构化输出场景
示例格式:
|
|
3.5 任务分解
复杂任务拆成子任务逐一完成,再汇总:
|
|
3.6 Prompt Injection 防护
攻击形式:用户恶意输入篡改 System Prompt
|
|
防护策略:
- 用户输入注入模板前,需清洗特殊符号
- 用 XML 标签(如
<user_input>)物理隔离防注入 - 关键指令与用户输入分离存储
四、结构化输出
4.1 为什么需要结构化输出
- 工程化需要:后续业务逻辑需要解析 LLM 输出
- 一致性保证:不同输入能得到格式统一的输出
- 减少幻觉:格式约束可以降低胡编乱造的概率
4.2 结构化输出方案对比
| 方案 | 特点 | 适用场景 |
|---|---|---|
| JSON Schema 约束 | 实现简单、跨供应商 | 快速原型、兼容性要求高 |
| Function Calling | 结构化更强,供应商差异大 | 需要精确参数调用 |
| Structured Outputs (Strict) | 受限解码,格式错误率趋近 0 | 生产环境,高可靠性要求 |
4.3 JSON Schema 约束
实现方式:在 Prompt 中要求模型输出符合特定 JSON Schema
|
|
风险:有少字段/错类型的风险
4.4 Function Calling
OpenAI Function Calling Schema 示例:
|
|
4.5 处理流水线
|
|
| 阶段 | 说明 | 失败处理 |
|---|---|---|
| 生成 | LLM 输出 JSON | - |
| 解析 | Jackson 反序列化 | 进入修复阶段 |
| 修复 | 将错误信息发回 LLM 要求修正 | 达最大次数则返回错误 |
| 校验 | JSR-380 Bean Validation | 进入修复阶段 |
4.6 Java 实现示例
Spring AI 结构化输出:
|
|
五、Embedding 与向量检索
5.1 Embedding 基础
概念:将文本映射到高维向量空间,与聊天模型不同,关注语义表示
常见模型:
| 模型 | 说明 |
|---|---|
| OpenAI Embedding | text-embedding-3-large |
| BGE | 开源中文优化 |
| 通义千问 Embedding | 国内合规 |
5.2 距离度量
| 度量方式 | 原理 | 适用场景 |
|---|---|---|
| Cosine Similarity | 向量夹角余弦 | 最常用,对长度不敏感 |
| Dot Product | 向量点积 | 关注向量长度/ magnitude |
| L2 (欧氏距离) | 直线距离 | 关注绝对距离 |
5.3 分块策略
| 策略 | 说明 | 注意事项 |
|---|---|---|
| 固定大小分块 | 按字符/Token 数切分 | 可能切断语义单元 |
| 语义分块 | 按段落/标题层级切分 | 保留上下文完整性 |
| Overlap | 相邻块之间有重叠 | 防信息丢失 |
5.4 混合检索
为什么需要混合检索?
- 向量检索:捕捉语义相似性
- BM25 关键词检索:捕捉精确关键词匹配
组合策略:先向量检索召回,再用 BM25 精排
5.5 Rerank
Cross-Encoder 精排:
- 对顶部候选结果进行更精细的相似度计算
- 提升顶部结果的准确性
- 延迟更高,通常用于最后一步精排
六、Context Engineering
6.1 什么是 Context Engineering
狭义:系统提示词的编排(如 Rules、角色的 Markdown 文档等)
广义:动态记忆注入、用户会话状态管理、工具与 Skills 描述的动态组装
6.2 三大核心板块
|
|
6.3 优化技巧
| 技巧 | 说明 |
|---|---|
| Prompt 压缩 | 摘要历史对话,减少无效信息 |
| 分层上下文 | 核心事实 + 临时细节分层管理 |
| 精准按需加载 | 上下文窗口不是垃圾桶 |
七、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| JSON 解析失败 | 低温导致格式不稳定 | 升温 + Strict Mode + Retry |
| 输出被截断 | Max Tokens 太小 | 增大 Max Tokens 或分批处理 |
| 幻觉严重 | 缺少上下文约束 | RAG + 知识图谱双引擎 |
| 响应太慢 | 上下文太长 | 减少输入 Token 或用流式输出 |
| 成本太高 | 频繁调用大窗口模型 | 语义缓存 + 模型降级 |
八、总结
理解大模型核心概念,是从"会用 API"到"能调优、能排查问题"的关键:
- Token:模型处理文本的基本单位,影响成本和容量规划
- 上下文窗口:LLM 的工作记忆,有上限和隐形占用
- 解码参数:Temperature 控制稳定性,Top-p 控制多样性
- Prompt 工程:CoT、Few-Shot、任务分解是核心技巧
- 结构化输出:工程落地的桥梁,JSON Schema / Function Calling / Strict Mode
- Context Engineering:为 LLM 构建高信噪比的信息输入环境