让家里的电脑变成一个 Telegram 机器人:带记忆的私人 AI 助手
上一篇搭好了一套跑在家里旧台式机上的本地 AI:35B 模型常驻、网页版对话、编程 agent。但有一个缺口 —— 手机上不好用。
网页版能用,可要打开浏览器、输一串 IP;而编程 agent 干脆用不了,因为它拒绝监听非本机地址(理由很正当:它能执行命令,谁访问到谁就控制了这台机器)。
这篇解决它:接一个 Telegram 机器人,在微信一样的聊天窗口里和自己的模型说话。而且它记得住 —— 上次说过的事,这次还在。
为什么 Telegram 比隧道好
我原本给编程 agent 搭了 SSH 隧道,笔记本上够用。但手机上做隧道很别扭。
Telegram 这条路完全绕开了这个问题:
传统做法 手机 → 公网 → 你家路由器端口映射 → 服务 要开端口,有暴露面
Telegram 手机 → Telegram 服务器 ← 你家电脑主动连出 零端口暴露
你的电脑是主动连出去的,不需要任何入站端口,不用碰路由器,不用动态域名。这在安全上比自己开端口干净得多。
选哪个 agent 框架
我已经装了 DeepSeek Harness(上一篇里的编程 agent)。这次用的是 Nous Research 的 Hermes Agent,因为两者定位不同:
| DeepSeek Harness | Hermes Agent | |
|---|---|---|
| 像什么 | 可拆解的编程工具 | 常驻的个人助手 |
| 入口 | Web UI + CLI | 15+ 平台:Telegram / Discord / Slack / WhatsApp / 邮件 / Home Assistant… |
| 记忆 | 会话内 | 跨会话累积,写进 MEMORY.md / USER.md |
| 定时任务 | 无 | 内置 cron |
| 架构 | 一切皆插件(135 个) | 三层:接口 / agent 逻辑 / 执行后端 |
简单说:dsh 是坐在电脑前写代码时用的,Hermes 是随时从手机戳一下的。 两个可以共存,我现在两个都留着。
安装前:先读那个安装脚本
官方的装法是这样:
iex (irm https://hermes-agent.nousresearch.com/install.ps1)
这行命令的意思是「把远程服务器上的脚本直接交给系统执行」。 这台机器上有我所有的实验数据和 SSH 密钥,所以我没有直接跑,而是先下载下来读一遍:
curl -fsSL -o hermes-install.ps1 https://hermes-agent.nousresearch.com/install.ps1
# 243 KB,读它做了什么
grep -nE "github.com|git clone|InstallDir" hermes-install.ps1
grep -niE "setx|schtasks|New-Service|HKLM|Set-NetFirewall|RunAs" hermes-install.ps1
审计结果是干净的:
仓库 github.com/NousResearch/hermes-agent 官方
装到 %LOCALAPPDATA%\hermes 用户级,不动系统目录
schtasks 只停/启它自己的任务 不创建别的持久化项
防火墙 无任何操作
提权 无
然后用我读过的这一份来装,而不是再从网上拉一次(避免下载到的和执行的是两个版本)。
这个习惯值得保留。 不是因为这个项目可疑 —— 恰恰相反,它的安装脚本写得相当克制。而是因为 curl | bash 这个模式本身把「信任」交给了一次网络请求,而读一遍只花五分钟。
接本地模型
Hermes 支持 18+ 家模型服务商,也支持任何 OpenAI 兼容端点。指向本机:
hermes config set model.base_url http://127.0.0.1:11435/v1
hermes config set model.provider custom
hermes config set model.default custom/qwen3.6:35b
(11435 是我上一篇里那个剥离思考模式的小代理,直连 Ollama 就是 11434。)
这里有个坑:文档里写的是 hermes config set CUSTOM_BASE_URL ...,但它会提示:
'CUSTOM_BASE_URL' is not a recognized config key — it was saved anyway,
but Hermes may not read it.
“存了但可能读不到” —— 这种半成功最麻烦。正确的键要去 config.yaml 里找,是 model.base_url。凡是工具提示“不认识这个键”,就别当它成功了。
验证:
hermes -z "Reply with exactly: HELLO" --cli
回了 HELLO,而且我的代理日志里出现了对应请求 —— 证明确实走的本地模型,不是云端。
申请机器人
两分钟的事,在 Telegram 里:
- 搜 @BotFather,发
/newbot,给机器人起个名字和一个以bot结尾的用户名 - 它给你一串 token,形如
8877670322:AAxxxxx - 再搜 @userinfobot,发任意消息,拿到你自己的数字 ID
第三步很重要 —— 用来锁定只有你能用这个机器人。
访问控制:默认拒绝,但你得配对地方
这一步我差点搞砸,值得展开说。
先看它的授权逻辑(源码里写得很清楚):
"""
Checks in order:
1. Per-platform allow-all flag
2. Environment variable allowlists (TELEGRAM_ALLOWED_USERS, etc.)
3. DM pairing approved list
4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true)
5. Default: deny
"""
默认拒绝,这是好的默认值。但我一开始这样配:
hermes config set TELEGRAM_ALLOWED_USERS 7622580257
它同样提示“不是识别的键,但还是存了” —— 存进了 config.yaml。而代码里是用 os.getenv() 读的,只认环境变量。
正确位置是 .env 文件:
TELEGRAM_BOT_TOKEN=8877670322:AAxxxxx
TELEGRAM_ALLOWED_USERS=7622580257
TELEGRAM_ALLOW_ALL_USERS=false
这次的错误方向是“更安全”的(白名单没生效 = 谁都用不了,包括我自己),但如果哪天配的是 ALLOW_ALL_USERS=false 却写错了地方,方向就反过来了。安全相关的配置,一定要验证它真的生效,不能只看命令有没有报错。
启动,然后手机上就能用了
hermes gateway run
第一条消息发过去,机器人回了。同时我在两处日志里确认了链路完整:
gateway 日志 session agent:main:telegram:dm:762258... ← 我的 user id,白名单生效
代理日志 RESULT /v1/chat/completions reasoning=0 ← 请求到了本地 35B
这时它会提示一句:
No home channel is set. Type /sethome to make this chat your home channel.
发个 /sethome 就好 —— 定时任务的结果会送到这个聊天。
记忆:它真的记得住吗
这是 Hermes 相比普通聊天界面最大的区别。测一下:
我: Remember this: my winpc runs qwen3.6:35b with a 196608 token context.
它: Done. Saved to memory.
但我没信这句话。 去找文件,MEMORY.md 不存在。
文档说记忆在 MEMORY.md / USER.md,而实际写在了 hermes\memories\USER.md:
User's Windows PC runs qwen3.6:35b with a 196608 token context window.
然后开一个全新的进程问它:
我: What context length does my winpc model run with? Answer from memory only.
它: Your Windows PC model (qwen3.6:35b) runs with a 196,608 token context window.
跨会话记忆确认可用。 但如果我信了那句 “Done. Saved to memory” 就不会去查,也就不会知道文件在哪、更不会知道它到底存没存。
这是整个过程里我最想强调的一点:程序说“完成了”,和事情真的完成了,是两件需要分别验证的事。
开机自启:四个服务,三个第一次都没起来
这台机器上现在有四个服务要开机自启:
OllamaServe 模型服务 + 自动预热
OpenWebUIServe 网页版 + 代理
DshServe 编程 agent
HermesGateway Telegram 机器人
模式都一样:一个守护脚本(崩了自动重启)+ 一个 schtasks 开机任务:
schtasks /create /tn HermesGateway ^
/tr "powershell.exe -NoProfile -ExecutionPolicy Bypass -File F:\hermes\serve-hermes.ps1" ^
/sc onstart /ru SYSTEM /rl HIGHEST /f
但配置正确不等于能跑起来。 我每个都实拉验证了一次(停掉手动实例,用计划任务启动),四个里有三个第一次失败:
失败一:SYSTEM 找不到命令
'dsh' 不是内部或外部命令
因为 dsh 是 npm 用户级全局安装,在 C:\Users\<用户>\AppData\Roaming\npm。计划任务以 SYSTEM 身份跑,读不到那个用户的 PATH。写绝对路径解决。
这个错误很好查 —— 它明确报错了。
失败二:进程活着,但什么都没启用
这个阴险得多。Hermes 的 gateway 启动了、进程在跑、任务状态 Running,但手机上发消息没反应。日志里只有两行 WARNING:
No env user allowlists configured
No messaging platforms enabled
它读到了一份空配置,于是“正常地”什么都不做。
根因同样是 SYSTEM 身份:Hermes 用 HERMES_HOME(默认是调用账户的 %LOCALAPPDATA%\hermes)定位 config.yaml 和 .env。SYSTEM 下那个路径解析到别处,读到空的,没有 token 也没有白名单。
解法是在守护脚本里把路径钉死:
$env:HERMES_HOME = "C:\Users\syang\AppData\Local\hermes"
$env:LOCALAPPDATA = "C:\Users\syang\AppData\Local"
$env:USERPROFILE = "C:\Users\syang"
如果我只检查“进程在不在”,会得出部署成功的结论。
排查过程里我还犯了个错值得记:为了确认 SYSTEM 的 %LOCALAPPDATA% 是什么,我起了个进程去 echo 它,得到的结果和用户账户一样 —— 让我一度排除了路径的嫌疑。但那个探测进程是从用户会话派生的、继承了用户环境,探测方法本身就是无效的。真正的证据是日志里那两行 WARNING,而不是我构造的测试。
失败三:多个实例抢同一个端口
编程 agent 那次:我以为自己重启了三次服务,实际每次新实例都因端口被占而立刻崩溃、守护脚本再拉起、再崩溃,而浏览器全程连着那个老进程 —— 所以我改的配置“看起来没生效”。
原因是我按命令行字符串匹配进程来杀,而真正持有端口的子进程命令行和我以为的不一样。正确做法是按端口占用者杀:
Get-NetTCPConnection -LocalPort $p -State Listen |
ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }
现在的样子
winpc(RTX 3060 12GB / 32GB RAM)
├─ Ollama qwen3.6:35b 常驻,196608 上下文
├─ thinkoff 代理 剥离思考 + 锁定上下文 + 请求日志
├─ Open WebUI :3000 浏览器,经 Tailscale
├─ dsh :3080 编程 agent,仅本机 + SSH 隧道
└─ Hermes gateway Telegram 机器人,跨会话记忆
四个服务全部开机自启,崩溃自动重启,每一个都实拉验证过
日常用法变成了:在 Telegram 里给自己发消息。问问题、让它读文件、记住点什么,下次接着聊。数据不出家门,不花 API 费用,手机在任何地方都能用。
如果你也要装
按顺序:
- 先读安装脚本,
curl | bash之前花五分钟看看它做什么 - 配置项提示“不认识这个键”就当它失败了,去源码或 config 文件里找正确的键名
- 安全配置要验证生效,不能只看命令有没有报错
- 开机任务必须实拉一次,配置正确和能跑起来是两回事
- 服务在跑 ≠ 服务在工作,查它的功能日志而不只是进程列表
- 杀进程按端口,不要匹配命令行
第 5 条是这次最大的收获。前一篇我总结说「最花时间的是搞清楚为什么它没按预期工作」,这次要补一句:其中最难的一类,是它看起来正在工作。