在 OCManager 中通过 OCAI 启用 ops-mate 打通 AI 诊断路径

本文是《部署 OCManager 智能运维平台并集成 AI 服务实现 EKS 节点的监控和维护》的续篇。

前一篇我们完成了 OCManager 控制平面部署、ocm-agent 批量下发到 EKS 节点、以及 OCAI 的接入。但留下了一个遗憾:OCAI 只能"咨询",无法到节点上执行命令做诊断。本文将解决这个问题。

为什么 OCAI "只能看不能动"?

前一篇结尾,我提到 OCAI service 无法直接执行命令"还是有点麻烦的"。当时系统里其实已经存在两条命令执行路径:

  1. 受控命令下发(agent mTLS 通道):控制台 → sysdiagnose → TMS(task-manager) → gateway →(mTLS :8989)→ ocm-agent。这是"命令助手"用的,安全受控、有审计、走制品 MD5 校验,但流程较重。
  2. AI 诊断(SSH 旁路):OCAI → diagagent → diaglite → ansible → SSH → 目标节点。这是 ops-mate,灵活、AI 驱动多轮诊断。

ops-mate 到底是什么?官方文档对这块讲得比较模糊,实际上:ops-mate = diaglite + diagagent

  • diaglite(:8787)本质是封装 ansible,负责通过 SSH 在目标机执行命令
  • diagagent(:8788)是AI 诊断编排器,负责多轮 LLM 调用和诊断逻辑编排

启用过程

由于 diaglite 依赖ansible,因此需要在控制平面安装 ansible,目标 EKS 节点不需要装任何东西(EC2 默认自带)。因为 ansible 是控制端工具。

bash 复制代码
sudo yum install -y ansible-core

装完验证版本:

bash 复制代码
$ ansible --version
ansible-core 2.15.3

同时会装上 sshpass,后面用得上。

编译 diaglite/diagagent

源码在 ocai-service/ops-mate/diaglite/,用 go build 编译:

bash 复制代码
cd ocai-service/ops-mate/diaglite/
go build -o diaglite ./cmd/diaglite
go build -o diagagent ./cmd/diagagent

生成 diaglitediagagent 两个二进制文件。

配置文件

修改 diaglite 的 config.yaml(listen 指定端口,ansible 段指定 SSH 参数)。这里指定的cluster-key.pem是因为我们在创建EKS节点的时候指定了SSH密钥,因此可以直接拿来复用,如果没有指定密钥,也可以通过SSM document下发密钥。

yaml 复制代码
# ocai-service/ops-mate/diaglite/config.example.yaml
listen: ":8787"
sqlite_path: "./data/diaglite.sqlite"

ansible:
  binary: ansible
  user: ec2-user
  port: 22
  private_key: "/home/ec2-user/.ssh/cluster-key.pem"
  become: false
  work_dir: "./data/ansible_work"
  host_key_checking: false
  ssh_extra_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
  run_timeout: 300s
  forks: 10
  batch_max_hosts: 500

rag:
  index_paths:
    - "./corpus"
  max_file_bytes: 1048576

修改 diagagent.yaml(base_url指向 diaglite):

yaml 复制代码
# ocai-service/ops-mate/diaglite/diagagent.yaml
listen: ":8788"

llm:
  base_url: "https://bedrock-mantle.us-east-1.api.aws/v1"
  api_key: ""   # 留空,由环境变量 DIAGAGENT_LLM_API_KEY 注入
  model: "moonshotai.kimi-k2.5"
  temperature: 0.2
  max_tokens: 4096

diaglite:
  base_url: "http://127.0.0.1:8787"

agent:
  max_rounds: 16
  task_poll_interval: 2s
  task_timeout: 15m
  max_output_chars: 16000

server:
  default_session_timeout: 30m

Docker 网络问题

ocai-service 跑在 Docker 里,如果 .env 里配 127.0.0.1:8788,容器内会指向自己而不是宿主机。

要用宿主机的 docker0 网桥 IP:

bash 复制代码
$ ip addr show docker0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

所以 .env 里要配:

bash 复制代码
OCAI_DIAGAGENT_BASE_URL=http://172.17.0.1:8788
OCAI_DIAGLITE_BASE_URL=http://172.17.0.1:8787

OCAI 容器镜像重新构建

改配置后 OCAI 容器需要重建。遇到的问题如下

(1)Dockerfile syntax 镜像源问题

Dockerfile 第一行是 # syntax=docker/dockerfile:1.7,构建时要拉取 syntax 镜像。默认走了某个出 TLS 错误的镜像源(docker.mirrors.sjtug.sjtu.edu.cn)。因为这个 Dockerfile 其实没用任何 1.7 特有语法,直接把 syntax 那行删掉即可绕过。

(2)ModuleNotFoundError: No module named 'ocai'

启动之后,容器一直重启,日志报找不到 ocai 模块。

通过以下命令确认 /app/ocai 目录确实存在

bash 复制代码
$ docker run --rm --entrypoint ls ocai-service:latest -la /app/ocai/
total 40
-rw-rw-r-- 1 ocai ocai    0 __init__.py
drwxrwxr-x 8 ocai ocai  185 ai
...

# 看启动脚本 deploy/start.py,核心是:uvicorn.run("ocai.main:create_app", factory=True, host=..., port=...)
# 在 /app 目录下手动导入没有异常
$ cd /app
$ python3 -c 'import ocai; print(ocai.__file__)'
/app/ocai/__init__.py 

# 但换个目录再运行脚本能复现现象
$ cd /tmp && python3 /app/deploy/start.py
ModuleNotFoundError: No module named 'ocai'

关键差异在 sys.path[0]:python3 /app/deploy/start.py 时,sys.path0 是脚本所在目录 /app/deploy。

由于Dockerfile 用了 uv sync --no-install-project(只装依赖不装项目本身),而 python deploy/start.py 运行时 sys.path[0] 是脚本所在目录 /app/deploy 而不是 /app,所以找不到 /app/ocai。在 docker-compose.yml 的 environment 里显式补上 PYTHONPATH

yaml 复制代码
services:
  ocai-service:
    environment:
      PYTHONPATH: /app

(3)日志目录权限问题

复制代码
PermissionError: [Errno 13] Permission denied: '/app/logs/ocai.log'

挂载进容器的宿主机日志目录属主不对。容器里以 ocai 用户(uid 1001)运行,而宿主机目录默认属主是别的用户,于是写不进去。把目录属主改成 1001:

bash 复制代码
sudo chown -R 1001:1001 <LOG_DIR>/ocai-service

进程常驻

我们用 systemd service托管diaglite,配置如下diaglite.service:

ini 复制代码
[Unit]
Description=OCManager DiagLite Service
After=network.target

[Service]
Type=simple
User=ec2-user
WorkingDirectory=/home/ec2-user/ocai-service/ops-mate/diaglite
ExecStart=/home/ec2-user/ocai-service/ops-mate/diaglite/diaglite -config config.yaml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

diagagent.service (注意 Environment 注入 LLM key,对应 diagagent.yaml 里留空的 api_key):

ini 复制代码
[Unit]
Description=OCManager DiagAgent Service
After=network.target diaglite.service

[Service]
Type=simple
User=ec2-user
WorkingDirectory=/home/ec2-user/ocai-service/ops-mate/diaglite
Environment=DIAGAGENT_LLM_API_KEY=<你的API Key>
ExecStart=/home/ec2-user/ocai-service/ops-mate/diaglite/diagagent -config diagagent.yaml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

启动服务

bash 复制代码
sudo systemctl daemon-reload
sudo systemctl enable diaglite diagagent
sudo systemctl start diaglite diagagent

端到端验证

测试底层 diaglite

diaglite 的任务是异步 的:先 POST /v1/tasks 拿到任务 id,再轮询 GET /v1/tasks/{id} 到终态。

bash 复制代码
# 创建任务
$ curl -s -X POST http://127.0.0.1:8787/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{"ip":"192.168.19.155","command":"hostname && uptime","title":"e2e"}'
{"id":"bb05d7649d81...","status":"pending", ...}

# 轮询结果
$ curl -s http://127.0.0.1:8787/v1/tasks/bb05d7649d81...
{
  "status": "success",
  "output": "192.168.19.155 | CHANGED => {\n  \"rc\": 0,\n  \"stdout\": \"ip-192-168-19-155...\"\n}"
}

底层用的是 ansible,所以 output 是 ansible 的 CHANGED / rc=0 结构。

测试 diagagent(AI 编排层)

bash 复制代码
curl -s -X POST http://127.0.0.1:8788/v1/agent/sessions \
  -H "Content-Type: application/json" \
  -d '{"ip":"192.168.19.155","q":"查看这台机器的主机名和运行时间","timeout":"3m"}'

AI 多轮采集后会返回 Markdown 报告:

markdown 复制代码
## 主机信息诊断报告

### 目标机
- IP: 192.168.19.155

### 关键信息
| 项目 | 值 |
|------|-----|
| 主机名 | ip-192-168-19-155.cn-north-1.compute.internal |
| 操作系统 | Amazon Linux 2023 |
| 内核版本 | 6.12.92-122.166.amzn2023.x86_64 |

### 补充信息
- 实例类型: AWS EC2(从主机名判断,位于 AWS 中国宁夏区域)
- 系统状态: 正常,无 systemd 失败服务
- 资源概览: 内存 7.8GB(可用 5.9GB),磁盘 30GB(使用率 26%)

OCAI 前端验证

打开 OCAI 聊天窗口,输入"在192.168.19.155 执行 top 命令查看进程",能看到 AI 主动调用 diagnose_machine 工具。

示例查询在192.168.19.155 执行ss -ntlp命令查看链接情况

再看 health 端点确认依赖已启用(OCAI 容器对外映射到 18000):

bash 复制代码
$ curl -s http://127.0.0.1:18000/health
{
  "status": "ok",
  "capabilities": ["streaming", "tool_calling", "user_confirmation", ...],
  "dependencies": {
    "oc_manager": {"enabled": true},
    "msg_etl":    {"enabled": true},
    "diag_agent": {"enabled": true},
    "mcp":        {"enabled": false}
  }
}

diag_agent: {"enabled": true} 就是启用成功的标志。容器启动日志里也能看到注册了 5 个诊断工具:

复制代码
Diag agent wired: diagagent=http://172.17.0.1:8788 diaglite=http://172.17.0.1:8787 tools=5
Registered diag tool: diagnose_machine
Registered diag tool: get_diagnosis_session
Registered diag tool: list_recent_diagnosis_sessions
Registered diag tool: list_diagnosis_presets
Registered diag tool: search_diagnosis_kb

结语

从"只能咨询"到"能到节点上执行诊断",算是补上了前一篇的遗憾。

主要的两条命令执行通道的分工为:

  • AI 诊断(SSH 旁路):追求灵活,适合只读/诊断类操作,AI 可以自主决定采集哪些信息
  • 受控命令下发(agent mTLS):追求可信可审计,关键操作必须有人工审批和制品校验

可以理解为,AI 的"自主性"应该被限制在诊断层面,真正需要"动手"改配置、重启服务这类操作,还是应该走受控通道并保留人工审批环节。如果要让 AI 走 agent 受控通道执行命令(带人工审批),需要在 ocai-service 里新增工具调用 TMS 的 /api/v1/ops_tasks 接口。这需要写代码,是另一个话题了。

相关推荐
慕容引刀1 小时前
告别Commit信息纠结:使用Git AI Commit插件实现规范化提交
人工智能·git·github·visual studio code·visual studio
QiLinkOS1 小时前
企业知识产权战略的“X光”透视报告:从“外包代工”到“法律军火库”的终极分野
大数据·人工智能·dna双螺旋归因模型·技术专利避坑指南·qilink·技术专利风险情报·技术专利洞察
风途科技~1 小时前
河道流量监测不用下水!雷达水流测速仪非接触测流更安全
人工智能·安全
weixin_397574091 小时前
企业认知基础设施是什么
人工智能
开发者小布1 小时前
AI 开发工具生态扫盲:中转平台、Cursor、Claude Code 如何协同
人工智能
ksueh1 小时前
AI 小说创作新手实战指南
人工智能
水龙吟啸1 小时前
华为2026.6.17机考选择题+编程题【速刷敲黑板】
人工智能·深度学习·算法·华为
OBiO20131 小时前
靶向脂肪组织的AAV如何选择启动子?
人工智能
accept 99%1 小时前
RAG从0到简单入门的一篇文章
人工智能