06 - 智能体轨道 - 自主决策 🤖

欢迎来到智能体(Agent)的世界!这条轨道探索如何让AI自主规划、决策和行动。

核心问题: 如何在不确定环境中做出最优决策?


🎯 本轨道内容

算法核心问题解决方案应用难度
Bandit探索vs利用ε-greedy, UCB推荐系统、A/B测试⭐⭐
Minimax零和对抗极小化极大搜索棋类游戏⭐⭐⭐
MCTS大搜索空间蒙特卡洛树搜索AlphaGo、游戏AI⭐⭐⭐⭐
ReAct推理+行动Thought→Action→ObservationLLM Agent⭐⭐⭐⭐
Memory Network长期记忆可寻址外部记忆问答、对话⭐⭐⭐

学习路径: Bandit(基础) → Minimax(完美信息) → MCTS(部分信息) → ReAct(语言Agent)


🎰 算法一: Multi-Armed Bandit - 探索与利用

什么是Bandit?

问题: K个老虎机(arm),每个有未知奖励分布,玩N次,如何最大化总奖励?

核心矛盾:

  • Exploration (探索): 尝试不确定的arm,收集信息
  • Exploitation (利用): 选择已知最好的arm,获取奖励

经典策略:

  1. ε-Greedy:
以ε概率: 随机探索
以1-ε概率: 选择当前最优arm
  1. UCB (Upper Confidence Bound):
选择: argmax_a [ Q(a) + c·√(ln(t)/N(a)) ]

Q(a): arm a的平均奖励
N(a): arm a被选次数
t: 总尝试次数
c: 探索系数

对话1: Bandit问题在LLM中的应用是什么?

🤔 学生: Bandit看起来是强化学习,和LLM有什么关系?

💡 导师: LLM的很多场景本质是Bandit问题!

应用1: Prompt选择

问题: 有5个不同prompt模板,哪个效果最好?

Bandit建模:
  - K=5个arm(5个prompt)
  - 奖励: 用户满意度评分
  - 目标: 快速找到最优prompt,同时不浪费太多尝试
  
UCB策略:
  初期: 每个prompt都试几次(探索)
  后期: 集中在高分prompt(利用)

应用2: 模型路由

问题: 多个LLM(GPT-4, Claude, Llama),每个成本和质量不同

Bandit建模:
  - arm: 选择哪个模型
  - 奖励: 质量/成本比
  - 约束: 成本预算
  
Contextual Bandit:
  根据查询类型(简单/复杂)动态选模型
  简单查询 → Llama(便宜)
  复杂查询 → GPT-4(贵但准)

应用3: RLHF中的探索

PPO训练时:
  - 生成多条回答候选(探索)
  - 奖励模型打分
  - 平衡"高奖励回答"vs"新尝试"
  
没有足够探索 → mode collapse
过度探索 → 训练慢

✨ 关键洞察: Bandit是”在线学习”的基础——边用边学,快速适应,是LLM部署中的核心技术。


代码片段: UCB算法

import numpy as np
 
class UCB:
    def __init__(self, n_arms, c=2.0):
        """
        Upper Confidence Bound算法
        
        Args:
            n_arms: arm数量
            c: 探索系数(通常取1-2)
        """
        self.n_arms = n_arms
        self.c = c
        
        self.q_values = np.zeros(n_arms)  # 平均奖励估计
        self.n_pulls = np.zeros(n_arms)  # 每个arm被拉次数
        self.t = 0  # 总步数
    
    def select_arm(self):
        """选择arm"""
        # 先确保每个arm至少被拉一次
        if self.t < self.n_arms:
            return self.t
        
        # UCB: Q(a) + c·√(ln(t)/N(a))
        ucb_values = self.q_values + self.c * np.sqrt(
            np.log(self.t) / (self.n_pulls + 1e-10)
        )
        return np.argmax(ucb_values)
    
    def update(self, arm, reward):
        """更新估计"""
        self.n_pulls[arm] += 1
        self.t += 1
        
        # 增量均值更新: Q_new = Q_old + (r - Q_old)/N
        self.q_values[arm] += (reward - self.q_values[arm]) / self.n_pulls[arm]
    
    def regret(self, optimal_reward):
        """累积遗憾: 最优策略 - 实际奖励"""
        return optimal_reward * self.t - np.sum(self.q_values * self.n_pulls)
 
# 使用示例
bandit = UCB(n_arms=5, c=2.0)
 
for round in range(1000):
    arm = bandit.select_arm()
    reward = pull_arm(arm)  # 与环境交互
    bandit.update(arm, reward)
    
    if round % 100 == 0:
        print(f"Round {round}: Best arm estimate = {np.argmax(bandit.q_values)}")

关键点:

  1. 不确定性奖励: √(ln(t)/N(a)) 大 → 该arm不确定性高 → 优先探索
  2. 自动平衡: 拉得多的arm,不确定性降低,探索减少
  3. 理论保证: Regret = O(√(t·ln(t))) (次线性增长)

♟️ 算法二: Minimax - 极小化极大搜索

什么是Minimax?

场景: 两人零和博弈(我得1分=你失1分),完全信息(棋盘可见)

核心思想:

  • Max玩家: 最大化自己得分
  • Min玩家: 最小化Max得分(即最大化自己)

递归定义:

Minimax(node, depth, isMax):
  if 终局 or depth=0:
    return 评估(node)
  
  if isMax:
    best = -∞
    for child in children(node):
      val = Minimax(child, depth-1, False)
      best = max(best, val)
    return best
  else:
    best = +∞
    for child in children(node):
      val = Minimax(child, depth-1, True)
      best = min(best, val)
    return best

Alpha-Beta剪枝: 避免搜索明显劣势分支,加速10-100倍


对话2: Minimax能用于LLM Agent吗?

🤔 学生: Minimax是游戏AI,LLM Agent怎么用?

💡 导师: 可以,但需要重新定义”对手”和”得分”!

应用1: 辩论Agent

场景: 两个LLM辩论
  Agent A(正方): 支持观点X
  Agent B(反方): 反对观点X
  
Minimax建模:
  - Max: Agent A,最大化"说服力评分"
  - Min: Agent B,最小化A的说服力(即攻击A论点)
  
搜索树:
  A: "X是正确的,因为理由1" (得分+3)
    B: "理由1有漏洞Y" (得分-2)
      A: "漏洞Y可以这样解释" (得分+1)
      ...

应用2: 红队测试

场景: 测试LLM安全性
  Attacker(Max): 尝试让LLM输出有害内容
  Defender(Min): LLM拒绝并给安全回答
  
Minimax搜索:
  Attacker思考: "什么prompt最可能绕过防护?"
  Defender思考: "如何识别和拒绝恶意prompt?"

局限性:

  1. 搜索空间爆炸: 语言空间远大于棋盘
  2. 评估函数难定义: 什么是”好回答”? (主观)
  3. 非零和: 辩论可能双赢/双输

改进:

  • MCTS (下一个算法): 用采样替代完全搜索
  • Self-Play: AlphaGo式自我对弈训练评估函数

✨ 关键洞察: Minimax的”对抗思维”适用于任何竞争场景,但语言Agent需要更灵活的搜索策略(如MCTS)。


🌳 算法三: MCTS - 蒙特卡洛树搜索

什么是MCTS?

问题: Minimax在围棋(分支因子250+)等大空间游戏中不可行

MCTS方案: 不完全搜索,用采样估计价值

四步循环:

1. Selection(选择): 从根沿UCB最优路径下降
2. Expansion(扩展): 遇到未访问节点,扩展一个子节点
3. Simulation(模拟): 从新节点随机rollout到终局
4. Backpropagation(回传): 更新路径上所有节点的统计

UCT公式 (MCTS的Selection):

UCT(node) = win_rate(node) + c·√(ln(N_parent)/N_node)

win_rate: 该节点胜率
N_parent: 父节点访问次数
N_node: 该节点访问次数

对话3: MCTS如何用于LLM规划?

🤔 学生: MCTS是围棋AI,和LLM Agent有什么联系?

💡 导师: MCTS的本质是”规划搜索”,LLM Agent也需要规划!

应用: LLM + MCTS做多步推理

例子: 数学题求解

问题: "小明有5个苹果,给了2个,买了3个,现在有几个?"

传统LLM:
  直接生成答案 "6个" (可能错!)

LLM + MCTS:
  1. Selection: 选择最有希望的推理路径
     "先算给了后剩多少"
  
  2. Expansion: LLM生成下一步
     "5 - 2 = 3"
  
  3. Simulation: LLM继续推理到底
     "3 + 3 = 6,答案是6"
  
  4. Backpropagation: 检查答案
     正确 → 这条路径score+1
     错误 → score-1
  
  重复1000次 → 选择访问次数最多的路径

Tree-of-Thoughts (ToT):

论文: Yao et al., 2023
核心: 用MCTS搜索思维链(Chain-of-Thought)

相比标准CoT:
  CoT: 单条线性推理链
  ToT: 树状探索多条推理路径,选最优

效果: GSM8K数学题准确率 74% → 85%

代码结构:

class LLM_MCTS:
    def search(self, problem, num_simulations=100):
        root = Node(problem)
        
        for _ in range(num_simulations):
            # 1. Selection
            node = self.select(root)
            
            # 2. Expansion
            if not node.is_terminal():
                child = self.expand(node)  # LLM生成下一步
                node = child
            
            # 3. Simulation
            reward = self.rollout(node)  # LLM推理到底,评估
            
            # 4. Backpropagation
            self.backpropagate(node, reward)
        
        # 选择最优路径
        return self.best_path(root)
    
    def expand(self, node):
        """用LLM生成子节点"""
        prompt = f"继续推理:\n{node.state}\n下一步:"
        next_step = llm.generate(prompt)
        return Node(node.state + "\n" + next_step)
    
    def rollout(self, node):
        """模拟到终局并评估"""
        final_answer = llm.generate(node.state + "\n最终答案:")
        return evaluate(final_answer)  # 检查正确性

优势:

  1. 探索多样性: 不只走一条路
  2. 自我纠错: 错误路径自然被淘汰
  3. 置信度: 访问次数高 = 多次验证 = 可靠

挑战:

  • LLM调用成本高(每次Simulation要多次生成)
  • 需要可靠的reward signal(如何评估中间步骤?)

✨ 关键洞察: MCTS把LLM从”单次生成”变成”搜索+验证”,大幅提升复杂推理任务的准确性。


💬 算法四: ReAct - 推理与行动

什么是ReAct?

ReAct (Yao et al., 2022) = Reasoning + Acting

核心思想: LLM不只生成答案,还能调用工具、观察结果,迭代推理

循环结构:

Thought (推理): "我需要查询最新GDP数据"
Action (行动): search("2024年美国GDP")
Observation (观察): "搜索结果: 28.78万亿美元"
Thought: "现在对比中国的..."
Action: search("2024年中国GDP")
Observation: "18.57万亿美元"
Thought: "美国GDP是中国的1.55倍"
Answer: "2024年美国GDP约为中国的1.55倍"

对话4: ReAct vs 标准Prompt有什么区别?

🤔 学生: 这不就是让LLM调用API吗?ReAct特别在哪?

💡 导师: ReAct的突破是”显式推理过程” — 不只调API,还解释为什么调。

标准Function Calling:

User: "查查今天天气"
LLM: [调用weather_api("北京")]
API: "晴,25度"
LLM: "今天北京晴天,25度"

问题: LLM像”转发器”,不理解为什么查天气

ReAct:

User: "我明天要去北京,需要带伞吗?"

Thought: "需要知道明天北京的天气,特别是会不会下雨"
Action: weather_api("北京", date="明天")
Observation: "明天北京:多云转阴,降水概率70%,温度18-24度"

Thought: "降水概率70%很高,有可能下雨,应该带伞"
Action: None (无需更多查询)
Answer: "建议带伞,明天北京降水概率70%"

区别:

维度Function CallingReAct
推理过程隐式(模型内部)显式(可见Thought)
工具选择规则/分类器推理驱动
多步规划困难自然(迭代Thought)
可解释性低高(每步有理由)

ReAct的威力: 多步工具组合

User: "帮我预订明天去上海的航班"

Thought 1: "需要先查明天的航班"
Action 1: search_flights(from="北京", to="上海", date="明天")
Observation 1: "MU5180, 08:00, ¥850 | CA1234, 14:00, ¥720"

Thought 2: "用户没说时间偏好,应该问一下"
Action 2: ask_user("您偏好早上还是下午的航班?")
Observation 2: "早上"

Thought 3: "选MU5180,08:00的航班,现在预订"
Action 3: book_flight("MU5180")
Observation 3: "预订成功,订单号ABC123"

Answer: "已为您预订明天08:00的MU5180航班,订单号ABC123"

没有ReAct: 可能直接预订第一个,或全部列出让用户选(差体验)

✨ 关键洞察: ReAct让LLM从”Pattern matching”升级为”Agent” — 能规划、执行、观察、调整,接近人类解决问题的方式。


对话5: ReAct的Prompt怎么设计?

🤔 学生: ReAct的Thought/Action/Observation是训练出来的吗?

💡 导师: 大多数是Prompt工程,不需要专门训练!

ReAct Prompt模板:

你是一个助手,可以使用以下工具:
- search(query): 搜索互联网
- calculator(expr): 计算数学表达式
- ask_user(question): 向用户提问

严格按照以下格式回答:
Thought: [你的推理过程]
Action: [工具名(参数)]

收到Observation后继续:
Thought: [基于observation的推理]
Action: [下一步工具] 或 Answer: [最终答案]

示例:
User: 100的平方根是多少?
Thought: 需要计算100的平方根
Action: calculator("sqrt(100)")
Observation: 10.0
Thought: 计算结果是10
Answer: 100的平方根是10

现在开始:
User: {user_query}

关键技巧:

  1. Few-shot Examples: 给2-3个示例教会格式
  2. Strict Format: 用正则或parser解析Action
  3. Max Iterations: 限制最多10步,防止死循环

伪代码:

def react_loop(user_query, max_iter=10):
    context = REACT_PROMPT + f"\nUser: {user_query}\n"
    
    for i in range(max_iter):
        # LLM生成Thought + Action
        response = llm.generate(context)
        
        # 解析Action
        action = parse_action(response)  # 正则匹配"Action: tool(args)"
        
        if action.is_final_answer():
            return action.answer
        
        # 执行工具
        observation = execute_tool(action.tool, action.args)
        
        # 添加到context
        context += response + f"\nObservation: {observation}\n"
    
    return "抱歉,未能在限定步数内完成"
 
def parse_action(text):
    """从LLM输出中提取Action"""
    # 正则: Action: tool_name(arg1, arg2, ...)
    match = re.search(r'Action:\s*(\w+)\((.*)\)', text)
    if match:
        return Action(tool=match[1], args=match[2])
    
    # 检查是否Final Answer
    if 'Answer:' in text:
        return Action(is_final=True, answer=text.split('Answer:')[1].strip())
    
    raise ParseError("未找到有效Action")

常见问题:

  1. 格式不稳定: LLM不严格遵守Thought/Action格式
    • 解决: 用强约束Prompt + 后处理parser
  2. 死循环: 一直调用同一工具
    • 解决: 记录历史,prompt提示”不要重复”
  3. 幻觉Action: 调用不存在的工具
    • 解决: 严格校验工具名,拒绝无效Action

✨ 关键洞察: ReAct证明了”结构化Prompt”的威力 — 不需要训练,只需好的格式设计,就能让LLM表现出Agent行为。


🧠 算法五: Memory Network - 外部记忆

什么是Memory Network?

问题: Transformer的上下文长度有限(如4k tokens),如何处理长对话/大知识库?

Memory Network (Weston et al., 2014): 给模型配备可读写的外部记忆

结构:

查询q → Memory [m1, m2, ..., mN] → 相关记忆 → 回答a

四步:

  1. Input: 把新信息存入Memory
  2. Generalization: 编码query和memory为向量
  3. Output: 从memory检索相关片段(Attention!)
  4. Response: 基于检索结果生成答案

对话6: Memory Network vs RAG有什么区别?

🤔 学生: 这不就是RAG(检索增强生成)吗?

💡 导师: 非常接近,但Memory Network更通用!

RAG (Retrieval-Augmented Generation):

用户query → 向量数据库检索 → Top-K文档 → 拼接prompt → LLM生成

特点:
  - 只读memory(文档库)
  - 检索=语义相似度搜索
  - 用于知识密集型QA

Memory Network:

用户query → Memory(可读写!) → 检索+推理 → 更新Memory → 回答

特点:
  - 可读可写(动态memory)
  - 多跳推理(查询A→得B→再查询B)
  - 用于对话、任务规划

对比:

维度RAGMemory Network
Memory类型静态文档动态,可更新
检索方式向量相似度Attention权重
推理能力单跳多跳
写入离线索引在线写入

Memory Network的”多跳推理”例子:

Memory:
  m1: "Tom住在纽约"
  m2: "纽约在美国东海岸"
  m3: "美国东海岸时区是EST"

Query: "Tom所在时区是什么?"

第1跳: query → m1 → "Tom住在纽约"
第2跳: "纽约" → m2 → "纽约在美国东海岸"
第3跳: "东海岸" → m3 → "东海岸时区是EST"
Answer: "EST"

RAG单跳: query → m1 → “Tom住在纽约” (缺信息!)

现代融合: LangChain的ConversationBufferMemory

from langchain.memory import ConversationBufferMemory
 
memory = ConversationBufferMemory()
 
# 写入
memory.save_context(
    {"input": "我叫Alice"},
    {"output": "你好Alice!"}
)
 
# 读取
history = memory.load_memory_variables({})
# {'history': "Human: 我叫Alice\nAI: 你好Alice!"}
 
# 下次对话自动携带history
chain = ConversationChain(llm=llm, memory=memory)
chain.run("我叫什么名字?")  # "你叫Alice"

✨ 关键洞察: Memory Network是”可学习的数据库” — RAG是其特例(只读检索)。未来Agent需要动态记忆:记住对话、更新信念、规划任务。


代码片段: 简化Memory Network

class MemoryNetwork:
    def __init__(self, embedding_model):
        self.memories = []  # 存储 (text, embedding)
        self.embed = embedding_model
    
    def store(self, text):
        """存入新记忆"""
        emb = self.embed(text)
        self.memories.append((text, emb))
    
    def retrieve(self, query, k=3):
        """检索top-k相关记忆"""
        q_emb = self.embed(query)
        
        # 计算相似度
        scores = [
            cosine_sim(q_emb, m_emb)
            for text, m_emb in self.memories
        ]
        
        # Top-K
        top_k_idx = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:k]
        return [self.memories[i][0] for i in top_k_idx]
    
    def answer(self, query):
        """基于记忆回答"""
        relevant = self.retrieve(query, k=3)
        context = "\n".join(relevant)
        
        prompt = f"记忆:\n{context}\n\n问题:{query}\n答案:"
        return llm.generate(prompt)
 
# 使用
memory = MemoryNetwork(embedding_model)
 
# 存储知识
memory.store("法国的首都是巴黎")
memory.store("巴黎的著名景点是埃菲尔铁塔")
memory.store("埃菲尔铁塔高324米")
 
# 多跳查询
answer = memory.answer("法国首都的著名景点有多高?")
# 内部检索到3条记忆,组合推理
# "法国首都是巴黎,巴黎著名景点是埃菲尔铁塔,高324米"

改进方向:

  1. 选择性写入: 不是所有对话都存,过滤重要信息
  2. 遗忘机制: 旧记忆衰减,防止memory爆炸
  3. 分层记忆: 短期(对话)/长期(知识)/工作(任务)记忆分离

📊 五大Agent算法对比

算法场景决策方式信息难度
Bandit推荐、A/B测试探索vs利用部分(每次拉arm)⭐⭐
Minimax棋类、对抗完全搜索完全(棋盘)⭐⭐⭐
MCTS复杂游戏、规划采样搜索完全(可模拟)⭐⭐⭐⭐
ReActLLM Agent推理+工具调用部分(API返回)⭐⭐⭐⭐
Memory长对话、QA检索+推理外部知识库⭐⭐⭐

综合应用: 复杂Agent系统

用户query
  ↓
Memory: 检索历史对话
  ↓
ReAct: 规划工具调用
  ↓
MCTS: 搜索最优行动序列
  ↓
Bandit: 选择模型/策略
  ↓
执行 & 更新Memory

🎯 本章总结

智能体的核心能力:感知→推理→行动→学习

演化路径:

规则Agent (if-then)
  ↓
搜索Agent (Minimax, MCTS)
  ↓
学习Agent (RL, Bandit)
  ↓
语言Agent (ReAct, Memory)
  ↓
多Agent协作 (未来)

LLM Agent的关键:

  1. 规划: MCTS式多步搜索
  2. 工具使用: ReAct式推理+行动
  3. 记忆管理: Memory Network长期知识
  4. 自我改进: 从失败中学习(Bandit探索)

挑战:

  • 长期规划(超过10步)
  • 环境复杂性(真实世界不确定)
  • 安全性(Agent自主行动的风险)

本轨道结束! 你已完成所有6条轨道,掌握了从基础到前沿的47个核心算法!

下一步:

🎉 恭喜完成no-magic交互式教程!