基于Python与Chart.js构建本地AI Token用量监控仪表盘

发布时间:2026/7/23 7:15:11

基于Python与Chart.js构建本地AI Token用量监控仪表盘 1. 项目概述与核心价值最近在深度使用一些AI辅助编程工具比如Cursor和Claude发现一个挺实际的问题你很难直观地知道这些工具到底“吃”了你多少算力或者说花了你多少钱。尤其是当你在一个项目里频繁调用不同模型比如Claude 3.5 Sonnet, GPT-4o, DeepSeek Coder时账单明细往往藏在后台日志里不翻个底朝天根本看不清。这种“黑盒”体验对于想控制成本、或者单纯想了解自己工作习惯的开发者来说并不友好。于是我动手搓了一个本地化的Token用量监控面板我把它叫做Token Usage UI。本质上它是一个运行在你本地的轻量级Web仪表盘专门用来解析、聚合并可视化你的AI工具目前主要适配OpenClaw框架的会话数据。它能清晰地告诉你过去一段时间你在哪些模型上花了多少Token对应的成本是多少以及用量随时间的变化趋势。整个工具完全本地运行不依赖任何外部服务数据隐私有保障启动也只需要一条命令。如果你也经常用Cursor、Claude Code或者基于OpenClaw的自定义工作流并且对“用量”和“成本”这两个词敏感那么这个工具应该能帮到你。它特别适合独立开发者、小团队或者任何想精细化运营自己AI开发工具链的人。接下来我会详细拆解这个工具的设计思路、实现细节并分享我在开发过程中踩过的坑和总结的经验。2. 整体架构与设计思路拆解2.1 核心需求与方案选型做这个工具的初衷很明确可视化、本地化、零依赖。我不想引入复杂的后端框架、数据库甚至不想用pip install装一堆包。目标就是写一个脚本双击或一条命令就能跑起来在浏览器里看到一个清晰的数据面板。基于这个目标技术选型就非常直接了数据源工具必须能直接读取AI工具产生的原生日志或会话文件。对于OpenClaw其会话数据以JSON格式存储在~/.openclaw/agents/main/sessions/sessions.json这个固定路径。这是最可靠的一手数据源。后端服务为了极致轻量我选择了Python标准库中的http.server模块。它虽然功能基础但足以胜任一个静态文件服务器加上简单的API端点用于提供动态数据的任务。这意味着你只需要有Python环境无需安装任何第三方库如Flask, FastAPI。前端呈现仪表盘需要图表。为了保持“零依赖”且便于部署我选择了通过CDN引入Chart.js。这是一个功能强大且易于使用的JavaScript图表库画个折线图、柱状图绰绰有余。页面样式则用纯CSS实现为了阅读舒适采用了深色主题和响应式布局并引入了Google Fonts的Inter字体提升视觉质感。数据兜底考虑到用户可能第一次使用或者sessions.json文件还不存在工具必须能优雅降级。我设计了一个Mock数据生成器当检测不到真实数据时会自动生成一份结构相同但数据为模拟值的JSON确保前端页面永远有数据可展示不会白屏报错。这个架构的优点是极其简单和便携。整个项目就是一个文件夹里面一个Python服务器脚本、一个HTML前端文件、一点CSS和JS。复制到任何有Python的机器上都能运行。缺点是功能扩展性有限比如无法做复杂的历史数据查询或用户管理但对于个人监控场景这完全够用。2.2 数据流与核心模块设计整个工具的数据流非常清晰可以分为三个核心模块数据采集与解析模块这是后端的核心。server.py中的一个函数会定期或根据请求去读取~/.openclaw/agents/main/sessions/sessions.json文件。这个JSON文件通常是一个数组里面的每个对象代表一次AI会话包含了model_name模型名称、input_tokens输入token数、output_tokens输出token数、timestamp时间戳等关键字段。解析器需要遍历所有会话按模型进行聚合统计。数据聚合与成本计算模块解析出原始数据后不能直接扔给前端。这里需要进行关键的计算按模型聚合将属于同一个模型如claude-3-5-sonnet-20241022的所有会话的输入、输出token分别累加。成本计算这是最有价值的部分。我需要维护一个内部的模型单价字典。例如我知道Claude 3.5 Sonnet的输入Token单价可能是$0.003 / 1K tokens输出是$0.015 / 1K tokens。那么成本 (输入Token数 / 1000 * 输入单价) (输出Token数 / 1000 * 输出单价)。所有模型的成本相加就是总成本。这个单价字典需要我根据各AI服务商公开的定价信息手动维护和更新。时间线聚合为了绘制7天趋势图需要按天对总Token数输入输出进行分组求和。HTTP服务与前端渲染模块Python的HTTP服务器会做两件事一是托管静态文件HTML, CSS, JS二是提供一个特定的API路由比如/api/usage。当浏览器打开页面时前端JavaScript会调用这个API获取上面计算好的聚合数据JSON格式然后利用Chart.js将数据渲染成图表和数字面板。注意模型单价是成本计算的核心但也是需要手动维护的部分。不同服务商、不同模型、甚至不同区域的单价都可能不同。在我的实现中我将这个单价字典直接硬编码在server.py里这对于个人使用是没问题的。如果你需要更灵活的配置可以考虑将其移到一个外部的JSON配置文件中。3. 核心实现细节与实操要点3.1 后端服务器 (server.py) 深度解析server.py是这个项目的大脑它虽然代码量不大但每一部分都承担着关键职责。我采用了一个继承自http.server.SimpleHTTPRequestHandler的自定义类通过重写do_GET方法来处理不同的请求路径。import http.server import json import os from datetime import datetime, timedelta import time # 1. 定义模型单价单位美元/1K tokens # 这里需要你根据实际使用的API价格进行更新 MODEL_PRICES { claude-3-5-sonnet-20241022: {input: 0.003, output: 0.015}, gpt-4o: {input: 0.005, output: 0.015}, gpt-4o-mini: {input: 0.00015, output: 0.0006}, deepseek-coder: {input: 0.00014, output: 0.00028}, # 可以继续添加其他模型 } class TokenUsageHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): # 处理API数据请求 if self.path /api/usage: data self.get_usage_data() self.send_response(200) self.send_header(Content-type, application/json) self.send_header(Access-Control-Allow-Origin, *) # 简单处理CORS self.end_headers() self.wfile.write(json.dumps(data).encode()) # 处理前端页面请求 elif self.path /: self.path /index.html # 将根路径重定向到首页 return super().do_GET() # 处理其他静态文件CSS, JS, 图片等 else: return super().do_GET() def get_usage_data(self): # 核心数据获取与计算函数 sessions_file os.path.expanduser(~/.openclaw/agents/main/sessions/sessions.json) sessions [] # 尝试读取真实数据 if os.path.exists(sessions_file): try: with open(sessions_file, r) as f: sessions json.load(f) if not isinstance(sessions, list): sessions [] # 如果文件格式不对回退到模拟数据 except (json.JSONDecodeError, IOError): sessions [] # 文件损坏或读取失败回退到模拟数据 # 如果真实数据为空生成模拟数据 if not sessions: sessions self.generate_mock_sessions() # 开始核心计算逻辑... # ... (后续聚合计算代码)关键点解析路由分发在do_GET方法中我根据self.path判断请求类型。如果是/api/usage就执行数据计算并返回JSON如果是/就返回index.html其他情况则交给父类处理静态文件。这种设计让一个简单的HTTP服务器同时具备了API和静态资源服务的能力。数据读取与兜底get_usage_data函数首先尝试从固定路径读取sessions.json。这里使用了os.path.expanduser来正确处理~符号保证在不同系统上都能找到用户目录。读取时加了try...except异常捕获防止因为文件格式错误导致整个服务崩溃。如果读取失败或文件为空则调用generate_mock_sessions生成模拟数据。这是一个非常重要的健壮性设计确保了工具在任何情况下都能“跑起来”。模拟数据生成generate_mock_sessions函数会生成过去7天内、不同模型的随机会话数据。这不仅用于兜底在开发测试阶段也极其有用让你在没有真实数据的情况下也能看到完整的UI效果。3.2 数据聚合与成本计算逻辑这是后端最核心的算法部分直接决定了仪表盘上数字的准确性。def get_usage_data(self): # ... (前述数据读取部分) # 初始化统计数据结构 model_breakdown {} total_input_tokens 0 total_output_tokens 0 total_cost 0.0 # 按模型聚合 for session in sessions: model session.get(model_name, unknown) input_tokens session.get(input_tokens, 0) output_tokens session.get(output_tokens, 0) timestamp session.get(timestamp) # 初始化该模型的统计项 if model not in model_breakdown: model_breakdown[model] { input_tokens: 0, output_tokens: 0, cost: 0.0, session_count: 0 } # 累加Token数 model_breakdown[model][input_tokens] input_tokens model_breakdown[model][output_tokens] output_tokens model_breakdown[model][session_count] 1 # 计算该会话成本并累加 cost self.calculate_session_cost(model, input_tokens, output_tokens) model_breakdown[model][cost] cost # 累加到总计 total_input_tokens input_tokens total_output_tokens output_tokens total_cost cost # 准备7天时间线数据 timeline_data self.aggregate_timeline_data(sessions) # 构建最终返回给前端的JSON result { total: { input_tokens: total_input_tokens, output_tokens: total_output_tokens, cost: round(total_cost, 4) # 保留4位小数 }, models: model_breakdown, timeline: timeline_data } return result def calculate_session_cost(self, model, input_tokens, output_tokens): 根据模型和Token数计算单次会话成本 if model not in MODEL_PRICES: return 0.0 # 未知模型成本记为0 price MODEL_PRICES[model] # 成本 (输入Token/1000 * 输入单价) (输出Token/1000 * 输出单价) input_cost (input_tokens / 1000.0) * price[input] output_cost (output_tokens / 1000.0) * price[output] return input_cost output_cost def aggregate_timeline_data(self, sessions): 聚合过去7天每天的Token总量 timeline {} today datetime.now().date() # 初始化过去7天的日期字典Token数默认为0 for i in range(6, -1, -1): # 从6天前到今天 date_str str(today - timedelta(daysi)) timeline[date_str] 0 # 遍历所有会话按日期累加 for session in sessions: ts session.get(timestamp) if not ts: continue # 假设timestamp是ISO格式字符串或时间戳 try: if isinstance(ts, (int, float)): dt datetime.fromtimestamp(ts) else: dt datetime.fromisoformat(ts.replace(Z, 00:00)) date_str str(dt.date()) # 只统计最近7天的数据 if date_str in timeline: total_tokens session.get(input_tokens, 0) session.get(output_tokens, 0) timeline[date_str] total_tokens except (ValueError, TypeError): continue # 时间戳格式错误跳过 # 转换为前端需要的数组格式[{date: 2024-01-01, tokens: 1234}, ...] timeline_list [{date: date, tokens: tokens} for date, tokens in timeline.items()] return timeline_list计算逻辑要点模型聚合使用一个字典model_breakdown来为每个遇到的模型维护一个统计对象。遍历所有会话时将Token数和会话次数累加到对应的模型下。这种“字典归类”的方法是处理此类聚合问题的标准做法时间复杂度是O(n)效率很高。成本计算calculate_session_cost函数是核心。它严格遵循成本 (Token数 / 1000) * 单价的公式。这里必须注意单位的统一我的MODEL_PRICES字典里单价单位是“美元/1K tokens”所以计算时要先将Token数除以1000。另外对于未在价格字典中定义的模型我选择返回0成本避免因价格缺失导致整个计算出错。在实际使用中你需要定期检查并更新这个价格字典。时间线聚合aggregate_timeline_data函数负责生成趋势图所需的数据。我首先初始化了一个包含过去7天日期的字典值都为0。然后遍历会话解析其时间戳如果日期落在最近7天内就将该会话的总Token数累加到对应的日期上。最后将字典转换为一个按日期排序的列表方便前端Chart.js使用。这里的时间戳解析需要一定的容错处理因为数据源格式可能不统一。实操心得在计算成本时浮点数精度是个小坑。美元金额通常只需要保留小数点后2-4位。我使用round(total_cost, 4)来格式化最终的总成本。但在内部累加时应保持原始精度只在最终展示时进行舍入避免误差累积。3.3 前端界面 (index.html) 与动态数据绑定前端页面是一个单页应用核心是使用JavaScript调用后端API获取数据后动态更新DOM和图表。!DOCTYPE html html langen head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleToken Usage Dashboard/title link hrefhttps://fonts.googleapis.com/css2?familyInter:wght300;400;500;600;700displayswap relstylesheet script srchttps://cdn.jsdelivr.net/npm/chart.js/script style /* 深色主题CSS样式 */ :root { --bg-primary: #0f172a; --bg-card: #1e293b; --text-primary: #f1f5f9; ... } body { font-family: Inter, sans-serif; background: var(--bg-primary); color: var(--text-primary); ... } .card { background: var(--bg-card); border-radius: 12px; padding: 1.5rem; margin-bottom: 1.5rem; } .stat-number { font-size: 2.5rem; font-weight: 700; color: #60a5fa; } /* 更多响应式样式... */ /style /head body div classcontainer header.../header section classcard h2 Total Usage Cost/h2 div classstats-grid div classstat div classstat-labelTotal Input Tokens/div div idtotal-input classstat-number0/div /div !-- 其他统计项Total Output, Total Cost -- /div /section section classcard h2 Per-Model Breakdown/h2 div idmodel-breakdown !-- 这里会被JS动态填充 -- /div /section section classcard h2 7-Day Token Usage Timeline/h2 canvas idtimeline-chart/canvas /section /div script let timelineChart null; // 保存图表实例 // 页面加载完成后执行 document.addEventListener(DOMContentLoaded, function() { fetchUsageData(); // 首次加载数据 setInterval(fetchUsageData, 8000); // 每8秒自动刷新 }); async function fetchUsageData() { try { const response await fetch(/api/usage); const data await response.json(); updateDashboard(data); } catch (error) { console.error(Failed to fetch usage data:, error); // 可以在这里添加一个友好的错误提示到页面上 } } function updateDashboard(data) { // 1. 更新总计面板 document.getElementById(total-input).textContent data.total.input_tokens.toLocaleString(); document.getElementById(total-output).textContent data.total.output_tokens.toLocaleString(); document.getElementById(total-cost).textContent $ data.total.cost.toFixed(4); // 2. 更新模型细分表格 updateModelBreakdown(data.models); // 3. 更新趋势图 updateTimelineChart(data.timeline); } function updateModelBreakdown(models) { const container document.getElementById(model-breakdown); let html table classmodel-tabletheadtrthModel/ththInput Tokens/ththOutput Tokens/ththCost/ththSessions/th/tr/theadtbody; for (const [modelName, stats] of Object.entries(models)) { html tr tdstrong${modelName}/strong/td td${stats.input_tokens.toLocaleString()}/td td${stats.output_tokens.toLocaleString()}/td td$${stats.cost.toFixed(4)}/td td${stats.session_count}/td /tr; } html /tbody/table; container.innerHTML html; } function updateTimelineChart(timelineData) { const ctx document.getElementById(timeline-chart).getContext(2d); const dates timelineData.map(d d.date); const tokenCounts timelineData.map(d d.tokens); // 如果图表已存在则更新数据否则创建新图表 if (timelineChart) { timelineChart.data.labels dates; timelineChart.data.datasets[0].data tokenCounts; timelineChart.update(); } else { timelineChart new Chart(ctx, { type: line, data: { labels: dates, datasets: [{ label: Total Tokens (InputOutput), data: tokenCounts, borderColor: #60a5fa, backgroundColor: rgba(96, 165, 250, 0.1), borderWidth: 2, fill: true, tension: 0.4 // 让折线更平滑 }] }, options: { responsive: true, plugins: { legend: { display: true } }, scales: { y: { beginAtZero: true, grid: { color: rgba(255,255,255,0.1) } }, x: { grid: { color: rgba(255,255,255,0.1) } } } } }); } } /script /body /html前端实现要点自动刷新通过setInterval(fetchUsageData, 8000)实现了每8秒自动获取最新数据并更新页面。这个间隔时间可以根据需要调整太短会增加服务器压力太长则数据不够实时。数据绑定updateDashboard函数是中枢它接收到后端传来的JSON数据后分发给三个子函数去更新页面的不同部分。这种“数据驱动视图”的模式非常清晰。模型细分表格updateModelBreakdown函数动态生成一个HTML表格。这里我使用了模板字符串来拼接HTML代码更清晰。注意使用.toLocaleString()为数字添加千位分隔符使用.toFixed(4)控制成本显示的小数位数这些都是提升可读性的细节。Chart.js 图表updateTimelineChart函数处理折线图。关键点在于第一次调用时创建图表实例并保存到timelineChart变量中后续调用时只需更新该实例的data属性并调用update()方法这样图表就会平滑过渡到新数据而不是重新创建整个图表体验更好。注意事项前端代码直接内嵌在HTML中没有使用任何构建工具这保持了项目的简洁性。但在实际开发中如果逻辑变复杂可以考虑将CSS和JS拆分到独立文件中。另外Chart.js通过CDN引入这意味着使用该工具时需要网络连接。如果需要在完全离线的环境使用可以将Chart.js库文件下载到本地并修改引用路径。4. 部署、运行与自定义指南4.1 环境准备与一键启动这个工具对环境的要求极低只需要一个Python 3.6的运行环境。无需虚拟环境无需安装任何第三方包。获取代码你可以直接从GitHub仓库克隆或者手动创建项目文件。# 假设你克隆到了本地 cd /path/to/token-usage-ui项目目录结构应如下所示token-usage-ui/ ├── server.py # Python后端服务器 ├── index.html # 前端主页面 └── (可能还有 style.css, 但本例中样式内联了)启动服务器在项目根目录下运行python server.py默认情况下服务器会监听本地的8765端口。你会看到类似下面的输出Serving HTTP on 0.0.0.0 port 8765 (http://0.0.0.0:8765/) ...访问仪表盘打开你的浏览器输入http://localhost:8765就能看到Token用量监控面板了。页面会每8秒自动刷新一次数据。4.2 关键配置与自定义点虽然工具开箱即用但有几个地方你可能需要根据自身情况调整修改监听端口如果8765端口被占用你可以在server.py文件末尾修改端口号。if __name__ __main__: server_address (, 8765) # 将8765改为其他端口如8080 httpd http.server.HTTPServer(server_address, TokenUsageHandler) print(fStarting server on port {server_address[1]}...) httpd.serve_forever()更新模型单价这是保证成本计算准确的关键。你需要编辑server.py文件开头的MODEL_PRICES字典。MODEL_PRICES { claude-3-5-sonnet-20241022: {input: 0.003, output: 0.015}, gpt-4o: {input: 0.005, output: 0.015}, # 访问OpenAI、Anthropic、DeepSeek等官网的Pricing页面获取最新价格并更新此处。 # 注意单位是 美元/1K tokens。 }重要模型名称必须与sessions.json文件中的model_name字段完全一致包括大小写和版本号否则会被识别为“未知模型”成本计算为0。调整数据源路径如果你的OpenClaw会话文件不在默认路径或者你想监控其他工具生成的日志需要修改get_usage_data函数中的sessions_file变量。sessions_file /your/custom/path/to/sessions.json # 修改为你的实际路径只要你的JSON文件格式与OpenClaw的sessions.json兼容即一个会话对象数组包含model_name,input_tokens,output_tokens,timestamp字段工具就能正常解析。修改自动刷新频率前端页面默认8秒刷新一次。如果你觉得太频繁或太慢可以编辑index.html文件中的JavaScript部分。setInterval(fetchUsageData, 8000); // 将8000毫秒改为你想要的间隔如1500015秒4.3 适配其他AI工具的数据源这个工具的核心是解析固定的JSON数据格式。如果你想用它来监控非OpenClaw的工具比如你自己写的一个脚本调用了OpenAI API你需要确保你的工具能以类似的格式输出日志。一个简单的日志生成示例Pythonimport json import time def log_ai_session(model, input_tokens, output_tokens, costNone): log_entry { model_name: model, input_tokens: input_tokens, output_tokens: output_tokens, timestamp: time.time(), # 或者 datetime.now().isoformat() # 可以添加其他自定义字段如project, user等 } log_file ~/my_ai_sessions.json # 读取现有日志 try: with open(os.path.expanduser(log_file), r) as f: sessions json.load(f) except (FileNotFoundError, json.JSONDecodeError): sessions [] # 追加新日志 sessions.append(log_entry) # 写回文件注意这不是线程安全的生产环境需加锁或使用数据库 with open(os.path.expanduser(log_file), w) as f: json.dump(sessions, f, indent2)然后将server.py中的数据源路径指向这个新的JSON文件即可。通过这种方式你可以将任何产生Token用量的服务接入到这个监控面板中。5. 常见问题排查与优化技巧在实际使用和开发过程中我遇到了一些典型问题这里总结出来方便你快速排查。5.1 问题排查速查表问题现象可能原因解决方案访问http://localhost:8765显示“无法连接”或“拒绝连接”。1.server.py没有运行。2. 端口被其他程序占用。3. 防火墙阻止了该端口。1. 在终端确认python server.py正在运行且无报错。2. 尝试更换端口如8080并访问http://localhost:8080。3. 检查本地防火墙设置暂时允许该端口的入站连接。页面能打开但所有数据都显示为0或“No Data”。1. 数据源路径 (sessions.json) 不正确或文件不存在。2. JSON文件格式错误无法解析。3. 文件权限不足无法读取。1. 检查server.py中的sessions_file路径是否正确。使用print语句输出该路径确认。2. 手动打开sessions.json文件检查是否是有效的JSON数组格式。可以使用在线JSON校验工具。3. 检查文件读权限 (ls -l ~/.openclaw/.../sessions.json)。成本计算为0但Token数有显示。1.MODEL_PRICES字典中没有匹配当前model_name的键。2. 模型名称字符串有细微差别如尾部空格、版本号不同。1. 在页面表格中查看具体的模型名称然后将其添加到MODEL_PRICES字典中并设置正确的单价。2. 仔细核对模型名称确保完全一致。可以在server.py中添加print(model)来调试。图表不显示或显示错误。1. 网络问题导致Chart.js CDN加载失败。2. 传递给Chart.js的数据格式不正确。3. Canvas上下文获取失败。1. 检查浏览器开发者工具F12的“网络(Network)”标签看chart.js是否加载成功。失败可尝试使用本地Chart.js文件。2. 在updateTimelineChart函数中console.log(dates, tokenCounts)检查数据格式。3. 确认HTML中canvas idtimeline-chart的ID与JavaScript中getElementById的ID一致。页面样式混乱或字体未加载。1. 网络问题导致Google Fonts (Inter) 加载失败。2. CSS代码有语法错误。1. 同样在开发者工具的“网络”标签中检查字体加载情况。可以考虑将Inter字体下载到本地或使用系统备用字体如font-family: Inter, -apple-system, sans-serif;。2. 检查内联CSS的语法确保括号闭合。5.2 性能与稳定性优化建议会话文件过大如果长期使用sessions.json文件可能会变得非常大几十MB甚至更大。每次请求都读取和解析整个大文件会严重影响服务器响应速度。优化方案可以在server.py中增加缓存机制。例如将解析后的数据缓存在内存中并设置一个过期时间如5秒。只有在缓存过期或检测到文件修改时间变化时才重新读取和解析文件。这能极大减少IO操作。import os import time class TokenUsageHandler(...): _data_cache None _cache_time 0 _file_mtime 0 def get_usage_data(self): current_time time.time() sessions_file ... # 获取文件最后修改时间 try: file_mtime os.path.getmtime(sessions_file) except OSError: file_mtime 0 # 如果缓存未过期比如5秒内且文件未修改则返回缓存 if (self._data_cache is not None and current_time - self._cache_time 5 and self._file_mtime file_mtime): return self._data_cache # 否则重新计算数据 data ... # 原有的计算逻辑 self._data_cache data self._cache_time current_time self._file_mtime file_mtime return data数据安全性这个工具默认监听0.0.0.0:8765意味着同一网络下的其他设备可能也能访问你的仪表盘。建议如果只在本地使用可以将服务器地址改为127.0.0.1本地回环地址这样只有本机可以访问。server_address (127.0.0.1, 8765) # 只绑定到本地扩展更多图表目前只展示了7天的总Token趋势图。你可以很容易地扩展更多视图。例如增加一个“模型成本占比”饼图。在后端get_usage_data函数中计算每个模型的成本占总成本的比例。然后在前端用Chart.js的doughnut或pie类型图表进行渲染。例如增加一个“每小时用量热力图”展示一天中哪些时段使用AI最频繁。这需要你的原始数据中包含更精确的时间戳。5.3 从“能用”到“好用”的打磨美化前端目前的UI比较基础。你可以通过CSS加入更多现代设计元素比如卡片阴影、悬停效果、渐变色、更优雅的字体排版等。也可以使用一些轻量级的CSS框架如Pico.css快速提升颜值。添加数据过滤在界面上增加日期选择器或模型筛选器让用户可以查看特定时间段或特定模型的数据。这需要后端API支持查询参数例如/api/usage?start_date2024-01-01modelclaude-3-5-sonnet。数据持久化与历史对比当前工具只展示实时数据没有历史趋势对比。你可以修改后端定期如每天将聚合结果保存到一个单独的JSON或SQLite数据库中。然后前端可以提供一个下拉菜单让用户选择查看“今天”、“昨天”、“上周同期”或“自定义日期范围”的数据对比。成本预警在后端逻辑中加入成本检查。可以设置一个每日或每周的预算阈值如10美元当计算出的成本超过阈值时在返回的数据中添加一个警告标志前端收到后可以在页面上显示一个明显的警告横幅或发送浏览器通知。这个工具麻雀虽小五脏俱全。它从一个具体的痛点出发用最简单的技术栈实现了一个可用的解决方案。更重要的是它的架构是清晰的你可以基于它根据自己的需求进行无限的定制和扩展。无论是想监控成本还是单纯想了解自己的工作模式这样一个透明的数据面板都能给你带来更踏实的掌控感。

相关新闻