使用 Spring AI 評估器測試 LLM 回應
1. 概述
現代網路應用程式越來越多地與大型語言模型 (LLM) 相結合,以建立聊天機器人和虛擬助理等解決方案。
然而,雖然法學碩士 (LLM) 很強大,但它們容易產生幻覺,並且它們的回答可能並不總是相關、適當或事實準確的。
評估 LLM 答案的一個解決方案是使用 LLM 本身,最好是單獨的。
為了實現這一點,Spring AI 定義了Evaluator
介面並提供了兩個實作來檢查 LLM 回應的相關性和事實準確性,即RelevanceEvaluator
和FactCheckingEvaluator
。
在本教程中,我們將探討如何使用 Spring AI Evaluators 來測試 LLM 回應。我們將使用 Spring AI 提供的兩個基本實作來評估檢索增強生成 (RAG) 聊天機器人的回應。
2. 建構 RAG 聊天機器人
在我們開始測試 LLM 回應之前,我們需要一個聊天機器人來測試。為了演示,我們將建立一個簡單的 RAG 聊天機器人,根據一組文件回答使用者的問題。
我們將使用開源工具Ollama在本地提取和運行我們的聊天完成和嵌入模型。
2.1.依賴項
讓我們先在專案的pom.xml
檔案中加入必要的依賴項:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>1.0.0-M5</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
<version>1.0.0-M5</version>
</dependency>
Ollama 啟動器依賴項可協助我們與 Ollama 服務建立連線。
此外,我們匯入了 Spring AI 的markdown 文件閱讀器依賴項,我們將使用它將.md
檔案轉換為可以儲存在向量儲存中的文件。
由於目前版本1.0.0-M5
是一個里程碑版本,我們還需要將 Spring Milestones 儲存庫新增到我們的pom.xml
中:
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
此儲存庫是發布里程碑版本的地方,與標準 Maven Central 儲存庫不同。
鑑於我們在專案中使用了多個 Spring AI 啟動器,我們還將在pom.xml
中包含Spring AI 物料清單 (BOM) :
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
透過這個添加,我們現在可以從兩個啟動依賴項中刪除version
標籤。
BOM 消除了版本衝突的風險,並確保我們的 Spring AI 依賴關係彼此相容。
2.2.配置聊天完成和嵌入模型
接下來,讓我們在application.yaml
檔案中設定我們的聊天完成和嵌入模型:
spring:
ai:
ollama:
chat:
options:
model: llama3.3
embedding:
options:
model: nomic-embed-text
init:
pull-model-strategy: when_missing
在這裡,我們指定 Meta 提供的[llama3.3](https://ollama.com/library/llama3.3)
模型作為我們的聊天完成模型,並指定 Nomic AI 提供的nomic-embed-text
模型作為我們的嵌入模型。請隨意使用不同的模型來嘗試此實作。
此外,我們將pull-model-strategy
設定為when_missing
。這可確保 Spring AI 在本地不可用時提取指定的模型。
在配置有效模型時, Spring AI 會自動建立ChatModel
和EmbeddingModel
類型的 bean ,分別讓我們可以與聊天完成和嵌入模型互動。
讓我們用它們來定義我們的聊天機器人所需的附加 bean:
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
return SimpleVectorStore
.builder(embeddingModel)
.build();
}
@Bean
public ChatClient contentGenerator(ChatModel chatModel, VectorStore vectorStore) {
return ChatClient.builder(chatModel)
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
.build();
}
首先,我們定義一個VectorStore
bean 並使用SimpleVectorStore
實現,這是一個使用java.util.Map
類別模擬向量儲存的記憶體實作。
在生產應用中,我們可以考慮使用真正的向量存儲,例如 ChromaDB。
接下來,使用ChatModel
和VectorStore
bean,我們建立一個ChatClient
類型的 bean,它是我們與聊天完成模型互動的主要入口點。
我們用QuestionAnswerAdvisor
對其進行配置,它使用向量儲存根據使用者的問題檢索儲存文件的相關部分,並將它們作為上下文提供給聊天模型。
2.3.填充記憶體向量存儲
為了演示,我們在src/main/resources/documents
目錄中包含一個leave-policy.md
文件,其中包含有關休假政策的範例資訊。
現在,為了在應用程式啟動期間用我們的文件填充向量存儲,我們將創建一個實現ApplicationRunner
介面的VectorStoreInitializer
類別:
`@Component
class VectorStoreInitializer implements ApplicationRunner {
private final VectorStore vectorStore;
private final ResourcePatternResolver resourcePatternResolver;
// standard constructor
@Override
public void run(ApplicationArguments args) {
List
Resource[] resources = resourcePatternResolver.getResources("classpath:documents/*.md");
Arrays.stream(resources).forEach(resource -> {
MarkdownDocumentReader markdownDocumentReader = new MarkdownDocumentReader(resource, MarkdownDocumentReaderConfig.defaultConfig());
documents.addAll(markdownDocumentReader.read());
});
vectorStore.add(new TokenTextSplitter().split(documents));
}
}`
在run()
方法內部,我們首先使用注入的ResourcePatternResolver
類別從src/main/resources/documents
目錄中取得所有 markdown 檔案。雖然我們只處理單一 markdown 文件,但我們的方法是可擴展的。
然後,我們使用MarkdownDocumentReader
類別將取得的resources
轉換為Document
物件。
最後,我們使用TokenTextSplitter
類別將documents
分成更小的區塊,然後將其加入向量儲存。
當我們呼叫add()
方法時, Spring AI 會自動將我們的純文字內容轉換為向量表示形式,然後將其儲存在向量儲存中。我們不需要使用EmbeddingModel
bean 明確地轉換它。
3. 使用 Testcontainers 設定 Ollama
為了方便本地開發和測試,我們將使用 Testcontainers 來設定 Ollama 服務,其先決條件是有一個活動的 Docker 執行個體。
3.1.測試依賴項
首先,讓我們在pom.xml
中加入必要的測試依賴項:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ollama</artifactId>
<scope>test</scope>
</dependency>
我們導入 Spring Boot 的Spring AI Testcontainers依賴項和 Testcontainers 的Ollama 模組。
這些相依性提供了為 Ollama 服務啟動臨時 Docker 執行個體所需的類別。
3.2.定義 Testcontainers Bean
接下來,讓我們建立一個@TestConfiguration
類別來定義我們的 Testcontainers bean:
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
public OllamaContainer ollamaContainer() {
return new OllamaContainer("ollama/ollama:0.5.7");
}
@Bean
public DynamicPropertyRegistrar dynamicPropertyRegistrar(OllamaContainer ollamaContainer) {
return registry -> {
registry.add("spring.ai.ollama.base-url", ollamaContainer::getEndpoint);
};
}
}
我們在建立OllamaContainer
bean 時指定了 Ollama 映像的最新穩定版本。
然後,我們定義一個DynamicPropertyRegistrar
bean 來設定 Ollama 服務的base-url
。這使得我們的應用程式可以連接到已啟動的容器。
現在,我們可以透過使用@Import(TestcontainersConfiguration.class)
註解來註解我們的測試類,在我們的整合測試中使用此配置。
4.使用 Spring AI 評估器
現在我們已經建立了 RAG 聊天機器人並設定了本地測試環境,讓我們看看如何使用 Spring AI 的Evaluator
介面的兩個可用實作來測試它產生的回應。
4.1.配置評估模型
我們的測試品質最終取決於我們使用的評估模型的品質。我們將選擇目前的行業標準,即bespoke-minicheck
模型,這是由 Bespoke Labs 專門為評估測試而訓練的開源模型。它在LLM-AggreFact 排行榜上名列前茅,並且只產生是/否的答案。
讓我們在application.yaml
檔案中進行設定:
com:
baeldung:
evaluation:
model: bespoke-minicheck
接下來,我們將建立一個單獨的ChatClient
bean 來與我們的評估模型互動:
@Bean
public ChatClient contentEvaluator(
OllamaApi olamaApi,
@Value("${com.baeldung.evaluation.model}") String evaluationModel
) {
ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(olamaApi)
.defaultOptions(OllamaOptions.builder()
.model(evaluationModel)
.build())
.modelManagementOptions(ModelManagementOptions.builder()
.pullModelStrategy(PullModelStrategy.WHEN_MISSING)
.build())
.build();
return ChatClient.builder(chatModel)
.build();
}
在這裡,我們使用 Spring AI 為我們創建的OllamaApi
bean 和我們的自訂評估模型屬性定義一個新的ChatClient
bean,我們使用@Value
註解注入該 bean。
值得注意的是,我們對評估模型使用了自訂屬性並手動建立其對應的ChatModel
類,因為**OllamaAutoConfiguration
類別只允許我們透過spring.ai.ollama.chat.options.model
屬性配置單一模型**,我們已經將其用於內容生成模型。
4.2.使用RelevancyEvaluator
評估 LLM 回應的相關性
Spring AI 提供了RelevancyEvaluator
實作來檢查 LLM 回應是否與使用者的查詢以及從向量儲存中檢索到的上下文相關。
首先,讓我們為其建立一個 bean:
@Bean
public RelevancyEvaluator relevancyEvaluator(
@Qualifier("contentEvaluator") ChatClient chatClient) {
return new RelevancyEvaluator(chatClient.mutate());
}
我們使用@Qualifier
註解來注入我們先前定義的relevancyEvaluator
ChatClient
bean 並建立RelevancyEvaluator
類別的實例。
現在,讓我們測試聊天機器人的回應是否相關:
String question = "How many days sick leave can I take?";
ChatResponse chatResponse = contentGenerator.prompt()
.user(question)
.call()
.chatResponse();
String answer = chatResponse.getResult().getOutput().getContent();
List<Document> documents = chatResponse.getMetadata().get(QuestionAnswerAdvisor.RETRIEVED_DOCUMENTS);
EvaluationRequest evaluationRequest = new EvaluationRequest(question, documents, answer);
EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest);
assertThat(evaluationResponse.isPass()).isTrue();
String nonRelevantAnswer = "A lion is the king of the jungle";
evaluationRequest = new EvaluationRequest(nonRelevantAnswer, documents, answer);
evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest);
assertThat(evaluationResponse.isPass()).isFalse();
我們首先透過question
呼叫contentGenerator
ChatClient
,然後從傳回的ChatResponse
中提取產生的answer
和用於產生該答案的documents
。
然後,我們建立一個EvaluationRequest
其中包含question
、檢索到的documents
和聊天機器人的answer
。我們將其傳遞給relevancyEvaluator
bean,並使用isPass()
方法斷言answer
是相關的。
然而,當我們傳遞一個關於獅子的完全不相關的答案時,評估人員正確地將其識別為不相關的。
4.3.使用FactCheckingEvaluator
評估法學碩士 (LLM) 答案的事實準確性
類似地, Spring AI 提供了一個FactCheckingEvaluator
實現,以根據檢索到的上下文驗證 LLM 回應的事實準確性。
讓我們使用contentEvaluator
ChatClient
建立一個FactCheckingEvaluator
bean:
@Bean
public FactCheckingEvaluator factCheckingEvaluator(
@Qualifier("contentEvaluator") ChatClient chatClient) {
return new FactCheckingEvaluator(chatClient.mutate());
}
最後,讓我們測試一下聊天機器人回應的事實準確性:
String question = "How many days sick leave can I take?";
ChatResponse chatResponse = contentGenerator.prompt()
.user(question)
.call()
.chatResponse();
String answer = chatResponse.getResult().getOutput().getContent();
List<Document> documents = chatResponse.getMetadata().get(QuestionAnswerAdvisor.RETRIEVED_DOCUMENTS);
EvaluationRequest evaluationRequest = new EvaluationRequest(question, documents, answer);
EvaluationResponse evaluationResponse = factCheckingEvaluator.evaluate(evaluationRequest);
assertThat(evaluationResponse.isPass()).isTrue();
String wrongAnswer = "You can take no leaves. Get back to work!";
evaluationRequest = new EvaluationRequest(wrongAnswer, documents, answer);
evaluationResponse = factCheckingEvaluator.evaluate(evaluationRequest);
assertThat(evaluationResponse.isPass()).isFalse();
與先前的方法類似,我們使用question
、檢索到的documents
和聊天機器人的answer
來建立一個EvaluationRequest
,並將其傳遞給我們的factCheckingEvaluator
bean。
我們斷言,根據檢索到的上下文,聊天機器人的response
在事實上是準確的。此外,我們以硬編碼的事實錯誤答案重新測試評估,並斷言isPass()
方法傳回false
。
值得注意的是,如果我們將硬編碼的wrongAnswer
傳遞給RelevancyEvaluator
,那麼評估就會通過,因為即使回應在事實上不正確,它仍然與用戶詢問的病假主題相關。
5. 結論
在本文中,我們探討了使用 Spring AI 的 Evaluator 介面測試 LLM 回應。
我們建立了一個簡單的 RAG 聊天機器人,它根據一組文件回答使用者的問題,並使用 Testcontainers 設定 Ollama 服務,創建本地測試環境。
然後,我們使用 Spring AI 提供的RelevancyEvaluator
和FactCheckingEvaluator
實作來評估我們的聊天機器人回應的相關性和事實準確性。
與往常一樣,本文使用的所有程式碼範例均可在 GitHub 上找到。