去年在整理技术文档时,我发现Obsidian的本地知识库虽然安全可靠,但缺乏智能交互能力。而大模型API调用又需要额外开发环境。于是萌生了一个想法:能否在Obsidian里直接调用AI代理?经过几轮实验,终于找到了一套极简实现方案。
这个方案的核心价值在于:
Obsidian的插件系统基于Electron架构,支持:
关键插件接口:
javascript复制// 示例:注册命令
this.addCommand({
id: 'ask-ai',
name: '咨询AI助手',
callback: () => { /*...*/ }
});
推荐使用开源代理框架LangChain.js,优势在于:
典型配置流程:
javascript复制const model = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-4-turbo",
streaming: true
});
bash复制npx @obsidianjs/cli create-plugin ai-assistant
bash复制npm install langchain @langchain/openai
对话界面实现:
typescript复制class AIPanel extends ItemView {
private chatHistory: string[] = [];
async onOpen() {
this.contentEl.createEl("h2", { text: "AI助手" });
const input = this.contentEl.createEl("textarea");
input.addEventListener("keydown", async (e) => {
if (e.key === "Enter") {
await this.handleQuery(input.value);
}
});
}
}
API调用逻辑:
javascript复制async function generateResponse(prompt: string) {
const chain = new ConversationChain({
llm: model,
memory: new BufferMemory()
});
const stream = await chain.stream({
input: `基于以下知识:\n${currentNoteContent}\n\n回答:${prompt}`
});
for await (const chunk of stream) {
// 实时渲染到界面
}
}
通过向量数据库实现:
getFiles()获取所有文档TextLoader和RecursiveCharacterTextSplitter处理文本HNSWLib建立本地向量索引javascript复制const loader = new TextLoader(filepath);
const docs = await loader.loadAndSplit(
new RecursiveCharacterTextSplitter()
);
const vectorStore = await HNSWLib.fromDocuments(
docs,
new OpenAIEmbeddings()
);
示例:每日摘要生成
javascript复制this.registerInterval(
window.setInterval(async () => {
const dailyNote = getDailyNote();
const summary = await model.invoke(
`用中文总结今日笔记要点:\n${dailyNote}`
);
appendToJournal(summary);
}, 86400000) // 每天执行
);
性能优化:
隐私保护:
错误处理:
javascript复制try {
await model.invoke(prompt);
} catch (e) {
if (e instanceof RateLimitError) {
showNotice("请求过于频繁,请稍后再试");
}
}
实测发现,当温度参数设为0.3-0.5时,技术类回答的准确性最佳。过高会导致创造性过强,过低则缺乏灵活性。
实现过程中最耗时的部分是处理流式响应渲染,最终采用虚拟滚动方案解决性能问题。建议初次开发时先实现基础功能,再逐步添加高级特性。