mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-30 07:33:45 +00:00
Update 大模型应用.md
补充模型训练与量化、工具调用的实现、AI 编程助手和 Harness 等内容。
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 287 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
+165
-8
@@ -35,6 +35,8 @@
|
||||
* [MCP](#mcp)
|
||||
* [Skills](#skills)
|
||||
* [多 Agent](#多-agent)
|
||||
* [AI 编程助手](#ai-编程助手)
|
||||
* [Harness](#harness)
|
||||
* [开发框架](#开发框架)
|
||||
* [可靠性与评测](#可靠性与评测)
|
||||
* [五、工程实践](#五工程实践)
|
||||
@@ -46,6 +48,13 @@
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
本篇面向准备 AI 应用、Agent 开发岗位,但之前没有接触过大模型开发的程序员。建议的阅读顺序:
|
||||
|
||||
1. 先读“Agent 是什么”和“工具调用”,理解两件最基本的事:模型只会输出文本,调用工具、读写数据都是应用代码做的;模型不保存状态,每次调用都要把需要的信息重新发过去;
|
||||
2. 再读“Agent 循环”和“上下文工程”,看懂一个 Agent 每一步发送了什么、为什么步数越多越贵;
|
||||
3. 然后按需要读检索增强生成、记忆、MCP、Skills 和多 Agent,它们都是在这两件基本事实之上解决具体问题;
|
||||
4. 第一部分的模型原理(注意力、KV Cache、训练)在面试中常被追问,可以最后补。
|
||||
|
||||
## 一、大模型基础
|
||||
|
||||
### Token 与分词
|
||||
@@ -167,6 +176,24 @@ KV Cache 是用显存换计算。每个 Token 要在每一层缓存 K 和 V 两
|
||||
|
||||
SFT 之后还要做偏好对齐,是因为 SFT 只告诉模型“这个回答是对的”,偏好数据告诉模型“两个回答中哪个更好”。有用、安全这类目标很难写出标准答案,但比较两个回答的好坏要容易得多。
|
||||
|
||||
偏好对齐常用的三种算法:
|
||||
|
||||
- PPO(RLHF 中使用的强化学习算法):模型对提示词生成回答,奖励模型打分,再用打分更新模型。训练时要同时加载 4 个模型:正在训练的策略模型、估计每一步价值的价值模型(Critic)、打分的奖励模型,以及冻结的参考模型;参考模型用来计算 KL 散度,限制新模型偏离原模型太远,防止它找到奖励模型的漏洞,生成得分高但没有意义的回答;
|
||||
- DPO:不训练奖励模型,也不在训练中生成回答,直接用标注好的偏好对 (提示词, 较好的回答, 较差的回答) 训练,只需要策略模型和参考模型;
|
||||
- GRPO(DeepSeekMath 提出,DeepSeek-R1 使用):去掉了价值模型。对同一个问题生成一组回答,用组内的平均分作为基准:比平均分高的回答得到正的优势,低的得到负的优势,省去了训练价值模型的显存和计算。
|
||||
|
||||
DPO 的损失函数如下,其中 y<sub>w</sub>、y<sub>l</sub> 分别是较好和较差的回答,π<sub>ref</sub> 是参考模型,β 控制偏离参考模型的程度:
|
||||
|
||||
```text
|
||||
loss = -log σ( β × [ (log π(y_w|x) - log π_ref(y_w|x)) - (log π(y_l|x) - log π_ref(y_l|x)) ] )
|
||||
```
|
||||
|
||||
训练开始时模型与参考模型相同,括号内为 0,损失是 log 2 ≈ 0.693。取 β = 0.1,如果训练后较好回答的对数概率比参考模型高了 2,较差回答的低了 1,括号内为 3,损失降为 -log σ(0.3) ≈ 0.554。也就是说,DPO 在拉高较好回答概率的同时压低较差回答的概率,是一个二分类问题。
|
||||
|
||||
GRPO 的优势计算举例:对一道数学题生成 4 个回答,答对得 1 分、答错得 0 分,得分为 [1, 0, 0, 1],平均分 0.5、标准差 0.5,每个回答的优势 (得分 - 平均分) / 标准差 为 [1, -1, -1, 1],答对的回答被强化,答错的被抑制。如果 4 个回答全对或全错,优势都是 0,这道题对训练没有贡献,所以题目的难度要适中。数学、代码这类能自动判断对错的任务可以直接用规则打分,不需要奖励模型。
|
||||
|
||||
三者的选择:DPO 实现简单、训练稳定,适合已有偏好数据的对齐;PPO 和 GRPO 在训练中生成新的回答,能探索出训练数据之外的更好回答,推理能力的训练通常使用 GRPO 这类强化学习方法。
|
||||
|
||||
### 微调
|
||||
|
||||
微调指的是用特定任务的数据继续训练模型。全量微调要更新所有参数,7B 模型的参数、梯度和优化器状态加起来需要上百 GB 显存。
|
||||
@@ -291,6 +318,8 @@ RAG、微调和提示词解决的是不同的问题:
|
||||
|
||||
需要注意的是,截断历史时工具调用和它的结果要成对保留或者成对删除。只剩下结果而缺少对应的调用时,很多接口会直接报错。
|
||||
|
||||
闲聊、客服这类前后轮关系不大的对话,用滑动窗口就够了,简单且不需要额外调用模型;Agent 执行长任务时,早期做出的决定和用户提出的约束一直有用,适合摘要加外置。压缩有没有丢掉关键信息,可以准备一组依赖早期对话才能回答的问题(例如“用户一开始要求的输出格式是什么”),分别用压缩前和压缩后的上下文回答,比较正确率。
|
||||
|
||||
## 三、检索增强生成
|
||||
|
||||
检索增强生成(Retrieval-Augmented Generation, RAG)指的是先从知识库中检索与问题相关的资料,再把资料和问题一起交给模型生成回答。它用于解决模型不了解私有数据和最新数据的问题,也能减少幻觉,回答还可以附上来源。
|
||||
@@ -340,6 +369,8 @@ c = “今天股价上涨了” (0.6, -0.8) cos(a, c) = 0.48 - 0.48 = 0
|
||||
|
||||
a 和 b 几乎没有相同的字,相似度却很高,这正是向量检索相比关键词检索的优势。向量通常会归一化为长度 1,这时余弦相似度等于点积,计算更快。
|
||||
|
||||
用真实的模型计算,结论相同但数值没有这么极端:用 bge-small-zh-v1.5(512 维)编码这三句话,“怎么申请年假”与“休假的流程是什么”的余弦相似度为 0.603,与“今天股价上涨了”为 0.194。实际系统中不同句子的相似度大多落在一个不太宽的区间里,判断是否相关要看排名和阈值,而不是期望不相关的结果接近 0。
|
||||
|
||||
检索时把问题也转为向量,找出与它最相似的 K 个切片,称为 Top-K 检索。问题和切片必须使用同一个 Embedding 模型,更换模型后所有切片都要重新计算向量。
|
||||
|
||||
### 向量索引
|
||||
@@ -367,6 +398,8 @@ a 和 b 几乎没有相同的字,相似度却很高,这正是向量检索相
|
||||
|
||||
Elasticsearch 中 BM25 的参数 k1 和 b 默认为 1.2 和 0.75,分别控制词频的饱和速度和文档长度的影响程度。
|
||||
|
||||
用同一个例子对比两种检索。查询“错误码 E1024 是什么意思”,候选文档是“错误码 E1024:库存不足”和“错误码 E1025:支付超时”:bge-small-zh 给出的相似度分别为 0.813 和 0.795,几乎分不开;BM25 中“错误码”出现在每篇文档里,逆文档频率很低,E1024 只出现在一篇文档中,权重高得多。在这两篇加一篇“常见错误码说明”组成的小语料上,按 k1 = 1.2、b = 0.75 计算,第一篇得 1.114 分,第二篇只有 0.134 分。
|
||||
|
||||
因此通常两路同时召回,再合并结果。两路的分数不能直接相加,BM25 的分数没有上限,余弦相似度在 -1 到 1 之间。倒数排名融合(Reciprocal Rank Fusion, RRF)只看排名:文档的得分是它在各路结果中排名的倒数之和,即 Σ 1 / (k + rank),k 通常取 60。例如:
|
||||
|
||||
| 文档 | 向量检索排名 | BM25 排名 | RRF 得分 |
|
||||
@@ -470,20 +503,20 @@ Agent(智能体)指的是让大模型通过调用工具来完成任务的系
|
||||
|
||||
可见工具调用本质上仍然是生成文本,所以模型可能写错参数、调用不存在的工具,应用必须先校验再执行。很多服务提供严格模式,用约束解码保证参数符合 Schema。
|
||||
|
||||
以 OpenAI 兼容的接口为例,请求中这样描述工具:
|
||||
以 OpenAI 兼容的接口为例,用户问“订单 1024 到哪了”,请求中这样描述查询订单的工具(省略了查询物流的 get_logistics):
|
||||
|
||||
```json
|
||||
"tools": [{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "查询城市当天的天气。用户询问天气、气温时使用",
|
||||
"name": "get_order",
|
||||
"description": "按订单号查询订单状态和运单号。用户询问订单、发货情况时使用",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {"type": "string", "description": "城市名,例如 杭州"}
|
||||
"order_id": {"type": "string", "description": "订单号,例如 1024"}
|
||||
},
|
||||
"required": ["city"]
|
||||
"required": ["order_id"]
|
||||
}
|
||||
}
|
||||
}]
|
||||
@@ -494,16 +527,22 @@ Agent(智能体)指的是让大模型通过调用工具来完成任务的系
|
||||
```json
|
||||
{"role": "assistant", "tool_calls": [{
|
||||
"id": "call_1", "type": "function",
|
||||
"function": {"name": "get_weather", "arguments": "{\"city\": \"杭州\"}"}
|
||||
"function": {"name": "get_order", "arguments": "{\"order_id\": \"1024\"}"}
|
||||
}]}
|
||||
```
|
||||
|
||||
应用执行函数后,用 tool 角色的消息把结果发回。一次回复中可能包含多个调用,tool_call_id 用于对应是哪一次调用:
|
||||
|
||||
```json
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "{\"weather\": \"晴\", \"temp\": 26}"}
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "{\"status\": \"已发货\", \"tracking_no\": \"SF1234567\"}"}
|
||||
```
|
||||
|
||||
这些结构化的字段只存在于接口中。在本机用 Qwen2.5-7B-Instruct 和 Hugging Face Transformers 运行“订单 1024 到哪了”这个例子,可以直接看到模型收到和输出的原始文本:
|
||||
|
||||
<div align="center"> <img src="pics/tool-call-text.png" width="760px"/> </div><br>
|
||||
|
||||
工具定义被拼进了系统消息,模型输出的是包在 <tool_call> 标签里的一段 JSON,应用用正则把它解析出来;工具结果则被包在 <tool_response> 中,作为下一条消息发回。不同模型的标签不同,但原理相同,OpenAI 兼容接口中的 tool_calls 字段就是推理服务从这段文本中解析出来的。
|
||||
|
||||
工具结果使用单独的角色,模型能区分这是工具返回的数据而不是用户的指令,但这并不能防止结果中藏有恶意指令,见下文的安全。
|
||||
|
||||
模型只能根据工具的名称、描述和参数说明来决定是否调用、怎么填参数,所以工具的设计直接影响调用的准确率:
|
||||
@@ -513,6 +552,8 @@ Agent(智能体)指的是让大模型通过调用工具来完成任务的系
|
||||
- 返回结果要精简,只返回模型需要的字段,大段的原始数据会占用上下文;
|
||||
- 出错时返回能指导模型修正的信息,例如“日期格式应为 2026-01-01”,而不是只返回“参数错误”。
|
||||
|
||||
描述写得是否清楚,对结果的影响很大。在上面的例子中,系统提示词只写“你是电商客服助手,回答要简短”时,7B 模型查到运单号就直接回答“已发货,运单号是 SF1234567”,没有继续查物流;采样运行 10 次,1 次也没有答出包裹的位置。在系统提示词中写明“用户问包裹到哪了时,先查订单拿到运单号,再查物流,告诉用户当前位置和预计送达时间”后,10 次全部依次调用了两个工具,回答“您的包裹目前位于杭州转运中心,预计明天送达”。同样的提示词换成 3B 模型,20 次都只调用了一个工具,可见模型能力不足时,提示词也弥补不了。
|
||||
|
||||
工具很多时,全部的工具定义会占用大量上下文,模型也更容易选错。可以只加载与当前任务相关的工具,例如先根据问题检索出最相关的十几个工具,或者把工具分组交给不同的子 Agent。
|
||||
|
||||
### Agent 循环
|
||||
@@ -599,6 +640,14 @@ ReAct 每一步都根据上一步的结果决定下一步,适合需要边做
|
||||
- 写入:提取出的每条事实连同它的向量、用户 ID 和时间一起保存。新事实与已有的记忆冲突时,例如“用户住在杭州”和“用户搬到了上海”,先检索出相似的旧记忆,以时间较新的为准更新或者删除;
|
||||
- 读取:新的请求到来时,用当前问题检索这个用户最相关的几条记忆,放进系统提示词,而不是把所有记忆都放进去。
|
||||
|
||||
长期记忆还需要遗忘,否则过时和无用的记忆越积越多,检索时会混入噪声:
|
||||
|
||||
- 给每条记忆记录写入时间和最近被用到的时间,检索时把相似度和时间衰减结合起来排序,很久没用到的记忆排在后面;
|
||||
- 定期清理长期没有被用到、或者已经被新事实覆盖的记忆;
|
||||
- 可以设置有效期的信息(例如“用户这周在出差”)在写入时就带上过期时间。
|
||||
|
||||
短期记忆与上下文工程的关系:短期记忆决定保存哪些对话,上下文工程决定每次调用时从中取出哪些、怎样压缩后放进窗口。
|
||||
|
||||
需要注意的是,记忆中存放的是用户的个人信息,应该允许用户查看、修改和删除。
|
||||
|
||||
### MCP
|
||||
@@ -643,7 +692,9 @@ MCP 与相关概念的区别:
|
||||
- 工具调用是模型这一侧的能力,模型根据工具描述给出调用;MCP 是应用与工具之间的协议,解决工具如何被发现和调用。两者配合使用:主机把从服务端获取的工具转换为工具定义交给模型,模型给出调用后,再通过 MCP 发给服务端执行;
|
||||
- 普通的 HTTP 接口也能封装成工具,但每个接口的认证方式、参数格式和文档各不相同,需要逐个适配;MCP 统一了发现和调用的方式,工具描述本身就是写给模型看的。
|
||||
|
||||
MCP 也有代价:每个服务端的工具定义都会进入上下文,接入的服务端多了会占用大量 Token;第三方服务端的工具描述和返回结果可能包含恶意指令,应该像安装软件一样只使用可信的服务端。MCP 规范仍在快速演进,细节以官方最新版本为准。
|
||||
MCP 也有代价。每个服务端的工具定义都会进入上下文,Anthropic 的文档中举的例子是同时接入 GitHub、Slack、Sentry、Grafana、Splunk 五个服务端,工具定义就占约 5.5 万个 Token;可用的工具超过 30~50 个后,模型选对工具的能力也会明显下降。解决方法是按需加载:上下文中只放一个搜索工具和最常用的几个工具,模型先搜索,再把找到的工具的完整定义加入上下文;也可以由应用先根据问题检索出相关的工具,只把这几个交给模型。
|
||||
|
||||
另外,第三方服务端的工具描述和返回结果可能包含恶意指令,应该像安装软件一样只使用可信的服务端。MCP 规范仍在快速演进,细节以官方最新版本为准。
|
||||
|
||||
### Skills
|
||||
|
||||
@@ -665,6 +716,29 @@ Skill 的核心是渐进式加载:
|
||||
- MCP 是连接工具和数据的协议,解决工具怎么接入;
|
||||
- Skill 提供做事的方法,告诉 Agent 完成某类任务的步骤和规范,执行时仍然需要调用工具。
|
||||
|
||||
例如一个生成周报的 Skill 的目录:
|
||||
|
||||
```text
|
||||
weekly-report/
|
||||
├── SKILL.md 名称、描述和操作步骤
|
||||
├── template.md 周报模板,按需读取
|
||||
└── scripts/
|
||||
└── collect.py 统计本周合入的 PR,Agent 运行它,只有输出进入上下文
|
||||
```
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: weekly-report
|
||||
description: 按团队模板生成周报。用户要求写周报、总结本周工作时使用。
|
||||
---
|
||||
|
||||
1. 运行 scripts/collect.py,得到本周合入的 PR 列表;
|
||||
2. 按 template.md 的格式,把 PR 归入“完成”“进行中”“风险”三部分;
|
||||
3. 每条不超过一句话,链接放在末尾。
|
||||
```
|
||||
|
||||
name 只能包含小写字母、数字和连字符,最长 64 个字符;description 最长 1024 个字符,要同时写清楚做什么和什么时候用,Agent 就是根据它判断是否加载这个 Skill。需要接入外部系统、要求权限控制的能力(例如查询公司的工单系统)适合做成 MCP 服务端;告诉 Agent 怎样组合已有工具完成任务的流程和规范,适合写成 Skill。Skill 中可以包含脚本,等同于让 Agent 执行代码,只应该使用可信来源的 Skill。
|
||||
|
||||
### 多 Agent
|
||||
|
||||
复杂任务可以交给多个 Agent 协作完成。常用的结构是主从模式:主 Agent 负责拆分任务和汇总结果,把子任务分派给子 Agent 并行执行。
|
||||
@@ -682,6 +756,57 @@ Skill 的核心是渐进式加载:
|
||||
|
||||
因此多 Agent 适合可以拆成多个独立部分、并行处理的任务,例如同时调研多个主题。子任务之间需要频繁共享上下文的任务,例如修改同一份代码,更适合由单个 Agent 完成。
|
||||
|
||||
多个 Agent 同时修改共享资源时,与多线程的问题相同,处理方法也类似:
|
||||
|
||||
- 隔离:每个 Agent 在自己的工作目录中修改代码,例如为每个 Agent 创建一个 git worktree,完成后再像合并分支一样合并,有冲突时由主 Agent 或人工解决;
|
||||
- 分区:拆分任务时就划清每个 Agent 负责的文件或数据范围,互不重叠;
|
||||
- 加锁或乐观并发:修改数据库时使用行锁或版本号,修改文件前检查文件自读取后是否被改过,被改过就重新读取再修改;
|
||||
- 共享状态显式传递:Agent 之间不共享上下文,需要的信息由主 Agent 在分派任务时写进子任务的说明,子 Agent 的结论写入文件或数据库,而不是依赖对方“记得”。
|
||||
|
||||
### AI 编程助手
|
||||
|
||||
Claude Code、Cursor、Codex 这类 AI 编程助手是目前最常用的 Agent,原理与前面的 Agent 循环相同,只是工具换成了读写文件、搜索代码和执行命令:
|
||||
|
||||
<div align="center"> <img src="pics/coding-agent.png" width="760px"/> </div><br>
|
||||
|
||||
以 Claude Code 为例,官方文档把它的工作过程描述为收集上下文、执行操作、验证结果三个阶段,内置的工具分为文件操作、搜索、执行命令、网络访问和代码理解几类。几个关键的设计:
|
||||
|
||||
- 项目约定:项目根目录的 CLAUDE.md 在每次会话开始时放入上下文,写入构建和测试命令、代码规范等每次都需要的信息,官方建议每个文件不超过 200 行,内容越长越占上下文,也越难被遵守;
|
||||
- 上下文管理:接近上限时自动把对话压缩成摘要,早期对话中的细节可能丢失,所以重要的规则应该写进 CLAUDE.md,而不是只在对话中说一次;子 Agent 在自己的上下文中工作,只把结论返回;
|
||||
- 安全:修改文件前保存快照,可以撤销;执行命令、修改文件前按权限模式决定是否请求确认。对数据库、线上接口这类外部系统的操作无法撤销,需要用权限加以限制。
|
||||
|
||||
用好这类工具的方法与带一个新同事类似:
|
||||
|
||||
- 给出可以验证的目标,例如“让 OrderServiceTest 通过”,而不是“优化一下代码”,Agent 才能自己运行测试判断是否完成;
|
||||
- 先让它阅读代码、给出方案,确认后再修改,避免方向一开始就错了;
|
||||
- 任务拆小,一次只改一个功能,便于审查;生成的代码同样要做代码审查和测试,不能因为是 AI 写的就跳过;
|
||||
- 把反复纠正的要求写进 CLAUDE.md 这类项目约定文件,而不是每次在对话中重复。
|
||||
|
||||
### Harness
|
||||
|
||||
Agent 由模型和 Harness 两部分组成。Harness 指的是模型之外的所有代码、配置和执行逻辑:Agent 循环、系统提示词、工具及其描述、上下文压缩、记忆、沙箱与权限、子 Agent 的调度,以及在固定时机执行的钩子(例如每次修改文件后自动格式化)。前面不到二十行的 Agent 循环就是一个最简单的 Harness;Claude Code、Codex 是功能完整的 Harness,可以换用不同的模型。MCP 和 Skill 也是 Harness 的组成部分,前者负责接入工具,后者负责按需提供做事的方法。
|
||||
|
||||
模型决定了能力的上限,Harness 决定了实际能发挥出多少。前面的实验中,模型不变,只在系统提示词中写清楚查询的步骤,成功率就从 0/10 变成了 10/10。围绕 Agent 设计和改进 Harness 的工作称为 Harness 工程(Harness Engineering),这个说法在 2026 年初开始流行,核心是两类控制:
|
||||
|
||||
- 引导:在 Agent 动手之前给出方向,例如 CLAUDE.md 中的规范、架构文档和 Skill;
|
||||
- 反馈:在 Agent 动手之后检查结果,例如编译、测试、lint、架构检查,以及用另一个模型审查。
|
||||
|
||||
以“Controller 只能调用 Service”这条分层规范为例:
|
||||
|
||||
<div align="center"> <img src="pics/harness-loop.png" width="760px"/> </div><br>
|
||||
|
||||
这条规范只写在 CLAUDE.md 中时,模型大多数时候会遵守,但无法保证。再加一条用 ArchUnit 写的架构测试,违反时测试失败,错误信息写明了违反哪条规则、在哪一行,Agent 读到后就能自己修正,不需要人逐行审查。编译、测试这类确定的检查快、便宜、结果可靠,应该优先使用;模型审查能发现规则写不出来的问题,但更慢,结论也不稳定。同一个问题反复出现时,应该补一条规范或一个检查,而不是每次在对话中纠正。
|
||||
|
||||
任务大到一个上下文窗口装不下时,Harness 还要负责多次会话之间的交接。Anthropic 介绍过一种做法:第一次会话只做准备,把需求拆成功能清单写入 JSON 文件,每项带测试步骤和 passes 字段,初始全部为 false,同时写好启动脚本并提交到 git;之后的每次会话先读进度文件和 git log,启动应用做一次冒烟测试,然后只挑一个未通过的功能实现,端到端测试通过后才把 passes 改为 true,最后提交代码、更新进度文件。清单中的一项例如:
|
||||
|
||||
```json
|
||||
{"id": 12, "description": "订单列表可以按状态筛选",
|
||||
"steps": ["打开 /orders", "选择状态“已发货”", "列表中只显示已发货的订单"],
|
||||
"passes": false}
|
||||
```
|
||||
|
||||
这些设计分别针对长任务中的常见问题:新会话不知道之前做了什么,靠进度文件和 git log 交接;一次做太多、上下文用完时留下半成品,靠每次只做一个功能;没有测试就宣布完成,靠功能清单和端到端测试。清单用 JSON 而不是 Markdown,是因为模型不容易随意改写 JSON 的结构;规定只能修改 passes 字段,是为了防止 Agent 删掉测试来“完成”任务。
|
||||
|
||||
### 开发框架
|
||||
|
||||
上面的循环、工具定义和消息管理在每个项目中都要重复编写,LangChain、LangGraph、Spring AI 等框架把它们封装了起来:
|
||||
@@ -695,6 +820,12 @@ LangGraph 把 Agent 表示为一张状态图:节点是一次模型调用或者
|
||||
|
||||
框架减少了重复代码,但也隐藏了细节,排查问题时仍然需要查看实际发送给模型的消息。
|
||||
|
||||
选择时可以参考:
|
||||
|
||||
- Java 项目优先考虑 Spring AI 或 LangChain4j,与现有的 Spring 体系集成;Python 项目常用 LangChain、LangGraph;
|
||||
- 流程固定、需要暂停等待人工审批或者从中断处恢复时,LangGraph 这类状态图框架更合适;
|
||||
- 逻辑简单时,直接调用模型的接口写一个循环往往更清楚,前面的 Agent 循环不到二十行。
|
||||
|
||||
### 可靠性与评测
|
||||
|
||||
Agent 的每一步都可能出错,需要在代码中兜底,而不是依赖模型自觉:
|
||||
@@ -722,6 +853,8 @@ tool 退款申请已提交,单号 R88
|
||||
- 效率:完成任务所需的步数、耗时和 Token 消耗;
|
||||
- 线上指标:转人工率、用户满意度。
|
||||
|
||||
由于采样的随机性,同一个任务运行多次,结果可能不同,只运行一次的成功率并不可靠。τ-bench 提出用 pass^k 衡量稳定性:同一个任务连续 k 次都成功的概率。它与常见的 pass@k(k 次中至少成功一次)正好相反,假设单次成功率为 90%,pass@8 接近 100%,pass^8 只有 0.9<sup>8</sup> ≈ 43%。面向用户的 Agent 每次都要做对,更应该关注 pass^k。该论文中 GPT-4o 在零售场景的任务上成功率不到 50%,pass^8 不到 25%。
|
||||
|
||||
每次修改提示词、工具或者模型,都在同一批任务上回归,并记录每次运行的完整轨迹,出错时才能复现和定位。
|
||||
|
||||
## 五、工程实践
|
||||
@@ -772,6 +905,7 @@ static void stream(HttpClient client, HttpRequest request, Consumer<String> onDa
|
||||
- Nginx 等反向代理默认会缓冲上游的响应,流式输出变成了一次性输出,需要关闭缓冲,例如在响应头中加上 X-Accel-Buffering: no;
|
||||
- 网关和负载均衡的读超时要大于最长的生成时间;
|
||||
- 用户关闭页面后要及时取消对模型的请求,否则仍在消耗 Token;
|
||||
- 网络抖动导致断线时,模型的生成无法从中途重新开始。需要续传时,让生成任务与连接解耦:生成的内容按会话和事件编号写入 Redis 等缓存,客户端带着 Last-Event-ID 重连后,先从缓存中补发之后的事件,再继续推送新的内容,超时没有重连再取消生成;
|
||||
- 工具调用的参数也是分片段流式返回的,需要按调用的序号拼接,生成结束后才能解析出完整的 JSON。
|
||||
|
||||
### 延迟与成本
|
||||
@@ -794,6 +928,18 @@ Agent 的成本会随步数快速增长,因为每一步都要把完整的历
|
||||
|
||||
自己部署模型时,vLLM 等推理框架通过连续批处理提高吞吐量:每生成一步就把已完成的请求移出批次、把新请求加入,而不是等整批请求全部生成结束。
|
||||
|
||||
自己部署时,显存首先被模型权重占用。量化指的是用更少的位数存储权重,例如把 4 个 BF16 权重量化为 INT8,取绝对值最大的 1.20 除以 127 作为缩放系数 s ≈ 0.00945,每个权重除以 s 后取整:
|
||||
|
||||
```text
|
||||
原始权重 0.12 -0.53 0.97 -1.20
|
||||
INT8 13 -56 103 -127
|
||||
还原(× s) 0.1228 -0.5291 0.9732 -1.2000
|
||||
```
|
||||
|
||||
误差在 0.003 左右;同样的权重量化为 INT4(-7~7)时还原为 0.171、-0.514、1.029、-1.2,误差明显变大。GPTQ、AWQ 等方法在量化时用少量校准数据减小误差,例如 AWQ 根据激活值找出对结果影响大的那部分权重,保护它们的精度。
|
||||
|
||||
以 Qwen2.5-7B(7.61B 参数)为例,权重在 BF16 下约 15.2 GB,INT8 约 7.6 GB,INT4 约 3.8 GB。在一张 24 GB 的显卡上,BF16 时只剩约 8.8 GB 放 KV Cache,按每个 Token 56 KB 计算约 15 万个 Token;INT4 时剩约 20 GB,能放约 35 万个 Token,可以同时服务更多的请求。代价是精度下降,量化后要在自己的任务上重新评测。
|
||||
|
||||
### 稳定性
|
||||
|
||||
- 限流与重试:模型服务会限制请求频率和 Token 速率,超限时返回 429 状态码,需要按指数退避重试;
|
||||
@@ -826,6 +972,9 @@ Agent 的成本会随步数快速增长,因为每一步都要把完整的历
|
||||
- [The Curious Case of Neural Text Degeneration](https://arxiv.org/abs/1904.09751)
|
||||
- [Training Language Models to Follow Instructions with Human Feedback](https://arxiv.org/abs/2203.02155)
|
||||
- [Direct Preference Optimization: Your Language Model is Secretly a Reward Model](https://arxiv.org/abs/2305.18290)
|
||||
- [DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models](https://arxiv.org/abs/2402.03300)
|
||||
- [AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration](https://arxiv.org/abs/2306.00978)
|
||||
- [GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers](https://arxiv.org/abs/2210.17323)
|
||||
- [LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/abs/2106.09685)
|
||||
- [QLoRA: Efficient Finetuning of Quantized LLMs](https://arxiv.org/abs/2305.14314)
|
||||
- [DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning](https://arxiv.org/abs/2501.12948)
|
||||
@@ -845,6 +994,11 @@ Agent 的成本会随步数快速增长,因为每一步都要把完整的历
|
||||
- [ReAct: Synergizing Reasoning and Acting in Language Models](https://arxiv.org/abs/2210.03629)
|
||||
- [LLM Powered Autonomous Agents](https://lilianweng.github.io/posts/2023-06-23-agent/)
|
||||
- [Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents)
|
||||
- [Effective Harnesses for Long-Running Agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents)
|
||||
- [Harness Engineering for Coding Agent Users](https://martinfowler.com/articles/harness-engineering.html)
|
||||
- [The Anatomy of an Agent Harness](https://www.langchain.com/blog/the-anatomy-of-an-agent-harness)
|
||||
- [ArchUnit User Guide](https://www.archunit.org/userguide/html/000_Index.html)
|
||||
- [Claude Docs: Tool Search Tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool)
|
||||
- [How We Built Our Multi-Agent Research System](https://www.anthropic.com/engineering/multi-agent-research-system)
|
||||
- [DeepSeek API 文档:Tool Calls](https://api-docs.deepseek.com/zh-cn/guides/tool_calls)
|
||||
- [Qwen2.5-7B-Instruct 对话模板](https://huggingface.co/Qwen/Qwen2.5-7B-Instruct/blob/main/tokenizer_config.json)
|
||||
@@ -857,3 +1011,6 @@ Agent 的成本会随步数快速增长,因为每一步都要把完整的历
|
||||
- [Nginx: proxy_buffering](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering)
|
||||
- [Orca: A Distributed Serving System for Transformer-Based Generative Models](https://www.usenix.org/conference/osdi22/presentation/yu)
|
||||
- [OWASP Top 10 for LLM Applications: Prompt Injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/)
|
||||
- [τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains](https://arxiv.org/abs/2406.12045)
|
||||
- [How Claude Code works](https://code.claude.com/docs/en/how-claude-code-works)
|
||||
- [How Claude remembers your project](https://code.claude.com/docs/en/memory)
|
||||
|
||||
Reference in New Issue
Block a user