简单地将整个文档分割成固定大小的块或分段有一些缺点:
- 它不允许包含上下文的所有语义,因为断点可能出现在任何位置
- 如果信息被放入更长的上下文中,你可能会丢失一些信息
实际上,对于非常结构化的主题来说,最好使用GraphRAG,尽管它更复杂,实施起来仍然是最好的选择。
所以我采用了这个技术,并在我的项目中使用了它,效果非常好。
这很可能已经被实现了(而且肯定比我做得更好)。
我知道是 TypeScript,而不是 Python!我习惯了用 TypeScript!
代码非常简单,可以直接看出来。
但是大部分工作是 __llm_
的细节描述。
如果你下载了它,就可以通过将你的 google gemini API_KEY
放入 .env
文件来运行它了。
这个演示处理罗马旅游指南,并将其存入 vector database 中:
npm run storeDB
然后你可以聊天来获取信息
例如:npm run chat
或者在选中的文件中设置断点,然后用 launch.json
调试。
如果你愿意冒险,你也可以将你的 API_KEY 放在 .env
文件里,直接在在线沙箱里运行。
想法是让一个大型语言模型将DOCUMENT分解成语义连贯的章节(CHAPTER)。
这些章节再被分成文本块(BLOCK),使用经典分割技术。
这些块会保留其所属章节的引用。
最后,这些块会被嵌入并存储在VECTOR DB(lancedb)中。
通过查询恢复
当我收到查询时,我会生成它的嵌入向量。
我使用向量数据库(VECTOR DB)获取一系列之前存储的与查询语义相似的文本块(BLOCKS)。
每个文本块都有一个所参考章节的引用,因此我恢复相关的章节。
这使你能够找回与查询语义上一致的一段文本,因此对RAG很有帮助。
创建知识库(KB即知识库)# 建库
好的,我有一个文档,想把它放到向量数据库里,应该怎么做?
你可以看这个
我基本上将 DOCUMENT 交给 LLM,他决定如何划分这些章节 这里。
这个操作是在整个文档的许多 token 上进行的,因此可能比较耗资源:必须一次完成,并使用一些技巧。
趣味事实:
如果你给一个大型语言模型(gemini-2.0-flash)一个很长的文档,并要求它返回章节,
这可能需要很长时间来完成,甚至会报错(它就给我报错了),因为它必须重新生成文档的所有内容。
目的让LLM只返回每个章节的起始位置。
这可以大大缩短响应的长度,从而加快响应速度。
趣味事实:
如果你让一个LLM给你一个具体的数值位置,
比如计算从文档开头到某个章节开始的字符数……它肯定算不准!
因为LLM使用的是TOKEN而不是直接计数字符,所以它无法准确计数。
窍门是拿到开头的前X个单词。
这还算不错。
趣味事实:
即使你威胁它,有时要求大语言模型或LLM按某种方式返回排序的列表,有时会按你的要求排序,有时则是“差不多”排序。
我得实现一个系统来确保拿到正确的章节这里
现在 LLM 已经创建了 CHAPTER 索引,我们已经从 DOCUMENT 中提取了它们。
接下来就简单多了:
- 我将章节拆分成文本块,同时保留章节的引用
- 我获取每个文本块的嵌入
- 我将这些嵌入存储在向量数据库中
```(https://codesandbox.io/p/devbox/embedding-d3x34d?file=%2Fsrc%2FstoreInDB.ts%3A44%2C2-60%2C4)
对话趣味事实:
如果你希望显著提高EMBEDDING的性能,尽量使用批处理API调用。
GEMINI每次API调用最多生成100个EMBEDDING(嵌入)。
这里
要开始聊天,你可以运行命令
npm run chat
或者启动文件 runChat.ts
它是一个带有查询向量数据库工具的ReAct代理 这里
基础类 "Agent" 的实现是值得单独讨论的
数据库的查询在这里。也就是说:
- 我通过QUERY的EMBEDDING向VECTOR DB查询语义相似度
- 如果需要,也可以直接检索章节,否则检索一系列的文本块
- 在这些文本块中,我检索章节,并赋予“最佳”文本块的距离给章节
- 然后我获得一个按照与QUERY的语义距离排序的章节列表
- 我用找到的章节来丰富提示(我用了前两章,但也可以用三章!)
我认为,至少有两个重要的优化点。
- 当 LLM 将文档拆分成章节时,也应该同时生成一个索引或摘要。
这将帮助代理理解整个文档的大致内容。
否则,当被问到“我在罗马可以做什么?”(在演示中我使用了罗马旅游指南的文本),它理论上需要下载所有相关的信息。 -
应该创建一个子代理专门处理向量数据库的查询,而不是由主代理来处理。
这将使请求得以处理而不影响主代理的工作窗口。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章