2026-10-26 13:00:00
家里的 [[Homelab]] 一直是一台 4 盘位的小主机跑 [[Proxmox VE]],上面跑着 Ubuntu 虚拟机、一堆 Docker 服务和下载机。平时远程管理全靠 SSH 加 [[Tailscale]],只要系统还活着基本没什么问题。真正让我不放心的是系统起不来的时候:升级内核后卡在启动界面、改网卡配置时手滑导致网络没起来、BIOS 被重置后不再从正确的硬盘启动,这时候 SSH 连不上,Tailscale 也不会在线,人不在家就只能干等。最近正好赶上一波促销,我入手了 [[GL.iNet]] 的 Comet Pro(型号 GL-RM10)远程 [[IP KVM]],叠加各种限时优惠之后实际支付 590.76 元,算是把 Homelab 远程管理的最后一块短板补上了。

我们平时用的 [[AnyDesk]]、[[TeamViewer]]、[[RDP]]、VNC 这类远程桌面工具,本质上都是跑在操作系统里的一个程序,系统没启动、卡死或者网络配置坏了,它们也就跟着失效。服务器厂商的解决办法是在主板上放一颗独立的 BMC 芯片,也就是常说的 [[IPMI]]、iDRAC、iLO,不管操作系统是什么状态都能远程看到画面、按电源键。但我们在家里用的迷你主机、NAS 小主机、旧台式机几乎都没有这个东西。
IP KVM 就是把 BMC 做成了一个外挂的小盒子。它通过 HDMI 采集目标电脑的画面,通过 USB 模拟键盘鼠标,再把这些信号经网络传到浏览器里,于是开机自检、BIOS/UEFI 设置、系统安装界面都能远程看到并操作,和坐在机器前面没什么区别。对 Homelab 来说,这意味着即使宿主机彻底起不来,也能在外面进 BIOS 改启动顺序,或者挂一个 ISO 重装系统。
几年前想要这个能力,基本只有 [[PiKVM]] 一个选择,要么买树莓派加采集模块自己 DIY,要么买 V4 Mini 这样的成品,价格在 275 美元左右。2024 年之后情况变了,[[JetKVM]] 在 Kickstarter 上众筹爆火,Sipeed 推出了 [[NanoKVM]],GL.iNet 也做了 Comet 系列,消费级 IP KVM 的价格被拉到了 100 美元上下,这也是我开始认真考虑买一台的原因。
GL.iNet 的 Comet 系列目前有好几款:入门的 Comet(GL-RM1)、带 PoE 的 Comet PoE(GL-RM1PE)、本文的主角 Comet Pro(GL-RM10),以及走 5G 网络的 Comet 5G(GL-RM10RC)。Comet Pro 是其中配置最完整的一款,相比入门款多了双频 Wi-Fi 6、2.22 英寸触摸屏和 HDMI 直通输出,存储从 8GB 提升到 32GB eMMC,处理器也从 Cortex-A7 换成了四核 Cortex-A53 加 1GB 内存。它在 2025 年 8 月上 Kickstarter 众筹,同年 12 月开始发货。
让我下决心的其实是价格。Comet Pro 官方定价 179.99 美元,亚马逊上平时的折扣价也在 145~160 美元,而我这次 590.76 元到手,按现在的汇率折合 80 多美元,不到官方定价的一半,基本和 JetKVM、Comet 入门款是一个价位。同样的钱能拿到 Wi-Fi 6、32GB 存储和 HDMI 直通,就没有太多犹豫的理由了。下面是我购买前整理的几款主流方案对比:
| 产品 | 参考价(美元) | 采集规格 | 网络 | 特点 |
|---|---|---|---|---|
| GL.iNet Comet Pro(GL-RM10) | 179.99(我实付 590.76 元) | 4K@30,带 HDMI 直通 | 千兆有线 + 双频 Wi-Fi 6 | 触摸屏,32GB eMMC,A53 + 1GB 内存 |
| GL.iNet Comet(GL-RM1) | 约 80~90 | 最高 2K | 千兆有线 | 小巧便宜,8GB 存储 |
| [[JetKVM]] | 约 69~100 | 1080p@60 | 百兆有线 | 完全开源,单核 A7 + 256MB 内存 |
| [[NanoKVM]] Pro | 约 155 | 4K@45 | 有线 / Wi-Fi | 采集规格最高,安全口碑有争议 |
| [[PiKVM]] V4 Mini | 约 275 | 1920x1200@60 | 千兆有线 | 生态最成熟,功能最全 |
如果按原价买,我大概率会选 JetKVM,毕竟便宜一半而且完全开源。但在价格拉平之后,Comet Pro 的硬件余量明显更大,A53 加 1GB 内存跑 Web 界面和视频编码都更从容,再加上 GL.iNet 做路由器多年,固件更新和售后相对靠谱,所以选它是比较自然的结果。
Comet Pro 最大的卖点是双频 Wi-Fi 6,官方称它是第一款同时支持 2.4GHz 和 5GHz 的桌面 KVM。不过这里要先说清楚它的边界:Wi-Fi 只是替代了 KVM 到路由器的那根网线,KVM 到被控电脑的 HDMI 线和 USB 线还是少不了,所以它不是「无线控制电脑」,而是「KVM 本身可以无线联网」。理解了这一点再看它的价值,主要体现在机器所在的位置没有网口,或者交换机口已经插满的场景,Comet Pro 可以直接放在机器旁边,不用为它再拉一根线。
配置过程也因此简单了不少。设备通电后在触摸屏上直接选择 Wi-Fi、输入密码,屏幕上就会显示分配到的 IP,用浏览器打开这个地址就能开始初始化,全程不需要先插网线或者去路由器后台找设备。远程画面用的是 H.264 硬件编码,局域网内官方标称延迟 30~60ms,网络一般的时候也可以从 4K@30 切到 1080p@60,这点码率对 Wi-Fi 6 来说绰绰有余。
不过有一个容易被忽略的坑:KVM 自己也要依赖网络才能被访问到。如果你的软路由(比如 OpenWrt、iKuai)是跑在 PVE 的虚拟机里,那么 PVE 宿主机挂掉的同时家里的网络也一起断了,KVM 不管是有线还是无线都连不上,形成一个循环依赖。这种情况下要么让 KVM 连到一台独立的主路由上,要么接受「宿主机挂了只能等回家」的现实。
32GB eMMC 是我比较看重的另一点。IP KVM 的虚拟媒体功能可以把上传的 ISO 挂载成一个 U 盘给被控电脑,远程重装系统就靠它。Proxmox VE 的安装镜像只有一两 GB,Windows 11 镜像也就 5~7GB,32GB 的空间足够同时放好几个常用镜像,需要的时候直接挂载,不用每次重新上传。入门款 Comet 只有 8GB,放一个 Windows 镜像就去掉了一大半。
除了装系统,这块存储也可以当成一个临时的文件中转站。比如被控机器网络坏了,需要把一个修好的网卡驱动或者配置文件送进去,就可以先传到 KVM 上,再以 U 盘的形式挂给目标机器,绕过它自己已经瘫痪的网络。
HDMI 直通(Loop Out)是我之前没太在意、但仔细想想很实用的功能。被控电脑的画面同时输出给 KVM 和本地显示器,机器旁边有人用的时候完全不受影响,不需要被控电脑额外拿出一个视频输出口。机身上的 2.22 英寸触摸屏不显示被控电脑的画面,主要用来查看 IP、连接 Wi-Fi、开关云服务,还可以设置一个 4 位 PIN 锁住屏幕,防止别人随手改设置。
软件方面,Comet Pro 的固件叫 GLKVM,核心守护进程 KVMD 是 PiKVM 的衍生项目,以 GPL-3.0 协议开源在 GitHub 的 gl-inet/glkvm 仓库里。GL.iNet 在上面套了一层 Vue 写的新界面,原版 PiKVM 的 Web UI 其实也打包在固件里,只是在 nginx 配置中被注释掉了,习惯 PiKVM 界面的话可以手动切回去。远程访问有几种方式可以选:官方的 GLKVM Cloud、开源可自托管的 glkvm-cloud、Apps Center 里内置的 [[Tailscale]],以及 [[ZeroTier]] 和 [[NetBird]]。我自己一直在用 Tailscale 组网(之前写过一篇 Tailscale 简单使用),所以会直接用内置的 Tailscale,不打算依赖官方云。
我的计划是把它接到跑 PVE 的宿主机上,作为整台 Homelab 的兜底方案。连接方式本身很简单,包装里电源适配器、两根 HDMI 线、USB-C 线和网线都有,基本不需要额外买线:
购买前翻文档和评测时,整理了几个比较容易踩的坑。第一是供电,Comet Pro 可以从被控电脑的 USB 口取电,但电脑关机或进入睡眠(S3/S4)时 USB 可能断电,KVM 也会跟着掉线,所以最好用单独的电源适配器供电,或者在 BIOS 里打开 USB 待机供电。第二是显示输出,不少电脑只在主显示输出上显示 BIOS 画面,多显示器的机器要把 KVM 接在主输出上;如果被控电脑只有 USB-C 视频输出,还要确认这个口支持 DP Alt Mode。
第三是远程开机。Comet Pro 内置了 [[Wake on LAN]],但前提是宿主机的 BIOS 和网卡都开启了 WOL,PVE 里也要确认网卡配置没有把它关掉。对完全断电关机的机器,官方提供了 ATX 控制板(接主板的电源和复位针脚)和 Fingerbot(一个物理按开机键的小机械臂),都通过 Comet Pro 的 USB-A 口连接。很多迷你主机主板上根本没有 ATX 针脚可接,这时候 Fingerbot 反而更通用。还有一个更便宜的思路是在 BIOS 里打开「来电自动开机」(Restore on AC Power Loss),再配合一个智能插座断电再上电,也能实现远程开机。
第四也是最重要的一点是安全。IP KVM 等于拿到了这台机器的物理控制权,能进 BIOS、能挂载 ISO、能输入任意按键,一旦被别人登录进来,后果比 SSH 密码泄露严重得多。NanoKVM 之前就因为默认密码、硬编码密钥、未说明的麦克风等问题被反复报道过,虽然后来大部分都修了,但足以说明这类设备出问题的代价。我的原则是绝不把 KVM 的 Web 界面直接暴露到公网,只通过 Tailscale 访问,同时开启两步验证、改掉默认密码,并保持固件更新。
Comet Pro 也不是没有短板。名字叫 Pro 却不支持 PoE,想要一根网线供电只能退回 Comet PoE;4K 只有 30FPS,日常操作没问题,但别指望用它看流畅的 4K 画面;存储也没有 microSD 或 USB 3 扩展,32GB 就是全部。另外它只能控制一台电脑,如果要管理多台机器,要么每台配一个,要么再加一个 KVM 切换器,到这个规模可能就该考虑带 BMC 的服务器主板了。
还有就是 ATX 控制板和 Fingerbot 都要另外购买,如果宿主机完全不支持 WOL,又没有 ATX 针脚,那远程开机这件事还得再多花一点钱。早期评测里也有提到音频和麦克风偶尔需要重启才能恢复,好在 GL.iNet 的固件更新还算频繁,这类问题基本随着版本迭代慢慢被修复了。
像 Proxmox VE 8 升级到 9 这种需要重启宿主机的操作,最怕的就是重启之后机器没回来,而人又不在旁边。有了 Comet Pro 之后,最坏的情况也只是远程进 BIOS 或者挂 ISO 重装,心理负担会小很多,这才是 IP KVM 真正的价值:它平时几乎用不上,但关键时刻能让你不用跑回家。
对我来说,590.76 元的价格让 Comet Pro 从一个「有点贵的高端款」变成了「同价位里配置最完整的选择」,Wi-Fi 6、32GB 存储和 HDMI 直通都是实打实的加分项。如果你的 Homelab 也是没有 IPMI 的迷你主机或者自组装机器,又正好遇到不错的折扣,很值得考虑。等实际用一段时间后,我再写一篇聊聊日常使用的体验,包括 Tailscale 远程访问的延迟、虚拟媒体重装系统的流程,以及原版 PiKVM 界面怎么切回去。
2026-10-19 13:00:00
| 前两天写了一篇 呆呆面板 daidai-panel:轻量级青龙面板替代品介绍与 Docker 部署教程,在那篇文章里我提到呆呆面板和 [[qinglong | 青龙面板]] 最明显的代差是内置了 [[MCP]] 服务,但只是简单带过。面板搭起来、脚本陆续迁移进来之后,我每天早上要做的第一件事就变成了打开面板,挨个看昨晚哪些任务失败了、报的什么错、是 Cookie 过期还是网站改版。这种重复又琐碎的巡检,恰好是 AI 最擅长替人做的事情。所以这一篇就专门聊聊呆呆面板的 MCP 怎么配置、怎么接入不同的客户端,以及在权限上应该怎么取舍。 |

[[MCP]](Model Context Protocol)是 [[Anthropic]] 提出的一套开放协议,用来让大模型客户端以统一的方式调用外部工具。以前想让 AI 帮你操作某个系统,要么自己写 Function Calling 的胶水代码,要么把 API 文档贴给它让它生成 curl 命令,再自己复制粘贴执行。有了 MCP 之后,服务端只需要把能力声明成一组工具,[[Claude Desktop]]、[[Claude Code]]、[[Cursor]]、[[Cherry Studio]]、[[AstrBot]] 这些客户端连上之后就能自己发现并调用这些工具,用户只需要用自然语言描述想做的事。
呆呆面板的 MCP 服务是直接内置在面板里的,不需要额外部署任何东西。它的工具集和面板的 Open API 能力是对齐的,日常运维里能通过开放 API 做的事情,基本都能在 MCP 里找到对应的工具。只读模式下,AI 可以分页查询任务、查看任务配置、读取最近一次的执行日志、按结果筛选执行记录、浏览脚本目录、读取脚本内容、查看订阅和备份列表以及系统资源占用。管理员放开写入之后,AI 还能运行和停止任务、新建和修改任务、增删改环境变量、保存和回滚脚本、直接执行一段代码片段、管理订阅、发送通知,甚至创建和恢复备份。
我觉得这个设计最聪明的地方在于,所有 MCP 工具最终都是经过面板的开放 API 执行的。这意味着 MCP 并没有引入一套新的权限体系,AI 能碰哪些模块完全由 Open API 应用勾选的权限范围决定,每一次工具调用都会记进「Open API」页面的调用日志,并且计入应用的调用频率限制。对于一个能在服务器上执行任意代码的面板来说,这种「复用已有边界」的做法比单独再造一套鉴权要让人放心得多。
MCP 默认是关闭的,需要在面板的「系统设置」→「MCP 服务」里手动打开。这里有两个开关,第一个是「启用 MCP 服务」,不打开的话 HTTP 接口一律返回 403,容器里的 ddp mcp 命令也会直接报错退出。第二个是「允许写入与执行工具」,默认同样关闭,此时 AI 只能看到和调用查询类工具,写入类工具在工具清单里是完全不出现的,而不是出现了再拒绝。打开之后才会提供运行任务、修改环境变量、保存与运行脚本这些能力。
两个开关修改后立即生效,不需要重启面板。比较贴心的一点是,已经连着的 ddp mcp 进程在每次调用工具之前都会重新检查开关状态,所以如果你发现 AI 在做一些不该做的事,直接去设置里把开关关掉,下一次调用就会拿到错误,不需要去找客户端那边的进程把它杀掉。我的建议是先只开第一个开关,用只读模式跑一段时间,确认客户端的行为符合预期之后,再考虑要不要放开写入。
MCP 的凭据来自 Open API 应用。在面板的「Open API」页面新建一个应用,记下 app_key 和 app_secret,注意密钥只会显示这一次,关掉弹窗就再也看不到了。然后按需要勾选权限范围,每个范围对应一组 MCP 工具:
| 权限范围 | 对应的工具 |
|---|---|
tasks |
list_tasks、get_task、get_task_log、list_task_labels、run_task、stop_task、set_task_enabled、batch_task_action、create_task、update_task、batch_set_task_notify |
logs |
list_logs、get_log |
envs |
list_envs、export_envs、create_env、update_env、delete_env、batch_env_action、import_envs |
scripts |
list_scripts、get_script_tree、read_script、list_script_versions、save_script、run_script、run_code、delete_script、rename_script、move_script、copy_script、batch_delete_scripts、rollback_script |
subscriptions |
list_subscriptions、pull_subscription、create_subscription、update_subscription、delete_subscription、set_subscription_enabled |
system |
get_system_info、get_dashboard |
notifications |
send_notification |
backup |
list_backups、create_backup、delete_backup、restore_backup |
没有勾选的模块,对应工具被调用时会返回「应用无权访问此资源」。这里需要特别注意几个高危权限。scripts 权限等同于可以在面板上执行任意代码,因为 run_code 可以直接执行代码片段,或者保存一个脚本再运行它;tasks 权限可以新建和修改任务的命令,本质上也是任意代码执行;envs 能改写所有凭据;backup 则可以用一份备份覆盖整个面板。
我的做法是按用途建多个应用,而不是一个应用勾满所有权限。比如给日常巡检用的应用只勾 tasks、logs、system,即使「允许写入」开关打开了,它最多也就是能运行和停止任务,碰不到脚本和环境变量。真要让 AI 帮忙改脚本的时候,再单独用另一个带 scripts 权限的应用,用完把这个应用禁用掉,禁用会立即生效。如果用面板的登录令牌而不是应用凭据来连接,权限会按账号角色来限制,其中备份类工具要求管理员身份。
HTTP 方式走的是 MCP 的 Streamable HTTP 传输,地址就是你在浏览器里访问面板的地址后面加上 /api/v1/mcp,例如 http://192.168.1.10:5700/api/v1/mcp。这是适用面最广的方式,大多数客户端都支持,而且如果你的面板是跑在手机上的 Magisk 模块版,这是唯一可用的方式。
鉴权有两种选择。一种是 Authorization: Basic <base64(app_key:app_secret)>,直接用应用凭据,最省事;另一种是 Authorization: Bearer <令牌>,令牌可以是通过 POST /api/v1/open-api/token 换来的应用令牌(有效期 24 小时),也可以是面板的登录令牌。对于 MCP 客户端这种长期配置的场景,Basic 显然更合适,不用担心令牌一天后过期。
生成 Basic 凭据的方法如下,注意 echo 一定要带 -n,否则末尾多出来的换行符也会被编码进去,导致鉴权失败:
# Linux / macOS
echo -n 'app_key:app_secret' | base64
# Windows PowerShell
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('app_key:app_secret'))
在配置任何客户端之前,我习惯先用 curl 确认服务是通的。下面这条命令会请求 MCP 的 tools/list 方法,正常情况下应该返回一份工具清单:
curl -s http://192.168.1.10:5700/api/v1/mcp \
-u 'app_key:app_secret' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
-u 参数会让 curl 自己处理 Basic 编码,比手动拼 header 方便。如果返回 403,先去确认「启用 MCP 服务」的开关有没有打开;如果返回的工具清单里只有查询类工具,说明「允许写入与执行工具」还没开,这是正常的。顺便可以观察一下,返回的工具数量会随着「允许写入」开关的状态变化,这也印证了前面说的写入类工具在只读模式下是完全隐藏的。
支持远程 MCP 地址的客户端,比如 [[Cursor]],直接在 MCP 配置文件里填 URL 和 header 即可:
{
"mcpServers": {
"daidai-panel": {
"url": "http://192.168.1.10:5700/api/v1/mcp",
"headers": {
"Authorization": "Basic <上面生成的 base64>"
}
}
}
}
如果你和我一样主要在终端里用 [[Claude Code]],可以用一条命令添加,--transport http 指定 Streamable HTTP,--header 带上鉴权信息,--scope user 让它在所有项目里都可用:
claude mcp add --transport http --scope user daidai-panel \
http://192.168.1.10:5700/api/v1/mcp \
--header "Authorization: Basic <上面生成的 base64>"
添加完成后在 Claude Code 里输入 /mcp 可以看到连接状态和可用的工具列表。
有些客户端只支持拉起本地进程的 stdio 方式,比如 [[Claude Desktop]] 的本地配置文件。这时可以借助 mcp-remote 把远程的 HTTP 服务转成本地的 stdio,前提是本机装了 Node.js:
{
"mcpServers": {
"daidai-panel": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"http://192.168.1.10:5700/api/v1/mcp",
"--header", "Authorization:Basic <上面生成的 base64>"
]
}
}
}
注意这里 Authorization: 和 Basic 之间没有空格,这是 mcp-remote 的写法。在 Windows 上,部分客户端传参时对空格的处理有问题,mcp-remote 官方文档也提到过这个坑,用这种不带空格的写法可以规避。
呆呆面板的 MCP 服务是无状态的,每个 POST 请求独立处理、直接返回 JSON,不会开 SSE 长连接,GET 和 DELETE 请求会返回 405。这意味着它对反向代理非常友好,不需要像日志流那样专门去关 proxy_buffering,套在 Nginx 或 [[Cloudflare Tunnel]] 后面都不会有断流的问题。
如果请求带有 Origin 头(也就是从浏览器里发起的),面板会要求来源与面板同源、来自局域网 IP,或者已经写在 config.yaml 的 cors.origins 里,否则返回 403。这是为了防止恶意网页在你浏览时偷偷调用局域网里的面板。
最重要的一点是,如果要从公网访问,一定要套 HTTPS。Basic 凭据只是 base64 编码,不是加密,在明文 HTTP 上传输时同一个网络里的人可以直接看到并解码,而这串凭据背后可能是一个能执行任意代码的应用。我自己的做法是不把面板暴露到公网,在外面通过 [[Tailscale]] 连回家里的网络再访问 MCP,这样配置里的地址也可以继续用内网 IP。
另一种方式是在面板所在的机器或容器里运行 ddp mcp 命令,它以 stdio 的方式和客户端通信,内部再通过本机 HTTP 调用正在运行的面板。所以面板本身必须是运行状态,ddp mcp 自己不会直接去改数据库。命令格式如下:
ddp mcp --app-key <app_key> --app-secret <app_secret> [--url http://127.0.0.1:5701]
三个参数都有对应的环境变量,不想把密钥写在命令行里的话可以用环境变量传:
| 参数 | 环境变量 | 说明 |
|---|---|---|
--url |
DDP_MCP_URL |
面板后端地址,默认 http://127.0.0.1:<面板后端端口>
|
--app-key |
DDP_MCP_APP_KEY |
应用的 app_key |
--app-secret |
DDP_MCP_APP_SECRET |
应用的 app_secret |
ddp mcp 的 stdout 只输出 MCP 协议消息,就绪提示和错误信息都写到 stderr,所以如果客户端连不上,去看客户端记录的 stderr 日志就能找到原因。
对于 Docker 部署、客户端装在宿主机上的情况,可以用 docker exec 把命令转进容器:
{
"mcpServers": {
"daidai-panel": {
"command": "docker",
"args": [
"exec", "-i", "daidai-panel",
"ddp", "mcp", "--app-key", "<app_key>", "--app-secret", "<app_secret>"
]
}
}
}
daidai-panel 换成你自己的容器名。这里的 -i 参数千万不能少,stdio 传输依赖标准输入,没有 -i 的话 stdin 接不上,客户端会一直卡在初始化阶段,也不会有明显的报错,排查起来很费劲。另外不要加 -t,分配 TTY 会让输出里混进控制字符,破坏 JSON-RPC 消息。如果是二进制部署,command 直接写 ddp 的完整路径,args 写成 ["mcp", "--app-key", "…", "--app-secret", "…"] 即可。
那么 HTTP 和 stdio 该怎么选?我的看法是,只要客户端支持远程地址,优先用 HTTP。stdio 方式要求客户端和面板在同一台机器上,或者至少能 docker exec 进容器,而大多数人的面板是跑在 NAS 或 VPS 上的,AI 客户端则在自己的笔记本上,这种情况下 stdio 反而要多绕一层 SSH。stdio 更适合面板和客户端在同一台机器上的场景,好处是不用经过网络,也就不用操心 HTTPS 的问题。Magisk 模块版的面板跑在手机的容器里,电脑上的客户端进不去这个容器,只能用 HTTP。
上面的几种配置方式有一个共同的麻烦:每个客户端都要单独配一遍。Cursor 写一份 JSON,Claude Code 跑一条 claude mcp add,Claude Desktop 再套一层 mcp-remote,同一串 Basic 凭据被复制到好几个配置文件里。哪天轮换了 app_secret,又得挨个去改。我之前写过一篇 Executor:多 Agents 和外部世界之间的统一代理层,[[Executor]] 正好解决这个问题:把呆呆面板的 MCP 作为一个上游集成加到 Executor 里,各个 Agent 只连 Executor 这一个端点,面板的工具就会自动出现在所有客户端里。
配置方式很直接。在 Executor 的 Web 控制台点 Add Integration,填入面板的 MCP 地址 http://192.168.1.10:5700/api/v1/mcp,Executor 会识别出这是一个远程 MCP server 并索引出工具清单。鉴权有两个地方可以放:一个是集成上的 Request headers,填进去的 header 会原样附加在每次请求上;另一个是认证方式里的 API Key 类型,把位置设成名为 Authorization 的 header、前缀设成 Basic ,值填前面生成的 base64,凭据就会挂在 connection 上。Executor 自己的说明是 Request headers 适合放端点级别的访问令牌(比如 Cloudflare Access 的 service token),每个账号自己的凭据应该放到认证方式里。呆呆面板的 app_key:app_secret 属于后者,所以我选了第二种。保存之后用 executor tools search 搜一下 list_tasks,能搜到就说明通了。
这样做除了省掉重复配置,还有几个和呆呆面板的权限设计很契合的地方。
第一是凭据不再出现在任何客户端的配置文件里。Basic 凭据存在 Executor 的 connection 上,只在真正向面板发请求的时候才附加上去,Agent 从头到尾看不到它。对于一个能执行任意代码的面板来说,凭据少散落一个地方,就少一分泄露的可能。
第二是前面说的「按用途建多个 Open API 应用」可以直接对应到 Executor 的 connection 上。同一个呆呆面板集成可以建两个 connection,一个用只勾了 tasks、logs、system 的巡检应用,另一个用带 scripts 权限的应用,需要改脚本时才用后者。如果你在好几台机器上都跑了面板,也可以每台建一个 connection,各自指向不同的地址。
第三是补上了工具级别的审批。呆呆面板的「允许写入」开关是一刀切的,打开之后 run_task 和 restore_backup 对 AI 来说同样可以直接调用。在 Executor 里可以给每个工具单独设策略,比如查询类工具设成 allow,run_task、update_env 设成 require approval,run_code、delete_script、import_envs、restore_backup 这类高危工具直接 block。这样即使面板那边放开了写入,危险操作也还要经过一道人工确认。
网络上也顺便简化了。我的 Executor 是在家里的服务器上用 Docker 自托管的,和呆呆面板在同一个局域网,Executor 直接用内网 IP 访问面板就行。在外面的时候,Agent 连的是 Executor 的端点,面板本身依然不需要暴露到公网。不过这也意味着 Executor 成了一个集中存放凭据的服务,它自己的入口一定要做好 HTTPS 和认证。另外大部分 MCP 客户端只在启动时加载工具列表,第一次在 Executor 里加完集成之后,记得重启客户端或者开一个新会话。
配置好之后,就可以直接用自然语言和面板对话了。下面是我觉得最有价值的几类用法。
第一类是每日巡检。直接问「昨天晚上有哪些任务失败了?把失败原因按类型归一下类」,AI 会调用 list_logs 按结果筛选出失败的执行记录,再逐个用 get_log 读日志正文,最后把 Cookie 失效、网络超时、脚本报错分开列出来。以前我要在网页上一个个点开日志翻,现在一句话就能拿到一份整理好的结论。如果再给应用勾上 notifications,还可以让 AI 把巡检结果通过 send_notification 推送到 Telegram 或 Bark,不指定渠道的话会发给所有标记为「默认推送」的渠道。
第二类是排查单个任务。比如「京东签到这个任务最近为什么一直失败?看看脚本和最近的日志」,AI 会先用 list_tasks 按关键词找到任务,get_task 看配置,get_task_log 读日志,再用 read_script 把脚本读出来对照分析。read_script 支持按字节分段读取,大文件不会被静默截断,返回里有 truncated 和 next_offset 字段,AI 会自动续读,而且分段一定落在 UTF-8 字符边界上,中文注释不会被切成乱码。
第三类是批量整理,这类操作需要放开写入。比如「把所有标签是签到的任务都打开失败通知」,AI 会用 list_task_labels 和 list_tasks 找到这些任务,再调用 batch_set_task_notify 批量修改,它只改传入的开关、不会动通知渠道。又比如脚本改名或者移动之后,引用它的任务命令不会自动更新,这时可以让 AI 找出所有受影响的任务,再用 update_task 逐个同步命令。这种机械但容易漏的活交给 AI 非常合适。
还有一个小技巧,get_system_info 返回的 machine_code 是面板首次启动时生成的,重启和升级都不会变,如果你和我一样在好几台机器上都跑了面板,可以用它作为实例的唯一标识,让 AI 区分「这份日志来自哪一台」。不过要注意,用另一个实例的备份恢复「配置」之后,机器码会变成那个实例的。
单次工具调用的输出上限大约是 64KB,超出的部分会被截断并注明。任务和日志比较多的时候,可以提示 AI 减小 page_size 或者加关键词缩小范围。get_task_log 和 get_log 遇到过长的日志也只会保留末尾,好在报错信息通常就在末尾,影响不大。
脚本相关的工具对一些目录是一律拒绝访问的,包括 .git、.svn、.hg、.bzr 这类版本库元数据目录,以及 node_modules、__pycache__ 这类依赖和缓存目录,路径中任意一段命中都算,不区分大小写。拦截 .git 的原因很有意思:订阅如果用 Token 鉴权,仓库的 .git/config 里存着带访问令牌的远程地址,能读这个文件就等于把令牌交给了任何有脚本权限的人,所以这条规则是写死的、不可配置。如果 AI 返回「该路径不可访问」,不要让它反复重试,这是有意的安全限制。确实需要清理 __pycache__ 之类的目录,可以让 AI 用 run_code 执行 rm -rf "$DAIDAI_SCRIPTS_DIR/某个子目录/__pycache__",run_code 的工作目录是临时目录,脚本目录的绝对路径在环境变量 DAIDAI_SCRIPTS_DIR 里。
环境变量的遮蔽规则和网页端一致,名称里含 TOKEN、SECRET、PASSWORD、COOKIE,或者某一段是 KEY、PWD、PASS、AUTH、CK 的变量,在 MCP 的输出里都会被遮蔽成星号,目的是不把真实凭据送进 AI 的上下文,毕竟这些内容最终是要发给模型服务商的。list_envs 的关键词搜索也不会去匹配这些变量的值,避免被拿来逐字符试出凭据。反过来,import_envs 会拒绝把遮蔽后的值(比如 pt_******zz;)导回敏感变量,防止真实凭据被一串星号覆盖。不过要清楚,这层遮蔽只作用于 MCP,拥有 envs 权限的应用直接调开放 API 仍然能读到明文。
另外,带有「重要」标记的环境变量在 list_envs 和 export_envs 的输出里会带 important 字段,按设计 AI 在删除或替换导入这些变量之前应该先向用户确认,但面板本身并不拦截删除操作,所以这条防线其实取决于 AI 客户端是否遵守。import_envs 的 replace 模式会先删除全部现有变量再导入,重要变量也不例外,这一点值得在让 AI 批量导入之前心里有数。
run_script 和 run_code 最多会等待大约 50 秒,脚本还没跑完的话会返回一个 run_id,用同一个 run_id 再调用就能继续取输出,加上 stop: true 可以停止。需要注意的是,这段等待期间的轮询也会计入调用频率限制,如果你给应用设的频率比较低,跑长脚本时可能会先撞上限流。
最后是备份恢复。restore_backup 会用备份覆盖当前数据,备份里包含的每一项都会先被清空再写入,恢复配置还会替换开放 API 应用,也就是说 AI 正在用的这个凭据可能在恢复之后直接失效。网页端恢复后面板会自动重启,而通过 MCP 恢复不会,恢复后需要手动重启一次面板。工具清单里所有会删除或覆盖数据的工具都带有 destructiveHint: true 注解,支持这个注解的客户端会在调用前弹出确认,但我还是建议在让 AI 执行 restore_backup 之前,先让它 create_backup 留一份当前状态。
呆呆面板的 MCP 实现给我的感觉是很克制。默认关闭、开启后默认只读、写入要单独授权、权限完全复用 Open API 的应用范围、每次调用都有日志、凭据类变量在 AI 上下文里自动遮蔽,这几层叠在一起,基本上把「让 AI 操作一个能执行任意代码的面板」这件事的风险控制在了可以接受的范围内。它没有为了 AI 另造一套体系,而是把已有的开放 API 包装成工具,这种做法既降低了维护成本,也让熟悉 Open API 的人几乎零学习成本就能上手。
写入权限我目前只在整理任务、批量改通知这类场景里临时打开,用完就关掉。如果你也在用呆呆面板,不妨先从一个只勾 tasks 和 logs 的只读应用开始试试,体验一下用自然语言管理定时任务是什么感觉。至于上一篇最后说到的各种脚本玩法,我会在下一篇里继续展开。
2026-10-17 13:00:00
| Homelab 里跑久了,总会攒下一堆零散的小脚本:每天早上抓一次汇率发到 Telegram,定时检查几个网站的证书有没有快过期,隔一段时间把某个 API 的数据拉下来存一份,再加上各种网站的自动签到。最开始我都是直接写进服务器的 crontab,时间一长就发现问题很多,脚本散落在不同的机器上,日志不知道去哪看,失败了也没人通知,Python 依赖装在系统里还经常和别的东西打架。几年前我为此折腾过 [[qinglong | 青龙面板]],最近在 GitHub 上又看到一个定位几乎一样、但更轻量的新项目,叫呆呆面板([[daidai-panel]])。 |

呆呆面板(Daidai Panel,英文仓库名 daidai-panel)是一个开源的定时任务管理平台,采用 MIT 协议。用一句话概括,它就是一个带 Web 界面的 cron:你在浏览器里上传或在线编写脚本,配置 Cron 表达式,面板负责按时执行、记录日志、在失败时推送通知。它支持的脚本语言包括 Python、Node.js(含 .js 和 .mjs)、Shell、TypeScript 和 Go,基本覆盖了日常自动化脚本会用到的全部语言。
技术栈方面,后端是 Go 1.25 + Gin + GORM,数据库用 SQLite(选的是纯 Go 实现的 glebarez/sqlite,编译时 CGO_ENABLED=0),前端是 Vue 3 + TypeScript + Element Plus,编辑器默认用 CodeMirror 6,桌面端可以切换成 Monaco。Docker 镜像内部由 Nginx 对外提供 5700 端口,再把 /api/* 反代到 5701 端口的 Go 后端。项目在 2026 年 3 月创建,到我写这篇文章的时候已经迭代到 v3.3.7,GitHub 上有接近 600 个 Star,提交非常频繁,几乎每隔几天就有新版本。作者在 README 的致谢里写得很清楚,后端架构参考了 [[白虎面板]](Baihu Panel),部分代码基于它改进,而定时任务、环境变量、订阅管理这些核心功能的设计则借鉴自青龙面板。
用过青龙面板的人看到呆呆面板的界面会有很强的熟悉感,因为它本来就是冲着青龙的使用习惯来设计的。环境变量支持批量导入导出,格式兼容青龙;订阅管理里的白名单、黑名单、依赖规则三个字段,对应的就是青龙 ql repo 命令的第 2、3、4 个参数;甚至在 v3.2.0 之后还加了一层青龙脚本兼容层,Docker 部署时会在容器里模拟 /ql/data/scripts、/ql/shell/env.sh 这类路径,那些一上来就 source $QL_DIR/shell/env.sh 的老脚本可以直接跑起来。所以从青龙迁移过来的成本是比较低的。
既然功能差不多,为什么还要换?我看下来主要有这么几点差异。第一是体积和资源占用,青龙是 Node.js 全家桶,呆呆面板的后端是一个 Go 二进制,加上 SQLite 单文件数据库,整个面板的状态就是一个 Dumb-Panel 目录,备份和迁移非常省心。第二是运行环境的选择更细,官方镜像分 Alpine 和 Debian 两个基础系统、精简和完整两个工具档位,还可以指定 Python 3.10、3.11、3.12,甚至有三个 Python 版本共存的镜像,支持 amd64、arm64、386、arm/v7 四种架构,连树莓派 Zero 2W 这种老设备都照顾到了。第三是部署方式更多样,除了 Docker,还提供了 Windows 免安装版(解压后双击 start.bat)和 Android Magisk 模块(Root 手机上直接跑,不需要 Termux)。第四是它对 AI 时代的工具链跟得很紧,内置了 MCP 服务,这一点我后面单独说。
当然也要客观看待,青龙面板经过多年的社区打磨,生态和教程都远比呆呆面板丰富,呆呆面板毕竟是一个 2026 年才出现的新项目,更新节奏快也意味着可能会有变化和不稳定的地方。如果你现在的青龙用得好好的,没必要为了换而换;如果是新部署,或者对青龙的资源占用和维护状态有意见,呆呆面板值得试一试。
呆呆面板的功能模块和青龙基本一一对应,但很多细节做得更深入,我挑几个自己比较在意的说一下。
定时任务是最核心的模块,除了标准的 Cron 表达式调度,还支持失败重试、执行超时、定时停止、多实例并发控制,以及前置和后置钩子,可以把多个任务串成依赖链。执行日志通过 SSE 实时推送到浏览器,会自动跟随最新输出,往上翻就暂停滚动,这种细节用起来很舒服。环境变量模块会自动识别名字里带 TOKEN、COOKIE、PASSWORD 之类关键词的变量,默认遮蔽显示,避免在别人面前打开面板时把敏感值直接露出来。任务执行时,环境变量会自动注入到脚本进程里。
依赖管理是我觉得做得最用心的部分。它可以可视化地安装和卸载 pip、npm 依赖,还能装 Linux 系统包,并且会自动识别容器里用的是 apk、apt、dnf、yum、microdnf 还是 zypper。README 里专门花了很大篇幅解释 pip 报 Failed building wheel、gcc: not found、CMake must be installed 时该怎么办,面板本身也会分析安装日志,在失败原因里直接告诉你是缺编译工具链还是 musl 上没有预编译包。对于经常在 Alpine 里被 Python 原生扩展折磨的人来说,这些提示能省下不少时间。
订阅管理可以从 Git 仓库定期拉取脚本,支持 SSH Key 和 Token 认证,脚本头部声明了 cron 的会自动建成定时任务。通知推送内置了 22 种渠道,常见的 Bark、Telegram、Server 酱、企业微信、钉钉、飞书都在里面。安全方面支持 2FA、IP 白名单、登录日志和多设备会话管理,备份可以一键执行,也能按天、周、月定时备份。另外还有 Open API,用 App Key 和 App Secret 认证,方便和第三方系统对接,作者还单独做了一个手机客户端 Dumb-Panel-APP。
还有一个很实用的设计是脚本内回调面板。任务执行时,面板会往脚本进程里注入 DAIDAI_API_BASE 和 DAIDAI_TOKEN 两个环境变量,前者是面板 API 的根地址,后者是一枚只在本次运行期间有效的临时令牌。脚本拿着它们就能直接发 HTTP 请求回调面板,比如发通知、更新环境变量、查询任务、触发订阅拉取,不需要另外申请 Open API 应用,也不用把账号密码写进脚本里。典型的场景是某个脚本刷新了 Token 之后,把新值写回面板的环境变量,下一次运行就能直接用。
这是呆呆面板和青龙最明显的代差。面板内置了一个 [[MCP]] 服务,[[Claude]]、[[Cursor]]、[[Cherry Studio]] 这些支持 MCP 的客户端连上之后,可以直接用自然语言查任务、看日志、巡检失败的任务。它提供两种连接方式,一种是 Streamable HTTP,地址是 http://<面板地址>/api/v1/mcp,适合大多数客户端;另一种是 stdio,在容器里执行 ddp mcp 命令,适合只支持拉起本地进程的客户端。
MCP 默认是关闭的,需要在「系统设置」里手动打开。所有 MCP 工具调用最终都走面板的开放 API,能访问哪些模块由 Open API 应用的权限范围决定,每次调用都会记进调用日志并计入频率限制。默认情况下只读,管理员放开写入之后,AI 才能运行任务、修改环境变量和脚本。我觉得这个默认值设得很合理,让 AI 帮忙看「昨晚哪些任务失败了、报的什么错」是非常省事的用法,至于让 AI 直接改线上脚本,还是谨慎一点比较好。
官方推荐用 Docker Compose 部署。如果想先看看界面再决定要不要装,可以直接打开作者提供的 在线演示,那是前端单独打包加浏览器内模拟数据做的,没有后端,所有操作只存在你自己的浏览器里,刷新就恢复,所以千万不要在里面填真实的 Cookie 或密钥。
新建一个目录,比如 ~/dockerfile/daidai-panel,在里面创建 docker-compose.yml。下面这份基于官方配置,我把时区改成了 Asia/Tokyo,并且把 Watchtower 的令牌改成从 .env 读取,你按自己的情况调整即可:
name: daidai-panel
services:
daidai-panel:
image: ${DAIDAI_PANEL_IMAGE:-linzixuanzz/daidai-panel:latest}
container_name: daidai-panel
restart: unless-stopped
ports:
- "5700:5700"
volumes:
- ./Dumb-Panel:/app/Dumb-Panel
environment:
- TZ=Asia/Tokyo
- CONTAINER_NAME=daidai-panel
- IMAGE_NAME=${DAIDAI_PANEL_IMAGE:-linzixuanzz/daidai-panel:latest}
- PANEL_UPDATE_MANAGER=watchtower
- WATCHTOWER_HTTP_API_URL=http://watchtower:8080
- WATCHTOWER_HTTP_API_TOKEN=${WATCHTOWER_HTTP_API_TOKEN}
- WATCHTOWER_HTTP_API_PERIODIC_POLLS=true
labels:
- com.centurylinklabs.watchtower.enable=true
watchtower:
image: nickfedor/watchtower:latest
container_name: daidai-watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
labels:
- com.centurylinklabs.watchtower.enable=false
environment:
- WATCHTOWER_HTTP_API_TOKEN=${WATCHTOWER_HTTP_API_TOKEN}
- WATCHTOWER_HTTP_API_PERIODIC_POLLS=true
- WATCHTOWER_HTTP_API_ENDPOINTS=update
command:
- --label-enable
- --cleanup
- --interval
- "3600"
同目录下创建 .env:
DAIDAI_PANEL_IMAGE=linzixuanzz/daidai-panel:latest
WATCHTOWER_HTTP_API_TOKEN=换成一串随机字符串
令牌可以用 openssl rand -hex 16 生成。然后启动:
docker compose up -d
浏览器访问 http://服务器IP:5700,第一次进入会让你初始化管理员账号。这份配置里有一个设计值得注意,docker.sock 只挂给了 Watchtower,没有挂给面板容器本身,面板需要更新时通过 Compose 内部网络调用 Watchtower 的 HTTP API,而这个 API 也没有映射到宿主机。官方 compose 里给令牌设了一个默认值 daidai-panel-watchtower-token,虽然它只在内部网络可达,但我还是建议换成自己的随机值。如果你不需要自动更新,可以参考后面不使用 Watchtower 的精简配置。
这里有一个 README 用警告框反复强调的坑:Watchtower 必须是 v1.20.0 或更新的版本。上面配置里的 WATCHTOWER_HTTP_API_ENDPOINTS=update 是 v1.20.0 才引入的环境变量,旧版本遇到不认识的环境变量会静默忽略,容器照常启动,日志也不报错,但 8080 端口根本没开,面板里点「检查系统更新」只会得到 connection refused。更隐蔽的是,Watchtower 自己打了 com.centurylinklabs.watchtower.enable=false 标签避免自己更新自己,所以本地如果早就有一份旧的 nickfedor/watchtower:latest,它会一直停留在那个版本。如果你之前用过这个镜像,先手动拉一次再确认版本:
docker compose pull watchtower
docker compose up -d watchtower
docker logs daidai-watchtower 2>&1 | head -n 5
我自己其实不太喜欢让容器在半夜自动更新,尤其是这种迭代很快的项目,更希望看过更新日志之后再手动升级。另外 Watchtower 需要挂载 docker.sock,等于拿到了宿主机 Docker 的全部权限,能少一个这样的容器就少一个。如果你也是这么想的,可以把 watchtower 服务、labels、PANEL_UPDATE_MANAGER 和所有 WATCHTOWER_* 变量都删掉,docker-compose.yml 就只剩下面这几行:
name: daidai-panel
services:
daidai-panel:
image: linzixuanzz/daidai-panel:latest
container_name: daidai-panel
restart: unless-stopped
ports:
- "5700:5700"
volumes:
- ./Dumb-Panel:/app/Dumb-Panel
environment:
- TZ=Asia/Tokyo
这样也就不需要 .env 文件了,想换镜像直接改 image 这一行,比如换成 linzixuanzz/daidai-panel:debian,或者固定到 linzixuanzz/daidai-panel:3.3.7 这样的具体版本。代价是面板里「检查系统更新」的一键更新用不了,因为精简版镜像里没有 Docker CLI,面板没法自己拉镜像重建容器。需要升级的时候,在 compose 所在目录手动执行:
docker compose pull
docker compose up -d
如果固定了版本号,先把 image 里的版本改成新版本再执行这两条命令即可。旧镜像不会自动清理,偶尔执行一下 docker image prune 释放磁盘空间。
默认的 latest 是 Alpine + Python 3.12 精简版,跑普通的 Python、JavaScript、Shell 脚本完全够用,体积也最小。但有几种情况需要换标签,通过 .env 里的 DAIDAI_PANEL_IMAGE 修改即可,Compose 会同时作用到 image 和 IMAGE_NAME,不会出现运行标签和更新标签不一致的情况。
| 需求 | 推荐标签 |
|---|---|
| 普通 Python / Node.js / Shell 脚本 | latest |
| 需要跑 Go 任务,或现场编译原生扩展 | latest-full |
| Playwright、Chromium 浏览器自动化 |
debian 或 debian-full
|
依赖只发布 glibc 预编译包的 Python 库(如 opencv-python、onnxruntime) |
debian |
| 指定 Python 3.10 或 3.11 |
latest-3.10、debian-3.11 等 |
| 需要多个 Python 版本共存 |
latest-all、debian-all
|
我的建议是,只要涉及浏览器自动化或者科学计算类的 Python 包,就直接上 Debian 版。原因是 PyPI 上很多包只发布认 glibc 的 manylinux wheel,不发 musllinux wheel,在 Alpine 上要么报 No matching distribution found,要么回退到源码编译,而完整版镜像虽然多了 gcc、make,却同样没有 CMake,换档位往往解决不了问题,换成 Debian 反而能直接下载预编译包,几秒就装完。另外需要注意,Alpine 和 Debian 用的 C 库不同,沿用同一个数据目录切换镜像时,之前装好的 Python 原生扩展会加载失败,v3.3.3 之后面板启动时会自动检测并重装,更早的版本需要手动删除 Dumb-Panel/deps/python/<版本> 目录再重启。
NAS 用户可以额外设置 PUID 和 PGID 让容器以宿主机用户身份运行,这样 SMB 或 NFS 共享下的文件属主才对得上。两个变量要同时设置,代价是降权后面板里装不了 Linux 系统包,但 pip 和 npm 依赖不受影响。
如果要通过域名访问,反代的目标应该是宿主机上的 5700 端口,而不是容器内部的 5701,绕过容器内的 Nginx 直连 Go 后端会导致 SSE 断流和鉴权丢失。另外 SSE 日志流要求关闭 proxy_buffering,一个最小的 Nginx 配置如下:
location / {
proxy_pass http://127.0.0.1:5700;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 300s;
}
用公网域名访问时,记得在环境变量 CORS_ORIGINS 里加上你的域名,私有和局域网 IP 面板已经自动放行了。我个人更倾向于不把这类面板暴露到公网,它本质上能在服务器上执行任意代码,依赖管理页甚至提供了网页版的系统命令行。放在内网,外部通过 [[Tailscale]] 或者 [[Cloudflare Tunnel]] 加一层 Access 认证来访问会更安心,如果一定要公网暴露,至少把 2FA 和 IP 白名单都打开。
容器里预置了一个叫 ddp 的命令行工具(没叫 dd 是因为会和 Linux 自带的 dd 冲突),日常运维基本都可以通过它完成。最常用的是账号恢复,忘了密码不需要删数据重装:
docker exec -it daidai-panel ddp list-users
docker exec -it daidai-panel ddp reset-password admin NewPass123
docker exec -it daidai-panel ddp disable-2fa admin
docker exec -it daidai-panel ddp ip-whitelist clear
最后一条在 IP 白名单填错、把自己锁在门外的时候非常有用。除此之外,ddp status 可以查看版本、端口、任务数和资源占用,ddp check 会检查配置、数据库和运行时,ddp task run 12 可以同步执行某个任务并实时输出,ddp backup create --name nightly 用来手动备份。需要注意的是,如果想在脚本里触发另一个任务,官方文档明确要求走 HTTP 接口,而不是在脚本里调用 ddp task run。
更新方面,最省事的是在面板「系统设置」→「概览」里点「检查系统更新」,面板会识别当前的部署方式,Docker 部署走 Watchtower 拉新镜像重建容器,二进制部署自动下载对应平台的 Release 包替换程序。由于 latest 是浮动标签,配合 Watchtower 每小时检查一次,相当于一直跟着最新版跑。考虑到这个项目更新很快,如果你的任务比较重要,可以把标签固定到某个具体版本(比如 3.3.7),看过更新日志之后再手动升级,稳定性会好很多。
备份就更简单了,整个面板的状态都在 Dumb-Panel 目录里,包括 SQLite 数据库 daidai.db、自动生成的 JWT 密钥、脚本、依赖、日志和面板自己做的备份。迁移的时候把这个目录整个带走,在新机器上用同样的 compose 启动就行。不过要注意这个目录里有所有环境变量的明文和 JWT 密钥,备份到云端的时候最好加密一下。
还有一个 Node.js 脚本作者容易踩的坑值得一提:如果某个 Promise 永远不 resolve 也不 reject,事件循环排空后 node 会以退出码 0 正常退出,从外面看就像任务成功了。呆呆面板默认会检测这种「半路静默结束」,命中时把退出码置为 75 并判定失败,日志里会出现 [任务疑似半路结束]。如果你的脚本里有故意抛弃的 Promise 导致误报,可以在主流程末尾加一行 globalThis.daidaiDone?.() 声明脚本已经跑完。另外脚本末尾尽量不要调用 process.exit(),它不会等待 stdout 写完,经常导致日志最后那段汇总丢失。
对于想在 Homelab 或者一台小 VPS 上统一管理定时脚本的人来说,一个 Go 二进制加一个 SQLite 文件的架构足够轻,Docker 一条命令就能起来,迁移只需要带走一个目录。呆呆面板把青龙面板的使用习惯用 Go 重新实现了一遍,并且文档写的非常详细。
不过它还是一个只有半年多历史的新项目,迭代速度很快,长期的维护情况还需要时间来验证,重要的任务建议固定版本号、定期备份 Dumb-Panel 目录。我接下来打算把散落在几台机器 crontab 里的脚本逐步收拢进来,再试试通过 MCP 让 Claude 每天帮我巡检一遍失败的任务,有新的体验再回来补充。
这篇主要讲的是面板本身是什么、怎么搭起来,而面板真正有意思的地方在于往里面放什么脚本。下一篇我会专门聊聊呆呆面板的各种玩法:比如用 smzdm_script 完成什么值得买的每日签到和任务,给 PT 站点定时登录保活、避免因为长期不登录被清理账号,每天收盘后拉取自选股行情并交给大模型做一份分析推送到手机,再加上 B 站每日任务、网盘和论坛签到、网站与证书监控这类零碎但实用的自动化。也会顺带讲讲订阅白名单怎么写、Cookie 怎么抓、多账号怎么配,以及哪些玩法存在封号风险需要谨慎,感兴趣的话可以关注一下。
2026-10-14 13:00:00
这两年我陆陆续续接触过一些 RAG 小工具,从最早自己用 [[LangChain]] 拼一个 demo,到后来用 [[AnythingLLM]]、[[Dify]] 的知识库功能,有一个体验几乎每次都会重演:拿几篇 Markdown 测试的时候效果惊艳,一旦换成真实的业务材料,比如一份带合并单元格的财报 PDF、一份双栏排版的论文、几页扫描的合同,回答马上就开始胡说八道。追查下去会发现问题根本不在大模型,而在最前面的解析和切块环节,表格被拍平成一串没有表头的数字,左右两栏的文字被交错拼接在一起,模型拿到的上下文本身就是错的。也正是因为这个痛点,我在调查 ESP32 小智 AI 后端的时候发现了 [[RAGFlow]],一个可以「深度文档理解」的开源 RAG 引擎。

RAGFlow 是 InfiniFlow 开源的检索增强生成([[RAG]],Retrieval-Augmented Generation)引擎,采用 Apache-2.0 协议,是开源 RAG 领域最活跃的项目之一。官方现在给自己的定位是「融合 RAG 与 Agent 能力、为大模型提供上下文层(context layer)的引擎」,翻译成大白话就是:负责把真实文档整理成大模型能可靠使用的知识,并在此之上提供问答、检索 API 和 Agent 编排。
和很多「上传文档加聊天框」的工具不同,RAGFlow 的口号是 “Quality in, quality out”。它认为 RAG 效果的上限在解析阶段就已经决定了,所以把大量工程投入放在了别人不太愿意做的脏活上:用视觉模型识别文档版面,分辨哪里是标题、正文、表格、图片和页眉页脚,再根据文档类型选择合适的切块方式。这也决定了 RAGFlow 不是一个 Python 库,而是一整套需要用 [[Docker]] 部署的服务栈,这一点是它和 LangChain、[[LlamaIndex]] 这类开发框架最本质的区别。
开源 RAG 生态现在的选择其实很多,我习惯把它们分成三类。第一类是开箱即用的应用,比如 RAGFlow、AnythingLLM、[[FastGPT]],部署好就能用;第二类是 AI 应用搭建平台,比如 Dify、[[Coze]]、[[n8n]],知识库只是其中一个节点;第三类是开发者框架,比如 LangChain、LlamaIndex、Haystack,需要自己写代码组装。RAGFlow 属于第一类,但它在解析质量上和同类拉开了明显的差距。
| 项目 | 定位 | 文档解析能力 | 部署形态 | 协议 |
|---|---|---|---|---|
| RAGFlow | 开箱即用的 RAG 引擎 | 版面感知,最强 | Docker 多容器 | Apache-2.0 |
| AnythingLLM | 无代码文档问答 | 一般 | 桌面端 / Docker | MIT |
| Dify | LLM 应用平台 | 中等 | Docker | Apache-2.0 附加条款 |
| FastGPT | 知识库加工作流 | 中等 | Docker | Apache-2.0 附加条款 |
| LlamaIndex | 数据索引框架 | 较强,依赖 loader | Python 库 | MIT |
如果瓶颈在解析质量,文档里全是表格、扫描件、多栏排版和严格的条款结构,RAGFlow 基本是开源方案里的第一选择;如果 RAG 只是一个更大业务流程里的一环,需要审批、分支和对接一堆外部系统,Dify 的工作流和插件生态更成熟;如果只是几个人想和几百份结构简单的 PDF 聊聊天,AnythingLLM 的桌面端更轻,没必要维护一套多容器的服务;如果需要在代码层面完全掌控检索策略,那还是 LlamaIndex 配合 [[Qdrant]] 或 [[Milvus]] 自己搭更合适。另外值得一提的是 RAGFlow 采用的是纯粹的 Apache-2.0 协议,没有多租户或者去除 Logo 之类的附加限制,商业使用相对干净,这也是不少公司内部选型时会考虑的因素。
RAGFlow 内置的 DeepDoc 模块负责版面分析、OCR 和表格结构识别,支持 PDF、Word、PPT、Excel、TXT、图片、扫描件、网页和 EPUB 等格式。它会识别出页面上每个区域的类型,再把表格还原成带表头的结构化内容,而不是简单地按行抽取文字。除了 DeepDoc,RAGFlow 还可以选择接入 [[MinerU]]、Docling、MonkeyOCR、PaddleOCR-VL 等外部解析器,PDF 和 DOCX 中嵌入的图片也可以交给多模态模型去理解。实际体验下来,对于财报、技术规范、学术论文这类文档,它的解析结果明显比按页抽文本的方案靠谱得多。
RAGFlow 没有提供 chunk size 、overlap 等切块参数,而是根据文档类型给出十几种切片模板,比如 General、Q&A、Resume、Manual、Table、Paper、Book、Laws、Presentation、One 等。Laws 模板会以法条为单位切分,保证每一条完整;Presentation 模板让 PPT 每一页成为一个独立的块;Q&A 模板直接使用问答对格式的 Excel;One 模板则把整篇文档作为一个块,适合需要全文语境的场景。
我认为比模板更有价值的是切块结果的可视化。解析完成后可以在 Web 界面上逐块查看每个 chunk 的内容,直接编辑、禁用或者添加关键词。做过 RAG 的人都知道,回答不好的时候最难的是定位问题出在哪一步,大多数工具只能盲猜调参,而 RAGFlow 让你直接「看到」模型拿到的上下文长什么样,这一点在调优阶段帮了我很大的忙。
RAGFlow 的回答默认带有引用标记,点击可以回溯到具体的 chunk 以及原始文档里的位置,最近的版本连 Excel 的引用来源都能在预览中定位到单元格附近。这对法务、金融这种需要审计答案来源的场景非常重要,也是降低幻觉最实用的手段。检索阶段同时使用向量相似度和全文关键词检索,再经过 rerank 模型做融合重排,相似度阈值、关键词权重、Top N 都可以调整,界面上还有专门的检索测试面板,可以输入问题直接看召回了哪些块、分数是多少。
2026 年 8 月 RAGFlow 引入了一个我觉得很有意思的功能,叫做知识编译(Knowledge Compilation)。它可以在单个文档或整个数据集的层面,用大模型把原始内容「编译」成结构化的知识产物,包括 Wiki、知识图谱、树状结构、PageIndex、思维导图、时间线和 Skills。这其实是对传统 RAG 只做「碎片检索」的补充,之前的 GraphRAG 和 RAPTOR 层级摘要也可以看作这个思路的前身,现在被统一到了一个框架下,产物还可以查看、更新和重新生成。
同一时期上线的还有 Agentic RAG,也就是多步检索。面对复杂问题,模型会先分析问题,必要时拆成多个子问题分别检索,再汇总回答,并提供 Low、Medium、High、Ultra 四档思考强度。档位越高效果越好,但 token 消耗和延迟也会成倍上升,日常问答我一般用 Low 或 Medium 就够了。
RAGFlow 提供了可视化的 Agent 画布和一批预置模板,支持条件分支、代码执行、网页浏览、联网搜索、Agent Memory 和 [[MCP]] 等组件。数据源方面可以从 Confluence、[[Notion]]、Google Drive、S3、WebDAV、Azure DevOps、Google BigQuery 等地方同步,并支持增量更新,9 月的版本还新增了基于 sitemap.xml 抓取整站内容的能力,拿来给自己的文档站或者博客做一个问答助手非常方便。做好的助手可以部署到飞书、Discord、Telegram、Line 等渠道,可观测性上则接入了 [[Langfuse]]。
在 0.x 时代,RAGFlow 的后端是 Python 写的,主要分为提供 API 和 Web 服务的 ragflow_server.py,以及负责解析、向量化、图谱构建的 task_executor.py。依赖的中间件包括 [[Elasticsearch]] 或 InfiniFlow 自研的 Infinity 作为文档引擎、[[MinIO]] 作为对象存储、[[MySQL]] 保存元数据,以及 [[Redis]] 作为缓存和任务队列。这套组合功能完整但相当吃资源,也是很多人望而却步的原因。
2026 年 9 月 29 日发布的 v1.0.0-rc1,整个后端用 Go 全面重写,官方说法是大幅降低系统资源消耗,把原先的重量级部署模型换成轻量架构。几个关键变化值得注意:消息队列从 Redis 换成了 [[NATS]],缓存和检查点改由 [[Kvrocks]] 承担;服务拆分成 Admin、Syncer、Ingestor、API 等几个角色,可以用同一个二进制以不同参数启动;命令行工具 RAGFlow CLI 也用 Go 重新实现;文档引擎除了 Elasticsearch 和 Infinity,还可以选 OpenSearch、OceanBase 等。对 API 用户来说,已经标记为废弃的旧 API 别名在 Go 版本里不再支持,但 Python SDK 不受影响。
无论是 0.x 还是 1.0,RAGFlow 都不内置大模型和 embedding 服务,而是通过配置对接外部推理服务,包括 OpenAI、[[DeepSeek]]、Gemini、AWS Bedrock 等云端 API,以及 [[Ollama]]、Xinference 等本地推理框架。这意味着只要本地推理跟得上,完全可以做到全离线、数据不出内网的私有化部署。
官方推荐的配置是 4 核 CPU、16 GB 内存、50 GB 可用磁盘,Docker 版本不低于 24.0.0,Docker Compose 不低于 v2.26.1。需要提醒的是,官方镜像目前只面向 x86 Linux,README 里明确写了 macOS 暂不支持,ARM64 需要自行构建镜像,想在 Mac 上直接试玩的话最好找一台 Linux 服务器 或者虚拟机。
如果使用默认的 Elasticsearch 作为文档引擎,首先要检查内核参数 vm.max_map_count,否则 ES 容器会启动失败:
sysctl vm.max_map_count
# 小于 262144 时临时调整
sudo sysctl -w vm.max_map_count=262144
# 永久生效需要写入 /etc/sysctl.conf
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf
然后克隆仓库并切换到想要的版本标签。这里我的建议是,生产环境先用 v0.27.2 稳定版,想体验 Go 重写版本的话在单独的测试机上跑 v1.0.0-rc1:
git clone https://github.com/infiniflow/ragflow.git
cd ragflow/docker
git checkout v0.27.2 # 或 v1.0.0-rc1
docker compose -f docker-compose.yml up -d
一定要先 git checkout 到和镜像匹配的标签再启动,否则 docker 目录下的 compose 文件和 entrypoint 脚本可能与镜像版本不一致,出现一些莫名其妙的问题。首次启动需要拉取镜像和初始化数据库,时间会比较长。1.0 版本提供了一个健康检查接口,返回 200 就说明服务就绪了:
curl -f http://localhost/api/v1/system/healthz
# 启动失败时查看日志
docker logs --tail 50 ragflow-cpu
默认情况下 Web 界面监听 80 端口,浏览器直接访问服务器 IP 即可,API 服务则在 9380 端口。如果太早打开页面可能会看到 network abnormal 之类的提示,等容器全部就绪即可。需要修改端口、密码、文档引擎或者是否启用 GPU 时,编辑 docker/.env 中的 SVR_HTTP_PORT、MYSQL_PASSWORD、ELASTIC_PASSWORD、DOC_ENGINE、DEVICE 等变量,改完之后执行 docker compose up -d 重新创建容器。默认的密码都是 infini_rag_flow 这种公开值,只要服务暴露在公网,第一件事就是把它们全部改掉,并在前面加一层 [[Caddy]] 或 [[Nginx]] 反向代理配上 HTTPS。
注册登录之后,第一步是到模型供应商页面添加三类模型:对话用的 LLM、向量化用的 embedding 模型,以及重排用的 reranker 模型,填写模型名称、服务地址和 API Key 即可。我自己的组合是云端 LLM 负责生成,embedding 和 rerank 用本地 Ollama 或者便宜的国内 API,既能控制成本,文档内容在向量化阶段也不用全部发到海外服务。
这里有一个非常重要的坑:知识库创建时选定的 embedding 模型决定了向量维度,之后中途更换 embedding 模型会导致检索报错或者结果错乱,只能重建知识库重新解析。所以在大量导入文档之前,务必先想清楚用哪个 embedding 模型,别等解析了几千份文档之后才发现要换。
新建知识库时需要选择 embedding 模型、切片方法(也就是前面说的模板)以及解析器。上传文档之后点击解析,RAGFlow 会在后台排队处理,复杂的 PDF 在 CPU 上解析比较慢,几十页的扫描件跑几分钟很正常。解析完成后,我强烈建议不要急着去聊天,而是先打开文档的切块列表,挑几份最有代表性的文档逐块看一遍:表格是否完整、标题有没有和正文粘在一起、页眉页脚有没有混进来。如果某类文档切得不理想,就换一个模板重新解析,或者针对少数关键文档手动编辑 chunk。
切块确认没问题之后,用检索测试面板输入几个真实用户会问的问题,看召回的结果是否包含正确答案。如果相关的块排不上来,可以调低相似度阈值、调整关键词权重或者加上 reranker;如果召回了太多不相干的内容,就提高阈值或者减少 Top N。这一步做扎实了,后面对话助手的效果基本就有保障。我的经验是,绝大多数「RAG 回答不准」的问题都能在这两步定位出来,而不需要去换更贵的大模型。
最后创建一个 Chat 助手,关联一个或多个知识库,设置系统提示词、是否显示引用、无召回时的兜底回复等。系统提示词里我一般会写清楚「只根据知识库内容回答,找不到时明确说不知道」,配合引用溯源,可以显著减少模型自由发挥的情况。需要更复杂的流程,比如先判断问题类型再决定查哪个知识库,或者查不到时去联网搜索,就可以转到 Agent 画布里用组件拼出来。
RAGFlow 提供完整的 HTTP API 和 Python SDK,基址是 http://<host>:9380/api/v1,请求时带上 Authorization: Bearer <API_KEY>,API Key 可以在个人设置里生成。如果只是想把 RAGFlow 当作检索后端,在自己的应用里调用检索接口,用 Python SDK 写起来很简单:
from ragflow_sdk import RAGFlow
rag = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://127.0.0.1:9380")
dataset = rag.create_dataset(name="product-docs")
dataset.upload_documents([
{"display_name": "spec.pdf", "blob": open("spec.pdf", "rb").read()}
])
for chunk in rag.retrieve(question="部署的硬件要求是什么", dataset_ids=[dataset.id]):
print(chunk.content)
这种用法下,RAGFlow 只负责它最擅长的解析和检索,生成部分交给你自己的应用,灵活度会高很多。另外 RAGFlow 也提供了 MCP Server,可以把知识库的检索能力直接暴露给 [[Claude Code]]、[[Cursor]] 这类支持 MCP 的客户端,让编码助手在回答时查询公司内部的文档,这是我最近觉得最实用的一种接入方式。
资源方面不要低估。16 GB 内存只是起步,Elasticsearch 自己就会吃掉好几个 GB,.env 里的 MEM_LIMIT 默认给到了 8 GB 左右,再加上解析任务并发时的峰值,小内存的 VPS 很容易被 OOM。如果只是个人试用,可以考虑把文档引擎换成 Infinity 节省一些资源,但切换文档引擎的官方步骤里包含 docker compose down -v,-v 会把数据卷一起删掉,已有的知识库会全部清空,非全新部署一定要先备份。
升级要谨慎。从 0.x 升级到 1.0.0-rc1 有几个硬性前提:必须先升级到 v0.27.2,容器启动时会自动进行数据迁移,而且这个迁移是不可逆的,一旦完成就不能回滚到 0.27.2。同时 rc1 还有一些已知问题,比如开源版的 DeepDoc 只支持 CPU 推理、本地代码沙箱不再支持、Team/Me 的权限模型暂未实现。所以我的建议是先在测试环境用备份数据演练一遍升级流程,确认依赖的功能都正常之后再考虑生产环境,或者干脆等正式版。
版本迭代很快,文档经常跟不上代码。RAGFlow 几乎每两周就有一个版本,新功能多,但很多细节,比如某个切片模板到底怎么处理章节标题、图片归属哪个块,文档里不一定写清楚,很多时候要靠实测。遇到问题先去翻 GitHub Issues 和 Release Notes,往往比翻官方文档更快找到答案。
不是所有场景都需要它。如果文档量很小、基本都是干净的 Markdown 或纯文本,RAGFlow 那套多容器服务带来的资源和运维开销并不划算,这时候 AnythingLLM 甚至直接把文档塞进长上下文模型里可能都更省事。RAGFlow 的优势只有在文档结构足够复杂时才会真正体现出来。
RAGFlow 把文档解析作为重中之重来处理,确保进入知识库的内容是正确的,然后再处理检索和生成,如果你的资料中有大量的表格、扫描件、复杂排版等,RAGFlow 几乎是开源方案中最省心的选择。
2026-10-14 13:00:00
由于本地 IDE、插件、Agent 的差异,所以经常打开项目之后会出现一些只属于我自己的文件,AI Agent 和一些 MCP,Skill 也喜欢在项目根目录里面添加文件。每次 git status 都能看到这一串红色的 untracked 文件,看着很烦,又不太好意思为了自己的习惯去改项目的 .gitignore。这时候我想起了 .git 目录里一个存在感极低的文件 info/exclude,它刚好就是为这种场景设计的。

每次执行 git init 或者 git clone,[[Git]] 都会根据模板目录在 .git/info/ 下创建一个 exclude 文件。我之前在 关于 .git 目录你需要知道的一切 里只用一句话带过了它,说它和 .gitignore 作用相同。这个说法没错,但其实漏掉了最关键的一点:exclude 位于 .git 目录内部,而 .git 目录本身是不会被提交和推送的,所以写在里面的规则只对当前这个本地仓库生效,不会影响任何其他协作者。打开一个新仓库的 info/exclude,默认内容只有几行注释:
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
它的语法和 .gitignore 完全一致,支持 * 通配符、** 匹配任意层级目录、以 / 结尾表示只匹配目录、以 ! 开头表示取反等规则。规则和 .gitignore 一样。
Git 判断一个文件是否被忽略时,会从多个来源读取规则。理解这几个来源的分工,比记住某个文件的用法更有价值。按照官方文档,优先级从高到低依次是:
| 来源 | 位置 | 是否随仓库共享 | 适合放什么 |
|---|---|---|---|
| 命令行参数 |
--exclude 等 |
否 | 临时性的一次性过滤 |
.gitignore |
仓库内各级目录 | 是,会被提交 | 所有人都应该忽略的文件,如 node_modules/、target/、构建产物 |
info/exclude |
.git/info/exclude |
否,仅当前仓库 | 只和你自己在这个项目里的工作方式有关的文件 |
| 全局忽略文件 |
core.excludesFile,默认 ~/.config/git/ignore
|
否,对本机所有仓库生效 | 和操作系统、编辑器相关的文件,如 .DS_Store、.idea/
|
如果一个文件是项目本身会产生的,任何人 clone 下来编译都会遇到,就应该写进 .gitignore 提交上去;如果是我的电脑、我的编辑器在所有项目里都会产生的,比如 [[macOS]] 的 .DS_Store,就放到全局忽略文件里;剩下那些只跟”我在这个具体项目里的习惯”有关的文件,才是 info/exclude 的主场。这三层各管一摊,项目的 .gitignore 也就不会被每个人的个人偏好塞得乱七八糟。
上面表格里的优先级不只是一个排列顺序,它会直接影响 ! 取反规则的效果。Git 的规则是同一来源中后出现的匹配优先,不同来源中高优先级来源的匹配优先。我在本地用 Git 2.56 做了一个小实验:.gitignore 里写了 *.log,然后在 info/exclude 里写 !a.log 想把它”捞回来”,结果用 git check-ignore -v 一看,a.log 依然被 .gitignore:1:*.log 这条规则忽略,info/exclude 里的取反完全没起作用。
$ git check-ignore -v a.log
.gitignore:1:*.log a.log
反过来就可以:info/exclude 里写 *.txt,.gitignore 里写 !b.txt,那么 b.txt 会重新出现在 git status 中。所以 info/exclude 只能”多忽略一些”,没法推翻项目 .gitignore 的决定。这其实挺合理的,本地个人规则本来就不应该和团队约定对着干,但如果你哪天发现某条取反规则不生效,可以先想想是不是写错了层级。
git check-ignore -v 这个命令非常值得记住,它会告诉你某个文件到底被哪个文件的第几行规则命中。当忽略规则分散在多个 .gitignore、info/exclude 和全局配置里的时候,靠肉眼排查非常痛苦,用这条命令几秒就能定位问题。
结合自己平时的使用,下面这些是我经常往 info/exclude 里加的东西:
# 个人笔记和草稿
notes.md
TODO.local.md
scratch/
# 只在本地使用的调试脚本
debug.sh
*.local.sh
# 本地环境变量和工具配置
.envrc
.tool-versions
# AI 编程工具的本地文件
CLAUDE.local.md
.aider*
第一类是个人笔记和草稿。我习惯在项目根目录放一个 notes.md,记录排查问题的过程、待办事项或者 review 时的疑问,这些内容对其他人没有价值,但对我自己很重要,放在项目目录里随手就能打开。第二类是本地调试脚本,比如一个连着测试数据库跑某个接口的 shell 脚本,里面可能还写死了本地的端口和账号。第三类是像 [[direnv]] 的 .envrc 这类本地环境配置,团队里不一定每个人都用同样的工具,把它写进项目 .gitignore 反而显得多余。最后一类是这两年新增的需求,[[Claude Code]]、[[Aider]] 之类的 AI 编程工具会在项目里生成各种本地配置和记忆文件,如果项目没有统一约定,放在 info/exclude 里是最不打扰别人的做法。
编辑方式也很直接,用任何编辑器打开 .git/info/exclude 追加内容即可,或者直接用一行命令:
echo "notes.md" >> .git/info/exclude
需要注意,如果你在一个 submodule 里操作,它的 git 目录并不在 submodule 根目录下的 .git 文件夹,而是在父仓库的 .git/modules/<name>/ 中,这时候用 $(git rev-parse --git-dir)/info/exclude 来定位会更保险。
我日常大量使用 git worktree 来同时处理多个分支,所以专门验证了一下 info/exclude 在 worktree 里的表现。结论是:所有 worktree 共享主仓库的 info/exclude。在主仓库的 .git/info/exclude 里写上 notes.md,然后新建一个 worktree,在里面创建同名文件,git status 同样不会显示它。这是因为 worktree 自己的 git 目录里只存放 HEAD、index 这类和工作区强相关的文件,而 info/exclude 位于通过 git rev-parse --git-common-dir 得到的公共目录中。
这个行为对我来说是好事,配置一次,所有 worktree 都生效,不用每开一个分支就重新写一遍。但也意味着你没办法给某一个 worktree 单独设置不同的本地忽略规则,如果真有这种需求,只能借助其他手段,比如在那个 worktree 的子目录里放一个不提交的 .gitignore,再把这个 .gitignore 本身写进 info/exclude。
这是使用 exclude 时最常见的误区,.gitignore 也有同样的问题:忽略规则只对 untracked 文件生效。如果一个文件已经被提交到仓库里,你再把它写进 exclude,Git 依然会跟踪它的修改,git status 照样会显示 M。我在实验仓库里提交了 t.cfg,再把它加进 exclude 并修改内容,git status 毫不客气地列出了它。
很多人想用 exclude 来解决”项目里有个配置文件,我本地要改,但不想提交”的问题,这种场景应该用的是另外两个命令:
# 告诉 Git 这个文件在本地会被修改,不要提交本地的改动
git update-index --skip-worktree config/application-local.yml
# 取消
git update-index --no-skip-worktree config/application-local.yml
# 查看哪些文件设置了 skip-worktree
git ls-files -v | grep '^S'
另一个经常被拿来对比的是 --assume-unchanged,它的设计初衷是性能优化,告诉 Git 不用去检查某个文件是否变化,在大仓库或者慢速文件系统上减少 lstat 调用。用它来隐藏本地修改虽然也能凑合,但语义不对,在某些操作下 Git 可能会重新检测并覆盖你的改动。如果目的是”保留本地修改不提交”,--skip-worktree 才是更合适的选择。当然,这两种方式在上游修改了同一个文件时都会让 git pull 报冲突,需要先取消标记再处理,这也是我更倾向于让项目提供 xxx.example 模板文件、真正的本地配置文件不入库的原因。
最后提一个容易踩的坑。被 info/exclude 忽略的文件,在 Git 眼里就是普通的 ignored 文件,和 node_modules/ 没有任何区别。如果你习惯用 git clean -fdx 或者 git clean -fX 来清理工作区,那么你的 notes.md、调试脚本也会被一并删除,而且这些文件从来没有被提交过,删了就真的找不回来了。
$ git status --short --ignored
!! a.log
!! notes.md
$ git clean -nX
Would remove a.log
Would remove notes.md
我的建议是执行 git clean 之前先加 -n 做一次 dry-run,看清楚会删哪些文件再动手。另外 git clean 支持 -e 参数临时添加排除规则,比如 git clean -fdx -e notes.md 可以在清理时保住这个文件。真正重要的笔记,最好还是放在项目目录之外,或者定期同步到自己的笔记系统里,别把 exclude 当成一个安全的存放位置,它只是让 Git 看不见这些文件,并不会保护它们。
.git/info/exclude 是一个非常小的功能,小到很多用了多年 Git 的人都没注意过它,但它恰好填补了 .gitignore 和全局忽略文件之间的空白:既不污染团队共享的规则,又能精确到单个仓库。如果你也有一堆不想提交但是每次又都很烦的文件,不妨现在就配置一下 exclude 文件试试。
2026-09-28 13:00:00
上一篇 Meta Muse 介绍与注册指南:一个会替你跑腿的个人 AI Agent 把 [[Muse]] 是什么、怎么注册、额度怎么算都整理了一遍,但是留了一个结尾:注册完之后到底该让它干什么。Muse 上线这几周,我查阅了一些测评体验,整理了 X 和 Threads 上用户晒出来的用法,发现大家的使用路径其实很像:从提醒和文件这类零风险的任务开始,确认它靠谱之后再慢慢接入邮箱、日历,最后才让它碰钱。这篇就按这个顺序来整理,从界面和设置讲起,再到具体的案例和踩过的坑。

Muse 的 App 结构不复杂,但几个入口藏得比较深,第一次用很容易找不到自己要的东西。和它对话、布置任务都在 Chat 标签页,它的交互方式就像发消息,你可以连着发好几条,不用等它回复。不知道能让它做什么的时候,可以去 Ideas 标签页看看官方推荐的任务,或者直接问它「你能帮我做什么」。它生成的文件,比如 PDF、表格、仪表盘,都会放到 Library 标签页里。
| 想做的事 | 入口 |
|---|---|
| 对话、布置任务 | Chat 标签页 |
| 找任务灵感 | Ideas 标签页 |
| 查看它生成的文件 | Library 标签页 |
| 提醒和定时任务 | 助手图标 → Upcoming |
| 查看它做过什么 | 助手图标 → Activity log |
| 连接或断开应用 | Settings → Connectors |
| 设置什么时候需要先问你 | Settings → Permissions |
| 导入记忆、关闭训练 | Settings → Data controls |
另外一个很实用的功能是侧边对话(side chat),在 Chat 标签页左上角的菜单里点铅笔图标就能新开一个。它适合把不同主题分开,比如一个专门聊旅行计划,一个专门处理账单。Muse 的记忆是跨对话共享的,所以分开聊并不会让它忘掉你在别处说过的事,只是让每个对话的上下文更干净。
第一步在对话中告诉 Muse 你的名字、时区和偏好,比如「回答尽量简短」「我不吃辣」「周末不要安排工作」,Muse 会把这些存成记忆,之后的任务都会参考。你也可以让它换个名字、换种说话语气,这些都不用去设置里找,直接说就行。
第二步是设置权限,这一步个人认为最重要。在 Settings → Permissions 里,连接器和网页访问都有两档:「Ask for some actions」表示所有会改变东西的操作(发送、购买)以及一部分重要的读取操作要先问你;「Always ask」表示任何操作都先问你。刚开始用的时候建议全部设成 Always ask,虽然会多点几次确认,但你能清楚看到它每一步想干什么,这是建立信任最快的方式。
第三步是只接一个应用,而且先给只读权限。直接说「连接我的 Gmail」,或者去 Settings → Connectors 点 Connect 登录。大多数连接器都可以设成只读,也就是它能看你的数据但不能替你做事。比较稳妥的顺序是先接日历只读,再接邮箱只读,用顺手了再考虑放开写权限。Facebook、Instagram、Threads 只要在同一个账户中心里会自动连上,这一点注意一下,不想让它看的话要手动去断开。
Muse 每次要执行需要确认的操作时,会弹出一张审批卡片,选项有五个:Allow once(只允许这一次)、Allow for this task(这个任务里都允许)、Allow for this site(在这个网站上都允许)、Always allow(这类操作以后都允许)、Deny(拒绝)。每张卡片上还有一个 See task details,点开能看到它具体要做什么,我的建议是初期每次都点开看一眼再决定。
这几个选项里,Always allow 要格外小心。它是按「某个连接器的某一类操作」来放权的,一旦对发邮件、发帖或者付款这类操作点了 Always allow,之后它就不会再来问你了。发出去的邮件是撤不回来的,所以涉及发送和付款的操作,最好一直停留在 Allow once。
Muse 用浏览器干活的时候,你还可以随时介入:Open browser 可以实时看它在网页上的操作,Take control of the browser 会让它暂停,由你接手操作页面,Stop the task 则直接终止任务。遇到登录、验证码或者它明显走错路的时候,自己接手处理完再交还给它,往往比让它反复重试要快得多。
和聊天机器人不同,给 Agent 布置任务更像是给一个新来的助理交代工作。说得越清楚,它来回确认的次数越少,消耗的 token 也越少。综合官方文档和用户的经验,有几个技巧比较管用。
交代清楚背景和标准。不要只说「帮我找个搬家公司」,而是说清楚从哪里搬到哪里、几居室、有没有电梯、希望什么时候搬,以及一个好结果应该长什么样,比如「给我三家报价,按价格排序」。
指定输出格式。你想要表格、列表、PDF 还是一段简短的文字,最好直接说出来。有人反馈说没指定格式时,它经常默认生成一个 Word 文件。
划定边界。比如「只搜索我的邮件,不要看短信」,或者「预算不超过 200 美元」。尤其是填表和预约类任务,在结尾加一句「提交之前停下来给我看」,这句话几乎是所有教程里都会出现的一句。
把持续性任务写成检查,而不是目标。与其说「帮我盯着这件事」,不如说「每天早上检查一次 Gmail,只有对方回复了才告诉我」。前者太模糊,后者它知道该做什么、什么时候该来找你。
直接贴步骤。Muse 目前不能让普通用户安装自己的 Skill 文件,但你可以把一套写好的操作步骤直接贴在任务里,效果差不多。另外,结果不满意就直接纠正,「太长了」「语气随意一点」,它会慢慢记住你的偏好。
下面这些案例按风险从低到高排列,前面几类出错了也没什么损失,适合刚上手时练习,后面几类涉及外部沟通和花钱,建议等你熟悉审批机制之后再尝试。
最简单的起点是定时提醒,比如「每周一早上 9 点提醒我提交工时表」,它会一直重复,直到你取消为止,还支持基于位置的提醒。在此基础上可以升级成定时简报:「每个工作日早上 7 点,给我发一份 5 条要点的行业新闻摘要,并附上来源链接」。Muse 还会主动提醒哪些文章需要付费订阅才能看。所有定时任务都可以在 Upcoming 里查看和管理。
需要注意的是,这些定时任务本质上是在聊天里推送给你的提醒,Muse 目前没有「定时发帖」这类功能,如果要定时发布内容,得接入第三方排程工具作为连接器。
Muse 能读 PDF、DOCX、纯文本、Markdown、XLSX、CSV 和常见图片格式,最常见的用法是丢一份文件过去,说「帮我总结一下,并列出我需要处理的事项」。合同、账单、学校通知这类文件特别适合这样处理。
反过来它也能生成东西,而且因为背后有一台完整的虚拟机,它能生成的东西比聊天机器人丰富得多。比如「做一份一页的 PDF,对比三款适合自由职业者的记账软件」,或者「做一个按类别追踪每月开销的仪表盘」,之后你说「加一行」,它会直接修改同一个文件,而不是重新生成一份。
接入 [[Gmail]] 和日历之后,最直接的问法是「明天日程上有什么,哪些邮件还没回」。Muse 的邮件连接器是按需搜索,不会把你的整个收件箱下载下来,除非你明确要求。日历则是少数会主动推送的连接器,你的日程一有变化,它会自动收到通知。
我们可以让 Muse 从邮件里筛出学校和游泳课的通知,追踪各种截止日期,同步到日历上,家里人随时可以问它最近有什么安排。这类「从一堆邮件里提取结构化信息」的任务,正是 Agent 比人做得又快又不容易漏的地方。
这是目前用户晒单最多的一类,也是最能直观感受到 Agent 价值的地方。一个比较典型的起手式是:「翻一遍我过去 12 个月的 Gmail 收据,列出所有我可能已经不用的定期扣费」,或者「列出我正在付费的所有订阅,写上月费和最后一次使用的时间」。
退款和申诉也很适合交给它。有一些案例是让 Muse 处理 Google Cloud 上一个闲置数据库产生的费用,第一次申请被拒,Muse 起草了跟进邮件之后,39 分钟后 114.14 美元被全额免除。银行手续费减免、保险账单争议也是类似的套路:让它起草申诉,你确认后发送,然后「每周跟进一次,直到对方回复」。
X 上还有人晒出让 Muse 跟宽带公司谈判,把月费从 80 美元砍到 30 美元,或者锁定 5 年优惠价的案例。这些都是用户的自述,没有第三方核实,看看就好,但至少说明这个方向是可行的。
Muse 在 9 月 17 日开放了给美国商家打电话的 Beta 功能,可以帮你预约理发、问店里有没有货、找承包商要报价、订餐厅。打完之后会给你一份通话记录和摘要。Yahoo Finance 的评测者让它帮忙找搬家公司报价,Muse 问清楚了新旧地址、几居室、有没有楼梯或电梯,然后联系了本地四家公司,大约两分钟后就有两家打电话过来,其中一家当场报了价,另外几家发了邮件。
不过这个功能要多留个心眼。路透社报道,Meta 在内部测试时,有一部分电话其实是外包的人工客服打的,而且测试者事先并不知情,其中一次谈宽带账单的通话里,外包人员还说了带种族歧视的话。Meta 事后承认没有提前说明是个失误,已经暂时撤回了人工代打,并表示会在有明确告知的前提下再推出。另外也有用户反映电话打不通,或者商家一听是 AI 就挂断。所以电话里涉及的个人信息,尽量控制在完成任务所需的最少范围。
预约类任务是 Muse 宣传里的主打场景。Yahoo Finance 的评测者给了几个约会的大致想法,Muse 给出四个方案,他选了一个,通过 [[OpenTable]] 直接订好了位子。另一位用户让它在 [[Zocdoc]] 上找医保网络内的家庭医生,网上预约不可用时,它把事情转交给用户,第二天又主动提醒,直到预约完成。
购物的体验则更复杂一些。同一位评测者让 Muse 在任天堂官网买一款 Switch 2 游戏,它每一步都会告诉你在做什么,需要登录时把账号密码存进它自己读不到的安全存储,最后列出总价,把下单按钮留给你来按。整体很顺畅,但花的时间明显比自己去官网买要长。付款默认走 Link by [[Stripe]],每次生成一次性虚拟卡。现在 [[Shopify]] 已经开放了整个商品目录给 Muse,Walmart、Best Buy、Sephora 等也在陆续接入,但 Amazon 已经屏蔽了 Muse,Target 等网站在结账环节也会拦截自动点击。这位评测者的看法是,Muse 最适合一次性采购很多不同的东西,比如办派对或者搬家,单买一件东西自己动手反而更快。
Muse 能在你关掉 App 之后继续工作,所以它很适合处理需要持续一段时间的事情。比如「我的目标是在 11 月前上线一个月更的邮件通讯」,它会拆出一份计划,每周来跟你确认一次进度。Yahoo Finance 的评测者则让它接入 Yahoo 梦幻橄榄球账号,找出预测得分最高的防守组,提交了堪萨斯城酋长队的 waiver 申请,第二天申请通过后,它自动把酋长队排进了首发阵容。
这类任务建议放在最后尝试,因为它意味着你要让 Muse 在很长一段时间里、在你不盯着的时候自主行动,前提是你对它的审批边界已经心里有数。
9 月 18 日上线的 Mac 版能直接操作你电脑上的文件和应用,包括 iMessage、备忘录、提醒事项、邮件和日历。官方给的例子是「把上个月的三张发票整理到一个文件夹里」,也可以让它根据电脑里的文件填表,或者汇总当天的邮件、信息和笔记做一份日终总结。
Mac 上的应用不走连接器,而是在 Settings → File System Access 里逐个设置 Off、Read only 或 Read and interact 三档。上一篇提到过,Inc. 的记者就遇到了 Muse 在没有明确授权的情况下同步了他的 Messages 数据库,所以这里我的建议是,默认全关,只给当前任务需要的那一两个应用开只读。好消息是它删除的文件会进废纸篓,误删了还能找回来。
如果你用的服务不在连接器列表里,可以直接让 Muse 自己写一个,前提是那个服务有公开的 API 或命令行工具,凭据会存进安全存储。这对开发者来说挺有吸引力,比如接入自己的记账系统或者 Home Assistant。但要注意,Meta 只对官方连接器做功能、安全和法务审查,你让 Muse 做的自定义连接器是没人审过的,接入前要先看清楚那个服务的条款和它能拿到的权限。
它会编造信息。Yahoo Finance 的评测者问一家餐厅有没有无麸质啤酒,Muse 先给了一个电话号码,接着又换成另一个,追问之下它承认第一个号码是自己编的,没有核实过。它推荐的餐厅里也有一家早就关门了,被指出后它解释说用了「过时的数据」。CNN 的测试也遇到了同样的问题。所以电话、地址、营业时间这类事实性信息,它给出来之后最好自己再确认一遍。
两步验证码要你自己来。Muse 的邮件连接器会过滤掉一次性验证码、重置密码链接和登录链接,这是出于安全考虑,但也意味着遇到需要验证码的网站,得你亲自把验证码输进去,这时候 Take control of the browser 就派上用场了。
断开连接不等于它忘了。断开一个连接器只是不让它获取新数据,它之前已经看到的内容可能还留在记忆和聊天记录里,要彻底清除得明确让它忘掉。如果是手机或 Mac 上的系统级权限,还要去系统设置里一并撤销。
长任务可能会卡住。浏览器任务跑得太长时经常会停住等你帮忙,加上网站的反爬拦截,有时候一件事来回折腾半天。遇到这种情况,与其反复让它重试消耗 token,不如自己接手把卡住的那一步做完。
提示词注入还没有解决。Meta 自己也承认 Muse 并非对攻击免疫,它在网页上读到的内容里可能藏着恶意指令。Sentinel 能拦下大部分越界操作,但这也是为什么发送和付款最好永远保留人工确认。
Muse 目前最擅长的是那些流程清晰、跨好几个网站或服务、需要持续跟进,但单步又不太难的琐事:翻收据找订阅、追退款、比报价、盯截止日期。这些事情人不是做不了,而是懒得做、容易忘、容易半途而废,而 Agent 恰好不会累也不会忘。反过来,需要精确事实、需要快速完成、或者出错代价很高的事情,它现在还不太靠得住。
我的建议是把它当成一个刚入职的助理来带:先用 Always ask 看它怎么干活,从提醒和文件这类零风险任务开始,一个一个地接入应用、一点一点地放开权限。每次放权之前问自己一句,如果它这一步做错了,我能不能承受。等你摸清了它的能力边界,再把那些真正让你头疼的琐事交给它,这时候它才会真正替你省下时间。