尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

DeepMind 发布 3.8 Live,TaoToken 给语音 Agent 做用量分层

DeepMind 发布 3.8 Live,TaoToken 给语音 Agent 做用量分层 1. 语音 Agent 平台的账单最怕在凌晨悄悄翻倍某多租户语音 Agent 平台出过一次典型事故有租户把「持续监听 每 800ms 触发一次意图识别」写进了自己的 Agent会话数没涨、轮次没涨Token 消耗却顶穿了总额度而当时所有租户共用一个 API Key看板上只剩一条总量曲线谁也定位不到是谁在烧。TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_tiering_intro先把 Key 领出来把 Base URL 统一设为 https://taotoken.net/api接下来的问题才有解——不是换模型而是把「一个 Key 打天下」拆成可记账的多租户分层。这件事正好撞上 Google DeepMind 发布 Gemini 3.8 Live 与 Gemini 3.8 Live Extended Thinking 两个近实时语音对话模型。近实时语音模型把「一轮对话」的边界彻底打散了过去一轮对话是「一次请求、一次响应」现在往往是一条持续音频流里穿插多次中间推理、外加一次最终输出Extended Thinking 还会在中间插一段更长的思考开销。对平台运营开发者来说这意味着原来那套按「请求数」做限流、按「调用次数」做计费的思路在语音场景里基本失效——真正消耗 Token 的是每个租户的语音 Agent 会话和任务执行而不是调用次数这个粗颗粒指标。所以这篇不聊模型评测只聊落地怎么在 TaoToken 上给语音 Agent 配多租户 Key 分层用量看板该埋哪些字段以及一次语音轮次到底该按什么口径折算 Token。全部配置可直接抄SQL 与命令请在本地自行执行。2. 语音 Agent 接入 TaoTokenBase URL 改动的最小闭环不管你用的是自研的流式语音链路WebSocket 推流 ASR LLM TTS还是官方 SDK接入侧的改动本质上只有两处把请求地址指到 TaoToken 的 Base URL把鉴权换成你的 Key。第一步去控制台创建 Key。打开 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_keys第二步把 Base URL 统一为https://taotoken.net/api注意这里不加任何 UTM 参数Base URL 必须保持干净否则部分 SDK 会在 URL 拼接时把 query string 带进路径导致 404 或鉴权失败。第三步用环境变量收口避免 Key 硬编码进业务代码# ~/.zshrc 或部署环境的环境变量 export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYYOUR_API_KEY写一段最小可跑的连通性验证Python只用标准库方便在容器里裸跑import json import os import urllib.request BASE_URL os.environ[TAOTOKEN_BASE_URL].rstrip(/) API_KEY os.environ[TAOTOKEN_API_KEY] payload { model: your-voice-agent-model, messages: [ {role: system, content: 你是语音客服 Agent回答控制在 30 字以内。}, {role: user, content: 帮我查一下上一笔订单的物流状态。}, ], stream: False, } req urllib.request.Request( urlf{BASE_URL}/v1/chat/completions, datajson.dumps(payload).encode(utf-8), headers{ Content-Type: application/json, Authorization: fBearer {API_KEY}, }, methodPOST, ) with urllib.request.urlopen(req, timeout60) as resp: body json.loads(resp.read().decode(utf-8)) usage body.get(usage, {}) print(prompt_tokens , usage.get(prompt_tokens)) print(completion_tokens , usage.get(completion_tokens)) print(total_tokens , usage.get(total_tokens))这一步跑通的意义不只是「能通」而是确认你的链路里 usage 字段能被正常回传——后面做租户记账全靠它。如果你更想先用现成的对话界面把语音模型的行为摸一遍直接走模型对话入口https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_chat3. 多租户 Key 分层平台层 / 业务线层 / 租户层单 Key 模式的根本问题不是不安全而是「不可归因」。一旦出现异常消耗你无法回答三个问题哪个租户、哪条业务线、哪类任务。多租户分层要解决的就是这三个问题。我们最终采用的是三层结构平台层 Key只用于内部运维与健康检查不承载任何真实会话流量。它存在的意义是当某个租户 Key 被意外吊销时运维侧还有一条独立通道可以做连通性验证。这个 Key 绝不下发到业务侧。业务线层 Key按业务形态划分。语音 Agent 平台通常至少有「实时对话」「批量任务执行」两条线。实时对话的特征是高并发、单轮 Token 少、QPS 敏感批量任务执行的特征是低并发、单次 Token 多、可排队。这两类流量如果混在一个 Key 下限流策略会互相打架——实时对话被批量任务挤占配额或者反过来批量任务被实时限流打断。租户层 Key每个租户一个这是记账的最小单位。租户层 Key 的命名一定要可解析因为看板、告警、对账都依赖它。Key 命名规范建议直接写进配置表用下划线分段tt_{env}_{line}_{tenant_id}_{purpose}示例tt_prod_voice_t_10086_agent tt_prod_voice_t_10086_batch tt_prod_archive_t_10086_task tt_stg_voice_t_10086_agent这里env区分生产/预发line区分业务线tenant_id是租户主键purpose区分用途。这套命名看起来啰嗦但它让你在日志里只靠 Key 名就能完成粗略归因不需要每次都回查数据库。把分层结构落到一张配置表里PostgreSQL 方言按需替换CREATE TABLE tenant_api_keys ( key_id BIGSERIAL PRIMARY KEY, key_alias TEXT NOT NULL UNIQUE, -- tt_prod_voice_t_10086_agent key_fingerprint TEXT NOT NULL, -- 只存 Key 的哈希不存明文 tier TEXT NOT NULL, -- platform / line / tenant env TEXT NOT NULL, -- prod / stg biz_line TEXT NOT NULL, -- voice / archive tenant_id TEXT, qps_limit INTEGER NOT NULL DEFAULT 5, daily_token_cap BIGINT NOT NULL DEFAULT 0, -- 0 表示不限额 status TEXT NOT NULL DEFAULT active, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), revoked_at TIMESTAMPTZ ); CREATE INDEX idx_tenant_keys_lookup ON tenant_api_keys (env, biz_line, tenant_id, status);业务代码里Key 的选取应该由「租户 用途」推导而不是散落在各处import os from dataclasses import dataclass dataclass(frozenTrue) class KeyRef: alias: str value: str _KEY_STORE: dict[str, str] { # 生产环境建议从密钥管理服务注入不要写在代码里 tt_prod_voice_t_10086_agent: os.environ.get(TT_PROD_VOICE_10086_AGENT, ), tt_prod_voice_t_10086_batch: os.environ.get(TT_PROD_VOICE_10086_BATCH, ), } def resolve_key(env: str, biz_line: str, tenant_id: str, purpose: str) - KeyRef: alias ftt_{env}_{biz_line}_{tenant_id}_{purpose} value _KEY_STORE.get(alias) if not value: raise RuntimeError(fkey not configured: {alias}) return KeyRef(aliasalias, valuevalue)这样做的直接收益是当某个租户的语音 Agent 出现异常轮询你能在 5 分钟内定位到具体 Key然后单独把它的daily_token_cap收紧而不是粗暴地把整条业务线停掉。4. 用量看板字段语音轮次怎么记账才不糊涂多租户拆开之后下一个问题才是真正的难点语音场景的「一轮」到底是什么。纯文本对话里一轮 一次请求。语音里不是。一个典型的语音 Agent 会话链路大致是这样用户说话音频流持续上行可能几秒到几十秒中间触发 N 次意图识别 / 状态判断每次都是一次 LLM 调用主推理产生最终回复TTS 合成下行如果按「一次请求一行日志」记账你会发现同一通对话被拆成散落的多行租户对账时完全看不懂。所以我们的做法是以会话session为容器以轮次turn为记账单元把中间调用挂到轮次下。看板核心字段设计一张明细表 一张会话汇总表CREATE TABLE voice_turn_usage ( id BIGSERIAL PRIMARY KEY, session_id TEXT NOT NULL, tenant_id TEXT NOT NULL, biz_line TEXT NOT NULL, key_alias TEXT NOT NULL, turn_index INTEGER NOT NULL, -- 会话内第几轮从 1 开始 model_name TEXT NOT NULL, -- 输入侧 audio_input_ms INTEGER NOT NULL DEFAULT 0, prompt_tokens BIGINT NOT NULL DEFAULT 0, -- 中间调用意图识别/状态判断等 inner_calls INTEGER NOT NULL DEFAULT 0, inner_tokens BIGINT NOT NULL DEFAULT 0, -- 输出侧 completion_tokens BIGINT NOT NULL DEFAULT 0, tts_chars INTEGER NOT NULL DEFAULT 0, -- 结果与耗时 first_token_ms INTEGER, total_latency_ms INTEGER, finish_reason TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_turn_usage_tenant_time ON voice_turn_usage (tenant_id, created_at DESC); CREATE INDEX idx_turn_usage_session ON voice_turn_usage (session_id, turn_index);几个字段值得单独说明inner_calls和inner_tokens是语音场景独有的。近实时语音模型 Extended Thinking 这类能力会在一次「用户感知到的一轮」里插入多次模型调用。如果只看最终那一次调用的 usage租户会觉得「我就说了一句话为什么扣这么多」。把中间调用单独记一列对账时可以直接摊开给租户看。audio_input_ms和tts_chars是和 Token 口径不同的两条独立计费曲线。有些平台按音频时长计费语音识别按字符数计费合成这两列不能和 Token 混在一起统计否则看板上的「总消耗」会变成一个没有意义的数字。finish_reason必须记。语音场景里超时截断、用户打断、Agent 主动终止这三种情况的占比通常不低它们对应的计费口径可能不同。没有这个字段你无法解释「为什么有些轮次的 completion_tokens 明显偏少」。按租户 业务线聚合的汇总查询可以直接接到看板SELECT tenant_id, biz_line, date_trunc(hour, created_at) AS bucket, COUNT(DISTINCT session_id) AS sessions, SUM(turn_index * 0 1) AS turns, SUM(prompt_tokens) AS prompt_tokens, SUM(inner_tokens) AS inner_tokens, SUM(completion_tokens) AS completion_tokens, SUM(prompt_tokens inner_tokens completion_tokens) AS total_tokens, ROUND(AVG(first_token_ms)) AS avg_first_token_ms FROM voice_turn_usage WHERE created_at now() - INTERVAL 24 hours GROUP BY tenant_id, biz_line, bucket ORDER BY total_tokens DESC;说明上面的turns用了一个不太优雅但通用的写法实际生产建议直接COUNT(*)因为一行就是一轮。语音轮次 Token 对照口径下面这张表是我们内部使用的折算示例口径用来给租户解释「同样说一句话为什么成本不一样」。具体数值请以你实际接入的计费口径为准这张表的价值在于列结构而不是绝对值。环节计入字段触发条件折算说明主推理输入prompt_tokens每轮必触发包含系统提示词 历史轮次 本轮文本主推理输出completion_tokens每轮必触发流式输出时按累计增量统计意图识别inner_tokensinner_calls每 800ms 轮询一次时最密集短 prompt但调用次数极高是异常消耗的主要来源状态判断inner_tokensinner_calls按业务状态机触发通常复用主推理上下文prompt 会重复计入音频上行audio_input_ms持续监听模式必触发与 Token 独立核算不并入 total_tokens语音合成tts_chars每轮回复必触发与 Token 独立核算这张表最实用的一点是当某个租户的inner_calls / turns比值明显高于基线例如超过 10基本可以判定他们把轮询逻辑写进了 Agent此时应该先谈限流口径而不是先谈涨价。另外给每个租户加一条日额度闸门比事后追账有用得多-- 每日汇总供前置闸门读取 CREATE OR REPLACE VIEW v_tenant_daily_usage AS SELECT tenant_id, biz_line, date_trunc(day, created_at)::date AS usage_date, SUM(prompt_tokens inner_tokens completion_tokens) AS total_tokens, COUNT(*) AS turns FROM voice_turn_usage GROUP BY tenant_id, biz_line, usage_date;闸门逻辑在网关层做别塞进模型调用里def check_quota(row: dict, daily_cap: int) - None: if daily_cap and row[total_tokens] daily_cap: raise PermissionError( ftenant {row[tenant_id]} daily token cap reached: f{row[total_tokens]}/{daily_cap} )5. 研发侧的 Claude Code / Codex / CC Switch 配置语音 Agent 的研发侧同样要接到 TaoToken 上——写 Agent 编排、调 prompt、跑回归测试这些活都在本地 IDE 或终端里完成。三种常见工具的配置方式不一样千万不要混用。Claude Code走settings.json用ANTHROPIC_*系列变量{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: your-preferred-model, ANTHROPIC_SMALL_FAST_MODEL: your-fast-model } }注意ANTHROPIC_AUTH_TOKEN和ANTHROPIC_API_KEY在不同版本里行为会有差异如果遇到 401先把两个都指向同一个 Key 试一次确认是哪一个生效再删掉多余的。Codex走config.toml用的是 provider 结构和ANTHROPIC_*完全是两套东西不要互相套model your-preferred-model model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api/v1 env_key TAOTOKEN_API_KEY wire_api chat对应环境变量export TAOTOKEN_API_KEYYOUR_API_KEYCC Switch是用来在多套配置之间切换的工具它需要填的是「三件套」供应商标识、Base URL、API Key。新建一条供应商记录时Base URL 填https://taotoken.net/apiKey 填你从控制台创建的那一个供应商标识自己起名即可。这样在多个项目、多个租户测试环境之间来回切的时候不需要手动改settings.json。如果你还没装 Claude Code官方接入文档在这里https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_cc_doc6. 接入排障清单语音 Agent 最常见的五类报错语音链路比纯文本链路长报错也更分散。按出现频率排一下我们踩过的坑401 Unauthorized。八成是 Key 前缀没写全或者环境变量在容器里没注入成功。先做一件事把 Key 的前 6 位和后 4 位打日志确认注入的确实是你要用的那个。语音服务经常是多进程部署某一个 worker 的环境变量漏配很常见。404 Not Found。检查 Base URL 是否被拼接了多余路径。https://taotoken.net/api后面是否被框架自动补了/v1/v1/或者 query string 被带进了 path。这类问题在自研 HTTP 客户端里高发。429 Too Many Requests。多租户场景下429 要区分是「租户级限流」还是「业务线级限流」。做法是在响应头里带上触发的 Key alias日志里一眼可辨。如果 429 集中出现在某个租户的purposebatch上说明这个租户把批量任务挂到了实时链路应该拆 Key 而不是放宽限流。流式响应中断。语音场景里表现为「说到一半没声音了」。先确认是模型侧finish_reason是length还是stop再确认中间是否被租户侧的超时打断。把first_token_ms和total_latency_ms分别打点能快速区分是首包慢还是中途卡。用量对不上。租户说「我就打了 3 分钟电话」看板显示几万 Token。这时候把inner_calls翻出来给租户看通常能直接定位到轮询逻辑。如果确实是对账口径问题把第 4 节的字段表拉出来逐项核对。控制台里的用量视图和 Key 管理都在同一个入口创建新租户前先把 Key 建好https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_keys_recheck7. 把灰度扩量和成本控制放进同一条流水线回到最开始那个凌晨翻倍的问题。现在我们的处理流程是这样的新租户接入时先给tt_stg_voice_{tenant}_agent一个测试 Key跑一轮真实语音会话看inner_calls / turns比值落在什么区间比值正常再切生产 Key 并设置daily_token_cap上线后第一天看板按小时聚合异常立刻在同一小时内发现而不是等月度账单。这套流程之所以成立前提是接入侧足够简单Base URL 一处改动、Key 一处注入、看板一张表。如果你也正在做语音 Agent 平台建议先把 Key 分层和记账字段定下来再谈扩容。先用现成对话界面把模型行为摸清楚再决定租户配额怎么切https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_chat_cta需要给研发侧统一额度、把 Claude Code / Codex 的调用也纳入同一套体系时可以从 Coding Plan 开始https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_plan_cta然后回到控制台创建正式的租户 Key把命名规范落到配置表https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_keys_ctaClaude Code 侧的完整接入步骤和常见问题在这里照着配一遍基本能覆盖 90% 的场景https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_agent_cc_doc_cta语音模型的能力还会继续往前跑近实时、多轮、带中间推理的形态只会越来越普遍。对平台运营开发者来说模型选型可以跟着热点走但用量分层和记账口径这些东西越早定下来越省事——毕竟凌晨两点的账单曲线不会因为你换了个更强的模型就变得好解释。
返回列表