cloudplz

本页是译文,以英文原文为准。 阅读原文

写作

把 AI Agent 当基础设施管:用 Ansible 预置 Hermes Agent

为什么我像预置云基础设施一样预置自己的 AI agent —— 以及让它可复现的几个模式。

我花了 18 年让云基础设施变得可复现。数据库用 Terraform module。VM 配置用 Ansible role。 golden image 用 Packer 模板。等到要在 Linux VM 上跑一个 AI agent,我没多想 —— 顺手就上了 Ansible。

三天、15 个 commit 之后,我就有了一个完全幂等的 playbook:从裸 Ubuntu 一路把 Hermes Agent 预置到生产可用。 那些模式呢?全是数据库和缓存集群那套做法。这不是巧合 —— 这正是重点。


还没人解决的问题

常见的做法是:装好工具,手工配置,下次重做全凭记忆。等到 VM 需要重建,问题就来了。

AI agent 会积累状态:

  • 记忆文件,跨会话不断增长和演化
  • 配置:模型 endpoint、API key、MCP server 定义、安全策略
  • Profile —— 彼此隔离的人格,各有配置、记忆和 gateway 连接
  • Skill:从 Git 仓库克隆来的
  • Hook 和定时任务,在生命周期事件上触发行为
  • 十来个不同 LLM provider 和集成的密钥

丢任何一样,都得手工恢复几个小时。更糟的是,想在新 VM 上复现整套环境,你得记得三周前跑过的每一条 config set。

这个问题,平台工程十年前就给数据库解决过:会积累状态的基础设施,必须声明式预置,不然就会腐烂。


架构:11 个任务文件,一个 role

整个 playbook 就是一个 Ansible role,里面 11 个任务文件,每个都由 tag 和 feature flag 控制开关:

harden → install → configure → memory → profiles →
mcp → skills → hooks → services → gateway → cron_jobs

每个阶段负责什么:

阶段做什么
hardenSSH 锁定、UFW 默认拒绝 + 放行 22、unattended-upgrades
install克隆 Hermes,安装 uv + Python venv、npm 依赖、PATH 软链接
configure从模板渲染 config.yaml、.env、SOUL.md、honcho.json
memory播种 MEMORY.md 和 USER.md(只做一次 —— 首次运行后归 agent 所有)
profiles用 hermes profile create 建隔离的 agent 人格
mcp按 hermes_mcp_servers 列表安装 MCP server 运行时(npm/pip)
skills把 skill 仓库克隆到 ~/.hermes/skills/
hooks播种事件 hook(活动日志、启动检查清单)
services给 agent 守护进程做 systemd unit 和健康检查 timer
gatewayhermes-gateway.service —— 消息守护进程 + OpenAI 兼容的 API server
cron_jobs用 hermes cron create 播种定时任务

每个阶段都能用 --tags 单独跑。首次预置之后,改一次配置就是 --tags configure, 12 秒跑完。


从云基础设施迁移过来的模式

这不是一篇「Ansible 项目好酷」的文章。有意思的是:每个模式都直接来自预置数据库和缓存集群。做过大规模 IaC 的话,这些模式会很眼熟。

1. 给子系统做 feature flag

每个 Hermes 子系统在 defaults/main.yml 里都有一个开关:

hermes_enable_mcp: true              # MCP servers + npm/pip installs
hermes_enable_skills: true           # Clone skill repos
hermes_enable_hooks: true            # Event hooks
hermes_enable_scheduled_tasks: true  # Cron-based prompt execution
hermes_enable_gateway: true          # Messaging daemon + API server

这跟我在 Aurora 集群上用的模式一样 —— Performance Insights、Enhanced Monitoring 这类可选特性都是布尔开关,不是独立 module。你肯定不想为了关掉一个子系统去 fork role。

2. 只播种一次,然后放手

记忆文件用 force: false:

- name: Seed MEMORY.md
  ansible.builtin.template:
    src: memory.md.j2
    dest: "{{ hermes_home }}/memories/MEMORY.md"
    mode: "0644"
    force: false   # Hermes owns this after first run

Ansible 写入初始内容。之后,agent 每个会话都重写自己的记忆。重跑 playbook 不会覆盖已经演化过的状态。

这跟我处理 RDS parameter group 一模一样 —— Terraform 负责创建,DBA 在运行时调具体参数。IaC 工具只播种起始状态,漂移归运行时系统管。

3. 文件归属模型

这个模式我最坚持。系统里每个文件有且只有一个归属方:

Ansible manages (rewrites every run):
  ~/.hermes/config.yaml
  ~/.hermes/.env
  ~/.hermes/SOUL.md

Ansible seeds once (force: false):
  ~/.hermes/memories/MEMORY.md    ← agent writes during sessions
  ~/.hermes/memories/USER.md      ← agent writes during sessions

Agent owns (Ansible never touches):
  ~/.hermes/state.db              ← session DB (SQLite)
  ~/.hermes/logs/                 ← runtime logs

没有歧义,也不会有「这是谁改的、为什么改」这类困惑。Ansible 管的,模板就是唯一事实来源;agent 拥有的,Ansible 不碰。这跟我在 Terraform 和应用团队之间定的归属契约一样 —— module 拥有资源生命周期,应用自身配置归团队管。

4. 用 ansible-vault 管理密钥

所有密钥都在 group_vars/all/vault.yml 里,用 ansible-vault 加密:

vault_glm_api_key: "..."           # Primary LLM provider
vault_openrouter_api_key: "sk-or-..."
vault_discord_bot_token: "..."
vault_honcho_api_key: "..."        # Persistent memory backend
vault_hermes_api_key: "..."        # API server bearer token

.env 模板按条件渲染每个 key:

{% if vault_glm_api_key is defined and vault_glm_api_key %}
GLM_API_KEY={{ vault_glm_api_key }}
{% endif %}

明文密钥不进 Git —— 渲染出来的 .env 只活在主机上,靠文件系统权限保护。仓库里没有 .env 文件。也没有「先硬编码一下,以后再改」。你在数据库凭据、云 provider key 上是什么纪律,这里就是什么纪律。

5. 幂等的 Profile 创建

Profile 通过 Hermes CLI 创建,外面包一层幂等的 Ansible task:

hermes_managed_profiles:
  - name: vector
    model: "glm-5.1"
    provider: "zai"
    install_gateway: true
    soul_template: "soul-vector.md.j2"
    memory_template: "memory-vector.md.j2"

playbook 跑 hermes profile create(名字已存在就跳过),每次运行还会重新 apply config set。systemd gateway service、.env、SOUL.md —— 每个 profile 各来一套。一个数据结构,产出三个彼此隔离的 agent 人格。

这就是 Terraform module 的模式 —— 声明你要什么,让工具自己判断该创建还是该更新。


重新预置这件事

任何 IaC 方案真正的考验:能不能把 VM 炸掉再重建?

# 1. Terraform: new VM
# 2. Update vault_vm_ip, re-encrypt
# 3. Run the playbook
ansible-playbook -i inventory.yml playbook.yml --ask-vault-pass
# 4. Done. Long-term memory comes back via Honcho, config from Ansible.

状态边界值得说清楚。长期记忆放在 Honcho 里 — — 一个外部持久记忆后端,跟 Hermes 内置本地记忆并行运行,不取代后者。所以主机上是这么个局面: state.db 和 logs/ 可丢弃,配置和密钥来自 Ansible 模板。真正会在主机上演化的只有两个文件 — — MEMORY.md 和 USER.md —— 由定时 off-box 状态备份兜底。「计算可丢弃」之所以成立, 只因为状态里每一个字节都有明确归宿,而那归宿不是 VM 磁盘。

这份契约还有后半段:漂移。agent 每个会话都重写自己的记忆,其中一部分是有益的漂移 — — 它学到了你从没写下的上下文规则。备份能捕获它,但不会把它纳入版本控制。规则很直接: agent 沉淀下来的东西,只要该成为以后每次预置的基线行为,我就手工收进 Git 里的 SOUL.md 模板。 配置住 Git,记忆住备份,两者之间的提升,必须是人刻意去做的。

这跟 RDS 是同一个模式:数据住托管存储,计算可丢弃。重新预置离一张白纸,就一个 terraform apply 加一次 ansible-playbook。


回退路由:因为主 provider 会挂

主力模型是 GLM-5.1,走智谱官方一等支持的 zai provider。但 LLM provider 有用量窗口、速率限制,偶尔还会宕。playbook 配了自动故障转移:

hermes_fallback_enabled: true
hermes_fallback_provider: "minimax-cn"
hermes_fallback_model: "MiniMax-M2.7"

zai 返回 429、500 或 503 时,Hermes 在 turn 中途切到 MiniMax,对话历史照样保留。故障转移按 turn 生效 —— 下一条用户消息会重试主力,所以某个 provider 糟一个小时,不会把你钉死在 fallback 上。每个 turn 只试一次;fallback 也失败,走正常错误处理。

这跟只读副本故障转移是同一招,但有个注意点,数据库工程师一定能体会:「副本」并不等价。fallback 在 context window、指令遵循行为上都可能跟主力不同,所以故障转移是降级路径,不是透明切换。挑 fallback 时,context window 要能覆盖你典型会话长度。谈不上精巧,但覆盖了 90% 的情况:某个 provider 糟一个小时,你的 agent 照样干活。


什么时候该这么做

条件对了,这套做法才有回报:

你的 agent 跑在 VM 上,而且会积累状态。 记忆文件、skill、定时任务、MCP server 配置 — — 丢任何一样都得花几小时手工恢复,那你需要 IaC。

同一个 agent 你要跑几个月,而不是每周换着玩。 playbook 结构上的前期投入,要到你不换 agent、 只迭代配置的阶段才收得回来。三天十五个 commit,之后每次改动就是 vars.yml 里一行, 加上一次 12 秒的 --tags configure。

你要在新机器上复现这套环境。 不管是 VM 迁移、灾难恢复,还是再起一个实例 —— 如果「这东西怎么重建? 」的答案是「我不记得了」,那你需要声明式预置。

你本来就按 IaC 的方式思考。 如果你对 Ansible role、Terraform module 这类工具很熟, 用到 AI agent 上就是自然延伸。模式一模一样 —— 变的只是 workload。

反过来也一样:**如果你还在评估该用哪个 agent,或者配置每周都在变,那就先别做这个。 ** 声明式预置的回报,建立在稳定 workload 之上。把移动靶自动化,只会换来一个永远过期的 playbook。 先手工跑一个月 agent;等配置不再频繁变动,那才是写 role 的信号。


结语

平台工程的本能很简单:只要它跑在基础设施上,就按基础设施来对待。

AI agent 现在就是基础设施。它们跑在 VM 上,会积累状态,需要密钥管理、健康监控、可复现的预置,还要一套干净的重新预置方案。这些模式早就有了 —— 在数据库和缓存集群上,我们已经用了很多年。

这里没有一个模式是为这个项目发明的。feature flag、只播种一次的语义、归属契约、vault 密钥管理、幂等创建 —— 全是我在 Aurora 集群和 Terraform module 上用的同一套纪律。workload 是新的,纪律不是。而且 agent 越自主,跳过的代价越高:拿无状态微服务的心态去套 agent,换来的不是一次 crash,而是灾难性失忆。声明式状态管理,才是它规模化该走的路。

如果你在生产环境跑 AI agent(或者快要跑了),值得像对待其他基础设施一样去预置它们。


*这里说的 playbook 预置的是 Hermes Agent — — 来自 Nous Research 的开源 AI agent。任何跑在 VM 上、会积累状态的 agent,这些模式都适用。 *