1. 项目概述:为 Hermes Agent 构建一个实时监控仪表盘
如果你正在使用 Hermes Agent 来管理多个独立的 AI 助手角色,比如让一个负责后端开发,一个负责前端,还有一个专职数据库管理,那么你很快就会遇到一个现实问题:如何同时掌握所有“分身”的状态?它们各自在聊什么?消耗了多少成本?哪个工具被频繁调用?手动去翻看每个 profile 目录下的日志和数据库文件,无疑是效率低下的。这正是 Argus 要解决的问题——一个为 Hermes Agent 多 Profile 场景量身定制的实时监控与管理仪表盘。
简单来说,Argus 是一个独立的 Web 应用,它通过直接、只读的方式,轮询 Hermes Agent 运行时产生的所有内部数据文件(如 SQLite 数据库、JSON 状态文件、日志等),将这些零散的信息聚合起来,并以一个清晰、统一的仪表盘视图呈现给你。你无需修改 Hermes Agent 的任何代码,也无需通过其 API(如果存在的话),Argus 就像一个安静的观察者,在不干扰原有工作流的前提下,为你提供全局视角。这对于团队协作、成本监控、性能调优以及日常运维来说,是一个强有力的增强工具。
2. 核心设计思路与架构解析
Argus 的设计哲学非常明确:非侵入式监控。这意味着它不会向 Hermes Agent 注入任何代码,不依赖可能变更的内部 API,而是直接读取 Hermes 在磁盘上持久化的运行时数据。这种方式的优势在于极高的稳定性和兼容性,只要 Hermes 的数据存储格式保持相对稳定,Argus 就能持续工作。当然,这也对 Argus 的数据解析逻辑提出了更高要求,必须精确理解 Hermes 的存储结构。
2.1 数据源与读取策略
Argus 监控的数据源覆盖了 Hermes Agent 运行时的方方面面,可以归纳为以下几类:
- 核心状态数据库 (
state.db):这是最重要的数据源,位于每个 Profile 目录下(如~/.hermes/profiles/backend/state.db)。它存储了会话、消息、工具调用记录、Token 使用量及成本估算等核心信息。Argus 需要解析其中的多个表,例如sessions,messages,tool_calls,token_usage等。 - 网关与进程状态文件:如
gateway_state.json和gateway.pid文件。通过检查gateway.pid文件是否存在以及对应的进程是否存活,可以判断该 Profile 的网关服务是否在运行。gateway_state.json则可能包含与外部平台(如 Telegram、Slack)的连接状态。 - 认证与凭证信息 (
auth.json):这里存储了不同 AI 模型提供商(如 OpenAI、Anthropic)的凭证池状态。Argus 可以监控这些凭证的健康状况,例如是否有凭证失效、配额是否将尽。 - 配置文件 (
config.yaml):读取每个 Profile 的配置,了解其使用的模型、提供商、压缩设置和安全策略,这有助于在仪表盘中展示 Profile 的基础配置信息。 - 日志文件 (
logs/目录):实时跟踪gateway.log可以观察消息流入流出、响应延迟等事件;分析errors.log有助于发现错误模式和频率限制等问题。 - 记忆数据库 (
memory-decay/memories.db):对于启用了记忆功能的 Profile,可以监控记忆条目的数量、激活历史和解码状态。 - 计划任务状态 (
cron/jobs.json):如果使用了定时任务功能,可以查看任务的执行历史和状态。
注意:由于是直接读取文件,必须确保 Argus 的数据收集器以只读模式访问这些文件,并且处理好并发读取。特别是 SQLite 数据库,在 Hermes Agent 正在写入时,Argus 应采用 WAL(Write-Ahead Logging)模式兼容的读取方式,避免阻塞或读取到不一致的数据。
2.2 系统架构与数据流
Argus 采用经典的数据采集-聚合-展示三层架构,数据流清晰且松耦合。
+-------------------+ +-------------------------+ +-------------------+ | | | | | | | Hermes Profile |---->| Argus 数据收集器 |---->| Argus 聚合数据库 | | (backend, | | (定时轮询,只读) | | (argus.db) | | frontend, dba) | | | | | | | +-------------------------+ +-------------------+ +-------------------+ | | | | | | (原始数据文件) | (清洗、转换后的聚合数据) | (HTTP 请求) v v v ~/.hermes/profiles/*/ 内存中的 Web 前端 state.db, config.yaml, 数据对象 (实时仪表盘) gateway.pid, logs/...- 数据收集器 (Collector):这是一个后台守护进程,以固定的时间间隔(例如每 3-5 秒)轮询所有已发现的 Hermes Profile 目录。它使用 SQLite 库和文件 IO 操作读取上述各类数据源,将原始数据解析为内部结构化的数据对象。
- 聚合数据库 (Aggregation DB):收集器将处理后的数据写入一个独立的 SQLite 数据库(例如
argus.db)。这个数据库是 Argus 专用的,其表结构针对监控查询优化,可能包含profile_status,message_stream,cost_aggregation,tool_usage_stats等表。这样做的好处是将高频的读取操作与 Hermes 的原生数据库解耦,也便于进行历史数据汇总和复杂查询。 - Web 前端与服务器:Argus 内置一个轻量级的 HTTP 服务器(例如使用 Python 的 FastAPI 或 Flask),提供 API 接口和静态文件服务。前端(可能使用现代框架如 Vue.js 或 React)通过 WebSocket 或短轮询从 API 获取实时数据,渲染出仪表盘。仪表盘的核心视图包括:
- Profile 选择器:动态列出所有可监控的 Profile。
- 单 Profile 仪表盘:展示该 Profile 的网关状态、活跃会话、当前活动消息流、Token 消耗与成本估算、工具调用频率分布、凭证健康状态等。
- 跨 Profile 概览:对比所有 Profile 的总成本、Token 消耗热力图、整体活动时间线等。
2.3 技术栈选型考量
根据项目描述,技术栈已初步选定:
- 后端 (Python):Python 是 Hermes Agent 本身使用的语言,选择它有利于复用其部分数据模型或工具库,减少环境复杂性。其丰富的生态系统(
sqlite3,aiofiles,watchdog用于文件监控,FastAPI用于构建高效 API)也完全满足需求。 - 数据库 (SQLite):用于 Argus 自身的聚合数据存储。SQLite 无需单独部署,零管理开销,非常适合这种桌面级或团队内部使用的监控工具。其性能对于这种规模的数据聚合和查询绰绰有余。
- 前端 (待定):考虑到实时性要求,一个支持 WebSocket 或 Server-Sent Events (SSE) 的现代框架是必要的。Vue.js 或 React 配合图表库(如 ECharts 或 Chart.js)可以快速构建出交互式仪表盘。由于是内部工具,对包大小要求不高,选择最熟悉或生态最丰富的即可。
实操心得:在架构设计初期,明确“只读”原则至关重要。这不仅能避免潜在的数据损坏风险,也让部署变得极其简单——只需将 Argus 安装在能访问 Hermes 数据目录的机器上即可。此外,将数据收集频率设计为可配置项(如
ARGUS_POLL_INTERVAL=3),便于在不同负载下平衡实时性和系统开销。
3. 环境准备与前置依赖安装
要运行或开发 Argus,首先需要确保基础环境就绪。以下步骤基于项目文档,并补充了详细的操作意图和避坑指南。
3.1 获取项目代码
第一步是克隆 Argus 的代码仓库。建议建立一个清晰的项目目录结构。
# 创建一个统一的项目存放目录,保持工作区整洁 mkdir -p ~/projects cd ~/projects # 克隆 Argus 仓库(请将 <YOUR_ARGUS_REPO_URL> 替换为实际仓库地址) git clone <YOUR_ARGUS_REPO_URL> argus # 进入项目目录 cd argus如果之前已经克隆过,只需直接进入目录:
cd ~/projects/argus3.2 安装核心依赖工具
Argus 依赖于 Hermes Agent 本身以及其他几个命令行工具来获取环境信息。必须安装指定版本以上的软件。
1. 安装 Hermes Agent CLI这是核心依赖。使用官方的一键安装脚本是最可靠的方式。
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash安装后,请重启终端或执行source ~/.bashrc(或~/.zshrc) 以使hermes命令生效。验证安装:
hermes --version2. 安装 Python 3.11+ 和 GitHub CLI (gh)
- macOS (使用 Homebrew):
brew install python@3.11 gh - Ubuntu/Debian:
sudo apt update sudo apt install -y python3 python3-venv python3-pip gh curl # 检查Python版本,如果默认版本低于3.11,可能需要通过 deadsnakes PPA 安装新版 python3 --version
3. 安装 Node.js 20+ (推荐使用 nvm)nvm 允许你在同一台机器上管理多个 Node.js 版本,非常灵活。
# 安装 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash安装完成后,关闭并重新打开终端,或者手动加载 nvm:
export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"然后安装并使用 Node.js 20:
nvm install 20 nvm use 20 # 设置为默认版本 nvm alias default 203.3 认证 GitHub CLI
Argus 的某些脚本或策略可能会用到gh命令与 GitHub 交互(例如拉取策略模板)。需要先完成登录。
gh auth login按照提示,选择登录方式(通常选择浏览器登录HTTPS方式最方便)。登录成功后,验证状态:
gh auth status你应该能看到类似Logged in to github.com as <你的用户名> (的输出。
3.4 环境验证与预检
在继续之前,运行一个完整的检查脚本来确认所有工具都已正确安装并可用。你可以手动逐条检查,也可以使用项目提供的预检脚本。
手动检查:
hermes --version # 应输出版本号,如 `hermes 0.1.0` gh --version # 应输出 gh 版本 gh auth status # 应显示已登录 python3 --version # 应显示 `Python 3.11.x` 或更高 node --version # 应显示 `v20.x.x` 或更高 npm --version # 应显示 npm 版本使用预检脚本:将以下内容保存为preflight_check.sh并运行:
#!/bin/bash set -e echo "=== 开始环境预检 ===" command -v hermes >/dev/null && echo "[OK] hermes 已安装" || { echo "[FAIL] hermes 未找到"; exit 1; } command -v gh >/dev/null && echo "[OK] gh 已安装" || { echo "[FAIL] gh 未找到"; exit 1; } command -v python3 >/dev/null && echo "[OK] python3 已安装" || { echo "[FAIL] python3 未找到"; exit 1; } command -v node >/dev/null && echo "[OK] node 已安装" || { echo "[FAIL] node 未找到"; exit 1; } echo "---- 版本信息 ----" hermes --version || echo "hermes 版本检查失败" gh --version || echo "gh 版本检查失败" python3 --version || echo "python3 版本检查失败" node --version || echo "node 版本检查失败" npm --version || echo "npm 版本检查失败" echo "=== 预检完成 ==="运行:
bash preflight_check.sh注意事项:如果
hermes命令找不到,很可能是安装脚本没有自动配置 PATH。检查~/.local/bin或~/.hermes/bin是否在 PATH 环境变量中。可以手动添加:echo 'export PATH="$HOME/.local/bin:$HOME/.hermes/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc。
4. 多 Profile 环境配置详解
Argus 的核心价值在于监控多个独立的 Hermes Profile。因此,在启动监控之前,你需要先按照团队角色(如后端、前端、DBA)创建好这些 Profile。这一步至关重要,它决定了 Argus 仪表盘中能看到哪些“监控对象”。
4.1 理解 Hermes Profile 的概念
一个 Hermes Profile 是一个完全独立的运行环境,包含:
- 专属的
config.yaml:模型、提供商、参数等配置。 - 独立的
.env文件:环境变量和敏感凭证。 - 自己的会话历史和记忆存储。
- 单独的工具技能集 (
skills)。 - 独立的状态数据库 (
state.db) 和日志。
这就像为不同的角色创建了不同的“电脑”,它们互不干扰。Argus 则可以同时查看所有这些“电脑”的屏幕。
4.2 创建标准化的多角色 Profile
项目文档推荐使用--clone --clone-from default参数来创建新 Profile。这样做的好处是,所有新 Profile 都从一个已知且干净的defaultProfile 克隆而来,确保了配置和技能起点的一致性,非常适合团队协作。
首先,检查现有 Profile:
hermes profile list你应该至少能看到一个
defaultprofile。使用标准命令创建三个角色 Profile:
hermes profile create backend --clone --clone-from default hermes profile create frontend --clone --clone-from default hermes profile create dba --clone --clone-from default命令解析:
hermes profile create <name>: 创建新 Profile。--clone: 指示 Hermes 复制源 Profile 的配置文件、环境变量和SOUL.md(角色定义文件)。--clone-from default: 明确指定克隆源为defaultProfile,避免因当前激活的 Profile 不同而导致克隆结果不一致。
验证创建结果:
hermes profile list输出应包含:
default,backend,frontend,dba。 进一步查看某个 Profile 的详情:hermes profile show backend这会显示该 Profile 的存储路径(如
~/.hermes/profiles/backend/)和配置来源。
4.3 配置 Profile 别名并测试
Hermes 在创建 Profile 时,通常会尝试在~/.local/bin/目录下生成一个同名的可执行文件(别名)。通过这个别名,你可以直接以该 Profile 的身份启动 Hermes。
验证或重新生成别名:
hermes profile alias backend hermes profile alias frontend hermes profile alias dba这些命令会确保在
~/.local/bin/下创建名为backend,frontend,dba的脚本。将别名目录加入 PATH:如果系统找不到这些命令,可能是因为
~/.local/bin不在 PATH 中。# 对于 Zsh 用户 echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc source ~/.zshrc # 对于 Bash 用户 echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc source ~/.bashrc测试别名是否生效:
backend chat这条命令会以
backendProfile 的身份启动 Hermes 的聊天界面。你可以输入exit退出。用同样方式测试frontend chat和dba chat。
实操心得:别名功能非常实用,它让你在不同角色间切换时,无需反复使用
--profile参数。但在某些系统上,别名生成可能会失败。如果遇到问题,除了检查 PATH,还可以直接查看~/.local/bin/目录下是否存在这些文件,并检查它们是否有可执行权限 (chmod +x ~/.local/bin/backend)。
4.4 理解并配置配对 (Pairing) 机制
配对机制是 Hermes 安全模型的一部分,尤其在团队使用 Telegram 或 Slack 机器人时尤为重要。当未经授权的用户直接私信(DM)你的机器人时,Hermes 的默认行为 (unauthorized_dm_behavior) 是进入配对流程,生成一个配对码。
核心配对命令:
hermes pairing list: 列出所有已配对和待处理的配对请求。hermes pairing approve telegram <PAIRING_CODE>: 批准来自 Telegram 的特定配对码。hermes pairing revoke telegram <USER_ID>: 撤销某个用户的访问权限。hermes pairing clear-pending: 清除所有待处理的配对请求。
对于团队监控场景的建议:
- 严格的白名单:在
config.yaml中,为每个 Profile 配置allowed_user_ids,只允许特定的团队成员 ID。这是最安全的方式。 - 使用 DM 配对流程:如果你希望临时授权某人,可以使用配对流程。Argus 的仪表盘未来可以集成此功能,方便管理员审批。
- 切勿在生产环境开启
ALLOW_ALL_USERS:特别是当 Profile 拥有终端或文件访问工具时,开放权限将带来严重安全风险。
注意事项:配对信息通常存储在 Profile 的
state.db或特定配置中。Argus 在读取这些数据时,应只用于展示“已配对用户数”或“待处理请求数”等监控信息,而不应提供直接进行配对批准/撤销的操作接口,除非其安全设计得非常完善。通常,这类敏感操作仍建议通过 Hermes CLI 完成。
完成以上所有步骤后,你的多 Profile 环境就已准备就绪。接下来,你需要根据backend/SETUP.md,frontend/SETUP.md,dba/SETUP.md等指南,为每个 Profile 配置具体的角色定义 (SOUL.md)、工具技能以及连接到相应的通信平台(如 Telegram Bot Token)。这些配置完成后,Argus 才能监控到有实际活动和数据的 Profile。
5. 数据收集器实现细节与避坑指南
数据收集器是 Argus 的“心脏”,它负责从分散的 Hermes 数据源中高效、准确地提取信息。实现一个健壮的收集器需要考虑多个方面。
5.1 实现策略:轮询与事件驱动
对于监控场景,通常有两种数据抓取策略:
- 定时轮询 (Polling):以固定间隔(如 3-5 秒)扫描所有数据源。实现简单,但可能产生延迟,且在不活动时造成不必要的开销。
- 事件驱动 (Event-driven):使用文件系统监控库(如 Python 的
watchdog)监听 Hermes 数据目录的变化(如state.db-wal文件的更新、新日志行的写入)。实时性更高,资源消耗更智能,但实现复杂度也更高。
推荐采用混合策略:
- 对于状态文件和配置(如
gateway.pid,config.yaml),采用轮询,因为它们变化不频繁。 - 对于核心数据库 (
state.db),可以结合轮询和 WAL 文件检测。通过检查state.db-wal文件的大小或修改时间来判断是否有新数据写入,从而决定是否执行查询。 - 对于日志文件,使用
watchdog监听logs/目录下的文件创建和追加事件,实现真正的实时日志跟踪。
5.2 安全读取 SQLite 数据库
Hermes 在运行时会对state.db进行写入。直接读取可能遇到“数据库被锁定”的错误。以下是安全读取的几种方法:
方法一:使用只读模式和 WAL 支持
import sqlite3 import time def safe_query_profile_db(profile_path, query): db_path = f"{profile_path}/state.db" for attempt in range(3): # 重试机制 try: # 以只读模式打开,并设置超时 conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True, timeout=5.0) conn.execute("PRAGMA query_only = ON;") # 确保只读(如果支持) cursor = conn.cursor() cursor.execute(query) results = cursor.fetchall() conn.close() return results except sqlite3.OperationalError as e: if "database is locked" in str(e) and attempt < 2: time.sleep(0.1) # 短暂等待后重试 continue else: # 记录错误或返回空数据 print(f"无法读取数据库 {db_path}: {e}") return []方法二:创建数据库副本(快照)在极短的时间内创建副本,然后读取副本。这能保证数据一致性,但 I/O 开销较大,适合低频次、需要复杂查询的场景。
# 使用命令行工具,在收集器外部进行 cp ~/.hermes/profiles/backend/state.db /tmp/state_backend_snapshot.db重要提示:绝对不要尝试在 Hermes 运行时写入其数据库。Argus 的角色是纯粹的观察者。任何写操作都可能导致数据损坏或 Hermes 崩溃。
5.3 解析关键数据表结构
要构建有意义的监控指标,必须理解state.db中关键表的结构。以下是一些推测的核心表及其可能用途(具体需根据 Hermes 实际 schema 调整):
sessions表:存储所有会话。- 监控指标:活跃会话数、会话创建时间、最后活动时间。
messages表:存储所有消息。- 监控指标:消息吞吐率(条/秒)、用户 vs 助手消息比例、最近 N 条消息预览。
tool_calls表:记录每次工具调用。- 监控指标:工具调用频率排行、平均执行时间、失败率。这是优化技能集的关键依据。
token_usage表:记录各模型的 Token 消耗。- 监控指标:实时 Token 消耗速率、按模型/提供商统计的成本、预测月度开销。
gateway_status(可能存在于gateway_state.json):网关连接状态。- 监控指标:平台(Telegram/Slack)连接是否正常、最后心跳时间。
收集器需要编写对应的 SQL 查询来提取这些数据。例如,获取最近5分钟内每个 Profile 的消息数:
SELECT profile_name, COUNT(*) as msg_count FROM messages WHERE timestamp > datetime('now', '-5 minutes') GROUP BY profile_name;5.4 处理多 Profile 的动态发现
Argus 不应硬编码 Profile 列表,而应能动态发现~/.hermes/profiles/目录下的所有子目录。每个子目录如果包含config.yaml和state.db,就可以被视为一个可监控的 Profile。
import os import yaml from pathlib import Path def discover_profiles(hermes_root="~/.hermes"): profiles = [] profiles_dir = Path(hermes_root).expanduser() / "profiles" if not profiles_dir.exists(): return profiles for item in profiles_dir.iterdir(): if item.is_dir(): config_file = item / "config.yaml" state_db = item / "state.db" # 基本验证:目录下应有配置文件和状态数据库 if config_file.exists() and state_db.exists(): try: with open(config_file, 'r') as f: config = yaml.safe_load(f) profiles.append({ "name": item.name, "path": str(item), "config": config }) except Exception as e: print(f"无法读取Profile配置 {config_file}: {e}") return profiles5.5 性能优化与错误处理
- 增量抓取:不要每次轮询都读取全部历史数据。为每类数据记录最后抓取的时间戳或位置(如最后读取的日志行号、最后一条消息的ID),下次只抓取新增部分。
- 异步 I/O:使用
asyncio和aiofiles库进行异步的文件和数据库读取,避免阻塞主线程,尤其是在监控大量 Profile 时。 - 优雅降级:如果某个 Profile 的数据文件暂时无法读取(如被锁定),应记录错误并跳过该 Profile 的本次轮询,而不是让整个收集器崩溃。在仪表盘上将该 Profile 标记为“数据暂不可用”。
- 资源限制:为收集器设置合理的轮询间隔和超时时间,避免对系统造成过大负担。
6. 聚合数据库设计与 Web 接口实现
收集到的原始数据需要经过清洗、转换和聚合,才能高效地供给前端仪表盘使用。这就是 Argus 自有数据库 (argus.db) 的作用。
6.1 聚合数据库 Schema 设计
argus.db的表结构设计应服务于监控查询,通常包含事实表和聚合表。
1. 事实表 (Fact Tables)存储从 Hermes 抓取的原始事件,带时间戳。
raw_messages:(id, profile_name, session_id, role, content, timestamp)raw_tool_calls:(id, profile_name, tool_name, arguments, result, success, duration_ms, timestamp)raw_token_usage:(id, profile_name, model, provider, prompt_tokens, completion_tokens, total_tokens, estimated_cost, timestamp)
2. 聚合表 / 物化视图 (Aggregated Views)为了快速响应前端查询,可以定期(如每分钟)计算并存储聚合数据。
profile_status:(profile_name, gateway_alive, active_sessions, last_activity, updated_at)– 每个 Profile 的当前状态快照。cost_aggregation_hourly:(profile_name, date_hour, total_cost, total_tokens)– 按小时聚合的成本。tool_usage_daily:(profile_name, tool_name, date, call_count, avg_duration, success_rate)– 工具使用日报。
3. 配置与元数据表
monitored_profiles:(name, path, is_active, discovered_at)– 记录已发现的 Profile。
使用 SQLite 的INSERT OR REPLACE或INSERT ON CONFLICT UPDATE来更新状态快照。对于时间序列数据,可以设置保留策略,例如自动删除30天前的原始数据。
6.2 构建后端 API 服务
使用一个轻量级但高性能的框架来提供 API。FastAPI 是一个绝佳选择,它自动生成 OpenAPI 文档,并原生支持异步操作。
# 示例:app/main.py from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse from .collector import DataCollector from .database import get_db_connection import asyncio import json app = FastAPI(title="Argus Monitoring API") # 允许前端跨域访问 app.add_middleware( CORSMiddleware, allow_origins=["*"], # 生产环境应限制为前端域名 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 初始化数据收集器 collector = DataCollector(poll_interval=5) @app.on_event("startup") async def startup_event(): asyncio.create_task(collector.start_polling()) @app.get("/api/profiles") async def list_profiles(): """获取所有被监控的Profile列表及其基础信息""" conn = get_db_connection() cursor = conn.cursor() cursor.execute("SELECT name, path, gateway_alive, active_sessions, last_activity FROM profile_status ORDER BY name") profiles = [dict(zip([col[0] for col in cursor.description], row)) for row in cursor.fetchall()] conn.close() return {"profiles": profiles} @app.get("/api/profile/{profile_name}/activity") async def get_recent_activity(profile_name: str, limit: int = 50): """获取指定Profile最近的活动消息""" conn = get_db_connection() cursor = conn.cursor() query = """ SELECT role, content, timestamp FROM raw_messages WHERE profile_name = ? ORDER BY timestamp DESC LIMIT ? """ cursor.execute(query, (profile_name, limit)) activity = [dict(zip([col[0] for col in cursor.description], row)) for row in cursor.fetchall()] conn.close() return {"profile": profile_name, "activity": activity} @app.websocket("/ws/live") async def websocket_endpoint(websocket: WebSocket): """WebSocket连接,用于向前端推送实时数据更新""" await websocket.accept() try: while True: # 这里可以从一个异步队列中获取实时更新数据 # 例如:data = await realtime_queue.get() data = collector.get_latest_snapshot() # 假设这个方法返回最新状态 await websocket.send_json(data) await asyncio.sleep(1) # 每秒推送一次 except WebSocketDisconnect: print("客户端断开连接")6.3 前端仪表盘核心组件
前端需要展示大量实时信息,良好的组件设计至关重要。
- Profile 选择侧边栏:一个可折叠的列表,展示所有 Profile 的名称和状态指示灯(绿色-运行中,红色-停止,黄色-异常)。点击后主视图切换至该 Profile 的详情。
- 主监控面板:
- 状态概览卡:以卡片形式展示网关状态、活跃会话数、今日成本、今日 Token 使用量等关键指标。
- 实时活动流:一个类似聊天界面的滚动区域,实时显示该 Profile 收到和发送的最新消息。可以使用不同颜色区分用户消息和助手消息。
- 工具调用图表:使用条形图或饼图展示最近24小时内各工具被调用的次数分布。
- 成本趋势图:使用折线图展示最近7天按小时或按天的成本消耗趋势。
- 凭证健康状态:以列表形式展示
auth.json中配置的各个提供商凭证,标记其状态(健康/接近限额/失效)。
- 跨 Profile 对比视图:
- 成本对比柱状图:对比所有 Profile 在过去一天/一周的总成本。
- 活动热力图:以日历热力图形式展示各 Profile 在不同时间段的活跃度(消息数量)。
技术实现建议:
- 状态管理:使用 Vuex (Vue) 或 Redux (React) 管理全局的 Profile 列表和实时数据。
- 实时更新:在组件挂载时建立 WebSocket 连接,监听后端推送的更新,并更新对应的状态存储。
- 图表库:ECharts 功能强大且文档丰富,是制作复杂监控图表的优秀选择。Chart.js 则更轻量,适合基础图表。
- UI 框架:使用 Element Plus (Vue) 或 Ant Design (React) 等成熟组件库快速搭建界面,专注于业务逻辑。
6.4 部署与运行
Argus 最终应打包为一个可以简单启动的服务。
作为独立应用运行:
# 安装Python依赖 cd ~/projects/argus/backend pip install -r requirements.txt # 启动后端API服务器和收集器 python main.py # 在另一个终端启动前端开发服务器 cd ~/projects/argus/frontend npm install npm run dev然后浏览器访问http://localhost:5173(Vite 默认端口)。
使用进程管理工具 (推荐用于长期运行):使用systemd(Linux) 或pm2来管理 Argus 的后台进程,确保其崩溃后能自动重启。
# 使用 pm2 示例 pm2 start ecosystem.config.jsecosystem.config.js配置示例:
module.exports = { apps: [{ name: 'argus-backend', script: 'main.py', interpreter: 'python3', cwd: '/path/to/argus/backend', env: { 'ARGUS_DATA_DIR': '/home/user/.hermes' } }] }7. 常见问题排查与运维技巧
在实际部署和运行 Argus 过程中,你可能会遇到以下典型问题。这里提供排查思路和解决方法。
7.1 数据收集类问题
问题1:收集器报错 “database is locked” 或无法读取state.db。
- 原因:Hermes Agent 正在频繁写入数据库,导致文件被独占锁定。
- 排查:
- 检查 Hermes 网关是否正在高负载运行(例如,正在处理大量消息)。
- 确认收集器连接数据库时使用了只读模式和适当的超时、重试机制(见5.2节)。
- 解决:
- 增加重试间隔和次数:将重试等待时间从 0.1 秒增加到 0.5 秒,重试次数增加到 5 次。
- 调整轮询频率:在 Hermes 高负载时段,降低 Argus 的轮询频率(如从 3 秒改为 10 秒)。
- 使用快照方式:对于非实时性要求极高的数据(如成本统计),改为每小时一次创建数据库副本并读取副本。
问题2:监控仪表盘上某个 Profile 显示“无数据”或状态一直不变。
- 原因:
- 该 Profile 的路径未被正确发现。
- 该 Profile 的网关未运行,没有产生新数据。
- 收集器对该 Profile 目录没有读取权限。
- 排查:
- 在 Argus 后端日志中查看
discover_profiles函数的输出,确认目标 Profile 是否在列表内。 - 检查
~/.hermes/profiles/<profile_name>/gateway.pid文件是否存在,以及 PID 对应的进程是否存活。 - 手动运行
ls -la ~/.hermes/profiles/<profile_name>/检查文件权限。
- 在 Argus 后端日志中查看
- 解决:
- 确保 Hermes Profile 已正确启动 (
backend start或hermes --profile backend start)。 - 确保运行 Argus 的用户对 Hermes 的数据目录有读取权限。
- 在 Argus 配置中手动指定 Profile 路径。
- 确保 Hermes Profile 已正确启动 (
7.2 前端展示类问题
问题3:WebSocket 连接频繁断开或数据更新延迟高。
- 原因:
- 网络不稳定或代理问题。
- 后端数据生成或推送频率过高,导致前端处理不过来或连接超时。
- 浏览器标签页进入后台,部分浏览器会限制 WebSocket 心跳。
- 排查:
- 打开浏览器开发者工具的“网络(Network)”标签,查看 WebSocket 连接状态和消息频率。
- 查看后端日志,看 WebSocket 广播循环是否有异常或阻塞。
- 解决:
- 在前端 WebSocket 客户端实现自动重连逻辑。
- 降低后端推送频率(如从每秒一次改为每两秒一次),并对推送的数据进行差分更新,只发送变化的部分。
- 使用 Server-Sent Events (SSE) 作为备选方案,它在某些网络环境下比 WebSocket 更稳定。
问题4:图表渲染大量数据时页面卡顿。
- 原因:前端一次渲染了过多数据点(例如一年的每秒成本数据)。
- 解决:
- 数据聚合:在后端 API 层面对历史数据进行聚合。例如,前端请求过去30天的成本数据时,后端按小时或按天聚合后再返回,而不是返回每秒的数据点。
- 虚拟滚动/分页:对于活动消息流这类列表数据,实施虚拟滚动,只渲染可视区域内的条目。
- 图表优化:ECharts 等库支持大数据集下的
dataZoom和采样显示,开启相关配置。
7.3 性能与资源类问题
问题5:Argus 服务本身占用 CPU 或内存过高。
- 原因:
- 轮询间隔太短,且监控的 Profile 数量众多。
- 数据聚合查询过于复杂,或没有为
argus.db建立有效索引。 - 内存泄漏,例如每次轮询都创建新的数据库连接而未关闭。
- 排查:
- 使用
top或htop命令观察 Argus 进程的资源占用。 - 检查
argus.db文件大小是否增长异常。 - 使用 Python 内存分析工具(如
tracemalloc)检查内存使用情况。
- 使用
- 解决:
- 根据实际需求调整
ARGUS_POLL_INTERVAL环境变量,找到一个平衡点。 - 为
argus.db中常用于查询的字段(如profile_name,timestamp)创建索引。 - 确保数据库连接、文件句柄等资源在使用后正确关闭。使用连接池管理数据库连接。
- 根据实际需求调整
问题6:argus.db文件体积增长过快。
- 原因:原始事实数据没有设置保留策略,全部永久保存。
- 解决:在收集器中实现一个清理旧数据的定时任务。
# 示例:每天凌晨清理30天前的原始数据 import schedule import time def cleanup_old_data(): conn = get_db_connection() cursor = conn.cursor() cursor.execute("DELETE FROM raw_messages WHERE timestamp < datetime('now', '-30 days')") cursor.execute("DELETE FROM raw_tool_calls WHERE timestamp < datetime('now', '-30 days')") # ... 清理其他表 conn.commit() conn.close() print("已清理旧数据") schedule.every().day.at("02:00").do(cleanup_old_data)
7.4 安全与配置类问题
问题7:如何安全地部署 Argus,避免未授权访问?
- 风险:Argus 仪表盘可能包含敏感信息(会话内容、Token 消耗)。
- 建议:
- 不要将 Argus 服务暴露在公网。仅在本地网络或通过 SSH 隧道访问。
- 为 Argus 后端 API 添加简单的认证。例如,在 FastAPI 中使用 HTTP Basic Auth 或一个简单的静态 Token 验证。
from fastapi import Security, HTTPException from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials security = HTTPBearer() async def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)): if credentials.credentials != "YOUR_STATIC_TOKEN": raise HTTPException(status_code=403, detail="Invalid token") @app.get("/api/profiles", dependencies=[Depends(verify_token)]) - 使用反向代理:通过 Nginx 将 Argus 前端和后端服务绑定在一起,并在 Nginx 层配置 HTTPS 和访问控制。
问题8:如何监控 Argus 自身的健康状态?
- 方案:Argus 应该暴露一个简单的健康检查端点。
然后可以使用@app.get("/health") async def health_check(): # 检查收集器线程是否存活,数据库是否可连接 collector_ok = collector.is_alive() db_ok = False try: conn = get_db_connection() db_ok = conn.execute("SELECT 1").fetchone() is not None conn.close() except: pass status = "healthy" if (collector_ok and db_ok) else "unhealthy" return {"status": status, "collector": collector_ok, "database": db_ok}systemd,supervisor或pm2的健康检查功能,或者使用外部的监控系统(如 Prometheus)来抓取这个端点。
通过系统地实施以上设计方案和规避这些常见陷阱,你就能构建出一个稳定、实用且信息丰富的 Hermes Agent 多 Profile 监控中心。它不仅能让团队对 AI 助手的运行状况了如指掌,更能通过数据洞察来优化使用策略和成本控制。