ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Token计费到Task计费,为什么编排是关键?

Token计费到Task计费,为什么编排是关键?

最近一次复盘,我看到团队上个月的AI服务账单,心里咯噔了一下——比预算高了两倍多。倒不是用量失控,而是我们按Token计费的方式,根本没法给客户一个稳定的成本预期。这种账单焦虑,做AI应用的人应该都不陌生。

今天想认真聊一个话题:为什么我认为AI服务的计费单位,迟早要从Token切到Task;以及这中间最关键的技术环节,为什么是“编排”。

我是奥锐方的邵剑秋,负责AI基础设施方向。我们团队一直在做AI工作流产品,从最早纯调接口,到后来做编排层,再到帮客户设计计费方案,踩了足够多的坑,才慢慢想明白这里面的逻辑。

先交代一下本文会聊些什么:Token计费的痛点出在哪、Task计费到底在计什么、编排为什么是转型的关键,以及我们落地编排系统时遇到的实际报错和解决方案(比如Token续签失败、输出超过上限被截断这类问题)。如果你正在做Agent产品,或者正在纠结怎么给AI能力定价,这篇文章应该能给你一些参考。

1. Token账单为什么让人睡不着觉

1.1 三座大山:不可预测、台阶式上涨、失败也收费

先看现状。现在绝大多数大模型厂商按Token计费,用多少扣多少。这种方式看起来公平,但它其实把波动性全部转移给了用户。

第一,成本不可预测。同一个需求,用户换个说法,Token数可能差好几倍。有一次我们做文本抽取,用户输入一份50页PDF,光预处理和分块就消耗了接近400K的输入Token,模型实际产出的有效信息可能只有几百字。按Token算,这笔费用绝大部分花在“读文档”上,而不是“解决问题”上。

第二,上下文越长越贵。模型的Context Window在增长,但成本也在增长。一次长对话,历史消息全部作为输入Token被重复计费。用户每多问一句话,前面的历史就要重新算一遍,费用不是线性增长,是台阶式上涨。

第三,失败也计费。模型超时、接口返回异常、任务做了一半被打断,只要Token已经送进去,费用已经产生。我们曾遇到一个任务,模型在生成长文时反复触发“已达到输出Token上限,回答被截断”,客户端不断重试,Token消耗变成正常情况的三倍。

这三座大山叠加起来,结果就是:用户根本没法给老板报预算。而做AI SaaS的团队,也没法向客户解释“为什么这个月账单贵了一半”。

1.2 Token在LLM里的“三个点”:Key、Query与Value

要理解为什么Token计费会跟产品价值脱节,得先看Token在LLM里的实际作用。网上流传一个说法,LLM的Token有“三个点”:

  • Key:我是谁,也就是当前对话的身份和上下文;
  • Query:我在找什么,也就是用户当下提出的指令或意图;
  • Value:我能提供什么,也就是模型针对这个指令生成的答案。

一次完整的调用,本质上是模型拿着Key和Query,在自己的参数空间里查找并生成Value。Token计费恰恰把这三者混在一起计价:既收“我是谁”的上下文钱,也收“我在找什么”的输入钱,还收“我能提供什么”的输出钱。

但用户真正愿意付费的,只有Value,也就是“问题被解决了”。Key和Query更像是成本,而不应该是利润。这就是Token计费和产品价值之间最核心的脱节点。

1.3 “不限Token”套餐为什么治标不治本

厂商其实也意识到这个问题,所以市场上出现了“不限Token”这类营销方案。但做产品的人都清楚,“不限Token”意味着要么亏本卖,要么在模型质量上动手脚——悄悄缩短上下文、限制并发、限速。

我们团队内部讨论过这个方向,结论是:这不解决本质问题。因为“不限Token”还是按Token的成本模型在估算盈亏,只是把计费动作藏到了背后。真正的出路,是彻底改变计费单位。

2. Task计费:按交付物计价,而不是按原料计价

2.1 什么是合格的Task定义

简单地说,Task是“用户发起的一个有明确目标的完整请求”。它不是一个Prompt,而是一次完整的问题解决过程。

举例:用户说“把这份会议纪要整理成三个待办事项”,这是一个Task。往深看,这个Task可能包含:读取文件、理解内容、抽取待办、格式化输出、生成回执。但在用户视角,他只完成了一件事。

我们团队在实践中,把Task定义为三个要素:

  • 明确的目标语义(用户感知的结果);
  • 明确的输入输出边界(输入是什么,输出是什么);
  • 可验证的完成条件(什么算做完了)。

这三点是把Task从“一个API调用”里分离出来的基础。如果一个请求拆不出“明确完成条件”,它就不适合直接按Task计费,需要再拆细一点。

2.2 Task计费带来的三个实际变化

我们帮客户切换计费模式之后,感受到了几个非常直接的变化。

第一,预算可预期。客户对单价有明确预期。比如“整理会议纪要”这个任务,一个固定价格;不管用户上传的会议记录是2页还是20页,客户知道单价。虽然供应商的成本可能略有浮动,但通过限流和分级模型,整体利润可控。

第二,对账简单。Token计费的对账,需要翻日志、看Token数明细,非技术客户完全看不懂。Task计费只需要一张表:哪个任务、多少次、多少钱,业务人员也能看得懂。

第三,价值绑定。客户愿意为一个“整理好的会议纪要”付费,而不是为“10万Token的文本处理过程”付费。这带来的直接结果,是同一个功能模块,按Task定价可以比按Token定价卖出更高的客单价,因为用户看到的交换物变清晰了。

下面用一个表来对比这两种模式:

维度按Token计费按Task计费
计费依据输入输出Token总量任务完成次数
成本可预测性低,随上下文波动高,单价固定
对账方式Token日志明细任务记录流水
用户感知“我为文本长度付费”“我为结果付费”
供应商风险单价固定,成本浮动任务固定,需编排控成本

2.3 几乎翻车的一次尝试:Task计价不是一口价

需要特别强调:从Token到Task,不是改一行计价脚本那么简单。

很多团队以为按Task计费,就是后台把Token用量汇总一下,除以某个系数,然后写一个固定的“一口价”。如果真这么做,很快就会亏穿,因为不同场景的Token消耗差异太大了。

我们其实经历过这个阶段。最早把Task计价表拿给客户看的时候,我们自己心里都在打鼓——有些客户的使用路径简单,一条Prompt就能完成,Token成本可能只有几千;有的客户把复杂数据喂进来,要经过解析、分块、多次推理,Token成本轻松突破百万级别。

如果都按一个Task一口价收,要么简单客户觉得贵,要么复杂客户直接让我们亏本。所以Task计费真正落地的前提,是要把“一个Task怎么被多个模型请求完成”这个过程计算清楚,这就必须依赖编排层的能力。

3. 编排为什么是关键:从串行接口调用到任务状态机

3.1 没有编排的Task只是一层封装

我见过不少团队,业务层已经定义了“任务”的概念,但后端仍然是“收到请求直接调用LLM”。这种架构下,Task只是一个HTTP接口的封装——任务拆解、失败重试、中间结果保存,这些能力全都没有。

结果就是:如果一个任务需要调用三次模型,中途第二次失败,整个任务就得从头再来。被浪费掉的Token,最后还是会计到客户头上。这本质上还是没有跳出Token思维。

要支撑Task计费,编排层至少得具备三个能力:子任务拆解能力、子任务执行记录能力、失败恢复能力。没有这三样,哪怕计费单位改成了Task,背后依然是Token级别的粗放消耗。

3.2 编排要管住三件大事:依赖图、上下文、不确定性

第一,依赖关系。一个Task往往可以拆成多个子步骤,子步骤之间有的是串行,有的是并行。比如“整理会议纪要”这个Task,可能是:先并行读取多个文件,然后统一做摘要,再抽取待办,最后组装输出。这是一张有向无环图(DAG),不是一条直线。

在编排系统里,我们把每个子步骤定义成一个节点,节点间声明依赖。并行节点可以同时调度,串行节点等待上游完成。这种结构在运维侧还有一个额外好处:如果某个节点失败,只需要重跑该节点,而不需要重跑整条链路。

第二,上下文传递。LLM的Context是有限的,一个Task如果整体塞进一个上下文,很快就到上限。编排的价值,是把大的任务切分成小的上下文分段,每段只留与当前子步骤相关的信息,子步骤的输出再作为下一个子步骤的输入。

第三,不确定性的处理。LLM不是传统意义上的确定性系统,同一个Prompt,两次调用结果可能不同。编排层要通过重试、候选结果择优、人工确认节点等手段,兜住这种不确定性。没有这层兜底,按Task计费很容易变成按“运气”计费。

3.3 卫星测控给编排引擎的两个启发

说来有意思,我们团队在设计编排引擎时,参考最多的不是互联网领域的调度系统,而是航天领域的卫星任务规划。

卫星在轨运行,测控站资源有限,一个测控任务要经过圈次预报、冲突消解、链路规划、指令上注、执行监视、结果评估这一整套流程,每一步都对应明确的“任务状态”。这跟AI工作流极其相似:同样是多个步骤、同样有资源约束、同样需要监控每一步的成败。

航天任务编排给了我们两个很重要的启发:

  • 每个任务必须有确定性的状态机(待执行、执行中、成功、失败、超时),不能出现“薛定谔的任务”;
  • 每个步骤都要留痕,出了问题能回溯到具体节点。

这两点,恰好也是Task计费对账的基础:每一次收费,都能对应到一组可回溯的任务执行记录。

4. 编排落地中的真实报错与排查复盘

理论说完了,说点实际的。我们从设计编排引擎到上线,中间踩了一堆坑,挑几个有代表性的记录一下。

4.1 token exchange failed:身份续期没做全局管理

编排系统要让不同节点共享同一个用户身份,就得先解决认证问题。我们最开始用的是常规做法:登录时拿一个短时Access Token,后续请求都带着它。

然后问题就来了。一个复杂Task可能执行几分钟,长一点的要十几分钟,而Access Token的有效期往往只有几十分钟。如果用户发起的任务执行时间刚好卡在Token过期节点上,后续子步骤请求就会报:

sign-in could not be completed token exchange failed: token endpoint returned status 403 forbidden

或者是:

failed to refresh token: 400 bad request: invalid 'refresh_token': empty string. expected a string with minimum length 1, but got an empty string instead.

我们当时排查了很久,最后发现是两个问题叠加:一是Access Token过期后,编排系统自动发起刷新请求,但刷新用的refresh_token本身已经过期或为空;二是编排引擎的节点间认证信息没有透传,导致子任务用的还是最初那个Token。

解决方案是引入JWT续签机制:把refresh_token的过期时间拉长,在编排上下文里维护Token生命周期;同时每个子节点发起请求前,统一走一个认证中间件,先检查Token剩余有效时间,低于阈值就主动刷新,而不是等到403返回再处理。

顺便说一句,JWT实现Token续签,网上的教程很多,但真正落地的时候注意两点:一是refresh_token一定要做服务端持久化,不能只存在内存里,否则编排引擎一重启,所有会话全部失效;二是刷新接口要做并发控制,别让多个子节点同时刷新同一个Token,不然刷新请求并发打到认证服务上,旧Token还没替换完就会报错。

4.2 输出Token上限截断:把长文本Task切成子任务

第二个高频问题,就是“已达到输出Token上限,回答被截断”。

我们做自动生成长文档的Task,经常碰到这种情况。明明任务还没完成,模型输出到达上限就停了,客户端拿到的是截断文本。按Token计费时,这截断的部分同样收钱,客户自然很不爽。

编排层的应对思路是:把长文本生成拆成“分段生成”的子任务,而不是一次让模型吐完全部内容。比如要生成一份一万字的报告,我们不会一次性调用模型,而是先让模型生成大纲(第一段),再按大纲逐节生成(后续多个子任务),最后做一次汇总校对。

每段的输出长度控制在模型最大输出Token数的60%以内,留足余量。这样即使某个子任务触发上限,重跑的成本也只是这一段的Token,而不是整个任务的Token。

这里还可以顺带说一个技巧:在编排节点里主动设置输出Token上限,不要直接用模型默认的最大值。把上限设低一点,配合重试机制,整体成功率反而更高,因为单次生成长度越长,模型越容易出现格式散乱和内容重复的问题。

4.3 could not determine the dependencies of task:状态机需要CAS

还有一个印象很深的坑,发生在我们自研编排引擎的依赖解析器上。当时有个任务配置依赖关系,本地调试一切正常,到生产环境一跑,日志里全是类似这样的报错:

could not determine the dependencies of task ':app:compiledebugjavawithjavac' could not resolve all task dependencies for configuration ':app:debugcompileclasspath'

看报错格式,猜得出来,我们早期的编排引擎,依赖解析逻辑参考了Gradle Task依赖系统的设计。这个思路本身没问题,但我们的实现里有个bug:当一个子任务既被A依赖、又被B依赖,而A、B又分属不同分支时,依赖解析器偶尔会返回“依赖未解析”的结果。

类比到Gradle场景,就是编译任务在Configuration阶段没有找到正确的Classpath依赖,最终报“could not determine the dependencies of task”。

排查下来的根因是:依赖图构建时,多个入边节点被同一个线程池调度,但节点状态没有做原子更新,导致下游节点查询依赖状态时,读到的是一个中间态。

修法也不复杂,给节点状态机加版本号,状态流转走CAS(Compare And Swap),下游节点只有看到稳定终态才允许消费上游数据。核心逻辑类似这样:

@dataclass class StepState: status: str # pending | running | success | failed | timeout version: int def mark_success(state): while True: current = state.version if state.status in ("success", "failed"): return False with state.lock: if state.version != current: continue state.status = "success" state.version += 1 return True

改完之后,类似的依赖报错再没出现过。这个教训我们后来写进了团队的研发规范:所有跨线程共享的状态,一律不允许裸读写。

4.4 幂等没做好:同一个Task被扣了两次费

最后说一下幂等设计,这是做Task计费最容易忽略的地方。

编排系统天然有重试机制,但重试如果做得不干净,同一个Task可能被重复执行。我们早期就是这样:某个节点超时,编排引擎自动重试,结果前一次请求其实已经成功,只是响应超时;重试又执行了一遍,模型调用费Double,最后还要给客户退钱。

解决办法是在编排上下文里维护一个全局的Task ID,每个模型调用节点都带上这个ID,配合数据库的唯一索引做去重;所有节点执行前先检查“是否已执行成功”。

这件事比想象中麻烦,因为LLM接口的响应不是事务性的,很多厂商的API没法做到“同一个ID只执行一次”。所以我们在中间加了一层执行记录表:每次调用前先落一条“执行中”记录,调用完成后更新为“成功”或“失败”。这样即使接口层面重复执行,对账层面也能识别出来。

5. 多Agent编排与Task计费的下一个战场

5.1 从单Agent到多Agent:Task粒度在变粗

编排层跑通之后,自然会往多Agent方向走。

现在的产品形态,已经不是一个Prompt对应一个模型调用,而是多个Agent协同完成一个复杂目标。比如客户要做一份市场调研报告,可能需要一个Agent做数据收集、一个Agent做资料分析、一个Agent做报告撰写、一个Agent做事实核查。这些Agent之间既可能是串行流水线,也可能是并行协同的关系。

到了这个阶段,Task的定义也随之升级:一个Task对应一次完整的Agent协作过程,而不是单个Agent的一次推理。大家现在经常听到的“Workflow编排”“Agent框架与编排”“多Agent编排示例”,讲的都是这件事。

多Agent编排的核心挑战是“责任边界”。我们用的模式是:主Agent负责把目标拆解成子任务,分发下去;子Agent各自执行,把结果返回主Agent。每一个Agent的执行记录,都作为Task的子步骤留存在编排引擎里。

5.2 框架选择:先借力,再自研

很多团队上来就想自研编排框架,我个人建议是不要着急。市面上成熟的Workflow编排工具很多,从低代码平台到偏底层的任务调度系统,其实都可以用来构建Task层。我们早期图省事,用了一部分成熟框架的思路来管理任务状态和重试,事实证明它的稳定性的确比自研要好。

自研的价值在于跟业务深度绑定,比如计费对账、Agent责任追踪、身份透传这些,通用框架都不太好覆盖。所以比较现实的路径是:先用成熟框架把任务编排跑通,把状态管理、重试、监控这些通用能力交给框架,自己集中精力做业务编排模型和计费模块。

选型的时候可以重点看三件事:是否支持DAG定义、是否有完善的重试和超时机制、是否方便接入外部API作为节点。这三件事直接决定了后续做Task计费时,能不能准确归集每个子步骤的Token消耗。

5.3 关于AI定价的一点点预判

最后聊聊趋势。

从我接触到的客户反馈来看,AI能力按Token计费一定会慢慢退出主流场景,留下来的是当成本价计量的内部口径;对外给客户展示的,应该是“任务包”“次数包”这类更直观的计费方式。甚至有客户开始问能不能做“不限Token”的打包方案,但我们的建议是打包可以,底层要做细颗粒度的Task计量,不然亏的时候都不知道亏在哪。

更深一层,我认为AI服务的定价最终会分化成两层:

  • 底层是基础设施成本,用Token做内部计量;
  • 上层是业务价值,用Task面向客户计费。

编排层正好卡在这两层中间:它既负责把上层的业务目标翻译成底层的模型调用,也负责把底层的Token消耗归集到上层的Task账单。这大概就是“编排是关键”这句话的真实含义——计费方式的变化,不只是财务层面的选择,更是技术架构和产品设计逻辑的共同升级。

我个人在实际操作中的体会是,从Token计费切到Task计费,核心的精力没有花在计价表上,全花在了编排系统上。回过头看,这个顺序是对的:编排能力跟不上,Task计费就是空中楼阁。如果你也准备做这件事,建议从一套靠谱的任务状态机开始,先把“任务成功、失败、超时、重试”这四个状态管清楚,再去想定价的事。另外,所有涉及Token的报错,别看一条解决一条,建议整体梳理一遍编排链路上的身份、上下文、依赖和幂等四个层面,很多所谓“随机报错”最后都能落到这四个层面之一。祝大家的AI产品都能找到一个让客户睡得着觉的计费方式。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进