ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Dagger TypeScript SDK 中的 LLMTokenUsage:LLM 令牌用量统计的完整指南

Dagger TypeScript SDK 中的 LLMTokenUsage:LLM 令牌用量统计的完整指南 Dagger TypeScript SDK 中的 LLMTokenUsageLLM 令牌用量统计的完整指南【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger本文围绕 Dagger 引擎内置 LLM大语言模型能力中的令牌用量统计对象LLMTokenUsage展开说明其在 TypeScript SDK 中的类结构、六个查询方法、各令牌桶的语义差异以及它背后从 GraphQL Schema、Go 核心实现到聚合逻辑的完整数据链路。读完本文你将能够在 Dagger 模块中准确读取单次会话的输入、输出、缓存读写与总令牌数理解缓存令牌为何单独计桶并学会在成本核算与上下文窗口管理中正确使用这些数据。一、LLMTokenUsage 是什么在 Dagger 中LLM 是一个核心 API 对象对应 core/schema/llm.go 中的llmSchema用于驱动基于大模型的任务注入 system prompt、追加消息、绑定工具、执行会话等。每次调用模型供应商的 API都会产生一组令牌消耗数据。LLMTokenUsage就是用来承载这些数据的对象——用官方文档中的类型描述来说它是一个由 LLM API 调用所消耗的令牌计数A count of tokens consumed by LLM API calls。在 TypeScript SDK 中该类的 API 参考文档位于 docs/versioned_docs/version-0.21/reference/typescript/api/client.gen/classes/LLMTokenUsage.md其实际生成源码位于 sdk/typescript/src/api/client.gen.ts。二、类的整体结构继承与构造2.1 继承关系LLMTokenUsage继承自BaseClient这是 Dagger TypeScript SDK 中所有客户端对象的公共基类负责持有Context查询执行上下文并提供 GraphQL 查询的基础执行能力LLMTokenUsage └── BaseClient2.2 构造函数仅供内部使用构造函数签名如下new LLMTokenUsage( ctx?: Context, _id?: ID, _cachedTokenReads?: number, _cachedTokenWrites?: number, _inputTokens?: number, _outputTokens?: number, _totalTokens?: number, ): LLMTokenUsage官方文档明确说明Constructor is used for internal usage only, do not create object from it.构造函数仅供内部使用请勿直接构造该对象。所有以_开头的参数都是内部缓存字段当对象从服务端返回时SDK 会预先填充这些值在客户端代码中你应该通过LLM.tokenUsage()方法获取该对象而不是自行new。三、正确获取方式LLM.tokenUsage()LLMTokenUsage的实例通常由LLM对象的tokenUsage()方法返回。在 TypeScript SDK 生成源码中可见sdk/typescript/src/api/client.gen.tstokenUsage (): LLMTokenUsage { return new LLMTokenUsage(ctx) }服务端这一查询在 core/schema/llm.go 中的解析器实现为func (s *llmSchema) tokenUsage(ctx context.Context, llm *core.LLM, _ struct{}) (*core.LLMTokenUsage, error) { srv, err : core.CurrentDagqlServer(ctx) if err ! nil { return nil, err } return llm.TokenUsage(ctx, srv) }即LLM.tokenUsage()直接调用核心层LLM.TokenUsage()方法完成聚合详见本文第五节。四、六个查询方法逐一详解LLMTokenUsage对外提供 6 个异步方法全部返回Promise可直接await。每个方法都会基于this._ctx.select(...)构造对应 GraphQL 字段的查询并执行若构造时已传入对应缓存值则直接返回缓存避免不必要的网络往返。4.1 inputTokens()未缓存输入令牌inputTokens(): Promisenumber返回未缓存的输入令牌数发送给模型的 prompt 中未被提示缓存命中的部分。底层字段语义见 core/llm.goUncached input tokens sent to the model.4.2 outputTokens()输出令牌outputTokens(): Promisenumber返回模型返回的令牌数包括文本与工具调用tool call部分。底层字段语义见 core/llm.goTokens received from the model, including text and tool calls.4.3 cachedTokenReads()缓存读取cachedTokenReads(): Promisenumber返回由供应商提示缓存prompt cache直接服务的输入令牌数。底层字段语义见 core/llm.goInput tokens served from the providers prompt cache.这类令牌成本通常显著低于未缓存输入。4.4 cachedTokenWrites()缓存写入cachedTokenWrites(): Promisenumber返回写入供应商提示缓存的输入令牌数。底层字段语义见 core/llm.goInput tokens written to the providers prompt cache.首次访问某段 prompt 时会产生写入成本后续命中则计入cachedTokenReads。4.5 totalTokens()总令牌数totalTokens(): Promisenumber返回供应商报告的总令牌数。底层字段语义见 core/llm.goTotal tokens consumed, as reported by the provider.注意它优先采用供应商原生上报的总量而非简单求和。4.6 id()唯一标识id(): PromiseID返回该LLMTokenUsage实例的唯一标识符ID类型。LLMTokenUsage在核心层实现了dagql.PersistedObject接口core/llm.go支持编码、解码与持久化id()即用于在 DAG 执行引擎中稳定引用该对象。五、字段语义与聚合原理Go 核心实现5.1 核心结构体定义TypeScript 类的每个字段最终都映射到核心 Go 结构体core.LLMTokenUsagecore/llm.gotype LLMTokenUsage struct { InputTokens int64 field:true json:input_tokens doc:Uncached input tokens sent to the model. OutputTokens int64 field:true json:output_tokens doc:Tokens received from the model, including text and tool calls. CachedTokenReads int64 field:true json:cached_token_reads doc:Input tokens served from the providers prompt cache. CachedTokenWrites int64 field:true json:cached_token_writes doc:Input tokens written to the providers prompt cache. TotalTokens int64 field:true json:total_tokens doc:Total tokens consumed, as reported by the provider. }结构体注释中明确说明了分桶设计的动机缓存输入被单独计入CachedTokenReads/CachedTokenWrites从而使各桶对于成本核算cost与上下文核算context accounting是可加additive的。5.2 会话级聚合TokenUsage() 的累加语义LLMTokenUsage是会话级累计值。核心层LLM.TokenUsage()core/llm.go遍历该 LLM 会话的全部消息逐条累加func (llm *LLM) TokenUsage(ctx context.Context, dag *dagql.Server) (*LLMTokenUsage, error) { var res LLMTokenUsage for _, msg : range llm.Messages { if msg.TokenUsage nil { continue } res.InputTokens msg.TokenUsage.InputTokens res.OutputTokens msg.TokenUsage.OutputTokens res.CachedTokenReads msg.TokenUsage.CachedTokenReads res.CachedTokenWrites msg.TokenUsage.CachedTokenWrites res.TotalTokens msg.TokenUsage.TotalTokens } return res, nil }因此你在 TypeScript 中await llm.tokenUsage()得到的数值是整个会话含多次模型调用的累计结果而不是最后一次调用的单次用量。单次用量挂在每条消息上每条LLMMessage都带有一个可选的TokenUsage字段注释说明它由产生该消息的 API 调用上报除 assistant 响应外全部为零core/llm.go。5.3 与上下文窗口的关系累计的TokenUsage用于成本核算而上下文窗口占用则使用单独的ContextTokens估算逻辑core/llm.go两者不要混用。核心层还提供contextTokens()辅助方法core/llm.gofunc (usage LLMTokenUsage) contextTokens() int64 { components : usage.InputTokens usage.OutputTokens usage.CachedTokenReads usage.CachedTokenWrites return max(usage.TotalTokens, components) }即先计算四个可加桶之和再与供应商上报的TotalTokens取最大值。这样既能在供应商未上报总量时兜底也能避免供应商原生总量可能额外计入 reasoning、tool-use 等分类被截断。5.4 未缓存输入的计算规则核心层通过uncachedInputTokens()core/llm.go在缓存令牌存在时推导未缓存输入func uncachedInputTokens(promptTokens, cachedTokens int64) int64 { if cachedTokens 0 { return promptTokens } if promptTokens cachedTokens { return promptTokens - cachedTokens } return promptTokens }这解释了为什么inputTokens与cachedTokenReads是互补的prompt 中被缓存命中的部分不再计入未缓存输入。六、数据从何而来provider 响应与消息历史LLMTokenUsage的数据源头是各模型供应商的 API 响应。核心层将供应商返回结果封装为LLMResponsecore/llm.go其中携带TokenUsage随后评估循环evaluation loop将其转换为LLMMessage历史条目并把用量写入消息的TokenUsage字段。整个链路为模型供应商 API 响应 ↓ 封装 core.LLMResponse.TokenUsage ↓ 写入消息历史 LLMMessage.TokenUsage仅 assistant 响应非零 ↓ 会话聚合 LLM.TokenUsage() → GraphQL tokenUsage 字段 ↓ TypeScript SDK llm.tokenUsage() → LLMTokenUsage 对象6 个查询方法此外withResponse函数core/schema/llm.go允许不调用模型、直接向消息历史追加一条 assistant 响应此时也可显式传入五个用量参数inputTokens、outputTokens、cachedTokenReads、cachedTokenWrites、totalTokens例如用于从其他来源重建一段对话及其成本数据。七、TypeScript 实战示例在 Dagger TypeScript 模块中典型的用法如下import { dag } from dagger.io/dagger // 获取当前 LLM 会话的累计令牌用量 const usage dag.llm().tokenUsage() // 分别读取各令牌桶 const input await usage.inputTokens() // 未缓存输入令牌 const output await usage.outputTokens() // 输出令牌含文本与工具调用 const cachedReads await usage.cachedTokenReads() // 缓存命中读取 const cachedWrites await usage.cachedTokenWrites() // 缓存写入 const total await usage.totalTokens() // 供应商上报总令牌数 // 成本核算各桶相加或直接使用总量 console.log({ uncachedInput: input, output, cachedReads, cachedWrites, total, })需要注意的是usage是惰性查询对象只有调用具体方法如await usage.totalTokens()时才会真正向引擎发起 GraphQL 查询若构造时已携带缓存值则直接返回。同时由于TokenUsage是会话累计值若你在多次调用模型后分别读取得到的是截至当前的累计数。八、小结LLMTokenUsage是 Dagger LLM 能力中面向成本核算的核心数据对象TypeScript SDK 通过继承BaseClient的 6 个异步方法暴露五个令牌桶与对象标识。理解其底层语义core/llm.go有助于正确解读数据inputTokens/outputTokens是常规输入输出cachedTokenReads/cachedTokenWrites单独计桶以支撑缓存成本核算totalTokens以供应商上报为准而会话级累加逻辑core/llm.go与上下文窗口估算ContextTokens的区分则是避免把总消耗误当当前占用的关键。结合官方 API 文档LLMTokenUsage.md与生成源码client.gen.ts你可以在自己的 Dagger 模块中精确跟踪每一次 LLM 会话的令牌消耗。【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表