简介:由快手科技快意大模型知识增强研发负责人毛航宇主讲的专题资料,系统梳理了强化学习(多)智能体到大语言模型(多)智能体十年的研究脉络。内容覆盖Deep RL、Deep MARL、Transformer-based RL、LLM-based AI Agent等方向,从DQN、PPO、MADDPG到GPT系列、AutoGPT等代表工作,并通过时间线图表清晰呈现技术演进关系与关键节点。资源包含十年研究脉络梳理、代表工作选讲、企业实践心得体会三个模块,既适合AI算法工程师与强化学习研究者纵览领域全貌,也适合大模型应用开发者把握智能体技术趋势。压缩包为1个PDF文件,大小约9.81MB,内容紧凑,可直接阅读。目前已有30人学习下载,读者可获得从强化学习到大语言模型智能体的路线图、关键模型对比及由演讲者多年经验凝练的业务落地心得,是一份浓缩的领域参考。
1. 把强化学习多智能体和大语言模型多智能体放在一起比,比的是什么
当我在做一个仓储调度的多设备演示时,团队里同时出现了两条技术路线:一边在调奖励函数让设备学避让,另一边在给语言模型写角色提示让它负责任务排序。A同学问了一句:这两个都叫智能体,到底做哪个?这个问题在今天做智能体选型时很典型。强化学习(多)智能体,重点在动作策略,靠环境和奖励信号不断试错;大语言模型(多)智能体,重点在语言理解和工具调用,靠预训练知识和上下文推理直接给出下一步。这两条路线不是替代关系,而是处理不同层级的问题:前者解决"这个动作该怎么做",后者解决"这件事该由谁做、按什么顺序做"。这篇笔记会把它们的差异、最小实现和结合方式讲清楚,也会给出我在实际项目里踩过的坑,适合正在做智能体选型,或者准备从零搭一个智能体 demo 的开发者。
2. 两种智能体的本质差异:一个在学动作,一个在学下一步怎么决策
2.1 RL智能体的最小闭环:状态、动作、奖励,以及它的两个致命短板
强化学习智能体的基础是马尔可夫决策过程:智能体在每个状态选择一个动作,环境返回下一个状态和一个奖励,目标是把累积折扣奖励最大化。它学的不是"怎么说话",而是"怎么做能在长期获益"。只要环境能反复交互、奖励信号足够明确,RL 就擅长逼近最优策略。工程上最常见的形态是查表型 Q-learning、值函数近似和策略梯度三类。
但 RL 有两个绕不开的短板。第一个是奖励设计决定一切。稀疏奖励下从零探索几乎不收敛,奖励给得不小心又会让智能体学到钻空子的行为。我在一个避让演示里遇到过:为了让设备快点到目标点,我把回合时长写进了惩罚项,结果它学会了卡在边界等对方先走,因为等待不触发时间惩罚,而我的惩罚只按回合结束时的步数计算,这个边界条件让惩罚失效了。第二个是环境必须可交互。真实产线不能拿来做几万次试错,所以 RL 项目基本都要先搭仿真环境,仿真和真实之间的差距又得单独处理。
选型判断就变得很具体:任务能不能低成本交互?状态和动作空间是不是明确可枚举?工程上能不能在两周内构建一个闭环环境?如果答案都是否,直接上手 RL 大概率是给自己挖坑。这里没有玄学,环境交互次数、奖励可设计性、状态观测质量,这三个问题想不清楚,后面训练曲线就会一直震荡。
2.2 LLM智能体的最小闭环:上下文、工具、反馈循环,以及它为什么慢
大语言模型智能体没有显式奖励函数。它的闭环是:系统提示加历史消息作为上下文,模型输出一段推理和一个动作;动作通常是文本指令、工具调用或查询结果;工具返回结果后又作为新消息放回上下文,进入下一轮。它靠"每轮把新信息拼进上下文"来即时适应,而不是靠梯度更新来改变策略。
这个结构的优势很明显:任务开放、语言理解强、有常识性规划能力,而且不需要搭仿真环境。比如让智能体去查文档、写 SQL、总结异常原因,这些任务的状态语义很复杂,但动作空间是有限可枚举的 API 调用,用 LLM 来做效率远高于为每个任务单独训练一个 RL 策略。
短板同样明显。单次推理慢且贵,做不了百万次试错;上下文窗口有限,长回合会丢掉早期信息;模型幻觉和输出格式错误会造成工具调用死循环;最重要的一点是它没有策略优化,你给它一个负反馈,它不会自动调整参数,只能靠下一次推理时"看到"这个反馈来临时改主意。选择逻辑也因此不同:如果你的动作空间没法清晰枚举,又不方便搭环境做大规模交互,LLM 智能体通常比 RL 更务实。
2.3 多智能体放大的难点:协作结构还是通信协议
把"多智能体"三个字加进来,两类技术的问题完全不同。
RL 多智能体的核心难点是三个:联合状态空间和联合动作空间随智能体数量指数膨胀;环境不再平稳,你的策略在变,队友和对手的策略也在变,对每个智能体来说环境的转移概率一直在漂;还有信用分配,一个全局奖励下来,到底是谁的功劳、谁该背锅。工程上最常用的方案是先做独立学习,每个智能体各用自己的策略,观察震荡和推锅后再升级到中心化训练、去中心化执行。这个升级思路在机器人集群和调度场景里是标准解法,训练时用全局信息,执行时只看局部观测。
LLM 多智能体的问题不在策略学习,而在协作文本协议:谁当协调者、谁当执行者;消息格式不统一导致的解析失败;多个智能体意见冲突时靠什么收敛。我在模拟项目里见过一次典型翻车:三个 LLM 智能体在一个群聊式结构里互相追问,八轮之后任务还没开始执行,因为没有任何一个角色有最终决定权。本质区别一句话就能讲清:RL 多智能体是真正的博弈与策略学习,LLM 多智能体更接近角色扮演和文本协商,模型权重不动,动的是"谁拿到了什么上下文"。
| 维度 | RL多智能体 | LLM多智能体 |
|---|---|---|
| 学习方式 | 在线策略学习,更新参数或查表值 | 上下文中的即时推理,不更新权重 |
| 知识来源 | 环境交互产生的奖励信号 | 预训练知识加工具返回结果 |
| 训练成本 | 高,需要仿真环境大规模试错 | 高,主要是 token 成本,不需要仿真 |
| 多体核心难点 | 联合状态维度、信用分配、环境非平稳 | 角色划分、消息协议、冲突裁决 |
| 适用场景 | 设备集群、调度博弈、需要真实动作决策 | 开放任务、内容生成、多角色协作规划 |
3. 先把 RL 这条线跑通:从单智能体到多智能体的最小示例
3.1 单智能体热身:离散环境优先用 Q-learning,连续环境再换策略梯度
我判断 RL 项目从哪起步时只问一个问题:状态空间能不能用一张表装下。能装下就用 Q-learning,查表更新直观,调参反馈快;状态连续或者动作连续,再换策略梯度类方法,因为表格会爆炸。下面这个 4x4 网格是最小的 Q-learning 闭环,起点在左上角,终点在右下角,动作是上下左右。
import numpy as np n_states = 16 n_actions = 4 q_table = np.zeros((n_states, n_actions)) alpha = 0.1 # 学习率:每次更新的步长 gamma = 0.95 # 折扣因子:未来奖励折算到当前的价值 eps = 0.9 # 初始探索率:随机动作的概率 eps_min = 0.02 eps_decay = 0.99 def move(s, a): r, c = divmod(s, 4) if a == 0: # 上 r = max(0, r - 1) elif a == 1: # 下 r = min(3, r + 1) elif a == 2: # 左 c = max(0, c - 1) else: # 右 c = min(3, c + 1) return r * 4 + c def step(s, a): ns = move(s, a) reward = 1.0 if ns == 15 else -0.01 return ns, reward for _ in range(3000): s = 0 while s != 15: a = np.random.randint(4) if np.random.rand() < eps else int(np.argmax(q_table[s])) ns, r = step(s, a) q_table[s, a] += alpha * (r + gamma * q_table[ns].max() - q_table[s, a]) s = ns eps = max(eps * eps_decay, eps_min)这段代码的核心更新公式是 Q 学习的 off-policy 更新:r + gamma * q_table[ns].max()表示"下一步的最优估计",不管当前策略选什么动作,更新目标都指向后续的最大值。alpha 控制更新幅度,调大了会震荡,调小了收敛慢,0.1 是这种小表格的合适起点。gamma 决定了智能体有多"近视",回合很短,0.95 就够,如果换成长期任务再调到 0.99。eps 从 0.9 开始衰减,保证前期大量探索、后期偏向利用,衰减到 0.02 之后停止,给策略留一点容错。
这个代码跑完,可以打印 q_table 看右下角附近的状态值,比看曲线更直观。如果 3000 回合后起点位置的值还不够高,先把 eps_decay 改小,让探索期更长,而不是急着调 alpha。
3.2 多智能体最小示例:两个智能体各学各的 Q 表,共享同一个奖励
多智能体 RL 的入门写法是独立学习:每个智能体维护自己的 Q 表,状态表示里加上对方的位置。下面这个例子让两个智能体从同一起点出发抢右下角终点,如果撞到同一个格子各扣 0.5,谁先到终点得 1.0。
import numpy as np q1 = np.zeros((16 * 16, 4)) # 状态 = 自己位置 * 16 + 对方位置 q2 = np.zeros((16 * 16, 4)) alpha, gamma, eps = 0.05, 0.95, 0.6 def move(s, a): # 动作定义与单智能体一致 r, c = divmod(s, 4) if a == 0: r = max(0, r - 1) elif a == 1: r = min(3, r + 1) elif a == 2: c = max(0, c - 1) else: c = min(3, c + 1) return r * 4 + c def joint_step(s1, s2, a1, a2): ns1, ns2 = move(s1, a1), move(s2, a2) if ns1 == ns2: return ns1, ns2, -0.5 if ns1 == 15 or ns2 == 15: return ns1, ns2, 1.0 return ns1, ns2, -0.01 def pick(q, s): return np.random.randint(4) if np.random.rand() < eps else int(np.argmax(q[s])) for _ in range(30000): s1, s2 = 0, 0 while s1 != 15 and s2 != 15: sidx = s1 * 16 + s2 a1, a2 = pick(q1, sidx), pick(q2, sidx) ns1, ns2, r = joint_step(s1, s2, a1, a2) q1[sidx, a1] += alpha * (r + gamma * q1[ns1 * 16 + ns2].max() - q1[sidx, a1]) q2[sidx, a2] += alpha * (r + gamma * q2[ns1 * 16 + ns2].max() - q2[sidx, a2]) s1, s2 = ns1, ns2 eps = max(eps * 0.999, 0.02)这个版本刻意做得简单,两个关键点要理解。第一,状态索引s1 * 16 + s2同时包含自己和对方的位置,单独看一个智能体的 Q 表没有意义,因为它的表格是"在对方处于某位置时我该怎么动"。第二,两个智能体拿到的是同一个r,这是最简单的共享奖励结构,代码能跑通,但训练中会频繁出现"一个智能体学会不动、等对方先走"的投机行为。
实际跑的时候不要只看总奖励。把两个智能体各自的单步平均奖励分别记下来,你会发现总奖励在涨,但可能只有一方在进步。这就是第 5 章要展开的信用分配问题。alpha 在这里我都给到了 0.05,比单智能体低一半,因为两个学习器同时更新,步长太大会互相干扰。如果震荡到完全发散,先不要调参,去检查奖励和状态表示。
3.3 接入仿真环境后必调的三个参数
从网格跳到真实一点的仿真环境,我通常只先动三个参数。第一是 gamma:回合越长越接近 1,回合能在几十步内结束就用 0.95,超过几百步的任务直接上 0.99。第二是学习率:样本量大的连续控制任务,alpha 通常会从 0.1 降到 0.01 这个量级,多智能体共享奖励的场景再降一半。第三是奖励尺度,如果奖励动不动就是几十的量级,更新步长会被放大,训练曲线像噪声,先把奖励归一化到 [-1, 1] 再跑。
另外一个值得养成的习惯是:画每个智能体的个体奖励曲线,而不是只画总奖励。多智能体场景下总奖励上涨说明不了任何问题,个体方差才是策略健康度的信号。某开发者接手我的代码后第一件事就是加了按智能体分组的奖励曲线,立刻发现我之前的第二智能体其实一直在拖后腿。多智能体参数调优的玄学大多发生在这里,但其实不是超参数的问题,是优化目标本身没设计对。
4. 再把 LLM 这条线跑通:从单智能体到多智能体的提示工程结构
4.1 单智能体最小循环:让模型只输出 JSON 动作,而不是自由文本
LLM 智能体的第一个工程决定是输出格式。让模型自由发挥会得到大量不可解析的自然语言,后面每个环节都要处理脏数据。我一般会强制模型只输出 JSON,结构固定为工具调用或结束两类。
import httpx import json API_URL = "http://localhost:8000/v1/chat/completions" # 替换成你的模型服务地址 MODEL = "your-model-name" def chat(messages, temperature=0.2, max_tokens=256): resp = httpx.post(API_URL, json={ "model": MODEL, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, }, timeout=30) return resp.json()["choices"][0]["message"]["content"] def run_agent(task, tools, max_iter=5): messages = [ {"role": "system", "content": "你只输出JSON,格式为{\"type\":\"call_tool\",\"tool\":\"工具名\",\"args\":{}} " "或{\"type\":\"finish\",\"answer\":\"最终答案\"}"}, {"role": "user", "content": task}, ] for _ in range(max_iter): out = chat(messages) try: act = json.loads(out) except json.JSONDecodeError: messages.append({"role": "user", "content": f"你刚才没输出合法JSON:{out},请重来"}) continue if act["type"] == "finish": return act["answer"] if act["tool"] not in tools: messages.append({"role": "user", "content": f"工具 {act['tool']} 不存在,请从 {list(tools)} 中选择"}) continue result = tools[act["tool"]](**act["args"]) messages.append({"role": "assistant", "content": out}) messages.append({"role": "user", "content": f"工具返回:{result}"}) raise TimeoutError("迭代次数用完仍未完成任务")temperature我固定 0.2,太低会变得机械重复,太高会让工具名都拼错。max_tokens256 对单次动作足够,因为只要求输出 JSON。max_iter设 5 是为了防死循环,实际生产我会放到 10 但叠加下面的兜底机制。两个关键设计:解析失败时不抛异常,而是把原始输出作为错误信息丢回给模型,让它自己纠正;工具不存在时同样回灌错误,下一轮模型大概率会换一个工具名。这套循环跑通之后,再加缓存和历史摘要,就是一个可上生产的单智能体骨架。
4.2 多智能体最小示例:一个模型后端,多个角色上下文轮流对话
LLM 多智能体的常见误入是把多个智能体理解成需要多个模型实例。实际不是,多数项目只要一个模型后端,每个智能体是独立的消息上下文,通过互相传递文本完成协作。下面这个双角色调度器例子,A 负责拆任务,B 负责执行,每轮把对方的输出拼进自己的上下文。
def make_agent(role_desc): return [{"role": "system", "content": role_desc}] def multi_agent_round(agent_a, agent_b, task, max_rounds=3): agent_a.append({"role": "user", "content": task}) last_out = "" for _ in range(max_rounds): out_a = chat(agent_a) agent_a.append({"role": "assistant", "content": out_a}) agent_b.append({"role": "user", "content": f"协调者说:{out_a}"}) out_b = chat(agent_b) agent_b.append({"role": "assistant", "content": out_b}) agent_a.append({"role": "user", "content": f"执行者说:{out_b}"}) last_out = out_b if '"type":"finish"' in out_b: break return last_out这个例子最值得注意的地方是:两个智能体共享同一个模型服务,但因为各自维护独立的消息列表,模型的角色行为被 system prompt 隔离开。协调者看到的上下文包含执行者的所有回复,执行者也一样,但两边的历史不完全一致,这是多智能体协作里很关键的一点——不需要上下文全部相同,只需要交换必要信息。
停止条件我用的是字符串匹配,实际项目里更可靠的做法是让每个智能体输出结构化消息,比如在最外层 JSON 里加"role"和"phase"字段,协调者根据phase判断是继续讨论还是给结论。用关键词判断"完成"很容易误停,这是我从一个多轮对话 demo 里踩出来的教训。
4.3 把 LLM 接进 RL 奖励函数:状态文本化打分
两条路线真正的交叉点在这里。常见做法之一是让 LLM 当奖励函数:把环境状态翻译成一段自然语言描述,让模型打分,再把分数喂给 RL 策略。下面这个函数可以直接替换上一章的奖励计算。
def llm_reward(state_desc, prev_reward=0.0): prompt = f"请评估以下状态对完成目标有利还是有弊,只输出-1到1之间的数字:{state_desc}" score = chat([{"role": "user", "content": prompt}], temperature=0.0, max_tokens=8) try: reward = float(score.strip()) except ValueError: return prev_reward or 0.0 # 解析失败回退,不抛异常 return max(-1.0, min(1.0, reward)) # 限制范围,防止异常值放大更新步长temperature=0.0是为了让打分尽量稳定,但即使这样,同一个状态分两次也可能差 0.3,所以我在实际项目里会同一个状态采样三次取平均。max_tokens=8是因为只需要输出一个数字,给多了反而容易夹带解释文本。解析失败时返回上一轮奖励或者 0.0,这是必须的兜底,解析失败返回 NaN 会让整个训练直接崩掉。
这类 LLM 奖励适合什么场景?低频、状态能用语言描述、不需要实时反馈的场景。举例:仓库里判断一个设备摆放是否合理,每 5 秒评估一次可以接受;但机械臂每个控制周期 10 毫秒,让 LLM 打分完全不现实。高频控制必须让 RL 直接吃数值状态,LLM 只能做高层指导。这是后面第 6 章组合方案的判断原则。
5. 多智能体项目里的常见翻车场景与排查清单
5.1 训练不收敛,但单智能体测试很正常
现象:把代码拆成单智能体版本,训练曲线正常,一旦多个智能体同时开始训练,奖励曲线变成一团乱麻,调低学习率也只能缓解不能根治。
原因:多智能体环境下每个智能体的策略都在变,对单个智能体来说环境是非平稳的,它学到的 Q 值或策略梯度时刻在被队友和对手打乱。这不是超参数问题,是独立学习的结构性问题。光调参等于用高倍望远镜看错方向。
解决:先确认每个智能体各自的奖励是否也在震荡;如果是个体奖励平稳而总奖励乱,问题在信用分配,见下一条。如果个体奖励本身就乱,把训练方式升级为中心化训练、去中心化执行,训练时让每个智能体额外看到全局状态或对方动作,执行时再退化到局部观测。
5.2 多智能体互相推锅:群体奖励上升,个体策略退化
现象:总奖励在缓慢上涨,但把每个智能体单独拎出来看,其中一个的动作越来越固化,永远在重复同一个动作序列,换一个对手后完全失效。
原因:共享奖励无法区分谁的贡献大。A 智能体学会了好策略,B 跟着捡分,B 的策略没有被淘汰的压力,于是退化成一个偷懒策略。
解决:第一步把总奖励拆成个人子奖励,至少让每个智能体有自己的任务项,哪怕只是很小的权重;第二步引入边际贡献的概念,对比"我在场"和"我不在场"的奖励差,用差值指导分配。工程上最省事的是前者,先拆开再优化。
5.3 LLM 智能体在工具调用上死循环
现象:日志里同一个工具名反复出现,模型每次都传接近相同的参数,工具每次都返回同一个错误,循环直到 max_iter 被截断。
原因:工具返回的错误信息没有给模型带来新的决策依据。常见的是错误信息太长被截断,或者报错内容是"参数错误"这种没有实际指导意义的文案。
解决:把错误信息摘要化,只保留关键字段,比如"第 3 个参数应为数字,当前为字符串";把重试次数从 max_iter 中分离,单独设 max_retry,超过后强制让模型换工具;如果模型始终纠结在同一工具,在下一轮消息里直接追加"重新检查工具清单,不要再次调用 tool_name"。
5.4 LLM 当奖励函数时打分漂移
现象:RL 训练曲线看起来像高频噪声,明明环境没变化,奖励值却上下大幅跳动,策略学到的是"去讨好打分规律"而不是"解决任务"。
原因:LLM 打分本身有随机性,温度再低也有采样波动,状态描述一旦含糊,模型会把注意力放到无关细节上。这是奖励信号的信噪比问题。
解决:降低到 temperature=0.0;把打分从连续值改成离散档位,只允许输出 -1、0、1;给 prompt 加参考锚点,比如"0 表示状态没有变化,1 表示明显接近目标";同一个状态采样三次取平均,方差超过阈值时用上一轮的奖励替代。
5.5 把微调的 LLM 直接当 RL 策略用,奖励曲线假漂亮
现象:有人想把 LLM 微调成动作生成器,训练阶段用模仿学习,验证时单轮成功率很高,但放进真实闭环环境里跑长回合,累积收益明显低于 RL 训练出来的策略。
原因:LLM 微调优化的是 token 级别的交叉熵,不是决策收益。单轮生成正确不代表多步决策能最大化长期回报,而且微调数据的分布和运行时的真实分布不一致,一步错后面全错。
解决:如果动作空间是开放式的,又确实需要 LLM 的理解力,把它放在规划层而不是控制层。让 LLM 输出方案建议,RL 负责执行和评价。如果动作空间是固定有限的枚举类型,直接用 RL 训练一个查表或策略网络,不要硬套 LLM。
6. 验证这套方案值不值得做:离线回放、两个在线信号和一个组合习惯
投入任何一个方向之前,先用离线回放估上限。把一段历史数据录下来,包含状态、动作、奖励和 LLM 输出,然后用这段数据回放两种方案:LLM 方案看在每个历史节点重新推理会得到什么动作、收益和单步延迟;RL 方案看用历史数据做批量训练时,个体奖励曲线的方差有多大。离线能跑通的效果,在线不一定能复现;但离线就崩的方案,在线几乎一定崩。
在线评估阶段,除了常规的成功率,我会额外盯两个信号:单步耗时和恢复成本。一个方案奖励涨了,但单步耗时从 0.2 秒涨到 2 秒,说明推理成本正在吃掉收益。恢复成本是失败后回到正常状态需要多少额外动作或 token,如果一次失败要消耗 20 次重试才恢复,这个方案在生产里的可用性要打问号。
我现在做智能体项目的默认组合是:LLM 负责目标分解、排序和冲突裁决,RL 负责高频动作执行,最后加一层规则兜底。仓储调度那个项目就是这个结构:LLM 接收订单和状态描述,输出任务队列;RL 控制每台设备是否移动、往哪个方向走;设备碰撞风险由独立的安全规则直接覆盖。LLM 只在场景状态变化时被调用,不参与每个控制周期,RL 在底层持续学习优化,规则负责不让任何一行代码出安全事故。
这个组合习惯来自一次教训。我最早尝试让 LLM 每分钟给 RL 打一次分,奖励信号变来变去,训练曲线起伏大得没法看;后来把奖励函数固定住,LLM 只负责调整任务顺序,RL 专心优化设备动作,两天内训练就稳定了。给我最大的感受是:多智能体项目真正难的不是某个模型有多强,而是分清哪些决策应该交给语言模型、哪些动作必须交给强化学习。高频动作放 RL,低频语义决策放 LLM,规则兜底不可省,按这个顺序切入,大多数方案都能在两周内给出一个值得继续投入的原型。希望帮到你。
本文还有配套的精品资源,点击获取