使用 Text Generator 批量总结笔记里的链接

目的:

我笔记里有些网址链接, 形如 参考链接 [title](https://xxxxx.com/page/url)
或者更简单只有个 url, 形如 见网址 https://xxxxx.com/page/url 一些备注说明
想用 Text Generator 给这些链接快速方便的生成内容摘要


为啥不用网页版大模型?

其实我一直就这么干的, 现在想更懒点

网页版大模型, 做这事的步骤:

  1. 在笔记里复制链接, 可能需要从中挑出 url 部分
  2. 浏览器找到网页版大模型, 一般这是随时开着备用的
  3. 对话框里敲字: 总结这个网址的文章 要简单不要太长:
  4. 之前复制好的链接, 粘对话框里: (...接上面): https://xxxxx.com/page/url
  5. 发送, 等它回复, 把对话复制下来贴笔记里
    • 注意: 要求你的模型能联网读网页, 虽然现在这并不稀罕

而 Text Generator 理论上能做到的步骤:

  • 使用场景A: 选中笔记里的 url, 运行 “总结文章”, AI 自动把摘要生成到链接底下, 完事
  • 使用场景B: 全选十篇文章链接, 运行 “总结文章”, 摘要就自动填写到每篇的 url 底下 (内部可以是发一次或发十次对话请求, 分开还是合并, 要综合考虑便捷和价格)

可以看到使用 TextGen 插件的好处:

  • 在当前笔记里做完所有事, 不切换窗口
  • 提示词存档复用, 方便改进优化 (实践发现提示词还是要持续调试的, 没法一次定型, 道理跟代码一样)
  • 预处理 “不必由 LLMs 出手的工作”, 比如抓取文章就属这类, TextGen 内置了网页提取器, 提交到 LLMs 时已是整理好的文本, 于是: 可以选择不能联网的大模型, 可以精确控制 tokens 成本

我想要的是, 争取实现 “使用场景B”, 以下为步骤


学习如何使用 Text Generator

1 配置模型

有模型 API 使用经验的朋友请略过本节

Ob 各家 AI 插件的配置方法都差不多, 通常需要准备好三个东西

# 以 OpenRouter 的免费 Mistral-7b 为例
请求地址: https://openrouter.ai/api/v1/chat/completions
模型名字: mistralai/mistral-7b-instruct:free
api_key: sk-or-v1-33dca1......

在 Text Generator 设置 → LLM Provider 新增一项 Custom 配置

  • 模型请求地址 填在 Endpoint 里
  • 模型名字 填在 model 里
  • api_key 填在 Headers 的 “Bearer apikey” 这里, 注意保留 Bearer 这几个字

对于兼容 OpenAI 接口的大模型, 这样就完成了
其余的都是可选参数, 先默认就行


2 测试模型可用

光标停在笔记的任一处, 点击 “Ob 最左边图标按钮” 的 Generate Text

text-gen-generatetext-icon

这会续写当前笔记, 大致等同于 “把光标前的全部段落粘贴给 AI, 之后 AI 给你回复”
这也为测试模型 api_key 是否配置妥了

textgen-demo-tianwang


3 写个简单模板

TextGen 用模板来管理各种不同功能的提示词

要在仓库根目录造文件夹 <仓库root>/textgenerator/templates/ 模板默认就放这里, 每个作者应该再造一级子目录

可以从 TextGen 的模板管理器里下载一些别人分享的, 或自己添加个模板试试, 例如:

---
promptId: test_simplify
name: 段落简化
description: 段落简化 改为简明易懂的描述
---
把以下段落简化, 改为简明易懂的描述, 输出化简文字就行, 别抄原文

{{tg_selection}}

模板也是个 .md 文件, 文件名不重要, FrontMatter 里的 promptId 和 name 重要
完后 Ob 里运行命令 Templates: Generate & Insert 选刚才的模板, 就能用了

textgen-demo-simplify-ob


4 用 Playground 调试模板

Template Playground, 这是好东西, 调试必备, 可以看看你发给 LLMs 的精确文本到底是个啥, 比如:

  • 模板里啥是 {{context}} ?
  • 选区 {{selection}}{{tg_selection}} 的区别?
  • 当前句子 {{cursorSentence}} 能认中文标点吗?
  • 反链列表 mentions.linked 具体输出的啥格式?
  • … 更多见 Understanding Context 示意图挺清楚

以上都不用猜, 也别查文档了, 直接拿 Template Playground 预览一看便知

Playground 启动方式是 Ob 里运行命令 Open Template Playground
试试把下面的贴到 Playground 里, 然后选中一小部分笔记, 点一下 Preview

#### TG Selection:
```
{{tg_selection}}
```

#### Selection:
```
{{selection}}
```

#### Context:
```
{{context}}
```

#### Note Content:
```
{{content}}
```

Playground 点击预览按钮, 默认不会发请求给 LLMs


5 测试提取网页

TextGen 内置了抓网页的工具函数

完整语法见

// 单行写法
{{extract "web_html" "https://lilianweng.github.io/posts/2024-02-05-human-data-quality/" "article"}}

// 块写法
{{#extract "web_md" "my_content" "article"}}
    https://lilianweng.github.io/posts/2024-02-05-human-data-quality/
{{/extract}}

// 两个写法等价, 第二种可控性更好, 提取的结果存到 my_content 里

这一小段 extract 函数也是合法模板, 在 Playground 里测应该跑通,

顺便可以自己改改 extract 的参数:

extract "web"      默认格式化 markdown, 即 "web" 等同于 "web_md"
extract "web_md"   见上
extract "web_html" 格式化为 html


extract "web" "https://xxxxxxxx" "article"
"article" 是 cssSelector, 用于提取网页网页的特定部分
如果改成 
extract "web" "https://xxxxxxxx" "a"
这个 "a" 就是只提取该文档里的链接的意思

省略 "article" 这参数, 就是默认提取整个页面

实践 造个总结文章模板

现在工具全了, 开整


level1 选中单个 url 提取正文并总结

先来实现: 让 TextGen 访问笔记当前选中的 url, 抓取正文, 以中文总结文章说了啥

整理完善的模板如下

---
promptId: SummaryArticleFromWebPageURL_lv1
name: 总结一个网页level1
description: 总结一个网页, 要求选中 url 部分, 暂不支持 [title](url) 和 [[my_local_note]] 两种形式
---

根据以下文章, 使用简短清晰的语言做 "总结摘要", 注意事项:

- 可以适当抄原文的段落或重点, 要多提及文章亮点和独特之处
- 不要写基本的背景知识介绍
- 对英文文章也生成中文摘要, 不要生成英文摘要
- 不要去读文章里的链接, 图片, 只关心我提供的文字就行
- 摘要必须尽量短小
- 如果原文里有写类似 "总结" 的段落, 可在摘要后, 原样抄一遍 "总结" 部分, 你的摘要和原作者总结都保留
- 直接回复最终生成的摘要文本即可, 不必说其他的
- 如果文章正文明显有误, 例如文章太短, 或文章只含一个 url, 就回答: 无法做摘要, 并原样输出这个错误的正文

以下是需要摘要的正文, 位于 "-------------" 之间 :

-------------

{{#extract "web" "main, article, div#main"}} 
    {{tg_selection}}
{{/extract}}

-------------

以下段落当成测试例子应该不错, 这些文章都很长, 且博客列表页没给你提供摘要

Introducing Chat Notebooks: Integrating LLMs into the Notebook Paradigm—Stephen Wolfram Writings
https://writings.stephenwolfram.com/2023/06/introducing-chat-notebooks-integrating-llms-into-the-notebook-paradigm/

Can AI Solve Science? (很长, 8k 模型不够, 32k 可以)
https://writings.stephenwolfram.com/2024/03/can-ai-solve-science/

Observer Theory (很长, 8k 模型不够, 32k 可以)
https://writings.stephenwolfram.com/2023/12/observer-theory/


执行模板前, 要精确选中 url, 周围可以有换行和空格, 但不要多选到标题文字上

大部分网页的正文都在 main, article, div#main 之类的标签里
抓取别的网站, 要自己把正文所在 css selector 写到 extract 第三参数里

level2 智能识别各种格式链接

如果选中文本里的链接有各种形态, 比如类似下面这样的

ChatGPT出来后,我们是否真的面临范式转变
https://hub.baai.ac.cn/view/22901

[LLM Powered Autonomous Agents | Lil'Log](https://lilianweng.github.io/posts/2023-06-23-agent/)

> [!tip]
> 这篇其实更早啊, 原文叫 "A Closer Look at Large Language Models Emergent Abilities" 
> Notion 原文 [A Closer Look at Large Language Models Emergent Abilities](https://yaofu.notion.site/A-Closer-Look-at-Large-Language-Models-Emergent-Abilities-493876b55df5479d80686f68a1abd72f)
> Notion 译文 [深入理解语言模型的突现能力](https://yaofu.notion.site/514f4e63918749398a1a8a4c660e0d5b)

- [ ] Chat Notebooks 试试
    - https://writings.stephenwolfram.com/2023/06/introducing-chat-notebooks-integrating-llms-into-the-notebook-paradigm/

现在要去掉无关文字, 只想要里面 url 部分, 怎么搞?

  • 以前的想法: 写个正则提取 url
  • 现在的想法: 先丢给大模型试试…

以下提示词可以做到, 输入 当前选中文本 (文本含数个链接), 输出 一行一个url
可以把它看做一个 “LLMs 工具函数”

---
promptId: ExtractURLsFromSelectingText
name: 提取段落里的链接 Extract Web Links
description: 提取文本中的链接, 要求选中带链接的文本块
---

提取以下文本段落中的链接 (url), 链接可能混在段落里, 形如 `[title](url)`, 或者链接是单独在一行里的 `标题 url 附带描述` 

只需要保留 url, 提取后, 按照一行一个 url 输出, 不用说别的话, 不要每行起始时带特殊语法格式, 不要包在代码块里

下面是需提取链接的文本段落, 位于 "-------------" 之间 :

-------------

{{tg_selection}}

-------------

这提示词我自己测大多数时候是可用的, 极少数情况 LLMs 会给你乱改返回格式


那么接下来, 要让外层的 “生成摘要的模板”, 调这个 “提取链接的模板”

首先, 确保上面这个模板 (将被调用, 负责提取链接) 放在这个路径里:

<仓库root>/textgenerator/templates/my_tmpls/模板文件名.md
模板文件名随意
但promptId必须是ExtractURLsFromSelectingText

然后, 让外部模板 (负责生成摘要) 调用它, 语法是

{{#run "my_tmpls/ExtractURLsFromSelectingText" "block_with_urls"}} 
    {{tg_selection}} 
{{/run}}


外层模板调用 `my_tmpls/ExtractURLsFromSelectingText`, 
结果存到 "block_with_urls" 里
外层模板最终输出:

{{get "block_with_urls"}}        <-- 变量要 get
{{log vars.block_with_urls}}     <-- 提取到的链接, 多行字符串, 不要有别的符号
{{notice vars.block_with_urls}}

把上面代码搁 Playground 里试试, 会看到 TextGen 的模板可以相互调用


当然, 用 LLMs 来提取简单段落里的 urls, 这是大材小用了,
也可以使用代码方式, 如下:

// 输入一段复杂文本, 输出每行一个url, 使用 LLMs 引擎
{{#run "my_tmpls/ExtractWebLinksFromSelection" "block_with_urls"}} 
    {{tg_selection}} 
{{/run}}

// or 使用正则提取 url, 
// 两者都是把当前选区的 urls 存到 block_with_urls 变量, 二选一, 别都用
{{#script}}
    function extractUrls(text) {
        const urlRegex = /https?:\/\/[^\s]+\b/g; 
        let urls = [];
        let match;
        while ((match = urlRegex.exec(text)) !== null) { urls.push(match[0]); }
        return urls;
    }
    this.vars.block_with_urls = extractUrls(this.tg_selection).join('\n\n');
    return "";
{{/script}}


{{notice vars.block_with_urls}}
{{log vars.block_with_urls}}
这里只做到提取 urls, 先不做抓页面和总结, 已解析到链接如下: 
{{vars.block_with_urls}}


测试可行后, 在 level2 先来实现: 选中一堆文字, 总结第一个遇到的 url (之后再遇到 url 不管)

该模板大致写成这样:

---
promptId: SummaryArticleFromWebPageURL_lv2
name: 总结一个网页level2
description: 总结一个网页, 要求选中文本中含有任意格式的 urls, 例如 [title](url) 和 单行的 "标题 url 额外备注" 等形式, 只会处理第一次遇到的 url
---

{{#run "my_tmpls/ExtractURLsFromSelectingText" "block_with_urls"}} 
    {{tg_selection}} 
{{/run}}

{{notice vars.block_with_urls}}

{{#script}}
    const first_webpage_url = this.vars.block_with_urls.trim().split('\n')[0];
    this.vars.first_webpage_url = first_webpage_url
    return "";
{{/script}}

{{notice vars.first_webpage_url}}


根据以下文章, 使用简短清晰的语言做 "总结摘要", 注意事项:

- 可以适当抄原文的段落或重点, 要多提及文章亮点和独特之处
- 不要写基本的背景知识介绍
- 对英文文章也生成中文摘要, 不要生成英文摘要
- 不要去读文章里的链接, 图片, 只关心我提供的文字就行
- 摘要必须尽量短小
- 如果原文里有写类似 "总结" 的段落, 可在摘要后, 原样抄一遍 "总结" 部分, 你的摘要和原作者总结都保留
- 直接回复最终生成的摘要文本即可, 不必说其他的
- 如果文章正文明显有误, 例如文章太短, 或文章只含一个 url, 就回答: 无法做摘要, 并原样输出这个错误的正文

以下是需要摘要的正文, 包裹在 "-------------" 之间 :

-------------

{{#extract "web" "main, article, div#main"}} 
    {{vars.first_webpage_url}}
{{/extract}}

-------------

我最后没搞定以它现有的 helper 函数, 怎么把多行字符串按行 split 之后放进 #each 迭代, 所以拆分 url 还是先用 script 解决的…


level3 批量总结多个文章

至此已经有如下工具:

  • 模板A SummaryArticleFromWebPageURL_lv1
    • 输入一个 url, 内部自动访问网页, 输出该文章的中文简要总结
  • 模板B ExtractURLsFromSelectingText
    • 输入多行文本, 输出其中 urls, 每行一个
    • 懒, 直接发给 LLMs 去干的
  • 模板B2 ExtractURLsFromSelectingTextRegExp
    • 功能同上, 老实以 js 函数实现的
  • 模板C SummaryArticleFromWebPageURL_lv2
    • 输入多行文本, 只把第一个遇到的 url 做总结
    • 在内部调用 模板B (或 模板B2)
    • 主要为测试模板间怎么调用

把所有的工具整合一下, 并对 模板C 做升级, 最终就可以实现: 输入多行文本, 提取其中所有 urls, 对所有文章做总结摘录

---
promptId: SummaryArticleFromWebPageURL_lv3
name: 总结多个网页level3
description: 批量总结网页, 要求选中文本中含有任意格式 urls, 例如 [title](url) 和 "标题 url 额外备注" 等形式, 自动抓取正文聚合后一次性总结, 相近主题的文章可能被总结在一起
---

{{#script}}
    function extractUrls(text) {
        const urlRegex = /https?:\/\/[^\s]+\b/g; 
        let urls = [];
        let match;
        while ((match = urlRegex.exec(text)) !== null) { urls.push(match[0]); }
        return urls;
    }
    this.vars.block_with_urls = extractUrls(this.tg_selection).join('\n\n');
    notice(`当前 urls 总共:\n ${this.vars.block_with_urls}`);
    return "";
{{/script}}

{{#script}}
```js
    // block_with_urls 可能是 LLMs 返回的, 此时是多行文本, 需按行拆开
    this.vars.url_list = this.vars.block_with_urls.split('\n') 
                        .map(line => line.trim())
                        .filter(line => line.startsWith('http'));
    notice(`准备获取 ${this.vars.url_list.length} 篇文章:\n${this.vars.url_list}`);
    const article_list = [];
    for (let index = 0; index < this.vars.url_list.length; index++) { 
        const url = this.vars.url_list[index];
        // 文章主体部分可能在多种标签里 例如 main div#main article 不好统一
        const selector = "main, article, div#main, div.site-body-center-column, div#content";
        article_list.push(await extract("web", url, selector));
    }

    this.vars.article_list = article_list;
    notice(`已获取 ${article_list.length} 篇文章正文, 总字符数 ${article_list.map(article => article.length).reduce((sum, charCount) => sum + charCount, 0)}`);
    return "";
```
{{/script}}


{{#script}}
```js
    // 对所有的文章正文, 控制一下总体字符数
    // 拼接提示词时, 多篇文章共享 25000 字的总额度
    // 最好是按 tiktoken 去算
    const max_chars = 25000;  // 适合 32k token 模型

    function truncate_article(content, limit) {
        // 删减到指定字数, 尽量保留头尾, 在换行处截断, 不要截到单词中间
        if (content.length <= limit) { return content; }
        const head_length = Math.floor(limit * 0.7);
        const tail_length = limit - head_length;
        const content_head = content.slice(0, head_length);
        const content_tail = content.slice(-tail_length);
        return `${content_head} ... 以下省略约 ${content.length - limit} 字... ${content_tail}`;
        // const content_lines = content_head.split('\n') + [`... 以下省略约 ${content.length - limit} 字...`] + content_tail.split('\n');
        // return content_lines.join('\n');
    }

    const article_list = this.vars.article_list;
    const total_chars = article_list.map(article => article.length).reduce((sum, charCount) => sum + charCount, 0);

    const truncated_articles = article_list.map((article, i)=> {
            const limit = Math.floor(article.length * max_chars / total_chars);
            const truncated = truncate_article(article, limit);
            return truncated;
        }
    );
    this.vars.output_articles = truncated_articles.map((truncated, i) => {
        return `第${i+1}篇文章:\n\nurl: ${this.vars.url_list[i]}\n\n${truncated}`
    }).join('\n\n\n\n@@@@@@@@\n\n\n\n');
    return "";
```
{{/script}}


根据以下一到多篇文章, 使用简短清晰的语言做 "总结摘要", 注意事项:

- 如果有多篇文章, 可以把主题近似的文章一并总结, 可以调整文章顺序
- 要保留 "第n篇文章" 和 url, 让人能知道总结和原文的对应关系
- 可以适当抄原文的段落或重点, 要多提及文章亮点和独特之处
- 不要写基本的背景知识介绍
- 对英文文章也生成中文摘要, 不要生成英文摘要
- 不要去读文章里的链接, 图片, 只关心我提供的文字就行
- 摘要必须尽量短小
- 如果原文里有写类似 "总结" 的段落, 可在摘要后, 原样抄一遍 "总结" 部分, 你的摘要和原作者总结都保留
- 直接回复最终生成的摘要文本即可, 不必说其他的
- 使用 markdown 格式化你的回答, 可以适当加粗 `**需要加粗的字**`, 划重点一些文字 `==需要高亮的字==`, 可以保留图片 `![img](xxxx)` 可以保留关键概念的链接 `[keyword](url)`
- 如果文章正文明显有误, 例如文章太短, 或文章只含一个 url, 就回答: 无法做摘要, 并原样输出这个错误的正文


以下是需要摘要的一到多篇文章的正文, 每篇文章以 "@@@@@@@@" 分割, 所有文章位于 "-------------" 之间 :

-------------

{{vars.output_articles}}

-------------

这里为防止写的过于复杂, 我又把 js 脚本搁回一个模板里了,
实践中, 不同功能拆分比较好, 让模板能灵活使用,
然后以互调方式实现综合功能


最后效果如下

textgen-moonshot-32k-summary

注: 这里提交给 LLMs 的仅是 “第几篇文章 + url + 文章正文删减版”
并未包括 “页面标题和来自笔记的提示信息” 如 原文 译文 字样 (但正文里译者一般会提)

例子中, 第三篇和第四篇是内容近似的文章, 被总结时, AI 顺便提了一嘴这俩文章有关

至此我已基本达到目的,
显然这方案仍有许多可优化之处, 以后再慢慢鼓捣吧, 感谢大家看到这里

2 个赞

问题和讨论

以下是我自己遇到的问题


问: 需要更多 TextGen 教程

推荐一个 Obsidian 的 ChatGPT 插件-CSDN博客 写了一些使用样例

Obsidian 插件:Textgenerator 简洁易用的 AI 书写增强 比较全面的功能说明

此外就是官方文档 Understanding Context - Text Generator Plugin 许多页面都有示意图, 挺清楚


问: TextGen 是直接操作笔记正文的, 有没有可能意外覆盖自己笔记?

目前我没遇到, 实际所见:

  • 无论选中文本与否, AI 生成文字都添加在后面, 不会覆盖你选区
  • 即使对生成的文字不满意, 也可以顺利撤销

此外从这插件 github issue 的数量来上, 还是比较稳的

但考虑到会有 中途断网 / 多光标输入 / 反复撤销重做时 … 等复杂场景,
一开始用还是得谨慎, 且最好别在超长笔记里用


问: 想频繁使用, 需要预估 tokens 成本

给几个例子:

moonshot-v1-8k, 1k tokens = 0.012元, Input 和 Output 相同
另两个更长的模型是 0.024元0.060元

qwen1.5-14b-chat, 1k tokens = 0.008元

第三方代理的 ChatGPT 3.5, 大概价格 1k tokens = 0.005~0.020元, 一般提问和回答 token 价格不同

OpenRouter 的 Mistral 7B 免费, Mixtral 8x7B 大概 1k tokens = 0.002元


问: 都有哪些工具函数

  • Handlebars helper: Handlebar Templates 存取变量, 读写本地笔记, 基本字符串处理如 转义 修剪空白 等等
  • script block: 就是直接写 “javascript 块”
  • extract helper: 支持 “web”, “youtube”, “pdf”, “image”, “audio”, “rss”
  • run helper: 调用子模版 {{run "packageId/promptId" "output" "input" "target"}}

问: Handlebars.js 是啥

用于生成 HTML 模板的 js 库, 基本用法:

<!-- 简单变量 -->
<p>{{name}}</p>

<!-- 条件语句 -->
{{#if loggedin}}
  <p>Welcome, {{username}}!</p>
{{else}}
  <p>Please log in.</p>
{{/if}}

<!-- 循环语句 -->
<ul>
  {{#each people}}
    <li>{{this}}</li>
  {{/each}}
</ul>

<!-- 嵌套模板 -->
{{> sidebar}}


问: 模板的组成

  • Metadata: 给模板分配唯一命名, 也有其他一些重要配置
  • Initialization: 预处理, 整理文字, 抓网页等 可以省略这个部分
  • Prompt: 发给 LLMs 的提示词
  • Output: 后处理 (修整 LLMs 的应答文本) 可以省略

抄一张官方文档图 Template Writing

400

  • 不同区域以 *** 分割
  • 如果没有 *** 则全部文本都是提示词
  • 如果只有一组 *** 则之前为提示词, 剩下是后处理格式化

问: 该怎么学写高级模板

插件的模板功能很强大, 但复杂语法带来了太多不确定性

我自己遇到的一个困难是, 在用了一堆 “调用子模版”, “嵌入 js 脚本块” 的模板里, 变量到底该怎么传? {{my_value}}, vars.my_value, this.my_value, ... 啥时候 work, 啥时候是 undefined? 变量啥时加引号, 啥时加花括号?

个人不成熟的建议是,
选一个最简单方案, 选一个功能最全方案,
略过中间这堆功能半高不低, 而学习成本很高的方案

最简单的方案: 就研究一个 “自己定制 prompt + {{tg_selection}}

最简模板长这样

---
promptId: 编一个id不要重复
name: 模板的名称显示在命令列表里
description: 模板的详情 备注 注意事项等, 可以稍微多写两句
---
此处全都是发给 LLMs 的提示词 
(例如: 需要对以下文本做 翻译, 修饰, 纠错, ... )
最后别忘附带上 "当前笔记里已选文本" 即

{{tg_selection}}

功能最全的方案: 研究 #script block

里面能以 javascript 写任意逻辑, 这胜在网上资料多, 且 Ob 其他一些插件也是这么写 js 的

---
promptId: 编一个id不要重复2
name: 模板的名称显示在命令列表里2
description: 模板的详情2 备注 注意事项等2, 可以稍微多写两句2
---

{{#script}}
    this.my_test_value = "hello world"; 
    this.lines = this.tg_selection.split('\n').filter(line => line.trim());
    return `从 script 块动态生成的提示词, 目前选中了 ${this.lines.length} 行, 分别是 ${this.lines.join('; ')}`;
{{/script}}

个人建议暂时忽略的:

  • 行级和块级的提取器 extract 写法, 例如 {{extract "extractorType" source}}
  • 行级和块级的调用子模版写法, 例如 {{run "packageId/promptId" "output" "input" "target"}}
  • 大部分 Handlebar 工具函数

以上这些东西, 固然也厉害,
但太难理解, 光是传参就够研究半天的 (可能有多种前端模板使用经验的人比较好懂), 研究写出来后, 发现其表达能力又不太够用, 最后还得 js 脚本


问: 怎么只编译不执行模板

在 TextGen 模板的术语里,

  • 编译 compile = 让 TextGen 预处理, 运行本地脚本, 运行爬虫, 拼接整理提示词, …
  • 执行 execute = 把拼好的提示词 POST 给 LLMs 接口, 等待 LLMs 应答

想要只编译不执行, 需要 disableProvider: true 例如:

---
promptId: ExtractURLsFromSelectingTextRegExp
name: 提取段落里的链接 Extract Web Links 本地正则
description: 提取文本中的链接, 要求选中带链接的文本块
disableProvider: true
---
{{#script}}
    function extractUrls(text) {
        const urlRegex = /https?:\/\/[^\s]+\b/g; 
        let urls = [];
        let match;
        while ((match = urlRegex.exec(text)) !== null) { urls.push(match[0]); }
        return urls;
    }
    this.vars.block_with_urls = extractUrls(this.tg_selection).join('\n\n');
    return "";
{{/script}}

***

dummy prompt 未使用

***

out = 
{{vars.block_with_urls}}

这模板只在本地执行文本处理, 输出提取到的 urls

即, TextGen 也可以拿来当 文本处理, 爬虫, 笔记拆分合并… 工具


问: 实现总结摘要一定得是 TextGen 吗, 做的这事也并不稀奇

确实不

只总结摘要的话, 许多工具都行
QuickAdd, Templater 也能搞定, 都是读网址, 转换数据, 送给模型, 抄回答

不太熟悉这些工具的朋友, 建议别挑了, 随便选个先跑起来都能用, 不一定非得 TextGen

附 QuickAdd 的 AI Assistant 教程 Obsidian 插件 QuickAdd 中 AI 助理(AI Assistant)的设置与使用

QuickAdd 的通用教程, 在这论坛找就行, 尤其推荐看看作者 PlayerMiller 他写了非常多教程


问: TextGen 还有什么好功能, 这文章没提到的

  • TextGen 可实现快速的续写 (默认两空格触发, 类似代码补全那种方式)
  • 自带了 提取视频, 提取 pdf, 提取图片, 语音转写工具 …
  • 可以联动 Smart Connections 等插件 (有个案例模板, 在 TextGen 里调用 SmartConn 的语义关联检索, 之后自动解释 Top5 检索结果为啥跟当前笔记相关)

个人理解, TextGen 跟其他 AI 插件相比, 主要还是胜在能编 “高度灵活的模板

但仅有模板功能是不行的, 得有人分享, 得有社区, 实话说, 现在没很多中文资料


问: TextGen 还可以拿来做什么 (使用场景)

例如

1
一个信息源, 总共 1000 多条文章, 获取文章列表, 从里面过滤你关心的主题 (还剩 100 条), 再对这个主题里 top10 文章做摘要, 综合考虑 top10 的主题 / 字数 / 难度 / 知识依赖关系, 给出这系列文章的推荐阅读顺序, 并附注文章难度和耗时预估

整个过程由 人 + 代码 + LLMs 交互协作完成, 这种情况下, 用编程不可能一次想清楚, 用网页 LLMs 又欠着 “应该把提示词迭代复用” 的意味, 且最好是全程都有 人 参与, 比如过滤文章, 用代码得写规则反复调, 我自己过滤直接把几行链接删了就完了

超长上下文大模型一站式解决问题 vs 把大模型当工具函数接受外界调度, 我目前感觉后者更实用, 理由: 前者贵且不可控, 后者分别发挥了 人 代码 LLMs 最优势之处

2
知识自动挖掘机, 对于某一知识点网页, 从链接和正文, 结合当前已有笔记, 分析我缺失的关联知识, 然后挨个访问缺失知识点的链接, 递归调自己, 最多挖两三层, 存储过程中涉及所有资料

简单起见可先从维基搞起, 这事可能类似知识图谱, 但它是个性化的, 只关心自己缺失的概念
之后再过渡到可在任意网页中执行 (需警惕变赛博仓鼠)

以上只是自己想的, 可能是伪需求, 实际得搞起来亲自用用才知道是否靠谱

1 个赞

精彩的分享居然没人关注?

1 个赞