如何追踪一次 AI 盯盘 Agent 的运行:PanWatch 可观测体系 trace_id + agent_runs 完整指南
【免费下载链接】PanWatchPanWatch — AI stock monitoring for A-shares, HK & US markets, powered by TradingAgents. Portfolio insights, real-time alerts & automated reports.|盯盘侠:覆盖 A股/港股/美股的 AI 盯盘、持仓分析、实时提醒与自动报告。项目地址: https://gitcode.com/GitHub_Trending/pa/PanWatch
PanWatch 是一款覆盖 A股、港股、美股的 AI 盯盘工具,而它的可观测体系解决了 AI 应用最头疼的问题:一次 Agent 运行到底发生了什么?PanWatch 用结构化日志 trace_id 贯穿全链路,再配合一张自建 agent_runs 运行表,让每一次定时任务、手动触发都能被完整追踪、回放与排查。
为什么 AI 盯盘系统需要一套可观测体系
AI Agent 和传统脚本不同:一次运行要拉行情、读新闻、调大模型、发通知,链路长、耗时久、还会遇到外部数据源限流。出了问题你只想知道三件事:
- 这次运行跑到哪一步了?
- 它成功还是失败?花了多久?
- 失败原因是什么?
PanWatch 的答案分两层:细粒度层是带 trace_id 的结构化日志(存在log_entries表),粗粒度层是记录运行生命周期的agent_runs表。两层共用同一个 trace_id,粗看运行表、细看日志流,互相对齐。
结构化日志:一条 trace_id 贯穿全链路
log_context:给日志打上"上下文标签"
核心实现在 src/platform/observability/log_context.py。PanWatch 用 Python 标准库的contextvars保存当前执行范围的上下文,包括:
trace_id:本次运行的唯一追踪标识run_id:运行 id(与 trace_id 对齐)agent_name:是哪个 Agent(如tradingagents、intraday_monitor)event:事件类型(如ta_progress进度事件)tags:任意扩展字典
使用方式是一个上下文管理器:任务开始时进入,所有日志自动带上这些字段,结束后自动恢复,不会污染其他并发任务。
更巧妙的是 install_log_record_factory():它在启动时替换了全局的日志 record 工厂,业务代码写logger.info(...)时完全无感知,每条日志记录都会被自动注入trace_id、agent_name等字段。这意味着整个项目不需要改任何一行日志语句,就完成了结构化改造。
DBLogHandler:日志落库,还能按链路检索
日志不只是打到控制台。src/platform/observability/log_handler.py 中的DBLogHandler把日志缓冲后批量写入 SQLite 的log_entries表(表结构见 src/platform/persistence/models.py),并建了trace_id索引,支持按链路直接检索。它的设计很有工程感:
- 批量节流:每 80 条或 ERROR 级别立即落库,最长 1 秒一刷,避免高频写库;
- 保留策略:基础设施噪音日志(httpx、sqlalchemy 等)单独限额 3 万条,总量上限 12 万条,优先保留业务日志;
- 自愈降级:写库失败只计数告警,绝不影响主流程。
启动接线在 server.py 的setup_logging()中完成:安装 record 工厂、挂控制台 handler 和 DB handler。
自建 agent_runs 运行表:粗粒度生命周期
一张表看懂每次运行
agent_runs表定义在 src/platform/persistence/models.py,字段包括:agent_name、status(running / success / failed)、trace_id、trigger_source(schedule / manual / api)、duration_ms耗时、result摘要、error错误信息、notify_sent通知是否发出等。
写入逻辑在 src/modules/automation/agent_runs.py,分两步:
- 任务真正开始前写入一条
status="running"的生命周期记录(start_agent_run())。这一步是关键:即使任务卡死在数据采集阶段,UI 也能看到"正在运行中"。 - 任务结束时回填终态(record_agent_run()),把 running 记录原地更新为 success/failed,而不是新增一行——同一个 trace_id 只有一条完整记录,写入是幂等的。
trace_id 的命名也携带了业务语义,例如手动触发单只股票时形如man-{agent}-{stock}-{毫秒时间戳}(见 server.py),一眼就能看出触发来源和标的。
用运行表判断"任务是否还在跑"
agent_runs表还有一个容易被忽略的高价值用法:find_active_tradingagents_trace()。当用户重复点击"立即分析"、或 API 和调度器同时触发同一个标的时,PanWatch 会先查运行表:
- 若该标的存在 45 分钟内的
running记录 → 直接复用,不重复创建任务(外部数据源限流重试期间也不会误判为卡死); - 没有 running 记录时,再退回查
log_entries里 30 分钟内的ta_progress进度日志做兜底。
这让幂等触发不依赖任何外部状态服务,一张本地表就搞定。
前端怎么消费这两份数据
- 运行历史:
agent_runs直接对应 Agents 页面的运行列表(成功/失败、耗时、触发源)。 - 实时进度时间线:TradingAgents 执行时,src/modules/automation/tradingagents/observability.py 把每个节点事件写成
event="ta_progress"的日志,前端按trace_id + event轮询/api/agents/runs/{trace_id}/progress聚合出阶段进度,无需额外的消息通道。 - 日志面板:frontend/packages/biz-ui/src/components/logs-modal.tsx 展示
log_entries内容,每条日志附带trace_id列,可直接按链路过滤,UI 层还会把 logger 名映射成中文便于阅读。
可选进阶:标准 OTel 桥接,默认零副作用
想接 Jaeger / Tempo / Langfuse 等标准 APM?PanWatch 在 src/platform/observability/otel.py 提供了一个薄桥接:
- 未配置
OTEL_EXPORTER_OTLP_ENDPOINT或未安装 SDK 时,init_otel()直接返回 False,所有 span 接口降级为 no-op,不影响默认部署; - 配置后,Agent 一次运行 → root span(复用
agent_runs的 trace_id 关联)、单次 LLM 调用 → 遵循 GenAI 语义约定的子 span(自动带上 token 用量)、TradingAgents 节点 → 子 span。
"自建体系打底 + 标准栈可选叠加",两条路都能拿到实证,且不破坏任何既有行为。依赖见 requirements-otel.txt。
快速上手:三步看懂这套体系
- 触发一次运行:在 Agents 页面手动触发任一 Agent,或等待定时任务执行;
- 看运行记录:运行列表里找到对应记录,注意其
trace_id与状态流转 running → success/failed; - 钻取日志:打开日志面板按该 trace_id 过滤,即可按时间线还原这次运行拉了哪些数据、调了几次模型、通知是否发出。
整套体系不依赖重型中间件,核心只是contextvars+ SQLite 两张表,但正是这种克制的设计,让一个个人开源项目也能拥有生产级的问题排查能力——这对任何想给 AI Agent 加上"黑匣子"的团队,都是一份可以直接参考的完整方案。
【免费下载链接】PanWatchPanWatch — AI stock monitoring for A-shares, HK & US markets, powered by TradingAgents. Portfolio insights, real-time alerts & automated reports.|盯盘侠:覆盖 A股/港股/美股的 AI 盯盘、持仓分析、实时提醒与自动报告。项目地址: https://gitcode.com/GitHub_Trending/pa/PanWatch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考