在小型 GCP VPS 上尝试用 OpenCode 接管个人助理
我希望把日常对话、QPC 知识库与日历逐步放进一个个人助理,同时控制小型 VPS 的资源占用。已有 Hermes 网关正在使用另一只 Lark 机器人。这个实验的目标是暂停 Hermes,在同一台共享小机上尝试运行 OpenCode,并使用 personal-assistant 的机器人验证接管链路。
**同日补充:**第一次窗口结束前的消息被中断;经再次授权,重开受保护的短时窗口后,真实 Lark 两轮私聊均收到回复,第二轮正确使用了前文。模型任务期间的内存峰值和压力依然偏高,尚不能据此判断适合在这台小机上长期常驻。
这次实验的边界很窄:测试短时对话、资源和回退;QPC 与日历不是这次已经实现的能力。两只机器人独立,暂停 Hermes 会暂时影响原机器人,不会自动把它的会话和权限迁移到新机器人。
为什么不能只比较一个 RSS
在决定切换前,观察到 Hermes 主进程的一次 RSS 约 341 MiB,另有进程数据进入 swap。本机 WSL 中 OpenCode 空闲服务的主进程 RSS 大约 224–262 MiB。但 Hermes 当时正在承担实际网关工作,OpenCode 的本机测试没有接真实 Lark 或模型。不同机器、不同负载和不同统计口径不能直接得出完整替代方案的节省量。
共享实例约有 952 MiB 可见内存,Hermes 运行时多次采样的可用内存只有一百多 MiB,还有明显 swap 使用。相比直接叠加一个服务,先暂停旧网关、在相同机器上测量完整链路,更接近实际问题。
把切换做成可回退试验
方案是由已有的轻量 Lark 入口负责本人身份检查、消息去重与回复状态,OpenCode 仅在本机回环地址提供模型对话接口。服务使用非特权用户、单独工作目录、内存和 CPU 限额;OpenCode 工具权限默认拒绝,试验不开放文件修改、Shell 或其他外部动作。模型凭证和机器人凭证分开提供,不放入仓库或文章。
切换前设置了十分钟自动回退,并持续观察内存余量、内存压力和关键服务状态。试验失败或达到时限时,停止试验服务,恢复 Hermes gateway 和 A2A bridge。两个服务独立恢复,是因为 bridge 没有声明会自动跟随 gateway 的依赖关系。
对话适配器每轮创建独立 OpenCode 会话,只加入已确认送达的有限历史;调用超时不自动重试。模型回答和 Lark 成功投递分开记录。OpenCode 自身会在状态目录写入会话数据,尝试调用 API 删除会话并不等于证明底层数据被彻底擦除,因此试验使用独立目录并在结束后清理。
不仅是定时:使用 systemd 构建企业级自动化运维体系
不仅是定时:使用 systemd 构建企业级自动化运维体系
在 Linux 环境下,如何让服务“永远在线”?对于企业级 Agent 系统而言,答案通常不是编写一个死循环的 Shell 脚本,而是将任务交给系统最底层的管家——systemd。
1. 为什么弃用用户态 Daemon?
在之前的运维实践中,我们尝试过使用自定义的 blog_daemon 进行自动化调度,但结果证明它不可靠:
- 状态丢失:Daemon 若因崩溃、重启或环境抖动导致 PID 文件丢失,整个调度机制就会“瘫痪”。
- 权限与依赖限制:Daemon 难以管理任务间的依赖顺序,也无法精准进行资源限制(如 CPU/内存配额)。
2. systemd 的核心优势
将任务转交给 systemd(通过 .service 与 .timer),意味着将任务的生存责任交给了内核:
- 内核级保障:
systemd与内核紧密协作,即使服务崩溃,它也能通过Restart=always实现秒级恢复。 - 资源限制 (Cgroups):通过配置
MemoryMax或CPUQuota,可以防止 Agent 因死循环或内存泄露耗尽主机所有资源。 - 依赖管理:通过
After=network.target,确保服务在网络就绪后再启动,解决了 Daemon 启动时常见的“网络不可用”问题。
3. 局限性与避坑指南
尽管 systemd 强大,但它并非完美,在生产环境中需注意:
systemd 的局限性:
- 侵入性强:配置需要 Root 权限。在部分云原生环境(如只读文件系统)中,我们无法触及系统级目录,此时
systemd会失效。 - 配置复杂:相比 crontab,
systemd的配置逻辑冗长。如果项目环境多变,配置迁移成本高。 - 单点风险:
systemd本身是系统守护进程,一旦它出现异常,整个系统的服务管理将瞬间停滞(尽管这种情况极少见)。
避坑指南:
- 配置验证:在上线前,务必使用
systemd-analyze verify检查语法,防止服务启动失败导致雪崩。 - 日志归一化:
systemd托管的服务日志默认由journald管理,需配置好日志轮转,防止/var/log空间被写满。 - 非 Root 环境替代方案:如果环境限制无法使用
systemd(如无 Root 权限的容器内),请考虑supervisord或基于 容器调度器 (K8s/Nomad) 的健康检查机制,而非编写简易 Daemon。
4. 企业级最佳实践:Systemd 配置模板
# /etc/systemd/system/agent-task.timer
[Unit]
Description=Daily Agent Task Timer
[Timer]
OnCalendar=*-*-* 12:00:00
Persistent=true
[Install]
WantedBy=timers.target
总结
对于企业级 Agent 系统,追求“高可靠性”的关键不在于代码写得多么复杂,而在于如何高效地利用操作系统的管理能力。systemd 是 Linux 运维的定海神针,但我们也需清楚它的边界——当系统权限受到限制时,拥抱云原生架构(如 GitHub Actions/K8s)将是更优雅的选择。
理解 Linux 进程:信号机制与守护进程的生与死
理解 Linux 进程:信号机制与守护进程的生与死
在 Linux 系统中,进程是资源分配和调度的主体。理解进程的运行状态、如何终止以及如何实现优雅的重启,是保障服务高可用的基石。
进程的生命周期
进程从 fork() 开始,经历运行、睡眠、僵尸 (Zombie) 等状态。
- 运行状态 (R):正在 CPU 上执行或在运行队列中等待。
- 睡眠状态 (S/D):等待某种事件发生(如 I/O 完成)。
- 僵尸状态 (Z):进程已终止但尚未被父进程回收,占用了进程描述符 (PID)。
信号 (Signals):进程间的“紧急通信”
信号是 Linux 中一种异步通知机制,用于通知进程发生了特定事件。
| 信号 | 名称 | 作用 |
|---|---|---|
| SIGTERM | 终止信号 | 请求进程优雅退出,允许进程清理资源 |
| SIGKILL | 强制杀死 | 立即终止进程,进程无法捕获或忽略 |
| SIGHUP | 终端挂起 | 常用于通知守护进程重新加载配置文件 |
优雅退出的艺术
许多线上事故源于不当的进程终止方式。
1. 为什么拒绝 kill -9?
强制使用 SIGKILL 会打断进程正在进行的事务(如数据库写入),导致数据不一致或内存溢出。
2. 优雅终止流程
守护进程应捕获 SIGTERM 信号:
- 停止接收新请求。
- 处理完当前正在进行的任务。
- 释放已申请的 socket、数据库连接。
- 进程退出。
# 推荐做法:首先发送 SIGTERM
kill <PID>
# 等待几秒后,如果进程依然存在,再视情况决定是否强制清除
# (但尽量避免直接使用 kill -9)
运维场景
在生产环境中,部署新的配置后,通常通过发送 SIGHUP 信号触发服务的“平滑重启”,实现零停机部署: