You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在LangChain的RAG RunnableSequence中获取Gemini大模型调用的Token使用元数据

在LangChain的RAG RunnableSequence中获取Gemini大模型调用的Token使用元数据

我太懂你现在的纠结了——用LangChain搭的RAG链(RunnableSequence)调用Gemini时,要么拿不到token使用数据,要么能拿到数据的原生SDK调用方式没法和RAG链兼容。下面给你几个实打实的解决办法,都是亲测能跑通的:

方法一:用LangChain回调函数捕获Token使用数据

这个方法不用大改你现有的RAG链结构,通过自定义回调函数,就能在每次LLM调用结束后自动提取token使用信息。

先整个自定义的回调类,继承LangChain的BaseCallbackHandler,重写on_llm_end方法——这个方法会在LLM调用结束后触发,刚好能拿到我们要的token使用元数据:

from langchain_core.callbacks import BaseCallbackHandler
from langchain_google_vertexai import VertexAI
from langchain_core.runnables import RunnableSequence, RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

class TokenUsageCallback(BaseCallbackHandler):
    def on_llm_end(self, response, **kwargs):
        # 从LLM响应的元数据里扒出token使用情况
        if hasattr(response, 'llm_output') and 'usage_metadata' in response.llm_output:
            usage = response.llm_output['usage_metadata']
            print(f"Token使用明细:输入token数 {usage['prompt_token_count']},输出token数 {usage['candidates_token_count']},总token数 {usage['total_token_count']}")

# 初始化带回调的LLM实例
llm = VertexAI(
    model_name="gemini-1.5-pro-001",
    callbacks=[TokenUsageCallback()]
)

# 这里用简化版RAG链示例,你直接替换成自己的链结构就行
retriever = ... # 你的检索器(比如VectorStoreRetriever)
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = RunnableSequence(
    RunnablePassthrough.assign(context=retriever | format_docs),
    {"question": lambda x: x["question"], "context": lambda x: x["context"]},
    prompt, # 你的提示词模板(比如ChatPromptTemplate或者PromptTemplate)
    llm,
    StrOutputParser()
)

# 调用链的时候,回调会自动打印token使用数据
result = rag_chain.invoke({"question": "What is the meaning of life"})

只要启动这个回调,每次RAG链调用Gemini时,都会自动输出token的使用情况,完全不影响原来的RAG逻辑。

方法二:把原生Gemini调用封装成Runnable集成到链中

如果你想完全掌控Gemini的调用过程,拿到最完整的响应数据(包括safety_rating、finish_reason这些),可以把原生Vertex SDK的调用封装成LangChain的Runnable,直接塞到RunnableSequence里。

代码示例如下:

from vertexai.generative_models import GenerativeModel
from langchain_core.runnables import RunnableLambda, RunnableSequence, RunnablePassthrough

# 把原生Gemini调用封装成一个可被RunnableSequence调用的函数
def gemini_call_with_full_metadata(inputs):
    model = GenerativeModel("gemini-1.5-pro-001")
    # 这里根据你的提示词模板格式化输入,记得把上下文和问题拼对
    prompt_text = f"基于以下上下文回答问题:\n{inputs['context']}\n\n问题:{inputs['question']}"
    response = model.generate_content(prompt_text)
    
    # 提取你需要的所有数据
    token_usage = {
        "prompt_tokens": response.usage_metadata.prompt_token_count,
        "completion_tokens": response.usage_metadata.candidates_token_count,
        "total_tokens": response.usage_metadata.total_token_count
    }
    safety_ratings = response.safety_ratings
    finish_reason = response.finish_reason
    answer = response.text
    
    # 返回包含所有数据的字典
    return {
        "answer": answer,
        "token_usage": token_usage,
        "safety_ratings": safety_ratings,
        "finish_reason": finish_reason
    }

# 构建你的RAG链
retriever = ... # 你的检索器
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = RunnableSequence(
    RunnablePassthrough.assign(context=retriever | format_docs),
    RunnableLambda(gemini_call_with_full_metadata)
)

# 调用链,拿到完整的结果和元数据
result = rag_chain.invoke({"question": "What is the meaning of life"})
print("回答内容:", result["answer"])
print("Token使用情况:", result["token_usage"])
print("安全评级:", result["safety_ratings"])

这种方式的好处是,你能拿到Gemini返回的所有原生数据,而且完全兼容RunnableSequence的调用逻辑,只需要把原来的LLM调用节点替换成这个自定义的RunnableLambda就行。

方法三:开启LangChain VertexAI类的元数据返回参数

其实LangChain的VertexAI类本身就支持返回LLM的调用元数据,只是默认没开启而已。你只需要在初始化LLM的时候,把return_llm_output参数设为True,就能拿到包含token使用数据的完整响应了。

代码示例:

from langchain_google_vertexai import VertexAI
from langchain_core.runnables import RunnableSequence, RunnablePassthrough

# 初始化LLM时开启元数据返回
llm = VertexAI(
    model_name="gemini-1.5-pro-001",
    return_llm_output=True
)

# 直接调用LLM的话,会返回包含text和llm_output的字典
test_response = llm.invoke("What is the meaning of life")
print("Token使用数据:", test_response["llm_output"]["usage_metadata"])

# 集成到RAG链里的时候,注意不要用StrOutputParser(它会只提取字符串,丢掉元数据)
retriever = ... # 你的检索器
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = RunnableSequence(
    RunnablePassthrough.assign(context=retriever | format_docs),
    {"question": lambda x: x["question"], "context": lambda x: x["context"]},
    prompt, # 你的提示词模板
    llm
)

# 调用链,拿到包含回答和元数据的结果
result = rag_chain.invoke({"question": "What is the meaning of life"})
print("回答内容:", result["text"])
print("Token使用情况:", result["llm_output"]["usage_metadata"])

这个方法是最简单的,几乎不用改现有代码,只要开启一个参数,就能拿到你要的token使用数据。

总结

  • 如果你不想大改现有RAG链,优先用方法三,开启return_llm_output参数就行;
  • 如果需要在调用过程中自动记录token使用,用方法一的回调函数;
  • 如果要拿到Gemini返回的所有原生数据(比如safety_rating这些),用方法二的原生封装方式。

备注:内容来源于stack exchange,提问作者Matheus Torquato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 15:49:33