资讯动态

大语言模型+传统算法:路径规划智能体混合架构实战与避坑指南

发布时间:2026/9/19 4:26:38 来源:尧图企业网站定制
我最早做路径规划智能体的时候走了一个大弯路我试图让大语言模型直接吐出一条完整的路径坐标序列从起点到终点一口气输出几十个格子。结果模型要么给出一串穿墙的坐标要么走到一半卡在死胡同里要么干脆在 JSON 里漏了一个括号让整个循环崩溃。后来我把架构改成“大语言模型负责决策传统算法负责执行”项目才真正跑起来。这篇文章记录的就是这个从零搭建路径规划智能体的过程适合想用大语言模型做机器人规划、做动态避障仿真、或者想搞懂 Agent 工具调用机制的读者。我不打算堆概念直接给你一套能跑通的最小实现以及那些文档里不会写的坑。1. 为什么是“大语言模型 路径规划”而不是直接拧开 A* 就用1.1 传统路径规划算法已经很能打短板在哪首先要承认A*、Dijkstra、RRT、PRM 这些经典算法在路径规划领域非常成熟而且在确定性的静态地图上它们往往比任何大语言模型方案都快、都准。A* 在栅格地图上能保证找到最短路径RRT 在高维空间里也有很强的探索能力。那为什么还要折腾大语言模型我自己的体会是传统算法的短板不在“找路”本身而在三个地方语义理解用户说的是“从办公室到仓库尽量别经过会议室”传统算法听不懂这句话你得手动把会议室区域标成障碍甚至要额外写规则来调整路径偏好。场景适配成本换个地图、加一个约束、改一个优化目标通常要改代码。比如今天要求路径最短明天要求转弯最少后天要求路过某个充电点传统算法每个变化都要重新建模。长尾决策动态场景里障碍物变了之后怎么办、算法搜路失败之后怎么办、多条可行路径里选哪条这些“规划之外”的决策逻辑写起来又碎又多。这三个短板恰好是大语言模型的强项于是就有了把两者结合起来的动力。1.2 大语言模型在路径规划里到底扮演什么角色大语言模型在路径规划项目里最适合干的活不是“算路径”而是“做规划决策”。举个实际例子。同样一句指令“我现在在 (2,2)要去 (8,8)路上别经过 (5,5) 到 (6,6) 那片区域优先少拐弯。”传统方案里你需要把这个需求拆成禁区坐标、目标代价函数、搜索策略写进代码。而大语言模型可以直接把这个自然语言指令解析成结构化约束交给底层搜索算法执行。除了语义理解LLM 还能做几件事策略选择根据当前环境判断该用 A*、RRT 还是直接报错“目标不可达”。异常处理规划失败时分析失败原因是目标被围住还是约束冲突决定是否调整约束后重试。多约束权衡路径短、拐弯少、避开拥挤区域、离充电点近这些目标冲突时给出取舍。换句话说大语言模型像是路径规划系统里的“调度员”而 A* 这类算法是“执行力很强的执行者”。调度员不需要自己搬砖但要知道什么时候让谁去搬、搬不动怎么办。1.3 我推荐的技术路线混合架构而不是二选一不要纠结“到底用 LLM 还是用算法”这俩不冲突。我实测下来最优解一直是混合架构LLM 负责目标理解、任务拆解、异常恢复传统算法负责网格搜索、代价计算、路径生成。我把方案对比放在下面方便你理解为什么我最终选混合架构。方案优点缺点纯传统算法快、稳定、可证明最优不懂语义改约束要改代码纯 LLM 生成路径灵活能理解自然语言容易生成非法路径耗 token不稳定LLM 底层搜索工具语义理解 路径质量兼顾架构复杂一点需要设计工具协议纯 LLM 生成路径这个方案看起来诱人但你多测几次就会发现模型经常输出“超出地图边界”“穿过障碍物”“路径不连续”这类问题。与其费劲去修正模型输出不如把路径搜索交给工具让 LLM 去调用工具。2. 智能体拆解先画清楚边界再动键盘2.1 智能体的四个标准组件路径规划智能体和其他 Agent 项目一样核心组件其实就四块大语言模型、工具层、状态管理、执行循环。大语言模型负责推理和决策它不直接操作地图而是通过“观察环境 - 思考 - 调用工具 - 观察结果”来推进任务。工具层暴露给 LLM 的函数集合比如查询地图、获取当前位置、调用底层路径搜索、执行移动等。状态管理记录当前坐标、目标坐标、已访问点、地图版本、规划结果等。状态越清晰LLM 就越不容易犯糊涂。执行循环Agent 的主循环把大模型输出解析成动作执行动作后把结果反馈给大模型直到完成目标或触发终止条件。这四块里最容易忽略的是状态管理。很多人在第一个版本里只想着“对话”让 LLM 自己记住走到哪了。结果模型上下文一长坐标就记错了。正确做法是把状态放在智能体框架里每一轮把状态注入到提示词中而不是靠模型的记忆力。2.2 路径规划智能体的工具集应该怎么设计工具集设计的核心是“给模型什么不给模型什么”。我给路径规划智能体定义了一组最小工具集你先感受一下get_map_info()获取地图尺寸、障碍物分布、起点终点。返回压缩后的地图摘要不需要把整张图全塞进去。get_neighbors(position)获取某个格子的可通行邻居。用于模型理解局部环境。search_path(start, goal)调用底层 A* 搜索返回最短路径。这是核心工具。validate_path(path)校验路径是否越界、是否穿障碍、是否连续。finish(answer)结束任务返回最终路径。为什么只有五个因为工具越少模型的选择越少出的幺蛾子也越少。很多新手喜欢一上来就暴露二三十个工具结果模型频繁调错工具。精简工具集是我踩坑之后最后悔没早点做的事。每次调用工具后工具返回的观测结果最好做裁剪比如只返回坐标和路径摘要不要返回整张地图的 JSON 大对象。工具结果太长既浪费 token又会让模型看不过来。2.3 为什么一定要给 LLM 接一个“底层路径搜索工具”这是整个混合架构里最关键的一步。我以前也让 LLM 自己一步步走迷宫类似 ReAct 的用法让模型在每一轮选择“走到上/下/左/右”。结果是小地图上能跑通但效率极低动不动几十轮遇到复杂地图时模型会原地转圈一旦上下文变长模型开始遗忘自己走过哪些格子。后来我做了个决定地图上逐格移动这种“低级操作”不给 LLM 了取而代之的是直接给一个search_path工具内部用 A* 算出完整路径。LLM 只需要告诉工具“起点在哪、终点在哪、要避开哪些额外约束”剩下的交回给算法。这样分工之后模型压力小多了路径质量也有保证。A* 保证路径不穿墙、尽量短LLM 负责判断什么时候用、用完之后怎么处理。举一个很现实的场景如果地图上有临时障碍旧路径无效了LLM 不需要重新逐格寻路它只需要调用search_path重新算一次然后检查新路径。这才是智能体该有的样子。3. 从零搭一个最小可运行版本3.1 模型怎么选本地部署还是调用 API在动手写代码前先解决模型来源。路径规划智能体对语言模型的要求不算高关键能力是能理解 JSON、能按照系统提示进行工具调用、能稳定输出结构化内容。基于这个要求你可以走两条路调用现成 API开发迭代速度快模型能力强但要关注额度和成本。很多云平台都有免费额度适合第一版验证。我会建议你先用有免费额度的接口跑通再考虑成本问题。本地部署开源模型如果你的场景对数据隐私要求高或者需要离线运行可以用 Ollama 这类工具跑开源权重模型。模型规模不要贪大7B 到 14B 级别在路径规划这种任务上基本够用还省去网络延迟。我自己在实验阶段是两种混着来的白天开发用云端 API 快速迭代晚上批量测试或者需要长时间跑动态避障时切到本地小模型。注意本地模型的推理速度和格式遵循能力参差不齐如果你发现输出频繁不符合 JSON 格式优先尝试换更大参数量的模型而不是死磕提示词会省很多时间。3.2 地图与环境的抽象别一上来就追求 3D第一个版本别想太复杂别一上来就搞 3D 点云、栅格地图加高度信息、多智能体协同。我建议从一张二维栅格地图开始地图用二维数组表示0 是可通行1 是障碍物。比如一张 10x10 的地图MAP [ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 0, 1, 0, 1, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 1, 0, 1, 0], [0, 0, 0, 1, 0, 0, 0, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 1, 0, 0, 1, 0], [0, 1, 0, 1, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], ]这段代码就是你的“仿真环境”。后面要扩展到机器人、无人机也只需要把地图来源换成传感器数据即可Agent 的逻辑不用变。环境抽象要注意一个原则地图数据只经由工具返回给 LLM不要把整张地图直接硬生生塞进 system prompt。我吃过这个亏一次把 100x100 的地图全塞进去既费 token又让模型抓不住重点。正确做法是给 LLM 一个精简地图摘要或者在工具返回值里提供局部信息让它按需查询。3.3 Agent 主循环代码实现下面给一套精简但完整的主循环实现。我刻意把模型调用部分做了解耦你只需要实现chat()函数把它切换到任意模型接口上。import json import heapq from typing import List, Optional, Tuple # ---------- 地图与A* ---------- MAP [...] # 上面的10x10地图这里省略重复 def neighbors(pos): x, y pos for dx, dy in [(1,0),(-1,0),(0,1),(0,-1)]: nx, ny xdx, ydy if 0 nx len(MAP) and 0 ny len(MAP[0]) and MAP[nx][ny] 0: yield (nx, ny) def astar(start, goal): open_heap [(0, start)] came_from {} g_score {start: 0} while open_heap: _, cur heapq.heappop(open_heap) if cur goal: path [] while cur in came_from: path.append(cur) cur came_from[cur] path.append(start) return path[::-1] for nxt in neighbors(cur): tentative g_score[cur] 1 if tentative g_score.get(nxt, float(inf)): came_from[nxt] cur g_score[nxt] tentative priority tentative abs(nxt[0]-goal[0]) abs(nxt[1]-goal[1]) heapq.heappush(open_heap, (priority, nxt)) return None # ---------- 状态与工具 ---------- state { start: (0, 0), goal: (9, 9), position: (0, 0), path: [], map_repr: 10x10 grid map, 0free, 1obstacle, } def tool_get_map_info(): return {map: state[map_repr], start: state[start], goal: state[goal], current_position: state[position]} def tool_search_path(start, goal): path astar(tuple(start), tuple(goal)) if path is None: return {status: failed, reason: no_path} state[path] path return {status: ok, path: path, path_length: len(path)} def tool_get_neighbors(position): return {neighbors: list(neighbors(tuple(position)))} def tool_validate_path(path): if not path: return {valid: False, reason: empty} for p in path: if MAP[p[0]][p[1]] ! 0: return {valid: False, reason: fobstacle_at_{p}} return {valid: True} TOOLS { get_map_info: tool_get_map_info, get_neighbors: tool_get_neighbors, search_path: tool_search_path, validate_path: tool_validate_path, finish: lambda answer: {status: finished, answer: answer}, } # ---------- LLM 对话接口占位 ---------- def chat(messages: List[dict]) - str: # 在这里替换成你的模型调用代码返回一段文本 # 例如调用本地模型或者云端 API raise NotImplementedError # ---------- Agent 主循环 ---------- def agent_loop(max_steps: int 10): messages [{role: system, content: SYSTEM_PROMPT}] messages.append({role: user, content: 请把从起点到终点的路径规划任务完成。}) for step in range(max_steps): response chat(messages) messages.append({role: assistant, content: response}) try: action json.loads(response) except json.JSONDecodeError: messages.append({role: user, content: 你的输出不是有效 JSON请只输出一个 JSON 对象。}) continue tool_name action.get(tool) args action.get(args, {}) if tool_name not in TOOLS: messages.append({role: user, content: f未知工具 {tool_name}请从 {list(TOOLS)} 中选择。}) continue result TOOLS[tool_name](**args) messages.append({role: user, content: f工具{result}执行结果: {json.dumps(result, ensure_asciiFalse)}}) if tool_name finish: return result return {status: max_steps_exceeded}注意代码里chat()函数是占位的你可以替换成任意模型的 API。关键点在于循环的每一步拿到 LLM 输出 - 解析 JSON - 执行工具 - 把结果返回给 LLM直到模型调用finish。3.4 第一次跑通预期输出和检查点第一次跑通时模型大概率不会一次就完成任务这是正常的。你不需要它一次到位只需要确认循环能正常推进。你可以把max_steps调大一点比如 10然后观察LLM 是否先调用get_map_info获取地图和起终点信息是否在某一步调用search_path而不是自己硬编路径是否在拿到路径后调用validate_path检查最后是否调用finish返回一个带路径的结论。如果流程能走到finish恭喜你第一个路径规划智能体已经跑通了。后面所有工作都是在这个基础上做强化和调优。4. 真正决定成败的是这些细节Prompt、协议与恢复4.1 系统提示词怎么写才不会让模型“放飞自我”系统提示词在路径规划智能体里扮演的角色比很多人想象的更重要。它不只是一个“角色设定”更是一份操作手册。我建议至少包含以下段落角色与目标明确告诉模型它是“路径规划决策引擎”不是聊天助手。地图规则说明地图是栅格地图0 可通行1 不可通行坐标从 0 开始。工具说明逐个介绍工具每个工具给一个调用示例示例越具体越好。输出格式强制 JSON 格式并且给出模板。决策原则一个原则是“能用 search_path 就不要自己一步一步走”另一个是“拿到路径后必须 validate_path”。下面这个片段可以作为参考你是路径规划智能体。你的任务是在栅格地图上规划从起点到终点的路径。 地图规模是 10x100 表示可通行1 表示障碍物。 你可以使用以下工具 - get_map_info: 获取地图信息、起终点、当前位置 - get_neighbors: 获取某格子的可通行邻居 - search_path: 使用底层搜索算法规划完整路径推荐使用 - validate_path: 验证路径合法性 - finish: 完成任务并返回最终路径 你必须遵循以下规则 1. 只输出一个 JSON 对象格式为 {tool: 工具名, args: {...} } 2. 除非没有可用工具否则必须先获取地图信息再行动 3. 规划路径时优先调用 search_path而不是自己逐步移动 4. 调用 finish 前必须拿到一条已通过 validate_path 的路径这段提示词有几个细节值得注意“推荐使用”这个词我刻意写了就是在给模型做选择倾向引导。你如果不写这句模型可能真的会傻乎乎地逐格走浪费大量轮次。4.2 强制模型输出结构化动作路径规划智能体比普通聊天 Agent 更依赖稳定解析因为你每次输出都要映射成工具调用。这里有个很朴素但有效的方法在提示词末尾加一个 JSON 模板告诉模型照着填。你可以在用户输入里每次都附带当前状态摘要当前状态 - 起点: (0, 0) - 终点: (9, 9) - 当前位置: (0, 0) - 已完成: 获取地图信息 请输出你的下一步动作参考格式 {tool: search_path, args: {start: [0, 0], goal: [9, 9]}}这样做的效果非常明显模型很少再输出游离于 JSON 之外的废话。如果模型偶尔不配合循环里的解析容错机制就派上用场了把“无法解析的响应”作为一个观察结果喂回给模型让它重新输出。我在主循环里加的就是这个逻辑。4.3 LLM 非法输出的兜底机制就算提示词写得很细模型还是会出错这是概率问题你得接受。常见错误有三种JSON 格式错误少了括号、多了一个逗号、把 JSON 码在了 markdown 代码块里。兜底方案是解析失败时重试同时清理掉 json 这种包裹。工具不存在模型自己编了一个工具名。兜底方案是返回错误信息让模型重新选择。参数类型错误把[0, 0]传成了字符串(0,0)或者坐标传成了负数。兜底方案是在工具内部做类型转换和边界检查越界时返回明确的错误提示。你可以在工具函数内部加一个防御性解析函数把所有输入都转成元组并校验范围。这一步虽然琐碎但能让整个循环稳定很多。5. 从静态规划到动态避障实测要点与评估方法5.1 静态地图测试绕障、多起点终点第一个正式测试是在静态地图上跑多组起点和终点。不要只测一组建议把起点和终点分布在障碍物密集区、地图角落、不可达区域覆盖各种情况。常规场景起点终点都在开阔地带模型应该直接调用search_path拿到正确路径。绕障场景起点和终点被 U 形障碍隔开A* 会绕一个圈此时模型要能接受这条绕行路线不能因为路径不是“直线”就反复重试。不可达场景终点被障碍完全围住A* 返回no_path。模型此时应该调用finish反馈失败而不是不停重试。测试时我习惯打印每一步的“LLM 输出 工具调用 工具返回值”把整个决策过程留痕。这比只看最终路径有用得多能帮你定位模型是在哪一步做了错误的决策。5.2 动态障碍给智能体注入“意外”动态避障是路径规划智能体真正闪光的场景。我在静态测试通过后启动了地图更新机制每一轮 Agent 循环结束后有一定概率把一个格子从 0 变成 1模拟突然出现的障碍物。这时智能体应该表现出“感知变化 - 触发重新规划 - 校验新路径”的行为链路。可以这样设计当前路径经过的某个格子突然变成了障碍物工具返回“路径失效”的错误LLM 被强制要求重新调用search_path。一个有趣的实测现象是在没有明确提示词约束的情况下很多模型会忽略“当前地图可能变化”这个设定继续沿用旧的路径直到校验失败。所以我建议在 system prompt 里加一句“地图可能动态变化如果 validate_path 返回 invalid必须重新规划”。这句话能显著提升动态场景下的成功率。动态场景测试建议从小到大先 5 步一变化再 2 步一变化最后每轮都随机变化看 Agent 是否还能保持路径有效。5.3 实用评估指标评估一个路径规划智能体不能只看“最后有没有到达终点”。我在测试中主要记录以下指标指标说明我的经验标准任务成功率到达终点的任务占比静态图 ≥ 95%动态图 ≥ 70%路径长度比智能体最终路径长度 / A* 最优路径长度≤ 1.1平均工具调用次数单个任务调用工具的总数≤ 6 次平均决策时延每轮 LLM 调用耗时因模型而异本地小模型通常 1-3 秒无效调用率解析失败、工具不存在、参数错误占比≤ 10%路径长度比这个指标很有意思它能反映出 LLM 会不会“多走冤枉路”。如果模型总是自己生成路径而不是调用search_path这个指标会立刻恶化。而工具调用次数则能反映模型对工具的理解是否到位调用次数过多说明它在瞎试。6. 踩坑清单、成本控制与后续进阶方向6.1 我实测中最容易翻车的三个问题第一个是模型自作主张“规划”路径。即使工具里有search_path小参数模型有时仍会自己在 JSON 里写一个路径列表而且漏洞百出。我后来不但在提示词里强调还在回调里加了检测如果模型直接输出路径字段而不是调用工具就返回“请使用 search_path 工具”。这样几次之后模型就被纠正过来了。第二个是无限循环。模型做完search_path后又去调get_map_info拿到地图后又不调search_path而是在原地转圈。我加了最大步数限制但这只是兜底。更好的办法是在提示词里明确工作流程get_map_info - search_path - validate_path - finish让流程收敛。第三个是坐标混乱。模型会把(x, y)和(row, col)搞混导致search_path参数传反。这个问题的解决办法不在提示词而在工具层我在search_path内部做了坐标合法性检查一旦越界立刻返回“坐标越界”的错误。工具层能挡住的问题不要指望模型自己觉醒。6.2 时延与 token 成本控制路径规划智能体如果用于机器人现场控制时延非常关键。一轮决策动辄两三秒根本没法做实时避障。我的调优经验是精简上下文不要把整张地图和历史路径全部塞进 messages只保留必要信息。每轮完成后把旧的工具调用结果压缩成一句话摘要。小模型优先如果任务本身不复杂优先用 7B 级本地模型而不是一味上大模型。路径规划对推理要求真没那么高。限制重试次数解析失败重试一次就够了连续失败说明模型能力不足这时候应该换模型或改提示词而不是死磕。用结构化输出有条件的话接一个支持 JSON 结构化输出的接口能省掉很多解析失败的消耗。成本控制的逻辑也一样工具结果的裁剪是省 token 的最大杠杆。我最开始会把完整路径列表返回给模型一来一回上千 token后来改成只返回路径长度和关键途经点成本直接降下去一半。6.3 进阶方向视觉输入、多智能体协同、全覆盖规划当你把上面这套最小实现跑通以后可以往三个方向扩展。这三个方向也是社区里大家问得比较多的我单独说一下。一是视觉大语言模型的接入。把“地图数据”换成“摄像头画面”让视觉语言模型识别障碍物、目标物再交给路径规划工具执行。适合做巡检机器人、动态避障小车的原型相当于给机器人装了一双会说话的眼睛。二是多智能体协同。多台机器人的路径规划不能各走各的不然会撞车。你可以把上面这个单智能体包装成一个可独立调度的单元再设计一个协调器智能体让多个单元共享地图和位置信息避免死锁。这个方向复杂度高但做出来很有成就感。三是全覆盖路径规划。如果你的目标是扫地机器人、喷漆机器人、植保无人机这类需要覆盖整个工作区域的场景就不能只规划一条点对点路径了而要用弓字形扫描、转圈扩展这类策略。LLM 在这里可以充当“区域分解器”把不规则区域拆成多个可覆盖的子区域再逐个调用路径规划工具。6.4 如果不想从零手写可以看看哪些现成工具自己手写一遍主循环对理解 Agent 原理帮助巨大但如果你时间紧张或者想快速做业务原型也可以参考社区里成熟的智能体编排工具。像 Coze、Dify 这类平台本身就提供了 Agent 编排、工具注册、会话管理的能力你把search_path封装成一个自定义工具上传进去也能搭出一个路径规划智能体只是底层逻辑不够透明调试起来不如自己写的代码方便。我的建议是教学和原理验证自己写业务交付用现成平台两条腿走路。我个人目前的习惯是凡是涉及路径规划核心算法的部分绝对不用 LLM 直接生成而是自己掌握。LLM 是脑算法是手脑可以换更好的但手必须稳。最后说一个我在多个项目里验证过的经验真正让路径规划智能体变好用的往往不是换更大的模型而是把工具边界划得更清楚、把提示词里的流程写得更死、把异常恢复做得更厚。你不需要急着追赶各种新框架先把手里的 Agent 循环打磨稳再去接视觉、接多智能体路会顺很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价