评估测试

测试 AI 应用程序需要评估生成的内容,以确保 AI 模型没有产生幻觉响应。

一种评估响应的方法是使用 AI 模型本身进行评估。选择最适合评估的 AI 模型,这可能与用于生成响应的模型不同。

Spring AI 用于评估响应的接口是 Evaluator,定义如下:

@FunctionalInterface
public interface Evaluator {
    EvaluationResponse evaluate(EvaluationRequest evaluationRequest);
}

评估的输入是 EvaluationRequest,定义如下:

public class EvaluationRequest {

	private final String userText;

	private final List<Content> dataList;

	private final String responseContent;

	public EvaluationRequest(String userText, List<Content> dataList, String responseContent) {
		this.userText = userText;
		this.dataList = dataList;
		this.responseContent = responseContent;
	}

  ...
}
  • userText: 用户的原始输入,作为 String 类型

  • dataList: 上下文数据,例如来自检索增强生成(RAG)的数据,附加到原始输入

  • responseContent: AI 模型的响应内容,作为 String 类型

相关性评估器

RelevancyEvaluatorEvaluator 接口的一个实现,旨在评估 AI 生成的响应与提供的上下文的相关性。这个评估器通过确定 AI 模型的响应是否与用户的输入和检索到的上下文相关,来帮助评估 RAG 流程的质量。

评估基于用户输入、AI 模型的响应和上下文信息。它使用提示模板来询问 AI 模型响应是否与用户输入和上下文相关。

这是 RelevancyEvaluator 使用的默认提示模板:

你的任务是评估查询的响应
是否符合提供的上下文信息。

你有两个选项来回答。要么是 YES 要么是 NO。

如果查询的响应
符合上下文信息则回答 YES,否则回答 NO。

查询:
{query}

响应:
{response}

上下文:
{context}

回答:

注意:你可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义提示模板。有关详细信息,请参见 自定义模板

在集成测试中的使用

以下是在集成测试中使用 RelevancyEvaluator 的示例,使用 RetrievalAugmentationAdvisor 验证 RAG 流程的结果:

@Test
void evaluateRelevancy() {
    String question = "Anacletus 和 Birba 的冒险发生在哪里?";

    RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
            .vectorStore(pgVectorStore)
            .build())
        .build();

    ChatResponse chatResponse = ChatClient.builder(chatModel).build()
        .prompt(question)
        .advisors(ragAdvisor)
        .call()
        .chatResponse();

    EvaluationRequest evaluationRequest = new EvaluationRequest(
        // 原始用户问题
        question,
        // 从 RAG 流程中检索到的上下文
        chatResponse.getMetadata().get(RetrievalAugmentationAdvisor.DOCUMENT_CONTEXT),
        // AI 模型的响应
        chatResponse.getResult().getOutput().getText()
    );

    RelevancyEvaluator evaluator = new RelevancyEvaluator(ChatClient.builder(chatModel));

    EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);

    assertThat(evaluationResponse.isPass()).isTrue();
}

你可以在 Spring AI 项目中找到几个使用 RelevancyEvaluator 来测试 QuestionAnswerAdvisor 功能的集成测试(参见 测试)和 RetrievalAugmentationAdvisor(参见 测试)。

自定义模板

RelevancyEvaluator 使用默认模板来提示 AI 模型进行评估。你可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为。

自定义 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。重要要求是模板必须包含以下占位符:

  • query 占位符用于接收用户问题

  • response 占位符用于接收 AI 模型的响应

  • context 占位符用于接收上下文信息

事实检查评估器

FactCheckingEvaluator 是 Evaluator 接口的另一个实现,旨在评估 AI 生成的响应与提供的上下文的事实准确性。这个评估器通过验证给定陈述(声明)是否在逻辑上得到提供的上下文(文档)的支持,来帮助检测和减少 AI 输出中的幻觉。

'声明’和’文档’被呈现给 AI 模型进行评估。有一些专门用于此目的的较小且更高效的 AI 模型可用,例如 Bespoke 的 Minicheck,这有助于降低执行这些检查的成本,相比旗舰模型如 GPT-4。Minicheck 也可以通过 Ollama 使用。

使用

FactCheckingEvaluator 构造函数接受一个 ChatClient.Builder 作为参数:

public FactCheckingEvaluator(ChatClient.Builder chatClientBuilder) {
  this.chatClientBuilder = chatClientBuilder;
}

评估器使用以下提示模板进行事实检查:

文档:{document}
声明:{claim}

其中 {document} 是上下文信息,{claim} 是要评估的 AI 模型响应。

示例

以下是如何使用基于 Ollama 的 ChatModel(特别是 Bespoke-Minicheck 模型)的 FactCheckingEvaluator 的示例:

@Test
void testFactChecking() {
  // 设置 Ollama API
  OllamaApi ollamaApi = new OllamaApi("http://localhost:11434");

  ChatModel chatModel = new OllamaChatModel(ollamaApi,
				OllamaOptions.builder().model(BESPOKE_MINICHECK).numPredict(2).temperature(0.0d).build())


  // 创建 FactCheckingEvaluator
  var factCheckingEvaluator = new FactCheckingEvaluator(ChatClient.builder(chatModel));

  // 示例上下文和声明
  String context = "地球是太阳系中第三颗行星,也是已知唯一存在生命的行星。";
  String claim = "地球是太阳系中第四颗行星。";

  // 创建 EvaluationRequest
  EvaluationRequest evaluationRequest = new EvaluationRequest(context, Collections.emptyList(), claim);

  // 执行评估
  EvaluationResponse evaluationResponse = factCheckingEvaluator.evaluate(evaluationRequest);

  assertFalse(evaluationResponse.isPass(), "声明不应该被上下文支持");

}