概述
本文是“AI Agent 基础概念与架构总览”系列的第 3 篇。在 [第2篇《Agent的四要素架构:LLM、Memory、Tools、Planning全景图》] 中,我们建立了一张四象限架构图,明确 LLM 作为中央推理引擎,承担意图理解、工具选择与参数生成、最终回复合成三大核心职责。本文将是该引擎的“深度拆解”——深入 LLM 的数学本质、核心能力、六大局限、API 调用参数,以及 Agent 如何通过五层补偿机制,在非确定性的推理引擎上构建确定性的行为。
只有先理解 LLM 能做什么、不能做什么、为什么有时不可靠,你才能真正理解为什么 Agent 需要 Memory、Tools、Planning 这三个外围子系统,以及它们的设计逻辑。 这是后续第4篇(Memory)、第5篇(Tools)、第6篇(Planning)的基础。
总结性引言
如果你是一名写了 10 年 Java 代码的架构师,你习惯的是确定性的世界。if (balance < amount) throw new InsufficientBalanceException() ——给定相同输入,永远走相同分支。SELECT * FROM orders WHERE id = 12345 ——只要数据没变,永远返回相同结果。但当你第一次调用 LLM API 时,你会发现一个“诡异”的现象:同样的 Prompt 发两遍,返回的内容可能不一样。 第一次是完美的 JSON,第二次可能多了一个不存在的字段。这不是 bug,这是 LLM 的本质——它是一个自回归概率模型,不是确定性函数。
P(xₜ | x₁, ..., xₜ₋₁) ——给定上文 Token 序列,LLM 输出的是所有可能的下一个 Token 的概率分布,然后从该分布中采样一个 Token。Temperature 控制这个分布的“陡峭程度”;Top-P 截断长尾。理解了这个本质,你就理解了 Agent 设计的核心挑战:如何在非确定性的推理引擎上构建确定性的行为。
为什么 Agent 的工具调用需要低 Temperature?因为你需要 LLM 每次都生成合法的 JSON 参数,不能有随机性。为什么需要 Function Calling 的 JSON Schema 约束?因为你需要在输出空间上施加硬约束,补偿其概率性。为什么需要 Memory?因为 LLM 每次调用都是无状态的。为什么需要 Tools?因为 LLM 不知道今天几号、不会算数、不能发邮件。本文将帮助你在 30 分钟内建立起对 LLM 的“工程师直觉”——知道它能做什么、不能做什么、如何让它做得更好。
核心要点
- LLM 的本质是自回归概率模型:
P(xₜ | x₁, ..., xₜ₋₁),从概率分布中采样而非取最大值。非确定性既是优势也是挑战。 - 四大核心能力:语言理解与生成、常识推理、模式识别(Few-Shot)、少样本学习。对 Agent 的意义:自然语言交互、减少追问、快速适配新工具、无需微调。
- 六大核心局限:知识截止日期、幻觉、无法精确计算、缺乏持久状态、上下文窗口限制、延迟与成本。每个局限对应一种 Agent 的补偿机制(Tools/Memory/参数控制)。
- Temperature 是控制输出随机性的“开关”:Temperature→0 = 确定性输出(适合工具调用),Temperature→1 = 创意性输出(适合对话)。
- 确定性补偿五层架构:低 Temperature → JSON Schema 强约束 → 结构化输出 → 参数校验与重试 → Few-Shot 示例。层层递进,将概率输出约束为可靠参数。
- LLM 选型决策:闭源 API(推理强/免运维) vs 开源本地(数据安全/低成本),混合路由可兼顾成本与准确率。
文章组织架构图
flowchart TB
1["1. LLM 的本质:自回归概率模型的数学直觉与工程含义"]
2["2. LLM 的核心能力:语言、推理、模式、少样本"]
3["3. LLM 的核心局限:知识截止、幻觉、无计算、无状态、窗口、成本"]
4["4. API 调用本质与关键参数:Temperature、Top-P、Max Tokens 等"]
5["5. 非确定性与确定性补偿:Agent 如何让 LLM 变得可靠"]
6["6. LLM 选型:闭源 API vs 开源本地 vs 混合路由"]
7["7. 与前后系列的衔接"]
8["8. 面试高频专题"]
1 --> 2
2 --> 3
3 --> 4
4 --> 5
5 --> 6
6 --> 7
7 --> 8
架构图说明:
- 总览说明:全文 8 个模块从 LLM 的数学本质出发,逐步深入到核心能力与局限、API 调用参数、Agent 的确定性补偿机制,最后以 LLM 选型、系列衔接和面试题收尾。
- 逐模块说明:模块 1 是全文核心——理解 LLM 的概率本质是理解所有后续内容的基础;模块 2-3 形成“能力-局限”对照体系,每个局限都有 Agent 的补偿机制对应;模块 4 是工程师最关心的实操内容——通过参数调优控制 LLM 行为;模块 5 将 Agent 的确定性补偿机制系统化为五层架构;模块 6 是工程选型决策;模块 7 承上启下;模块 8 面试巩固。
- 关键结论:LLM 不是一个“万能的知识库”,而是一个“推理能力强但需要约束和补偿的组件”。它的概率本质意味着你永远不能 100% 信任它的输出——你需要用低 Temperature 降低随机性,用 JSON Schema 约束输出格式,用参数校验拦截非法值,用 Few-Shot 引导正确模式,用 Tools 补偿它获取实时信息和执行精确计算的能力,用 Memory 补偿它缺乏持久状态的缺陷。掌握了这个心态,你就能在设计 Agent 时正确地“使用”LLM,而不是“依赖”LLM。这就像你在设计一个微服务系统时不会依赖某个服务的“永远在线”——你会加上超时、重试、熔断、降级。对待 LLM,你需要同样的工程化思维。
1. LLM 的本质:自回归概率模型的数学直觉与工程含义
1.1 从数学公式到工程直觉
LLM 的核心数学公式极其简洁:
P(xₜ | x₁, x₂, ..., xₜ₋₁)
它表示:给定前文 Token 序列(从第 1 个到第 t-1 个),模型输出第 t 个 Token 为词表中每个可能 Token 的条件概率分布。整个生成过程就是反复执行“输入上文 → 计算下一个 Token 的概率分布 → 按策略采样一个 Token → 将 Token 追加到上文 → 循环”,直到生成特殊结束符 <EOS> 或达到预设的最大 Token 数。
与确定性计算的对比:
| 计算类型 | 典型场景 | 确定性 | 输出机制 | 异常表现 |
|---|---|---|---|---|
| 关系型数据库 | SELECT * FROM orders WHERE id=12345 | 强确定 | 返回固定数据集,相同查询永远相同结果(除非数据变更) | 不存在的数据返回空集 |
| 规则引擎 | if (balance < amount) reject | 强确定 | 布尔逻辑驱动,相同条件永远走相同分支 | 规则缺失时无匹配 |
| 纯函数 | Math.sqrt(4.0) | 强确定 | 数学运算,输入确定输出确定 | 输入非法时抛异常 |
| 传统 NLP 流水线 | 词典匹配 + 正则 + CRF | 弱确定 | 固定规则和模型参数,输出稳定 | 泛化能力差 |
| LLM | “翻译: Hello” | 非确定 | 从条件概率分布 P(xₜ | ...) 中采样 | 可能生成流畅但错误的内容 |
LLM 的非确定性源于采样。即使对于训练好的模型,给定完全相同的前文,其输出的 logits 向量(softmax 之前的原始分数)是确定的,但采样步骤引入了随机性。你可以把 logits 看成一个巨大的 double[],长度为词表大小(通常 32000~200000),每个元素代表对应 Token 的原始相关性分数。Temperature 就是操作这个数组的“增益”旋钮,而采样策略则决定如何从这个数组中挑出一个值。
1.2 深入自回归生成循环
LLM 不是一次生成整个回答,而是一个 Token 一个 Token 地“生长”。这一循环的每一步都包含 Transformer 网络的一次前向传播。理解这个循环,是理解 Max Tokens 限制、流式响应和推理成本的基础。
flowchart LR
classDef inputSub fill:#f8fafc,stroke:#94a3b8,stroke-width:1.5px
classDef modelSub fill:#f0f4ff,stroke:#93a3d3,stroke-width:1.5px
classDef sampleSub fill:#f0fff4,stroke:#93c5a3,stroke-width:1.5px
classDef outputSub fill:#fef9f0,stroke:#c4a77d,stroke-width:1.5px
classDef inputNode fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#1e3a8a
classDef modelNode fill:#ede9fe,stroke:#8b5cf6,stroke-width:1.5px,color:#4c1d95
classDef sampleNode fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#92400e
classDef outputNode fill:#d1fae5,stroke:#10b981,stroke-width:1.5px,color:#064e3b
classDef decisionNode fill:#fff4e0,stroke:#d4a373,stroke-width:1.5px,color:#7f4f24
classDef endNode fill:#fce4ec,stroke:#e57373,stroke-width:1.5px,color:#1e293b
subgraph InputSub["输入"]
A["输入 Token 序列<br>x1, x2, ..., xt-1"]
end
subgraph ModelSub["模型推理"]
B["Transformer 前向传播<br>计算 logits 向量"]
C["应用 Temperature<br>计算 softmax<br>得到概率分布"]
end
subgraph SampleSub["采样决策"]
D["Top-P / Top-K 截断<br>过滤低概率 Token"]
E["从剩余集合中采样<br>获得一个 Token ID"]
end
subgraph OutputSub["输出与循环"]
F["输出第 t 个 Token<br>追加到输入序列"]
G{"Token == EOS<br>或达到 Max Tokens?"}
end
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
F -->|"否"| A
G -->|"是"| H["结束生成"]
class InputSub inputSub
class ModelSub modelSub
class SampleSub sampleSub
class OutputSub outputSub
class A inputNode
class B,C modelNode
class D,E sampleNode
class F outputNode
class G decisionNode
class H endNode
图表说明:
- 主旨概括:该流程图描绘了 LLM 自回归生成的完整循环,即“输入 → 推理 → 概率分布 → 采样 → 输出一个 Token → 追加再输入”的迭代过程。
- 逐元素分解:
- 输入:一个整数序列(Token IDs),代表当前上下文。第一次调用时,这个序列是 System Prompt + 用户消息的 Token 化结果。
- 模型推理:Transformer 解码器通过多头自注意力和前馈网络,为词表中的每个 Token 输出一个 logit。这一步是计算密集型,占据了绝大部分延迟。
- 应用 Temperature:将 logits 除以 Temperature 值 T,然后计算 softmax:
softmax(logits / T)。T 越小,分布越“陡峭”,最高概率的 Token 被放大的程度越高。 - 采样决策:根据 Top-P(核采样)或 Top-K 参数,截断概率分布的“长尾”(那些不太可能的词),然后在剩余的高概率 Token 中进行随机采样。
- 循环终止:若采到
<EOS>或达到设定的 Max Tokens,循环结束。
- 设计原理映射:自回归是 Transformer 解码器的标准工作模式,利用因果注意力掩码确保每个 Token 只能看到它之前的 Token。这种串行生成的方式,决定了 LLM 推理无法完全并行化,是推理延迟的根本瓶颈。
- 工程联系与关键结论:Max Tokens 参数就是在这个循环的外围设置强制退出条件。设置过小,输出会被截断(例如工具调用的 JSON 参数只输出一半导致
JsonParseException);设置过大,会浪费时间和成本。在 Agent 的工具调用场景,Max Tokens 必须足够容纳完整的 JSON 参数,一般设置为 2000-4000。同时,每次追加 Token 都会增加下一次推理的输入长度,导致推理时间随生成长度线性增加。
1.3 Token:LLM 的“字”
LLM 不直接处理文本,而是处理 Token——文本的压缩表示。分词器(Tokenizer)将原始字符串转换为 Token 序列。不同模型的分词器差异很大:
- GPT-4o:使用
o200k_base分词器,词表约 200,000 个 Token。1 Token ≈ 0.75 英文单词,1 Token ≈ 0.5 个中文字符(“你好世界” → 4 个 Token,“Hello world” → 2 个 Token)。 - Claude 3.5:使用自己的分词器,词表大小未公开,但同样遵循类似的压缩比。
- 开源模型:Llama-3 使用
tiktoken扩展的分词器,词表 128,000;Qwen-2 词表 152,064。
为什么 Token 这么重要?
- 上下文窗口:GPT-4o 的 128K Token 窗口意味着整个输入(System Prompt + 所有历史消息 + 工具定义 + 检索结果)加上输出(推理内容 + 工具调用 JSON)的总 Token 数不能超过 128,000。超过部分会被截断,或者 API 直接报错(400 Bad Request)。
- 成本计算:API 按 Token 计费。GPT-4o 输入 15/1M Token。一个包含完整历史(约 10K Token)的工具调用请求,成本约 $0.05-0.10。在规划 Agent 的循环轮次时,必须计算每次循环的 Token 消耗。
- 工具调用的精确性:分词器的差异会导致一些有趣的问题。例如,“北京”在英文分词器中可能被分成
["北", "京"]两个 Token,而在中文分词器中可能是一个 Token。当你通过 JSON Schema 约束city参数必须是Beijing时,你需要确保模型能准确生成这一字符串,而不会被分词方式干扰。Few-Shot 示例的作用之一,就是通过明确的示例告诉模型,在工具调用时应该输出什么样的字符串格式,从而绕过分词的不确定性。
Token 计数示例(使用 OpenAI tiktoken 库估算):
“请帮我查一下北京今天的天气”→ 约 12 个 Token。- 一个典型的函数定义 JSON Schema(如天气查询) → 约 80 Token。
- 一轮 ReAct 循环中的
Thought: ... Action: ... Observation: ...→ 约 200-500 Token。
在实际开发中,你需要定期统计 API 返回的 usage 字段,监控 Token 消耗趋势。
2. LLM 的核心能力:语言、推理、模式、少样本
这四项能力使 LLM 能胜任 Agent 的大脑,但它们并非魔法,而是大规模预训练带来的涌现。
2.1 语言理解与生成
LLM 能解析人类复杂、模糊的意图:“帮我订明天从北京到上海的机票,尽量便宜但不要红眼航班。” 它能理解“红眼航班”指深夜出发的航班,理解“尽量便宜”意味着按价格排序并取低。在回复阶段,它能将工具返回的结构化 JSON(如 {"flights": [...]})转化为人性化的总结:“为您找到以下 3 个航班,其中最低价 520 元,22:00 后的红眼航班已为您排除。”
Agent 场景示例:用户在对话中突然说“太贵了,有没有更便宜的?”,LLM 需要理解“太贵了”指的是上一轮工具返回的机票价格,然后生成新一轮工具调用,将价格上限下压 20%。这种语境延续的能力,是传统 if-else 聊天机器人无法实现的。
2.2 常识推理
从有限信息推断隐含条件。用户说“订一张去上海的机票”,若对话历史上文提到“我在北京”,LLM 能推断出发地是北京,而不必反问“您从哪里出发?”。更进一步,如果用户说“我要出差三天,帮我在上海订个酒店”,LLM 能推断需要入住和离店日期,并可能调用日历工具查询当前日期,然后计算日期范围。
对 Agent 的意义:常识推理减少了交互轮次,使 Agent 看起来更“懂你”。在工程上,这意味着你可以减少强制追问的代码逻辑,将更多控制权交给 LLM,但也要设置兜底:当 LLM 无法推理出必要参数时,仍需生成澄清问题。
2.3 模式识别与少样本学习
LLM 能从几个示例中快速学习输入输出模式。你在 System Prompt 中提供 3 个 “自然语言 → JSON 函数调用参数” 的示例,它能精准地生成第 4 个符合要求的 JSON。例如:
示例1: 用户: “北京天气” → 工具调用: get_weather(city="Beijing")
示例2: 用户: “今天上海几度” → 工具调用: get_weather(city="Shanghai")
示例3: 用户: “杭州明天会下雨吗” → 工具调用: get_weather(city="Hangzhou", date="2024-05-28")
当用户输入“南京后天呢?”,模型会输出 get_weather(city="Nanjing", date="2024-05-29")。
对 Agent 的意义:Few-Shot 是引导 LLM 生成正确工具调用参数的核心技术。与微调(Fine-tuning)相比,Few-Shot 的优势在于:
- 低成本:无需准备数据集和训练资源,只需修改 Prompt。
- 高灵活:新增工具时,只需在 System Prompt 中添加新工具的示例。
- 可解释:示例就是文档,开发者可以直接看到模型被“教导”了什么。
其代价是占用宝贵的上下文窗口 Token。对于固定不变的复杂格式,微调可能更合适;但对于动态变化的工具集,Few-Shot 是首选。
3. LLM 的核心局限:知识截止、幻觉、无计算、无状态、窗口限制、延迟成本
每个局限都直接定义了 Agent 外围子系统的设计需求。我们必须用工程化的方式来弥补这些缺陷,而非寄希望于模型的“自我进化”。
flowchart LR
subgraph LLM_Capabilities["LLM 核心能力"]
C1[语言理解与生成]
C2[常识推理]
C3[模式识别/少样本学习]
end
subgraph LLM_Limitations["LLM 核心局限"]
L1[知识截止日期]
L2[幻觉]
L3[无法精确计算]
L4[缺乏持久状态]
L5[上下文窗口限制]
L6[延迟与成本]
end
subgraph Agent_Compensations["Agent 补偿机制"]
S1["Tools(搜索/数据库)"]
S2["低 Temp + JSON Schema + RAG + 校验"]
S3["Tools(计算器/代码执行)"]
S4["Memory 系统"]
S5["Memory 裁剪策略 + Planning 循环控制"]
S6["缓存 + 模型分级路由"]
end
C1 --> L2
C2 --> L1
C3 --> L3
L1 --> S1
L2 --> S2
L3 --> S3
L4 --> S4
L5 --> S5
L6 --> S6
图表说明:
- 主旨概括:本图展示了 LLM 的四大核心能力、六大核心局限,以及每个局限如何由 Agent 的特定子系统或策略进行补偿,形成“问题→补偿”的对照体系。
- 逐元素分解:
- 左侧:三大核心能力。
- 中间:六大局限。注意能力与局限的映射:常识推理强,但当面对训练数据截止后的新知识时,就出现“知识截止”局限;语言生成强,但缺乏事实校验机制导致“幻觉”;模式识别强,但不擅于精确数值计算。
- 右侧:Agent 系统的补偿映射。Tools 补偿了知识截止和无法计算;Memory 补偿了无状态和窗口限制;调参和校验补偿了幻觉与不确定性。
- 设计原理映射:Agent 的架构本质上是围绕 LLM 的“短板”进行系统化增强。LLM 是核心推理引擎,但绝不是全能的神。
- 工程联系与关键结论:在设计 Agent 时,应始终遵循“先问 LLM 缺什么,再用外围系统补什么”的原则。不要在 LLM 能力边界内死磕,而应为它配备合适的“工具”和“记忆”。
3.1 知识截止日期
GPT-4o 的知识截止于 2023 年 10 月,Claude 3.5 Sonnet 截止于 2024 年 4 月。这意味着关于截止日期之后的事件、数据、技术变更,模型只能靠“猜”。例如,问“2024 年诺贝尔物理学奖得主是谁?”,模型会编造一个看似合理的答案,或者声称自己不知道(取决于对齐训练)。
补偿机制:Agent 必须通过 Tools 调用实时搜索 API(如 Google Custom Search、Bing API)或查询企业内部知识库,获取最新信息。典型的流程是:
- LLM 识别到用户问题涉及实时信息。
- LLM 生成一个搜索工具调用:
web_search(query="2024 Nobel Prize Physics winner")。 - 工具返回搜索结果片段。
- LLM 基于片段合成最终回答,并附上引用来源。
在 LangChain4j 中,可以定义 WebSearchTool,并在 System Prompt 中指示:“当问题涉及实时数据时,必须使用 web_search 工具。”
3.2 幻觉
幻觉是 LLM 最棘手的问题。它会一本正经地编造 API 参数(如 city="大概北京")、虚构函数名(如 send_email_v2 可能不在工具列表中)、捏造产品功能。根源在于其训练目标是“最大化似然”(给定上文,哪个词接在后面最合理),而非“最大化真实性”。此外,模型没有内在的事实校验回路——它甚至不知道自己“不知道”。
GPT-4 技术报告指出,虽然 GPT-4 在事实准确性上相比前代有显著提升,但在专业领域(医学、法律)的幻觉率仍不可忽视。Anthropic 的 Claude 3 模型卡也强调了通过宪法 AI 对齐减少有害幻觉,但无法根除。
Agent 的补偿机制(按优先级排序):
- 低 Temperature(0.0-0.3):降低采样随机性,使输出更倾向于最高概率的 Token 序列。幻觉往往是低概率路径,低 Temperature 能将其概率压得更低。
- JSON Schema 强约束:当 LLM 被强制按照给定的 JSON Schema 输出时,它无法随意添加字段。例如,
city属性被定义为"type": "string",模型无法将其输出为一个对象。这极大地限制了幻觉的形态。 - RAG(检索增强生成):在调用 LLM 前,先通过向量检索从知识库中获取相关事实文档,并将这些文档作为上下文注入 Prompt。这让 LLM 有了“事实依据”,从“凭空创造”变为“总结改写”,幻觉率可降低 30%-50%(根据 Meta 等公司的研究)。
- 参数校验与重试:工具执行层对 LLM 生成的参数进行业务校验。若
city值不在合法城市列表中,则返回错误信息,让 LLM 重试。这是最后一道防线,可以捕获前几层漏掉的荒谬参数。
幻觉示例与防御效果:
| 场景 | 无防御(Temp=1.0) | 低 Temp + JSON Schema | + RAG | + 参数校验 |
|---|---|---|---|---|
| 查询天气 | city="大概北京" | city="Beijing" | city="Beijing" | city="Beijing" |
| 发送邮件 | to="张三" (缺少邮箱) | to="zhangsan@example.com" | to="zhangsan@example.com" | to="zhangsan@example.com" |
| 调用不存在的 API | update_user_v2 | 仅从 tools 列表选 | 仅从 tools 列表选 | 工具名校验失败,重试 |
3.3 无法精确计算
大模型不擅长多位数乘法、阶乘、复杂数学运算。因为它没有内置的数值计算模块,而是将数学问题当作语言模式来处理。例如,12345 * 67890,正确答案是 838,102,050,但 LLM 可能给出一个接近但错误的结果。
补偿机制:Agent 使用 Tools 调用计算器或代码执行引擎。OpenAI 的 Code Interpreter 就是一个典型的补偿工具。在 Agent 中,你可以定义一个 calculate 工具,接收一个数学表达式字符串,利用 Java 的 ScriptEngine 或 Python 执行器计算并返回结果。LLM 学会在遇到计算问题时生成 calculate(expression="12345*67890") 调用。
3.4 缺乏持久状态与上下文窗口限制
这是两个紧密相关的局限。
- 无状态:每次 API 调用都是独立的,模型不记得上一轮对话的任何内容,除非你将完整的历史消息重新传给它。
- 上下文窗口限制:GPT-4o 最多处理 128K Token,Claude 3 200K。超出则 API 报错,或在内部被粗暴截断(取决于实现)。
补偿机制:Memory 系统。
- 短期记忆:在每次 LLM 调用前,将最近 N 轮的对话历史序列化为
List<ChatMessage>传入。这正是 LangChain4j 中ChatMemory接口的职责。 - 长期记忆:将摘要或重要实体持久化到外部存储(如数据库),在需要时检索并注入 Prompt。
- 窗口管理:当历史消息过长时,Memory 系统必须进行裁剪:滑动窗口(保留最近 K 轮)或摘要压缩(将旧对话总结为一个短消息)。这是 Agent 的 Memory 子系统最核心的工程挑战,将在[第4篇]中深入展开。
3.5 延迟与成本
每次 API 调用都涉及网络往返和 GPU 推理时间,且按 Token 计费。在一个复杂的 Agent 任务中,可能需要 5-10 轮 LLM 调用,导致端到端延迟数秒到数十秒,成本从几分钱到几美元不等。
延迟数据(典型值):
| 模型 | 输入 Token 数 | 输出 Token 数 | 首 Token 延迟(TTFT) | 总延迟 | 近似成本(输出) |
|---|---|---|---|---|---|
| GPT-4o | 500 | 100 | ~300ms | ~1.5s | $0.0015 |
| GPT-4o | 5000 | 2000 | ~500ms | ~8s | $0.03 |
| Claude 3.5 Sonnet | 500 | 100 | ~250ms | ~1.2s | $0.0015 |
| 本地 Llama-3-8B (A100) | 500 | 100 | ~20ms | ~100ms | 电费忽略 |
成本估算示例:一个智能客服 Agent,每次对话平均 3 轮 LLM 调用,每轮输入 2K Token,输出 500 Token。使用 GPT-4o,单次对话成本约 (3 * 2000 / 1,000,000) * 5 + (3 * 500 / 1,000,000) * 15 = $0.03 + $0.0225 = $0.0525。如果日活 10 万次对话,每日成本 2M。
补偿机制:
- 缓存:对相同或语义相似的请求,直接返回缓存结果。使用向量检索判断语义相似度。
- 模型分级路由:简单闲聊用便宜的 GPT-4o-mini,复杂推理用 GPT-4o。
- 限制 Planning 循环轮次:设定最大迭代次数(如 10 次),防止 Agent 陷入死循环。
- 使用流式响应:尽早向用户展示部分结果,降低感知延迟。
4. API 调用本质与关键参数
从 Java 工程师的视角看,LLM API 就是一个 HTTP 服务:发送一个包含文本序列和配置参数的 JSON,返回一个包含生成文本的 JSON。但其中的参数设计深刻地反映了概率模型的本质。
4.1 Chat Completion API 结构
请求体核心字段:
{
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"temperature": 0.2,
"max_tokens": 2000,
"top_p": 0.1,
"tools": [{ /* JSON Schema */ }],
"tool_choice": "auto"
}
响应体核心字段:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_xxx",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Beijing\"}"
}
}]
},
"finish_reason": "tool_calls"
}],
"usage": {
"prompt_tokens": 150,
"completion_tokens": 20,
"total_tokens": 170
}
}
Java 映射(LangChain4j):
OpenAiChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o")
.temperature(0.2)
.maxTokens(2000)
.topP(0.1)
.build();
List<ChatMessage> messages = Arrays.asList(
SystemMessage.from("You are a helpful assistant."),
UserMessage.from("What's the weather in Beijing?")
);
Response<AiMessage> response = model.generate(messages);
// 如果 LLM 决定调用工具
if (response.content().hasToolExecutionRequests()) {
ToolExecutionRequest req = response.content().toolExecutionRequests().get(0);
String functionName = req.name(); // "get_weather"
String arguments = req.arguments(); // "{\"city\":\"Beijing\"}"
}
设计意图:temperature(0.2) 和 topP(0.1) 的组合是 Agent 开发的关键,它告知模型:“不要创意,请严格按模式生成”。tool_choice: "auto" 允许模型在需要时返回工具调用,否则返回文本;你也可以设置为 "required" 强制调用。
4.2 核心参数:Temperature、Top-P、Top-K、Max Tokens
Temperature 深入剖析
数学上,模型最后一层输出一个 vocab_size 维的 logits 向量。采样时,先除以 Temperature T:logits_scaled = logits / T,然后计算 softmax 得到概率分布:P(i) = exp(logits_scaled[i]) / sum(exp(logits_scaled[j]))。
- T = 0:极限情况,分布退化为一个 one-hot 向量,概率最高的 Token 概率为 1,其他为 0。输出完全确定,但 API 中 T=0 实际上会使用极小的 epsilon 实现,近似确定。
- T = 0.1:分布极陡,概率质量高度集中在前几个 Token,随机性极低。
- T = 1.0:保持原始概率分布,多样性适中。
- T = 2.0:分布趋于平坦,低概率 Token 被显著放大,输出变得“疯狂”。
实验对比(同 Prompt:“请生成查询城市天气的函数调用参数,城市是北京”):
| Temperature | 可能的 tool_call arguments | 评估 |
|---|---|---|
| 0.0 | {"city": "Beijing"} (几乎总是) | ✅ 确定、合法 |
| 0.2 | {"city": "Beijing"} 或 {"city": "beijing"} | ⚠️ 极小不确定性,可接受 |
| 0.5 | {"city": "Beijing"} 或 {"city": "Peking", "note": "北京天气"} | ❌ 出现额外字段或别名,非法率高 |
| 1.0 | {"city": "Beijing"} 或 {"city": "大概北京"} 或 {"city": "Bei Jing"} | ❌ 幻觉参数,高概率失败 |
Temperature 对概率分布影响的对比图:
flowchart TD
subgraph T0["Temperature = 0.1"]
direction LR
A0["Beijing<br>概率: 0.99"] --> S0(采样: 几乎必选)
B0["beijing<br>概率: 0.01"]
end
subgraph T1["Temperature = 0.5"]
direction LR
A1["Beijing<br>概率: 0.80"] --> S1(采样: 大概率)
B1["beijing<br>概率: 0.15"]
C1["Peking<br>概率: 0.05"]
end
subgraph T2["Temperature = 1.0"]
direction LR
A2["Beijing<br>概率: 0.60"]
B2["beijing<br>概率: 0.25"]
C2["Peking<br>概率: 0.10"]
D2["约北京...<br>概率: 0.05"]
end
subgraph T3["Temperature = 2.0"]
direction LR
A3["Beijing<br>概率: 0.35"] --> S3(采样: 高度随机)
B3["beijing<br>概率: 0.30"]
C3["Peking<br>概率: 0.15"]
D3["大概北京<br>概率: 0.20"]
end
图表说明:
- 主旨概括:该图直观对比了同一概率分布在四个不同温度值下,概率质量分布的变化,以及这种变化对采样结果确定性的影响。
- 逐元素分解:随着温度升高,概率分布从尖锐(T=0.1)变得平滑(T=1.0),再趋于均匀(T=2.0)。低概率的“噪声” Token 在高温下被放大。
- 设计原理映射:Temperature 直接作用于 softmax 的温度系数,是控制模型“创造力”与“确定性”的核心旋钮。
- 工程联系与关键结论:对于需要精确、合法参数的 Agent 工具调用场景,必须将 Temperature 设置在 0.0-0.3,以避免因采样随机性引入非法字符或幻觉字段。建议配合 Top-P 进一步收紧候选 Token 集。
Top-P(核采样)与 Top-K
- Top-K:取概率最高的 K 个 Token,重新归一化后采样。例如 K=50,则始终保留 50 个候选词。缺点:对于非常集中的分布,可能保留过多噪声;对于平坦分布,可能漏掉高概率但排名超出 K 的词。
- Top-P:按概率从高到低排序,累加概率,当累积概率达到 P 时停止,只从这部分 Token 中采样。例如 P=0.1,则只保留累积概率前 10% 的最小集合(可能只有 2-3 个 Token)。动态调整候选集大小,更符合直觉。
在 Agent 场景,通常关闭 Top-K,使用低 Top-P(0.1-0.2)配合低 Temperature,实现接近确定性的输出。OpenAI API 推荐不要同时改变 temperature 和 top_p,但从工程实践看,组合使用可获得更强的确定性。
Max Tokens 设置策略
该参数控制生成 Token 的上限。如果工具调用的 JSON 参数很复杂(嵌套对象、长数组),务必预留足够空间。一般建议:
- 简单工具(1-2 个字符串参数):
max_tokens=500 - 复杂工具(多参数、嵌套):
max_tokens=2000-4000 - 对话生成:
max_tokens=1024或更多
若输出被截断,finish_reason 会返回 "length",此时你需要重新发送请求,并让 LLM 从前一次结束处续写,但这在 Agent 中非常麻烦。最佳实践是设置一个足够大的值,然后依赖 stop 序列或 <EOS> 来终止。
4.3 API 调用完整请求/响应流程图
sequenceDiagram
participant J as Java App
participant LC as LangChain4j<br>ChatLanguageModel
participant HTTP as HTTP Client
participant OAI as OpenAI Server
participant GPU as GPU 推理集群
J->>LC: generate(List<ChatMessage>)
LC->>LC: 序列化消息为 JSON<br>SystemMessage, UserMessage...
LC->>HTTP: POST /v1/chat/completions<br>JSON Body {model, messages, temperature, tools...}
HTTP-->>OAI: HTTPS Request
OAI->>GPU: 提交推理任务
GPU-->>OAI: 自回归生成 Token 序列
OAI-->>HTTP: HTTP Response<br>JSON {choices: [{message: {role, content, tool_calls}}], usage: {...}}
HTTP-->>LC: Response Body
LC->>LC: 反序列化为 Response<AiMessage><br>解析 tool_calls 为 ToolExecutionRequest
LC-->>J: Response<AiMessage>
图表说明:
- 主旨概括:该序列图展示了从 Java 代码调用
chatModel.generate()到 OpenAI 服务器推理并返回结果的完整端到端链路。 - 逐元素分解:
- Java 应用层:调用 LangChain4j 的统一接口,传递
ChatMessage列表。 - LangChain4j 层:负责将领域对象序列化为符合 API 规范的 JSON,并处理鉴权;在收到响应后,将 JSON 反序列化为
AiMessage及可能的ToolExecutionRequest列表。 - 网络与服务器层:通过 HTTPS 与 OpenAI 服务器交互。延迟包含网络往返(RTT)和服务器排队+推理时间。
- 推理层:在远程 GPU 集群上执行自回归生成。推理时间与输出 Token 数成正比。
- Java 应用层:调用 LangChain4j 的统一接口,传递
- 设计原理映射:LangChain4j 的
ChatLanguageModel接口屏蔽了不同 LLM 提供者的 API 差异,使得切换模型(如从 OpenAI 换成 Claude)仅需更换一个依赖和构建参数。 - 工程联系与关键结论:整个调用链路中的关键风险点在于网络超时、服务限流(429错误)、以及 JSON 解析失败。因此,Agent 框架必须在这一层封装重试、超时、熔断等弹性策略。LangChain4j 内置了
RetryPolicy和RequestLogger等拦截器,这正是工程化落地的必备组件。
4.4 流式响应(Streaming)
对于长文本生成或需要实时反馈的场景,流式调用可以显著降低感知延迟。LangChain4j 提供了 StreamingChatLanguageModel 接口:
StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder()
.apiKey(...)
.modelName("gpt-4o")
.temperature(0.2)
.build();
model.generate(messages, new StreamingResponseHandler<>() {
@Override
public void onNext(String token) {
System.out.print(token); // 实时输出每个 Token
}
@Override
public void onComplete(Response<AiMessage> response) {
// 完整消息
}
@Override
public void onError(Throwable error) { ... }
});
Agent 注意事项:在流式模式下,工具调用参数会分散在多个 onNext 中,框架会累积并解析完整的 JSON。如果 JSON 被截断或格式错误,你需要处理 onError 中的异常。
5. 非确定性与确定性补偿:Agent 如何让 LLM 变得可靠
Agent 需要确定性:工具调用参数必须合法,行为必须可复现。我们通过五层架构逐级补偿 LLM 的非确定性。
flowchart TB
subgraph Layer1["第1层:概率层 (Temperature/Top-P)"]
L1["设置 Temperature=0.1, Top-P=0.1<br>极大降低采样随机性"]
end
subgraph Layer2["第2层:格式层 (JSON Schema)"]
L2["通过 tools 参数注入 JSON Schema<br>将输出空间强制限定为合法 JSON"]
end
subgraph Layer3["第3层:语法层 (Structured Output)"]
L3["使用 response_format: json_object<br>或 Function Calling 模式,杜绝非 JSON 内容"]
end
subgraph Layer4["第4层:业务层 (参数校验与重试)"]
L4["工具执行前校验参数合法性<br>校验失败将错误信息反馈 LLM 重试"]
end
subgraph Layer5["第5层:语义层 (Few-Shot 示例)"]
L5["在 System Prompt 中提供 3-5 个完美示例<br>引导 LLM 模仿正确的参数模式"]
end
Layer1 --> Layer2
Layer2 --> Layer3
Layer3 --> Layer4
Layer4 --> Layer5
图表说明:
- 主旨概括:该图展示了 Agent 系统为确保 LLM 输出可靠、合法的工具调用参数,所采用的五层确定性补偿架构,从底层概率控制到顶层语义引导,层层递进。
- 逐元素分解:
- 第 1 层:在采样阶段通过极低 Temperature 和 Top-P 直接压制随机性。
- 第 2-3 层:利用 API 提供的
tools列表和结构化输出功能,在模型推理时施加格式与语法硬约束,确保输出是合法的 JSON 且符合给定 Schema。 - 第 4 层:在 Agent 应用层进行业务校验(如 city 必须在合法城市列表内)。这是最后一道防线,校验失败后可抛出明确的错误信息,让 LLM 自我修正。
- 第 5 层:通过 Few-Shot 示例从“语义”上教会 LLM 什么是好的参数,降低第 4 层被触发的概率。
- 设计原理映射:这五层形成了一个防御体系:前两层是“预防”,中间两层是“矫正”,最后一层是“教育”,共同保证最终执行的工具动作安全、准确。
- 工程联系与关键结论:从 Java 工程的角度看,这类似于输入校验和异常处理。第 1-3 层是基础设施配置(如数据库连接池大小),第 4 层是业务逻辑前的
Validator,第 5 层是给新手的“代码模板”。一个健壮的 Agent,这五层缺一不可。
5.1 工程实现:可重试的校验装饰器
我们可以用装饰器模式包装 LangChain4j 的 ChatLanguageModel,实现自动校验和重试。以下是一个简化但可运行的 Java 示例:
public class ValidatingChatModel implements ChatLanguageModel {
private final ChatLanguageModel delegate;
private final ObjectMapper objectMapper;
private final Map<String, Class<? extends ToolParams>> paramTypes;
private final int maxRetries;
@Override
public Response<AiMessage> generate(List<ChatMessage> messages) {
for (int retry = 0; retry <= maxRetries; retry++) {
Response<AiMessage> response = delegate.generate(messages);
if (!response.content().hasToolExecutionRequests()) {
return response; // 没有工具调用,直接返回
}
ToolExecutionRequest req = response.content().toolExecutionRequests().get(0);
try {
// 第4层:参数校验
Class<? extends ToolParams> paramType = paramTypes.get(req.name());
if (paramType != null) {
objectMapper.readValue(req.arguments(), paramType); // 反序列化失败会抛异常
// 可以添加更多业务规则校验
}
return response; // 校验通过
} catch (Exception e) {
// 构造错误反馈消息,让 LLM 修正
String errorMsg = "Your previous tool call arguments were invalid: " + e.getMessage()
+ " Please provide valid JSON arguments.";
messages.add(AiMessage.from(req));
messages.add(ToolExecutionResultMessage.from(req.id(), req.name(), errorMsg));
messages.add(UserMessage.from("Please fix the error and call the tool again."));
// 继续循环,重试
}
}
throw new RuntimeException("Exceeded max retries for tool calling.");
}
// 其他委托方法省略...
}
使用:
ChatLanguageModel baseModel = OpenAiChatModel.builder()
.apiKey(...).modelName("gpt-4o").temperature(0.1).topP(0.1).build();
ValidatingChatModel model = new ValidatingChatModel(baseModel,
Map.of("get_weather", WeatherParams.class), 3);
Response<AiMessage> response = model.generate(messages);
这个模式将确定性补偿直接内建到调用层,非常符合 Java 的 AOP 思想。
6. LLM 选型:闭源 API vs 开源本地 vs 混合路由
选型不只是成本计算,更是架构权衡。
| 维度 | 闭源 API (GPT-4o, Claude 3.5) | 开源本地 (Llama-3-70B, Qwen-2-72B) | 混合路由策略 |
|---|---|---|---|
| 推理能力 | ⭐⭐⭐⭐⭐ 顶级,复杂推理、工具调用精准 | ⭐⭐⭐⭐ 接近,但在长上下文、复杂指令遵循上仍有差距 | 复杂任务→闭源,简单任务→开源 |
| 数据隐私 | 数据出域,需签署 DPA,仍有合规风险 | 完全私有化部署,数据安全可控 | 敏感数据→本地,普通数据→云端 |
| 延迟(P50/P99) | ~1.5s / ~8s(受网络和排队影响) | ~0.1s / ~2s(VLLM on A100) | 简单任务低延迟,复杂任务稍高 |
| 成本 | 按量付费,高并发时成本飙升 | 硬件成本固定,单次推理边际成本趋近零 | 可降低 60%-80% API 费用 |
| 运维 | 零运维,但需处理限流和降级 | 需运维 GPU 集群(K8s, VLLM, 量化) | 需维护路由与监控系统 |
| 吞吐(TPS) | 受限 API 速率限制(如 500K TPM) | VLLM 部署 Llama-3-8B:~2000 TPS(A100) | 动态伸缩 |
成本盈亏平衡点简算:
- 云端 API:假设每 1K Token 输出成本 0.15。
- 本地部署:一台 8×A100 服务器年租赁成本约 80,000 除以 3650 万次 ≈ $0.0022/次。
- 如果调用量足够大,本地部署成本优势明显。但需要额外考虑运维人力。
混合路由架构:在 LangChain4j 中,可以自定义一个 RoutingChatLanguageModel,根据消息复杂度(如 Token 数、是否包含工具调用)或用户标签,动态分发到不同的底层模型。这部分详见系列五第 15 篇。
7. 与前后系列的衔接
- 本系列第 2 篇(四要素架构):本文是 LLM 作为“中央推理引擎”的深度展开。第 2 篇中 LLM 的三大职责(意图理解、工具选择、回复合成),其背后的运作机制和可靠性保障,均由本文的参数调优和补偿架构所支撑。
- 本系列第 4 篇(Memory 深度):Memory 的设计是为了补偿 LLM 的“无状态”和“上下文窗口限制”。你将看到如何用 Java 实现滑动窗口、摘要记忆,本质上是在管理传入 LLM 的
messages列表。 - 本系列第 5 篇(Tools 深度):Tools 补偿 LLM 的“知识截止”、“无法计算”和“无法行动”。第 5 篇将详解 Function Calling 协议和 MCP 标准化。
- 本系列第 6 篇(Planning 深度):Planning 的 ReAct 循环直接受本文的 Temperature、Max Tokens 和成本约束影响。低 Temperature 确保规划步骤的稳定,成本控制要求循环轮次必须有限。
- 系列四第 1-3 篇(提示词工程):本文的 Few-Shot 和结构化输出是提示词工程的核心基础技术。
8. 面试高频专题
问题 1:LLM 的本质是什么?为什么说它是“概率模型”而不是“知识库”?这对 Agent 的设计有什么影响?
① 一句话回答:LLM 的本质是自回归概率模型 P(xₜ | x₁,...,xₜ₋₁),它从概率分布中采样生成文本,而非从存储中检索事实,因此它是“想象力引擎”而非“真理数据库”。
② 详细解释:LLM 通过在海量文本上训练,学会了词与词之间的概率关系。给定上文,它输出的是下一个 Token 在整个词表上的概率分布,然后通过采样生成内容。它没有“事实”的概念,只有“哪个词接在后面最合理”的统计规律。这就导致了它会生成看上去合理但实际错误的内容(幻觉)。对 Agent 设计的影响:必须将 LLM 视为一个推理和生成组件,而不是知识源。 你需要用低 Temperature 控制随机性,用 JSON Schema 限制输出格式,用 Tools 获取实时事实,用 Memory 补偿无状态。
③ 多角度追问:
- 架构角度:如何在 Agent 中设计事实核查流水线?
- 性能角度:高 Temperature 对 Agent 工具调用失败率的影响量化。
- 安全角度:如何防止攻击者利用概率采样的随机性进行提示注入?
④ 加分回答:Lilian Weng 将 LLM 描述为“随机鹦鹉”(Stochastic Parrots),强调其没有真正的理解。最新研究如 “Chain-of-Verification” 通过让 LLM 生成多步验证问题来减少幻觉。从工程角度,temperature=0 也并非绝对确定,因为 GPU 浮点运算的并行性可能导致轻微的不一致。OpenAI 推出的 seed 参数可以进一步锁定随机性。
问题 2:Temperature 参数的作用是什么?Agent 的工具调用场景应该设置多少?为什么?
① 一句话回答:Temperature 控制采样概率分布的陡峭程度;工具调用应设置在 0.0-0.3,以获得接近确定性的输出,确保 JSON 参数合法、一致。
② 详细解释:Temperature 是 softmax 函数中的温度系数 T。P = softmax(logits / T)。T 越小,分布越陡峭,概率最高的 Token 被选中的概率无限接近 1,输出趋向确定。这对需要精确语法的 JSON 生成至关重要。若 T 过高,低概率 Token 被采样的概率大增,导致非法参数或幻觉字段。
③ 多角度追问:
- 成本角度:低 Temperature 是否会影响推理延迟或成本?
- 后备策略:如果 Temperature=0 仍输出非法 JSON,如何处理?
- 模型差异:不同模型对 Temperature 的敏感度是否一致?
④ 加分回答:在 OpenAI 的 API 中,将 Temperature 设为 0 可以获得近似确定的输出,但不保证绝对一致。对于需要绝对复现的场景,OpenAI 推出了 seed 参数配合 temperature=0 使用。在 Agent 中,我们结合 JSON Schema 的 tool_choice: "required" 进一步强制约束。
问题 3:LLM 的“幻觉”是什么?Agent 如何通过工程手段降低幻觉率?
① 一句话回答:幻觉是 LLM 生成内容看似合理但不符合事实或用户指令的现象;Agent 通过低 Temperature、RAG、JSON Schema 约束和参数校验多管齐下降低幻觉率。
② 详细解释:幻觉根植于 LLM 的“最大似然”训练目标,而非“最大真实”。模型没有真正的事实校验机制。在 Agent 中,我们通过四道防线降低幻觉:(1)RAG 注入可靠上下文,限制自由发挥;(2)低 Temperature 减少随机性;(3)工具调用时的 JSON Schema 硬约束,杜绝生成参数名以外的内容;(4)业务层的参数校验,对值域进行二次确认。
③ 多角度追问:
- 评估角度:如何量化地衡量一个 Agent 的幻觉率?
- 架构比较:RAG 与 Fine-tuning 在防幻觉方面各自的优劣。
- 监控运维:如何实时检测并告警 Agent 的幻觉事件?
④ 加分回答:Vectara 的 Hallucination Leaderboard 提供了一种基于 NLI 模型的幻觉检测评分。在工程上,可以实施“影子审计”:让一个更强大的模型(如 GPT-4o)对 Agent 的输出进行事实一致性评分,不通过的自动修正或告警。
问题 4:LLM 有哪些核心局限?Agent 如何通过 Memory、Tools、Planning 补偿这些局限?
① 一句话回答:核心局限包括知识截止、幻觉、无计算、无状态、上下文窗口限制、延迟成本,分别由 Tools 获取实时信息/执行计算、Memory 提供状态、Planning 控制循环、参数调优控制成本来补偿。
② 详细解释:见文章第 3 节图表。无状态和窗口限制由 Memory 系统解决,通过消息列表管理和摘要/滑动窗口策略实现。知识截止和无计算能力由 Tools 解决,让 LLM 能调用搜索、计算器等。高延迟和成本由 Planning 的循环控制和模型路由解决。
③ 多角度追问:
- 设计权衡:Memory 的滑动窗口大小如何权衡性能与成本?
- 故障注入:如果工具调用超时,Agent 如何优雅降级?
- 安全边界:如何防止 LLM 通过 Tools 执行危险操作?
④ 加分回答:这就是 Andy Ng 等人强调的 “Agentic Design Pattern”——不要试图让 LLM 做所有事,而是给它构建一个丰富的“技能包”(Tools)和一个“记事本”(Memory)。这种设计模式借鉴了微服务架构中的单体拆分为多个服务,并通过编排实现复杂业务。
问题 5:Top-P 和 Top-K 的区别是什么?在 Agent 场景下通常如何选择?
① 一句话回答:Top-K 是保留概率最高的固定 K 个 Token,Top-P 是保留累积概率达到 P 的最小 Token 集合,动态调整候选集;Agent 工具调用场景推荐使用低 Temperature 配合较小的 Top-P(如 0.1),获得更强确定性。
② 详细解释:Top-K 简单粗暴,但在分布陡峭和分布平坦时都用相同的 K,可能会截断过多或包含太多噪声。Top-P 根据分布形态动态调整,更符合直觉。但两者都用于截断长尾低概率 Token。在已使用极低 Temperature 的情况下,Top-P 的效果会更明显,几乎只保留 1-2 个最高概率 Token。
③ 多角度追问:
- 性能影响:Top-P 动态计算是否会增加推理耗时?
- 开源模型:像 Llama 等模型在 VLLM 中如何配置这些参数?
- 组合使用:Top-K 和 Top-P 同时开启会怎样?
④ 加分回答:OpenAI API 建议不要同时改变 Temperature 和 Top-P,一般选择一个进行调整。在确定性要求极高的场景,组合使用 temperature=0 和 top_p=0.1 可以几乎消除随机性。
问题 6:Few-Shot 示例为什么能提升 LLM 输出的确定性?它与微调有什么区别?
① 一句话回答:Few-Shot 通过在上下文窗口内提供示例,极大地限制了 LLM 的输出模式,使其倾向于模仿;这与微调不同,微调是永久改变模型权重,而 Few-Shot 仅影响单次推理。
② 详细解释:从注意力机制看,示例 Token 与生成 Token 之间有直接的注意力连接,因此示例的输出格式会被直接“拷贝”。Few-Shot 成本低、灵活,但占用上下文窗口 Token。微调能永久植入能力,不占窗口,但需要数据和算力,且可能降低模型通用性。
③ 多角度追问:
- 成本权衡:大量 Few-Shot 示例带来的 Token 成本 vs 微调的一次性成本。
- 动态性:如何根据用户类型动态选择 Few-Shot 示例?
- 负面效应:Few-Shot 示例会不会引入偏见?
④ 加分回答:在实际系统中,通常使用一个轻量级检索器从示例库中找出与当前用户查询最相关的 3-5 个示例,动态注入 Prompt,这被称为“动态 Few-Shot”或“示例检索”。
问题 7:Agent 的“确定性补偿五层架构”是什么?每层的作用和工程实现是什么?
① 一句话回答:五层架构是 Temperature/Top-P(概率层) → JSON Schema(格式层) → Structured Output(语法层) → 参数校验(业务层) → Few-Shot(语义层),各层从低到高逐步将 LLM 的随机输出约束为确定合法的工具调用参数。
② 详细解释:详见本文第 5 节图表及说明。
③ 多角度追问:
- 降级策略:某一层失败后如何降级?
- 自动修复:参数校验失败后,如何构造最优的“修正提示”发给 LLM?
- 测试:如何单元测试这五层防御体系?
④ 加分回答:这五层架构类似于 Java Web 开发中的“过滤器链”(Filter Chain)。请求依次通过安全过滤、编码过滤、参数绑定、验证器、业务逻辑。我们可以将每一层实现为一个独立的拦截器(ChatModelListener 或 ToolExecutionListener),形成一条可插拔的确定性增强链。
问题 8:闭源 API 和开源本地模型如何选型?混合路由策略如何设计?
① 一句话回答:复杂推理、高精工具调用用闭源 API;高频低风险、数据敏感任务用开源本地模型;通过一个智能路由网关,根据任务类型、成本预算和数据隐私标签自动分发请求。
② 详细解释:设计一个 LLMRouter,输入用户查询和上下文,输出目标模型。路由决策可基于规则(查询长度、工具调用复杂度)或一个训练好的分类模型。该路由器还需监控各模型健康状态、速率限制和成本,实现动态切换。
③ 多角度追问:
- 灰度发布:如何安全地将流量从闭源切换到开源模型?
- 数据同步:微调开源模型时,如何获取与闭源模型一致的高质量训练数据?
- 监控指标:混合路由系统需要监控哪些核心指标?
④ 加分回答:类似 API 网关中的服务路由,可以使用 OpenResty 或自定义 Java Filter 实现。更高级的方案是使用强化学习动态调整路由策略,以优化全局的“成本-质量-延迟”帕累托前沿。
问题 9:什么是 Token?为什么了解 Token 对 Agent 开发很重要?
① 一句话回答:Token 是 LLM 处理文本的最小单元,约 0.5 个中文字;它直接决定了 Agent 的上下文窗口利用、API 成本计算和历史记忆的裁剪策略。
② 详细解释:所有 LLM 都有上下文窗口上限(如 128K Token)。你的 System Prompt、对话历史、工具定义、检索到的文档以及预期的模型回复,都必须在这个窗口内。精打细算地使用 Token,是对成本、延迟和性能的全局优化。
③ 多角度追问:
- 计数工具:如何精确计算一条消息的 Token 数?
- 压缩策略:如何压缩历史对话以节省 Token?
- 不同模型:不同模型的 Tokenizer 有何差异?
④ 加分回答:LangChain4j 提供了 TokenStream 和 TokenCountEstimator 等工具。在生产中,你可以在 Memory 组件中加入基于 Token 计数的消息驱逐策略(“当总 Token 数超过总窗口的 80% 时,启动摘要压缩或丢弃最早的消息”),以保证不会触发 API 的上下文超限错误。
问题 10(系统设计题):设计一个 LLM 调用层的可靠性保障方案
要求: ① 当 LLM 返回非法的工具调用参数时,自动重试并修正。 ② 当 LLM 调用超时或失败时,自动降级到备用模型。 ③ 当 LLM 成本超过预算阈值时,自动切换到更便宜的模型。
请给出:① 五层确定性补偿架构的具体设计;② 超时/重试/熔断的策略参数;③ 备用模型的选择标准与切换流程;④ 架构图与时序图;⑤ 成本预算的监控与限流方案。
① 一句话回答:这是一个典型的“弹性工程”问题,解决方案可以设计为一个集成在 LangChain4j 中的 ResilientChatLanguageModel 装饰器,它封装了参数校验重试、超时熔断降级、基于成本的动态路由三大模块。
② 详细解释:
- 五层确定性补偿架构:如本文第 5 节所述,可独立实现为
DeterministicEnhancementPipeline,在调用底层模型前配置 Temperature/Top-P/JSON Schema,在收到响应后执行参数校验和重试修正。 - 超时/重试/熔断:使用 Resilience4j 库。配置如下:
- 超时:API 调用超时 10 秒。
- 重试:最多重试 3 次,仅针对
SocketTimeoutException和429限流错误,间隔采用指数退避(1s, 2s, 4s)。 - 熔断:当 1 分钟内失败率超过 50% 时,熔断器打开,所有请求直接失败或降级,持续 30 秒后进入半开状态试探。
- 成本控制:维护一个
CostTracker服务,记录每分钟/每小时/每日的 API 花费。设定预算阈值(如每日 $100)。当费用达到 80% 时,向管理员告警;达到 100% 时,ModelSelector自动将所有请求降级到成本更低的备用模型(如从 GPT-4o 降级到 GPT-4o-mini 或本地 Llama-3)。 - 架构与时序:见下图。
架构图:
flowchart TB
Client[Agent 业务逻辑]
Decorator[ResilientChatLanguageModel<br>装饰器]
SubDet[DeterministicEnhancementPipeline<br>五层补偿]
SubMod[ModelSelector<br>动态路由]
SubRes[Resilience4j 模块<br>超时/重试/熔断]
SubCost[CostTracker<br>预算监控]
PrimaryModel[主模型 GPT-4o]
FallbackModel[备用模型 GPT-4o-mini / Llama-3]
Client --> Decorator
Decorator --> SubDet
Decorator --> SubMod
Decorator --> SubRes
Decorator --> SubCost
SubMod --> PrimaryModel
SubMod --> FallbackModel
SubRes --> PrimaryModel
SubRes --> FallbackModel
业务时序图(参数校验失败重试与降级):
sequenceDiagram
participant Agent as Agent Logic
participant R as ResilientDecorator
participant P as Pipeline(校验)
participant MS as ModelSelector
participant GPT4 as GPT-4o
participant GPT4M as GPT-4o-mini
Agent->>R: generate(messages)
R->>MS: selectModel()
MS->>R: return GPT-4o
R->>GPT4: generate (with Temp/JSON Schema)
GPT4-->>R: Response (tool_call args)
R->>P: validate(args)
alt 校验失败
P-->>R: Error: "Invalid city"
R->>R: 重试计数+1, 未超最大重试
R->>GPT4: generate (with error feedback)
GPT4-->>R: Response (修正后 args)
R->>P: validate(args)
P-->>R: OK
else GPT-4o 超时/熔断
R->>MS: switchToFallback()
MS->>R: return GPT-4o-mini
R->>GPT4M: generate (降级调用)
GPT4M-->>R: Response
end
R-->>Agent: 最终合法 Response
③ 多角度追问:
- 数据一致性:降级到弱模型后,如何保证工具调用参数的质量不显著下降?
- 自动化恢复:当主模型恢复正常后,如何自动切回?
- 可观测性:如何记录和追踪每次调用的降级、重试事件?
④ 加分回答:可以在 CostTracker 中实现类似 Sentinel 的滑动窗口限流。当成本超过预算时,不仅仅是切换模型,还可以对低优先级的 Agent 任务(如后台分析任务)直接拒绝服务,对高优先级任务(如实时对话)保持昂贵模型可用。这借鉴了“交易系统”中的优先队列和资源隔离思想。
文末速查表:LLM 核心参数速查
| 参数名 | 含义 | Agent 推荐值 | 设置依据 |
|---|---|---|---|
temperature | 采样随机性 | 0.0 - 0.3 (工具调用) 0.7 - 0.9 (闲聊) | 低值确保 JSON 参数确定性,高值用于创意性对话 |
top_p | 核采样截断 | 0.1 - 0.2 (工具调用) | 与低 Temperature 配合,极致压缩采样空间 |
top_k | Top-K 截断 | 通常不设置 | Top-P 更智能,二者二选一 |
max_tokens | 最大生成 Token 数 | 2000 - 4000 (工具调用) | 必须足够容纳完整的 JSON 参数,避免截断 |
frequency_penalty | 重复词惩罚 | 0 (工具调用) | 避免降低 JSON 键名(如 city)的生成概率 |
presence_penalty | 新词鼓励惩罚 | 0 (工具调用) | 同上 |
seed | 随机种子 | 指定固定值 | 配合 temperature=0 实现可复现性,便于调试 |
tool_choice | 工具调用控制 | "required" (当明确需调用工具) | 强制 LLM 必须生成 tool_calls,防止它仅回复文本 |
延伸阅读:
- OpenAI 官方文档:Chat Completion API
- Anthropic 官方文档:Claude API Parameters Guide
- Lilian Weng 博客:“How to Generate: Sampling Methods”
- HuggingFace Open LLM Leaderboard
- 《Attention Is All You Need》论文