You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Gemini与LangChain4j的结合场景中,如何实现结构化输出的同时获取请求元数据(如Token使用量)

在Gemini与LangChain4j的结合场景中,如何实现结构化输出的同时获取请求元数据(如Token使用量)

嘿,我刚好碰到过类似的需求,其实LangChain4j和Gemini的组合里,要同时拿结构化输出和Token元数据很简单,只是需要调整一下调用方式——原来的AiServices代理帮你封装了太多细节,我们得稍微“钻”到底层一点,直接拿到包含元数据的AiResponse就行。下面给你一步步拆解实现方法:

核心思路

原来的AiServices生成的代理会直接返回你定义的Person对象,但请求的元数据(比如Token使用量)其实藏在ChatModel返回的AiResponse里。所以我们需要:

  1. 手动构建请求Prompt
  2. 调用ChatModel.generate()拿到完整的AiResponse
  3. AiResponse里分别提取Token元数据和解析结构化的Person对象

完整代码实现

直接上修改后的可运行代码,每一步都加了注释:

import dev.langchain4j.model.ai.AiResponse;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.gemini.GeminiChatModel;
import dev.langchain4j.model.google.gemini.GeminiResponseMetadata;
import dev.langchain4j.model.output.Prompt;
import dev.langchain4j.model.output.structured.StructuredOutputParser;

import java.time.LocalDate;
import java.util.Map;

// 保持你原来的Person record不变
public record Person(String nom, LocalDate dateOfBirth, LocalDate dateOfDeath, String mainFact) { }

public class HistorianExample {
    public static void main(String[] args) {
        // 1. 初始化Gemini ChatModel(和你原来的方式一致)
        ChatModel model = GeminiChatModel.builder()
                .apiKey("YOUR_GEMINI_API_KEY") // 替换成你的API Key
                .build();

        // 2. 构建Prompt,和你原来@UserMessage里的内容一致,同时明确要求返回JSON(保证结构化解析)
        String promptTemplate = """
                You are a historian who is collecting information about a person whose name is {{name}}.
                Please return the information in strict JSON format matching this structure:
                {"nom": "person name", "dateOfBirth": "yyyy-MM-dd", "dateOfDeath": "yyyy-MM-dd", "mainFact": "key fact"}
                """;
        Prompt prompt = Prompt.from(promptTemplate, Map.of("name", "Napoléon"));

        // 3. 调用模型拿到完整的AiResponse(这是核心!元数据就在这里面)
        AiResponse aiResponse = model.generate(prompt);

        // 4. 提取Token使用量元数据
        // GeminiResponseMetadata是Gemini专属的元数据类,包含所有Token统计
        GeminiResponseMetadata geminiMetadata = (GeminiResponseMetadata) aiResponse.metadata();
        System.out.println("📊 Token使用统计:");
        System.out.println("提问Token数:" + geminiMetadata.usage().promptTokenCount());
        System.out.println("回答Token数:" + geminiMetadata.usage().candidatesTokenCount());
        System.out.println("总Token数:" + geminiMetadata.usage().totalTokenCount());

        // 5. 解析结构化的Person对象,和你原来的效果完全一致
        StructuredOutputParser<Person> parser = StructuredOutputParser.create(Person.class);
        Person person = parser.parse(aiResponse.content().text());

        // 6. 输出结构化结果
        System.out.println("\n👤 人物信息:");
        System.out.println("姓名:" + person.nom());
        System.out.println("出生日期:" + person.dateOfBirth());
        System.out.println("主要事迹:" + person.mainFact());
    }
}

关键细节说明

  • 为什么不用原来的AiServices代理?
    原来的代理确实方便,但它把AiResponse的细节都封装了,只返回最终的结构化对象。我们要拿元数据,就必须直接和ChatModelgenerate()方法打交道,因为只有它会返回包含所有信息的AiResponse
  • GeminiResponseMetadata是什么?
    这是LangChain4j专门为Gemini模型提供的元数据类,里面的usage()方法返回的对象包含了所有Token相关的统计数据,完全覆盖你的需求。
  • 为什么要在Prompt里要求JSON格式?
    和你原来用@UserMessage+Person record的原理一样,必须让模型返回结构化的JSON,StructuredOutputParser才能正确解析成Java对象,这一步不能省哦。

总结

其实核心就是绕过AiServices的封装,直接拿AiResponse——这样既保留了结构化解析的能力,又能拿到所有你需要的请求元数据。这个方法既简单又灵活,你还可以从AiResponse里拿到其他元数据,比如模型的响应ID、生成时间之类的。

火山引擎 最新活动