20220826 - Obsidian Content Linker 插件开发回顾


开发 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,不知能否顺利加入官方插件库哈哈。

后续

已经上架官方插件库。


指令小记

使用 GitHub Actions 发布您的插件

开发环境
  • 进入插件文件夹后
    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

Author:
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source !
  TOC