☰
AI Agent Harness实时监控大屏搭建实战:TaoToken统一Key接入React可视化面板
2026/10/3 7:07:08 网站建设 项目流程

1. 从「盲跑」到「看得见」:AI Agent Harness 监控大屏要解决什么

AI Agent Harness 实时监控大屏,本质上是给跑在后台的智能体集群装一块「仪表盘」:把任务链路、调用耗时、异常告警这些原本散落在日志里的运行态数据,用 React 可视化面板实时呈现出来。它适合已经用 Harness 管理多 Agent 协作、但排查问题还靠翻日志的团队,也适合刚接触 Agent 可观测性、想先跑通一套 Demo 的开发者。

我见过太多团队把 Agent 系统上线后就不管了。用户说响应慢,你去翻几十 G 日志,最后发现是某个工具调用接口超时;月底看账单吓一跳,原来有个 Agent 死循环调工具,白白烧掉几千万 Token;线上 Agent 突然不响应,排查半小时才发现某个实例早就离线了,根本没人知道。这些问题的共同点是:Agent 跑起来之后,你不知道它在干嘛、调了多少次工具、延迟多少、有没有异常、消耗了多少 Token,完全是「盲跑」状态。

Harness 作为 AI Agent 的管控框架,本身已经内置了全链路指标采集能力:每个 Agent 实例定期向控制平面上报心跳、任务状态、工具调用日志和异常信息,控制平面做初步聚合后,通过 REST API 和 WebSocket 暴露给外部消费。我们要做的监控大屏,只需要消费这些接口做可视化,不需要自己给 Agent 埋点。整条链路是:Agent 实例集群上报指标 → Harness 控制平面聚合数据 → 监控大屏前端展示。

这篇内容会带你从零搭一套可用的面板:先讲清楚监控指标怎么设计,再给出 TaoToken 统一 Key 的接入配置,然后拆解大屏组件、写数据轮询代码,最后附上本地启动和指标验证步骤。技术栈选 React 18 + ECharts + Ant Design + socket.io-client + dayjs,都是国内监控场景里最常用的组合。ECharts 对中文支持好、图表类型全,仪表盘、热力图、关系图这些监控常用图形都有,大数据量渲染性能也比 Chart.js 更稳。

2. TaoToken 统一 Key 接入:给 Harness 配一条稳定的模型通道

在搭大屏之前,得先解决一个前置问题:Harness 里的 Agent 要调用大模型,如果每个 Agent 各配一套 Key,管理起来会非常乱,而且不同模型的 Base URL、鉴权方式还不一样。TaoToken 提供统一 Key 和统一 API 通道,把模型调用收敛到一个入口,Harness 侧只需要维护一份配置,监控大屏里统计 Token 消耗、调用延迟时也有统一的数据来源。

TaoToken 的官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api (这个不加 UTM)。你需要先在控制台创建一个 API Key,控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建好之后,把 Key 填到 Harness 的模型配置里,Base URL 指向 TaoToken 的 API 地址。

这里要强调一个原则:Harness 里所有 Agent 的模型调用都走这一条通道,不要有的走直连、有的走统一 Key,否则监控大屏统计出来的 Token 消耗和延迟数据会失真。统一通道之后,你在 TaoToken 控制台能看到每个 Key 的调用量,在大屏上也能按 Agent 维度拆分,两边数据能对上。

如果你用的是 Claude Code 这类编码 Agent,TaoToken 也提供了对应的接入方式,文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。Claude Code 的接入可以参考 https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite ,里面写了 Base URL 和 Key 的填法。对于长期跑编码任务或 Agent 工作流的场景,Coding Plan 会更划算,入口是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。

配好之后,Harness 控制平面就能正常调用模型了,Agent 的心跳、任务、工具调用数据也会正常上报,接下来我们才能在大屏上看到真实的运行态指标。这一步没配好,后面大屏上全是空数据,排查起来会很痛苦。

3. 可复制配置:Harness 模型通道与大屏项目初始化

这一节给出可以直接复制的配置片段。先配 Harness 侧的模型通道,再初始化 React 大屏项目。

Harness 的模型配置一般放在控制平面的配置文件里,不同版本路径可能略有差异,常见的是config/model.yaml或config/agent.yaml。下面是一份走 TaoToken 统一通道的 YAML 配置,把 Base URL、Key、Model ID 三件套都写全:

# config/model.yaml model_provider: name: taotoken base_url: "https://taotoken.net/api" api_key: "sk-你的TaoTokenKey" default_model: "claude-sonnet-4-20250514" timeout: 60 max_retries: 3 agent_runtime: heartbeat_interval: 10 # Agent 心跳上报间隔,单位秒 metrics_push: true # 开启指标上报 stream_path: "/api/v1/stream" # WebSocket 推送路径

如果你用的是 JSON 格式的配置,等价写法如下:

{ "model_provider": { "name": "taotoken", "base_url": "https://taotoken.net/api", "api_key": "sk-你的TaoTokenKey", "default_model": "claude-sonnet-4-20250514", "timeout": 60, "max_retries": 3 }, "agent_runtime": { "heartbeat_interval": 10, "metrics_push": true, "stream_path": "/api/v1/stream" } }

注意 Base URL 后面不要多加/v1,TaoToken 的 API 地址已经包含了版本路径,多写会导致 404。Key 建议用环境变量注入,不要硬编码在配置文件里,生产环境尤其要注意。

接下来初始化 React 大屏项目。用 Vite 创建项目,安装依赖:

# 创建 React 项目 pnpm create vite agent-monitor-dashboard --template react cd agent-monitor-dashboard # 安装依赖 pnpm install echarts echarts-for-react antd socket.io-client dayjs # 启动项目 pnpm dev

项目结构建议这样组织,后面组件拆分和轮询逻辑都按这个结构来:

agent-monitor-dashboard/ ├── src/ │ ├── components/ │ │ ├── OverviewCards.jsx # 顶部概览卡片 │ │ ├── StatusPie.jsx # Agent 状态饼图 │ │ ├── ToolBar.jsx # 工具调用 Top10 │ │ ├── TrendLine.jsx # 请求量&延迟趋势 │ │ ├── ErrorRing.jsx # 异常类型分布 │ │ ├── AlertTable.jsx # 实时告警列表 │ │ └── LogStream.jsx # 实时日志流 │ ├── hooks/ │ │ └── useMetrics.js # 数据轮询 Hook │ ├── App.jsx │ └── App.css ├── .env.local # 存放 API 地址等环境变量 └── package.json

.env.local里放两个变量,一个是 Harness 控制平面地址,一个是轮询间隔:

VITE_HARNESS_API=http://localhost:8080 VITE_POLL_INTERVAL=2000

这样配置的好处是,本地开发和生产环境只需要改环境变量,代码不用动。项目初始化完成后,先跑pnpm dev确认页面能打开,再往下写组件。

4. 大屏组件拆分与数据轮询:从静态布局到实时刷新

大屏布局有个通用原则:重要指标放顶部,辅助指标放两侧,详情内容放底部,整体用深色主题,避免长时间盯屏视觉疲劳,同时突出颜色告警。最终布局是:顶部标题栏加 5 个全局概览数字卡片;中间左侧 Agent 状态饼图和工具调用 Top10 柱状图;中间右侧近 1 小时请求量&延迟双 Y 轴折线图和异常类型分布环形图;底部实时告警表格和实时日志滚动流。

先写数据轮询 Hook,这是整个大屏的数据源。用useMetrics封装轮询逻辑,每 2 秒拉一次 Harness 的指标接口,同时保留 WebSocket 推送的扩展位:

// src/hooks/useMetrics.js import { useState, useEffect, useRef } from 'react'; const API_BASE = import.meta.env.VITE_HARNESS_API || 'http://localhost:8080'; const POLL_INTERVAL = Number(import.meta.env.VITE_POLL_INTERVAL) || 2000; export function useMetrics() { const [overview, setOverview] = useState({ onlineAgent: 0, totalTask: 0, avgLatency: 0, errorRate: 0, tokenCost: 0, }); const [statusData, setStatusData] = useState([]); const [toolRank, setToolRank] = useState([]); const [trendData, setTrendData] = useState({ times: [], requests: [], latencies: [] }); const [errorDist, setErrorDist] = useState([]); const [alerts, setAlerts] = useState([]); const [logs, setLogs] = useState([]); const timerRef = useRef(null); const fetchMetrics = async () => { try { const res = await fetch(`${API_BASE}/api/v1/metrics/summary`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); setOverview(data.overview); setStatusData(data.statusDistribution); setToolRank(data.toolRank); setTrendData(data.trend); setErrorDist(data.errorDistribution); setAlerts(data.alerts); setLogs(data.logs); } catch (err) { console.error('拉取指标失败:', err.message); } }; useEffect(() => { fetchMetrics(); timerRef.current = setInterval(fetchMetrics, POLL_INTERVAL); return () => clearInterval(timerRef.current); }, []); return { overview, statusData, toolRank, trendData, errorDist, alerts, logs }; }

这个 Hook 的关键点是:用setInterval做轮询,组件卸载时清理定时器,避免内存泄漏;接口失败时只打日志不中断轮询,保证大屏不会因为一次网络抖动就卡死。如果 Harness 的 WebSocket 推送更实时,可以把fetchMetrics换成socket.on监听,数据结构保持一致即可。

接下来写概览卡片组件,把 5 个核心指标渲染出来,异常率超过阈值时变红:

// src/components/OverviewCards.jsx import React from 'react'; import { Row, Col, Card } from 'antd'; const cardStyle = { background: '#1e293b', textAlign: 'center', border: 'none' }; const labelStyle = { color: '#94a3b8', fontSize: 14, marginBottom: 8 }; const valueStyle = { fontSize: 32, fontWeight: 'bold' }; export default function OverviewCards({ overview }) { const items = [ { label: '在线 Agent 数', value: overview.onlineAgent, color: '#22c55e' }, { label: '今日总任务数', value: overview.totalTask, color: '#ffffff' }, { label: '平均响应延迟', value: `${overview.avgLatency}s`, color: '#3b82f6' }, { label: '异常率', value: `${overview.errorRate}%`, color: overview.errorRate > 5 ? '#ef4444' : '#22c55e', }, { label: '今日 Token 消耗', value: `¥${overview.tokenCost}`, color: '#f59e0b' }, ]; return ( <Row gutter={16} style={{ marginBottom: 20 }}> {items.map((item) => ( <Col span={4} key={item.label}> <Card style={cardStyle}> <div style={labelStyle}>{item.label}</div> <div style={{ ...valueStyle, color: item.color }}>{item.value}</div> </Card> </Col> ))} </Row> ); }

状态饼图用 ECharts 的环形图,颜色按行业通用规范:绿色运行中、黄色空闲、红色异常、灰色离线:

// src/components/StatusPie.jsx import React from 'react'; import ReactECharts from 'echarts-for-react'; export default function StatusPie({ data }) { const option = { color: ['#22c55e', '#f59e0b', '#ef4444', '#94a3b8'], tooltip: { trigger: 'item', formatter: '{b}: {c}个 ({d}%)', backgroundColor: '#0f172a', borderColor: '#334155', textStyle: { color: '#fff' }, }, legend: { orient: 'vertical', left: 'left', textStyle: { color: '#fff' }, }, series: [ { name: 'Agent 状态', type: 'pie', radius: ['40%', '70%'], label: { show: true, color: '#fff', formatter: '{b}: {d}%' }, data, }, ], }; return <ReactECharts option={option} style={{ height: '100%' }} theme="dark" />; }

趋势折线图用双 Y 轴,左边请求量、右边延迟,两个指标放一起能直观看出延迟和请求量的关系:

// src/components/TrendLine.jsx import React from 'react'; import ReactECharts from 'echarts-for-react'; export default function TrendLine({ trendData }) { const option = { tooltip: { trigger: 'axis', backgroundColor: '#0f172a', borderColor: '#334155', textStyle: { color: '#fff' }, }, legend: { data: ['请求量', '平均延迟'], textStyle: { color: '#fff' }, top: 0 }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: { type: 'category', boundaryGap: false, data: trendData.times, axisLabel: { color: '#94a3b8' }, axisLine: { lineStyle: { color: '#334155' } }, }, yAxis: [ { type: 'value', name: '请求量', axisLabel: { color: '#94a3b8', formatter: '{value}次' }, splitLine: { lineStyle: { color: '#334155' } }, }, { type: 'value', name: '平均延迟', axisLabel: { color: '#94a3b8', formatter: '{value}s' }, splitLine: { show: false }, }, ], series: [ { name: '请求量', type: 'line', yAxisIndex: 0, data: trendData.requests, smooth: true, itemStyle: { color: '#3b82f6' }, areaStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: 'rgba(59,130,246,0.3)' }, { offset: 1, color: 'rgba(59,130,246,0.05)' }, ], }, }, }, { name: '平均延迟', type: 'line', yAxisIndex: 1, data: trendData.latencies, smooth: true, itemStyle: { color: '#f59e0b' }, }, ], }; return <ReactECharts option={option} style={{ height: '100%' }} theme="dark" />; }

最后在App.jsx里把组件拼起来,用useMetrics拿数据往下传:

// src/App.jsx import React from 'react'; import { Layout, Row, Col, Card } from 'antd'; import { useMetrics } from './hooks/useMetrics'; import OverviewCards from './components/OverviewCards'; import StatusPie from './components/StatusPie'; import TrendLine from './components/TrendLine'; import './App.css'; const { Header, Content } = Layout; export default function App() { const { overview, statusData, trendData } = useMetrics(); return ( <Layout style={{ minHeight: '100vh', background: '#0f172a' }}> <Header style={{ background: '#1e293b', display: 'flex', alignItems: 'center' }}> <h1 style={{ color: '#fff', margin: 0, fontSize: 22 }}> AI Agent Harness 实时监控大屏 </h1> </Header> <Content style={{ padding: 20 }}> <OverviewCards overview={overview} /> <Row gutter={16}> <Col span={12}> <Card title="Agent 状态分布" style={{ background: '#1e293b', height: 350 }}> <StatusPie data={statusData} /> </Card> </Col> <Col span={12}> <Card title="近 1 小时请求量&延迟趋势" style={{ background: '#1e293b', height: 350 }}> <TrendLine trendData={trendData} /> </Card> </Col> </Row> </Content> </Layout> ); }

到这里,大屏的组件拆分和轮询逻辑就完成了。工具调用 Top10、异常分布、告警表格、日志流的写法思路一样,都是「Hook 拿数据 → 组件渲染 → ECharts 或 AntD 表格展示」,按需扩展即可。

5. 本地启动与指标验证:常见报错排查

启动项目之前,先确认 Harness 控制平面在跑,并且模型通道配的是 TaoToken 统一 Key。然后执行:

pnpm dev

浏览器打开http://localhost:5173,如果概览卡片有数字、饼图和折线图有数据,说明链路通了。如果没数据,按下面的顺序排查。

报错一:401 Unauthorized。这是最常见的,说明 TaoToken Key 没配对或者过期了。检查config/model.yaml里的api_key是不是以sk-开头,有没有多余空格。如果 Key 是从环境变量注入的,确认环境变量名和配置文件里引用的一致。可以先用 curl 单独测一下 Key 是否有效:

curl -X POST https://taotoken.net/api/v1/messages \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4-20250514","max_tokens":10,"messages":[{"role":"user","content":"hi"}]}'

返回 200 说明 Key 没问题,返回 401 就去控制台重新生成一个。

报错二:local proxy failed。这个报错通常出现在 Harness 控制平面启动时,说明它尝试走本地代理但没找到。检查配置文件里有没有残留的proxy字段,有的话删掉,Base URL 直接写https://taotoken.net/api。另外确认机器上没有设置HTTP_PROXY之类的环境变量,有的话先 unset。

报错三:reading 'choices' of undefined。这是前端解析模型响应时报的,说明返回结构不是预期的 OpenAI 兼容格式。检查 Harness 里模型 provider 的name是不是写成了openai但实际走的是 TaoToken 通道,两者响应结构可能有差异。把 provider 配置改成 TaoToken 对应的格式,或者确认 TaoToken 返回的是标准choices数组。如果用的是 Claude 系列模型,响应结构是content数组,需要在 Harness 侧做一层适配。

报错四:OAuth 相关报错。如果你用的是 Claude Code 接入,可能会遇到 OAuth 认证失败。这种情况不要走 OAuth,直接用 API Key 方式接入,参考 https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite 里的配置,把 Base URL 和 Key 填对即可。

报错五:大屏数据不刷新。检查useMetrics里的POLL_INTERVAL是不是被设成了 0 或者负数,另外确认 Harness 的/api/v1/metrics/summary接口能正常返回。可以在浏览器 Network 面板看请求状态,如果是 CORS 报错,需要在 Harness 侧加跨域头,或者用 Vite 的 proxy 转发。

排查完这些,大屏应该能稳定刷新了。验证指标是否准确,可以对照 TaoToken 控制台的调用量,看大屏上的 Token 消耗和实际是否一致,误差在合理范围内就说明数据链路没问题。

6. 继续往下走:把监控大屏用起来

大屏跑通之后,有几个实用的增强方向。异常告警可以在useMetrics里加判断,异常率超过 5% 时用 AntD 的notification.error弹窗,配合播放提示音,这样不用一直盯着屏幕。图表下钻可以给饼图加onEvents点击事件,点「异常」部分弹出异常 Agent 列表,展示 AgentID、异常原因、最近心跳时间。

如果 Agent 集群超过 1000 个实例,每秒推送事件超过 1 万条,直接推给前端会卡顿,这时候需要在 Harness 和前端之间加一层预聚合,把每秒的指标先算好再推。历史数据可以存到 InfluxDB 或 Prometheus,支持按时间范围查询,做趋势对比。

长期跑编码任务或 Agent 工作流的团队,可以看看 Coding Plan,入口是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,统一 Key 加统一通道,配合这套监控大屏,Agent 的运行态就真正「看得见」了。模型对话调试可以用 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询