拆解 Olav:一个工业级多 Agent NetOps 平台的架构全景

0 阅读5分钟

在 AI Agent 开发中,我们往往被大模型的推理能力所震撼。但在面对网络基础设施级别的真实任务时,单一 Agent 的"全能"反而是致命的 —— 万一 Agent 幻觉误删了核心路由配置?万一它在业务高峰期重启了 OSPF 进程?

解决这些问题的核心不在于让 AI "更聪明",而在于构建一套分权制衡的多 Agent 架构

本文完整拆解 Olav 的技术栈 —— 从 Agent 层级到沙箱隔离,从确定性模拟到数字孪生验证。

1. 架构全景:六大 Agent 分工

diagram-1.webp

olav "R1 的 BGP 邻居?"        → Quick Agent(SQL 查询,一步完成)
olav --agent ops "模拟链路故障"  → Ops → Analysis(networkx 计算)
olav --agent ops "检查所有设备"  → Ops → Probe(并行 SSH)
olav --agent ops "部署数字孪生"  → Ops → Lab(ContainerLab)
olav --agent audit "创建审计"    → Audit → Designer(Profile 设计)

每个 Agent 的权限是架构级约束

# Analysis: network_isolation=True — 内核级网络隔离
execute_in_sandbox(code, network_isolation=True)  # unshare --net

# Lab: network_isolation=False — 必须访问 CLAB API
execute_in_sandbox(code, network_isolation=False)  # httpx → clab:8080

# Probe: 不用沙箱,直接 Nornir SSH — 但有白名单/黑名单双重门禁

2. 数据层:快照模型 + TextFSM 4 层优先级

所有网络状态存储在 DuckDB 的 netops schema:

-- 每次采集生成唯一 snapshot_id
SELECT * FROM netops.parsed_outputs WHERE snapshot_id = 'snap-2026-04-06-0300';

-- 自动生成视图(无需手动建表)
SELECT * FROM v_bgp_neighbors_auto WHERE state != 'Established';

TextFSM 模板解析 CLI 输出,4 层优先级:

优先级 1: workspace/ops/config/textfsm/        ← 用户自定义覆盖
优先级 2: workspace/ops/config/templates/custom/ ← /learn_cmd 学习生成
优先级 3: workspace/ops/config/templates/        ← 内置默认
优先级 4: ntc-templates 包                       ← 社区模板

CommandRegistry.reload() 支持热重载 —— 新增模板不需重启。

3. Analysis Agent:确定性模拟引擎

这是 Olav 架构最精巧的部分。Analysis Agent 只有一个工具run_python_simulation

沙箱内可用的全局变量:

变量类型用途
dbDatabaseProxydb.query(sql) — 只读生产快照
simSimulationProxysim.clone(tables) + sim.execute(sql) — 内存可写副本
nxnetworkx图引擎:DiGraph, shortest_path, connected_components
netutilsnetutilsIP 计算:子网重叠、接口规范化

What-If 模拟的核心流程:

# 1. 克隆生产数据到内存
sim.clone(['topology_links', 'ospf_neighbors', 'bgp_neighbors'])

# 2. 变异:模拟 R2 故障
sim.execute(
    "UPDATE sim_topology_links SET link_status='down' WHERE source_device=?", ['R2']
)

# 3. 构建加权有向图
G = nx.DiGraph()
for link in sim.execute("SELECT * FROM sim_topology_links WHERE link_status='active'").fetchall():
    G.add_edge(link[0], link[1], weight=link[4], interface=link[2])

# 4. OSPF 最短路径 — 真实 Dijkstra,按 OSPF cost 加权
if nx.has_path(G, 'R1', 'R4'):
    path = nx.shortest_path(G, 'R1', 'R4', weight='weight')
    cost = nx.shortest_path_length(G, 'R1', 'R4', weight='weight')

# 5. 影响范围 — 精确计算,非 LLM 推理
components = list(nx.connected_components(G.to_undirected()))
isolated = [n for n in all_devices if n not in G]

_result = {
    "blast_radius": isolated,
    "path_after_failure": path,
    "ospf_cost": cost,
    "connected_components": [list(c) for c in components]
}

关键设计决策: LLM 写代码,沙箱执行代码。模拟结果是数学计算,不是概率猜测

4. Lab Agent:数字孪生 CAB 门禁

Lab Agent 的完整流程:

# Step 1: 从快照 DB 发现生产拓扑
devices = db.query("SELECT hostname, ip_address, platform FROM netops.devices")
links = db.query("SELECT * FROM netops.topology_links WHERE link_status='up'")
configs = db.query("SELECT device_name, raw_output FROM netops.raw_output_store WHERE command='show running-config'")

# Step 2: 构建 CLAB YAML + 翻译配置到 SRL CLI
yaml_str = build_clab_yaml(devices, links)  # 在沙箱中执行
srl_configs = translate_cisco_to_srl(configs)  # Cisco → SRL CLI

# Step 3: 部署(deploy_lab 自动处理 CLAB bug)
deploy_lab(yaml_content=yaml_str)

# Step 4: 推送配置(base64 + stdin 管道)
for device, cfg in srl_configs.items():
    b64 = base64.b64encode(f"enter candidate\ndiscard now\n{cfg}\ncommit now\n".encode()).decode()
    resp = httpx.post(f"{base_url}/api/v1/labs/{lab}/exec",
        params={"nodeFilter": f"clab-{lab}-{device}"},
        json={"command": f"bash -c 'echo {b64} | base64 -d | sr_cli 2>&1'"},
        headers=headers, verify=False)

# Step 5: 验证协议收敛
exec_on_node(lab_name=lab, node="R1", 
    command="show network-instance default protocols bgp neighbor")

# Step 6: 报告 PASS/FAIL — 不修复,只报告

设计者 (Analysis) 和验证者 (Lab) 分离 — 如果方案有问题,Lab 报告失败原因并分类:

  • BLOCKER — 设计错误(如 iBGP 无 IGP)
  • PREREQ — 缺少前置条件(如无到对端 loopback 的路由)
  • SYNTAX — 平台版本限制(如 SRL v26.3.1 不支持 peer-group 下的 ebgp-multihop)

5. 安全栈

Layer 1: sandbox_guard        执行前静态扫描(识别 HTTP 写/DB 写/危险命令)
Layer 2: DuckDB monkey-patch  沙箱内所有 duckdb.connect() 强制 read_only=True
Layer 3: network namespace    Analysis 运行在 unshare --net 中,物理断网
Layer 4: 命令白名单/黑名单     每条 CLI 必须在 commands 表中 + 不匹配黑名单正则
Layer 5: HITL 审批门           写操作挂起等待人工确认

6. Skill 插件体系

.olav/workspace/ops/analysis/
├── SKILL.md       # 声明: tools: [run_python_simulation], network_isolation: true
├── prompts/system.md  # Agent "人设" + 工作流模板
├── references/        # ROUTING_EXPERT_GUIDE.md
└── tools/             # run_python_simulation.py → ../../tools/ (symlink)

olav skill install <path> 安装新能力。支持 parent_agent 子技能注册。跨 workspace 委托 olav_delegate()。工具文件统一存放在 ops/tools/(单一真相源),子 agent 通过 symlink 引用。

7. 审计系统

两阶段 Map-Reduce:

  1. Designer — 交互式创建 Profile(SQL 查询 + P50/P90/P95 统计阈值)
  2. Auditor — 执行 Profile → JSON 结果 → LLM 渲染 → Markdown 健康报告
# profiles/bgp-health.md
---
name: bgp-health
schedule: daily
---
## Job: Down Neighbors
SELECT device_name, neighbor_ip, state FROM v_bgp_neighbors_auto WHERE state != 'Established'
severity: high

Designer 在写入 SQL 前验证表名和列名存在database_introspection 工具),避免写出引用不存在表的 profile。

结语

Olav 的架构核心不在于 AI 有多强大 —— 在于约束有多精确

每个 Agent 只做一件事。Analysis 算,Probe 查,Diff 比,Lab 验,Audit 审。它们之间通过 orchestrator 协作,通过 harness 约束,通过快照数据共享状态。

这就是工业级 AI Agent 平台的样子:不是一个全能的超人,是一支各司其职的精锐部队。


完整文档:docs.olavai.com | GitHub: github.com/olav-ai/ola…