一、前言

        平时我们出门坐公交,打开地图输入起点和终点,几秒钟就能跳出好几条路线,有的最快、有的少换乘、有的走路少。一直都很好奇,这种路径规划的实现方式到底是怎么实现的,是什么算法支撑的,总在琢磨,但最后又在不解中慢慢就遗忘了,往往就是这种日常应用的功能看着很普通,适应了之后好像理所应当就该这样,但背后其实是一整套非常复杂的技术在支撑。从最早只能按固定线路查询,到现在能自动避开拥堵、理解我们“不想走路”、“少换乘” 的要求,公交路线规划早就不是简单算个最短距离那么简单了。

        尤其是近几年大模型快速普及,传统的算法又迎来了一次全新升级。以前的系统只能处理死板的数据和规则,现在却能听懂人话、看懂实时路况、还能根据不同人的习惯给出个性化方案。对城市交通管理者来说,智能规划能优化线路、提高运力;对普通用户来说,就是出行更省心、更快、更舒服。今天我们一起好好琢磨,从最基础的原理讲起,一步步带的搞懂:公交车站点线路智能规划到底是怎么实现的,大模型在里面又起到了哪些关键作用,哪怕我们之前没接触过算法和模型,也能通俗的看明白。

二、核心基础

1. 公交路线智能规划

        公交站点线路的智能规划,本质是在给定公交站点网络中,包括站点、线路、站点间通行时间和距离、发车频率等信息,以起始站和终点站为输入,通过算法或模型输出最优以及次优的公交出行方案。这里的最优可定义为耗时最短、换乘次数最少、步行距离最短、费用最低等单一目标,也可以是多目标加权的综合最优。

        从应用场景来看,它是城市智慧交通的核心模块之一,常见于公交 APP、城市交通调度系统、出行服务平台等。而大模型的融入,让这一过程从基于固定规则或传统算法升级为“数据驱动 + 语义理解 + 动态决策”,既能处理结构化的站点数据,也能理解非结构化的用户需求,如“我想避开早高峰拥堵的站点”、“优先走有空调的公交线路”。

2. 基础术语解析

为了强化快速理解,我们先梳理核心术语:

  • 站点网络(Graph):可以把整个城市的公交系统想象成一张“网”,每个公交站点是“节点(Node)”,站点之间的公交线路或步行路径是“边(Edge)”,边会附带权重,如通行时间、距离、是否需要换乘。这是所有路线规划的基础数据结构。
  • 静态数据:不随时间变化的基础信息,如站点名称、站点经纬度、公交线路的固定停靠站、站点间的基础距离。
  • 动态数据:实时变化的信息,如公交实时位置、路段拥堵情况、站点候车时间、天气对通行的影响,如下雨天步行意愿降低。
  • 最短路径:狭义上指物理距离最短,广义上可扩展为 “综合成本最低”(如时间成本 = 乘车时间 + 换乘时间 + 步行时间)。
  • 大模型(LLM):这里特指具备自然语言理解、知识推理、数据挖掘能力的大语言模型,也包括融合了空间数据的多模态大模型。
  • 语义化需求:用户用自然语言表达的非标准化需求,如“从 XX 小区到 XX 医院,尽量少走路,下午 3 点左右出发”。

3. 大模型赋能的规划对比

3.1 数据处理能力

  • 传统公交规划:局限于结构化数据。只能识别和处理如站点ID、经纬度坐标、固定时刻表等标准化数字信息,无法理解文本描述或图像信息。
  • 大模型赋能的规划:具备多模态融合处理能力。不仅能处理结构化数据,还能直接解析非结构化数据,如乘客的自然语言反馈、社交媒体上的路况文本描述、甚至实时监控视频中的拥堵情况,实现全量数据利用。

3.2 需求理解

  • 传统公交规划:依赖固定格式输入。用户必须提供精确的起点和终点,通常是站点编号或标准地名,系统无法理解模糊指令或复杂约束。
  • 大模型赋能的规划:支持深层语义理解。用户可以像与人对话一样提出需求,例如“我想避开XX路正在施工的路段”或“找一条风景好且不太挤的路线”,系统能精准提取意图并转化为规划约束。

3.3 决策灵活性

  • 传统公交规划:基于预设静态规则。逻辑通常是写死的,如“换乘次数最少”或“距离最短”,无法根据实时环境变化自动调整权重,缺乏自适应能力。
  • 大模型赋能的规划:实现动态策略调整。系统能结合历史规律与实时数据,如突发暴雨、大型活动散场,动态重构决策逻辑。例如在早高峰自动优先推荐“大站快车”,而在平峰期优先推荐“覆盖率广”的线路。

3.4 个性化适配

  • 传统公交规划:采用千人一面的模式。所有用户面对相同的算法逻辑,无法区分老人、通勤族或游客的不同偏好。
  • 大模型赋能的规划:提供千人千面的服务。通过长期学习用户的行为习惯,系统能主动适配个人偏好。例如,为携带行李的用户推荐电梯覆盖多的站点,为赶时间的用户推荐虽然需换乘但总耗时更少的方案。

3.5 异常场景处理

  • 传统公交规划:应对能力脆弱。一旦遇到规则库之外的突发状况,如临时封站、道路塌方,系统往往无法生成有效方案,甚至直接报错或提供不可行路线。
  • 大模型赋能的规划:具备强推理与泛化能力。面对未知异常,大模型可以利用通用知识库进行逻辑推理,迅速规划出临时的替代方案,如建议“地铁+共享单车”的组合接驳,并给出解释性建议,保障出行连续性。

三、公交路线规划的基础原理

1. 图论与最短路径算法

所有公交路线规划的底层逻辑都基于图论,我们先从最基础的 “图” 开始理解:

1.1 公交网络的图建模

假设我们有如下简化的公交网络:

  • 站点:A(小区)、B(商场)、C(地铁站)、D(医院)
  • 线路 1:A→B→C(每段耗时 5 分钟)
  • 线路 2:C→D(耗时 8 分钟)
  • 步行路径:A→C(耗时 20 分钟)

我们可以把这个网络建模为 “无向图”,忽略行驶方向,仅关注连通性,用表格表示节点和边的权重(耗时):

起点 终点 权重(耗时 / 分钟) 类型
A B 5 公交
B C 5 公交
C D 8 公交
A C 20 步行

在代码中,我们可以用networkx库来构建这个图:

import networkx as nx
import matplotlib.pyplot as plt

# 1. 创建无向图
G = nx.Graph()

# 2. 添加节点(公交站点)
nodes = ["A", "B", "C", "D"]
G.add_nodes_from(nodes)

# 3. 添加边(站点间的连接),并设置权重(耗时)
edges = [
    ("A", "B", {"weight": 5, "type": "公交"}),
    ("B", "C", {"weight": 5, "type": "公交"}),
    ("C", "D", {"weight": 8, "type": "公交"}),
    ("A", "C", {"weight": 20, "type": "步行"})
]
G.add_edges_from(edges)

# 4. 可视化图(输出图片)
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 解决中文显示
pos = nx.spring_layout(G, seed=42)  # 固定布局
nx.draw(G, pos, with_labels=True, node_size=2000, node_color="lightblue", font_size=12)

# 添加边的权重标签
edge_labels = {(u, v): f"{d['weight']}分钟({d['type']})" for u, v, d in G.edges(data=True)}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=10)

plt.title("简化公交网络拓扑图")
plt.savefig("bus_network_graph.png", dpi=300, bbox_inches="tight")
plt.show()

代码说明:

  • networkx是 Python 中处理图结构的核心库,适合快速上手;
  • nx.Graph()创建无向图,若需考虑公交行驶方向,如单行线,可改用nx.DiGraph()画有向图;
  • add_nodes_from添加所有站点节点,add_edges_from添加站点间的连接,并附带“权重(耗时)”和“类型(公交/步行)”属性;
  • matplotlib负责可视化,最终生成bus_network_graph.png图片,直观展示公交网络结构。

结果图示:

考虑公交行驶方向,可用带箭头的有向图展示:

2. 经典最短路径算法:Dijkstra 算法

        Dijkstra 算法是公交规划中最常用的基础算法,核心逻辑是“从起点出发,逐步找到到每个节点的最短路径”,适合权重为非负数的场景,特别是公交耗时、距离均为正数。

我们用代码实现 Dijkstra 算法,求解从 A 到 D 的最短耗时路径:

def dijkstra_shortest_path(graph, start, end):
    """
    实现Dijkstra算法,返回从起点到终点的最短路径和总权重
    :param graph: networkx构建的图
    :param start: 起始节点
    :param end: 终止节点
    :return: 最短路径列表、总权重
    """
    # 初始化:距离字典(起点到各节点的距离),前驱节点字典
    distance = {node: float("inf") for node in graph.nodes}
    distance[start] = 0  # 起点到自身距离为0
    predecessor = {node: None for node in graph.nodes}
    unvisited = set(graph.nodes)  # 未访问节点集合

    print("=" * 60)
    print("Dijkstra算法计算过程")
    print("=" * 60)
    print()

    iteration = 0
    while unvisited:
        iteration += 1
        # 选择当前距离最小的未访问节点
        current_node = min(unvisited, key=lambda node: distance[node])
        unvisited.remove(current_node)

        print(f"第{iteration}步: 选中节点 {current_node} (距离: {distance[current_node]})")

        # 若当前节点是终点,提前终止
        if current_node == end:
            print(f"✓ 已到达终点,算法完成")
            break

        # 遍历当前节点的邻居
        for neighbor in graph.neighbors(current_node):
            if neighbor in unvisited:
                edge_weight = graph[current_node][neighbor]["weight"]
                edge_type = graph[current_node][neighbor]["type"]
                new_distance = distance[current_node] + edge_weight

                # 更新距离和前驱节点
                if new_distance < distance[neighbor]:
                    print(f"  更新 {neighbor}: {distance[current_node]} + {edge_weight} = {new_distance} (原距离{distance[neighbor]})")
                    distance[neighbor] = new_distance
                    predecessor[neighbor] = current_node
        print()

    # 回溯构建最短路径
    path = []
    current = end
    while current is not None:
        path.append(current)
        current = predecessor[current]
    path.reverse()  # 反转得到从起点到终点的路径

    # 若路径只有终点(无连通路径),返回空
    if path[0] != start:
        return [], float("inf")

    return path, distance[end]

# 调用函数求解A到D的最短路径
shortest_path, total_time = dijkstra_shortest_path(G, "A", "D")
print(f"从A到D的最短耗时路径:{shortest_path}")
print(f"总耗时:{total_time}分钟")

算法说明:

  • 初始化阶段:给所有节点设置“无穷大”的初始距离,仅起点距离为 0;
  • 迭代阶段:每次选择“未访问且距离最小”的节点,更新其邻居的距离,若经过当前节点到邻居的距离更短;
  • 回溯阶段:从终点反向遍历前驱节点,构建完整路径;
  • 结果验证:A→B→C→D 的总耗时 = 5+5+8=18 分钟,远小于 A→C→D 的 20+8=28 分钟,符合预期。

输出结果:

============================================================
Dijkstra算法计算过程
============================================================

第1步: 选中节点 A (距离: 0)
  更新 B: 0 + 5 = 5 (原距离inf)
  更新 C: 0 + 20 = 20 (原距离inf)

第2步: 选中节点 B (距离: 5)
  更新 C: 5 + 5 = 10 (原距离20)

第3步: 选中节点 C (距离: 10)
  更新 D: 10 + 8 = 18 (原距离inf)

第4步: 选中节点 D (距离: 18)
✓ 已到达终点,算法完成
从A到D的最短耗时路径:['A', 'B', 'C', 'D']
总耗时:18分钟

正常情况下耗时18分钟,如果公交拥堵,考虑备选路线:

3. 动态数据的融合原理

        实际公交规划中,静态的基础耗时无法反映真实情况,需要融合动态数据,如拥堵导致耗时增加、公交晚点。核心原理是:

  • 1. 实时获取动态数据,如通过公交 API 获取某路段的拥堵系数;
  • 2. 动态调整图中边的权重,如拥堵系数 = 2,则原耗时 ×2;
  • 3. 重新运行最短路径算法,得到贴合实时路况的路线。

示例代码(模拟动态拥堵调整):

# 模拟获取实时拥堵数据(实际中可通过API获取)
congestion_factor = {
    ("A", "B"): 1.5,  # A→B拥堵,耗时×1.5
    ("B", "C"): 1.2,  # B→C轻微拥堵,耗时×1.2
    ("C", "D"): 1.0,  # C→D无拥堵
    ("A", "C"): 1.0   # 步行无拥堵
}

# 动态更新图的边权重
for (u, v), factor in congestion_factor.items():
    original_weight = G[u][v]["weight"]
    G[u][v]["weight"] = original_weight * factor

# 重新计算最短路径
updated_path, updated_time = dijkstra_shortest_path(G, "A", "D")
print(f"拥堵后从A到D的最短路径:{updated_path}")
print(f"拥堵后总耗时:{updated_time}分钟")

输出结果:

============================================================
Dijkstra算法计算过程
============================================================

第1步: 选中节点 A (距离: 0)
  更新 B: 0 + 7.5 = 7.5 (原距离inf)
  更新 C: 0 + 20.0 = 20.0 (原距离inf)

第2步: 选中节点 B (距离: 7.5)
  更新 C: 7.5 + 6.0 = 13.5 (原距离20.0)

第3步: 选中节点 C (距离: 13.5)
  更新 D: 13.5 + 8.0 = 21.5 (原距离inf)

第4步: 选中节点 D (距离: 21.5)
✓ 已到达终点,算法完成
拥堵后从A到D的最短路径:['A', 'B', 'C', 'D']
拥堵后总耗时:21.5分钟

四、大模型核心价值体现

        大模型的核心价值:从规则驱动到智能驱动,传统公交规划的痛点是刚性,所有决策依赖预设规则,无法处理复杂、个性化、非标准化的需求。而大模型的融入,解决了以下核心问题:

1. 语义化需求的解析

        用户实际使用时,不会输入“起点 A + 终点 D”,而是说“我从 XX 小区(A)到 XX 医院(D),下午 5 点下班高峰出发,尽量少换乘,不想走超过 5 分钟的路”。大模型能将这种自然语言需求转化为规划算法可识别的结构化参数:

  • 起点:A(XX 小区)
  • 终点:D(XX 医院)
  • 出发时间:17:00(高峰时段)
  • 约束条件:换乘次数≤1,步行距离≤5 分钟(对应步行耗时≤5 分钟)

2. 多目标决策的智能加权

公交规划的最优是多维度的,如耗时、换乘、步行,不同用户的偏好不同:

  • 老年人:优先少步行、少换乘,对耗时不敏感;
  • 上班族:优先耗时最短,可接受少量换乘+步行;
  • 学生:优先费用最低,如公交卡优惠。

大模型可通过学习用户历史行为,或通过对话交互,自动调整多目标的权重,如给步行距离设置更高的权重惩罚,生成个性化路线。

3. 异常场景的推理与应对

临时封站、线路改道、极端天气等异常场景,超出了传统规则的覆盖范围。大模型可通过知识推理给出替代方案:

  • 例:“XX 路因施工封站,从 A 到 D 的替代路线是什么?”
  • 大模型推理:封站影响 B→C 的公交,因此推荐 A→C(步行)→D,或 A→B→其他站点→C→D。

4. 自然语言的结果输出

传统规划仅输出“站点列表”,大模型可将结果转化为通俗易懂的自然语言:

  • 算法输出:A→B→C→D,耗时 18 分钟;
  • 大模型输出:“推荐您乘坐 1 路公交(A 站→B 站→C 站),换乘 2 路公交(C 站→D 站),全程 18 分钟,步行仅需 2 分钟(仅站点内换乘),下午 5 点高峰时段该路线拥堵概率较低。”

五、整体执行流程

  • 大模型不替代传统最短路径算法,而是“前端解析 + 后端优化 + 结果输出”;
  • 传统算法负责“高效求解路径”,大模型负责“理解需求、优化决策、友好输出”;
  • 核心是“算法 + 大模型”的协同,兼顾效率与智能。

流程说明:

1. 用户输入自然语言需求:
用户用日常语言提出出行需求,例如“从西湖到灵隐寺,想要少走路,优先地铁”。

2. 大模型语义解析:
大模型理解用户意图,从自然语言中提取关键信息,如起点、终点、时间、偏好(少步行、优先地铁、避开拥堵等)。

3. 结构化参数输出:
将解析结果转化为程序可处理的标准化格式,例如:{start: "西湖", end: "灵隐寺", constraints: ["少步行"], preferences: ["优先地铁"]}。

4. 融合静态+动态公交数据:
整合基础公交线路数据(站点、票价、运营时间)和实时动态数据(拥堵情况、到站预测、临时管制)。

5. 构建/更新公交网络图:
将公交线路抽象为图结构:站点是节点,公交线路是边,并根据实时数据动态更新边的权重(时间、拥挤度等)。

6. 传统最短路径算法求解候选路径:
使用Dijkstra或A*算法在公交网络图上搜索出若干条候选路线,每条路线包含换乘方案、预计时间、步行距离等。

7. 大模型多目标评估+个性化排序:
大模型结合用户偏好(少步行、少换乘、低票价等)对候选路线进行综合评分,并重新排序,选出最适合用户的路线。

8. 大模型生成自然语言路线说明:
将排序后的路线转化为清晰易懂的文字说明,包含起点、换乘点、步行指引、预计耗时等细节。

9. 输出最终路线方案:
将完整的路线说明返回给用户,支持文本展示、语音播报或地图可视化。

六、完整示例

使用算法结合大模型,根据用户需求"从A_小区到D_医院,下午5点高峰时段出发,换乘次数不要超过1次,步行时间尽量少,优先耗时最短的路线",实现最优路线规划;

import os
import networkx as nx
import matplotlib.pyplot as plt
from openai import OpenAI
from dotenv import load_dotenv
import json

# 使用腾讯混元大模型
api_key = os.getenv('TENCENT_API_KEY', 'sk-bWlJPKjBrSF********8Ze')
client = OpenAI(
    api_key=api_key,
    base_url="https://api.hunyuan.cloud.tencent.com/v1",
)

# ===================== 第一步:构建基础公交网络 =====================
def build_bus_network():
    """构建包含更多站点的公交网络(贴近真实场景)"""
    G = nx.Graph()
    # 站点列表(含小区、商场、地铁、医院、学校)
    nodes = ["A_小区", "B_商场", "C_地铁1号线", "D_医院", "E_学校", "F_公园", "G_地铁站2号线"]
    G.add_nodes_from(nodes)
    # 边列表:(起点, 终点, 权重(耗时/分钟), 类型, 线路号)
    edges = [
        ("A_小区", "B_商场", {"weight": 5, "type": "公交", "line": "1路"}),
        ("B_商场", "C_地铁1号线", {"weight": 5, "type": "公交", "line": "1路"}),
        ("C_地铁1号线", "D_医院", {"weight": 8, "type": "公交", "line": "2路"}),
        ("A_小区", "C_地铁1号线", {"weight": 20, "type": "步行"}),
        ("C_地铁1号线", "E_学校", {"weight": 10, "type": "公交", "line": "3路"}),
        ("E_学校", "D_医院", {"weight": 15, "type": "公交", "line": "4路"}),
        ("B_商场", "F_公园", {"weight": 7, "type": "公交", "line": "5路"}),
        ("F_公园", "G_地铁站2号线", {"weight": 6, "type": "公交", "line": "5路"}),
        ("G_地铁站2号线", "D_医院", {"weight": 12, "type": "地铁", "line": "2号线"})
    ]
    G.add_edges_from(edges)
    return G

# ===================== 第二步:大模型解析自然语言需求 =====================
def parse_user_request_with_llm(user_request):
    """
    使用大模型解析用户自然语言需求,提取结构化参数
    :param user_request: 用户输入的自然语言需求
    :return: 结构化参数字典(起点、终点、约束条件、偏好)
    """
    prompt = f"""
    请你作为公交路线规划助手,解析用户的自然语言需求,提取以下结构化信息:
    1. 起点(精确提取站点名称,如"A_小区"、"B_商场"、"C_地铁1号线"、"D_医院"、"E_学校"、"F_公园"、"G_地铁站2号线");
    2. 终点(精确提取站点名称,如"A_小区"、"B_商场"、"C_地铁1号线"、"D_医院"、"E_学校"、"F_公园"、"G_地铁站2号线");
    3. 约束条件(如换乘次数≤1、步行耗时≤5分钟、避免拥堵路段);
    4. 用户偏好(如优先少换乘、优先耗时最短、优先步行少、优先地铁)。

    用户需求:{user_request}

    输出格式要求:
    以JSON格式输出,键为"start_point", "end_point", "constraints", "preferences",
    constraints和preferences为列表形式,若无则为空列表。
    
    重要提示:
    - 必须从用户输入中准确提取起点和终点的完整站点名称
    - 起点和终点必须是下列站点之一:A_小区、B_商场、C_地铁1号线、D_医院、E_学校、F_公园、G_地铁站2号线
    - 严禁编造站点名称或使用省略号
    
    示例输出:
    {{
        "start_point": "A_小区",
        "end_point": "D_医院",
        "constraints": ["换乘次数≤1", "步行耗时≤5分钟"],
        "preferences": ["优先耗时最短"]
    }}
    """

    # 调用腾讯混元API
    response = client.chat.completions.create(
        model="hunyuan-lite",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.1  # 低随机性,保证解析结果稳定
    )

    # 提取并返回JSON结果
    try:
        content = response.choices[0].message.content.strip()
        # 清理可能存在的markdown代码块标记
        if content.startswith("```json"):
            content = content[7:]
        if content.startswith("```"):
            content = content[3:]
        if content.endswith("```"):
            content = content[:-3]
        content = content.strip()
        
        result = json.loads(content)
        
        # 验证站点名称是否在图节点中
        valid_nodes = ["A_小区", "B_商场", "C_地铁1号线", "D_医院", "E_学校", "F_公园", "G_地铁站2号线"]
        if result.get("start_point") not in valid_nodes or result.get("end_point") not in valid_nodes:
            # 如果解析的站点名称无效,返回默认值
            print(f"警告:解析的站点名称无效,start={result.get('start_point')}, end={result.get('end_point')}")
            return {
                "start_point": "A_小区",
                "end_point": "D_医院",
                "constraints": result.get("constraints", []),
                "preferences": result.get("preferences", [])
            }
        
        return result
    except json.JSONDecodeError as e:
        # 解析失败时返回默认值
        print(f"JSON解析失败: {e}, 返回内容: {content}")
        return {
            "start_point": "A_小区",
            "end_point": "D_医院",
            "constraints": ["换乘次数≤1"],
            "preferences": ["优先耗时最短"]
        }

# ===================== 第三步:Dijkstra算法求解候选路径 =====================
def dijkstra_with_constraints(graph, start, end, constraints=None, preferences=None):
    """
    带约束和偏好的最短路径求解
    :param graph: 公交网络图
    :param start: 起点
    :param end: 终点
    :param constraints: 约束条件列表
    :param preferences: 偏好列表
    :return: 最优路径、总耗时、路径详情(含线路、类型)
    """
    if constraints is None:
        constraints = []
    if preferences is None:
        preferences = []

    # 1. 先获取所有可能的路径(简化版:仅获取前3条最短路径)
    all_paths = list(nx.shortest_simple_paths(graph, start, end, weight="weight"))[:3]
    path_details = []

    # 2. 为每条路径计算详细信息(总耗时、换乘次数、步行耗时)
    for path in all_paths:
        total_time = 0
        transfer_count = 0
        walk_time = 0
        last_line = None
        line_details = []

        for i in range(len(path)-1):
            u = path[i]
            v = path[i+1]
            edge_data = graph[u][v]
            time = edge_data["weight"]
            line = edge_data.get("line", "步行")
            type_ = edge_data["type"]

            total_time += time
            if type_ == "步行":
                walk_time += time
            else:
                if line != last_line and last_line is not None:
                    transfer_count += 1
                last_line = line
                line_details.append(f"{line}({type_}): {u}→{v} ({time}分钟)")

        # 3. 构建路径详情字典
        path_info = {
            "path": path,
            "total_time": total_time,
            "transfer_count": transfer_count,
            "walk_time": walk_time,
            "line_details": line_details
        }
        path_details.append(path_info)

    # 4. 根据约束条件过滤路径
    filtered_paths = []
    for info in path_details:
        valid = True
        # 处理换乘次数约束
        for constraint in constraints:
            if "换乘次数≤" in constraint:
                max_transfer = int(constraint.split("≤")[1])
                if info["transfer_count"] > max_transfer:
                    valid = False
            if "步行耗时≤" in constraint:
                max_walk = int(constraint.split("≤")[1])
                if info["walk_time"] > max_walk:
                    valid = False
        if valid:
            filtered_paths.append(info)

    # 5. 根据偏好排序
    if not filtered_paths:
        filtered_paths = path_details  # 无符合约束的路径,返回所有

    if "优先耗时最短" in preferences:
        filtered_paths.sort(key=lambda x: x["total_time"])
    elif "优先少换乘" in preferences:
        filtered_paths.sort(key=lambda x: x["transfer_count"])
    elif "优先步行少" in preferences:
        filtered_paths.sort(key=lambda x: x["walk_time"])

    # 返回最优路径(第一条)
    if filtered_paths:
        best_path = filtered_paths[0]
        return best_path["path"], best_path["total_time"], best_path["line_details"]
    else:
        return [], 0, []

# ===================== 第四步:大模型生成自然语言路线说明 =====================
def generate_route_description_with_llm(path, total_time, line_details, user_preferences):
    """
    使用大模型将路径信息转化为自然语言说明
    :param path: 最优路径列表
    :param total_time: 总耗时
    :param line_details: 线路详情
    :param user_preferences: 用户偏好
    :return: 自然语言路线说明
    """
    prompt = f"""
    请你作为公交路线规划助手,根据以下信息生成通俗易懂的路线说明:
    1. 最优路径:{path}
    2. 总耗时:{total_time}分钟
    3. 线路详情:{line_details}
    4. 用户偏好:{user_preferences}

    要求:
    - 语言简洁明了,步骤清晰;
    - 突出用户偏好(如优先少换乘);
    - 给出温馨提示(如换乘站点、步行注意事项);
    - 避免使用专业术语,适合普通用户理解。
    """

    response = client.chat.completions.create(
        model="hunyuan-lite",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7  # 适度随机性,让语言更自然
    )

    return response.choices[0].message.content

# ===================== 主函数:整合所有流程 =====================
def main():
    # 1. 构建公交网络
    bus_graph = build_bus_network()

    # 2. 可视化公交网络(保存图片)
    plt.rcParams["font.sans-serif"] = ["SimHei"]
    pos = nx.spring_layout(bus_graph, seed=42)
    nx.draw(bus_graph, pos, with_labels=True, node_size=1500, node_color="lightgreen", font_size=8)
    edge_labels = {(u, v): f"{d.get('line', '步行')}\n{d['weight']}分钟" for u, v, d in bus_graph.edges(data=True)}
    nx.draw_networkx_edge_labels(bus_graph, pos, edge_labels=edge_labels, font_size=6)
    plt.title("真实场景公交网络拓扑图")
    plt.savefig("real_bus_network.png", dpi=300, bbox_inches="tight")
    plt.show()

    # 3. 用户输入自然语言需求
    user_request = "我从A_小区到D_医院,下午5点高峰时段出发,换乘次数不要超过1次,步行时间尽量少,优先耗时最短的路线"
    print(f"用户需求:{user_request}\n")

    # 4. 大模型解析需求
    parsed_info = parse_user_request_with_llm(user_request)
    print(f"大模型解析的结构化参数:{parsed_info}\n")

    # 5. 求解最优路径
    start = parsed_info["start_point"]
    end = parsed_info["end_point"]
    constraints = parsed_info["constraints"]
    preferences = parsed_info["preferences"]

    best_path, total_time, line_details = dijkstra_with_constraints(
        bus_graph, start, end, constraints, preferences
    )

    print(f"算法求解的最优路径:{best_path}")
    print(f"总耗时:{total_time}分钟")
    print(f"线路详情:{line_details}\n")

    # 6. 大模型生成自然语言说明
    route_description = generate_route_description_with_llm(best_path, total_time, line_details, preferences)
    print("=== 最终路线推荐 ===")
    print(route_description)

if __name__ == "__main__":
    main()

代码重点说明:

  • 构建公交网络:build_bus_network函数构建了更贴近真实场景的网络,包含公交、地铁、步行三种类型,增加了线路号、站点用途(小区 / 医院)等属性;
  • 大模型解析需求:parse_user_request_with_llm函数通过 Prompt 引导大模型将自然语言需求转化为结构化参数,核心是“Prompt 工程”,明确输出格式(JSON),降低解析难度;
  • 带约束的路径求解:dijkstra_with_constraints函数在传统 Dijkstra 基础上,增加了约束过滤(如换乘次数)和偏好排序(如优先耗时最短),返回路径的详细信息(线路、耗时、换乘次数);
  • 大模型生成说明:generate_route_description_with_llm函数将算法输出的冰冷数据转化为人性化说明,提升用户体验;
  • 主函数整合:串联所有步骤,从构建网络→解析需求→求解路径→生成说明,形成完整的智能规划流程。

输出结果:

用户需求:我从A_小区到D_医院,下午5点高峰时段出发,换乘次数不要超过1次,步行时间尽量少,优先耗时最短的路线

大模型解析的结构化参数:{'start_point': 'A_小区', 'end_point': 'D_医院', 'constraints': ['换乘次数≤1'], 'preferences': ['优先耗时最短']}

算法求解的最优路径:['A_小区', 'B_商场', 'C_地铁1号线', 'D_医院']
总耗时:18分钟
线路详情:['1路(公交): A_小区→B_商场 (5分钟)', '1路(公交): B_商场→C_地铁1号线 (5分钟)', '2路(公交): C_地铁1号线→D_医院 (8分钟)']

=== 最终路线推荐 ===
根据您的需求,这里有一条最优的公交路线供您参考:

**路线概述**:

* **起始站**:A_小区
* **终点站**:D_医院

**行驶路线及时间**:

1. 从A_小区出发,乘坐1路公交直达B_商场,全程约5分钟。
2. 在B_商场换乘另一辆1路公交,直接前往C_地铁1号线站,用时也是5分钟。此时,您已经完成了第一次换乘。
3. 在C_地铁1号线站乘坐2路公交,直达D_医院,用时8分钟。

**总耗时**:整个行程大约需要18分钟,符合您的要求。

**温馨提示**:

* 在换乘时,请注意安全,确保随身携带的物品安全。
* 地铁站内请遵守公共交通规则,文明乘车。
* 如有需要,您可以在途中适当休息,祝您旅途愉快!

这条路线严格遵循了您的优先耗时最短的偏好,并且语言简洁明了,方便您理解。

真实场景公交网络拓扑图:

七、总结

        简单说,公交规划的本质,就是把整个城市的公交网看成一张图:站点是节点,线路是连线,时间、距离、换乘都是权重。以前靠 Dijkstra经典最短路径算法,只能算最快、最近,规则死板、不够聪明。而大模型一加入,整个系统直接升级:它能听懂我们用自然语言说的“少走路、少换乘、避开高峰”,把模糊需求变成算法能看懂的结构化条件;还能结合实时拥堵、天气、施工这些动态信息,自动调整路线权重。

        整个流程也很清晰:先采集公交数据、建好路网,再用大模型解析用户需求,接着用传统算法算出候选路线,最后再由大模型把结果翻译成通俗易懂的出行建议。既保留了算法算得快、算得准的优点,又拥有大模型会理解、会推理、会说话的能力。也是没有想到今天的知识在无意间解答了曾经的疑虑,这或许就是学习的好处吧!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐