如何在ChatModels中跟踪token使用情况
本指南假定您熟悉以下概念
跟踪token使用情况以计算成本是您的应用程序投入生产的重要组成部分。本指南将介绍如何从 LangChain 模型调用中获取此信息。
本指南需要 langchain-anthropic
和 langchain-openai >= 0.3.11
。
%pip install -qU langchain-anthropic langchain-openai
OpenAI 的聊天完成 API 默认不流式传输 token 使用统计信息(参见此处的 API 参考)。要在使用 ChatOpenAI
或 AzureChatOpenAI
进行流式传输时恢复 token 计数,请将 stream_usage=True
设置为本指南中所示。
使用 LangSmith
您可以使用 LangSmith 帮助跟踪 LLM 应用程序中的 token 使用情况。请参阅 LangSmith 快速入门指南。
使用 AIMessage.usage_metadata
许多模型提供商会将 token 使用信息作为聊天生成响应的一部分返回。如果可用,此信息将包含在相应模型生成的 AIMessage
对象中。
LangChain AIMessage
对象包含一个 usage_metadata 属性。当填充时,此属性将是一个 UsageMetadata 字典,其中包含标准键(例如,"input_tokens"
和 "output_tokens"
)。它们还将包含有关缓存 token 使用情况和多模态数据中 token 的信息。
示例
OpenAI:
from langchain.chat_models import init_chat_model
llm = init_chat_model(model="gpt-4o-mini")
openai_response = llm.invoke("hello")
openai_response.usage_metadata
{'input_tokens': 8, 'output_tokens': 9, 'total_tokens': 17}
Anthropic:
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-3-haiku-20240307")
anthropic_response = llm.invoke("hello")
anthropic_response.usage_metadata
{'input_tokens': 8, 'output_tokens': 12, 'total_tokens': 20}
流式传输
一些提供商在流式传输上下文中支持 token 计数元数据。
OpenAI
例如,OpenAI 将在流结束时返回一个包含 token 使用信息的消息块。此行为由 langchain-openai >= 0.1.9
支持,并且可以通过设置 stream_usage=True
来启用。此属性也可以在实例化 ChatOpenAI
时设置。
默认情况下,流中的最后一个消息块将在消息的 response_metadata
属性中包含一个 "finish_reason"
。如果我们在流式传输模式下包含 token 用量,则会向流的末尾添加一个包含用量元数据的额外块,以便 "finish_reason"
出现在倒数第二个消息块中。
llm = init_chat_model(model="gpt-4o-mini")
aggregate = None
for chunk in llm.stream("hello", stream_usage=True):
print(chunk)
aggregate = chunk if aggregate is None else aggregate + chunk
content='' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content='Hello' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content='!' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content=' How' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content=' can' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content=' I' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content=' assist' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content=' you' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content=' today' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content='?' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content='' response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-mini'} id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623'
content='' id='run-adb20c31-60c7-43a2-99b2-d4a53ca5f623' usage_metadata={'input_tokens': 8, 'output_tokens': 9, 'total_tokens': 17}
请注意,使用元数据将包含在单个消息块的总和中
print(aggregate.content)
print(aggregate.usage_metadata)
Hello! How can I assist you today?
{'input_tokens': 8, 'output_tokens': 9, 'total_tokens': 17}
要禁用 OpenAI 的流式 token 计数,请将 stream_usage
设置为 False,或从参数中省略它
aggregate = None
for chunk in llm.stream("hello"):
print(chunk)
content='' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content='Hello' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content='!' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content=' How' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content=' can' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content=' I' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content=' assist' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content=' you' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content=' today' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content='?' id='run-8e758550-94b0-4cca-a298-57482793c25d'
content='' response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-mini'} id='run-8e758550-94b0-4cca-a298-57482793c25d'
您还可以在实例化聊天模型时通过设置 stream_usage
来启用流式 token 用量。这在将聊天模型集成到 LangChain 链中时非常有用:当流式传输中间步骤或使用LangSmith等追踪软件时,可以监控用量元数据。
请看下面的示例,我们返回按照所需模式结构化的输出,但仍然可以观察到从中间步骤流式传输的 token 用量。
from pydantic import BaseModel, Field
class Joke(BaseModel):
"""Joke to tell user."""
setup: str = Field(description="question to set up a joke")
punchline: str = Field(description="answer to resolve the joke")
llm = init_chat_model(
model="gpt-4o-mini",
stream_usage=True,
)
# Under the hood, .with_structured_output binds tools to the
# chat model and appends a parser.
structured_llm = llm.with_structured_output(Joke)
async for event in structured_llm.astream_events("Tell me a joke"):
if event["event"] == "on_chat_model_end":
print(f"Token usage: {event['data']['output'].usage_metadata}\n")
elif event["event"] == "on_chain_end" and event["name"] == "RunnableSequence":
print(event["data"]["output"])
else:
pass
Token usage: {'input_tokens': 79, 'output_tokens': 23, 'total_tokens': 102}
setup='Why was the math book sad?' punchline='Because it had too many problems.'
Token 用量在 LangSmith 对应的追踪中聊天模型的有效载荷中也可见。
使用回调函数
langchain-core>=0.3.49
LangChain 实现了一个回调处理程序和上下文管理器,可以跟踪任何返回 usage_metadata
的聊天模型调用中的 token 用量。
还有一些针对特定 API 的回调上下文管理器,它们维护不同模型的定价,从而可以实时估算成本。它们目前仅针对 OpenAI API 和 Bedrock Anthropic API 实现,并可在 langchain-community
中使用。
下面,我们演示通用用量元数据回调管理器。我们可以通过配置或作为上下文管理器来跟踪 token 用量。
通过配置跟踪 token 用量
要通过配置跟踪 token 用量,请实例化一个 UsageMetadataCallbackHandler
并将其传递给配置
from langchain.chat_models import init_chat_model
from langchain_core.callbacks import UsageMetadataCallbackHandler
llm_1 = init_chat_model(model="openai:gpt-4o-mini")
llm_2 = init_chat_model(model="anthropic:claude-3-5-haiku-latest")
callback = UsageMetadataCallbackHandler()
result_1 = llm_1.invoke("Hello", config={"callbacks": [callback]})
result_2 = llm_2.invoke("Hello", config={"callbacks": [callback]})
callback.usage_metadata
{'gpt-4o-mini-2024-07-18': {'input_tokens': 8,
'output_tokens': 10,
'total_tokens': 18,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}},
'claude-3-5-haiku-20241022': {'input_tokens': 8,
'output_tokens': 21,
'total_tokens': 29,
'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}
使用上下文管理器跟踪 token 用量
您还可以使用 get_usage_metadata_callback
创建一个上下文管理器并在其中聚合用量元数据
from langchain.chat_models import init_chat_model
from langchain_core.callbacks import get_usage_metadata_callback
llm_1 = init_chat_model(model="openai:gpt-4o-mini")
llm_2 = init_chat_model(model="anthropic:claude-3-5-haiku-latest")
with get_usage_metadata_callback() as cb:
llm_1.invoke("Hello")
llm_2.invoke("Hello")
print(cb.usage_metadata)
{'gpt-4o-mini-2024-07-18': {'input_tokens': 8, 'output_tokens': 10, 'total_tokens': 18, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'claude-3-5-haiku-20241022': {'input_tokens': 8, 'output_tokens': 21, 'total_tokens': 29, 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}}
这两种方法都将聚合模型多次调用中的 token 用量。例如,您可以在代理中使用它来跟踪对一个模型的重复调用中的 token 用量
%pip install -qU langgraph
from langgraph.prebuilt import create_react_agent
# Create a tool
def get_weather(location: str) -> str:
"""Get the weather at a location."""
return "It's sunny."
callback = UsageMetadataCallbackHandler()
tools = [get_weather]
agent = create_react_agent("openai:gpt-4o-mini", tools)
for step in agent.stream(
{"messages": [{"role": "user", "content": "What's the weather in Boston?"}]},
stream_mode="values",
config={"callbacks": [callback]},
):
step["messages"][-1].pretty_print()
print(f"\nTotal usage: {callback.usage_metadata}")
================================[1m Human Message [0m=================================
What's the weather in Boston?
==================================[1m Ai Message [0m==================================
Tool Calls:
get_weather (call_izMdhUYpp9Vhx7DTNAiybzGa)
Call ID: call_izMdhUYpp9Vhx7DTNAiybzGa
Args:
location: Boston
=================================[1m Tool Message [0m=================================
Name: get_weather
It's sunny.
==================================[1m Ai Message [0m==================================
The weather in Boston is sunny.
Total usage: {'gpt-4o-mini-2024-07-18': {'input_token_details': {'audio': 0, 'cache_read': 0}, 'input_tokens': 125, 'total_tokens': 149, 'output_tokens': 24, 'output_token_details': {'audio': 0, 'reasoning': 0}}}
下一步
您现在已经看到了几个如何跟踪受支持提供商的 token 用量的示例。
接下来,查看本节中关于聊天模型的其他操作指南,例如如何让模型返回结构化输出或如何为聊天模型添加缓存。