从监控焦虑到”看一眼就行”

前几天,我的AI Agent集群跑着 Hermes 智能体引擎、OpenLLM 推理服务、GA Control Center 仪表盘……服务一多,心里就开始发毛:那个服务挂了没?响应还正常吗?

传统的监控方案太重了——Prometheus + Grafana 那一套,配置起来少说要半天。而且我只是想看一眼现在的界面长什么样,心跳是不是还跳着。

所以我想:既然我的 AI Agent 已经有了 Vision API(能截图),能不能拿它来当监控摄像头用?

于是就有了这个项目——用 Vision API 做服务监控,直接在 GA Control Center 里开一个”监控页”,实时截图展示各服务的运行状态。

Vision API:从OCR工具到监控摄像头

我在 v169 版本已经把 Vision API 封装成了一个独立的 systemd 服务(vision_api_server.service),跑在 8765 端口上。它的核心能力其实很简单:

1
2
GET /screenshot?url=http://localhost:8501
→ 返回 Xvfb 浏览器截图

底层走的是 Chromium headless + Xvfb 虚拟显示器,能完整渲染页面,返回 PNG 图片。之前主要拿来做 OCR 识别和页面验证,但我发现——截图本身就是最直观的监控数据

你能一眼看出:

  • 页面有没有加载完整
  • 仪表盘的实时数值对不对
  • 服务有没有报错页面

不需要解析 metrics,不需要写告警规则。看一眼,全知道

集成到 Control Center:前后只花了2小时

GA Control Center 基于 Streamlit,之前已经搭好了框架,有 7 个页面。要新增一个”服务监控”页面,只需要做三件事:

1. 添加截图函数

Streamlit 有自带的缓存机制,配合 st.cache 可以避免重复截图。但我想要手动刷新 + 自动轮播两种模式,所以设计了更灵活的架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
def get_multi_screenshots(urls):
"""并行截图多个服务,返回 {name: image_bytes}"""
results = {}
for name, url in urls:
try:
params = urllib.parse.urlencode({'url': url})
resp = urllib.request.urlopen(
f"http://localhost:8765/screenshot?{params}", timeout=20
)
results[name] = {"data": resp.read(), "status": "ok"}
except Exception as e:
results[name] = {"data": None, "status": f"failed: {str(e)[:50]}"}
return results

2. 设计监控面板布局

Streamlit 的 st.columns 让我轻松实现了双列卡片布局——每个服务一张截图卡,显示:

  • 服务名称 + 端口
  • 截图(带时间戳)
  • 状态指示(✅ 正常 / ⚠️ 失败)
  • 最后刷新时间
1
2
3
4
5
6
7
8
cols = st.columns(2)
for i, (name, url) in enumerate(services):
with cols[i % 2]:
st.subheader(name)
if screenshot_data:
st.image(screenshot_data, use_column_width=True)
else:
st.error(f"⚠️ 无法连接 {url}")

3. 一键全部截图

在页面顶部放了一个「📸 全部截图」按钮,再配合侧栏的自动刷新滑块(10-120秒可调),实现了全自动轮播监控

1
2
3
4
5
if st.button("📸 全部截图") or st.session_state.auto_screenshot:
with st.spinner("正在截图所有服务..."):
screenshots = get_multi_screenshots(service_urls)
# 更新 session_state
st.rerun()

实战效果:三服务截图对比

目前我监控了三个核心服务,来看实际效果:

GA Control Center (port 8501)

Control Center 本身是最复杂的页面——多个面板、实时数据、图表。Vision API 的截图效果:

  • ✅ 页面完整加载
  • ✅ 侧栏导航可见
  • ✅ 实时数据正确渲染
  • ⏱ 截图耗时约 5-8 秒

Hermes Dashboard (port 9119)

Hermes 智能体引擎的仪表盘,展示活跃会话和任务队列:

  • ✅ 数据面板完整
  • ✅ Agent 状态指示器正常
  • ⏱ 截图耗时约 3-5 秒

GA Web App (port 8082)

主要的 Web 应用界面:

  • ✅ 页面完全渲染
  • ✅ 交互元素可见
  • ⏱ 截图耗时约 2-4 秒

三个服务截图总耗时约 12-18 秒,对于轮播监控来说完全可以接受。

踩坑与反脆弱设计

在实现过程中,遇到几个典型的”AI Agent 反脆弱”问题:

坑1:截图服务挂掉怎么办?

Vision API 是一个独立进程,它也可能挂。所以监控页必须有失败优雅降级

1
2
3
4
5
# 截图失败 → 显示错误 + 列表汇总
failed = [name for name, s in screenshots.items() if s["status"] != "ok"]
if failed:
st.warning(f"⚠️ 以下服务截图失败:{', '.join(failed)}")
# 继续显示已成功的截图,不整体崩溃

坑2:页面渲染超时

Chromium 截图有时会卡住,所以每个请求设 20 秒超时:

1
resp = urllib.request.urlopen(url, timeout=20)

坑3:自动刷新不要无限循环

Streamlit 的 st.rerun() 需要配合条件判断,避免死循环:

1
2
3
4
5
6
7
# 只在以下条件同时满足时自动截图:
# 1. 侧栏"自动截图"开启
# 2. 当前在监控页面
# 3. 距离上次截图 > 间隔时间
if st.session_state.auto_screenshot and page == "📷 服务监控":
if time.time() - last_refresh > interval:
# 截图 + rerun

架构全景

最后放一张架构图,看看 Vision API 监控在整个 Agent 系统中的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────┐     ┌──────────────────┐
│ GA Control │ │ Hermes Agent │
│ Center (8501) │ │ Dashboard(9119) │
└──────┬──────────┘ └───────┬──────────┘
│ │
▼ ▼
┌─────────────────────────────────────────┐
│ Vision API (port 8765) │
│ ┌───────────────────────────────┐ │
│ │ Xvfb + Chromium Headless │ │
│ │ → 截图 → 返回 PNG │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────────┘


┌─────────────────────────────────────────┐
│ GA Control Center — 📷 服务监控页 │
│ - 定时截图轮播 │
│ - 失败告警 │
│ - 手动/自动双模式 │
└─────────────────────────────────────────┘

总结与下一步

这套监控方案的核心理念是:直接看界面,比看数字更直观

工具链很简单:

  • Vision API(截图能力)→ systemd 服务化
  • Streamlit(展示层)→ 现有 Control Center 扩展
  • 配置化服务列表 → 想加新服务就加一行

接下来想做的:

  1. 截图对比:保存历史截图,对比 UI 变化
  2. 智能告警:用 OCR 识别截图中的错误关键词
  3. 多节点监控:扩展到远程服务的截图

给 AI 装上眼睛,它就能帮你看着这个世界——这大概就是 AI Agent 进化的方向之一吧。