在本博客中,您将了解如何使用 Weaviate、LangChain4j 和 LocalAI 实现检索增强生成 (RAG)。此实现允许您使用自然语言询问有关文档的问题。享受吧!
1。简介
在上一篇中post,Weaviate 被用作矢量数据库以执行语义搜索。使用的源文档是两个维基百科文档。 唱片目录和Bruce Springsteen 录制的歌曲列表是使用的文档。这些文档的有趣之处在于它们包含事实并且主要采用表格格式。这些文档的部分内容被转换为 Markdown,以便更好地表示。 Markdown 文件嵌入到 Weaviate 的集合中。结果是惊人的:所有提出的问题都得到了正确的答案。也就是说,返回了正确的段。您仍然需要自己提取答案,但这很容易。
但是,可以通过创建正确的提示将 Weaviate 搜索结果提供给 LLM(大型语言模型)来解决这个问题吗? LLM 能够提取问题的正确答案吗?
该设置如下图所示:
- 文档嵌入并存储在 Weaviate 中;
- 嵌入问题并使用 Weaviate 执行语义搜索;
- Weaviate 返回语义搜索结果;
- 结果将添加到提示中并馈送到 LocalAI,后者使用 LangChain4j 运行 LLM;
- 法学硕士返回问题的答案。
Weaviate 还支持 RAG,那么为什么还要使用 LocalAI和LangChain4j?遗憾的是,Weaviate 不支持与 LocalAI 集成,并且仅支持云 LLM可以使用。如果您的文档包含敏感信息或您不想发送到基于云的 LLM 的信息,您需要运行本地 LLM,这可以使用 LocalAI 和 LangChain4j 来完成。
如果您想运行本博客中的示例,则需要阅读上一篇博客。
本博客中使用的来源可以在 GitHub 上找到。 p>
2。先决条件
此博客的先决条件是:
- 嵌入和向量存储的基本知识;
- Java基础知识,使用Java 21;
- Docker 基础知识;
- LangChain4j基础知识;
- 您需要 Weaviate 并且需要嵌入文档,请参阅上一篇博客介绍了如何执行此操作;
- 如果您想运行示例,则需要 LocalAI,请参阅上一篇博客 了解如何利用 LocalAI。本博客使用的是 2.2.0 版本。
- 如果您想了解有关 RAG 的更多信息,请阅读此博客.
3。创建设置
开始之前,需要进行一些设置。
3.1 设置 LocalAI
LocalAI 必须正在运行并配置。博客中解释了如何执行此操作本地运行LLM:分步-步骤指南。
3.2 设置 Weaviate
Weaviate 必须启动。与 Weaviate 博客的唯一区别是您将在端口 8081 而不是端口 8080 上运行它。这是因为 LocalAI 已经在端口 8080 上运行。
从存储库的根目录启动撰写文件。
$ docker compose -f docker/compose-embed-8081.yaml
运行类 EmbedMarkdown 以嵌入文档(将端口更改为 8081!)。创建了三个集合:
- CompilationAlbum:布鲁斯·斯普林斯汀所有合辑专辑列表;
- 歌曲:布鲁斯·斯普林斯汀的所有歌曲列表;
- StudioAlbum:Bruce Springsteen 所有录音室专辑的列表。
4。实施RAG
4.1 语义搜索
实现的第一部分基于类 SearchCollectionNearText。这里假设您知道要在哪个集合(参数className
)中搜索。
在上一篇中发布后,您注意到严格来说,您不需要知道要搜索哪个集合。然而,此时,它使实现变得更容易一些,并且结果保持不变。
代码将接受问题,并在 NearTextArgument
的帮助下嵌入问题。 Weaviate 的 GraphQL API 用于执行搜索。
private static String createPrompt(String Question, String inputData, String extraInstruction) {
return "回答以下问题:" + 问题 + "\n" +
额外指令 + "\n" +
"使用以下数据回答问题:" + inputData;
}
4.3 使用法学硕士
最后要做的是将提示提供给 LLM 并将问题和答案打印到控制台。
private static void AskQuestion(String className, Field[] fields, String Question, String extraInstruction) {
...
ChatLanguageModel 模型 = LocalAiChatModel.builder()
.baseUrl("http://localhost:8080")
.modelName("lunademo")
.温度(0.0)
。建造();
String Answer = model.generate(createPrompt(question, result.getResult().getData().toString(), extraInstruction));
System.out.println(问题);
System.out.println(答案);
}
4.4 问题
要问的问题与之前的帖子相同。他们将调用上面的代码。
public static void main(String[] args) {
AskQuestion(Song.NAME, Song.getFields(), "最初发行的《亚当举起凯恩》是在哪张专辑上?", "");
AskQuestion(StudioAlbum.NAME, StudioAlbum.getFields(), "《来自新泽西州阿斯伯里帕克的问候》在美国的最高排行榜位置是多少?", "");
AskQuestion(CompilationAlbum.NAME, CompilationAlbum.getFields(), "专辑\"tracks\"在加拿大的最高排行榜位置是多少?", "");
AskQuestion(Song.NAME, Song.getFields(), "《公路巡警》是哪一年发行的?", "");
AskQuestion(Song.NAME, Song.getFields(), "谁创作了\"全有或全无?\"", "");
}
完整源码可查看此处。
5。结果
运行代码,结果如下:
- 《Adam Raising a Cain》最初发行于哪张专辑?
专辑《Darkness on the Edge of Town》最初发行于 1978 年,歌曲“ Adam Raising a Cain”收录在该专辑中。 - “来自新泽西州阿斯伯里帕克的问候”的最高排行榜位置是多少?在美国?
排行榜最高位置是“来自新泽西州阿斯伯里帕克的问候”在美国是 60。 - 专辑《Tracks》在加拿大的最高排行榜位置是多少?
根据提供的数据,专辑《Tracks》在加拿大的最高排行榜位置是 -。这是因为数据不包括该专辑的任何加拿大排行榜位置。 - 《Highway Patrolman》是哪一年发行的?
歌曲《Highway Patrolman》于 1982 年发行。 - 谁制作了“all or nothin’ at all?”
歌曲“All or Nothingth’ at All”由 Bruce Springsteen、Roy Bittan、Jon Landau 制作,和查克·普洛特金。
问题的所有答案都是正确的。最重要的工作已在 上一篇文章,以正确的方式嵌入文档,导致找到正确的段。当提供正确的数据时,法学硕士能够提取问题的答案。
6。注意事项
在实现过程中,我遇到了一些奇怪的行为,了解这些行为对于您何时开始实现用例非常重要。
6.1 Weaviate结果格式
Weaviate 响应包含一个 GraphQLResponse
对象,如下所示:
GraphQLResponse(
数据={
获取={
歌曲=[
{_additional={确定性=0.7534831166267395,距离=0.49303377},
OriginalRelease=城镇边缘的黑暗,
制片人=乔恩·兰道、布鲁斯·斯普林斯汀、史蒂文·范·赞特(助理)、
歌曲=“亚当养育了该隐”,作者=布鲁斯·斯普林斯汀,年份=1978}
]
}
},
错误=空)
在代码中,数据部分用于添加到提示中。
字符串答案 = model.generate(createPrompt(question, result.getResult().getData().toString(), extraInstruction));
上一篇>
当您将响应按原样添加到提示时会发生什么?
字符串答案 = model.generate(createPrompt(question, result.getResult().toString(), extraInstruction));
运行代码会返回问题 3 的以下错误答案以及问题 4 的一些不必要的附加信息。其他问题均已正确回答。
- 专辑《Tracks》在加拿大的最高排行榜位置是多少?
根据提供的数据,专辑《Tracks》在加拿大的最高排行榜位置是 50。 - 《高速公路巡警》是哪一年发布的?
根据提供的 GraphQLResponse,《高速公路巡警》于 1982 年发布。
谁制作了“所有或者什么都没有?”
6.2 提示格式
该代码包含向提示添加额外指令的功能。您可能已经注意到,没有使用此功能。让我们看看当您从提示中删除此内容时会发生什么。 createPrompt
方法如下(我没有删除所有内容,因此只需要进行较小的代码更改)。
private static String createPrompt(String Question, String inputData, String extraInstruction) {
return "回答以下问题:" + 问题 + "\n" +
"使用以下数据回答问题:" + inputData;
}
运行代码会为问题 3 的答案添加一些额外的信息,这并不完全正确。这张专辑在美国、英国、德国和瑞典的排行榜上的位置是正确的。说这张专辑进入英国和美国排行榜前十名是不正确的。所有其他问题均已正确回答。
- 专辑《Tracks》在加拿大的最高排行榜位置是多少?
根据提供的数据,专辑《Tracks》在加拿大的最高排行榜位置没有指定。该数据仅包括美国、英国、德国和瑞典等其他国家的图表位置。不过,这张专辑确实进入了英国和美国排行榜前十名。
使用法学硕士时它仍然有点脆弱。你不能总是相信它给出的答案。相应地改变提示似乎可以最大限度地减少法学硕士的幻觉。因此,收集用户的反馈非常重要,以便确定法学硕士何时出现幻觉。这样,您将能够改善对用户的响应。一个有趣的 博客是由 Fiddler 编写的,它解决了此类问题。
7。结论
在本博客中,您学习了如何使用 Weaviate、LangChain4j 和 LocalAI 实现 RAG。结果是相当惊人的。以正确的方式嵌入文档、过滤结果并将其提供给法学硕士是一个非常强大的组合,可用于许多用例。