开发 Content-Linker 插件
起因
在结合 Obsidian 使用卡片笔记法过程中其实存在一个痛点——需要手动添加双链。这个看似简单的操作在面对以往的大量笔记时变得异常困难。
当然了,这里提到的并非严格遵循随记 -> 文献笔记 -> 永久笔记(详见 与 PKM 的相识和实践中 双链和卢曼卡片法
部分)这样的转化过程中的双链添加场景,而是面对以往很多未添加过双链的笔记时感到头疼的情况。
在尝试搜寻目前的插件市场后发现了以下插件:
- Note Linker
- 该插件按理来说和我的需求已经很接近了,但在使用过程中发现其好像无法处理太大量的数据
- 搜索一次库中内容差不多要 5 分钟
- 而且在尝试勾选相关内容时出现了无法忍受的卡顿(估计也是数据量太大导致的)
- 该插件按理来说和我的需求已经很接近了,但在使用过程中发现其好像无法处理太大量的数据
- Various Complements
- 该插件可以基于库中已有内容进行快速补全,但前提是已经构建过相关双链
- Sidekick
- 该插件还未上线插件商店
- 该插件可用于在输入过程中很快发现已输入内容中潜在的双链,但前提是已经构建过相关双链
- 官方的 Outgoing links 功能
- 这个是官方提供的核心功能之一,可以发现其他笔记中与该页面同名的,还未添加双链的内容
如上所说,Note Linker 不适用于大量笔记的场景,而 Various Complements 和 Sidekick 虽然可以配合使用来在写作过程中快速构建双链,但前提是相关双链已经存在。而 Outgoing links 针对的是编写完之后的场景,而且只能寻找与当前页面标题同名的内容,场景有限。
没办法,那就只能自己动手,丰衣足食了。
强大的 CodeGPT
其实为过往库中的大量文件添加潜在双链的需求早就存在,为什么现在终于被提上日程了呢?——一是因为抽出时间学 Typescript 然后从头构建感觉太花时间;二是得益于强大的 CodeGPT。
近来,以 ChatGPT 领衔的一众 AI 工具已经在各个领域展现了不俗的能力。又因为我的需求是非常明确的,所以就动起了“能不能让 AI 来写这个插件”的心。没想到还真就做成了。
AI 炼丹
一开始当然还是得过一遍插件开发教程。这里也感谢 @luhaifeng666 大佬翻译的中文版开发教程 。
过完之后就正式开始“炼丹了”。
工具是 Visual Studio Code + CodeGPT。
在 20220709 - ChatGPT Prompt Engineering for Developers 一文中,已经提过了应该为 AI 预设身份,使其做出更有针对性的回答的办法。
所以最开始的 prompt 其实是这样的:
你是一位出色的 obsidian 插件开发者。现在想为 Obsidian 开发一款新的插件 Content Linker,以便能让用户能够为库中的已有内容添加双链。
需要实现的功能如下:
1. 需要能够索引库中的所有内容
2. 需要根据索引结果筛选出可能被新建的“双链”列表
3. 可以让用户从 2. 的结果中的列表中选择想要新建的双链关键词
4. 根据 3. 中用户所选结果为对应内容建立双链
请问我该怎么做
后来经过多轮迭代,最终是类似下面的形式:
你是一位出色的 obsidian 插件开发者。现在想为 Obsidian 开发一款新的插件 Content Linker,以便能让用户能够为库中的已有内容添加双链。
请逐条分析所选代码是否能实现以下功能,如果不能,请分析原因并给出修正后的完整代码:
1. 该插件应该可以通过在编辑窗口中调用函数,或是在设置页面中点击“Search Possible Bi-Directional Link in Vault” 按钮来搜索库中已有的所有内容;
2. 该插件应该有一个单独的设置页面;
3. 基于 1. 的搜索结果,存储库中所有重复出现过的内容,并且这些内容不双链形式,那么就将这些内容作为潜在的双链关键字。完成后给出弹窗提示“Search Finished!”;
4. 将 2. 中存储的结果中重复次数从高到低的前 n 个结果以列表形式呈现在该插件的设置页面中。在设置页面中添加一个名为"Option Count"的只能输入数字的输入框和名为“Update”的按钮,根据用户在输入框所输入的数字作为列表中可显示的选项数量(即 n 的数量);
5. 基于前面的回答,将 4.的结果以一个四列的列表形式呈现在该插件的设置页面中。每一个潜在的双链关键字即列表中的一个选项。第一列为序号,第二列为该潜在双链关键字在库中重复出现的次数,第三列为该潜在双链关键字,第四列为当前该选项的选择情况。
6. 当用户点击设置页面中的 “Update Bi-Link For Selected Options” 按钮后,遍历 5. 中用户所选择的所有选项,并将其所选内容在原文位置中替换为双链形式;
7. 该插件的设置页面中应该有一个单独的名为“Ignored Content List”的列表。该列表有四列,第一列为序号,第二列为该潜在双链关键字在库中重复出现的次数,第三列为该潜在双链关键字,第四列为当前该选项的选择情况。
8. 当用户点击设置页面中的“Ignore Selected Option(s)”按钮后,遍历 5. 中用户所选择的所有选项,将这些选项从潜在双链关键字列表中排除,并将这些选项加入 Ignored Content List,并在 Ignored Content List 中显示。对 Ignored Content List 中的内容根据其第二列的 Count 数量进行倒序排列。
9. 当用户点击设置页面中的“Remove From Ignored Content List”按钮时,遍历 7. 中所选的所有选项,并将这些选项重新加入潜在双链关键字列表。
一些炼丹体悟
记得调整 Model
gpt-3.5-turbo-16k
是比较好的,既因为其表现稳定,也因为其上下文长度足够长
一定要记得调整 Max Tokens
- 不然就会出现回答到一半停下来的情况
Query Language
- 最好还是设成和 Prompt 相同的语言
Temperature
- 可以理解为 0 是异常严格,近乎死板地执行要求,1 为具有创意地理解需求并提出解决方案
- 从实验结果来看 0.1~0.2 之间能够提供最好的回答
若是一次性给出的需求过多,那么它的回答就只会给出框架
- 所以一开始可以先构建一个功能,在确认实现之后再构建下一个
给出太具体的实现方案有时候反而效果不好
- 即有时候如果给它列举了对应的技术框架,最终它的回答会过于局限于框架本身,而忽略插件整体的实现,最终导致结果不太理想
为了多给 AI 一些思考时间,尝试让其逐条分析功能是否已经实现
- 但有时候这样它也无法很精确地分析到有 bug 的情况,这种时候可能只能人工调试 bug 了
也可以让其对代码进行整体分析
- 这样有时 AI 能给出一些原本可能没有注意到的优化方案
在得到可运行版本后记得及时存档哈哈
有时候会出现无法解决的 bug
- 比如同时存在 bugA 和 bugB,让其一起解决,它可能会解决了 bugA 然后忽略 bugB,或者反过来。有时候甚至还会继续引入新的 bug…
- 这种时候一般只能人工调试了
- 还有一个办法是去 ChatGPT 那里单开一个窗口,针对这个具体问题进行询问,有时候也能解决
小结
插件的代码加完注释共有 545 行,可以说 95%,甚至 99%以上的代码都是由 CodeGPT 自己完成的。我在其中发挥的功能主要是针对它错误给出的方案进行人工调试,并对结果进行测试,然后根据测试结果进行迭代。
整体而言,非常震撼。要知道我之前是没有接触过 Typescript 的,虽然是 JavaScript 的某种变体,可读性上来说很不错,但是我也就是能看懂,但具体要以什么语法调用什么 API 是毫无头绪的。CodeGPT 的能力可见一斑。
当然了,并不是毫无问题。除了上面炼丹体悟中提到的一些问题之外,目前的 CodeGPT 因为受制于 gpt-3.5-turbo-16k
,应该无法联网,且上下文长度有限。估计大概 700 行左右就是能输出的极限长度了。但这些问题,都可以随着模型的更新而被解决。
未来可期。
插件页面
目前 Content Linker 插件的 Github 页面 已经算是更新完了,也已经提了 Pull request,不知能否顺利加入官方插件库哈哈。
后续
已经上架官方插件库。
指令小记
开发环境
- 进入插件文件夹后
npm run dev
插件发布
- 提交更改
git commit -m "feat: Add settings"
- 设置更新版本
npm run release # Release as minor npm run release -- --release-as minor # Release as major npm run release -- --release-as major # Release as patch npm run release -- --release-as patch
- 发布
git push --follow-tags origin master && npm publish