PKM(个人知识管理)构建手册 - Nvim

Aug 02, 2024 · 10635 words · 22 min ·   #PKM #Nvim

AI摘要

正在生成中……



Intro#

在之前的脱离笔记软件,CLI 笔记工具 —nb里,那个时候已经在琢磨使用bash脚本来做笔记的框架。期间因为没有找到有效的处理双链和反链的方法,无意间了解到了Emacs里的org-roam可以提供反链,正好又在学习代码的初期急需一个编辑器,于是在Emacs还是Nvim之间选择了Emacs。

直到这几天无意间发现了vimwiki/vimwiki: Personal Wiki for Vim,如果当初的搜索关键字多了几个字,这几个月关于做笔记的探索将会截然不同。

脱离笔记软件回归数据本身,这是当初停止使用Logseq、obsidian的最大原因。

我的 Neovim Zettelkasten:我如何使用 Vim 和 Bash 在 Markdown 中做笔记 |米沙·范登伯格 ,随即搜到了这篇文章,让我决定开始尝试Nvim做笔记的方案。Bash其实就是依赖性最小的工具。

org-capture可以用bash脚本来实现,双链通过VImwiki实现,反链同样可以通过bash脚本正则查询实现。在迁移配置到其他环境时Nvim的轻便优势就更大了,在我的Linux虚拟机里迁移一次DoomEmacs将近要安装配置半个多小时,而Nvim一个dotfile,5分钟搞定安装到配置。

Emacs是一个赛博乐高,我很享受这几个月使用Emacs收获的快乐,但它并不能完全扩展成一个兼容性很强的方法。

Emacs也完全是一个操作系统,对比起来Nvim作为编辑器的最大优势就在于轻巧。Org-mode里让我最喜欢的一点就是标题可折叠和自定义显示层级,对各种语言、格式的兼容性都很强。代码块部分可以直接生成结果,可以直接在代码块里使用jupyter,这对于做coding笔记来说很方便。再就是org-capture。它像是一个办公室。这里有书柜、办公桌、台灯,你可以在这里构建任何其他你喜欢的东西,但搬家是个大工程。

这些功能都依赖于Emacs、依赖于org-mode,用的越多,对它们的依赖就越大,因为Emacs和org真的很好用。.org文件转换起来并不麻烦,org基本可以输出成任何其他格式的文件,但迁移之后也就失去了org的功能特性。

这还是背离了我脱离“笔记软件”的初衷。

Notion、Logseq、Obsidian、Roam Research、Heptabase,未来还会有更多的笔记软件,适合自己的才是最好的,选择一个笔记软件、笔记工具就是在选择一个生态、一个系统。你可能会想,既然有这么多成熟的笔记生态了,为什么还要自己折腾这些?因为这些就是他们当初不满意现有的生态,折腾出来的适合自己的笔记方法。我不太想依赖于别人的方法,下一个笔记软件,何必是笔记软件。

通过RAG将PKM和LLM结合起来一定以未来更有效的学习方式。将数据困在.org里绝不是一个很好的方案,结构化的文档有更少的噪声、清晰的数据结构和关系进、支持更复杂的SQL查询,进而从数据源头提高RAG向量化的质量。但好处是,AI可能就不那么好偷学我的笔记了。

从数据层面来思考笔记的形式、回归数据本质是这次要继续探索的方向。PKM笔记文档和PostgreSQL链接起来是下一步要解决的问题。

Nvim更像一支笔,把PKM方法、笔记数据、操作系统链接起来,你可以在任何环境使用这只笔。

这让我感觉更自由。

Vscode?太没个性了。

Manual#

这是一个以 Neovim 为基础编辑器、文件管理的Markdown本地笔记PKM。

本手册包括:

  • Prerequisites:开始构建本PKM需要的工具和准备工作
  • PKM:本PKM的构建原则和具体方法
  • PKM with LLM:PKM和大语言模型结合的使用

总目录在页面最上方:Table of Contents

Prerequisites#

Start with:GitNeovimLazyVimVimwikiBashkitty 或其他terminal。

开始你可能需要对Nvim,Markdown,Git,vim编辑模式有一些了解,这些可以帮助到你:

不太建议上手Nvim就直接使用NvChad这种类似于DoomEmacs的集成配置,它们的文件结构不利于自定义个性化配置。Nvim配置起来比Emacs简易许多,不太需要一步到位的集成方案。

LazyVim既是一个插件安装管理器同时也是一个初始的简易Nvim配置,从这里开始。

PKM#

整体的笔记文件管理结构,还是ACCESS,可以选择不用。有文件夹和没有文件夹是两种不同的管理模式,例如ob和Logseq。这个手册里更主要的是分享我在构建PKM的过程中迭代的一些原则和方法,以供参考,形成、巩固自己的方法才是这个手册真正的目的,remember:有形易于无形。具体可以参看:§Vandee的PKM

Journals用来记录每天的日志,作为简单明了的回顾和展开。如果你不喜欢文件夹管理,我觉得这个是很有必要的。

在Nvim里,我暂时使用Bash脚本来创建Journals。以年月日展示,在Typora的侧边栏可以直观的选择特定日期的日志。为了展示MD标题层级的效果,脚本设置了在Typora里默认打开,可以根据需求更改。Nvim里正则搜索特定日期的标题就可以找到当天的日志了。

核心:原子笔记 + 双链

你可能需要了解「原子笔记」(或者常青笔记,whatever):Evergreen notes should be atomic

双链和反链让笔记之间的关联更加直观,当原子笔记很多的时候,反链可以直观的反应出和当前笔记相关的其他笔记。它应该同步于思维的链条。

Zettelkasten 其实并不那么重要。

Stay foolish,stay simple。

关于同步

本地笔记没有在线笔记软件方便的地方就是多端同步与预览。我的本地笔记已经上传到了GitHub仓库,通过Git同步。临时需要修改的直接在GitHub仓库里就修改了。预览方面,我的笔记输出到了Vandee’s Digital Garden

Principles#

无思维不笔记,PKM不做本末倒置的事,让它帮助自己拓展思维,提升思维、学习、认知的效率,找到知识的缝隙,更好的认知世界、认知自我才是本质。

Methods#

在最开始需要构建的关键体系就是检索的逻辑。

在双链部分由于原则上已经和思维高度同步,因此不需要做额外的增加思维成本的检索,只要在记录原子笔记的时候,顺其自然,高度提炼出明确、简洁的一句话概念即可。

在标签tags部分,我习惯把标签当作是一个object的二、三级属性来看待。例如《黄金时代》这本书,它的基础属性是book,我不会再重复标注它,而是写在property里,记下Type: book方便我通过函数来列举所有的书目,tag则记下文学、王小波。我习惯用单数来强调它是个属性,区别与复数的category集合的概念。

我看过许多博客和PKM,许多情况下,他们的tags在语义概念上都混用了,我习惯把tags和categories分开,分类就是分类,标签就是标签。category、property、tag还是需要在逻辑里有自然的分类,理清楚一个概念的内涵与外延。混用会导致tags越来越多,成百上千的tags反而会增加检索的成本,过个几年,大多数的tags你已经忘了当时为什么记下了,单独再去整理标签无疑是个巨大的工程,tags也失去了应当有的作用。tags的构建同样应当同步与自身的思维习惯,自身对概念、语义的记忆。

当然这也完全取决于每个人自己的习惯。

学习、获取信息和知识是在做加法,PKM里更多的应该是减法,Learn and unlearn

整体配置、配置文件结构参考:craftzdog/dotfiles-public: My personal dotfiles

Vimwiki#

Vimwiki提供了类似于org-roam的insert功能,选中文字直接Enter即可在配置的笔记目录下生成笔记。当然也可以/path/notes的形式在相对路径的文件夹里生成对应文件。

VImwiki这个相对路径直接生成文件的实现很重要。在ob里可以直接[[note1]][note1](note1)引用note1,但这在其他笔记软件里是不支持的。许多md博客生成框架例如hugo,是不支持这种全局通过文件名引用的。Vimwiki支持[/pkm/note1](note1)这样的格式,保留了相对路径在以后兼容其他框架的时候修改起来就很方便。

然后就可以开始愉快的写md笔记了。反链暂时我使用正则对当前文件名搜索,得到其他引用文件。Lua还不是很熟,后面写一个脚本来打印出搜索的结果。这个在Emacs里可以通过embark在minibuffer里实现。

  • Lazy安装:

    {'vimwiki/vimwiki'}
    

    官方文档里,没有直接写明Lazy的安装方式,我还以为不支持,其实是支持的。

  • 配置笔记文件夹

    -- vim
    let g:vimwiki_list = [{'path': '~/vimwiki/',
                          \ 'syntax': 'markdown', 'ext': 'md'}]
    
    -- lua
    vim.g.vimwiki_list = {
      {
        path = '~/vimwiki/',
        syntax = 'markdown',
        ext = 'md'
      }
    }
    
  • Basic key bindings

    • <Leader>ww – Open default wiki index file.
    • <Leader>wt – Open default wiki index file in a new tab.
    • <Leader>ws – Select and open wiki index file.
    • <Leader>wd – Delete wiki file you are in.
    • <Leader>wr – Rename wiki file you are in.
    • <Enter> – Follow/Create wiki link.
    • <Shift-Enter> – Split and follow/create wiki link.
    • <Ctrl-Enter> – Vertical split and follow/create wiki link.
    • <Backspace> – Go back to parent(previous) wiki link.
    • <Tab> – Find next wiki link.
    • <Shift-Tab> – Find previous wiki link.

Bash - MD capture#

我现在自用的bash脚本包括导航脚本和特定笔记脚本两种,根据这两个模板常用的笔记需求基本可以满足。基本复现org-capture里自定义模板、特定位置写入内容、指定文件生成位置。

使用Bash脚本步骤#
  • 添加脚本文件夹环境全局访问:.bash_profile or .bashrc文件里写入export PATH="~/path/my_scripts:$PATH"

  • 添加脚本可执行权限:terminal里cd ~/path/my_scripts到脚本文件夹位置,chmod +x script_name.sh

  • 添加别名:alias script_alias='path/my_scripts/script_name.sh',然后source ~/.bash_profile or source ~/.bashrc

  • Nvim里打开terminal或直接打开系统terminal,输入脚本名字或别名。

  • 如果使用的是Homebrew安装的Bash,在脚本里替换#!/bin/bash#!/opt/homebrew/bin/bash

脚本模板参考#
  • 导航脚本,选择创建笔记的类型:

    #!/bin/bash
    
    # 设置退出脚本的陷阱
    trap 'echo "退出脚本。"; exit' SIGINT
    
    # 定义脚本目录
    script_dir="$HOME/path/"
    
    # 定义操作和对应的脚本
    declare -A operations=(
      [1]="Journals.sh"
      [2]="NotesMD.sh"
      [3]="AtomicMD.sh"
      [4]="BlogMD.sh"
    )
    
    # 显示操作选择列表并提示用户输入
    echo "请选择要执行的操作(输入序号):"
    for i in $(seq 1 ${#operations[@]}); do
      echo "$i. ${operations[$i]}"
    done
    echo "或者输入 'q' 退出脚本"
    
    # 读取用户输入
    read -p "输入序号: " choice
    
    # 检查用户是否选择退出脚本
    if [[ "$choice" == "q" ]]; then
      echo "退出脚本。"
      exit
    fi
    
    # 根据用户输入的序号执行相应的脚本
    if [[ -n "${operations[$choice]}" ]]; then
      script_path="${script_dir}${operations[$choice]}"
      echo "执行 ${operations[$choice]} 脚本..."
      # 执行相应的脚本
      "$script_path"
    else
      echo "无效的序号,请输入1到4之间的数字。"
      exit 1
    fi
    
    # 提醒用户已执行相应脚本
    echo "已执行选择的脚本。"
    
  • Journal脚本,生成每天的日志:

    #!/bin/bash
    
    # 设置系统语言环境为英文
    export LC_ALL=en_US.UTF-8
    export LANG=en_US.UTF-8
    
    # 设置绝对路径
    base_path="$HOME/path/Journals"
    
    # 获取当前年份
    current_year=$(date +"%Y")
    
    # 询问用户输入日期
    read -p "Please enter the date (YYYY-MM-DD or MM-DD) [default: $(date +"%Y-%m-%d")], or 'q' to quit: " input_date
    
    # 如果用户输入 'q',则退出脚本
    if [ "$input_date" == "q" ]; then
        echo "Exiting the script."
        exit 0
    fi
    
    # 如果用户没有输入日期,则使用当天日期
    if [ -z "$input_date" ]; then
        input_date=$(date +"%Y-%m-%d")
    fi
    
    # 检查输入格式
    if [[ $input_date =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})$ ]]; then
        # 完整的 YYYY-MM-DD 格式
        input_year=$(echo "$input_date" | cut -d'-' -f1)
        input_month=$(echo "$input_date" | cut -d'-' -f2)
        input_day=$(echo "$input_date" | cut -d'-' -f3)
    elif [[ $input_date =~ ^([0-9]{2})-([0-9]{2})$ ]]; then
        # MM-DD 格式,使用当前年份
        input_year=$current_year
        input_month=$(echo "$input_date" | cut -d'-' -f1)
        input_day=$(echo "$input_date" | cut -d'-' -f2)
    else
        echo "Invalid date format. Please use YYYY-MM-DD or MM-DD."
        exit 1
    fi
    
    # 生成 Journal 文件路径
    journal_filename="Journal-$input_year.md"
    journal_path="$base_path/$journal_filename"
    
    # 检查文件是否已存在
    if [ -f "$journal_path" ]; then
        echo "Journal file already exists: $journal_path"
        target_file="$journal_path"
    else
        touch "$journal_path"
        echo "Journal file created: $journal_path"
        target_file="$journal_path"
    fi
    
    # 获取当前日期信息
    current_date=$(date +"%Y-%m-%d")
    current_month_name=$(date +"%b")
    current_day_name=$(date +"%A")
    
    # 使用 date 命令获取输入日期的月份和星期几的名称
    input_month_name=$(date -j -f "%Y-%m-%d" "$input_year-$input_month-$input_day" +"%b")
    input_day_name=$(date -j -f "%Y-%m-%d" "$input_year-$input_month-$input_day" +"%A")
    
    # 检查当前年份头是否存在
    if ! grep -q "# $input_year" "$target_file"; then
        echo "# $input_year" >> "$target_file"
        echo "Current year header inserted in $target_file: # $input_year"
    else
        echo "Current year header already exists, no need to insert it again"
    fi
    
    # 检查当前月份头是否存在
    if ! grep -q "## $input_year-$input_month $input_month_name" "$target_file"; then
        echo "## $input_year-$input_month $input_month_name" >> "$target_file"
        echo "Current month header inserted in $target_file: ## $input_year-$input_month $input_month_name"
    else
        echo "Current month header already exists, no need to insert it again"
    fi
    
    # 检查当前日期头是否存在
    if ! grep -q "### $input_year-$input_month-$input_day $input_day_name" "$target_file"; then
        echo "### $input_year-$input_month-$input_day $input_day_name" >> "$target_file"
        echo "Current day of the week header inserted in $target_file: ### $input_year-$input_month-$input_day $input_day_name"
        echo "" >> "$target_file"
        echo "- TODOs" >> "$target_file"
        echo "- Inbox" >> "$target_file"
        # 将光标定位在"- Inbox"这一行
        sed -i '' -e '/- Inbox/G' "$target_file"
    else
        echo "Current day of the week header already exists"
    fi
    
    # 用Typora打开生成的日记文件
    open -a Typora "$target_file"
    
  • Notes脚本,生成一般的笔记:

    #!/bin/bash
    
    # 提示用户输入Markdown文件的名称,并检查是否输入了"q"以退出脚本
    while true; do
      read -p "请输入Markdown文件的名称(不包含.md扩展名)或输入'q'退出: " filename
      if [[ "$filename" == "q" ]]; then
        echo "退出脚本。"
        exit
      fi
      # 如果用户没有输入任何内容,则使用当前日期和时间作为文件名
      filename=${filename:-$(date '+%Y%m%d%H%M%S')}
      # 检查输入是否有效,如果为空则继续询问
      if [ -z "$filename" ]; then
        echo "文件名不能为空,请输入文件名或输入'q'退出。"
        continue
      fi
      # 一旦获得有效输入,跳出循环
      break
    done
    
    # 检查是否已经存在该目录,如果不存在则创建
    mdNotesDir="$HOME/path/"
    if [ ! -d "$mdNotesDir" ]; then
      mkdir -p "$mdNotesDir"
    fi
    
    # 构建完整的文件路径
    fullPath="${mdNotesDir}${filename}.md"
    
    # 获取当前的日期和时间
    current_date=$(date '+%Y-%m-%d')
    current_date_yyyyMMddHHmmss=$(date '+%Y%m%d%H%M%S')
    
    # 创建并写入 YAML 头信息到 Markdown 文件
    cat > "$fullPath" <<EOF
    ---
    UID: $current_date_yyyyMMddHHmmss
    title: $filename
    alias: 
    areas: 
    tags: 
    rank: 
    source: 
    type: Article
    date: $current_date 
    ---
    EOF
    
    # 提醒用户文件已创建
    echo "Markdown 文件 '$fullPath' 已创建。"
    
    # 使用 open 命令自动打开文件
    open -a "typora" "$fullPath"
    

ToggleTerm#

akinsho/toggleterm.nvim 可以在Nvim里快速创建一个内置终端。结合Bash脚本,体验和Emacs里使用org-capture差不多。当然,org-capture可以结合org-protocol,还是要强大太多。

  • 安装

    {
      -- amongst your other plugins
      {'akinsho/toggleterm.nvim', version = "*", config = true}
      -- or
      {'akinsho/toggleterm.nvim', version = "*", opts = {--[[ things you want to change go here]]}}
    }
    
  • 参考配置

    local toggleterm = require("toggleterm")
    
    toggleterm.setup({
        size = 15, - 设置窗口大小
        open_mapping = "<C-\\>",
        start_in_insert = true,
        direction = "horizontal", - 设置窗口横向或竖向
        shell = vim.o.shell, - 设置需要使用的shell
    })
    
    - Terminal里使用vim的移动指令
    function _G.set_terminal_keymaps()
      local opts = { noremap = true }
      vim.api.nvim_buf_set_keymap(0, 't', '<esc>', [[<C-\><C-n>]], opts)
      vim.api.nvim_buf_set_keymap(0, 't', 'jk', [[<C-\><C-n>]], opts)
      vim.api.nvim_buf_set_keymap(0, 't', '<C-h>', [[<C-\><C-n><C-W>h]], opts)
      vim.api.nvim_buf_set_keymap(0, 't', '<C-j>', [[<C-\><C-n><C-W>j]], opts)
      vim.api.nvim_buf_set_keymap(0, 't', '<C-k>', [[<C-\><C-n><C-W>k]], opts)
      vim.api.nvim_buf_set_keymap(0, 't', '<C-l>', [[<C-\><C-n><C-W>l]], opts)
    end
    
    vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')
    
    local Terminal = require("toggleterm.terminal").Terminal
    local lazygit = Terminal:new({ cmd = "lazygit", hidden = true })
    
    function _LAZYGIT_TOGGLE()
      lazygit:toggle()
    end
    
    local node = Terminal:new({ cmd = "node", hidden = true })
    
    function _NODE_TOGGLE()
      node:toggle()
    end
    
    local python = Terminal:new({ cmd = "python3", hidden = true })
    
    function _PYTHON_TOGGLE()
      python:toggle()
    end
    
    -- 为 Python 终端设置快捷键
    vim.api.nvim_set_keymap('n', '<leader>tp', ':lua _PYTHON_TOGGLE()<CR>', { noremap = true, silent = true })
    -- 快速打开Terminal
    vim.keymap.set("n", "<Leader>tt", function()
      require("toggleterm").toggle()
    end, { desc = "ToggleTerm" })
    
    -- end
    

中英输入法问题#

im-select-nvim#

keaising/im-select.nvim 这个插件可以在Nvim里让nomal模式下自动切换成英文输入,在insert模式下自动切换回中文输入。刚需。

  • 先安装im-select

    Install
    
    Please install im-select
    
    Download URL: https://github.com/daipeihust/im-select
    
    Check installation in bash/zsh
    
    # find binary
    $ which im-select
    
    # Get current im name
    $ im-select
    
    # Try to switch to English keyboard
    $ im-select com.apple.keylayout.ABC
    
    Check in NeoVim
    
    :!which im-select
    
  • im-select-nvim的安装和配置

    {
        "keaising/im-select.nvim",
        config = function()
            require('im_select').setup({
                -- IM will be set to `default_im_select` in `normal` mode
                -- For Windows/WSL, default: "1033", aka: English US Keyboard
                -- For macOS, default: "com.apple.keylayout.ABC", aka: US
                -- For Linux, default:
                --               "keyboard-us" for Fcitx5
                --               "1" for Fcitx
                --               "xkb:us::eng" for ibus
                -- You can use `im-select` or `fcitx5-remote -n` to get the IM's name
                default_im_select  = "com.apple.keylayout.ABC",
    
                -- Can be binary's name, binary's full path, or a table, e.g. 'im-select',
                -- '/usr/local/bin/im-select' for binary without extra arguments,
                -- or { "AIMSwitcher.exe", "--imm" } for binary need extra arguments to work.
                -- For Windows/WSL, default: "im-select.exe"
                -- For macOS, default: "im-select"
                -- For Linux, default: "fcitx5-remote" or "fcitx-remote" or "ibus"
                default_command = "/opt/homebrew/bin/im-select",
    
                -- Restore the default input method state when the following events are triggered
                set_default_events = { "VimEnter", "FocusGained", "InsertLeave", "CmdlineLeave" },
    
                -- Restore the previous used input method state when the following events
                -- are triggered, if you don't want to restore previous used im in Insert mode,
                -- e.g. deprecated `disable_auto_restore = 1`, just let it empty
                -- as `set_previous_events = {}`
                set_previous_events = { "InsertEnter" },
    
                -- Show notification about how to install executable binary when binary missed
                keep_quiet_on_no_binary = false,
    
                -- Async run `default_command` to switch IM or not
                async_switch_im = true
            })
        end,
    },
    

Ref:

RIME#
org-roam-ui-2024-05-22 13.31.40

如果说PKM是纸,那么输入法就是笔。

再次强烈推荐 RIME | 中州韻輸入法引擎,配合 iDvel/rime-ice: Rime 配置:雾凇拼音 | 长期维护的简体词库,多平台多端同步。已经使用了几年,非常巴适,手机端也可以同步配置。

以前输入法就各种偷偷记录用户输入习惯,现在各个输入法又还要内置AI再记录一遍。本地配置、不需要联网的输入法是我的刚需,更何况,rime还可以自挂词库。

一个巨坑,最近的RIME更新在Release 1.0.0 · rime/squirrel版本里改变了候选词横向和竖向的设置:

style/horizontal 將徹底移除,雖然本版程序仍支持,但會被新控件的默認值覆蓋
請使用 candidate_list_layout: stacked/linear 和 text_orientation: horizontal/vertical

而且最好直接在输入法外观的配置文件里,修改输入法外观的配置,而不是通用配置,反正我在通用设置里不起作用。

下面是简单的安装步骤

;; mac:
;; mac rime: https://rime.im/download/
Homebrew: brew install --cask squirrel
或者直接下载
;; mac rime-ice
使用东风破 plum 安装: https://github.com/rime/plum
curl -fsSL https://raw.githubusercontent.com/rime/plum/master/rime-install | bash

安装 rime-ice:
℞ 安装或更新全部文件
bash rime-install iDvel/rime-ice:others/recipes/full

℞ 安装或更新所有词库文件(包含下面三个)
bash rime-install iDvel/rime-ice:others/recipes/all_dicts

℞ 安装或更新拼音词库文件( cn_dicts/ 目录内所有文件)
bash rime-install iDvel/rime-ice:others/recipes/cn_dicts

℞ 安装或更新英文词库文件( en_dicts/ 目录内所有文件)
bash rime-install iDvel/rime-ice:others/recipes/en_dicts

℞ 安装或更新 opencc ( opencc/ 目录内所有文件)
bash rime-install iDvel/rime-ice:others/recipes/opencc

;;或使用 rime-auto-deploy:https://github.com/Mark24Code/rime-auto-deploy,这个和plum只用选一个。

=======================================================
;;Linux
ibus:
https://github.com/rime/home/wiki/RimeWithIBus
sudo apt-get install ibus-rime                   #ibus 

fcitx5:
sudo pacman -Sy fcitx5-rime                      # Arch Linux
sudo apt update && sudo apt install fcitx5-rime  # Ubuntu / Debian / Deepin
sudo zypper install fcitx5-rime                  # OpenSUSE
sudo dnf install fcitx5-rime                     # Fedora

plum and rime-ice:
git clone --depth 1 https://github.com/rime/plum ~/plum

# 切换到东风破的目录
cd ~/plum

# 如果你使用Fcitx5,你需要加入参数,让东风破把配置文件写到正确的位置
rime_frontend=fcitx5-rime bash rime-install iDvel/rime-ice:others/recipes/full

# 如果你是用IBus,则不需加参数,因为东风破默认是为IBus版的RIME打造。
bash rime-install iDvel/rime-ice:others/recipes/full

其他可以参考下面的文章:

MarkdownPreview#

iamcco/markdown-preview.nvim提供实时的markdown预览,对于经常写md的不是那么必须,拿不准的可以预览看看的整体效果。输入:MarkdownPreview就会在浏览器里打开预览。

当然,还有个更简单的办法,在Typora里打开:

-- 定义一个函数,用于在 Typora 中打开当前缓冲区文件 --
local function open_with_typora()
    local current_file = vim.api.nvim_buf_get_name(0)
    vim.cmd('!open -a Typora "' .. current_file .. '"')
end

-- 将该函数绑定到 <leader>t 快捷键
vim.keymap.set('n', '<leader>fo', open_with_typora, { desc = 'Open current buffer in Typora' })

-- end
  • 安装

    在Nvim里使用Lazy安装会报错,这个 issue 给出了解决办法:

      {
        -- Install markdown preview, use npx if available.
        "iamcco/markdown-preview.nvim",
        cmd = { "MarkdownPreviewToggle", "MarkdownPreview", "MarkdownPreviewStop" },
        ft = { "markdown" },
        build = function(plugin)
          if vim.fn.executable "npx" then
            vim.cmd("!cd " .. plugin.dir .. " && cd app && npx --yes yarn install")
          else
            vim.cmd [[Lazy load markdown-preview.nvim]]
            vim.fn["mkdp#util#install"]()
          end
        end,
        init = function()
          if vim.fn.executable "npx" then vim.g.mkdp_filetypes = { "markdown" } end
        end,
      },
    
  • 配置

    其他的基本不用怎么配置,下面是指定浏览器的配置:

    Add the following to your Neovim init script:
    
    Linux
    
      function OpenMarkdownPreview (url)
        execute "silent ! firefox --new-window " . a:url
      endfunction
      let g:mkdp_browserfunc = 'OpenMarkdownPreview'
    
    Replace firefox with chrome if you prefer. Both browsers recognize the --new-window option.
    
    macOS
    
      function OpenMarkdownPreview (url)
        execute "silent ! open -a Firefox -n --args --new-window " . a:url
      endfunction
      let g:mkdp_browserfunc = 'OpenMarkdownPreview'
    
    Replace Firefox with Google\ Chrome or Brave\ Browser if you prefer. They all recognize the --new-window option.
    

Image render#

3rd/image.nvim这个插件可以让Nvim以![img](img.png)的md语法显示本地图片和remote图片。插件推荐使用kitty终端,刚好我也用的kitty。

  • 依赖

    Mandatory Deps:

  • 安装

    {
        "vhyrro/luarocks.nvim",
        priority = 1001, -- this plugin needs to run before anything else
        opts = {
            rocks = { "magick" },
        },
    },
    {
        "3rd/image.nvim",
        dependencies = { "luarocks.nvim" },
        config = function()
            -- ...
        end
    }
    
  • 配置

    require('image').setup({
      integrations = {
        markdown = {
          resolve_image_path = function(document_path, image_path, fallback)
            -- document_path is the path to the file that contains the image
            -- image_path is the potentially relative path to the image. for
            -- markdown it's `![](this text)`
    
            -- you can call the fallback function to get the default behavior
            return fallback(document_path, image_path)
          end,
        }
      }
    })
    

网页摘录和after-reading#

有一个JavaScript我一直在用,可以选定一个网页的特定内容,在翻译和剪藏的时候挺有用,同时也微微提升了一点阅读体验。我用 DeepLX 通过沉浸式翻译如果单次翻译内容太多会报错和限制,Deepl的中文翻译还是吊打其他的。

下面的代码保存到书签,单击书签就可以了:

javascript:(function(){var e=document.body;const n=document.head.appendChild(document.createElement("style"));n.textContent=".mainonly { outline: 2px solid red; }";const t=CSS.supports("selector(:has(*))");function o(n){n instanceof HTMLElement&&(e.classList.remove("mainonly"),(e=n).classList.add("mainonly"))}function s(e){o(e.target)}function a(o){if(o.preventDefault(),t)n.textContent=":not(:has(.mainonly), .mainonly, .mainonly *) { display: none; }";else{n.textContent=":not(.mainonly *, .mainonly-ancestor) { display: none; }";var s=e;do{s.classList.add("mainonly-ancestor")}while(s=s.parentElement)}l()}function i(n){n.preventDefault(),n.deltaY<0?o(e.parentElement):o(e.firstElementChild)}function l(){document.removeEventListener("mouseover",s),document.removeEventListener("click",a),document.removeEventListener("wheel",i)}document.addEventListener("mouseover",s),document.addEventListener("click",a),document.addEventListener("wheel",i,{passive:!1}),document.addEventListener("keydown",(function o(s){if("Escape"===s.key&&(n.remove(),document.removeEventListener("keydown",o),l(),e?.classList.remove("mainonly"),!t))for(const e of document.getElementsByClassName("mainonly-ancestor"))e.classList.remove("mainonly-ancestor")}))}())

Omnivore,开源免费,支持RSS和Newsletter,可以自动同步高亮标注、摘录到Obsidian和Logseq。这部分用来做 流动知识的检索,文档+RAG现在的项目也越来越多了。用AI来过滤、总结这些信息流很巴适。

简单写了一个python用来直接获取高亮摘录并与本地LLM问答:RSS 订阅和本地 LLM 结合的初步尝试 - 流动知识检索 | Vandee’s Blog

欢迎来到 n8n 中文教程 | 简单易懂的现代魔法这里给出了一个 omnivore 到 notion 的 workflow。

最近还是入坑了 Readwise

Tools#
  • 网站:https://jina.ai/reader/

    Github:https://github.com/jina-ai/reader

    它可以提取网页内容并转换为markdown格式,还支持直接搜索,支持API。

    Reader does two things:

    • Read: It converts any URL to an LLM-friendly input with https://r.jina.ai/https://your.url. Get improved output for your agent and RAG systems at no cost.
    • Search: It searches the web for a given query with https://s.jina.ai/your+query. This allows your LLMs to access the latest world knowledge from the web.

    添加下面代码到书签保存,点击书签就可以提取网页内容到markdown格式了。

    javascript: var currentUrl = window.location.href;var newUrl = "https://r.jina.ai/" + currentUrl;window.open(newUrl, '_blank');window.history.pushState({}, '', currentUrl);
    

    以这个工具为跳板,就可以干许多有意思的事情了。例如在Cloudflare的workers里通过它的url请求来同时获取多个网页的md文档内容,再白嫖Cloudflare的AI整理、分类、总结文档,存入Cloudflare的KV或D1。

    进阶版: md.dhr.wtf/

  • Markdownload,一个提取网页内容到md格式文档的浏览器插件。推荐沉浸式翻译作者修改的版本markdownload

  • JimmyLv/BibiGPT-v1 这个项目可以自己部署,总结B站、YouTube、抖音等等的视频内容,挺好用。

  • u-Sir/drag-to-preview,最近发现一个Firefox浏览器插件,可以选中网页链接拖拽弹出单独的窗口预览网页,在窗口外区域鼠标点击即可关闭,很符合我的胃口。

PKM with LLM#

本地笔记的优势除了隐私性,再就是,数据在自己手上,就可以干很多事。把本地的笔记文档结合LLM做思维拓展,基于个人思维习惯更精确的问答。如果说有时候双链不足以串联整个思维链条,下面这些工具如虎添翼。

看到一个大佬已经用Ollama、RAG在MacBook Pro M3 Max上实现了基于obsidian的个人AI知识助手:个人本地 (Llama3 8B) LLM 使用 WhatsApp + Obsidian 数据扩展 - Byte Tank,和我的思路基本一致。

简单写了一个python用来直接获取高亮摘录并与本地LLM问答:RSS 订阅和本地 LLM 结合的初步尝试 - 流动知识检索 | Vandee’s Blog

工具推荐#

Ollama#

Ollama 可以本地部署大语言模型,目前GitHub 69.1k star,一直在用。obsidian,Emacs,Logseq都支持。如果说Cloudflare是赛博活菩萨,那ollama就是LLM筋斗云。

$ ollama serve 即可后台启动ollama服务。

  • docker 部署:

    # Docker Pull Command
    
    docker pull ollama/ollama
    
    # CPU only
    
    docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
    
    # AMD GPU
    # To run Ollama using Docker with AMD GPUs, use the rocm tag and the following command:
    
    docker run -d --device /dev/kfd --device /dev/dri -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama:rocm
    
    # Run model locally
    # Now you can run a model:
    
    docker exec -it ollama ollama run llama3
    
    # 有时候 Docker 容器无法访问 Ollama 服务。localhost 通常指的是容器本身,而不是主机或其他容器。要解决此问题,需要将 Ollama 服务暴露给网络。
    
    # 在Mac上设置环境变量
    
    # 如果 Ollama 作为 macOS 应用程序运行,则应使用以下命令设置环境变量launchctl:
    # 通过调用launchctl setenv设置环境变量:
    
    launchctl setenv OLLAMA_HOST "0.0.0.0"
    
    # 重启Ollama应用程序。
    # 如果以上步骤无效,可以使用以下方法:
    # 问题是在docker内部,你应该连接到host.docker.internal,才能访问docker的主机,所以将localhost替换为host.docker.internal服务就可以生效了:
    
    http://host.docker.internal:11434
    

ollama支持主流的开源模型如llama3,所有支持模型可查看:

Model library:Ollama supports a list of models available on ollama.com/library

相关文章:

Open WebUI#

Open WebUI 提供一个WebUI运行本地LLM,目前GitHub 31k star。良好的兼容ollama,聊天数据本地保存,可本地导入文档做RAG查询。

社区提供了插件和功能,质变的是:可以让本地LLM搜索web的内容。

其他主要功能

  • 📚 本地 RAG 集成:通过突破性的检索增强生成 (RAG) 支持深入探索聊天交互的未来。此功能将文档交互无缝集成到您的聊天体验中。您可以将文档直接加载到聊天中或将文件添加到文档库中,在查询之前使用 # 命令轻松访问它们。

  • 🔍 RAG 的网络搜索:使用 SearXNGGoogle PSEBrave SearchserpstackserperSerplyDuckDuckGoTavilySearch 并将结果直接注入您的聊天体验中。

  • 🌐 网页浏览功能:使用 # 命令后跟 URL,将网站无缝集成到您的聊天体验中。此功能允许您将网络内容直接合并到您的对话中,从而增强交互的丰富性和深度。

docker部署相当简单

  • CLI: docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

  • Docker compose: https://github.com/open-webui/open-webui/blob/main/docker-compose.yaml

    services:
      ollama:
        volumes:
          - ollama:/root/.ollama
        container_name: ollama
        pull_policy: always
        tty: true
        restart: unless-stopped
        image: ollama/ollama:${OLLAMA_DOCKER_TAG-latest}
    
      open-webui:
        build:
          context: .
          args:
            OLLAMA_BASE_URL: '/ollama'
          dockerfile: Dockerfile
        image: ghcr.io/open-webui/open-webui:${WEBUI_DOCKER_TAG-main}
        container_name: open-webui
        volumes:
          - open-webui:/app/backend/data
        depends_on:
          - ollama
        ports:
          - ${OPEN_WEBUI_PORT-3000}:8080
        environment:
          - 'OLLAMA_BASE_URL=http://ollama:11434'
          - 'WEBUI_SECRET_KEY='
        extra_hosts:
          - host.docker.internal:host-gateway
        restart: unless-stopped
    
    volumes:
      ollama: {}
      open-webui: {}
    
  • Updating | Open WebUI

    # Manual Update 手动更新
    
    # Pull the Latest Docker Image:
      拉取最新的 Docker 镜像:
    docker pull ghcr.io/open-webui/open-webui:main
    
    # Stop and Remove the Existing Container:
      停止并删除现有容器:
      This step ensures that you can create a new container from the updated image.
      此步骤确保您可以从更新的映像创建新容器。
    
    docker stop open-webui
    docker rm open-webui
    
    # Create a New Container with the Updated Image:
      使用更新后的映像创建一个新容器:
      Use the same docker run command you used initially to create the container, ensuring all your configurations remain the same.
      使用最初用于创建容器的相同 docker run 命令,确保所有配置保持不变。
    
    docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
    
    This process updates your Open WebUI container to the latest version while preserving your data stored in Docker volumes.
    此过程将您的 Open WebUI 容器更新到最新版本,同时保留存储在 Docker 卷中的数据。
    

Ref:Open WebUI 官方手册

Dify#

Dify是一个支持本地部署的,兼容多方LLM平台(当然也支持ollama的本地模型)的,LLM-agent构建系统,约等于开源的扣子,目前GitHub 38.2K star。把本地的笔记加入到知识库,再用模板构建一个问答机器人助手,就可以简单的把本地笔记和LLM链接起来了。当然它可以做的太多了,爬取rss订阅、爬取新闻分类摘要,还在研究。

bestblogs.dev 就是一个很好的Dify案例: BestBlogs.dev 基于 Dify Workflow 的文章智能分析实践

官方简介

你或许可以把 LangChain 这类的开发库(Library)想象为有着锤子、钉子的工具箱。与之相比,Dify 提供了更接近生产需要的完整方案,Dify 好比是一套脚手架,并且经过了精良的工程设计和软件测试。

重要的是,Dify 是开源的,它由一个专业的全职团队和社区共同打造。你可以基于任何模型自部署类似 Assistants API 和 GPTs 的能力,在灵活和安全的基础上,同时保持对数据的完全控制。

我们的社区用户对 Dify 的产品评价可以归结为简单、克制、迭代迅速。

——路宇,Dify.AI CEO

  • docker部署

    克隆 Dify 源代码至本地。

    git clone https://github.com/langgenius/dify.git
    

    进入 Dify 源代码的 docker 目录,执行一键启动命令:

    cd dify/docker
    cp .env.example .env
    docker compose up -d
    

Ref:

Kotaemon#

从发现到现在差不多就一个多星期,GitHub直接飙升到10.9K star,win、mac、Linux都支持。边用边学习它的RAG构建框架,支持ollama,支持docker。

GitHub: https://github.com/Cinnamon/kotaemon

官方介绍:

This project serves as a functional RAG UI for both end users who want to do QA on their documents and developers who want to build their own RAG pipeline.

  • For end users:
    • A clean & minimalistic UI for RAG-based QA.
    • Supports LLM API providers (OpenAI, AzureOpenAI, Cohere, etc) and local LLMs (via ollama and llama-cpp-python).
    • Easy installation scripts.
  • For developers:
    • A framework for building your own RAG-based document QA pipeline.
    • Customize and see your RAG pipeline in action with the provided UI (built with Gradio).
    • If you use Gradio for development, check out our theme here: kotaemon-gradio-theme.

Thanks#

这个手册会持续更新,如果对你有所帮助,我会很开心。

另见:PKM(个人知识管理)构建手册 - Emacs

这里是我日常使用的实用小工具: 实用小玩意收集

Imagining and creating!



See also