UNagent
approvedby UNcore
Mobile-first AI assistant: lightweight on phones/tablets, heavy-duty task capabilities on desktop via hermes ACP. - This plugin has not been manually reviewed by Obsidian staff.
UNagent
给 Obsidian 用户的移动优先 AI 助手——手机平板上轻量自足,桌面上同样完整可用。
English: UNagent is a mobile-first AI assistant plugin for Obsidian — lightweight and self-contained on phones and tablets, and equally complete on desktop. It relies on in-plugin JavaScript + remote HTTP only (native
fetchwith hand-written SSE), has no LLM SDK dependencies, and runs no local processes on mobile.
插件 id 是 unagent、显示名是「UNagent」,作者 UNcore。核心是「纯插件内 JS + 远程 HTTP」:不依赖任何 LLM SDK(原生 fetch + 手写 SSE),移动端零本地进程;桌面端在此之上只多一条本地命令执行(run_command,永远强制确认)。
5 分钟上手(BYO key:自带你自己的模型 Key)
第一步:安装
目前手动安装。把构建产物三件套放进你的 vault:
<你的 vault>/.obsidian/plugins/unagent/
├── main.js
├── manifest.json
└── styles.css
然后 设置 → 第三方插件 → 启用「UNagent」。左侧栏 ✨ 图标或命令面板「Open UNagent chat」打开对话框。
第二步:添加模型档案
设置 → UNagent →「模型」标签页 → 点「模型厂商」标题行右侧的 「+ 添加厂商」,在弹窗里填三项关键配置:
- API 协议(下拉):选你的服务商协议(OpenAI 兼容 / Anthropic 等),选完会自动回填对应的默认地址;
- API 地址:即 Base URL,接口地址不含
/chat/completions等路径后缀(选协议时已预填,可任意改写); - API 密钥:你的服务商 Key(右侧「眼睛」按钮可显示/隐藏)。
再往下是模型列表:输入模型名回车添加(会自动从 API 拉取联想),保存即生效。可以同时添加多个厂商、多种协议并存;对话里发 /model 随时切换本会话模型。
第三步:开聊
直接说需求就行,例如「搜索关于读书的笔记并总结一下」「给《项目计划》加上 #work 标签」。AI 会流式回答、按需调用工具读写你的笔记与文件夹;默认/自动模式下,新建/编辑类工具执行后会在文件卡片给出接受/拒绝(编辑类带 diff),移动与删除在执行前确认,其中 delete_note、delete_folder 和 run_command 无论审批模式都强制确认。笔记编辑/删除、文件夹创建/删除会留撤销快照,改错了点顶部「撤销」。
到这里插件已完整可用——不配 MCP,功能一样不缺。
能力清单(移动 + 桌面一致)
| 能力 | 说明 |
|---|---|
| 流式对话 | 逐字输出、可随时停止、错误有友好提示可重试;多厂商多协议档案并存,/model 切换会话模型,/think <档位> 直接设思考强度(裸 /think 打开同一个面板),/search <档位> 直接设检索模式(裸 /search 同样打开那个面板)。输入框下方一行:左半边是对话标题(点开对话管理面板),右半边是模型键(点开「模型 · 思考强度与检索模式」面板,最上面是思考强度与检索模式两个档位控件,下面直接选模型);手机上键盘弹起时这一行整体隐藏,键盘上方只剩输入框 |
| 检索模式 | 会话级旋钮,五档:关闭搜索(既不翻库内笔记也不联网)/ 自动(默认,由 AI 自己决定)/ 搜索笔记 / 搜索网页 / 搜索网页和笔记。档位是硬边界:对应通道的检索工具真的不会进入这一轮的工具表(读写笔记的工具不受影响),同时系统提示里写明本轮要求。入口两个:输入框下方模型键的面板顶部档位控件,或 /search <档位>。搜索网页走远程 MCP 联网服务(插件已内置 exa / bailian-websearch),模型档案勾了「联网搜索」时还会额外注入服务端内置联网;另有内置的 fetch_url 打开用户给出的网址(同样属网页通道,所以这两档下不可用——搜索服务给的是标题与链接,打不开页面) |
| 21 个工具(桌面 22) | AI 可读、搜、写你的笔记与文件夹(清单见下,共 22 个);默认/自动模式下新建/编辑类执行后走文件卡片审批,移动/删除执行前确认,其中删除文件夹、删除笔记与本地命令永远强制确认;笔记编辑/删除、文件夹创建/删除可撤销;桌面端另有本地命令执行(run_command,移动端不可见,故移动端为 21 个) |
| 技能 (Skills) | 纯提示文本的 SKILL.md 指南,//技能名 调用或 AI 按需 load_skill 载入;绝不执行代码。官方技能的目录行(每轮进上下文的那一行)中英双语,英文界面自动取英文版;用户自建技能用 description_en 与兄弟文件 SKILL.en.md 提供英文版 |
| 混合检索 | 关键词 + 元数据为主通道;语义检索可选(远程 embedding + 本地向量缓存,见下) |
| 生图 | generate_image 文生图存入 vault,可插入笔记/设为封面;存放目录可配置(设置 → 通用 →「AI 生图目录」) |
| 电子书阅读 | book_read 读 .epub / .fb2 / .mobi / .azw3 / .txt:先取书目与目录,再按章输出 Markdown(超长分段续读);配 book-read / ebook-workflows 技能做摘录、翻译、整本导入;DRM 加密书与 .azw/.pdf 不支持(引导 Calibre 转换)。只读:改书/拆章/改封面属于二期,请用 Calibre |
| 网页阅读 | fetch_url 打开一个 http/https 网页并转成 Markdown(HTML 转换复用电子书那套,JSON/纯文本原样返回)。搜索服务给的是标题与链接、打不开页面,这一条补的正是那一步。长页只保留开头并如实报出省略了多少;二进制内容与 file: / data: 之类一律拒绝;响应不带 content-type 时不猜、直接说打不开(不把可能的乱码当正文)。属网页通道——「关闭搜索 / 只搜索笔记」两档下不可用;不拦内网地址,见「边界与安全」 |
| 记忆与沉淀 | agent.md / user.md / memory.md 三个可见文件 + 完全由你发起的显式记忆(save_memory);一段工作结束时用 /distill 沉淀成一篇带 [[链接]] 的笔记(见下) |
| 文字引用 | 编辑器 / 画布 / 表格 / 内置浏览器里选中文字按 Option+Z(Alt+Z,或命令面板「引用选中文字到 AI 输入框」),一键跳到 AI 输入框并自动带上「来源 + 选中文字」引用;网页选区带页面地址(选中处是链接时连带链接本身);没有选中内容(或引用失败)时按下也会直接聚焦输入框,可当纯聚焦快捷键用 |
| 对话管理 | 自动保存进 vault、重启恢复、多层分支(/branch)、任意轮回溯(/rewind)、/compact 压缩 |
| MCP(最小形态) | 仅远程 streamableHttp + tools 面,见「边界」一节 |
工具清单(22 个)
| 工具 | 作用 | 审批 / 风险 |
|---|---|---|
search_notes | 关键词 + 元数据(标签/文件夹)检索;只带文件夹过滤时即文件夹浏览(返回子文件夹) | 否 |
semantic_search | 语义检索(远程 embedding,本地只存向量缓存) | 否 |
library_index | 库目录(启发式摘要缓存) | 否 |
list_folder | 列文件夹内容:子文件夹、笔记,以及图片/PDF/电子书等非笔记文件;可按名字过滤、可下钻 2-5 层、分页续读。属笔记检索通道,故「关闭搜索 / 只搜索网页」两档下不可用 | 否 |
read_note | 读取笔记内容(含元数据,超长分段续读) | 否 |
book_read | 读取库内电子书(.epub / .fb2 / .mobi / .azw3 / .txt):书目 + 目录,按章读为 Markdown,长章分段续读;只读,DRM 加密书不支持(建议 Calibre 去 DRM) | 否 |
create_note | 新建笔记(支持 frontmatter;也可建 .canvas/.excalidraw/.base) | 默认/自动模式下执行后卡片审批(拒绝则移入回收站) |
create_folder | 新建文件夹(缺失的父级一并创建;文件夹不是笔记,不加扩展名) | 默认/自动模式下执行后卡片审批(拒绝则删掉刚建的文件夹,非空时不删;可撤销) |
edit_note | 追加 / 替换章节 / 全文替换(匹配失败会报最相似片段) | 默认/自动模式下执行后卡片审批(diff / 接受 / 拒绝;可撤销) |
update_frontmatter | 增删改 frontmatter 字段;数组字段可合并去重(加标签用它) | 默认/自动模式下执行后卡片审批(接受 / 拒绝;可撤销) |
rename_or_move | 改名/移动(自动更新引用) | 默认/自动模式下执行前确认 |
rename_or_move_folder | 重命名/移动整个文件夹(其中笔记随之移动;链接是否改写取决于你的「自动更新内部链接」设置) | 默认/自动模式下执行前确认;拒绝搬进自己的子目录与数据文件夹 |
delete_note | 移入回收站 | 执行前强制确认;可撤销 |
delete_folder | 删除整个文件夹及其内容(移入回收站)。非空必须显式 recursive: true | 执行前强制确认;快照有上限(200 文件 / 500 KB,且必须全是文本文件),超出时不记录快照并明确告知「本次无法在插件内撤销」 |
run_command | 本地命令/脚本执行(仅桌面;库外计算专用,不碰库内文件) | 执行前强制确认;输出保尾部并报告截断规模 |
fetch_url | 打开 http/https 网页转成 Markdown(HTML→Markdown;JSON/纯文本原样返回)。长页只保留开头并报出省略规模;非文本内容与 file:/data: 拒绝。属网页通道,「关闭搜索 / 只搜索笔记」两档下不可用 | 否(只读;但会把该网址发给对应站点) |
generate_image | 文生图并存入 vault(目录可配置:设置 → 通用 →「AI 生图目录」,留空 = 数据文件夹下的 images/) | 否 |
mcp_admin | 远程 MCP 服务增/改/删(action=add/update/remove) | 联网发现前确认;删除前确认;官方服务不可删 |
load_skill | 按名载入某个技能的完整指南 | 否 |
save_memory | 写入 memory.md(长期记忆)/ user.md(用户画像) | 否 |
todo_write | 任务清单(长任务的进度可视化) | 否 |
ask_user | AI 主动向你提问 | 否 |
检索怎么工作(如实版)
检索以关键词 + 元数据(metadataCache)为主通道,CJK 友好。可选开启语义通道:笔记按标题切块 → 远程 embedding API 算向量 → 向量只是远程结果的本地缓存(存数据文件夹 .retrieval/)→ 暴力余弦 top-k。embedding 计算不在本地发生,不引入 ANN 索引与重排序模型。embedding 模型复用统一的厂商体系(模型能力勾「向量化(检索)」),未配置时零启动成本。
库内检索(search_notes / semantic_search / library_index)只覆盖 Markdown 笔记——图片、PDF、电子书等非笔记文件对它们不可见,「没搜到」不等于「不存在」;被排除的文件夹也默认不在范围内。要枚举文件夹内容(含非笔记文件)用 list_folder。这一条只写在系统提示里一次,不再散落在各工具描述里。联网那一半:搜索靠 MCP 服务,打开具体页面靠 fetch_url。
记忆与沉淀
数据文件夹(默认 AI 助手/,可见可编辑)里三个文件:
| 文件 | 职责 | 注入方式 |
|---|---|---|
agent.md | 助手人设与工作守则 | 整篇注入系统提示 |
user.md | 用户画像 | - 开头条目注入 |
memory.md | 长期记忆 | - 开头条目注入 |
记忆只有一条路径,且完全由你发起:你说「记住 xxx」,AI 用 save_memory 写入(带提示注入防护与额度),下次新对话生效。没有任何自动复盘、没有「要不要记住」的弹窗——需要长期记住什么由你决定。
沉淀解决的是另一件事:一段工作做完之后,怎么让下次不用从零开始。
/learn把一次对话结晶成一个可复用技能(纯提示文本的操作指南)。/distill <要沉淀什么>把这段工作沉淀成一篇笔记,落在数据文件夹的digests/下,并在digests/index.md追加一行索引。笔记带 frontmatter、一行摘要,以及四段固定结构(结论/决定、动过的笔记、未完成/下次从这里开始、关键上下文),其中「动过的笔记」是指向相关笔记的[[链接]]——写链接前会先核对路径真实存在,核对不了的写成纯文本,避免死链。下次做同一件事时,从那篇接着走。
注入是按需的:只有当你真的用过 /distill(digests/index.md 存在)时,系统提示里才会多出一句「沉淀索引在哪、问起旧事先读它」;没用过就一个字都不提,零额外开销。沉淀不需要你维护,它只是某个时点的快照——当前真相永远在你自己的笔记里。
上下文成本怎么控制(如实版)
每轮要重发的输入 = 系统提示 + 工具 schema + 对话历史 + 工具结果。四者都有上限:
| 通道 | 机制 | 数值 |
|---|---|---|
| 工具 schema | 固定开销,芯片按注册表里的真实工具量出 | 22 个内置工具 ≈ 5,463 tokens(estimateToolSchemaTokens 实测,2026-09-14 加 fetch_url 后;此前 21 个工具为 5,399、17 个为 4,215);已注册的 MCP 工具另计 |
| 技能目录(系统提示里的一部分) | 目录随本轮工具表与运行时能力裁剪:被摘掉的能力连名字都不出现 | 26 条官方技能 ≈ 1,292 tokens(能力全开时实测,2026-09-14,平均约 50/条) |
| 工具结果 | 单条预算 + 每轮累计预算;自报分窗的工具不裁 | 单条 6,000;累计 min(24,000, 窗口×15%);用掉后单条降到 1,500 |
| 对话历史 | 非破坏性预算:只缩减发给模型的那份,可见记录 / 已落盘对话 / 分支都不动 | min(24,000, 窗口×30%);开了 prompt 缓存则退化为纯溢出兜底(窗口×70%) |
| 系统提示缓存 | Anthropic 协议把系统提示标成可缓存前缀(默认开,厂商档案可关) | 命中约 0.1x 计费,写入约 1.25x |
几点如实说明:
- 自报分窗的工具刻意不裁:一次 2 万字符的笔记分 4 次 5 千读,输入是 5k+10k+15k+20k = 50k;一次读完只发 20k。截断它的窗口反而更贵,还会让
nextOffset变成谎话、静默跳过中间那段——所以read_note/book_read/list_folder/load_skill的结果完全跳过预算(list_folder自带limit ≤ 200的窗口上限,load_skill一份技能正文一个窗口、超长续读)。load_skill是这条规则里唯一「不是数据而是指令」的一个:被截断的指南更糟——模型不会发现自己少了半份,只会照着前半段做。 - 技能目录会随能力消失(不只是工具):技能声明的运行时能力不存在时,目录里连名字都不出现——没配生图模型(
image-generator)、库里没有可读电子书(book-read/ebook-workflows)、Obsidian 版本不支持.base(obsidian-bases,Bases 是 1.9.0 的核心功能)。Dataview/Excalidraw这两个依赖社区插件的技能不做自动门控(可靠侦测要用未公开 API),改为在目录行里写明依赖,并在设置页可自由开关。 - 历史预算不是固定 24,000:未开缓存时取
min(24,000, 窗口×30%),所以 8K 模型实际只有约 2,400;长窗口模型的常见短对话通常够不到。它的价值是给真正长的会话兜住溢出、止住历史越长每轮重发越贵的二次增长。要主动压缩整段上下文,仍然敲/compact。 - 不做自动压缩:
/compact会替换可见的消息列表并落盘,自动触发等于静默删掉你的对话记录,所以只在你敲命令时发生。 - 缓存可能静默失效:缓存是前缀匹配,前缀里任何一处变化(包括工具集)都会让它整体失效。开诊断日志后每轮会有一条
cache read=… write=…,读侧连续为 0 就说明没命中。 - 芯片:首轮之前是粗估(系统提示 + 工具 schema + 消息),首轮之后一律用服务商返回的真实用量。
MCP 边界(如实描述,不夸大)
只做远程 streamableHttp 传输 + tools 面:initialize / tools/list / tools/call 三个方法,纯 fetch 手写 JSON-RPC、零 SDK,单次请求超时 10 秒。不做 stdio / WebSocket / OAuth / resources / prompts / sampling / 会话恢复。工具总数上限 8 个;单条文本结果超过 2 万字符时保留最后 2 万字符,并返回 keptChars / originalLength,非文本片段数量也会明确上报。设置 →「MCP」标签页添加服务,Agent 级可再按代理开关。
内置官方预设服务(bailian-websearch 联网搜索、exa 搜索),key 一律留空不在插件里内置——需在「MCP」设置里编辑对应服务、填入你自己的 Authorization 再「测试并刷新工具」才能调用;官方服务不可删除,只能开关/编辑。
MCP 工具是否被标成破坏性,只看服务端在 tools/list 里自报的 annotations;是否真正弹执行前确认,还受全局审批模式约束。分三档:
destructiveHint: true且未声明readOnlyHint: true:按破坏性工具处理;默认与「自动(编辑放行)」模式下执行前确认,「免询」模式仍会放行;readOnlyHint: true:按只读工具处理,不确认;- 未声明:维持不确认,但工具描述会明确告诉模型「未声明不等于只读,调用前先向用户说明」。
这些 annotation 是远程服务端的自报提示,不是可信证明。MCP 工具没有 vault 读写句柄,也不进撤销栈;结果文本是不可信输入,应只接入你信任的服务。
完全配置版(规划中)
现在是 BYO key:你自己去各家申请 Key、自己填。我们规划中会提供一个零配置的托管版本——统一 API、开箱即用,任何设备不必折腾密钥。具体形态与时间待定,本文档不承诺日期;当前版本的一切能力它就是它的全部。
边界与安全(先看这段再用)
- API Key 明文存储:所有 Key 以明文存在 vault 的
data.json里(v1 从众做法)。不要把data.json提交到公开仓库、不要放进会公开同步的目录。 - 技能是提示注入面:技能正文会原样注入 AI 的上下文,等同于提示词——只安装你信任来源的技能。技能永远是纯提示文本、绝不执行代码,也不能绕过审批:默认/自动模式下的编辑类仍落文件卡片,移动/删除仍走执行前确认。
- 删除有双保险:
delete_note与delete_folder永远强制弹窗确认(不受任何「跳过确认」设置影响);删除与编辑会先尝试留全文快照,成功后对话框顶部「撤销」可还原(撤销栈落盘,重启不丢)。文件夹删除的快照有上限:200 个文件 / 500 KB,且子树上必须全是文本文件——文件过多、含图片/PDF/电子书、或有文件读不出来时不记录快照,结果、卡片与提示都会明确写出「本次无法在插件内撤销,只能从回收站找回」,绝不假装可撤销。(另外:超过 100 KB 的快照仍可在本次会话内撤销,但超出落盘上限,重启后该条目会被丢弃。) - MCP 工具与结果都不可信:远程调用只按服务端自报 annotation 标记是否破坏性,未声明时不会弹确认;即使标记为破坏性,「免询」模式也会放行。输出会进入模型上下文,插件不授予它 vault 句柄,也没有撤销。别接入不受信服务,也不要把 MCP 输出当成可信指令。
fetch_url会把网址发出去、把页面读进来:它是只读工具、不弹确认(与 MCP 工具的调用一致——审批留给改动本地数据的操作),但要知道:请求会到达那个站点(对方能看到你的 IP 与请求头,插件不伪装 User-Agent),页面文本会进入本次对话上下文(因此也可能被一起发给你的模型服务商)。插件不拦内网/本地地址(http://127.0.0.1:…、192.168.*之类),也不做「哪些地址算内网」的穷举判断——这是本地优先工具的有意取舍,不是遗漏;只让 AI 打开你信得过的网址。另外它只处理文本类内容,二进制(PDF/图片/压缩包)按 content-type 拒绝。- 第三方解析组件:电子书解析层 vendor 自 foliate-js(MIT)与 fflate(MIT),纯 JS 内存解析、移动端一致;来源与版本见
src/vendor/foliate/README.md。
平台差异声明
- 移动端(手机/平板)= 纯插件内 JS + 远程 HTTP:零本地进程、零本地算力(embedding 也走远程),所有核心功能三端一致。
- 桌面专属能力只有一条:
run_command本地命令/脚本执行(库外计算,永远强制确认,绝不用于库内文件)。移动端该入口缺席而非报错;除此之外桌面与移动没有任何功能差异。
开发(给改代码的人)
cd unagent
npm install
npm run dev # esbuild watch,自动同步产物到测试 vault
npm run build # tsc strict 类型检查 + esbuild 生产构建 → main.js / manifest.json / styles.css
npm test # jest 全量
产物体积受关注(main.js 当前 894,761 bytes,约 874 KiB,2026-09-13 加文件夹工具后 npm run build 实测;此前记为 832,118 bytes);每次构建后 grep 产物确认无隐藏依赖泄漏(pglite / lexical / framer-motion / langchain 须全 0)。
License
Source Available License — 商用需授权。详见 LICENSE。
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.