本文基于这篇文章描述了 AIDocumentLibraryChat 项目,该项目具有基于 Open AI 的基于 RAG 的搜索服务嵌入/GPT 模型服务。

AIDocumentLibraryChat 项目已扩展为可以在 奥拉马。这样做的优点是文档永远不会离开本地服务器。这是一个解决方案,以防禁止将文档传输到外部服务。

架构

借助 Ollama,AI 模型可以在本地服务器上运行。这会将架构更改为如下所示:

Ollama 架构

该架构可以在本地组织控制的本地部署环境中部署所有需要的系统。例如,在本地 Kubernetes 集群中部署 AIDocumentLibraryChat 应用程序、PostgreSQL 数据库和基于 Ollama 的 AI 模型,并通过入口为用户提供对 AIDocumentLibraryChat 的访问。通过此架构,AIDocumentLibraryChat 应用程序仅提供结果并可供外部方访问。

系统架构具有用户 UI 和 AIDocumentLibraryChat 应用程序中的应用程序逻辑。该应用程序使用 Spring AI 和 ONNX 库函数来创建文档的嵌入。嵌入和文档通过 JDBC 存储在带有向量扩展的 PostgreSQL 数据库中。为了根据文档/段落内容创建答案,使用 REST 调用基于 Ollama 的模型。 AIDocumentLibraryChat应用程序、Postgresql数据库和基于Ollama的模型可以打包在Docker镜像中并部署在Kubernetes集群中。这使得系统独立于外部系统。 Ollama 模型支持服务器上所需的 GPU 加速。

使用 Ollama Docker 镜像的 shell 命令位于 runOllama.sh 文件。使用具有矢量扩展的 Postgresql DB Docker 映像的 shell 命令位于 runPostgresql.sh 文件。

构建 Ollama 应用程序

应用程序的 Gradle 版本已更新,可关闭 OpenAI 支持并使用 useOllama 属性打开 Ollama 支持:

科特林

 

插件 {
id 'java'
id 'org.springframework.boot' 版本 '3.2.1'
 id 'io.spring.dependency-management' 版本 '1.1.4'
}

组 = 'ch.xxx'
版本 = '0.0.1-SNAPSHOT'

爪哇{
 源兼容性 = '21'
}

存储库{
 mavenCentral()
 maven { url "https://repo.spring.io/snapshot" }
}

依赖项{
 实现 'org.springframework.boot:spring-boot-starter-actuator'
 实现 'org.springframework.boot:spring-boot-starter-data-jpa'
 实现 'org.springframework.boot:spring-boot-starter-security'
 实现 'org.springframework.boot:spring-boot-starter-web'
 实现 'org.springframework.ai:spring-ai-tika-document-reader:
0.8.0-快照'
 实施 'org.liquibase:liquibase-core'
 实现 'net.javacrumbs.shedlock:shedlock-spring:5.2.0'
 实现'net.javacrumbs.shedlock:
shedlock-provider-jdbc-template:5.2.0'
实施'org.springframework.ai:
spring-ai-pgvector-store-spring-boot-starter:0.8.0-SNAPSHOT'
 实施'org.springframework.ai:
spring-ai-transformers-spring-boot-starter:0.8.0-SNAPSHOT'
 testImplementation 'org.springframework.boot:spring-boot-starter-test'
 testImplementation 'org.springframework.security:spring-security-test'
 testImplementation 'com.tngtech.archunit:archunit-junit5:1.1.0'
 testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 
 if(project.hasProperty('useOllama')) {
 实施'org.springframework.ai:
spring-ai-ollama-spring-boot-starter:0.8.0-SNAPSHOT'
 } 别的 {
实施'org.springframework.ai:
spring-ai-openai-spring-boot-starter:0.8.0-SNAPSHOT'
 }
}

bootJar {
 archiveFileName = 'aidocumentlibrarychat.jar'
}

任务.named('测试') {
 使用JUnitPlatform()
}

Gradle 构建使用 'if(project.hasProperty('useOllama))' 语句添加 Ollama Spring Starter 和嵌入库,否则添加 OpenAI Spring Starter。

数据库设置

应用程序需要使用 Spring Profile 'ollama' 启动,以打开 Ollama 支持所需的功能。数据库设置需要不同的嵌入向量类型,该向量类型可通过 application-ollama.properties 文件:

属性文件

 

...
spring.liquibase.change-log=classpath:/dbchangelog/db.changelog-master-ollama.xml
...

spring.liquibase.change-log 属性设置包含 Ollama 初始化的 Liquibase 脚本。该脚本包括 db.changelog-1-ollama.xml 初始化脚本:

XML

 

this.splitStringToTokenLimit(
myDocument1.getContent(), embeddingTokenLimit).stream()
.map(myStr -> 新 TikaDocumentAndContent(myDocument1, myStr)))
.map(myTikaRecord -> 新的 org.springframework.ai.document.Document(
myTikaRecord.content(), myTikaRecord.document().getMetadata()))
.peek(myDocument1 -> myDocument1.getMetadata().put(ID,
myDocument.getId().toString()))
.peek(myDocument1 -> myDocument1.getMetadata()
.put(MetaData.DATATYPE, MetaData.DataType.DOCUMENT.toString()))
.toList();

}

公共 AiResult queryDocuments(SearchDto searchDto) {

消息 systemMessage = switch (searchDto.getSearchType()) {
案例 SearchDto.SearchType.DOCUMENT ->
this.getSystemMessage(documentChunks,
this.documentTokenLimit, searchDto.getSearchString());
案例 SearchDto.SearchType.PARAGRAPH ->
this.getSystemMessage(mostSimilar.stream().toList(),
this.documentTokenLimit, searchDto.getSearchString());

};

私有消息 getSystemMessage(
字符串 documentStr = this.cutStringToTokenLimit(
类似Documents.stream().map(entry->entry.getContent())
.filter(myStr -> myStr != null && !myStr.isBlank())
.collect(Collectors.joining(“\n”)), tokenLimit);
SystemPromptTemplate systemPromptTemplate = this.activeProfile
.contains(“ollama”) ?新的 SystemPromptTemplate(this.ollamaPrompt)
: new SystemPromptTemplate(this.systemPrompt);
消息 systemMessage = systemPromptTemplate.createMessage(
Map.of(“文档”, documentStr, “提示”, 提示));
返回系统消息;
}’ data-lang=”text/x-java”>

public Long storeDocument(文档文档) {
...
var aiDocuments = tikaDocuments.stream()
.flatMap(myDocument1 -> this.splitStringToTokenLimit(
myDocument1.getContent(), embeddingTokenLimit).stream()
.map(myStr -> 新 TikaDocumentAndContent(myDocument1, myStr)))
.map(myTikaRecord -> 新的 org.springframework.ai.document.Document(
myTikaRecord.content(), myTikaRecord.document().getMetadata()))
.peek(myDocument1 -> myDocument1.getMetadata().put(ID,
myDocument.getId().toString()))
.peek(myDocument1 -> myDocument1.getMetadata()
.put(MetaData.DATATYPE, MetaData.DataType.DOCUMENT.toString()))
.toList();
...
}

公共 AiResult queryDocuments(SearchDto searchDto) {
...
消息 systemMessage = switch (searchDto.getSearchType()) {
案例 SearchDto.SearchType.DOCUMENT ->
this.getSystemMessage(documentChunks,
this.documentTokenLimit, searchDto.getSearchString());
案例 SearchDto.SearchType.PARAGRAPH ->
this.getSystemMessage(mostSimilar.stream().toList(),
this.documentTokenLimit, searchDto.getSearchString());
...
};

私有消息 getSystemMessage(
字符串 documentStr = this.cutStringToTokenLimit(
类似Documents.stream().map(entry->entry.getContent())
.filter(myStr -> myStr != null && !myStr.isBlank())
.collect(Collectors.joining("\n")), tokenLimit);
SystemPromptTemplate systemPromptTemplate = this.activeProfile
.contains("ollama") ?新的 SystemPromptTemplate(this.ollamaPrompt)
: new SystemPromptTemplate(this.systemPrompt);
消息 systemMessage = systemPromptTemplate.createMessage(
Map.of("文档", documentStr, "提示", 提示));
返回系统消息;
}

storeDocument(...) 方法现在使用属性文件的 embeddingTokenLimit 来限制创建嵌入的文本块。 queryDocument(...) 方法现在使用属性文件的 documentTokenLimit 来限制提供给模型进行生成的文本块。

systemPromptTemplate 检查 ollama 配置文件的 activeProfile 属性,并创建包含问题的 SystemPromptTemplatecreateMessage(...) 方法创建 AI Message 并替换提示中的 documentsprompt 占位符字符串。

结论

Spring AI 与 Ollama 配合得很好。 Ollama Docker 容器中使用的模型是 stable-beluga:13b。实现中的唯一区别是更改了依赖项,并且缺少 Llama 模型的用户提示,但这是一个小修复。

Spring AI 为 OpenAI 等外部 AI 服务和基于 Ollama 的模型等本地 AI 服务提供了非常相似的实现。这很好地将 Java 代码与 AI 模型接口解耦。

Ollama 模型的性能需要将 document-token-limit 从 OpenAI 的 2000 减少到 Ollama 的 150(无需 GPU 加速)。 AI模型答案的质量也相应下降。要运行具有更好质量和可接受的答案响应时间的参数的 Ollama 模型,需要具有 GPU 加速功能的服务器。

对于商业/生产用途,需要具有适当许可证的模型。 beluga 模型的情况并非如此:可以使用 falcon:40b 模型。

Comments are closed.