想象你第一次去一座陌生城市旅游,身边跟着一位热情但路盲的导游。你说“我想去看古城、美术馆和海边日落”,导游立刻拍胸脯说“没问题,我们往南走,再拐两个弯就到!”结果带你绕了三小时,美术馆关门了,海边根本没去成。这个导游就是大语言模型:它很会理解你说的话、很会“接话”,但真要它规划一条长路线,它容易凭印象编路、漏景点、绕远路。现在换一种做法:把整座城市的地铁线路图画成一张精确的、计算机能读懂的地图,再把“今天要去的三个地方”写成一式两份的行程单,交给地铁调度中心的换乘规划器去算出最优换乘方案,最后让导游把专业换乘术语翻译成“从 A 口出站,步行 3 分钟到美术馆”。这就是 LLM+P:让大模型负责听懂人话和说人话,让符号规划器负责严格计算动作序列。本文会用这个“导游 + 地铁图”的故事,带你完整理解 LLM+P 符号规划的实现思路、适用边界和工程落地方法。
学完 LLM+P,你将获得设计可靠智能体的关键技能:让大模型负责自由表达,让符号规划器负责严谨求解,二者结合后,任务分解和决策不再只靠“感觉”。
可视化演示
可视化展示大语言模型结合符号规划的自主决策流程:自然语言转PDDL、规划器求解与状态空间搜索,交互切换观察混合智能架构。
本节配有交互动效(进入视口后自动加载,约 3KB)。
LLM+P规划管线 LLM将自然语言任务翻译为PDDL规划问题 经典规划器求解PDDL输出最优动作序列 NL \u2192 LLM \u2192 PDDL \u2192 Plan 点击切换查看PDDL翻译
大模型规划短板
热情的路盲导游为什么总把游客带偏?因为他不是真的“认识路”,只是记住了很多“游客爱听的表达”:你说往南,他立刻联想“南边有古城”,再联想“古城附近应该有美术馆”,于是脑补出一条看似顺理成章、实际上不存在的路线。大语言模型也有同样的毛病:它在海量文本上学到了“计划长什么样”,却没有学过在状态空间里一步一步验证“这条路径真的能走通”。
从技术角度看,LLM 是一个自回归语言模型,它的目标是“预测下一个词”,不是“找到可达目标的动作序列”。当任务链条超过 5~10 步,它容易产生三类问题:第一,路径幻觉,编造出地图上不存在的地点或动作;第二,漏掉步骤,比如规划“订机票+订酒店+租车”时,把“租车”这个大目标忘了;第三,组合搜索弱,当动作空间很大、约束交错时,真正的最优解需要系统性搜索,而生成式模型只会沿着概率最高的方向一路走到底。
这就解释了为什么很多智能体 demo 看着聪明,一到真实多步任务就掉链子。单靠 LLM 规划,相当于让一位记性好但从不看地图的导游带团;我们需要给他配一张精确的地铁线路图,并把“找路”这步交给专业计算者。
PDDL语言基础
地铁图要发挥作用,必须有一套统一画法:每条线路用颜色标出,每个站点有标准名称,换乘关系清清楚楚。符号规划里的 PDDL 就是这套“标准画法”。PDDL 全称是 Planning Domain Definition Language,它把规划问题拆成两个文件:域文件(domain)描述通用的规则,比如“人可以乘坐地铁”“列车在站间移动”;问题文件(problem)描述具体场景,比如“当前在 A 站,要去 C 站”。域文件相当于地铁线路图,问题文件相当于你手写的行程单。
在 PDDL 中,动作是改变世界状态的最小单元。一个动作通常写作三元组:参数、前提条件、执行效果。只有当前状态满足前提条件,动作才能执行;执行后,世界按照效果更新。
把动作格式标准化以后,任何人都能写“地铁图”,任何规划器都能读“地铁图”。导游不需要亲手设计线路,他只需要用这套标准语言,把游客的需求准确写进行程单。
- 动作参数 params这是动作涉及的“人”和“物”,比如“从 A 站坐地铁去 B 站”中的 A、B。
- 前提条件 preconditions动作执行前必须成立的谓词,比如“from 和 to 必须在同一条线路的相邻站台上”。
- 执行效果 effects动作执行后要删除和新增的谓词,比如删除“当前在 from”,新增“当前在 to”。
LLM+P流程
现在故事进入正题:导游怎么和调度中心合作?第一步,导游听完游客诉求后,用标准格式写出一份“需求单”;第二步,把需求单交给调度中心的换乘规划器;第三步,规划器算出精确换乘方案;第四步,导游把方案翻译成游客听得懂的话。LLM+P 就是把这四步搬到 AI 系统里:先用 LLM 把自然语言任务转成 PDDL 域文件和问题文件,再调用经典规划器求解,最后让 LLM 把规划器输出的符号动作序列翻译回自然语言步骤。
整个流程可以写成一条简洁的流水线:
这个流程的美妙之处在于各司其职:LLM 不负责“算路”,规划器不负责“听懂人话”。导游的短板被调度中心的精确计算补上,调度中心的“冷冰冰”又被导游的人性化翻译化解。
- 自然语言任务 T用户的原始需求,例如“帮我把三箱货物从仓库运到发货区”。
- LLM 生成 PDDLLLM 将 T 转换成域文件 D 和问题文件 P,其中包含对象、初始状态和目标状态。
- 规划器求解 π经典规划器在 D 定义的规则下,从初始状态到目标状态搜索出动作序列。
- LLM 反向翻译 StepsLLM 把 π 里的符号动作(如
(move truck1 depot dock))翻译成可读指令。
经典规划器
地铁调度中心的换乘规划器是怎么工作的?它不会像导游那样凭感觉说“往南走”,而是把地铁网络想象成一张巨大的图,每个站点是一个状态,每段线路是一个动作。它从当前站出发,逐条尝试所有能坐的线路,每到一个新站就记录下来,再继续尝试,直到找到一条能到达目标站的路线。这个过程就是状态空间搜索。
在符号规划中,每个状态是一组谓词的真假集合,动作会删除一些谓词、新增一些谓词。规划器从初始状态出发,反复执行“检查前提 → 应用效果”的步骤,形成一棵搜索树。
真实场景中,盲目搜索会指数爆炸。现代规划器如 Fast Downward 会计算启发式函数,优先探索“看起来离目标更近”的分支,再结合因果图、landmark 等技巧大幅剪枝。对 LLM+P 来说,规划器不需要多聪明,只要它给出的是可证可达的符号动作序列,就已经弥补了 LLM 最致命的可靠性短板。
- 前提检查 pre(a) ⊆ s动作 a 的所有前提谓词都必须已经在当前状态 s 中,否则不能执行。
- 删除效果 del(a)从 s 中去掉被动作废弃的谓词,比如“货物已在仓库”这个事实。
- 新增效果 add(a)把动作产生的新谓词加入状态,比如“货物已在发货区”。
转换接口设计
导游想要把需求单递给调度中心,中间需要一位既懂中文又懂地铁黑话的翻译官。如果翻译官把“美术馆”写成了“博物馆”,或者漏掉了换乘站的楼层信息,调度中心要么报错,要么给出一条完全错误的路。LLM+P 里最考验工程能力的,正是 LLM 和 PDDL 之间的“翻译官”——转换接口。
设计这个接口通常分三步:第一步,设计提示模板,用 few-shot 示例告诉 LLM“你只需要输出 PDDL 格式的 JSON,不要解释”;第二步,写语法校验器,把 LLM 输出解析成语法树,检查谓词是否定义过、对象类型是否匹配;第三步,设置错误恢复,如果校验失败,把错误信息回喂给 LLM,要求它重新生成。
好的翻译官能让两个体系无缝协作。工程上,这个接口往往是 LLM+P 系统里投入最多精力的部分:提示模板决定了 LLM 输出的规范性,校验器决定了坏数据不会被放行,错误恢复则给了 LLM 一次“重新翻译”的机会。
- 语法解析 ParseOK检查括号是否配对、动作名是否合法,就像检查需求单上的字是否看得懂。
- 类型检查 TypeOK检查每个对象和谓词是否在域文件中声明,防止把“美术馆”当成“地铁站”。
- 可解性检查 Solvable调用规划器快速验证是否存在可行计划,避免把无解问题反复丢给下游。
适用场景边界
是不是所有规划任务都适合“导游 + 地铁图”?当然不是。地铁图再精确,也没法告诉你哪家店好吃、哪个路口临时封路、游客突然想改行程怎么办。符号规划本质上要求三件事:状态可以枚举、动作效果可以确定、目标可以形式化。一旦任务涉及实时感知、开放世界或主观偏好,纯符号规划就会失灵。
适合 LLM+P 的任务通常有这些特征:步骤多但规则明确,比如装配流程、机器人搬运、API 编排;约束复杂但可描述,比如“先做身份验证再做余额查询”;目标可验证,比如“货物最终必须在发货区”。不适合的任务则是开放式闲聊、实时驾驶决策、个性化推荐等,这些需要常识和感知,符号化成本极高。
所以真正落地的混合架构不是二选一:LLM 负责把模糊需求抽象成结构化问题,符号规划器负责在结构化空间里精算,执行器负责感知真实世界并把反馈送回 LLM。导游、调度中心、现场工作人员三方配合,才能应对真实世界的复杂性。
质量评估方法
怎么判断一个导游带团带得好不好?最简单粗暴的标准是:承诺的景点是否都去了?去的顺序是否合理?游客是否真的满意?对应到 LLM+P,我们也有三个经典评估口径:规划成功率看重在可解任务中,系统最终是否输出合法计划;步骤正确率对比计划中的每一步是否和人工标注的 ground truth 一致;端到端完成率则把计划交给模拟器或真机执行,看最终目标是否真的达成。
规划成功率是最常用的基础指标,可以写成:
在对比评测中,纯 LLM 在短任务上成功率可能不错,但任务一长就急剧下降;LLM+P 虽然多了一道 PDDL 转换开销,成功率却更稳定。用数据说话,我们才能说服自己和团队:那个路盲导游,真的需要一张精确的地铁图。
- 分子 #solvable runs在所有“确有解”的测试任务中,系统最终找到并输出了合法动作序列的次数。
- 分母 #total runs全部测试任务的次数,包括有解和无解的样本,保证统计口径完整。
- 比值计算用分子除以分母,得到 0 到 1 之间的比例,便于和纯 LLM 基线对比。
AI 应用场景
智能客服任务拆解
阶段:任务规划与执行
解决的问题:纯 LLM 客服容易在连续服务中漏动作或搞错顺序,比如用户要求“改地址、查余额、补办卡”,模型可能只执行前两项。LLM+P 先把用户语义转成标准服务动作图,再用符号规划器生成合法执行序列,保证每项诉求都被覆盖。
具体案例:某银行智能客服把“身份验证”“账户查询”“地址修改”“卡片补办”定义为 PDDL 动作,LLM 从用户对话里提取初始状态和目标状态,规划器输出“先验证身份,再修改地址,最后补办卡”的动作序列,客服机器人按序列调用后台 API。
仓储机器人任务调度
阶段:动作规划与调度
解决的问题:自然语言指令“把 A 区货架移到 B 区并清点货物”如果只靠 LLM 生成,容易漏掉“必须先确认货架无遮挡”这类隐性约束。符号规划器可以在离散状态空间中验证每一步可行性。
具体案例:某仓储机器人团队用 LLM 将运单指令转成 PDDL 问题文件,Fast Downward 规划出“移动机械臂→抓取货架→避让通道→放下货架→扫码清点”的动作序列,导航控制器负责底层执行。
企业级 API 编排智能体
阶段:工作流规划与校验
解决的问题:企业入职流程涉及账号开通、权限申请、培训预约、工位分配,顺序严格且容易出错。LLM+P 把抽象流程转成 PDDL 动作集,规划器输出合法审批序列,避免“权限还没开就去预约系统”这类依赖错误。
具体案例:某 HR 系统的智能体接收“为新员工办理入职”指令后,LLM 生成初始状态(员工已报到、账号未创建)和目标状态(账号已创建、权限已分配、培训已预约),规划器输出带前置依赖的动作序列,再由 RPA 执行每个 API 调用。
Python 演示
# -*- coding: utf-8 -*-"""简化版 LLM+P 演示:模拟 LLM 把自然语言任务转成 PDDL 风格的初始/目标状态,再用 BFS 规划器搜索动作序列,最后翻译成自然语言步骤。"""from collections import deque class LLMTranslator: """模拟 LLM 的翻译能力:自然语言 -> PDDL 状态谓词""" def translate(self, task_text): # 真实场景中这里调用大模型,生成 (init, goal) 二元组 if "从3走到7" in task_text: init = {("at", 3)} goal = {("at", 7)} elif "从1走到4" in task_text: init = {("at", 1)} goal = {("at", 4)} else: init = {("at", 0)} goal = {("at", 5)} return init, goal class BFSPlanner: """经典符号规划器:广度优先搜索可执行动作序列""" def solve(self, init, goal, max_steps=15): start = frozenset(init) target = frozenset(goal) queue = deque([(start, [])]) visited = {start} while queue: state, plan = queue.popleft() if target.issubset(state): return plan if len(plan) >= max_steps: continue current_at = [value for pred, value in state if pred == "at"] if not current_at: continue pos = current_at[0] for next_pos in (pos - 1, pos + 1): new_state = set(state) new_state.discard(("at", pos)) new_state.add(("at", next_pos)) frozen = frozenset(new_state) if frozen not in visited: visited.add(frozen) queue.append((frozen, plan + [("move", next_pos)])) return None class PlanTranslator: """把符号动作序列翻译回自然语言""" def translate(self, plan): if plan is None: return "抱歉,没有找到可行的行动计划。" lines = [] for idx, (action, target) in enumerate(plan, 1): lines.append(f"{idx}. 向前移动到位置 {target}") return "\n".join(lines) def main(): task = "帮我从3走到7" translator = LLMTranslator() planner = BFSPlanner() plan_translator = PlanTranslator() init, goal = translator.translate(task) print("PDDL 初始状态:", sorted(init)) print("PDDL 目标状态:", sorted(goal)) plan = planner.solve(init, goal) print("符号规划器输出:", plan) print("自然语言计划:") print(plan_translator.translate(plan)) if __name__ == "__main__": main()
init = {("at", 3)}用 PDDL 谓词 “at“ 表示”当前位置是 3”,对应问题文件中的初始状态。target.issubset(state)检查目标谓词是否全部满足,等价于 PDDL 中的目标判定。new_state.discard(("at", pos))执行动作效果:从状态中删除旧的 "at" 事实。new_state.add(("at", next_pos))执行动作效果:向状态中新增 "at" 事实,形成下一个状态。planner.solve(init, goal)调用符号规划器完成组合搜索,这一步正是纯 LLM 最不擅长的部分。
常见误区
误区“现在 ChatGPT 连代码都能写,让它规划任务不是小菜一碟吗?再加一套 PDDL 和规划器,纯属脱裤子放屁。”
正解LLM 生成的是“看起来像计划”的文本,不是“经过验证可执行”的计划。它不会在状态空间里做回溯搜索,所以容易漏步骤、编造不存在的依赖。符号规划器则保证:只要 PDDL 模型正确,输出的动作序列一定是从初始状态到目标状态的合法路径。导游可以很会说话,但认路这事还是交给调度中心更稳妥。
为何常见:LLM 在短任务、常见场景上表现太好,让人误以为它已经掌握了“推理”能力,而忽略了长链条可靠性问题。
误区“LLM 在 LLM+P 里就是做个格式转换,把中文变成 PDDL,再把 PDDL 变回中文,没什么技术含量。”
正解最难的部分恰恰是 LLM 完成的“问题建模”:从一段开放式的自然语言里,选出合适的谓词、对象、目标状态,甚至补全域文件缺失的动作。规划器只能解决“封闭世界里的搜索”,而 LLM 负责把模糊的开放世界装进封闭世界。导游最大的价值不是念站名,而是听懂游客到底想看什么。
为何常见:人们容易把 LLM 类比成“翻译软件”,而低估了从非结构化到结构化的建模难度。
误区“既然 LLM 规划不可靠,那以后所有任务都交给 PDDL + Fast Downward 不就行了?”
正解规划器的可靠性有前提——它必须拿到一个准确、完整、封闭的 PDDL 域模型。如果域模型描述不准确,规划器会非常自信地给出不可执行的计划。实时感知、开放目标、主观偏好这类任务,符号化成本极高,仍需要 LLM 处理常识和意图理解。地铁图再精确,也回答不了“哪家餐厅更适合约会”。
为何常见:看到 LLM 在长链任务上翻车后,容易走到另一个极端,低估符号系统的建模瓶颈。
误区“深度学习派和符号主义派不是吵了很多年吗?LLM+P 肯定只是学术拼盘,早晚会被纯端到端模型取代。”
正解两者解决的是不同问题:LLM 擅长自然语言理解、抽象和常识补全;符号规划擅长可验证的组合搜索。LLM+P 正是把两者放在各自擅长的位置,属于典型的混合架构,也是当前智能体落地最务实的路线之一。导游和调度中心不是竞争关系,而是搭档关系。
为何常见:技术社区长期存在“神经网络 vs 符号 AI”的叙事惯性,让人忽略了工程系统可以同时受益于两套工具。
面试题
Q1. 大语言模型直接做长链任务规划的主要问题是什么?
- A.训练数据严重不足
- B.长链推理容易漏步← 正确答案
- C.推理速度非常缓慢
- D.无法理解中文任务
答案:B。LLM 是自回归生成模型,擅长生成“像计划”的文本,但不会在状态空间里做回溯搜索,任务越长越容易漏步骤、产生路径幻觉。速度和语言理解不是核心瓶颈。就像路盲导游不是走得慢,而是根本不认路。
Q2. PDDL 中域文件(Domain)主要描述什么?
- A.对象初始目标描述
- B.谓词动作通用定义← 正确答案
- C.用户对话历史记录
- D.模型训练超参数配置
答案:B。域文件是“地铁线路图”,定义谓词和动作的通用规则;问题文件才是“行程单”,描述具体对象、初始和目标。对话历史和训练超参数不属于 PDDL 范畴。
Q3. 在 LLM+P 流程中,LLM 的核心职责是什么?
- A.执行状态空间搜索
- B.任务转成 PDDL 并翻译回来← 正确答案
- C.对规划动作做剪枝
- D.计算启发式函数
答案:B。LLM 负责把自然语言任务建模成 PDDL,再把规划器输出的符号序列翻译回自然语言。搜索、剪枝、启发式计算都是经典规划器的活。导游负责写需求单和翻译,调度中心负责算路线。
Q4. 下列哪些属于经典规划器的输入或输出?
- A.初始状态描述← 正确答案
- B.目标状态描述← 正确答案
- C.自然语言摘要
- D.可执行动作序列← 正确答案
答案:ABD。经典规划器输入 PDDL 问题文件中的初始状态和目标状态,输出动作序列。自然语言摘要是 LLM 的产物,不是规划器的直接输入输出。
Q5. 状态转移公式 s' = (s \ del(a)) ∪ add(a) 中,del(a) 表示什么?
- A.被删除的谓词集合← 正确答案
- B.被新增的谓词集合
- C.动作的前提条件集合
- D.规划问题的目标集合
答案:A。del(a) 是动作执行后要从世界中删掉的事实,add(a) 是要新增的事实。pre(a) 才是前提条件。执行动作就像坐地铁:删掉“在旧站”,新增“在新站”。
Q6. 哪种任务最适合使用 LLM+P 组合?
- A.开放式闲聊对话
- B.确定性强的多步任务规划← 正确答案
- C.实时道路驾驶决策
- D.短视频个性化推荐
答案:B。LLM+P 适合状态可枚举、动作效果确定、目标可验证的任务,比如装配、调度、API 编排。闲聊、驾驶、推荐依赖实时感知和主观偏好,符号化成本极高,不适用。
Q7. 设计 LLM 到 PDDL 的转换接口时,以下做法合理的有?
- A.用提示模板约束输出格式← 正确答案
- B.直接使用任意自然语言
- C.加入 PDDL 语法校验← 正确答案
- D.忽略非法 PDDL 输出
答案:AC。提示模板和语法校验是接口设计的核心,能显著提高 LLM 转 PDDL 的成功率。任意自然语言输出和不校验非法结果,都会让下游规划器崩溃或被垃圾数据污染。
Q8. 纯 LLM 规划在组合空间大的问题上容易失败,根本原因是什么?
- A.上下文窗口太小
- B.缺乏符号级回溯搜索← 正确答案
- C.没有训练数据可用
- D.代码质量普遍偏低
答案:B。组合空间大意味着需要系统性搜索和回溯。LLM 只能单向生成 token,没有“试错—回退—再试”的机制。上下文窗口只是限制,不是失败的本质原因。
Q9. 评估 LLM+P 规划质量时,常用的指标有哪些?
- A.规划成功率← 正确答案
- B.步骤正确率← 正确答案
- C.端到端任务完成率← 正确答案
- D.模型参数量大小
答案:ABC。规划成功率、步骤正确率、端到端完成率分别衡量“能不能出合法计划”“每一步是否标准”“执行后是否真达成目标”。模型参数量是模型的属性,不是规划质量指标。
Q10. 经典规划器求解时,如果 PDDL 域模型描述不准确,会发生什么?
- A.规划器自动修正域模型
- B.规划结果可能不可执行← 正确答案
- C.LLM 自动补全缺失动作
- D.规划器改用深度学习
答案:B。经典规划器严格依据域模型搜索,不会自动修正模型。如果谓词、动作或状态描述错了,它会自信地输出一条在真实世界走不通的路线。因此 LLM 负责的“建模”环节才是 LLM+P 成功的关键。