构建基于 Spring AI 的文档查询系统

想象一下,能够像与一位知识渊博的助手聊天一样查询你的文档。无论是从冗长的 PDF 中提取关键见解,还是提出上下文相关的问题,**检索增强生成(Retrieval-Augmented Generation, RAG)**正在将这一愿景变为现实。随着 AI 的快速发展,构建文档查询系统不再是大型科技公司的专属领域。

在本文中,我将分享如何使用 Spring AIPostgreSQL with pgvector 和 Ollama 构建一个文档查询系统。这不仅仅是上传文档,而是创建一个能够从数据中理解和检索有意义见解的智能系统。


为什么要构建文档查询系统?

现代世界生成了海量的文本数据。想想法律合同、用户手册或研究论文。在这些文档中搜索特定信息就像大海捞针。这就是 RAG 系统的用武之地。通过将文档嵌入与基于向量的搜索相结合,这些系统可以:

  1. 将文档分词并嵌入到可搜索的向量空间中。

  2. 使用 AI 模型将你的查询与相关文本部分匹配。

  3. 返回精确且上下文丰富的响应。

对我来说,这不仅是一个技术练习,更是为了创建一些有用、切实且可扩展的东西。


项目背后的想法

下面从开源的 documentgpt-spring-ai 项目开始,该项目为支持 RAG 风格交互的 Spring Boot 应用程序奠定了基础。原始项目支持文档上传和向量搜索,但针对特定设置进行了定制。

我希望更进一步:

  • 添加对 多个 AI 提供商(如 OpenAI 和 Ollama)的支持。

  • 使系统灵活且易于配置,使用 Spring 配置文件。

  • 确保设置可以在本地运行,利用 Ollama 等工具进行私有 AI 推理。

这段旅程是关于学习、适应和构建有意义的东西。


技术栈

该项目利用了 AI 和软件工程中最令人兴奋的工具:

  1. Spring AI:简化与 AI 提供商和向量存储的集成。

  2. Ollama:一个以隐私为先的本地推理引擎,用于嵌入和查询。

  3. OpenAI:基于云的强大工具,提供高级 AI 功能。

  4. PostgreSQL with pgvector:允许存储文档嵌入,以便进行快速的基于向量的相似性搜索。


分解:系统如何工作

第一步:上传文档

当你上传文档时,系统会读取并处理文件:

  1. 从 PDF 中提取文本。

  2. 将文本分词为可管理的块。

  3. 使用所选 AI 提供商生成的嵌入将每个块转换为向量。

  4. 将向量与元数据一起存储在 PostgreSQL 的 documentgpt_vectors 表中。

这个嵌入步骤本质上将你的文档转换为 AI 可以“理解”的数学表示。

第二步:查询文档

当你提出问题时,系统会:

  1. 使用相同的 AI 提供商将你的查询转换为嵌入。

  2. 使用 pgvector 在 documentgpt_vectors 中搜索相似的嵌入。

  3. 检索最相关的文本块。

  4. 将检索到的信息与 AI 模型的生成能力结合,提供丰富、易读的响应。


为什么选择 Ollama?

该项目的一个突出特点是支持 Ollama。虽然 OpenAI 是一个强大的选择,但它需要互联网连接并依赖基于云的 API。而 Ollama 则:

  • 本地运行,确保数据隐私

  • 消除了网络调用的延迟。

  • 对于本地实验和部署来说,具有成本效益。

为了使系统更加灵活,我使用 Spring 配置文件在 OpenAI 和 Ollama 之间无缝切换。


我面临的挑战

没有一个好项目是没有挑战的。以下是我在过程中学到的东西:

1. 在一个应用程序中管理两个 LLM

在单个 Spring Boot 应用程序中配置 Ollama 和 OpenAI 是最具挑战性的方面之一。每个都需要不同的属性、Bean 和集成点。为了处理这个问题,我利用 Spring 配置文件来分离 Ollama 和 OpenAI 的配置。虽然这种方法效果很好,但调试特定配置文件的问题(如配置不匹配或 Bean 冲突)有时会很棘手。最终,Spring 配置文件的灵活性使得在不使代码库变得复杂的情况下管理两个 LLM 成为可能。

2. 使用 pgvector 作为向量数据库

将 pgvector 与 PostgreSQL 集成增加了另一层复杂性。虽然 pgvector 是存储和查询嵌入的强大工具,但它需要特定的模式定义和索引策略。确保数据库模式与不同模型生成的嵌入维度之间的兼容性尤其具有挑战性。例如,OpenAI 的 text-embedding-ada-002 生成 1536 维向量,而某些 Ollama 模型可能生成 768 维向量。仔细的配置和模式验证对于避免运行时错误至关重要。

3. 管理嵌入维度

每个嵌入模型生成特定大小的向量。将数据库模式与这些维度对齐需要清晰的理解和配置。例如,OpenAI 的 text-embedding-ada-002 生成 1536 维向量,但某些模型生成较小的维度(例如 768 维)。如果数据库模式与嵌入的维度不匹配,则会导致运行时错误,如在集成测试期间所见。管理跨 LLM 的维度兼容性是一个关键挑战。

4. 预加载 Ollama 模型

与基于云的 OpenAI 不同,Ollama 是一个本地推理引擎,需要在使用前预加载模型。最初,我尝试在 Docker 中自动化模型加载过程,但这种方法在容器启动时引入了意外的复杂性。相反,我记录了手动拉取模型的过程,使用户能够控制加载哪些模型以及何时加载。

5. 在灵活性与简单性之间取得平衡

支持多个 AI 提供商和嵌入配置引入了显著的复杂性。Spring 配置文件有助于保持设置的整洁,但代价是管理 Bean 的生命周期并确保提供商之间的平稳过渡。在灵活性(支持多个提供商)和简单性(易于配置)之间取得适当的平衡需要仔细的设计和测试。


如何构建你自己的系统

以下是复制该项目的高级指南:

1. 设置环境

首先克隆项目:

git clone https://github.com/halilural/springai-document-rag-app.git
cd springai-document-rag-app

设置 Docker:

  • 如果你要使用 OpenAI,请使用 compose.yaml docker-compose 文件。

docker-compose -f compose.yaml up -d
docker-compose -f compose-ollama.yaml up -d

2. 拉取 Ollama 模型(仅限 Ollama)

如果你使用 Ollama,拉取必要的模型:

docker exec -it ollama ollama pull mistral nomic-embed-text

3. 配置 Spring 配置文件

通过在 application.properties 文件中指定或使用 SPRING_PROFILES_ACTIVE 环境变量来激活配置文件。

激活 Ollama:

在 application.properties 中设置以下内容:

spring.profiles.active=ollama

或使用环境变量:

SPRING_PROFILES_ACTIVE=ollama

激活 OpenAI:

在 application.properties 中设置以下内容:

spring.profiles.active=openai

或使用环境变量:

SPRING_PROFILES_ACTIVE=openai

4. 启动应用程序

运行 Spring Boot 应用程序:

./mvnw spring-boot:run

5. 上传和查询

  • 通过 /upload 上传文档。

  • 通过 /query 提出问题。


潜在应用

该系统可以应用于多个领域:

  • 法律研究:从合同中提取条款或摘要。

  • 学术辅助:查询学术论文以获取关键发现或定义。

  • 客户支持:通过上传产品文档自动回答常见问题。


下一步是什么?

虽然系统功能齐全,但总有改进的空间:

1. 优化本地模型的性能

虽然使用 Mistral 和 Ollama 是一个令人兴奋的前景,但我未能达到预期的性能。这可能是由于硬件限制或需要进一步优化。

运行像 Mistral 这样复杂的模型需要大量的计算能力,升级硬件可能是下一步。或者,我可以探索更适合可用资源的其他本地模型,或尝试结合本地和基于云的推理的混合设置。

2. 探索模型微调

为了提高响应的相关性和准确性,对领域特定数据集进行嵌入模型的微调可能是一个有价值的实验。例如,使用法律文档、研究论文或客户支持的自定义嵌入可能会比依赖通用嵌入产生更好的结果。

3. 增强多文档查询

目前,系统一次处理和查询一个文档。扩展此功能以处理多文档查询将打开跨多个研究论文或从一组相关文档中提取见解的用例。

4. 实现身份验证和访问控制

为了使系统具备生产就绪性,添加安全身份验证机制(如 OAuth 或 API 密钥)至关重要。这将确保文档上传和查询仅限于授权用户。

5. 基准测试和部署

最后,我计划使用不同的工作负载和模型对系统进行基准测试,以更好地了解其可扩展性和性能。在具有强大监控的生产环境中部署应用程序将是其能力的最终测试。


结论

这个项目探索了当 AI 与实际需求相遇时可能实现的东西。通过结合嵌入、向量搜索和生成模型的力量,可以将静态文档转变为交互式、可查询的资源。

如果你希望尝试 RAG 系统,这个项目是一个极好的起点。深入研究代码,尝试一下,让你的文档活起来。

请登录后发表评论

    没有回复内容