diff --git a/site/README.md b/site/README.md
index 07f0a68..dac1506 100644
--- a/site/README.md
+++ b/site/README.md
@@ -107,14 +107,41 @@ print(im.mode) # RGB —— 没有 alpha 通道
结论:**这类素材要么重新出一张带透明通道的图,要么就不放**。
现在 Hero 是单栏文字,版面反而更稳。
-## 改内容前请核对事实
+## 插件卡片
-页面上的数字(外部插件数、工具数、代码行数、适配器数)都是**实测值**,
-不是估的。改动前建议重新取一遍:
+「开箱可用的插件」一节里,每个徽章都是**按钮**:
+
+- 点一下展开该插件卡(名称 / 版本 / 用途),再点收起
+- 可同时展开多个
+- 末尾「展开全部 N 个」一次全开
+
+数据来自各插件的 `plugin.json`,不是手写的:
```bash
-ls /home/newqqagent/plugins/ | grep -v hmap | wc -l # 外部插件
-grep -oE '[0-9]+ tools' $(ls -t /home/newqqagent/log/*.log | head -1) | tail -1
-ls internal/lua/adapters/*.lua | wc -l # LLM 适配器
-find . -name '*.go' -not -path './.git/*' -not -path './.go/*' -not -path './third_party/*' | xargs wc -l | tail -1
+cd /home/newqqagent/plugins
+for d in */; do [ -f "$d/plugin.json" ] && python3 -c "
+import json;d=json.load(open('$d/plugin.json'))
+print(d.get('name'), d.get('version'), d.get('description','')[:60])"; done
+```
+
+## 改内容前请核对事实
+
+页面上的数字都是**实测值**。**注意「在跑插件 36」是加载总数**
+(16 内置 + 20 外部),不是"外部插件数" —— 早期版本写成「36 外部插件」是错的,
+已更正。改动前建议重新取一遍:
+
+```bash
+# 在跑插件(以日志里实际 loaded 的为准,比数目录可靠)
+grep -h '\[plugin\] loaded:' $(ls -t /home/newqqagent/log/homed_*.log | head -1) \
+ | sed 's/.*loaded: //' | sort -u | wc -l
+
+# 工具数(同上,日志里注册后的真实值)
+grep -ohE '[0-9]+ tools' $(ls -t /home/newqqagent/log/homed_*.log | head -1) | tail -1
+
+# Go 代码行(排除第三方与鸿蒙工程)
+find . -name '*.go' -not -path './.git/*' -not -path './.go/*' \
+ -not -path './third_party/*' -not -path './cmd/ohos/*' | xargs cat | wc -l
+
+# LLM 适配器(注意 server.lua 是网关脚本,不是厂商适配器,故为 9)
+ls internal/lua/adapters/*.lua | xargs -n1 basename
```
diff --git a/site/index.html b/site/index.html
index 062a23a..564726d 100644
--- a/site/index.html
+++ b/site/index.html
@@ -482,6 +482,174 @@
.js-reveal { opacity: 1; transform: none; transition: none; }
}
+
+ /* ── 插件:徽章可点,下方显示详情 ── */
+ .badges {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-top: 1rem;
+ }
+ .badge {
+ /* 重置 button 默认样式 */
+ appearance: none;
+ font: inherit;
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ padding: 0.34rem 0.7rem;
+ border-radius: var(--radius-pill);
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ color: var(--text-dim);
+ cursor: pointer;
+ transition: border-color 0.22s var(--ease), color 0.22s var(--ease),
+ background 0.22s var(--ease), transform 0.22s var(--ease-back),
+ box-shadow 0.22s var(--ease);
+ }
+ .badge:hover {
+ border-color: var(--border-hi);
+ color: var(--text);
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-md);
+ }
+ .badge:focus-visible {
+ outline: 2px solid var(--brand-sky);
+ outline-offset: 2px;
+ }
+ /* 选中态:淡底 + 主色描边(实心渐变太重,20 个齐亮像一堵墙) */
+ .badge[aria-pressed="true"] {
+ /* 回退:不支持 color-mix 的浏览器给淡青底 */
+ background: rgba(6, 182, 212, 0.14);
+ background: color-mix(in srgb, var(--brand-cyan) 12%, var(--bg-card));
+ border-color: var(--brand-cyan);
+ color: var(--text);
+ }
+ /* 小圆点提示可点 */
+ .badge::after {
+ content: "";
+ display: inline-block;
+ width: 4px; height: 4px;
+ margin-left: 0.4rem;
+ border-radius: 50%;
+ background: currentColor;
+ opacity: 0.45;
+ vertical-align: middle;
+ transition: opacity 0.22s var(--ease);
+ }
+ .badge[aria-pressed="true"]::after { opacity: 1; }
+ /* 「展开全部」:虚线边框,与具体插件徽章区分 */
+ .badge.muted {
+ font-style: italic;
+ color: var(--text-muted);
+ border-style: dashed;
+ }
+ .badge.muted::after { display: none; }
+
+ .pdetail {
+ margin-top: 1.2rem;
+ display: grid;
+ gap: 0.85rem;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ }
+ .phint {
+ grid-column: 1 / -1;
+ margin: 0;
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 0.88rem;
+ padding: 0.85rem 0;
+ border: 1px dashed var(--border);
+ border-radius: var(--radius-md);
+ }
+ .pcard {
+ display: none;
+ position: relative;
+ background: var(--bg-card);
+ border: 1px solid var(--border-hi);
+ border-radius: var(--radius-lg);
+ padding: 1rem 1.15rem;
+ box-shadow: var(--shadow-md);
+ }
+ .pcard.show {
+ display: block;
+ animation: pcardIn 0.4s var(--ease-back);
+ }
+ @keyframes pcardIn {
+ from { opacity: 0; transform: translateY(10px) scale(0.98); }
+ to { opacity: 1; transform: none; }
+ }
+ .pcard-head {
+ display: flex;
+ align-items: baseline;
+ gap: 0.6rem;
+ margin-bottom: 0.5rem;
+ }
+ .pcard-head code {
+ font-size: 0.95rem;
+ font-weight: 700;
+ color: var(--brand-cyan);
+ }
+ .pcard-head .ver {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ font-family: var(--font-mono);
+ margin-left: auto;
+ }
+ .pcard p {
+ margin: 0;
+ font-size: 0.88rem;
+ color: var(--text-dim);
+ line-height: 1.65;
+ }
+ /* 全部展开时的网格 */
+ .pdetail.all {
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ }
+ .pdetail.all .pcard { display: block; }
+ .pdetail.all .phint { display: none; }
+
+ /* ── 统计卡:补一行小字,避免数字孤零零 ── */
+ .stat .sm {
+ display: block;
+ margin-top: 0.25rem;
+ font-size: 0.72rem;
+ color: var(--text-muted);
+ letter-spacing: 0.01em;
+ }
+ .stat .l { line-height: 1.5; }
+
+ /* ── 版面精致化:统一节奏与呼吸 ── */
+ section { padding: 4.5rem 0; }
+ @media (max-width: 860px) { section { padding: 3rem 0; } }
+ h2 { margin: 0 0 0.7rem; letter-spacing: -0.01em; }
+ .sub { max-width: 44rem; }
+ /* 卡片内边距与标题间距收敛一档 */
+ .card { padding: 1.4rem 1.5rem; }
+ .card h3 { margin: 0 0 0.55rem; }
+ .card p { font-size: 0.9rem; }
+ /* 组件表:行高收敛、代码列定宽对齐 */
+ .comp .row { padding: 0.65rem 1rem; }
+ .comp .row code { min-width: 7.5rem; }
+ /* 统计卡内边距 */
+ .stat { padding: 1.25rem 1rem; }
+ .stat .n { margin-bottom: 0.15rem; }
+ /* 三层记忆卡收紧 */
+ .layer .body { padding: 1rem 1.2rem 1rem 0; }
+ .layer h3 { margin-bottom: 0.3rem; }
+ .layer p { margin: 0; }
+
+ /* 分隔:章节之间给一条极淡的线,强化节奏 */
+ section + section > .wrap > h2,
+ section + section > .wrap > .sub {
+ /* 占位:保持选择器有效,视觉由间距承担 */
+ }
+
+ /* 打印:不显示装饰与折叠 */
+ @media print {
+ .aurora, #fx, #progress, .badges, .pdetail { display: none !important; }
+ body { background: #fff; color: #000; }
+ }
+
/* ═══════════════════════════════════════════════════
动效层。全部遵守 prefers-reduced-motion(见文件末脚本)。
原则:**内容默认可读**,动效只是增强,JS 挂了也不影响阅读。
@@ -857,9 +1025,8 @@
- 大多数框架先写功能再补边界。HomeAgent 反过来:先把
- 边界和调度定死,再往上加能力。
+
+ 先定边界与调度,再加能力。
- 内核不直接读写任何外部世界。收发消息、文件操作、网络请求、硬件交互
- 全部由插件经 PluginSDK 实现。于是「内核有多可信」与「某个插件有多安全」
- 成为两个可独立评估的问题。
+ 内核不碰任何外部世界。消息、文件、网络、硬件全部由插件经
+ PluginSDK 实现。于是「内核是否可信」与「某个插件是否安全」
+ 可以分开评估。
- 外部插件编译成普通 Go 二进制,由内核 spawn 为子进程,经
- stdio JSON-RPC(控制)+ 共享内存段(数据)+ 事件环(通知)通信。
- 插件崩溃不影响内核,换掉 plugin.bin 即真热重载。
+ 插件是普通 Go 二进制,由内核 spawn 为子进程,经 stdio JSON-RPC
+ (控制)、共享内存段(数据)、事件环(通知)通信。插件崩溃不牵连内核,
+ 换掉 plugin.bin 即热重载。
- 输入先进调度器,分「排队」与「中断」两类,中断再按“有多不能等”分 L1–L4。
- 高优先级可抢占并保存现场,同级不抢占。L4 只归内核
- —— 所以“停止”按钮真的能立刻停下。
+ 输入分「排队」与「中断」两类,中断再按紧迫度分 L1–L4。
+ 高级可抢占并保存现场,同级不抢占。L4 归内核独占
+ —— 所以「停止」真的能立刻停下。
- 主 agent 执行长任务时,后来的消息不会干等十几分钟:内核把积压交给
- 临时分诊助手 —— 简单的直接办完,需要主 agent 的立刻回
- 「忙碌中,请稍候」。
+ 主 agent 忙长任务时,积压交给临时分诊助手:
+ 能办的直接办,需要主 agent 的立刻回「忙碌中,请稍候」。
+ 不必干等十几分钟。
- 不是一个向量库包打天下,而是三层各司其职、逐层衰减与整合。
- 品牌的蓝 / 青 / 金三色,对应的就是这三层。
+ 不是一个大向量库包打天下,而是三层各司其职、逐层下沉。
+ 蓝 / 青 / 金三色对应的就是这三层。
- 内存中的事件窗口,按与当前输入的相关性评分排序(预训练词向量 → 余弦相似度,
- TF-IDF 回退),并保护最近若干条。低分事件自动下沉。
+ 内存中的事件窗口。按与当前输入的相关性排序(预训练词向量 → 余弦相似度,
+ TF-IDF 回退),最近若干条受保护。低分事件自动下沉。
- JSON + TF-IDF 索引的中间层。支持显式提交与隐式归档;
- 冷数据按时间下沉,蒸馏成三元组进入图谱。
+ JSON + TF-IDF 索引的中间层。支持显式提交与隐式归档,
+ 冷数据下沉后蒸馏成三元组进入图谱。
- SQLite 持久化实体与关系,BFS 遍历召回。蒸馏管道从对话里提取三元组,
- 越用越准;这是“几个月前的事还记得”的落点。
+ SQLite 持久化实体与关系,BFS 遍历召回。三元组从对话中蒸馏而来,
+ 越用越准 —— 「几个月前的事还记得」靠的就是这层。
- 图片/音频不是附件,而是三层里的一类节点:内容寻址存储(相同字节只存一份)、
+ 图片、音频不是附件,而是三层里的一类节点:内容寻址(相同字节只存一份)、
引用计数 GC(有引用绝不删)。
- 视觉模型生成的描述以 [mime digest] 描述 写进纯文本记忆并参与检索;
- blob 只是可被淘汰的缓存。所以几个月后“那张三色图”仍能搜到。
+ 视觉模型生成的描述以 [mime digest] 描述 写入纯文本记忆并参与检索,
+ blob 只是可淘汰的缓存。所以几个月后「那张三色图」仍搜得到。
- 文本与图像落在同一向量空间(默认 Chinese-CLIP),
- 因此可以用一句话搜图,也可以用图找相关记忆。
+ 文本与图像落在同一向量空间(默认 Chinese-CLIP):
+ 可以用一句话搜图,也可以用图找记忆。
- 输入先过调度器,再进处理管道;管道有 7 个阶段钩子,插件可在此改写或短路。
+ 输入先过调度器,再进处理管道。管道有 7 个阶段钩子,插件可改写或短路。
四个不那么常见的设计决定
- 内核零 IO
插件是独立进程
输入是有级别的
忙时有人顶班
三层记忆:从「刚才说了什么」到「三年前说过什么」
① Context工作窗口
② Document文件记忆
③ Graph图数据库
媒体也是一等节点
描述才是持久语义
统一多模态空间
一条消息进来之后
16 内置 + 20 外部
实测注册数
不含第三方
另有 1 个网关脚本
- 依赖 Go 1.25+ 与 CGo(sqlite3)。内核需 Linux —— Windows 上 - homed 跑在 WSL2 里,只构建 waiter.exe。 + 依赖 Go 1.25+ 与 CGo(sqlite3)。内核需 Linux;Windows 上 + homed 跑在 WSL2 里,仅需构建 waiter.exe。
- WebUI 与 API 密钥在 http://localhost:8080 配置,持久化在 SQLite。 + WebUI 与 API 密钥在 http://localhost:8080 配置,持久化到 SQLite。
外部插件经 hmapdev 构建为独立二进制,可单独安装、卸载、热重载。
+外部插件经 hmapdev 构建为独立二进制,可单独装卸、热重载。点任意插件看它能做什么。
点上方任一插件查看用途
+a2av1.3.1Agent-to-Agent 协议通信插件,支持双向 A2A 通信:可查询其他 Agent 并回复其请求。提供 HTTP 服务端暴露本 Agent 能力。
+acpv1.2.1Agent Client Protocol 通信插件:充当 ACP 服务端接受其他 Agent 的任务请求,同时提供客户端工具向远程 ACP Agent(如 opencode)发起会话并读取回复
+biliv1.2.0B站视频下载工具,基于 yt-dlp 引擎。支持查看视频清晰度列表、指定格式下载、可配置下载目录。
+browserv2.4.1统一浏览器插件:搜索、HTTP抓取(quick)、无头渲染(normal)、交互式浏览器(interactive/CDP)
+calendarv1.1.0日历事件管理,支持提醒和重复事件
+deepsearchv1.1.2为 agent 提供真正的联网信息检索:本地 SearXNG 聚合多引擎(返回标题/URL/摘要/时间),支持新闻、时间范围、指定引擎;并提供网页正文抽取与「搜索+读前K篇」的深检索
+editdocv2.0.0全能办公插件:新建/读取/编辑/转换 docx·xlsx·pptx·md·csv·txt(基于 python-docx / openpyxl / python-pptx / soffice / pandoc)
+homeagent-mail-bridgev0.2.2HomeAgent 接入 AgentMail:邮件驱动的多智能体协作
+mcv0.1.8把 mineflayer 虚拟 MC 客户端封装为 HomeAgent 的 chan:mc IO 通道(两阶段激活 + 动作工具)
+memov1.1.0待办与备忘录插件。待办(todo_add/todo_complete/todo_list)会主动提醒;备忘录(memo_create/memo_list/memo_delete)纯记事不提醒。
+musicv0.1.0音乐搜索插件,支持搜索歌曲和查看歌词(基于网易云音乐)
+ocrv1.0.0图片文字识别工具,基于 Tesseract OCR 引擎。支持从 URL 或 base64 图片中提取文字内容。
+plugindevv1.0.0把 hmapdev 工具链封装成 Agent 可调用的工具:脚手架生成插件工程、构建打包 .hmap、管理 SDK 版本。配合 plugin_install 即可让 Agent 自己做完「新建插件 → 构建 → 安装」全流程。
+qqv1.4.1QQ 消息收发插件,通过 NapCat 协议桥接
+recoverydiagv0.2.0快速检查/崩溃取证工具集:diag_triage(退出码/信号/存活粗分)、diag_db(config.db 完整性 + LLM 源解析校验)、diag_log_scan(日志签名命中)、diag_delta(last-good 快照 vs 现状 diff)、diag_loc(正交综合定位)。全部返回结论而非原文,确定性、不消耗 LLM token,供 guard / failback 恢复决策使用。
+rssv1.1.0RSS/Atom 订阅监控插件,自动检测更新并推送通知
+sanitizerv0.1.0清洗 LLM 输出中的工具调用残留(思维泄漏)
+vanblogv1.0.0管理 VanBlog 开源博客系统:文章的增删改查、分类标签管理、草稿发布、备份导出等
+vikunjav1.0.1Vikunja 待办/任务管理:任务增删改查、项目与看板桶、标签、指派、评论、关联、附件、保存筛选器、团队与分享、通知、订阅、Webhook、时间跟踪、数据导入、实例管理;并附通用 API 直通工具兜底
+weatherv1.0.0天气查询插件(基于 wttr.in),支持实时天气和未来预报
+这些是设计上最容易引起误解的地方。
+最容易引起误解的六个地方。
- 恰恰相反——零 IO 是为了让内核能专注做难的部分:输入调度(四级中断、 - 抢占与现场保存)、上下文预算、三层记忆的蒸馏与召回。 - 把 IO 挪出去之后,这些才有清晰的所有权。 + 恰恰相反。零 IO 是为了让内核专注做难的部分:输入调度(四级中断、抢占与 + 现场保存)、上下文预算、三层记忆的蒸馏与召回。IO 挪出去之后,这些才有 + 清晰的所有权。
- 不会。从 v1.0.0 起外部插件是独立子进程,与内核只通过 - stdio JSON-RPC、共享内存段、事件环三个面通信。插件崩溃后内核摘除其 - 注册面(工具/阶段/通道)并按退避重启它,其余插件与内核不受影响。 + 不会。外部插件是独立子进程,与内核只通过 stdio JSON-RPC、共享内存段、 + 事件环通信。插件崩溃后,内核摘除它注册的工具、阶段与通道,按退避重启, + 其余部分不受影响。
- 因为同一时刻只应该有一个任务在改上下文。同级中断不抢占同级任务—— - 抢了会让两边都做一半。需要立刻插队的场景请用更高级别的中断 - (如 L4:内核与内核级插件),而不是让所有消息都变成抢占者。 + 因为同一时刻只应有一个任务在改上下文。同级抢占会让两边都做一半。 + 需要立刻插队时用更高级别的中断(如 L4:内核与内核级插件), + 而不是让所有消息都变成抢占者。
- 这正是「分诊助手」要解决的。主 agent 忙超过阈值(默认 5 分钟)且积压够多时, - 内核会拉起一个临时驻留子接手积压:能直接办的办完并回复, - 需要主 agent 的立刻回一句「忙碌中,请稍候」。 + 这正是「分诊助手」的用途。主 agent 忙超阈值且积压够多时,内核拉起临时 + 驻留子接手:能办的直接办,需要主 agent 的立刻回「忙碌中,请稍候」。
- 不会。三层是迁移关系而不是复制:上下文事件被裁剪后归档成文档, - 文档冷掉后蒸馏成图谱三元组。图谱有去重与 UNIQUE 约束, - 媒体则靠引用计数 GC 回收无主内容。 + 不会。三层是迁移而非复制:上下文事件裁剪后归档成文档,文档冷掉后蒸馏成 + 图谱三元组。图谱有去重与 UNIQUE 约束,媒体靠引用计数 GC 回收无主内容。
- 可以。内核不绑定任何厂商:每个 LLM 源对应一个 Lua 脚本负责请求/响应转换, - 仓库内置 10 个适配器(OpenAI、Anthropic、Gemini、DeepSeek、Groq、Ollama 等), - 也可以自己写一个接任意兼容 API。 + 可以。内核不绑定厂商:每个 LLM 源对应一个 Lua 脚本负责请求与响应转换。 + 内置 9 个适配器(OpenAI、Anthropic、Gemini、DeepSeek、Groq、Mistral、 + Ollama、GitHub、KimiCode),也可自己写一个接任意兼容 API。
- AI 辅助编程声明:本项目代码、文档与提交历史中,部分内容由 AI 辅助生成或修改。 - 人工已审阅关键改动,使用时请自行评估与验证。 + AI 辅助声明:本项目代码、文档与提交历史含 AI 生成或修改的内容。 + 关键改动经人工审阅,使用前请自行评估验证。
- HomeAgent 以 AGPL-3.0-only 发布:通过 HTTP 提供交互式服务时, - 须向使用者提供完整对应源码(§13)。 + AGPL-3.0-only。通过 HTTP 提供交互式服务时,须向使用者提供完整对应源码(§13)。