今回も「LangChain / LangGraph を使ったネットワーク運用支援ツール」の構築の続きです。

 

今回はCML(Cisco Modeling Labs)上のCiscoルータにSSHでアクセスしてshowコマンドを取得するというTool Calling(ツール呼び出し)の部分だけをやってみたいと思います。LangChainやLangGraphは使いません。

 

スクリプト

「Cisco機器の状態はどうですか?」と質問すると、AIエージェントがCisco機器へSSHアクセスした上でshowコマンドを実行するツールを呼び出し、内容を回答するというスクリプトです。使用したコードは以下のとおりです。

 

今回はテストなので決め打ちしたルータ(172.16.1.254)にアクセスします。実行するCLIコマンドは以下のとおりです。

  1. show ip int brief
  2. show ip ospf nei
  3. show ip bgp summary
 

import os
from netmiko import ConnectHandler
from google import genai


# ============================================================
# 設定
# ============================================================

CISCO_USER = os.environ["CISCO_user"]
CISCO_PASSWORD = os.environ["CISCO_password"]
GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]


# ============================================================
# Gemini
# ============================================================

client = genai.Client(
    api_key=GOOGLE_API_KEY
)

# ============================================================
# Tool: Cisco機器から現在の障害を取得
# ============================================================

def get_cisco_int():
    device = {
        "device_type": "cisco_ios",
        "host": "172.16.1.254",
        "username": CISCO_USER,
        "password": CISCO_PASSWORD,
        "secret": CISCO_PASSWORD,
    }

    connection = ConnectHandler(**device)

    # enableモードへ移行
    connection.enable()

    output_list = []
    output_list.append(connection.send_command("show ip int brief"))
    output_list.append(connection.send_command("show ip ospf nei"))
    output_list.append(connection.send_command("show ip bgp summary"))
    
    connection.disconnect()


    return output_list


# ============================================================
# AI Agent
# ============================================================

system_instruction = """
あなたはネットワーク運用を支援するAIエージェントです。

ユーザーからCisco機器の状態について聞かれたら状態を分かりやすく説明してください。

Ciscoの状態情報が必要であれば、
get_cisco_int ツールを使用してください。


"""

# ============================================================
# Geminiに質問
# ============================================================

user_message = "Cisco機器の状態はどうですか?"


response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=user_message,
    config={
        "system_instruction": system_instruction,
        "tools": [get_cisco_int]
    }
)


# ============================================================
# 結果表示
# ============================================================

print(response.text)

結果

以下の回答になりました。インターフェースの状態、OSPFネイバー、BGPネイバーの状態を正しく回答しています。

 

Cisco機器の状態は以下の通りです。 

 

**インターフェースの状態:** 

* **Ethernet0/0:** IPアドレス 172.16.1.254 で稼働しており、問題ありません。 

* **Ethernet0/1:** IPアドレス 10.0.0.1 で稼働しており、問題ありません。

 * **Ethernet0/2 と Ethernet0/3:** 管理者によって停止されています。 

* **Serial1/0 から Serial1/3:** 管理者によって停止されています。 

* **Loopback0:** IPアドレス 10.0.1.1 で稼働しており、問題ありません。 

 

**ネイバー情報:** * **Ethernet0/1** 上に **10.0.0.2** というネイバーがいて、状態は **FULL** で問題ありません。

 

 **BGP (Border Gateway Protocol) の状態:** * ルータ識別子: **10.0.1.1** * ローカルAS番号: **64512** * BGPテーブルバージョン: **2** * **10.0.1.2** というネイバーがいて、**00:25:50** (25分50秒) 稼働しており、問題なく動作しています。 

 

全体的に、主要なインターフェースとBGPネイバーは正常に稼働しているようです。停止しているインターフェースは管理者によって意図的に停止されている状態です。

 

Ethernet0/1をshutdownした後の結果

Ethernet0/1をshutdownした後では、以下の回答になりました。Ethernet0/1がダウンしていること、BGPがダウンしていることを正しく回答しています。OSPFについてはネイバーがダウンするとshow ip ospf neiコマンドの結果の出力がなくなるので省略されたようです。

 

Cisco機器の状態は以下の通りです。 

 

**インターフェースの状態:** 

* **Ethernet0/0:** IPアドレス `172.16.1.254` で `up` 状態です。 

* **Ethernet0/1:** IPアドレス `10.0.0.1` が設定されていますが、管理上シャットダウンされており `down` 状態です。 

* **Ethernet0/2, Ethernet0/3, Serial1/0, Serial1/1, Serial1/2, Serial1/3:** IPアドレスは未割り当てで、管理上シャットダウンされており `down` 状態です。 

* **Loopback0:** IPアドレス `10.0.1.1` で `up` 状態です。 

 

**BGP (Border Gateway Protocol) の状態:** * ルータの識別子は `10.0.1.1` で、ローカルAS番号は `64512` です。 * BGPテーブルのバージョンは `3` です。 * `10.0.1.2` のネイバーとのBGP接続は、`00:01:56` 間 `Idle` 状態です。これは、BGP接続が確立されていないことを示しています。 

 

まとめると、Ethernet0/0とLoopback0は正常に稼働していますが、他のインターフェースは管理上シャットダウンされています。また、BGPネイバー `10.0.1.2` との接続は確立されていません。

 

次回は 

次回はZabbixを組み合わせたりしてもう少し動的な動作を試していきたいと思います。