在 Obsidian 筆記中運用 LLMs

2023 年 9 月 21 日

今天我在駭客新聞上看到一篇關於另一個 Obsidian 外掛程式的文章,這個外掛程式整合了 ChatGPT。市面上已經有很多類似的工具,我很高興看到各種將它們與 Obsidian 結合使用的方式。建立連結,讓你的筆記更上一層樓。有些評論者認為這是在做你應該自己做的工作,但我認為它以嶄新且令人難以置信的方式賦予你力量。

與你的筆記對話

你可能想做的第一件也是最明顯的事情,就是能夠與你的筆記對話。向它提問以獲得更深入的見解。如果可以直接將模型指向你的筆記就能完成,那就太方便了。但大多數模型無法一次接受所有內容。

當你提出問題時,並非所有筆記都相關。因此,你需要找到相關的部分並將其交給模型。Obsidian 有搜尋功能,但它只是搜尋確切的詞語和短語,而我們需要搜尋概念。這就是嵌入 (embeddings) 發揮作用的地方。我們必須建立索引。事實證明這很容易做到。

讓我們建立索引器

當你建立 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 也具有 embed 功能。你也可以在雲端超快速的自託管實例上使用這些服務,然後在索引完成後關閉它。

現在讓我們搜尋筆記

現在我們有了這個檔案的索引。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 個 tokens。我將模型設定為使用包含我們文字區塊的系統提示詞。只需提出問題,它就會在幾秒鐘內給你答案。

太棒了。現在,我們的 Obsidian 外掛程式將適當地顯示該答案。

我們還可以做些什麼?

你也可以考慮總結文字,或找到與你的文字最匹配的關鍵字並將其添加到前言中,這樣你就可以在筆記之間建立更好的連結。我嘗試過製作 10 個好的問題和答案發送到 Anki。你會想要嘗試不同的模型和提示詞來完成這些不同的事情。更改提示詞甚至模型權重以使其更適合任務非常容易。

我希望這篇文章為你提供了一些關於如何為 Obsidian 或任何其他筆記工具建立下一個偉大外掛程式的想法。使用最新的本地 AI 工具,例如你在 ollama.com 上找到的那些,這種強大的功能輕而易舉,我希望你能向我展示你正在做什麼。