
使用 Grafana 构建 LLM 应用实时监控看板从 PostgreSQL 到可视化面板【免费下载链接】llm-zoomcampLLM Zoomcamp - a free online course about real-life applications of LLMs. In 10 weeks you will learn how to build an AI system that answers questions about your knowledge base. Register here 项目地址: https://gitcode.com/GitHub_Trending/ll/llm-zoomcamp实时监控是任何 RAG 应用上线后的刚需离线评估无法告诉你真实用户使用时的响应速度、token 消耗、成本与回答质量。本指南基于 LLM Zoomcamp 2026 期 Module 5Monitoring的 Grafana 课程讲解如何用 Grafana 直接读取监控专用的 PostgreSQL 数据库通过一个又一个 SQL 查询面板把响应时间、token 用量、成本、模型分布、相关性判定与用户反馈汇总到一张实时更新的看板上。学完你将掌握 Grafana 的容器化启动、PostgreSQL 数据源接入、$__timeFrom/$__timeTo/$__timeGroup等时间过滤宏的使用以及七类常用面板的查询写法与布局技巧。为什么在 Streamlit 看板之外还需要 Grafana本模块前面已经构建了一个 Streamlit 聊天应用并保存了对话记录同时也用 Streamlit 写了一个简单的监控看板见 dashboard.py用get_conversations与get_stats展示总数、平均响应时间、总成本与平均 token。它够用但能力有限。引入 Grafana 的原因在于它做得更多它是专业的看板工具可连接多种数据源不仅仅是 PostgreSQL提供更丰富的面板类型时序图Time series、柱状图Bar chart、饼图Pie chart、仪表盘Gauge、表格Table等支持告警当某个指标越过阈值时主动发出通知。代价是它是一套独立、更重的应用需要单独运行。这正是取舍的关键如果需求简单Streamlit 看板足够当想要更强大的可视化与告警能力时Grafana 直接读取我们已经建好的 PostgreSQL 库实时展示系统状态。在本模块中PostgreSQL 只服务于监控用途其他任何系统部分都不会碰这个数据库——选 Postgres 的两大理由正是「擅长结构化数据」和「Grafana 后续接入容易」这一决策在 05-database.md 中有明确说明。开始之前看板背后的数据Grafana 面板本质上就是针对conversations与feedback两张表的 SQL 查询。理解表结构才能理解面板查询。conversations表保存每一次 LLM 调用的完整记录其建表 SQL 定义在 db_init.py 中CREATE TABLE conversations ( id SERIAL PRIMARY KEY, question TEXT NOT NULL, answer TEXT NOT NULL, course TEXT NOT NULL, model TEXT NOT NULL, instructions TEXT NOT NULL, prompt TEXT NOT NULL, prompt_tokens INTEGER NOT NULL, completion_tokens INTEGER NOT NULL, total_tokens INTEGER NOT NULL, response_time FLOAT NOT NULL, cost FLOAT NOT NULL, timestamp TIMESTAMP WITH TIME ZONE NOT NULL )两个字段值得特别说明course同一个 assistant 未来可能服务多门课程现在所有数据都是llm-zoomcamp但保留该列意味着将来新增课程时无需重建表结构timestamp刻意使用TIMESTAMP WITH TIME ZONE时区感知时间戳。没有时区信息Grafana 后续在时间轴上就无法正确对齐数据——这是 05-database.md 中反复强调的设计决策也是本课所有按时间过滤查询的前提。feedback表则保存两类反馈用户的赞/踩source userscore为 1 或 -1和 LLM judge 的相关性判定source judgerelevance为RELEVANT/PARTLY_RELEVANT/NON_RELEVANT见 db_init.py 的init_feedback()。写面板查询前请确认数据正在产生如果只有零星几条数据看板会显得空荡。本模块提供了 generate_data.py它每秒向 Postgres 插入一条模拟对话约 70% 概率附带 judge 相关性反馈约 50% 概率附带用户评分让你在接近真实的数据量下观察看板滚动更新。启动方式见 11-synthetic-data.md。用 Docker 启动 GrafanaGrafana 需要和 PostgreSQL 在同一 Docker 网络中因为 Grafana 要通过容器名course-assistant-pg访问数据库。该网络与 PostgreSQL 容器已在本模块的数据库一课中创建好对应 Makefile 中的network与postgres两个 targetmake postgres会先创建monitoring网络再启动容器。在同网络下启动 Grafana并挂载数据卷实现持久化docker run -d \ --name grafana \ --network monitoring \ -p 3000:3000 \ -v grafana_data:/var/lib/grafana \ grafana/grafana参数说明参数作用-d后台detached运行。若想启动时盯着日志排错去掉-d改为前台运行即可--name grafana容器名便于后续引用--network monitoring加入与 PostgreSQL 共享的monitoring网络使 Grafana 能用主机名course-assistant-pg访问数据库-p 3000:3000将容器 3000 端口映射到宿主机浏览器访问入口-v grafana_data:/var/lib/grafana命名卷持久化 Grafana 数据。你在 Grafana 中配置的数据源和看板都会在容器重启后保留访问http://localhost:3000。首次登录使用默认账号admin / admin系统会提示设置新密码本地开发场景直接继续用admin / admin也可以。顺带一提在本模块最后的 13-docker-compose.md 中PostgreSQL、Grafana 与 Streamlit 应用会被一起收进一个docker-compose.yaml用docker-compose up一条命令启动全部服务。手动逐个docker run适合教学演示Compose 适合日常反复启动。配置 PostgreSQL 数据源进入Configuration Data Sources Add data source选择PostgreSQL填写连接信息配置项值Hostcourse-assistant-pg:5432Databasecourse_assistantUseruserPasswordpasswordSSL Modedisable这里的 Host 用的是容器名而非localhost——因为 Grafana 运行在容器里必须通过共享网络上的容器名解析到 PostgreSQL。其余四个值分别对应docker run时通过POSTGRES_USERuser、POSTGRES_PASSWORDpassword、POSTGRES_DBcourse_assistant注入的环境变量见 Makefile 的postgrestarget也与 db_init.py 中get_db_connection()读取的默认值一致def get_db_connection(): return psycopg.connect( hostos.getenv(POSTGRES_HOST, localhost), dbnameos.getenv(POSTGRES_DB, course_assistant), useros.getenv(POSTGRES_USER, user), passwordos.getenv(POSTGRES_PASSWORD, password), )点击Save Test应显示Database Connection OK。Grafana 面板 SQL 的两个好习惯创建看板后面板逐个添加每个面板本质是一条 Grafana 对 PostgreSQL 执行的 SQL 查询。两个习惯能让这些查询表现良好把时间列别名为timeGrafana 读取该列把数据点放到 x 轴上时序图必须有它按选中的时间范围过滤这样面板只展示你在看板顶部选择的时间区间内的数据而不是全表。Grafana 为此提供了三个专用 SQL 宏宏含义$__timeFrom()所选时间范围的起点$__timeTo()所选时间范围的终点$__timeGroup(column, interval)按时间间隔对结果分组分桶在WHERE子句中显式加入$__timeFrom()和$__timeTo()并非强制要求但显式写出更稳妥——面板会严格跟随顶部选择的时间范围。面板一响应时间Response Time展示 LLM 应答耗时随时间的变化。conversations表中的每一行本就是一次 LLM 调用所以直接绘制原始值即可SELECT timestamp AS time, response_time FROM conversations WHERE timestamp BETWEEN $__timeFrom() AND $__timeTo() ORDER BY timestamp面板可视化类型选择Time series时序图。这里的response_time来自 metrics.py 中RAGWithMetrics的埋点llm()方法在调用前后各取一次time.time()计算差值连同 token 与成本一起封装进LLMCallRecord。也就是说本面板绘制的是真实用户请求全链路中最核心的耗时指标。面板二Token 用量Token Usage展示 token 消耗随时间的变化。时间范围拉长后逐点绘制的数据点会过多。因此按时间间隔如每 5 分钟分桶取每个桶内的平均值SELECT $__timeGroup(timestamp, $__interval) AS time, AVG(total_tokens) AS avg_tokens FROM conversations WHERE timestamp BETWEEN $__timeFrom() AND $__timeTo() GROUP BY 1 ORDER BY 1三个要点$__timeGroup把时间戳归入桶bucket。$__interval是 Grafana 根据当前时间范围与面板宽度自动计算的合理间隔GROUP BY 1按第一列即时间桶分组AVG给出每个桶的平均 token 数。可视化类型同样选择Time series。total_tokens直接来自 LLM 响应的usage对象usage.total_tokens并在LLMCallRecord中记录后随每次对话写入conversations表见 db_save.py 的save_conversation。面板三成本Cost展示成本随时间的变化。思路与 token 面板相同只是把AVG换成SUM并加上cost 0过滤掉零成本记录SELECT $__timeGroup(timestamp, $__interval) AS time, SUM(cost) AS total_cost FROM conversations WHERE timestamp BETWEEN $__timeFrom() AND $__timeTo() AND cost 0 GROUP BY 1 ORDER BY 1可视化类型为Time series。cost字段由 metrics.py 中的calculate_cost(model, usage)计算按「每百万输入 token 单价 × 输入量 每百万输出 token 单价 × 输出量」再除以一百万得出。注意这只是课程示例中的计价逻辑实际生产应根据你的模型提供方与价格表调整。面板四模型使用Model Usage展示正在使用哪些模型SELECT model, COUNT(*) as count FROM conversations WHERE timestamp BETWEEN $__timeFrom() AND $__timeTo() GROUP BY model可视化类型选择Bar chart柱状图。model列来自每次调用时记录在LLMCallRecord.model的模型名。面板五相关性分布Relevance Distribution展示 judge 判定为RELEVANT、PARTLY_RELEVANT、NON_RELEVANT的答案数量分布SELECT relevance, COUNT(*) as count FROM feedback WHERE source judge AND timestamp BETWEEN $__timeFrom() AND $__timeTo() GROUP BY relevance可视化类型选择Pie chart饼图。这条查询的数据来源是内置 judge本模块在每次回答后用 LLM 自动评估答案与问题的相关性09-built-in-judge.md通过evaluate_relevance得到 verdict再以source judge写入feedback表见 app.py。当相关性分布开始明显下滑说明搜索或 prompt 出了问题——这是自动质量信号的价值所在无需等待用户手动点击。面板六用户反馈User Feedback展示用户的赞thumbs up与踩thumbs down数量SELECT SUM(CASE WHEN score 0 THEN 1 ELSE 0 END) as thumbs_up, SUM(CASE WHEN score 0 THEN 1 ELSE 0 END) as thumbs_down FROM feedback WHERE source user AND timestamp BETWEEN $__timeFrom() AND $__timeTo()可视化类型选择Gauge仪表盘或Pie chart。score为 1 表示赞、-1 表示踩由 app.py 中「Ask」按钮下方的反馈按钮写入。一个小时内涌来大量 thumbs down 是非常明确的问题信号值得立刻去排查详见 08-user-feedback.md。面板七最近对话Recent Conversations以表格展示最近 5 条对话SELECT timestamp AS time, question, answer, response_time, cost FROM conversations WHERE timestamp BETWEEN $__timeFrom() AND $__timeTo() ORDER BY timestamp DESC LIMIT 5面板类型选择Table表格。此面板是运营时最常看的「原始数据窗口」能直接看到真实用户问了什么、答了什么、耗时与成本多少。看板设置自动刷新与布局设置看板每 30 秒自动刷新让数据保持最新同时可以把默认时间范围设为Last 6 hours打开看板即看到最近六小时的全貌。面板布局建议顶行最近对话表格宽幅中间行模型使用柱状图 | 相关性饼图底行响应时间 | token 用量 | 成本。不要把面板类型看作定死的。同样的查询可以尝试柱状图、饼图或时序图哪个读起来最直观就保留哪个。配合 generate_data.py 每秒持续注入的合成数据你会看到图表在眼前实时跳动。从看板到生产监控闭环的下一步这张看板把响应速度、成本、相关性和用户评分集中在一处构成了对本模块所建 RAG 系统的实时体检数据链路回顾埋点采集metrics.py 的RAGWithMetrics→ 落库db_save.py 与 db_feedback.py→ 查询db_query.py 展示同样的表能被 Python 侧如何读取→ Grafana 可视化走向生产将三个服务整合进 13-docker-compose.md 的docker-compose.yaml一条命令启动全套环境更进一步的探索方向OpenTelemetry、告警、监控框架见 14-next-steps.md注意边界本课 Grafana 直接读取监控专用库连接信息user/password/course_assistant仅适用于本地 Docker 教学环境真实部署务必替换为安全的凭据管理方案并依据实际模型价格表校正成本计算。【免费下载链接】llm-zoomcampLLM Zoomcamp - a free online course about real-life applications of LLMs. In 10 weeks you will learn how to build an AI system that answers questions about your knowledge base. Register here 项目地址: https://gitcode.com/GitHub_Trending/ll/llm-zoomcamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考