公司动态

Python旅游景点信息可视化系统:Django+ECharts+大模型Agent实战

📅 2026/9/2 2:24:53
Python旅游景点信息可视化系统:Django+ECharts+大模型Agent实战
做一个 Python 旅游景点信息可视化系统最容易被低估的不是 Django 怎么搭也不是图表怎么画而是数据怎么在业务场景里被真正用起来。我接过一个类似需求对方一开始只说要“几个看得过去的图表”但聊到后面才发现真正要解决的是数据分散、口径不统一、领导问的问题随时会变、图表做完就没人维护。最后我给出的方案是用 Django 做业务和数据服务骨架用 Pandas 做分析和挖掘用 ECharts 做可视化再通过 DeepSeek 大模型的 Agent 能力把“看图表”升级成“问数据”。这篇文章就把这套系统的设计思路、落地步骤和坑点完整讲一遍。1. 先想清楚这个系统真正要解决的是什么问题1.1 可视化只是出口数据管道才是骨架很多教程把“旅游景点信息可视化系统”等同于“画几个图表”柱状图展示游客量饼图展示景点类型占比地图展示地区分布。但真实场景里图表只是最后一步。数据从哪来怎么清洗字段怎么统一按天还是按月聚合数据更新频率是多少这些问题不解决图表画得再漂亮也没有长期价值。我把这个系统拆成三段数据接入、数据组织、数据服务。数据接入负责从 Excel、CSV、数据库甚至爬虫接口收集景点信息、游客量、评论、天气等数据数据组织负责清洗、转换、去重、打时间戳落到统一的业务表里数据服务负责把分析结果通过 API 暴露给前端或者交给大模型 Agent 去解读。可视化只是数据服务的一种输出形式。所以这篇文章的主判断是旅游景点信息可视化系统的核心价值不是几张图表而是把“散乱的数据”加工成“能被查询和回答问题的服务”。如果你只做图表项目交付后大概率没人用如果你把数据服务做通了后续加图表、加问答、加预测都是自然延伸。1.2 为什么是 Django而不是 Flask 或 Node选择 Django 不是因为它“重”而是因为这个系统需要业务开发效率。Django 自带 ORM、数据迁移、Admin 后台、用户认证还支持 Django REST Framework 快速提供 JSON 接口。景区信息、游客记录、评论数据之间有关系ORM 能减少大量 SQL 维护成本后台可以直接录入和修正基础数据不用自己写管理页面。Flask 更轻但组件要自己拼。做小 Demo 没问题一旦遇到权限、数据库迁移、多环境配置Flask 需要额外花时间集成。Node 的优势在后端高并发和前端同构但在数据分析生态上不如 Python。旅游景点可视化这个场景核心分析能力都在 Python 的 Pandas、NumPy、scikit-learn 生态里所以技术栈选型上Python Django 是一个稳妥组合。Django 的短板也要说清楚它不适合做实时高并发的大盘页面。如果未来要服务几千甚至上万用户同时查询需要加缓存、异步任务、甚至把查询接口独立成服务。但对于教学项目、毕业设计、中小型景区内部工具Django 完全够用。1.3 大模型 Agent 在这里并不是“智能客服”很多人一听“接入大模型”第一反应是做一个聊天机器人。但在旅游景点可视化系统里Agent 的定位不是陪聊而是把查询方式从“固定下拉框”变成“自然语言到数据操作”。用户不会只问“今天天气怎么样”更可能问“上周哪个景区游客量下降最明显”“评分低于4分的景点有什么共同点”“暑假期间哪些景点组合最受欢迎”。这类问题无法用固定的统计接口覆盖。Agent 的价值在于理解用户意图决定需要调用哪一个数据接口或分析模块拿到结果后组织成自然语言回答必要时驱动前端刷新对应图表。这个过程本质上是一个“工具调用”循环DeepSeek 之类的模型负责语义理解Django 后端负责执行真实的数据查询两者配合才让系统真正具备回答开放性问题的能力。2. 系统架构与数据层设计2.1 核心模块接入、存储、分析、接口、交互一个可运行的系统最少需要五个模块数据接入模块读取 CSV、Excel或通过脚本写入数据库。这一层不一定要做成界面可以先写成独立脚本。存储模块Django ORM 管理的数据库通常用 SQLite 起步正式部署再切换到 PostgreSQL。分析模块Pandas 负责数据聚合、统计、关联分析、情感倾向判断结果写到临时表或直接返回给接口。接口模块DRF 提供GET /api/overview、GET /api/trend?city...这类 REST 接口。交互模块前端页面用 ECharts 渲染图表同时提供一个对话输入框把自然语言请求交给 Agent 处理Agent 再调用分析接口。这个架构的关键是“模块之间通过数据接口通信”不要把分析逻辑散在页面里。否则后期每改一个指标前端后端都要一起改维护成本会快速上升。2.2 数据模型怎么设计才不别扭旅游景点可视化涉及的核心实体可以先用四张表承载ScenicSpot景点表名称、城市、所在区域、景区等级、门票价格、简介、创建时间。VisitorStat游客统计表景点外键、日期、游客量、收入、天气情况、备注。Comment评论表景点外键、用户昵称、评分、评论内容、评论时间。CityInfo城市信息表城市名称、省份、经度、纬度、热门季节。设计时要重视三个字段时间字段数据分析必须有时间维度、景点外键所有指标要能按景点聚合、采集来源字段数据来自哪个文件或接口方便日后排查。不需要一开始就把模型设计得非常复杂。更稳妥的做法是先建一张ScenicSpot和VisitorStat跑通整个链路再逐步增加Comment和CityInfo。如果刚开始就设计八张表光录入数据就会劝退自己。2.3 数据挖掘在旅游场景里的几个落地用法数据挖掘这个标签很容易让人觉得一定要上 Hadoop、Spark实际在这个项目里更多是“用合适的算法从旅游数据中发现规律”。常见落地点有三个第一是关联规则。比如把游客的景点游览记录和消费记录放在一起分析“去了 A 景点的游客还喜欢去 B 景点”为路线推荐和联票设计提供依据。实现时可以用mlxtend库的apriori算法。第二是聚类分析。将景点按游客量、门票价格、评分、所在城市等特征聚类区分出“热门打卡型”“小众体验型”“高性价比型”方便运营做差异化推荐。用 KMeans 就够了重点是特征标准化和结果解读。第三是时间序列趋势识别。识别游客量上升、下降、周期性波动甚至做简单的短期预测。这个环节不建议一上来就用复杂的深度学习模型先看移动平均、季节分解给业务提供一个可解释的判断。数据挖掘本身不是最终交付物最终交付的是“基于发现做出的运营建议”。所以写代码前先想清楚每个结论怎么用否则算法跑完只是自我感动。3. 从数据到看板可视化的落地路径3.1 选型为什么优先考虑 ECharts可视化库有很多选择Plotly 交互丰富但 Django 模板方案里ECharts 的集成成本最低、生态最成熟、文档最全。ECharts 支持按需引入图表类型覆盖折线图、柱状图、地图、雷达图、热力图几行配置就能出一个不错的图表。如果你做前后端分离也可以考虑 Vue ECharts。但如果是 Django 模板 jQuery AjaxECharts 仍然是最不容易出错的选择。核心原因是它不绑定前端框架直接拿来用就行。3.2 后端接口设计按维度、指标、时间范围返回聚合结果前端图表不要直接查询数据库。正确做法是后端提供一个聚合接口比如/api/visitor_trend/参数包含start_date开始日期end_date结束日期city城市metric指标名比如visitor_countgranularity聚合粒度day或month后端先用 Django ORM 读取数据再用 Pandas 做 groupby最后返回 JSON[ {date: 2025-01-01, visitor_count: 12000}, {date: 2025-01-02, visitor_count: 14500} ]这样设计的好处是前端只看数据和字段名不需要关心 SQL 和数据处理。日后要增加指标只需后端扩展接口前端保持不变。3.3 典型可视化场景游客量趋势、热度 TopN、评论情感这个系统里最值得做的可视化不是花哨大屏而是能回答业务问题的几个基础图表游客量时间趋势按日/月展示游客量折线图能看出节假日高峰和季节性。景点热度 TopN按城市或按景区等级做柱状图看哪些地方是客流主力。评论情感分布对Comment表的文本做简单情感判断正面、中性、负面比例用饼图或堆叠柱状图展示。区域热度地图用地图展示不同城市或区域的游客总量颜色越深代表热度越高。前三个用 ECharts 的折线图、柱状图、饼图就能实现地图需要单独引入中国地图 GeoJSON。地图不是必需如果数据没有经纬度可以先把城市名和数字用表格展示。3.4 容易踩的数据口径问题很多人做完图表后发现数字对不上不是代码写错了是数据口径没定清楚。最常见的坑有三个空值处理不一致。有的日期没有数据是补 0 还是忽略前端趋势图如果补 0会出现断崖下跌的假象如果忽略折线会断开。建议在分析脚本里统一策略不补 0但标注“无数据”。统计周期不统一。按日、按周、按月聚合结果完全不同。做接口时要把granularity明确传参前端不要自己猜。重复数据没去重。同一天同一景点的游客量如果被导入了两次图表会直接翻倍。建议在数据清洗阶段按“景点 日期 来源”去重并让数据库加唯一约束。这些口径问题看起来小但会消耗大量排查时间。建议在项目初期就写一个数据质量检查函数自动检查缺失值、重复项和字段格式。4. 接入 DeepSeek 大模型 Agent把看板变成能对话的数据服务4.1 为什么不是简单关键词匹配如果只做“查询某些字段”写一个关键词匹配规则就够了。但真实问题往往是自然语言的“哪个景点的评分最近变差了”“春节和暑假比游客量差多少”“给我推荐几个适合亲子游的景区。”这些句子没有固定模板还存在指代和比较。关键词匹配很难覆盖。大模型的好处是能理解意图但大模型不知道数据库里有什么所以必须给它工具。4.2 工具调用让模型能查数据库、能刷新图表Agent 的常见做法是“模型 工具列表”。工具列表就是后端可执行函数的描述例如[ { name: get_visitor_trend, description: 查询指定时间范围内、指定城市的游客量趋势, parameters: { start_date: 字符串起始日期, end_date: 字符串结束日期, city: 字符串城市名称选填 } }, { name: get_top_spots, description: 查询游客量最高的前N个景点, parameters: { top_n: 整数返回数量 } } ]当用户问“上周哪个景区游客量下降最明显”Agent 不会直接硬答而是先判断需要调用哪个工具把问题转换成工具参数后端拿到参数后执行真实查询再把查询结果返回给模型模型把这些数字组织成一句用户能看懂的话。这个循环就是“工具调用 Agent”的最小实现。4.3 一次完整的自然语言查询请求流转我用一个例子说明用户输入上周游客量排名前三的景区是哪些流转过程前端把这句话发给 Django 的/api/chat/接口。Django 拿到文本后调用 DeepSeek API并把工具列表一并传给模型。模型返回一个结构化意图调用get_top_spots参数top_n3时间范围是“上周”。Django 解析这个意图执行真实的数据库查询得到三个景点名称和游客量。Django 把查询结果再发给模型让模型生成自然语言回答。前端展示回答文本同时根据返回的景点列表刷新一个排名柱状图。这个流程看起来长但每一步都很简单。最需要注意的环节是第 4 步绝不能把用户输入直接拼接进 SQL必须由中间层解析出可信参数再交给 ORM 查询。4.4 API 调用稳定性与成本控制接入大模型 API 不是调通就完了工程上还要考虑这几件事超时控制网络请求可能很慢Django 接口要设置超时避免前端一直转圈。通常 3 到 5 秒没响应就返回提示。重试策略遇到模型服务瞬时错误可以做一次重试。重试次数别太多两次足够。缓存同样的问题短期内重复查询可以把结果缓存起来减少 API 调用成本。输入输出限制限制用户最多输入多少字模型返回的最大 token 也要限制避免一次调用把预算烧掉。API Key 管理绝对不要把 Key 硬编码在代码里放到环境变量或密钥管理工具中。还建议在 Agent 调用前做一次简单的意图过滤避免用户输入与数据无关的内容。否则模型会花大量 token 回答“今天天气怎么样”这类无关问题。5. 实操从零跑通最小可用系统5.1 环境准备与依赖建议使用 Python 3.10 或 3.11创建独立虚拟环境python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install django djangorestframework pandas requests如果要跑关联规则和聚类可以再加pip install mlxtend scikit-learn前端图表不需要额外安装直接在前端页面里引用 ECharts 的 CDN 或本地静态文件。5.2 创建 Django 项目和核心应用django-admin startproject travel_vis cd travel_vis python manage.py startapp spots在settings.py里注册rest_framework和spots应用。在spots应用下写数据模型。5.3 定义数据模型并导入样例数据在models.py里先定义核心模型from django.db import models class ScenicSpot(models.Model): name models.CharField(max_length100) city models.CharField(max_length50) province models.CharField(max_length50) level models.CharField(max_length20, blankTrue) ticket_price models.FloatField(default0) description models.TextField(blankTrue) def __str__(self): return self.name class VisitorStat(models.Model): spot models.ForeignKey(ScenicSpot, on_deletemodels.CASCADE) date models.DateField() visitor_count models.IntegerField() income models.FloatField(default0) weather models.CharField(max_length50, blankTrue) class Meta: unique_together (spot, date, weather)导入样例数据时可以写一个load_data.py脚本用 Pandas 读取 CSV 后逐行写入数据库。注意先做去重和格式转换。5.4 完成一次数据分析并暴露接口写一个视图函数比如统计某城市每月的游客量from rest_framework.decorators import api_view from rest_framework.response import Response from django.db.models import Sum from .models import VisitorStat, ScenicSpot from datetime import datetime api_view([GET]) def visitor_trend(request): city request.GET.get(city, ) start request.GET.get(start_date, 2025-01-01) end request.GET.get(end_date, 2025-12-31) qs VisitorStat.objects.filter( date__range[start, end], spot__citycity, ).values(date).annotate(totalSum(visitor_count)) data [{date: item[date], visitor_count: item[total]} for item in qs] return Response({data: data})上面只是示例结构真实项目还要加异常处理和参数校验。前端用 Ajax 请求这个接口再交给 ECharts 渲染。5.5 接入 Agent 对话接口先封装一个DeepSeekClient核心是调用远程 API 并支持工具列表。关键代码思路import requests def chat_with_tools(user_message, tools, execute_tool): # 第一轮把用户消息和工具列表发给模型请求模型返回意图 response requests.post( https://api.deepseek.com/chat/completions, headers{Authorization: fBearer {API_KEY}}, json{ model: deepseek-chat, messages: [{role: user, content: user_message}], tools: tools, tool_choice: auto, }, timeout10, ) # 解析模型输出如果有工具调用则执行工具再把结果回传模型 # 第二轮把工具结果加入 messages请求模型生成最终回答 ...具体 API 字段以你使用的版本为准。这里的重点是理解两轮请求的必要性第一轮让模型决定“该做什么”第二轮让模型基于真实数据“说人话”。6. 最容易被忽略的工程环节6.1 输入安全别把用户问题直接拼进查询接入大模型之后最大的安全隐患不是模型本身而是把用户输入不加处理地拼进 SQL 或连接字符串。比如用户说“给我删掉所有数据”Agent 如果把它翻译成危险操作后果严重。所以 Agent 工具调用必须满足三个原则工具白名单模型只能调用我们封装的几个函数不能直接操作数据库。参数白名单比如查询城市后端检查它是否在景点城市列表里查询字段必须是预先定义好的字段名。只读优先可视化系统的 Agent 默认只读不提供删除、更新、写入功能。就算模型理解了用户意图也不能执行写操作。6.2 API Key、日志与异常重试API Key 应放在.env文件或运行环境里DEEPSEEK_API_KEYyour_api_key_hereDjango 中通过os.environ.get(DEEPSEEK_API_KEY)读取。日志方面每次 Agent 调用都记录用户输入、模型返回的意图、工具执行结果、最终回答、耗时、错误信息。这样才能在模型回答不对时知道问题出在哪一层。重试要注意“幂等性”。查询类工具可以安全重试写操作不行。由于这个系统默认只读所以最多重试一次即可。6.3 数据更新节奏不要让可视化变成一次性产物很多系统上线时数据是新的但一个月后不再更新看板就变成“历史文物”。为了避免这个问题至少要做两件事给数据导入脚本增加增量更新能力。比如只导入date大于当前最大日期的数据。用系统的定时任务或外部 Cron 每天执行一次更新脚本。Django 本身不支持内建定时任务可以借助 Celery Beat或简单地在 Linux 服务器上配置 Crontab。刚开始不用做得很复杂但至少要预留“重新导入数据”的命令入口否则每次更新都要手工跑脚本最后一定没人更新。7. 排查链路从页面白屏到 Agent 不回答问题7.1 先分层定位问题遇到问题不要急着改代码。先判断问题发生在哪一层数据层、接口层、前端层还是大模型层。一个简单方法是直接访问后端接口看返回的 JSON 是否正确。如果接口返回正常问题在前端渲染。如果接口返回错误问题在服务端或数据。如果服务端日志正常但交互接口超时问题在模型调用或第三方服务。这个分层排查能节省大量时间。7.2 图表不显示或数据为空先从浏览器开发者工具看网络请求。如果请求 404检查 Django 路由是否注册。如果请求 500看服务端 Traceback通常是数据表字段名错误。如果请求 200 但data为空查看数据库中该时间范围是否有数据或者参数是否拼接错误。如果数据有但图表空白检查前端 ECharts 拿到的字段名是不是visitor_count和配置里的name是否一致。7.3 分析结果和预期不一致先看原始数据。用 Django Admin 或数据库客户端查出某一天某一景点的原始记录再手动用 Pandas 重算一次。常见原因聚合范围重叠。比如同一天数据出现在多张表里。字段类型不一致。日期是字符串月份截取出错。统计时包含了测试数据。建议给测试数据加一个is_test标记分析时过滤掉。7.4 Agent 调用失败或回答不准确分三步排查看模型 API 返回的状态。如果是鉴权失败检查 Key 和环境变量如果是超时增加超时时间或重试。看模型是否成功返回工具调用意图。如果模型没有调用工具而是直接编造答案很可能是工具列表描述不清晰或者用户问题明显超出工具能力范围。看工具执行结果。如果模型调用了工具但返回最终回答时没有引用工具结果说明第二轮回传上下文时消息格式写错了。这里最容易误判的是“模型回答不准”就急着换模型或调 Prompt实际上大部分原因是工具描述、参数解析、结果回传这三步里有一环断了。8. 这套方案的适用边界与长期价值8.1 适合谁、不适合谁这套技术组合适合以下场景高校毕业设计或课程项目需要展示数据分析、可视化、大模型 Agent 三项能力的结合。中小型景区、旅游平台的内部运营看板数据量在百万级以内团队没有专门的数据工程师。想学习 Django 工程化、数据分析、大模型应用集成的一体化练习。不太适合的场景也很明确高并发实时大屏需要秒级更新多地实时客流Django 单机方案不够。涉密或敏感景区数据不方便接入第三方大模型 API需要私有化模型工程复杂度会高很多。数据量达到千万条以上且需要复杂离线计算应该引入数据仓库和计算引擎而不是直接在 Django 里用 Pandas。8.2 从教程项目到生产级系统还差什么如果要把这个项目部署上线并长期维护还需要补上权限控制不同角色只能看对应范围的数据。配置管理开发环境、测试环境、生产环境分别加载不同配置。监控告警API 失败率、模型调用耗时、数据更新任务是否成功。自动化测试至少覆盖数据导入、聚合接口、Agent 工具调用三个核心链路。文档数据字典、接口文档、部署步骤、模型调用说明。这些工程能力不一定要一次性做完但要意识到跑通 Demo 只是开始真正决定项目能否活下去的是基础设施。8.3 一个可复用的执行框架如果你打算从零做类似系统我建议按这个顺序推进先定目标做给谁看要回答哪三个核心问题。再定数据找一份至少包含日期、景点、指标字段的样例数据。再定接口画出前端需要的 3 到 5 个 JSON 接口。再做前端图表用 ECharts 接上接口跑通一个最小闭环。最后接 Agent在闭环稳定后再让大模型调用这些接口。这个框架的核心逻辑是“先让数据流跑通再加入智能交互”。大部分项目失败不是因为没用大模型而是数据链路都没做通就急着叠功能。回到开头那个需求如果我当年只交付一个看板它大概率会被丢在收藏夹里。真正有用的是把景点数据从静态表格变成一套能看、能查、能问的服务。技术栈可以换但这个判断不会变Python、Django、数据分析、可视化、大模型 Agent本质上是同一件事的五个环节。先把最小链路做通再一步步加能力才是这类系统最务实的落地路径。