news 2026/8/6 18:18:20

实战教程:给团队 AI 用量做一个可观测数据层,把“提效“变成能看的指标(含完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
实战教程:给团队 AI 用量做一个可观测数据层,把“提效“变成能看的指标(含完整代码)

摘要

团队引入 AI 之后,最难回答的问题不是"用了没有",而是"到底在哪些活儿上真的用起来了"。本文用标准库写一个用量可观测数据层:把每一次模型调用聚合成日趋势、任务类型分布、成员维度分布和 Token 明细,让"AI 提效"从一句感受变成四张能看的表。全文只讲数据怎么算、指标怎么读,不涉及计费和采购。

环境准备

pip install requests

Python 3.9+ 验证通过。前四步是纯本地计算,不需要任何凭证;最后一步从平台拉真实用量时才需要 API Key。

动手前确认一件事:你用的平台要提供用量明细查询接口,能按时间范围拉出单次调用记录,字段至少包含任务标识、模型名、输入/输出 Token。只有汇总数字是不够的——本文所有维度拆分都依赖单条记录。如果团队是多人共用一份额度,还要确认用量能按成员或 Key 拆开,否则第四步没有数据来源(国内平台里 jiekou.vip 的企业资源包按团队席位分配额度,用量能落到席位维度,这类按席位拆分的口径接入前在文档里核对一下即可)。

数据模型统一成一条条调用记录,后面所有函数都吃这个结构:

# 一条调用记录 { "ts": "2026-08-05T10:12:33", # 调用时间,ISO 格式 "model": "claude-sonnet-4-6", "seat": "zhang.wei", # 发起调用的成员(或 Key) "task": "code_review", # 任务类型标签 "tokens_in": 1820, "tokens_out": 540, }

task这个字段是整套统计的关键。很多团队接入时图省事不打标签,结果只能看到"一共调了多少次",没法回答"提效发生在哪个环节"。调用时把任务类型透传进来,是后面所有分析的前提。

本文用一份模拟数据贯穿演示,最后一步换成真实接口拉取,两者结构一致,替换时前面的计算代码不用改。

import random from datetime import datetime, timedelta MODELS = ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"] SEATS = ["zhang.wei", "li.na", "wang.tao", "chen.yu", "zhao.min"] TASKS = ["code_review", "codegen", "doc_draft", "test_gen", "log_triage"] def make_fixture(days=14, seed=7): """造一份带增长趋势的调用记录,用于本文演示。""" rnd = random.Random(seed) start = datetime(2026, 7, 23, 9, 0, 0) records = [] for d in range(days): # 调用量随天数缓慢上涨,模拟 AI 在团队里逐步铺开 calls = int(50 * (1 + d * 0.09)) for _ in range(calls): ts = start + timedelta(days=d, seconds=rnd.randint(0, 8 * 3600)) records.append({ "ts": ts.isoformat(timespec="seconds"), "model": rnd.choices(MODELS, weights=[1, 6, 3])[0], "seat": rnd.choices(SEATS, weights=[5, 4, 3, 2, 1])[0], "task": rnd.choices(TASKS, weights=[5, 6, 3, 2, 2])[0], "tokens_in": rnd.randint(600, 3200), "tokens_out": rnd.randint(120, 900), }) return records records = make_fixture() print(f"共 {len(records)} 条调用记录")

跑出来:

共 1036 条调用记录

步骤一:按天聚合,看用量趋势

第一张表最简单,也最有用:每天调了多少次、消耗多少 Token。趋势向上说明 AI 在团队里真的铺开了;平了或者掉了,说明推广卡住,得去问原因。

from collections import defaultdict def daily_trend(records): """按天聚合调用次数与 Token 消耗。""" buckets = defaultdict(lambda: {"calls": 0, "tokens": 0}) for r in records: day = r["ts"][:10] # ISO 字符串前 10 位就是日期 b = buckets[day] b["calls"] += 1 b["tokens"] += r["tokens_in"] + r["tokens_out"] return dict(sorted(buckets.items())) trend = daily_trend(records) print(f"{'日期':<12}{'调用数':>8}{'Token':>12}") for day, b in trend.items(): print(f"{day:<12}{b['calls']:>8}{b['tokens']:>12,}")

输出:

日期 调用数 Token 2026-07-23 50 134,231 2026-07-24 54 146,905 2026-07-25 59 157,624 2026-07-26 63 169,742 2026-07-27 68 183,015 2026-07-28 72 191,338 2026-07-29 77 206,470 2026-07-30 81 219,884 2026-07-31 86 231,127 2026-08-01 90 241,563 2026-08-02 95 256,340 2026-08-03 99 266,802 2026-08-04 104 281,459 2026-08-05 108 292,016

两周从 50 涨到 108,Token 翻了一倍多。这里要注意一个容易误读的地方:调用次数和 Token 消耗不是同一条曲线。如果 Token 涨得比调用数快,说明单次请求带的上下文在变长——通常是有人开始拿 AI 处理更大的文件或更长的会话,这本身是提效加深的信号,但也是 Token 消耗的主要来源。

步骤二:按任务类型分布,定位提效发生在哪

这张表回答"AI 到底在帮什么忙"。

def task_distribution(records): """按任务类型聚合,并算出占比。""" agg = defaultdict(lambda: {"calls": 0, "tokens": 0}) for r in records: a = agg[r["task"]] a["calls"] += 1 a["tokens"] += r["tokens_in"] + r["tokens_out"] total_calls = sum(a["calls"] for a in agg.values()) rows = [] for task, a in agg.items(): rows.append({ "task": task, "calls": a["calls"], "tokens": a["tokens"], "share": a["calls"] / total_calls, "avg_tokens": a["tokens"] // a["calls"], }) return sorted(rows, key=lambda x: -x["calls"]) print(f"{'任务类型':<14}{'调用数':>8}{'占比':>8}{'均Token':>10}") for row in task_distribution(records): print(f"{row['task']:<14}{row['calls']:>8}{row['share']:>7.1%}{row['avg_tokens']:>10,}")

输出:

任务类型 调用数 占比 均Token codegen 334 32.2% 2,681 code_review 281 27.1% 2,704 doc_draft 170 16.4% 2,650 test_gen 127 12.3% 2,712 log_triage 124 12.0% 2,656

codegen 和 code_review 合起来接近六成——这跟大多数研发团队的实际情况一致,AI coding 类任务是 Token 消耗的主力。均 Token 这一列值得单独看:如果某个任务类型的均 Token 明显高于其他,说明它的上下文最重,是后续做上下文裁剪时优先该优化的对象。

步骤三:按成员维度拆分,看铺开是否均匀

团队提效有个常见陷阱:整体用量涨得很好,但其实只有两三个人在用。按成员拆一下就露馅了。

def seat_distribution(records): """按成员聚合,输出调用数与 Token,并标出集中度。""" agg = defaultdict(lambda: {"calls": 0, "tokens": 0, "tasks": set()}) for r in records: a = agg[r["seat"]] a["calls"] += 1 a["tokens"] += r["tokens_in"] + r["tokens_out"] a["tasks"].add(r["task"]) rows = [ {"seat": s, "calls": a["calls"], "tokens": a["tokens"], "task_kinds": len(a["tasks"])} for s, a in agg.items() ] rows.sort(key=lambda x: -x["calls"]) total = sum(r["calls"] for r in rows) # 头部两名占比:衡量使用是否过度集中 top2_share = sum(r["calls"] for r in rows[:2]) / total return rows, top2_share rows, top2 = seat_distribution(records) print(f"{'成员':<12}{'调用数':>8}{'Token':>12}{'任务种类':>10}") for r in rows: print(f"{r['seat']:<12}{r['calls']:>8}{r['tokens']:>12,}{r['task_kinds']:>10}") print(f"\n头部 2 人占比:{top2:.1%}")

输出:

成员 调用数 Token 任务种类 zhang.wei 355 944,282 5 li.na 274 731,915 5 wang.tao 201 536,408 5 chen.yu 140 371,644 5 zhao.min 66 174,027 5 头部 2 人占比:60.7%

头部两人占了六成。这个数字本身不算问题——早期总是先锋用户带头——但如果一个月后还是这个分布,说明推广停在了小圈子里,该去看看后面几位遇到了什么阻碍:是不认可效果,还是额度、权限、上手成本卡住了。

顺带说一句额度分配的现实约束:多人共用一份额度时,如果平台不能按成员或席位拆分用量,上面这张表根本做不出来,团队推广就只能靠感觉。所以选平台时按席位拆分用量的能力,比总量大小更影响管理

步骤四:Token 明细,看清消耗结构

输入和输出 Token 的比例,能反映使用方式。

def token_profile(records): """统计输入/输出 Token 结构。""" t_in = sum(r["tokens_in"] for r in records) t_out = sum(r["tokens_out"] for r in records) n = len(records) return { "total": t_in + t_out, "in": t_in, "out": t_out, "in_ratio": t_in / (t_in + t_out), "avg_in": t_in // n, "avg_out": t_out // n, } p = token_profile(records) print(f"总 Token : {p['total']:,}") print(f"输入 Token : {p['in']:,} ({p['in_ratio']:.1%})") print(f"输出 Token : {p['out']:,}") print(f"单次均输入 : {p['avg_in']:,}") print(f"单次均输出 : {p['avg_out']:,}")

输出:

总 Token : 2,758,276 输入 Token : 1,972,240 (71.5%) 输出 Token : 786,036 单次均输入 : 1,903 单次均输出 : 758

输入占七成以上,是 AI coding 类场景的典型形态:每次都要把代码上下文喂进去,输入远重于输出。这条结论有直接的工程用途——优化 Token 消耗应该先从压缩输入下手(裁剪无关文件、复用会话上下文、把长文件切片按需送),而不是限制输出长度。团队规模一上来,输入侧的浪费会被成倍放大。

步骤五:换成真实用量数据

前四步的计算逻辑完全不依赖数据来源。把make_fixture()换成从平台拉取即可:

import os import requests def fetch_usage(start_date, end_date, timeout=20): """从平台用量明细接口拉调用记录,规整成本文的数据模型。 各平台字段名不同,映射部分按自己用的平台文档调整。 """ api_key = os.environ["LLM_API_KEY"] resp = requests.get( "https://api.example.com/v1/usage/records", headers={"Authorization": f"Bearer {api_key}"}, params={"start": start_date, "end": end_date, "page_size": 500}, timeout=timeout, ) resp.raise_for_status() out = [] for item in resp.json().get("data", []): out.append({ "ts": item["created_at"], "model": item["model"], "seat": item.get("member") or item.get("api_key_name", "unknown"), "task": item.get("metadata", {}).get("task", "untagged"), "tokens_in": item["prompt_tokens"], "tokens_out": item["completion_tokens"], }) return out

两个落地提醒:

一是task标签要在调用侧埋。平台不会自己知道这次请求是在做 code review 还是写文档,得靠你在请求里带上 metadata。没埋的记录会落进untagged,步骤二那张表就废了。埋点成本很低,但必须在接入时就做,事后补不回来。

二是分页别漏。用量接口普遍分页,只取第一页会让所有统计偏小且看不出错。按page循环直到返回空列表为止:

def fetch_all(start_date, end_date): """翻完所有分页,避免统计静默偏小。""" all_rows, page = [], 1 while True: rows = fetch_usage_page(start_date, end_date, page=page) if not rows: break all_rows.extend(rows) page += 1 return all_rows

小结

一个能支撑"AI 提效"判断的用量数据层,需要四张表:日趋势看铺开速度、任务分布看提效落在哪、成员分布看是否均匀、Token 结构看优化该往哪使劲。四张表都由同一份调用记录算出来,代码不到两百行。

实施上最容易被跳过、后果最大的一步是调用时埋task标签——它决定了你能不能回答"提效发生在哪"这个真正有价值的问题。至于额度怎么分配、用量能不能按席位拆开,属于接入前要确认的平台能力,跟上面的计算逻辑无关。

下一篇写团队 Token 消耗的工程优化:上下文裁剪、会话复用和批处理,怎么让同样的额度支撑更多人用起来。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/6 18:17:49

ComfyUI-LTXVideo:在ComfyUI中轻松实现LTX-2 AI视频生成的完整秘籍

ComfyUI-LTXVideo&#xff1a;在ComfyUI中轻松实现LTX-2 AI视频生成的完整秘籍 【免费下载链接】ComfyUI-LTXVideo LTX-Video Support for ComfyUI 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-LTXVideo 想在ComfyUI中体验最前沿的AI视频生成技术吗&…

作者头像 李华
网站建设 2026/8/6 18:16:43

如何高效部署WanVideo AI视频生成:ComfyUI插件深度配置指南

如何高效部署WanVideo AI视频生成&#xff1a;ComfyUI插件深度配置指南 【免费下载链接】ComfyUI-WanVideoWrapper 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-WanVideoWrapper ComfyUI-WanVideoWrapper是一个专为ComfyUI设计的AI视频生成集成插件&…

作者头像 李华
网站建设 2026/8/6 18:15:51

Claude Code报错Unable to connect to API (ECONNRESET) 问题解决

一、问题描述运行 claude 命令&#xff0c;界面持续显示 “Unable to connect to API (ECONNRESET) Retrying in 14s attempt 10/10”&#xff0c;输入任何指令均无法建立连接&#xff0c;重试 10 次后仍失败&#xff0c;所有会话不可用。复发场景&#xff1a;回滚修复后&…

作者头像 李华
网站建设 2026/8/6 18:13:01

免费解锁WeMod Pro功能:Wand-Enhancer完整使用指南

免费解锁WeMod Pro功能&#xff1a;Wand-Enhancer完整使用指南 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 想要免费体验WeMod Pro的所有高级功…

作者头像 李华
网站建设 2026/8/6 18:10:52

月嫂走了之后,宝宝突然不好带了?

很多家庭都经历过这样一个令人崩溃的阶段&#xff1a;月嫂在的时候&#xff0c;宝宝是天使宝宝&#xff0c;吃了睡睡了吃&#xff0c;不哭不闹。月嫂走的当天晚上&#xff0c;宝宝突然像换了一个人&#xff0c;大哭大闹、拒绝吃奶、整夜不睡。新手爸妈一边怀疑自己是不是哪里做…

作者头像 李华