Skip to main content
Open In Colab在 GitHub 上打开

如何在 ChatModels 中跟踪令牌使用情况

先决条件

本指南假定您熟悉以下概念:

跟踪令牌使用情况以计算成本是将应用程序投入生产的重要部分。本指南介绍如何从 LangChain 模型调用中获取此信息。

本指南要求langchain-anthropiclangchain-openai >= 0.3.11.

%pip install -qU langchain-anthropic langchain-openai
关于使用 OpenAI 进行流式传输的说明

默认情况下,OpenAI 的 Chat Completions API 不会流式传输令牌使用统计数据(请参阅此处的 API 参考)。 在使用ChatOpenAIAzureChatOpenAI设置stream_usage=True如 在本指南中演示。

使用 LangSmith

您可以使用 LangSmith 来帮助跟踪 LLM 应用程序中的令牌使用情况。请参阅 LangSmith 快速入门指南

使用 AIMessage.usage_metadata

许多模型提供商在聊天生成响应中返回令牌使用信息。如果可用,此信息将包含在AIMessage由相应模型生成的对象。

LangChain 语言链AIMessage对象包括 usage_metadata 属性。填充后,此属性将是具有标准键的 UsageMetadata 字典(例如"input_tokens""output_tokens").它们还将包括有关缓存令牌使用情况和来自多模式数据的令牌的信息。

例子:

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
API 参考:init_chat_model
{'input_tokens': 8, 'output_tokens': 9, 'total_tokens': 17}

人类学

from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(model="claude-3-haiku-20240307")
anthropic_response = llm.invoke("hello")
anthropic_response.usage_metadata
API 参考:ChatAnthropic
{'input_tokens': 8, 'output_tokens': 12, 'total_tokens': 20}

某些提供程序在流式处理上下文中支持令牌计数元数据。

OpenAI

例如,OpenAI 将在流的末尾返回一个包含令牌使用信息的消息。此行为受langchain-openai >= 0.1.9,并且可以通过设置stream_usage=True.此属性也可以在ChatOpenAI实例化。

注意

默认情况中的最后一个消息块将包含一个"finish_reason"在消息的response_metadata属性。如果我们在流式处理模式中包含令牌使用情况,则包含使用情况元数据的额外数据块将添加到流式处理的末尾,以便"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 的流式处理令牌计数,请将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实例化 chat 模型时。这在将聊天模型整合到 LangChain Chains中时非常有用:在流式传输中间步骤或使用 LangSmith 等跟踪软件时,可以监控使用元数据。

请参阅以下示例,其中我们将 output 结构化返回为所需架构,但仍可以观察从中间步骤流式传输的令牌使用情况。

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.'

令牌使用情况也显示在聊天模型有效负载的相应 LangSmith 跟踪中。

使用回调

需要langchain-core>=0.3.49

LangChain 实现了一个回调处理程序和上下文管理器,它将跟踪任何返回usage_metadata.

还有一些特定于 API 的回调上下文管理器,它们维护不同模型的定价,允许实时估算成本。它们目前仅针对 OpenAI API 和 Bedrock Anthropic API 实现,并在langchain-community:

下面,我们演示了通用用法元数据回调管理器。我们可以通过配置或作为上下文管理器来跟踪令牌的使用情况。

通过配置跟踪令牌使用情况

要通过配置跟踪令牌使用情况,请实例化UsageMetadataCallbackHandler并将其传递到 config 中:

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}}}

使用上下文管理器跟踪令牌使用情况

您还可以使用get_usage_metadata_callback要创建 Context Manager 并在其中聚合使用情况元数据,请执行以下作:

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}}}

这些方法中的任何一种都将聚合对每个模型的多次调用的令牌使用情况。例如,您可以在代理中使用它来跟踪对一个模型的重复调用中的令牌使用情况:

%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}")
API 参考:create_react_agent
================================ Human Message =================================

What's the weather in Boston?
================================== Ai Message ==================================
Tool Calls:
get_weather (call_izMdhUYpp9Vhx7DTNAiybzGa)
Call ID: call_izMdhUYpp9Vhx7DTNAiybzGa
Args:
location: Boston
================================= Tool Message =================================
Name: get_weather

It's sunny.
================================== Ai Message ==================================

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}}}

后续步骤

您现在已经看到了一些示例,说明如何跟踪受支持提供商的令牌使用情况。

接下来,查看本节中的其他操作指南聊天模型,例如如何让模型返回结构化输出如何向聊天模型添加缓存