前面刚把 AI Agent、Workflow、Tool Calling 这些概念理了一遍,我一直有个想法:

能不能不要再拿“查天气”举例,直接让 Agent 干一点我熟悉的事情?

于是我挑了一个网络工程里再普通不过的故障:

一台 Linux 服务器访问不了互联网,让 AI 自己判断应该怎么查。

这个实验不追求做出什么“全自动运维平台”。

我反而想看一个更基础的问题:

如果我只告诉它:
“服务器无法上网”

它会先查 IP?
先查路由?
先 Ping 网关?
还是上来就查 DNS?

这件事特别适合拿来理解 Agent 和普通 Workflow 到底有什么区别。


一、如果是人来排查,我脑子里大概会怎么想?

“服务器无法上网”其实是一个很模糊的描述。

可能是:

网卡没起来
IP 没配置
掩码错了
默认路由没了
网关不通
公网不通
DNS 解析失败
目标端口被拦
应用自己挂了

所以正常情况下不会一上来就改配置。

我一般会先把问题缩小:

1. 网卡和 IP 正常吗?
2. 默认路由有没有?
3. 网关能不能到?
4. 公网 IP 能不能到?
5. 只有域名不行,还是 IP 也不行?
6. DNS 配置是什么?
7. 如果网络都正常,再看目标 TCP 端口和应用。

但这只是一个经验顺序。

如果 ip -br addr 一看服务器压根没有业务 IP,后面的 DNS、HTTPS、应用端口就暂时没必要查。

反过来,如果网关能 Ping,公网 IP 也能 Ping,但是域名解析失败,问题范围其实已经非常小了。

这正是我想让 Agent 做的事情:

不给它写死完整步骤,只给它几个诊断工具,让它根据上一步结果自己决定下一步。


二、这次我没有直接给 AI 一个 Shell

最省事的方式其实是给 Agent 一个:

shell_exec()

然后让它随便执行 Linux 命令。

但我越想越觉得这不适合第一版。

因为只要 Agent 能执行任意 Shell,它理论上就不只是能运行:

ip route

也可能有能力运行:

ip route del default
systemctl stop NetworkManager

所以我这次故意把权限收得很窄。

只给它几个明确的、只读的网络诊断工具:

get_interfaces()
查看网卡和 IP

get_routes()
查看路由表

ping_host()
测试基本三层连通性

resolve_domain()
测试 DNS

read_dns_config()
读取 DNS 配置

tcp_check()
测试 TCP 端口

Agent 可以决定:

先用谁
后用谁
要不要继续用

但它不能直接修改:

IP
路由
DNS
网卡状态
防火墙

我觉得这才比较像一个可以继续往真实运维方向扩展的第一版。


三、Tool 本身并不智能,决定“什么时候用”才是 Agent 的工作

比如:

get_routes()

说到底只是一个工具。

真实 Linux 上,它做的事情大概就是:

ip route

真正有意思的是:

模型什么时候觉得“现在应该看路由”?

整个结构可以理解成:

              用户故障描述
                   │
                   ↓
            ┌────────────┐
            │  AI Agent   │
            │  LLM 决策   │
            └─────┬──────┘
                  │
             下一步查什么?
                  │
        ┌─────────┼──────────┐
        ↓         ↓          ↓
      IP工具     路由工具     Ping工具
        │         │          │
        └─────────┼──────────┘
                  ↓
               返回结果
                  ↓
            Agent 再次判断
                  ↓
          DNS?TCP?还是结束?

这就是一个很小的 Agent Loop。


四、我没有真的把自己的网络改坏

为了写篇文章把默认路由删掉、DNS 改坏,没什么必要。

所以代码里先放了:

DEMO_MODE = True

它会模拟一台存在 DNS 故障的 Linux 服务器:

eth0:UP
IP:192.168.10.20/24

默认网关:
192.168.10.1

Ping 网关:
成功

Ping 公网 IP 1.1.1.1:
成功

解析 example.com:
失败

/etc/resolv.conf:
nameserver 192.168.10.253

我作为实验设计者当然知道答案:

问题大概率集中在 DNS。

但 Agent 一开始只知道:

一台 Linux 服务器访问 example.com 失败。

我要看的不是它能不能背出“DNS 是什么”,而是:

它能不能靠工具一步步把范围缩到 DNS。


五、准备一个最小 Agent 环境

这次示例使用 OpenAI Agents SDK。

安装:

pip install openai-agents

配置 API Key:

export OPENAI_API_KEY="你的 API Key"

Python 里先引入:

from agents import Agent, Runner, function_tool

我现在对这三个东西的理解很简单:

Agent
定义这个 AI 的身份、指令和可用工具

function_tool
把普通 Python 函数包装成 Agent 可调用工具

Runner
真正运行 Agent Loop

六、第一个工具:看网卡和 IP

@function_tool
def get_interfaces() -> str:
    """Show Linux interface state and IP addresses."""
    if DEMO_MODE:
        return """lo       UNKNOWN  127.0.0.1/8
eth0     UP       192.168.10.20/24"""

    return run_readonly(["ip", "-br", "addr"])

真实命令就是:

ip -br addr

它很适合排障时先快速确认:

网卡状态
IPv4 地址
接口名称

如果这里就发现:

eth0 DOWN

或者:

根本没有业务 IP

后面的排障方向会马上变化。


七、第二个工具:看路由

@function_tool
def get_routes() -> str:
    """Show the Linux routing table."""

真实模式里对应:

ip route

我主要关心:

有没有默认路由?
默认网关是谁?
从哪个接口出去?

例如:

default via 192.168.10.1 dev eth0

至少说明服务器已经有一条默认出口。

如果连 default 都没有,访问互联网失败就很值得先查路由配置。


八、Ping 其实可以提供不同层次的信息

我又给它一个:

ping_host(host)

这个工具可以测试:

网关
公网 IP
域名

这三种结果的意义并不一样。

例如:

网关都不通

先怀疑:

本机地址
二层链路
VLAN
ARP
网关接口

网关通,公网 IP 不通

可能继续看:

上游路由
NAT
ACL
防火墙
运营商路径

公网 IP 通,域名不通

DNS 的优先级就会迅速上升。

所以 Agent 调同一个 Ping 工具,目标不同,得到的信息量也不同。


九、再给它两个 DNS 工具

第一个:

resolve_domain(domain)

真实模式调用:

getent hosts example.com

回答:

域名到底能不能解析?

第二个:

read_dns_config()

读取:

/etc/resolv.conf

回答:

这台机器目前准备把 DNS 请求发给谁?

这两个问题是不一样的。

一个是验证现象,一个是查看配置。


十、我还加了一个 TCP 检查

网络排障特别容易在一句话上结束:

Ping 通了,所以网络没问题。

其实不够。

能 Ping:

只证明了一部分网络连通性。

并不等于:

TCP 22
TCP 80
TCP 443

一定正常。

所以我还给 Agent 一个:

tcp_check(host, port)

底层使用:

socket.create_connection()

这样后面如果做 Web、SSH、数据库等问题,就可以继续区分:

IP 可达

和:

应用端口可达

十一、最关键的地方:我没有把排障步骤写死

如果我这样写:

第一步检查 IP
第二步检查 Route
第三步 Ping 网关
第四步 Ping 公网
第五步检查 DNS

模型只是按照固定步骤跑。

这更像:

Workflow

所以我给 Agent 的指令反而是:

只做诊断,不修改配置。

不要为了走流程把所有工具调用一遍。

根据故障描述和上一步结果,选择最有价值的下一项检查。

所有结论必须引用工具结果。

如果公网 IP 正常但域名失败,应优先考虑 DNS。

最后给出故障定位、证据链、排除项和下一步建议。

然后把工具交给它。


十二、所以它应该先查 IP,还是先查路由?

答案是:

不一定。

这反而是这个实验最有意思的地方。

如果用户只说:

服务器完全无法访问任何网络。

Agent 很可能优先确认:

IP
路由

因为本机网络配置都没有任何已知信息。

但如果用户一开始已经告诉它:

网关能 Ping
1.1.1.1 也能 Ping
但是域名打不开

这时候再重新检查:

eth0 是不是 UP

信息增益就不大。

一个合理 Agent 应该更快走向:

resolve_domain()
read_dns_config()

所以真正的价值不是:

“AI 永远先查哪个命令。”

而是:

它能不能利用已经拿到的信息,减少没必要的检查。


十三、这次模拟故障,一个合理的证据链应该长这样

eth0 UP
192.168.10.20/24
        ↓
本机接口和 IP 基本正常

default via 192.168.10.1
        ↓
存在默认路由

Ping 192.168.10.1 成功
        ↓
本机到网关正常

Ping 1.1.1.1 成功
        ↓
公网三层连通性正常

example.com 解析失败
        ↓
问题开始集中到 DNS

resolv.conf:
nameserver 192.168.10.253
        ↓
DNS Server 配置和可达性值得重点检查

最后一个比较靠谱的结论应该是:

服务器并不是“完全断网”,而是公网 IP 连通正常、域名解析失败。现有证据把问题范围集中到了 DNS 配置或 DNS Server 可达性。

这和一句:

可能是 DNS。

区别很大。

前者有证据链。


十四、不要看到 DNS 地址陌生,就直接说它错了

比如模拟环境中:

nameserver 192.168.10.253

是我故意设计的故障点。

但在真实企业网络里,不能因为这个地址:

看起来不像 8.8.8.8

就宣布:

“DNS 配错了。”

企业内部使用私网 DNS 太正常了。

真实环境还应该继续验证:

192.168.10.253 能不能到?
53/UDP 是否可达?
53/TCP 是否可达?
DNS 服务是否正常?
是不是 systemd-resolved?
是不是内部域名系统?

所以更严谨的输出应该是:

当前证据高度指向 DNS,下一步建议验证 DNS Server 192.168.10.253 的可达性和服务状态。

而不是:

已确认 192.168.10.253 是错误地址。


十五、为什么我要求 Agent “不能编”?

因为模型很容易从:

DNS 解析失败

直接脑补成:

DNS Server 宕机。

但证据还远远不够。

也可能是:

到 DNS 的路由有问题
ACL 拦截
本地 resolver 异常
配置写错
DNS 服务端故障

所以 Agent 做运维时,我觉得最重要的一条指令反而是:

证据到哪里,结论就到哪里。

不要把“可能”写成“确定”。


十六、代码里我还刻意不用 shell=True

真实模式里执行:

subprocess.run(
    ["ip", "-br", "addr"],
    ...
)

而不是把模型生成的字符串直接扔进:

shell=True

因为如果模型可以任意拼 Shell,它拥有的就不只是:

网络诊断能力

而可能演变成:

任意命令执行能力

所以我更喜欢:

Agent
  ↓
预先定义好的工具
  ↓
固定且可审核的能力

而不是:

Agent
  ↓
root shell

十七、从模拟模式切到真实 Linux 很简单

代码默认:

DEMO_MODE = True

先跑文章里的 DNS 故障模拟。

真正想在自己的实验 Linux 上读取网络状态时,改成:

DEMO_MODE = False

这些 Tool 就会调用真实的只读操作:

ip -br addr
ip route
ping
getent hosts
读取 /etc/resolv.conf
TCP Connect

第一遍一定建议在:

自己的虚拟机
测试服务器
实验环境

使用。

目前这版没有:

修改 IP
删除 Route
修改 DNS
重启 NetworkManager
关闭网卡
修改防火墙

它只负责:

看
判断
给建议

十八、我反而觉得“只诊断”比“自动修复”更适合第一版

因为网络运维里的查询和变更风险完全不同。

例如:

ip route

只是查看。

但:

ip route del default

可能直接让远程服务器失联。

同样:

cat /etc/resolv.conf

只是查看。

但直接重写 DNS 配置就可能影响真实业务。

所以以后即使继续升级,我也更愿意按照:

第一阶段
AI 只读诊断

第二阶段
AI 给修复方案,人确认

第三阶段
低风险操作审批后执行

第四阶段
高风险操作始终人工确认

慢慢走。


十九、这次我终于真正理解了 Workflow 和 Agent 的区别

以前只看概念时,我会背:

Workflow 固定
Agent 动态

但没有很强的感觉。

放到网络排障里一下就清楚了。

如果程序永远执行:

check_ip()
check_route()
ping_gateway()
ping_public()
check_dns()

无论什么故障都从头跑到底,

这就是很典型的:

Workflow / Script

而如果我只给模型:

IP Tool
Route Tool
Ping Tool
DNS Tool
TCP Tool

然后说:

根据证据自己决定下一步。

它才开始有 Agent 的味道。

当然,这不意味着 Agent 一定更好。

比如每天固定巡检:

CPU
内存
磁盘
服务状态

我反而更倾向 Workflow。

但:

为什么这台服务器无法访问外网?

这种后续路径不固定的问题,就比较适合试 Agent。


二十、这个 Agent 后面还能继续加什么?

现在只有:

IP
Route
Ping
DNS
TCP

还很基础。

以后可以继续增加:

ip neigh
查看 ARP / 邻居表

ss -lntup
看监听端口

tracepath / traceroute
看路径

ethtool
看物理链路

nmcli
看 NetworkManager

tcpdump
抓包

nft / iptables
看本机防火墙

curl
验证 HTTP / HTTPS

再往数通方向走,甚至可以加入:

交换机接口状态
VLAN
ARP
MAC Table
OSPF
BGP
ACL

到这里它就开始从:

Linux Network Agent

慢慢往:

网络自动化故障排查助手

靠近了。


二十一、以后甚至可以接真实交换机

例如后面通过:

SSH
Netmiko
NAPALM
厂商 API

给 Agent 提供:

get_device_interface()
get_arp_table()
get_mac_table()
get_ospf_neighbor()
get_bgp_peer()

用户只描述:

PC 访问服务器失败。

Agent 可以逐步检查:

终端
↓
接入交换机
↓
VLAN
↓
网关
↓
路由
↓
服务器

这就比单纯让 AI:

“解释一下 OSPF 邻居状态。”

有意思多了。

因为 AI 开始真的和网络技能产生交集。


二十二、但我暂时不会让 AI 自动改交换机

比如:

undo shutdown
改 VLAN
下发 ACL
修改 OSPF Cost
重启接口

风险明显高一个等级。

真要做,至少得考虑:

命令白名单
设备权限限制
人工审批
配置备份
变更前检查
回滚
日志审计

因为 Agent 一旦拥有 Tool,它犯错时的影响已经不只是:

“答错一道题”

而可能变成:

“真的改错了设备”

这也是我现在觉得 Agent 工程里非常值得关注的一点。


二十三、完整代码怎么运行?

环境:

Linux
Python 3.10+

安装:

pip install openai-agents

配置:

export OPENAI_API_KEY="你的 API Key"

运行:

python network_diagnosis_agent.py

第一次:

DEMO_MODE = True

观察模拟故障。

理解以后,再在自己的实验环境切:

DEMO_MODE = False

二十四、最后把这次实验压缩成一张逻辑图

用户:
服务器访问 example.com 失败
            │
            ↓
      Network Agent
            │
       自己选择 Tool
            │
   ┌────────┼────────┐
   ↓        ↓        ↓
 网卡/IP   Route    Ping
                     │
                  公网正常?
                     │
                     ↓
                    DNS
                     │
              解析是否成功?
                     │
                     ↓
               DNS Config
                     │
                     ↓
                 证据链
                     │
                     ↓
       “不是完全断网,更像 DNS 问题”

这就是这篇文章最想表达的东西。


写在最后

这次我最想验证的其实不是:

AI 知不知道 ip route 是干什么的。

普通大模型当然知道。

真正让我觉得 Agent 有意思的地方是:

当它拥有几个真实工具以后,能不能根据环境返回的结果自己决定下一步查什么。

网络故障排查本来就是一个不断缩小范围的过程:

现象
↓
获取证据
↓
排除一部分原因
↓
继续验证
↓
再次缩小范围
↓
定位

Agent Loop 则是:

观察
↓
判断
↓
行动
↓
拿到新结果
↓
再次判断

两者放在一起,其实非常自然。

这个 Demo 离真正的生产网络自动化还很远。

但它至少让我第一次感觉:

AI Agent 不再只是一个“会调用天气 API 的聊天机器人”。

它真的可以和原来学的:

Linux
网络
路由
DNS
服务器排障

接起来。

下一步我更想做的是:

给它继续加 ip neighsstracepath 甚至 tcpdump,看看它能不能把一次“网络不通”继续排得更深。

代码在这里:

import socket
import subprocess
from pathlib import Path

from agents import Agent, Runner, function_tool

DEMO_MODE = True


def run_readonly(cmd: list[str]) -> str:
    """Execute a read-only command without a shell."""
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=8,
            check=False,
        )
        output = (result.stdout + result.stderr).strip()
        return output or f"command finished, exit code={result.returncode}"
    except Exception as e:
        return f"failed: {type(e).__name__}: {e}"


@function_tool
def get_interfaces() -> str:
    """Show Linux interface state and IP addresses."""
    if DEMO_MODE:
        return """lo       UNKNOWN  127.0.0.1/8
eth0     UP       192.168.10.20/24"""
    return run_readonly(["ip", "-br", "addr"])


@function_tool
def get_routes() -> str:
    """Show the Linux routing table."""
    if DEMO_MODE:
        return """default via 192.168.10.1 dev eth0
192.168.10.0/24 dev eth0 proto kernel scope link src 192.168.10.20"""
    return run_readonly(["ip", "route"])


@function_tool
def ping_host(host: str) -> str:
    """Ping an IP address or hostname."""
    if DEMO_MODE:
        demo = {
            "192.168.10.1": "3 packets transmitted, 3 received, 0% packet loss",
            "1.1.1.1": "3 packets transmitted, 3 received, 0% packet loss",
            "example.com": "ping: example.com: Temporary failure in name resolution",
        }
        return demo.get(host, f"demo target not configured: {host}")
    return run_readonly(["ping", "-c", "3", "-W", "1", host])


@function_tool
def resolve_domain(domain: str) -> str:
    """Test DNS resolution."""
    if DEMO_MODE:
        if domain in {"example.com", "www.baidu.com"}:
            return f"getent: {domain}: Temporary failure in name resolution"
        return f"demo domain not configured: {domain}"
    return run_readonly(["getent", "hosts", domain])


@function_tool
def read_dns_config() -> str:
    """Read /etc/resolv.conf."""
    if DEMO_MODE:
        return """# /etc/resolv.conf
nameserver 192.168.10.253
options timeout:1 attempts:2"""
    try:
        return Path("/etc/resolv.conf").read_text(encoding="utf-8", errors="ignore")
    except Exception as e:
        return f"failed: {type(e).__name__}: {e}"


@function_tool
def tcp_check(host: str, port: int) -> str:
    """Try to establish a TCP connection."""
    if not (1 <= port <= 65535):
        return "port must be in 1..65535"

    if DEMO_MODE:
        if host == "1.1.1.1" and port == 443:
            return "TCP 1.1.1.1:443 connected"
        return f"demo TCP target not configured: {host}:{port}"

    try:
        with socket.create_connection((host, port), timeout=3):
            return f"TCP {host}:{port} connected"
    except Exception as e:
        return f"TCP {host}:{port} failed: {type(e).__name__}: {e}"


agent = Agent(
    name="Linux 网络故障排查助手",
    instructions="""
你是一名 Linux 网络故障排查 Agent。

只做诊断,不修改系统配置。
不要为了走流程把所有工具都调用一遍,要根据现象和上一步结果选择最有价值的下一步。
任何结论都必须引用工具结果,不能编造。
如果公网 IP 正常但域名失败,应优先怀疑 DNS。
最终输出:故障定位、证据链、排除项、下一步建议。
""",
    tools=[
        get_interfaces,
        get_routes,
        ping_host,
        resolve_domain,
        read_dns_config,
        tcp_check,
    ],
)

if __name__ == "__main__":
    result = Runner.run_sync(
        agent,
        """
一台 Linux 服务器现在“无法上网”:访问 example.com 失败。
请你自己选择排查步骤,尽量用最少的检查定位问题。
""",
    )
    print(result.final_output)
Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐