Agent综述
agent综述
前言:理解技术作用的四个层面
在阅读每篇论文时,我们需要明确它的技术创新作用于哪个层面:
| 层面 | 说明 | 典型特征 |
|---|---|---|
| 🔧 LLM 训练 | 需要修改模型权重,进行微调或预训练 | 需要 GPU 集群、训练数据、梯度更新 |
| ⚡ LLM 推理 | 在模型生成时的策略优化 | 修改解码策略、采样方式 |
| 🎭 Agent 编排 | 设计智能体的工作流程和架构 | 定义角色、流程、工具调用逻辑 |
| 📝 提示词工程 | 通过设计 Prompt 引导模型行为 | 零成本、即插即用、无需训练 |
第一部分:智能体框架(Agent)
1.1 ReAct: 推理与行动的协同
论文: ReAct: Synergizing Reasoning and Acting in Language Models
技术层面: 📝 提示词工程 + 🎭 Agent 编排
1.1.1 过去的工作是怎么做的?
在 ReAct 之前,研究者主要采用两种独立的方法:
方法一:Chain-of-Thought (CoT) 纯推理方法
CoT 让模型在回答问题前先进行"思考":
问题:小明有5个苹果,给了小红3个,又买了2个,现在有几个?
【CoT 方式】
思考:小明原有5个苹果
思考:给出3个后剩余 5-3=2 个
思考:买入2个后变成 2+2=4 个
答案:4个
局限性:CoT 无法与外部世界交互,遇到需要查询实时信息的问题就束手无策。
方法二:Action-only 纯行动方法
模型直接输出动作序列,依赖强化学习训练:
其中:
- \(\pi(a|s)\) 是策略函数(给定状态 s,输出动作 a 的概率)
- \(r_t\) 是时间步 t 获得的奖励
- \(\gamma\) 是折扣因子
局限性:缺乏显式推理,决策过程不可解释,容易陷入局部最优。
1.1.2 ReAct 是怎么做的?
ReAct 的核心创新是将推理和行动交织在一起,形成 Thought-Action-Observation 循环:
┌─────────────────────────────────────────────────┐
│ ReAct 循环 │
│ │
│ Thought ──→ Action ──→ Observation ──┐ │
│ ↑ │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
具体实现方式(通过 Prompt Engineering):
ReAct 不需要训练模型,只需要设计特定的提示词模板:
REACT_PROMPT = """
你是一个问答助手。请按照以下格式回答问题:
Thought: [分析当前情况,思考下一步该做什么]
Action: [选择一个动作] Search[关键词] 或 Lookup[术语] 或 Finish[答案]
Observation: [动作执行后的结果,由系统提供]
重复上述过程直到得到最终答案。
问题:{question}
"""
数学形式化:
给定问题 \(q\) 和上下文 \(c\),ReAct 生成交织序列:
其中:
- \(t_i\) 是第 i 步的思考(Thought)
- \(a_i\) 是第 i 步的动作(Action)
- \(o_i\) 是第 i 步的观察(Observation,来自环境)
生成过程遵循自回归方式:
1.1.3 完整示例
问题:2024年诺贝尔物理学奖获得者的出生地是哪里?
Thought 1: 这个问题需要两步:先找到获奖者是谁,再找出生地。
让我先搜索2024年诺贝尔物理学奖获得者。
Action 1: Search[2024 Nobel Prize Physics winner]
Observation 1: The 2024 Nobel Prize in Physics was awarded to
John Hopfield and Geoffrey Hinton for foundational
discoveries in machine learning.
Thought 2: 获奖者是 John Hopfield 和 Geoffrey Hinton,有两位。
我先查询 Geoffrey Hinton 的出生地。
Action 2: Search[Geoffrey Hinton birthplace]
Observation 2: Geoffrey Hinton was born in Wimbledon, London, England.
Thought 3: Hinton 出生于英国伦敦。再查询 Hopfield 的出生地。
Action 3: Search[John Hopfield birthplace]
Observation 3: John Hopfield was born in Chicago, Illinois, USA.
Thought 4: 我已经得到了所有信息。
Action 4: Finish[Geoffrey Hinton 出生于英国伦敦,John Hopfield 出生于美国芝加哥]
1.2 Reflexion: 语言智能体的自我反思
论文: Reflexion: Language Agents with Verbal Reinforcement Learning
技术层面: 📝 提示词工程 + 🎭 Agent 编排
1.2.1 过去的工作是怎么做的?
传统强化学习方法:
智能体通过试错学习,更新神经网络权重:
其中:
- \(\theta\) 是模型参数
- \(\alpha\) 是学习率
- \(J(\theta)\) 是目标函数(期望累积奖励)
策略梯度的核心公式(REINFORCE):
局限性:
- 需要大量样本(sample inefficient)
- 需要可微的奖励函数
- 难以处理稀疏奖励
- 不适用于"冻结"的 LLM(如 GPT-4 API)
1.2.2 Reflexion 是怎么做的?
Reflexion 的革命性创新:用自然语言替代梯度更新!
┌─────────────────────────────────────────────────────────┐
│ Reflexion 架构 │
│ │
│ ┌─────────┐ ┌───────────┐ ┌──────────────────┐ │
│ │ Actor │───→│ Evaluator │───→│ Self-Reflection │ │
│ │ (执行者) │ │ (评估器) │ │ (自我反思器) │ │
│ └─────────┘ └───────────┘ └──────────────────┘ │
│ ↑ │ │
│ │ ┌──────────────┐ │ │
│ └─────────│ Memory (记忆) │←─────────┘ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
核心公式——语言强化学习:
传统 RL 更新权重,Reflexion 更新"经验记忆":
其中:
- \(m_t\) 是第 t 次尝试时的记忆集合
- \(r_t\) 是第 t 次尝试后生成的反思文本
下一次尝试时,策略基于记忆条件生成:
反思生成的 Prompt 模板:
REFLECTION_PROMPT = """
你刚才尝试完成一个任务但失败了。
任务描述:{task}
你的执行轨迹:
{trajectory}
失败原因:{failure_reason}
请反思:
1. 哪里出了问题?
2. 你学到了什么?
3. 下次应该怎么改进?
反思总结(一段话):
"""
1.2.3 完整示例:代码生成任务
第一次尝试:
# 任务:实现一个函数,返回列表中第二大的数
def second_largest(nums):
nums.sort()
return nums[-2] # 错误!没考虑重复元素
# 测试失败:second_largest([1, 5, 5]) 返回 5,期望 1
反思生成:
反思:我的实现没有考虑到列表中可能有重复的最大值。
当输入 [1, 5, 5] 时,排序后取倒数第二个仍是 5。
下次应该先去重,或者遍历找第二大的不同值。
第二次尝试(基于反思):
def second_largest(nums):
unique_nums = list(set(nums)) # 去重
unique_nums.sort()
return unique_nums[-2]
# 测试通过!
1.2.4 与传统 RL 的对比
| 维度 | 传统 RL | Reflexion |
|---|---|---|
| 学习信号 | 标量奖励 | 自然语言反思 |
| 更新方式 | 梯度更新权重 | 追加记忆文本 |
| 样本效率 | 低(需要大量样本) | 高(几次尝试) |
| 可解释性 | 低 | 高(反思可阅读) |
| 适用模型 | 需要可训练 | 适用于 API 模型 |
1.3 Tree of Thoughts: 深思熟虑的问题求解
论文: Tree of Thoughts: Deliberate Problem Solving with Large Language Models
技术层面: ⚡ LLM 推理 + 📝 提示词工程
1.3.1 过去的工作是怎么做的?
方法一:标准 Prompting(IO)
直接输入问题,模型一步输出答案:
方法二:Chain-of-Thought (CoT)
引导模型逐步推理:
其中 \(z_i\) 是中间推理步骤。
方法三:Self-Consistency (CoT-SC)
多次采样,投票选择最一致的答案:
所有这些方法的共同局限:
- 只能沿单一路径前进
- 无法回溯和尝试其他可能性
- 遇到错误无法纠正
1.3.2 Tree of Thoughts 是怎么做的?
ToT 引入搜索算法的思想,将推理过程组织成树形结构:
[问题]
│
┌───────────────┼───────────────┐
▼ ▼ ▼
[思路A] [思路B] [思路C]
分数:0.8 分数:0.3 分数:0.6
│ ✗ │
┌─────┼─────┐ ┌─────┼─────┐
▼ ▼ ▼ ▼ ▼ ▼
[A1] [A2] [A3] [C1] [C2] [C3]
0.9 0.4 0.7 0.5 0.8 0.3
│ ✗ │ ✗ │ ✗
▼ ▼ ▼
[答案1] [答案2] [答案3]
核心组件与数学定义:
- 状态空间 \(S\):所有可能的中间推理状态
- 思维生成器 \(G(s, k)\):给定状态 \(s\),生成 \(k\) 个候选后续思维
- 状态评估器 \(V(s)\):评估状态 \(s\) 的"前景"(0-1分)
- 搜索算法:BFS 或 DFS
BFS(广度优先搜索)算法:
def bfs_tot(problem, max_depth, branch_factor):
# 初始化
current_level = [problem] # 当前层的状态
for depth in range(max_depth):
candidates = []
# 对当前层每个状态生成候选
for state in current_level:
thoughts = generate_thoughts(state, k=branch_factor)
for thought in thoughts:
new_state = state + thought
score = evaluate_state(new_state)
candidates.append((new_state, score))
# 保留分数最高的 b 个状态
candidates.sort(key=lambda x: x[1], reverse=True)
current_level = [c[0] for c in candidates[:branch_factor]]
return best_solution(current_level)
1.3.3 经典案例:24点游戏
问题:使用数字 4, 5, 6, 10 和四则运算,得到 24。
ToT 解题过程:
输入:4, 5, 6, 10
第一层候选思路:
├─ 10 - 4 = 6 (剩余: 5, 6, 6) → 评分: 0.7 ✓
├─ 10 - 6 = 4 (剩余: 4, 4, 5) → 评分: 0.5
├─ 5 + 4 = 9 (剩余: 6, 9, 10) → 评分: 0.6
└─ 6 - 4 = 2 (剩余: 2, 5, 10) → 评分: 0.4
选择最优继续:10 - 4 = 6
第二层候选思路:
├─ 5 + 6 = 11 (剩余: 6, 11) → 评分: 0.3
├─ 6 - 5 = 1 (剩余: 1, 6) → 评分: 0.2
└─ 6 / (6-5) = 6 (剩余: 6, 6) → 评分: 0.8 ✓
选择最优继续:6 / (6-5) = 6
第三层:
└─ 6 + 6 = 12 (✗) 或 6 × 6 = 36 (✗)... 需要回溯
回溯到第一层,尝试其他路径...
最终找到:(10 - 4) × 6 - 5 = 31 (✗)
继续搜索:5 × (10 - 6) + 4 = 24 ✓
1.3.4 评估函数的实现
ToT 使用 LLM 本身作为评估器:
EVALUATION_PROMPT = """
我正在玩24点游戏,需要用四则运算让结果等于24。
当前进度:{current_state}
剩余数字:{remaining_numbers}
请评估这个状态能得到24的可能性:
- sure (确定能)
- likely (可能能)
- impossible (不可能)
你的判断:
"""
def evaluate_state(state):
response = llm(EVALUATION_PROMPT.format(...))
if "sure" in response: return 1.0
if "likely" in response: return 0.5
return 0.0
1.3.5 不同方法的推理范式对比
IO Prompting: 输入 ────────────────────→ 输出
CoT: 输入 → 思考1 → 思考2 → ... → 输出
CoT-SC: 输入 → 思考1a → 思考2a → 输出a ─┐
输入 → 思考1b → 思考2b → 输出b ─┼→ 投票 → 最终输出
输入 → 思考1c → 思考2c → 输出c ─┘
ToT: ┌─ 思考B1 ──→ ...
输入 ──→ 思考A ─┤
│ └─ 思考B2 ──→ 输出
│
└─ 思考C ─── (剪枝)
1.4 AutoGen: 多智能体对话框架
论文: AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation
技术层面: 🎭 Agent 编排
1.4.1 过去的工作是怎么做的?
单智能体模式:
一个 LLM 承担所有角色,通过 Prompt 切换"身份":
# 传统方式:一个模型扮演多个角色
response = llm("你是一个程序员,请写一段代码...")
response = llm("你是一个测试工程师,请检查这段代码...")
response = llm("你是一个代码审查员,请评审...")
局限性:
- 上下文窗口被多角色共享,容易混乱
- 难以实现真正的"辩论"和"协作"
- 流程控制复杂
1.4.2 AutoGen 是怎么做的?
AutoGen 设计了真正独立的多个智能体,通过对话协议协作:
┌────────────────────────────────────────────────────────────┐
│ AutoGen 多智能体架构 │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Agent A │ ←─────→ │ Agent B │ │
│ │ (助手) │ 对话 │ (用户代理) │ │
│ └─────────────┘ └─────────────┘ │
│ ↑ ↑ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Agent C │ │ Agent D │ │
│ │ (评审员) │ │ (执行器) │ │
│ └─────────────┘ └─────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘
核心抽象——ConversableAgent:
class ConversableAgent:
def __init__(self, name, system_message, llm_config):
self.name = name
self.system_message = system_message
self.llm = create_llm(llm_config)
self.chat_history = []
def receive(self, message, sender):
"""接收来自其他智能体的消息"""
self.chat_history.append({
"role": "user",
"content": f"[{sender.name}]: {message}"
})
def generate_reply(self):
"""生成回复"""
response = self.llm(
system=self.system_message,
messages=self.chat_history
)
return response
def send(self, message, recipient):
"""发送消息给其他智能体"""
recipient.receive(message, self)
对话流转的数学建模:
定义智能体集合 \(\mathcal{A} = \{A_1, A_2, ..., A_n\}\),对话状态为:
其中 \(m_i = (a_i, c_i)\) 表示智能体 \(a_i\) 发送的内容 \(c_i\)。
下一条消息由调度器决定:
1.4.3 实际代码示例
from autogen import AssistantAgent, UserProxyAgent
# 创建助手智能体(使用 GPT-4)
assistant = AssistantAgent(
name="程序员小明",
system_message="你是一个Python专家,擅长写简洁高效的代码。",
llm_config={"model": "gpt-4"}
)
# 创建用户代理(可执行代码)
user_proxy = UserProxyAgent(
name="用户代理",
human_input_mode="NEVER", # 全自动模式
code_execution_config={"work_dir": "coding"}
)
# 发起对话
user_proxy.initiate_chat(
assistant,
message="请写一个快速排序算法,并用随机数组测试"
)
对话过程:
用户代理: 请写一个快速排序算法,并用随机数组测试
程序员小明: 好的,这是快速排序的实现:
```python
import random
def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
# 测试
test_arr = [random.randint(1, 100) for _ in range(10)]
print(f"原数组: {test_arr}")
print(f"排序后: {quicksort(test_arr)}")
用户代理: [执行代码]
输出:
原数组: [34, 67, 23, 89, 12, 45, 78, 56, 90, 11]
排序后: [11, 12, 23, 34, 45, 56, 67, 78, 89, 90]
用户代理: 代码执行成功!
#### 1.4.4 群聊模式
```python
from autogen import GroupChat, GroupChatManager
# 创建多个专家智能体
coder = AssistantAgent(name="程序员", ...)
reviewer = AssistantAgent(name="代码审查员", ...)
tester = AssistantAgent(name="测试工程师", ...)
# 创建群聊
group_chat = GroupChat(
agents=[coder, reviewer, tester],
messages=[],
max_round=10
)
# 群聊管理器
manager = GroupChatManager(groupchat=group_chat)
# 发起群聊
user_proxy.initiate_chat(
manager,
message="请设计并实现一个安全的密码验证函数"
)
第二部分:检索增强生成(RAG)
2.1 Dense Passage Retrieval: 密集检索的基石
论文: Dense Passage Retrieval for Open-Domain Question Answering
技术层面: 🔧 LLM 训练(需要训练编码器)
2.1.1 过去的工作是怎么做的?
稀疏检索方法(BM25/TF-IDF):
TF-IDF 的数学定义:
其中:
- \(\text{TF}(t, d) = \frac{f_{t,d}}{\sum_{t' \in d} f_{t',d}}\) (词频)
- \(\text{IDF}(t, D) = \log \frac{|D|}{|\{d \in D: t \in d\}|}\) (逆文档频率)
BM25 改进版:
稀疏检索的局限性:
查询:"苹果手机的创始人是谁?"
文档:"乔布斯创立了 iPhone 的制造商。"
词袋匹配:
- "苹果" 不在文档中 ✗
- "手机" 不在文档中 ✗
- "创始人" 不在文档中 ✗
BM25 分数很低,但这是正确答案!
问题本质:词汇鸿沟(Lexical Gap)——同义词、释义无法匹配。
2.1.2 DPR 是怎么做的?
DPR 使用神经网络编码器将文本映射到密集向量空间:
┌──────────────────────────────────────────────────────────┐
│ DPR 双编码器架构 │
│ │
│ 问题: "苹果手机创始人" 文档: "乔布斯创立了..." │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Query Encoder │ │ Passage Encoder │ │
│ │ (BERT) │ │ (BERT) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ [0.2, -0.5, ...] [0.3, -0.4, ...] │
│ 768维向量 768维向量 │
│ │ │ │
│ └───────────┬──────────────────┘ │
│ ▼ │
│ 点积相似度 │
│ sim(q, p) = q · p = 0.92 │
└──────────────────────────────────────────────────────────┘
数学定义:
给定问题 \(q\) 和段落 \(p\),相似度计算为:
其中:
- \(E_Q: \mathbb{R}^{|V|^*} \rightarrow \mathbb{R}^d\) 是问题编码器
- \(E_P: \mathbb{R}^{|V|^*} \rightarrow \mathbb{R}^d\) 是段落编码器
- \(d = 768\) 是向量维度
训练目标——对比学习:
对于问题 \(q_i\) 及其正确段落 \(p_i^+\),训练目标是最大化:
其中 \(p_{i,j}^-\) 是负样本(不相关的段落)。
负样本策略:
| 类型 | 来源 | 效果 |
|---|---|---|
| Random | 随机段落 | 一般 |
| In-batch | 同批次其他问题的正样本 | 好 |
| Hard Negative | BM25 高分但错误的段落 | 最好 |
Hard Negative 的直觉:迫使模型学习更细微的语义差异。
2.1.3 为什么密集检索更好?
向量空间的语义属性:
在训练后的向量空间中:
- "苹果手机" 和 "iPhone" 的向量接近
- "创始人" 和 "创立者" 的向量接近
- "乔布斯" 和 "苹果公司CEO" 的向量接近
实验结果:
| 方法 | Top-5 准确率 | Top-20 准确率 | Top-100 准确率 |
|---|---|---|---|
| BM25 | 43.2% | 59.1% | 73.7% |
| DPR | 67.5% | 79.4% | 86.0% |
提升:Top-20 准确率从 59.1% 提升到 79.4%(+20.3%)
2.2 GraphRAG: 知识图谱增强的检索
资料: Microsoft GraphRAG Project
技术层面: 🎭 Agent 编排 + ⚡ LLM 推理
2.2.1 传统 RAG 的局限性
问题:缺乏全局理解
文档集:
- 文档1: "张三是公司A的CEO"
- 文档2: "公司A收购了公司B"
- 文档3: "公司B的CTO是李四"
- 文档4: "王五是公司A的董事"
用户问题:"公司A的高管都有谁?"
传统RAG:
1. 向量检索找到文档1和文档4
2. 回答:"张三(CEO)和王五(董事)"
遗漏:李四(因为公司A收购了公司B,李四也是公司A的高管!)
问题本质:传统 RAG 只能进行局部匹配,无法进行多跳推理。
2.2.2 GraphRAG 是怎么做的?
GraphRAG 构建知识图谱,将关系显式化:
┌────────────────────────────────────────────────────────────┐
│ GraphRAG 流程 │
│ │
│ ┌─────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │原始文档 │ ─→ │ LLM实体抽取 │ ─→ │ 知识图谱构建 │ │
│ └─────────┘ └──────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 社区检测 │ │
│ │ (Leiden算法) │ │
│ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 层级摘要生成 │ │
│ └─────────────────┘ │
└────────────────────────────────────────────────────────────┘
步骤一:实体和关系抽取
使用 LLM 从文档中提取实体和关系:
EXTRACTION_PROMPT = """
从以下文本中提取所有实体和它们之间的关系。
文本:{text}
输出格式(JSON):
{
"entities": [
{"name": "实体名", "type": "类型", "description": "描述"}
],
"relationships": [
{"source": "实体1", "target": "实体2", "relation": "关系类型"}
]
}
"""
步骤二:图结构构建
┌─────────────────────────────────────┐
│ 知识图谱 │
│ │
│ [张三] ──CEO──→ [公司A] ←──董事── [王五]
│ │ │
│ 收购 │
│ ▼ │
│ [公司B] │
│ ↑ │
│ CTO │
│ │ │
│ [李四] │
└─────────────────────────────────────┘
步骤三:社区检测与摘要
使用 Leiden 算法进行图分区,然后为每个社区生成摘要:
Leiden 算法优化目标(模块度):
其中:
- \(A_{ij}\) 是邻接矩阵
- \(k_i\) 是节点 \(i\) 的度
- \(m\) 是总边数
- \(\delta(c_i, c_j)\) 在节点 \(i\) 和 \(j\) 同社区时为 1
两种查询模式:
| 模式 | 适用场景 | 实现方式 |
|---|---|---|
| Local Search | 具体实体查询 | 从实体出发遍历子图 |
| Global Search | 全局摘要问题 | 聚合社区摘要 |
2.2.3 Global Search 示例
问题:"这家公司的整体组织架构是怎样的?"
GraphRAG Global Search:
1. 获取顶层社区摘要
2. 聚合得到全局视图
回答:
"公司A是一家科技企业,由CEO张三领导,董事王五参与治理。
公司A已收购公司B,其CTO李四现也是公司A的技术负责人。
整体形成了CEO-董事-CTO的三级管理架构..."
2.3 Self-RAG: 自主检索策略规划
论文: Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection
技术层面: 🔧 LLM 训练(需要微调模型)
2.3.1 传统 RAG 的问题
问题一:盲目检索
问题:"1+1等于几?"
传统RAG:还是会去检索,浪费资源
问题二:无法评估检索质量
问题:"量子计算的原理是什么?"
检索结果:一篇不相关的文档
传统RAG:直接基于这篇文档生成,导致幻觉
2.3.2 Self-RAG 是怎么做的?
Self-RAG 训练模型学会自我反思,使用特殊的反思令牌:
┌────────────────────────────────────────────────────────────┐
│ Self-RAG 反思令牌 │
│ │
│ [Retrieve] ── 是否需要检索? │
│ │ Yes: 需要检索 │
│ │ No: 不需要 │
│ │ Continue: 继续当前生成 │
│ ▼ │
│ [IsRel] ─── 检索内容是否相关? │
│ │ Relevant / Irrelevant │
│ ▼ │
│ [IsSup] ─── 生成内容是否有检索支持? │
│ │ Fully Supported / Partially / No Support │
│ ▼ │
│ [IsUse] ─── 生成内容对用户是否有用? │
│ Useful(1-5分) │
└────────────────────────────────────────────────────────────┘
生成过程的形式化定义:
设输入为 \(x\),输出为 \(y\),检索结果为 \(d\),反思令牌为 \(r\):
训练目标同时优化生成和反思:
训练数据构建:
使用 Critic 模型自动标注:
# Critic 模型标注示例
def annotate_with_critic(query, document, generation):
# 判断是否相关
is_relevant = critic_model(
f"文档与问题是否相关?\n问题:{query}\n文档:{document}"
)
# 判断是否有支持
is_supported = critic_model(
f"回答是否有文档支持?\n回答:{generation}\n文档:{document}"
)
# 判断是否有用
is_useful = critic_model(
f"这个回答对用户有用吗?评分1-5\n问题:{query}\n回答:{generation}"
)
return is_relevant, is_supported, is_useful
2.3.3 推理时的自适应检索
输入:"法国的首都是哪里?"
步骤1: 模型生成 [Retrieve]
输出: No (这是常识问题,不需要检索)
步骤2: 直接生成答案
输出: "法国的首都是巴黎。"
步骤3: 生成 [IsUse]
输出: [5] (非常有用)
---
输入:"2024年诺贝尔化学奖得主的研究方向是什么?"
步骤1: 模型生成 [Retrieve]
输出: Yes (需要实时信息)
步骤2: 执行检索,获取文档
步骤3: 模型生成 [IsRel]
输出: Relevant
步骤4: 生成答案
输出: "2024年诺贝尔化学奖授予了..."
步骤5: 生成 [IsSup]
输出: Fully Supported
步骤6: 生成 [IsUse]
输出: [5]
2.3.4 与其他 RAG 变体对比
| 方法 | 检索策略 | 质量评估 | 训练要求 |
|---|---|---|---|
| Naive RAG | 总是检索 | 无 | 无 |
| FLARE | 低置信度时检索 | 无 | 无 |
| Self-RAG | 自主判断 | 多维度评估 | 需要微调 |
第三部分:记忆系统(Memory)
3.1 Generative Agents: 生成式智能体的社会行为模拟
论文: Generative Agents: Interactive Simulacra of Human Behavior
技术层面: 🎭 Agent 编排 + 📝 提示词工程
3.1.1 过去 NPC/智能体是怎么做的?
有限状态机(FSM)方法:
┌──────┐ 看到敌人 ┌──────┐
│ 巡逻 │ ─────────────────→ │ 攻击 │
└──────┘ └──────┘
↑ │
│ 敌人死亡 │
└───────────────────────────┘
行为树(Behavior Tree)方法:
[根节点]
│
┌─────────────┼─────────────┐
▼ ▼ ▼
[检查血量] [检查敌人] [巡逻]
│ │
低于20%? 存在敌人?
│ │
[逃跑] [攻击]
局限性:
- 行为预设、不灵活
- 无法记忆过去事件
- 无法进行社交对话
- 无法展现个性
3.1.2 Generative Agents 是怎么做的?
设计了三大核心机制:观察、反思、规划
┌─────────────────────────────────────────────────────────────┐
│ Generative Agent 认知架构 │
│ │
│ ┌────────────┐ │
│ │ 感知环境 │ ──→ 观察事件 ──→ ┌──────────────┐ │
│ └────────────┘ │ │ │
│ │ 记忆流 │ │
│ ┌────────────┐ │ (Memory │ │
│ │ 规划系统 │ ←──────────────→│ Stream) │ │
│ └────────────┘ │ │ │
│ │ └──────────────┘ │
│ │ ↓ │
│ ▼ ┌──────────────┐ │
│ ┌────────────┐ │ 反思系统 │ │
│ │ 执行行动 │ │ (高层洞察) │ │
│ └────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
(1)记忆流(Memory Stream)
每条记忆包含三个属性:
class Memory:
description: str # "Klaus 正在画画"
timestamp: datetime # 2024-01-15 14:30
importance: float # 重要性评分 (0-10)
重要性评分由 LLM 判断:
IMPORTANCE_PROMPT = """
在1-10的范围内,评估以下事件对于当事人的重要性:
1 = 完全普通(如:早上刷牙)
10 = 极其重要(如:结婚、亲人去世)
事件:{event}
重要性评分:
"""
(2)记忆检索
当智能体需要回忆时,使用综合评分检索相关记忆:
其中:
- 时效性(Recency):指数衰减
-
重要性(Importance):LLM 评分,归一化到 [0, 1]
-
相关性(Relevance):查询与记忆的余弦相似度
(3)反思机制
定期从记忆中提取高层次洞察:
REFLECTION_PROMPT = """
以下是 {agent_name} 最近的一些记忆:
{memories}
基于以上记忆,你能得出关于 {agent_name} 的什么高层次结论?
请列出3个洞察,每个洞察一句话。
"""
反思示例:
记忆片段:
- "Klaus 早上 8 点开始画画"
- "Klaus 买了新的画笔和颜料"
- "Klaus 跳过午饭继续画画"
- "Klaus 在画廊待了 3 小时"
反思结果:
1. Klaus 对艺术充满热情,画画是他最重要的事
2. Klaus 愿意为艺术投入时间和金钱
3. Klaus 可能是专业画家或有志成为画家
(4)规划系统
自顶向下分解计划:
日计划 → 小时计划 → 分钟级行动
示例:
日计划:今天要完成油画作品
└→ 09:00-12:00: 在工作室画画
└→ 09:00: 准备画具
└→ 09:30: 绘制轮廓
└→ 10:00: 上色
└→ 11:30: 细节修饰
└→ 12:00-13:00: 午餐
└→ 13:00-17:00: 继续创作
3.1.3 涌现的社会行为
在 Smallville 实验中(25 个智能体),观察到:
| 涌现行为 | 说明 |
|---|---|
| 信息传播 | 一个智能体告诉另一个的消息会口口相传 |
| 关系形成 | 智能体记住过去的对话,形成友谊 |
| 协调活动 | 多个智能体自发组织派对 |
| 冲突记忆 | 智能体记住谁曾经伤害过自己 |
3.2 MemGPT: 面向无限上下文的记忆管理
论文: MemGPT: Towards LLMs as Operating Systems
技术层面: 🎭 Agent 编排 + 📝 提示词工程
3.2.1 LLM 上下文窗口的问题
问题:固定长度的上下文窗口
GPT-4: 128K tokens
Claude: 200K tokens
Llama 2: 4K tokens
问题1:超长对话会被截断
问题2:重要信息可能被挤出窗口
问题3:无法持久化跨会话记忆
传统解决方案的局限:
| 方案 | 问题 |
|---|---|
| 截断旧消息 | 丢失重要历史信息 |
| 摘要压缩 | 细节丢失,无法还原 |
| 增大窗口 | 计算成本二次增长 \(O(n^2)\) |
3.2.2 MemGPT 是怎么做的?
核心思想:借鉴操作系统的虚拟内存
操作系统虚拟内存 MemGPT 记忆系统
───────────────── ────────────────
CPU 寄存器 (极快) 核心记忆 (始终在 Prompt)
↕ ↕
主内存 RAM (快) 主上下文 (当前对话)
↕ ↕
硬盘 SSD (慢) 外部存储 (数据库)
分层记忆架构:
┌─────────────────────────────────────────────────────────────┐
│ 主上下文窗口 │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ System Prompt: 你是一个有记忆的AI助手... │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 核心记忆 (Core Memory): │ │
│ │ - 用户偏好: {"name": "小明", "likes": "编程"} │ │
│ │ - AI人设: {"personality": "友好", "style": "专业"} │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 消息队列 (FIFO Buffer): │ │
│ │ - [最近的10条对话消息] │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↑ │
│ │ 函数调用 │
│ ↓ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 外部存储: │ │
│ │ - 对话历史数据库 (全部历史) │ │
│ │ - 归档记忆数据库 (长期知识) │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
核心函数定义:
# 核心记忆操作
def core_memory_append(key: str, value: str) -> str:
"""向核心记忆追加信息"""
memory[key] += value
return f"已添加到 {key}"
def core_memory_replace(key: str, old_value: str, new_value: str) -> str:
"""更新核心记忆中的信息"""
memory[key] = memory[key].replace(old_value, new_value)
return f"已更新 {key}"
# 对话历史搜索
def conversation_search(query: str, limit: int = 10) -> List[str]:
"""搜索历史对话"""
results = vector_db.search(query, limit=limit)
return [r.content for r in results]
# 归档记忆操作
def archival_memory_insert(content: str) -> str:
"""将信息存入归档记忆"""
archive_db.insert(content)
return "已归档"
def archival_memory_search(query: str, limit: int = 10) -> List[str]:
"""搜索归档记忆"""
results = archive_db.search(query, limit=limit)
return [r.content for r in results]
自主记忆管理的 Prompt:
MEMGPT_SYSTEM_PROMPT = """
你是一个有持久记忆的AI助手。你的记忆分为几个部分:
1. 核心记忆 (Core Memory): 始终可见,存储关键信息
当前内容:{core_memory}
2. 对话历史 (Recall Storage): 所有历史对话
使用 conversation_search(query) 搜索
3. 归档记忆 (Archival Storage): 长期知识存储
使用 archival_memory_search(query) 搜索
使用 archival_memory_insert(content) 存储
你可以随时调用这些函数来管理记忆。当:
- 用户提到重要信息 → 使用 core_memory_append 保存
- 需要回忆过去对话 → 使用 conversation_search
- 需要长期存储知识 → 使用 archival_memory_insert
"""
3.2.3 实际对话示例
用户:我叫小明,最喜欢Python编程
MemGPT 内部思考:
<function_call>
core_memory_append(
key="user_info",
value="用户名:小明;爱好:Python编程"
)
</function_call>
MemGPT:很高兴认识你,小明!Python是一门很棒的语言。
--- 三天后 ---
用户:你还记得我喜欢什么吗?
MemGPT 内部:
(查看核心记忆,发现 user_info 包含信息)
MemGPT:当然记得,小明!你喜欢Python编程。上次我们聊过这个。
--- 用户问了一个复杂问题 ---
用户:我之前问过你一个关于异步编程的问题,具体是什么来着?
MemGPT 内部:
<function_call>
conversation_search("异步编程")
</function_call>
返回:["2024-01-10: 用户问如何用asyncio处理并发请求..."]
MemGPT:找到了!在1月10日,你问过如何用asyncio处理并发请求...
3.2.4 与传统方法的数学对比
传统 LLM 的上下文限制:
其中 \(L_{\text{model}}\) 是模型最大上下文长度。
当 \(L_{\text{history}} > L_{\text{model}}\) 时,必须截断。
MemGPT 的虚拟上下文:
其中:
- \(L_{\text{core}}\) 是核心记忆大小(固定)
- \(f(\cdot)\) 是按需检索函数
复杂度分析:
| 方法 | 空间复杂度 | 检索复杂度 |
|---|---|---|
| 全量上下文 | \(O(n)\) | \(O(1)\) |
| 截断 | \(O(k)\), \(k\) 固定 | \(O(1)\) |
| MemGPT | \(O(k)\) 主存 + \(O(n)\) 外存 | \(O(\log n)\) 向量检索 |
总结:技术层面分类汇总
按技术作用层面分类
| 论文 | 🔧 训练 | ⚡ 推理 | 🎭 编排 | 📝 提示词 |
|---|---|---|---|---|
| ReAct | ✓ | ✓ | ||
| Reflexion | ✓ | ✓ | ||
| Tree of Thoughts | ✓ | ✓ | ||
| AutoGen | ✓ | |||
| DPR | ✓ | |||
| GraphRAG | ✓ | ✓ | ||
| Self-RAG | ✓ | |||
| Generative Agents | ✓ | ✓ | ||
| MemGPT | ✓ | ✓ |
关键洞察
-
无需训练的创新:ReAct、Reflexion、ToT、AutoGen、Generative Agents、MemGPT 都不需要修改 LLM 权重,仅通过提示词和编排即可实现强大功能。
-
需要训练的创新:DPR(训练编码器)、Self-RAG(微调生成模型)需要训练,但获得了更强的能力。
-
融合趋势:现代系统往往融合多种技术,例如:
- Agent(ReAct) + RAG(DPR) + Memory(MemGPT)
技术演进路线图
2020 ┌─────────────────┐
│ DPR │ ← 密集检索基础
└────────┬────────┘
│
2022 ┌────────▼────────┐
│ ReAct │ ← 推理+行动范式
└────────┬────────┘
│
2023 ┌────────▼────────┬───────────────┬───────────────┐
│ Reflexion │ ToT │ AutoGen │
│ (自我反思) │ (搜索+推理) │ (多智能体) │
└────────┬────────┴───────────────┴───────────────┘
│
┌────────▼────────┬───────────────┐
│ Generative │ MemGPT │
│ Agents (记忆) │ (分层记忆) │
└────────┬────────┴───────────────┘
│
┌────────▼────────┬───────────────┐
│ GraphRAG │ Self-RAG │
│ (图谱+检索) │ (自主检索) │
└─────────────────┴───────────────┘
│
2024 ▼
┌─────────────────────────────────┐
│ 融合系统:Agent + RAG + Memory │
└─────────────────────────────────┘
参考文献
- Yao, S., et al. (2022). ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629
- Shinn, N., et al. (2023). Reflexion: Language Agents with Verbal Reinforcement Learning. arXiv:2303.11366
- Yao, S., et al. (2023). Tree of Thoughts: Deliberate Problem Solving with Large Language Models. arXiv:2305.10601
- Wu, Q., et al. (2023). AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation. arXiv:2308.08155
- Karpukhin, V., et al. (2020). Dense Passage Retrieval for Open-Domain Question Answering. arXiv:2004.04906
- Microsoft Research. (2024). GraphRAG: A modular graph-based Retrieval-Augmented Generation system.
- Asai, A., et al. (2023). Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection. arXiv:2310.11511
- Park, J.S., et al. (2023). Generative Agents: Interactive Simulacra of Human Behavior. arXiv:2304.03442
- Packer, C., et al. (2023). MemGPT: Towards LLMs as Operating Systems. arXiv:2310.08560

浙公网安备 33010602011771号