在您的 Obsidian 笔记中运用大型语言模型
2023 年 9 月 21 日
今天我在 Hacker News 上看到一篇关于 Obsidian 的另一个插件的文章,该插件集成了 ChatGPT。市面上有很多这样的工具,我很高兴看到在 Obsidian 中使用它们的不同方式。建立连接,让您的笔记更进一步。一些评论者认为它正在做您自己需要做的工作,但我认为它以新的和令人难以置信的方式赋予您力量。
与您的笔记对话
您可能想做的第一件也是最明显的事情是能够与您的笔记对话。向它提问以获得更多见解。如果您可以仅将模型指向您的笔记并完成操作,那将会很方便。但是大多数模型无法一次性接受所有这些内容。
当您提出问题时,并非所有笔记都相关。因此,您需要找到相关的部分并将其交给模型。Obsidian 具有搜索功能,但它只是搜索确切的单词和短语,而我们需要搜索概念。这就是嵌入的用武之地。我们必须创建一个索引。事实证明这很容易做到。
让我们构建索引器
当您创建 Obsidian 插件时,您可以让它在插件加载时执行某些操作,然后在您触发命令或打开笔记或在 Obsidian 中进行其他活动时执行其他操作。因此,我们希望在插件开始时有一些东西可以理解您的笔记,并且它应该保存其进度,因此不必再次重新生成索引。让我们看一个代码示例来为我们的一个笔记编制索引。我将在这个示例中使用 Llama Index,但 LangChain 是另一个不错的选择。
import { VectorStoreIndex, serviceContextFromDefaults, storageContextFromDefaults, MarkdownReader } from "llamaindex";
const service_context = serviceContextFromDefaults({ chunkSize: 256 })
const storage_context = await storageContextFromDefaults({ persistDir: "./storage" });
const mdpath = process.argv[2];
const mdreader = new MarkdownReader();
const thedoc = await mdreader.loadData(mdpath)
首先,我们需要初始化一个内存数据存储。这是 Llama Index 附带的内存存储,但 Chroma DB 是另一个流行的选择。第二行表示我们将持久化我们索引的所有内容。接下来,我获取文件的路径并初始化一个阅读器。然后我读取文件。Llama Index 知道 Markdown,因此它会适当地读取并索引它。它还了解 PDF、文本文件、Notion 文档等等。它不仅存储单词,还理解单词的含义以及它们与此文本中其他单词的关系。
await VectorStoreIndex.fromDocuments(thedoc, { storageContext: storage_context, serviceContext: service_context });
现在,这部分使用的是 OpenAI 的一项服务,但它与 ChatGPT 是分开的,不同的模型,不同的产品,并且 Langchain 中有一些替代方案可以在本地执行此操作,但速度有点慢。Ollama 也有嵌入功能。您还可以在云中超快的自托管实例上使用这些服务,然后在索引完成后关闭该实例。
现在让我们搜索我们的笔记
现在我们有了这个文件的索引。Obsidian 可以为我们提供所有文件的列表,因此我们可以一遍又一遍地运行它。而且我们正在持久化,所以这是一次性操作。现在,我们如何提出问题?我们需要一些代码来查找笔记中的相关位,将其交给模型,并使用该信息得出答案。
const storage_context = await storageContextFromDefaults({ persistDir: "./storage" });
const index = await VectorStoreIndex.init({ storageContext: storage_context });
const ret = index.asRetriever();
ret.similarityTopK = 5
const prompt = process.argv[2];
const response = await ret.retrieve(prompt);
const systemPrompt = `Use the following text to help come up with an answer to the prompt: ${response.map(r => r.node.toJSON().text).join(" - ")} `
因此,在这个代码示例中,我们正在使用我们已经处理过的内容初始化索引。Retriever.retrieve
行将获取提示并查找所有相关的笔记块并将文本返回给我们。我们说这里使用前 5 个匹配项。因此,我将从我们的笔记中获得 5 个文本块。有了这些原始信息,我们可以生成一个系统提示,以帮助我们的模型知道当我们提出问题时该做什么。
const ollama = new Ollama();
ollama.setModel("llama2");
ollama.setSystemPrompt(systemPrompt);
const genout = await ollama.generate(prompt);
现在我们可以使用模型了。我正在使用几天前创建的一个库,它在 npm 上。我可以将模型设置为使用 llama2,它已经使用命令 ollama pull llama2
下载到我的机器上。您可以尝试不同的模型来找到最适合您的模型。
为了快速获得答案,您需要坚持使用小型模型。但您也需要一个具有足够大的输入上下文大小的模型来接受我们所有的文本块。我最多有 5 个块,每个块 256 个标记。我将模型设置为使用包含我们文本块的系统提示。只需提出问题,它就会在几秒钟内为您提供答案。
太棒了。现在,我们的 obsidian 插件将适当地显示该答案。
我们还能做什么?
您还可以考虑总结文本或找到与您的文本匹配的最佳关键字并将它们添加到前言中,以便您可以在笔记之间建立更好的连接。我尝试过提出 10 个好问题和答案发送到 Anki。您需要尝试不同的模型和提示来完成这些不同的事情。更改提示甚至将模型权重更改为更适合任务的内容非常容易。
我希望这篇文章能为您提供一些关于如何为 Obsidian 或任何其他笔记工具构建下一个伟大插件的想法。使用最新的本地 AI 工具,例如您可以在 ollama.com 上找到的工具,这种能力轻而易举,我希望您能向我展示您的进展。