Update 大模型应用.md

补充知识库更新期间的可见性与删除检测、MCP 工具名的约束、多 Agent 讨论与投票的比较、Spring AI 记忆与 Mem0 的版本说明,幻觉一节补充弃答与评测计分;KV Cache 配图改为直接标注。
This commit is contained in:
CyC2018
2026-10-31 14:03:51 +08:00
parent 4e9608a9aa
commit 27f47f946c
3 changed files with 22 additions and 12 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 94 KiB

+22 -12
View File
@@ -184,7 +184,7 @@ MoE 与参数量相同的 Dense 模型相比:
<div align="center"> <img src="pics/attention-example.png" width="680px"/> </div><br>
1. 用“饿”的 Q 与每个 Token 的 K 做点积得到相关性分数,再除以 √d,d 是向量的维度;
1. 用“饿”的 Q 与每个 Token 的 K 做点积得到相关性分数,再除以 √d,d 是 Q、K 向量的维度,多头注意力中是每个头的维度(Qwen2.5-7B 为 3584 ÷ 28 = 128);
2. 对分数做 softmax 得到权重,权重之和为 1,其中“小猫”的权重最大,为 0.70;
3. 按权重对所有 Token 的 V 求和,得到“饿”这个位置的输出,它的大部分信息来自“小猫”。
@@ -468,7 +468,7 @@ QLoRA 进一步把冻结的原始参数量化为 4 位存储,计算时临时
- 双重量化:把缩放系数本身再量化一次,平均每个参数再节省约 0.37 位;
- 分页优化器:显存不足时把优化器状态临时换出到内存。
Qwen2.5-7B 的权重在 4 位下约 4 GB,比 BF16 的 15.2 GB 少了 11 GB 左右;论文中 65B 的模型可以在一张 48 GB 的显卡上微调。
Qwen2.5-7B 的权重在 4 位下约 5.5 GB,比 BF16 的 15.2 GB 少了 10 GB 左右,其中约 2.2 GB 是保持 BF16 的嵌入层和输出层(估算);论文中 65B 的模型可以在一张 48 GB 的显卡上微调。
全参数微调与 LoRA 的取舍:在代码和数学上的对比实验中,LoRA 在目标领域的效果明显不如全参数微调,但更好地保留了原有的能力;全参数微调学到的变化量的秩是常见 LoRA 配置的 10~100 倍。因此:
@@ -497,7 +497,7 @@ LoRA 微调的效果不好时,按以下顺序检查:
### 量化
量化(Quantization)指的是用更少的位数存储模型的参数,例如把 BF16(16 位)的权重转为 INT8 或 INT4。Qwen2.5-7B 的权重在 BF16 下约 15.2 GB,INT8 约 7.6 GB,INT4 约 3.8 GB(本节的 GB 按 10<sup>9</sup> 字节计算,按 2<sup>30</sup> 字节计算时 BF16 权重约 14.2 GB)。
量化(Quantization)指的是用更少的位数存储模型的参数,例如把 BF16(16 位)的权重转为 INT8 或 INT4。Qwen2.5-7B 的权重在 BF16 下约 15.2 GB,INT8 约 7.6 GB,INT4 约 3.8 GB(本节的 GB 按 10<sup>9</sup> 字节计算,按 2<sup>30</sup> 字节计算时 BF16 权重约 14.2 GB)。这是全部参数都按同样的位数折算的值。嵌入层和输出层通常不量化,Qwen2.5-7B 的这两层不共享权重,约 10.9 亿个参数,所以官方发布的 INT4(AWQ)模型权重实际约 5.6 GB。
量化不仅省显存,还能加快生成。这是因为解码阶段每生成一个 Token,都要把全部权重从显存读一遍,而计算量很小,速度受显存带宽限制,权重越小读得越快。推理服务的整体显存怎样估算,见[显存:权重与 KV Cache](#3-显存权重与-kv-cache)。
@@ -555,7 +555,7 @@ KV Cache 也可以量化。Qwen2.5-7B 每个 Token 的 KV Cache 在 BF16 下占
#### 2. 为什么会产生
根本原因在于训练目标:模型学的是生成通顺的下一个 Token,而不是核对事实,不知道的内容也会被“合理地”补全。评测也在鼓励这种行为。多数评测只按答对的比例打分,回答“不知道”得 0 分,猜一个答案还有机会得分,所以倾向于猜测的模型在评测上分数更高,训练和挑选模型时也就偏向了猜测,而不是承认不确定。
根本原因在于训练目标:模型学的是生成通顺的下一个 Token,而不是核对事实,不知道的内容也会被“合理地”补全。评测也在鼓励这种行为。多数评测只按答对的比例打分,回答“不知道”得 0 分,猜一个答案还有机会得分,所以倾向于猜测的模型在评测上分数更高,训练和挑选模型时也就偏向了猜测,而不是承认不确定。自建评测集时要把答对、答错和回答不知道分开统计,答错扣的分比不回答更多。
其它原因:
@@ -603,7 +603,7 @@ Agent 的幻觉除了回答错误,还会出现在工具调用的过程中。
- 结果核对:最终回答中的时间、金额、单号等关键信息,用代码检查是否出现在本轮的工具结果中;“已退款”这类操作结论,以退款工具是否真的返回成功为准,由代码生成确认消息;
- 多 Agent:子 Agent 之间传递结构化的结果和来源,关键结论回到原始数据核对,避免一个子 Agent 的错误被其它 Agent 当成事实继续使用。
幻觉无法完全消除,工程上的目标是降低出现的概率,并让错误能被发现。
模型不可能答对所有问题,但不确定时可以回答“不知道”,而不是猜一个答案。工程上的目标是降低自信地答错的概率,并让错误能被发现。
### 推理模型
@@ -1021,6 +1021,10 @@ embedding 由 text 经 Embedding 模型计算得到,bge-small-zh-v1.5 输出 5
文档修改后,按文档 ID 和内容的哈希值找出变化的文档,删除它的全部旧切片再写入新切片,不需要重建整个索引;只追加新切片会让旧内容继续被召回。只是权限或版本状态变化时,更新切片上的元数据即可,不必重新计算向量。文档的原文和元数据一般以关系数据库为准,向量库按切片 ID 幂等写入,失败时重试,并定期对账两边的数据。
哈希只能发现被修改的文档,整篇源文件被删除时,要靠文档系统的删除事件,或者定期拿源系统的全部文档 ID 与库中比对,删掉多出来的切片。
删旧切片和写新切片不是一次完成的,期间查询可能缺少这篇文档或新旧内容并存;在意这一点时,先写入带新版本号的切片,再把元数据中的当前版本切过去,最后删除旧切片。另外,Milvus 默认的一致性级别是 Bounded,刚写入的数据可能要稍等一会才能查到。
更换 Embedding 模型或切片方式时,所有向量都要重新计算。这时在新的集合中建好索引并评测,再把查询使用的别名(Alias)从旧集合切到新集合,切换过程中查询不中断,出问题时切回去。Milvus 和 Elasticsearch 都支持别名。换了模型时,查询向量也要用新模型计算,所以查询用的模型和集合作为一组配置同时切换、同时回滚。
#### 3. 大规模数据
@@ -1221,7 +1225,7 @@ GraphRAG 在建索引时让模型从每个切片中抽取实体和关系,组
### Agent 是什么 🔥🔥
普通的大模型调用是一问一答:模型根据输入生成一段文本。它无法查询数据库、调用接口,也就拿不到训练数据以外的信息,更不能替用户执行操作。
普通的大模型调用是一问一答:模型根据输入生成一段文本。它无法自行查询数据库、调用接口,只能依据训练中学到的知识和输入中给出的资料,更不能替用户执行操作。
Agent(智能体)指的是让大模型通过调用工具来完成任务的系统。模型负责决定下一步做什么,应用程序负责执行,并把结果交还给模型,如此循环,直到任务完成。例如用户问“订单 1024 到哪了”:
@@ -1576,6 +1580,8 @@ EXPIRE session:7f3a:state 86400
模型正确地选择了更新 m1,但在新记忆中写了一个编造的日期。所以模型只负责判断做哪种操作、新的内容是什么,用户 ID、时间这类字段由代码填写。
需要注意的是,这是 Mem0 论文和早期版本的做法。Mem0 开源版后来改为自动写入只做新增:一次模型调用提取新事实,旧记忆只用来去重,搬家后新旧两条地址都保留,更正和删除由应用调用 update、delete 完成(以官方最新版本为准)。
新旧信息冲突时,按可信程度和适用范围判断:用户本次明确说的优先于记忆中的,用户亲口说的优先于模型推断的,来源相同时以新的为准;只对这一次有效的说法不覆盖长期记忆,例如“这次说详细一点”不会改变“喜欢简短的回答”;订单、账户这类数据以业务系统为准,本来就不写进记忆。仍然无法判断时,在对话中向用户确认。
被更新或删除的旧记忆不直接物理删除,而是标记失效时间,出错时可以追溯和恢复。错误或者过时的记忆被反复检索出来、影响回答,称为记忆污染。除了上面的规则,还可以在放入上下文的记忆前注明“来自长期记忆,可能已经过时,与用户本次的说法冲突时以用户为准”,并给每条记忆带上时间,让模型自己判断。
@@ -1701,7 +1707,8 @@ MCP 中有三种角色:
"params": {"name": "read_file", "arguments": {"path": "README.md"}}}
{"jsonrpc": "2.0", "id": 2,
"result": {"content": [{"type": "text", "text": "# CS-Notes ..."}], "isError": false}}
"result": {"resultType": "complete",
"content": [{"type": "text", "text": "# CS-Notes ..."}], "isError": false}}
```
调用之前,双方还要确认协议版本和各自支持的能力,例如服务端是否提供工具、工具列表变化时是否发通知。2025-11-25 及以前的版本在连接建立时用 initialize 握手协商一次;2026-07-28 版去掉了握手,每个请求在 _meta 中带上协议版本和客户端能力,服务端逐个请求判断是否支持,客户端也可以先用 server/discover 查询服务端支持的版本和能力。
@@ -1735,7 +1742,7 @@ MCP 与相关概念的区别:
- 普通的 HTTP 接口也能封装成工具,但每个接口的认证方式、参数格式和文档各不相同,需要逐个适配;MCP 统一了发现和调用的方式,工具描述本身就是写给模型看的;
- 能执行命令的 Agent 可以直接调用 git、kubectl 这类命令行工具(CLI)。模型在训练中见过大量常用命令,不需要事先把定义放进上下文,需要时运行 --help 查看,输出还可以先用管道过滤,只有结果进入上下文;代价是需要能执行命令的环境,权限只能靠沙箱和命令白名单限制,输出是给人看的文本,没有统一的 Schema。MCP 适合需要统一授权、远程访问或者结构化结果的场景,以及没有命令行环境的应用,例如聊天客户端接入公司的工单系统。
一个 Agent 接入多个服务端时,主机为每个服务端建立一个客户端,把各服务端的工具合并后交给模型。工具名只要求在一个服务端内唯一,合并时可能重名,所以主机通常加上服务端名作前缀,例如 Claude Code 中的工具名形如 mcp__服务端名__工具名。
一个 Agent 接入多个服务端时,主机为每个服务端建立一个客户端,把各服务端的工具合并后交给模型。规范只建议工具名在一个服务端内唯一,不同服务端之间可能重名,所以主机通常加上服务端名作前缀(服务端自报的名称也不保证唯一,Claude Code 用的是用户配置中的名称),例如 Claude Code 中的工具名形如 mcp__服务端名__工具名。
MCP 也有代价。每个服务端的工具定义都会进入上下文,Anthropic 的文档中举的例子是同时接入 GitHub、Slack、Sentry、Grafana、Splunk 五个服务端,工具定义就占约 5.5 万个 Token;可用的工具超过 30~50 个后,模型选对工具的能力也会明显下降。解决方法是按需加载:上下文中只放一个搜索工具和最常用的几个工具,模型先搜索,再把找到的工具的完整定义加入上下文;也可以由应用先根据问题检索出相关的工具,只把这几个交给模型。Anthropic 还介绍过一种做法:把 MCP 工具生成为代码文件,Agent 按需读取工具的定义、编写代码调用,中间结果留在执行环境中,不经过模型,例子中的 Token 消耗从 15 万降到 2000。
@@ -1833,7 +1840,7 @@ Skill 越来越多时,目录中每个 Skill 的描述只占约 100 个 Token
- 主管分派:主管 Agent 决定把哪些子任务交给哪个 Agent,最后汇总,也就是上面的主从模式。子任务由主管根据输入临时决定,适合子任务互相独立、可以并行的情况;
- 流水线:几个 Agent 按固定的顺序执行,上一个的输出是下一个的输入,例如提取商品参数、撰写文案、合规审核。它本质上是节点恰好为 Agent 的工作流,审核不通过可以退回上一步,但要限制退回的次数;
- 讨论:几个 Agent 先各自给出答案,再看过彼此的答案和理由后修改,几轮之后投票或者由裁决者给出结论。研究表明这种方式能提高算术和事实问答的准确率,代价是调用次数等于 Agent 数乘以轮数,适合出错代价高、答案可以互相检验的判断;
- 讨论:几个 Agent 先各自给出答案,再看过彼此的答案和理由后修改,几轮之后投票或者由裁决者给出结论。研究表明这种方式能提高算术和事实问答的准确率,但后来的对照实验发现,提升大部分来自最后的多数投票,只让几个 Agent 各答一次再投票就能得到大部分收益;它的代价是调用次数等于 Agent 数乘以轮数,适合出错代价高、答案可以互相检验的判断;
- 转交(Handoff):没有固定的主管,当前 Agent 判断问题不归自己处理时,把控制权和需要的上下文(完整历史、筛选后的消息或摘要)交给另一个 Agent,由它接着处理并直接回复用户。例如客服的分诊 Agent 调用 transfer_to_refund_agent 工具,之后由退款 Agent 接手对话。与主管分派的区别是接手的 Agent 处理完不会像工具调用那样自动返回原来的 Agent,需要时再转交回去,适合按业务领域划分、由专门的 Agent 直接面对用户的场景。
Agent 之间传递信息的方式有三种:主管分派时通过工具调用的参数和返回值;同一个应用内通过共享的状态对象,例如 LangGraph 中各节点读写的状态,见[开发框架](#开发框架);跨服务、跨团队时通过消息队列或者下文的 A2A 协议。通过消息队列传递时,同一条消息可能被投递多次,接收方要用任务 ID 这类业务键去重,见[接收端的可靠性](消息队列.md#接收端的可靠性)。
@@ -1930,7 +1937,7 @@ Harness 的每个部分都对应一个模型自己做不到或做不稳的问题
| 钩子与校验 | 在固定时机执行确定的检查,不通过就把错误送回模型继续 |
| 评测与可观测 | 记录每一步的输入输出和 Token,改动 Harness 后用同一批任务回归,见 [可靠性与评测](#可靠性与评测) |
工具的返回很长时,Harness 通常只把开头和结尾,或者最新的几条放进上下文,完整结果写到文件中,模型需要时再读取;Claude Code 对 MCP 工具的返回默认限制为 25,000 个 Token,超过 5 万字符的文本结果写入文件,只把路径交给模型。
工具的返回很长时,Harness 通常只把开头和结尾,或者最新的几条放进上下文,完整结果写到文件中,模型需要时再读取;Claude Code 对 MCP 工具的返回默认限制为 25,000 个 Token,超过 5 万字符的文本结果写入文件,只把路径交给模型(2026 年 10 月的官方文档)。
#### 2. 引导与反馈
@@ -2065,7 +2072,9 @@ String answer = ChatClient.create(chatModel)
框架根据方法签名生成参数的 JSON Schema,模型返回工具调用后,由框架执行方法、把结果发回模型,直到得到最终回答。工具抛出运行时异常时,默认把异常信息作为工具结果发回模型,让它修正或者向用户说明。需要注意的是,编译时要保留参数名(javac 的 -parameters 选项,继承 spring-boot-starter-parent 时默认开启),否则 Schema 中的参数名是 arg0(Spring AI 2.0.2 实测,见[附录 A9](#a9-spring-ai-的工具调用循环))。
对话记忆由 ChatMemory 接口管理:默认的 MessageWindowChatMemory 保留最近的 20 条消息,系统消息始终保留;消息存放在 ChatMemoryRepository 中,默认在内存里,也可以换成 JDBC、Cassandra 等实现。使用时给 ChatClient 加上 MessageChatMemoryAdvisor,每次调用传入会话 ID,它在调用前按会话 ID 取出历史放进请求,调用后保存新的消息。需要注意的是,工具调用的请求和结果不会写入记忆,下一轮对话时模型看不到上一轮查过什么(Spring AI 2.0.2 实测,见[附录 A28](#a28-spring-ai-的对话记忆与工具调用)),需要时由应用自己保存,见[短期记忆](#1-短期记忆)。
对话记忆由 ChatMemory 接口管理:默认的 MessageWindowChatMemory 保留最近的 20 条消息,系统消息始终保留;消息存放在 ChatMemoryRepository 中,默认在内存里,也可以换成 JDBC、Cassandra 等实现。使用时给 ChatClient 加上 MessageChatMemoryAdvisor,每次调用传入会话 ID,它在调用前按会话 ID 取出历史放进请求,调用后保存新的消息。需要注意的是,默认情况下工具调用的请求和结果不会写入记忆,下一轮对话时模型看不到上一轮查过什么(Spring AI 2.0.2 实测,见[附录 A28](#a28-spring-ai-的对话记忆与工具调用))。这是因为 MessageChatMemoryAdvisor 默认排在工具调用循环之外,只保存最终的用户消息和回答。
需要保存工具记录时,可以把 MessageChatMemoryAdvisor 的 order 设为大于 ToolCallingAdvisor 的值;Spring AI 2.0 的内置仓库中只有内存、Redis、Neo4j 能保存工具消息,JDBC 不能(以官方最新文档为准),用 JDBC 时由应用自己保存,见[短期记忆](#1-短期记忆)。
Spring AI 主要解决模型接入和单个 Agent 的工具调用。Java 中另一个常用的框架是 LangChain4j,它不依赖 Spring,可以集成到 Spring Boot 或 Quarkus 中,特点是 AI Services:声明一个接口,框架生成实现这个接口的代理对象,与 Spring Data JPA 的 Repository 类似。两者的功能相近,Spring 项目用 Spring AI 与已有的配置、依赖注入结合得更自然。需要状态图编排时,可以用仿照 LangGraph 的 LangGraph4j,它可以与 LangChain4j 或 Spring AI 一起使用。
@@ -2399,7 +2408,7 @@ batch 从 1 增大到 64,总吞吐从 22 Token/s 提高到 214 Token/s,约 9
所以在线对话服务要在延迟和吞吐之间取舍:先定下延迟的目标,例如 TPOT 不超过 50 毫秒,再选能满足目标的最大并发数;离线的批量任务不在乎单个请求的延迟,批次尽量大。
需要注意的是,同一个请求在不同大小的批次中,浮点运算的顺序可能不同,结果可能出现细微的差别(这次在笔记本上的测试中,各个 batch 大小下的贪心输出一致)。对可复现性要求高时,vLLM 提供批次不变(Batch Invariance)的模式,代价是速度可能变慢。
需要注意的是,同一个请求在不同大小的批次中,浮点运算的顺序可能不同,结果可能出现细微的差别(附录 A21 的实验中,各个 batch 大小下的贪心输出一致)。对可复现性要求高时,vLLM 提供批次不变(Batch Invariance)的模式,代价是速度可能变慢。
#### 2. 连续批处理
@@ -3788,6 +3797,7 @@ for r, j in enumerate(B[i]): sc[j] += w / (61 + r)
- [Fast Inference from Transformers via Speculative Decoding](https://arxiv.org/abs/2211.17192)
- [Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism](https://arxiv.org/abs/1909.08053)
- [SGLang: Efficient Execution of Structured Language Model Programs](https://arxiv.org/abs/2312.07104)
- [Debate or Vote: Which Yields Better Decisions in Multi-Agent Large Language Models?](https://arxiv.org/abs/2508.17536)
- [SGLang: Server Arguments](https://docs.sglang.io/docs/advanced_features/server_arguments)
- [vLLM: Parallelism and Scaling](https://docs.vllm.ai/en/latest/serving/parallelism_scaling/)
- [vLLM: Metrics](https://docs.vllm.ai/en/latest/design/metrics/)