Langchain + Neo4j + DeepSeek 实现旅游行程规划
Langchain + Neo4j + DeepSeek 实现旅游行程规划
最终在neo4j中的效果如下: 
运行本地测试 测试结果如下: 问题1:我在深圳,有10天假期,想去贵州游玩,请推荐几个景点,顺便安排下行程 


问题2:贵阳有哪些景区,交通方便吗 

前些时间,家人准备计划完成今年的出游任务,但只提出了几个旅游景点,剩下的就是我的了,家里小孩比较小,所以就放弃了跟团的打算,准备自由行,为了规划行程,各种查询......
最近根据前段时间的“痛苦”经历,也为了了解点新东西,就使用python做了个旅游行程规划的简单版。
1、准备各种数据,我在准备时把景点、交通枢纽、城市、交通方式、票务等作为节点,然后创建关联关系,以备后期查询。
CREATE (tm_bus:TransportMode {mode_id: 'bus', name: '公交', avg_speed_kmh: 30});CREATE (gys:City {code: 'GYS', name: '贵阳市', province: '贵州'});CREATE (gy_east:TransportHub {hub_id: 'gy_east_rail', name: '贵阳东站', type: 'train_station', city_code: 'GYS'});CREATE (qygz:Attraction {id: 'gys_qygz', name: '青岩古镇', city: '贵阳市', district: '青岩古镇', category: ['古镇','文化'], description: '青岩古镇'});CREATE (tr_qygz:TicketRule {rule_id: 'qygz_all_09_17', start_time: '09:00', end_time: '17:00', valid_days: '1-7', price: 0.0, currency: 'CNY', ticket_type: '门票', is_peak: false});MATCH (a:Attraction {id: 'gys_qygz'}), (c:City {code: 'GYS'}) CREATE (a)-[:LOCATED_IN]->(c);MATCH (a:Attraction {id: 'gys_qygz'}), (tr:TicketRule {rule_id: 'qygz_all_09_17'}) CREATE (a)-[:HAS_TICKET_RULE {booking_lead_hours: 0}]->(tr);MATCH (a:Attraction {id: 'gys_qygz'}), (h:TransportHub {hub_id: 'gy_east_rail'}) CREATE (a)-[:TO_HUB {duration_min: 80, distance_km: 38.5, mode_ref: 'bus'}]->(h),(h)-[:TO_ATT {duration_min: 80, distance_km: 38.5, mode_ref: 'bus'}]->(a), (a)-[:TO_HUB {duration_min: 30, distance_km: 38.5, mode_ref: 'car'}]->(h),(h)-[:TO_ATT {duration_min: 30, distance_km: 38.5, mode_ref: 'car'}]->(a), (a)-[:TO_HUB {duration_min: 50, distance_km: 38.5, mode_ref: 'subway'}]->(h),(h)-[:TO_ATT {duration_min: 50, distance_km: 38.5, mode_ref: 'subway'}]->(a);// 贵阳东站 ↔ 贵阳北站 (地铁,双向)MATCH (h1:TransportHub {hub_id: 'gy_east_rail'}), (h2:TransportHub {hub_id: 'gy_north_rail'})CREATE (h1)-[:INTER_CITY { mode_ref: 'subway', duration_min: 20, price: 10, freq_min: 5, departure_station: '贵阳东站', arrival_station: '贵阳北站'}]->(h2),(h2)-[:INTER_CITY { mode_ref: 'subway', duration_min: 20, price: 10, freq_min: 5, departure_station: '贵阳北站', arrival_station: '贵阳东站'}]->(h1);
2、编写相关查询工具:
from langchain_neo4j import Neo4jGraphfrom neo4j import GraphDatabasefrom agent.utils.env_utils import NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD, NEO4J_DATABASE# 创建Neo4jGraph实例(用于简单查询)# refresh_schema=False:无 APOC 插件时也能连接;安装 APOC 后可改为 True 或之后调用 refresh_schema()graph = Neo4jGraph(url=NEO4J_URI,username=NEO4J_USERNAME,password=NEO4J_PASSWORD,database=NEO4J_DATABASE,refresh_schema=False,)# 原生驱动(用于复杂查询)driver = GraphDatabase.driver(NEO4J_URI,auth=(NEO4J_USERNAME, NEO4J_PASSWORD),database=NEO4J_DATABASE)
# tools.pyfrom langchain_core.tools import toolfrom agent.utils.neo4j_config import driverimport re@tooldef route_planner_tool(start_attraction: str, end_attraction: str):"""规划跨城行程(含市内+跨城+市内)参数:start_attraction: 起点景点(如"深圳")end_attraction: 终点景点(如"黄果树瀑布")"""cypher = """OPTIONAL MATCH (startA:Attraction {name: $start})OPTIONAL MATCH (startH:TransportHub {name: $start})WITH coalesce(startA, startH) AS startWHERE start IS NOT NULLOPTIONAL MATCH (endA:Attraction {name: $end})OPTIONAL MATCH (endH:TransportHub {name: $end})WITH start, coalesce(endA, endH) AS endWHERE end IS NOT NULLMATCH path = allShortestPaths((start)-[*1..5]->(end))// 格式化输出WITH path,[r IN relationships(path) | {从: startNode(r).name,到: endNode(r).name,方式: r.mode_ref,耗时: r.duration_min+'分钟',距离: r.distance_km +'千米',价格: r.price +'元'}] AS rels_detailRETURNrels_detail AS 交通详情"""try:with driver.session() as session:result = session.run(cypher,start=start_attraction,end=end_attraction)record = list(result)if not record:return f"「{start_attraction}」无法到达 「{end_attraction}」 "resp = f"*两地之间通行方式如下" + "\n"for i, r in enumerate(record, 1):resp += f"\n{i}. {r['交通详情']}\n "return respexcept Exception as e:print(e)return f"规划「{start_attraction}→{end_attraction}」路线时出错:{str(e)[:100]}"@tooldef city_attractions_tool(city_name: str):"""查询某城市景点列表(支持按类别筛选)参数:city_name: 城市名称(如"贵州、铜仁")"""cypher = """MATCH (a:Attraction)-[:LOCATED_IN]->(c:City)WHERE c.name CONTAINS $city or c.province CONTAINS $cityRETURN a.name AS 景点,a.city as 所在城市, a.description AS 描述,[(a)-[:HAS_TICKET_RULE]->(tr) | tr.price][0] AS 门票ORDER BY 门票 ASCLIMIT 10"""try:with driver.session() as session:results = session.run(cypher, city=city_name)records = list(results)if not records:return f"「{city_name}」暂无收录景点数据"resp = f"**{city_name}推荐景点**" + "\n"for i, r in enumerate(records, 1):resp += f"\n{i}. **{r['景点']}**\n {r['所在城市']}\n 门票:{r['门票']}元"return respexcept Exception as e:return f"查询「{city_name}」景点时出错:{str(e)[:100]}"@tooldef city_transport_hub_tool(city_name: str):"""查询某个城市的交通枢纽参数:city_name: 城市名称(如"贵州、铜仁")"""cypher = """MATCH (c:City)<-[:IN_CITY]-(t:TransportHub)WHERE c.name CONTAINS $city or c.province CONTAINS $cityRETURN t.name AS 交通枢纽,c.name as 所在城市LIMIT 10"""try:with driver.session() as session:results = session.run(cypher, city=city_name)records = list(results)if not records:return f"「{city_name}」暂无收录交通枢纽数据"resp = f"{city_name}的交通枢纽" + "\n"for i, r in enumerate(records, 1):resp += f"\n{i}. **{r['交通枢纽']}**\n {r['所在城市']}"return respexcept Exception as e:return f"查询「{city_name}」交通枢纽时出错:{str(e)[:100]}"if __name__ == '__main__':#print(city_attractions_tool.invoke("贵州"))# print(route_planner_tool.invoke({"start_attraction": "深圳北站", "end_attraction": "黄果树瀑布"}))print(city_transport_hub_tool.invoke("贵州"))
3、使用langchainGraph+smith在本地运行
# agent.pyfrom langchain.agents import create_agentfrom langchain_openai import ChatOpenAIfrom agent.tools.tools import attraction_info_tool, route_planner_tool, city_attractions_tool, city_transport_hub_toolfrom agent.utils.env_utils import DEEPSEEK_MODEL_NAME, DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL# 初始化LLM(替换为你的模型)llm = ChatOpenAI(model_name=DEEPSEEK_MODEL_NAME,api_key=DEEPSEEK_API_KEY,base_url=DEEPSEEK_BASE_URL)# 精心设计的系统提示词(关键!)AGENT_PROMPT = """你是一名专业的**文旅智能助手**,精通贵州省及周边景点、票务、交通信息。请严格遵守以下规则:## 知识范围- 仅回答与景点、门票、行程规划相关的问题- 数据覆盖:贵阳、安顺、铜仁等城市景点(黔灵山公园、黄果树瀑布、梵净山)- 交通方式:高铁(highspeed)、飞机(plane)、公交(bus)、自驾(car)、步行(walk)、地铁(subway)## 可用工具说明1. `route_planner_tool`:- 用途:规划跨城行程(自动计算市内+跨城+市内总耗时)- 参数:start_attraction(必填), end_attraction(必填)- 示例:start_attraction="灵隐寺", end_attraction="拙政园"2. `city_attractions_tool`:- 用途:查询某城市景点列表- 参数:city_name(必填)- 示例:city_name="贵州"3. `city_transport_hub_tool`:- 用途:查询某城市交通枢纽列表- 参数:city_name(必填)- 示例:city_name="深圳"## 回答规范- 工具返回后,用**自然语言总结**,突出关键信息(耗时/价格/注意事项)- 涉及时间计算时,明确说明"总耗时=市内+跨城+市内"- 若工具返回错误,友好提示用户调整查询(如"请确认景点名称是否正确")- **禁止**编造工具未返回的数据## 示例对话用户:西湖下午2点能进去吗?门票多少?思考:需查询景点票务信息 → 调用attraction_info_tool工具返回:...回答:西湖下午2点可入园!开放时间09:00-17:00,成人票60元,无需预约。用户:从灵隐寺到拙政园坐高铁要多久?思考:需规划跨城行程 → 调用route_planner_tool工具返回:...回答:灵隐寺→拙政园(高铁)总耗时110分钟:灵隐寺→杭州东站(25min)→苏州北站(70min ¥120)→拙政园(15min)现在请开始服务用户:Question: {input}{agent_scratchpad}"""# 创建Agenttools = [attraction_info_tool, route_planner_tool, city_attractions_tool, city_transport_hub_tool]# agent = create_agent(llm, tools, AGENT_PROMPT)agent = create_agent(llm,tools=tools,system_prompt=AGENT_PROMPT)
langgraph dev 大模型根据输入,自动分析需要查询哪些工具,然后调用相关工具,最后根据查询结果组织返回





源码已放在github:https://github.com/awbqty/TourismTransportAgent
相关文章
发表评论
评论列表
- 这篇文章还没有收到评论,赶紧来抢沙发吧~