Python Generation

 Примеры ИИ запросов

AI generated JSON training models


Python скрипт для генерирования JSON примеров для fine tuning локальной модели Qwen2.5 (7B) для анализа динамической маршрутизации - анализа результатов выполнения команды Cisco "show ip route"


Ниже представлен полноценный Python-скрипт, который генерирует синтетический Dataset в формате .jsonl (JSON Lines), готовый для fine-tuning моделей семейства Qwen (через LLaMA-Factory, Axolotl или Unsloth).

Скрипт создает как здоровые сценарии (чтобы модель не искала ошибки там, где их нет), так и сценарии с инжектированными ошибками (Null0 Blackhole, Missing Default Route, Unreachable Next-hop).

Python-скрипт для генерации Dataset (generate_dataset.py)


Python


import json

import random

import ipaddress

from typing import List, Dict, Tuple


# ==========================================

# 1. Базовые шаблоны и пулы данных

# ==========================================

INTERNAL_SUBNETS = ["10.10.0.0/16", "10.20.0.0/16", "172.16.1.0/24", "192.168.10.0/24"]

WAN_SUBNETS = ["203.0.113.0/30", "198.51.100.0/30"]

PROTOCOLS = ["C", "L", "S", "O", "D", "B"]


def get_ip_from_subnet(subnet_str: str, is_gateway: bool = False) -> str:

    """Получает IP-адрес из подсети (первый или последний для шлюза)"""

    net = ipaddress.IPv4Network(subnet_str)

    if is_gateway:

        return str(net.broadcast_address - 1)

    return str(net.network_address + 1)


def generate_interface_brief(interfaces: List[Dict]) -> str:

    """Генерирует вывод show ip interface brief"""

    lines = ["Interface              IP-Address      OK? Method Status                Protocol"]

    for intf in interfaces:

        lines.append(f"{intf['name']:<22} {intf['ip']:<15} YES manual up                    up")

    return "\n".join(lines)


def generate_route_table(routes: List[Dict]) -> str:

    """Генерирует вывод show ip route"""

    header = (

        "Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP\n"

        "       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area\n"

        "       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2\n"

        "       O E2 - OSPF external type 2\n\n"

    )

    lines = [header]

    for route in routes:

        lines.append(route['line'])

    return "\n".join(lines)


# ==========================================

# 2. Генераторы сценариев (Scenario Generators)

# ==========================================


def generate_healthy_edge_router() -> Dict:

    """Генерирует здоровый пограничный маршрутизатор"""

    wan_subnet = random.choice(WAN_SUBNETS)

    wan_ip = get_ip_from_subnet(wan_subnet, is_gateway=False)

    wan_gw = get_ip_from_subnet(wan_subnet, is_gateway=True)

    lan_subnet = random.choice(INTERNAL_SUBNETS)

    lan_ip = get_ip_from_subnet(lan_subnet, is_gateway=False)


    interfaces = [

        {"name": "GigabitEthernet0/0", "ip": lan_ip},

        {"name": "GigabitEthernet0/1", "ip": wan_ip}

    ]

    

    routes = [

        {"line": f"C        {lan_subnet} is directly connected, GigabitEthernet0/0"},

        {"line": f"L        {lan_ip}/32 is directly connected, GigabitEthernet0/0"},

        {"line": f"C        {wan_subnet} is directly connected, GigabitEthernet0/1"},

        {"line": f"L        {wan_ip}/32 is directly connected, GigabitEthernet0/1"},

        {"line": f"S*       0.0.0.0/0 [1/0] via {wan_gw}"},

        {"line": f"O IA     10.0.0.0/8 [110/50] via {lan_ip}, 01:20:00, GigabitEthernet0/0"}

    ]

    

    return {

        "scenario": "healthy",

        "interfaces": interfaces,

        "routes": routes,

        "expected_analysis": "Конфигурация маршрутизатора корректна. Присутствуют напрямую подключенные сети, маршрут по умолчанию (0.0.0.0/0) указывает на корректный шлюз провайдера, и отсутствуют признаки черных дыр или недостижимых next-hop адресов.",

        "error_type": "None",

        "severity": "Normal"

    }


def generate_null0_blackhole() -> Dict:

    """Генерирует ошибку: суммарный маршрут в Null0 без конкретных подсетей"""

    lan_subnet = "10.10.0.0/16"

    lan_ip = get_ip_from_subnet(lan_subnet, is_gateway=False)

    

    interfaces = [{"name": "GigabitEthernet0/0", "ip": lan_ip}]

    

    # ОШИБКА: Есть суммарный маршрут, но НЕТ конкретных маршрутов внутри него

    routes = [

        {"line": f"C        {lan_subnet} is directly connected, GigabitEthernet0/0"},

        {"line": f"L        {lan_ip}/32 is directly connected, GigabitEthernet0/0"},

        {"line": f"O        10.0.0.0/8 is a summary, 00:15:23, Null0"} 

    ]

    

    return {

        "scenario": "null0_blackhole",

        "interfaces": interfaces,

        "routes": routes,

        "expected_analysis": f"В таблице маршрутизации присутствует суммарный маршрут '10.0.0.0/8' с интерфейсом вывода Null0. Однако в таблице отсутствуют более специфичные маршруты (например, /16 или /24), входящие в этот диапазон (кроме напрямую подключенной {lan_subnet}). Любой трафик, предназначенный для других подсетей диапазона 10.x.x.x, будет немедленно отброшен интерфейсом Null0.",

        "error_type": "Null0 Blackhole / Missing Specific Routes",

        "severity": "High"

    }


def generate_missing_default_route() -> Dict:

    """Генерирует ошибку: пограничный роутер без маршрута по умолчанию"""

    wan_subnet = random.choice(WAN_SUBNETS)

    wan_ip = get_ip_from_subnet(wan_subnet, is_gateway=False)

    lan_subnet = random.choice(INTERNAL_SUBNETS)

    lan_ip = get_ip_from_subnet(lan_subnet, is_gateway=False)


    interfaces = [

        {"name": "GigabitEthernet0/0", "ip": lan_ip},

        {"name": "GigabitEthernet0/1", "ip": wan_ip} # Внешний интерфейс активен

    ]

    

    # ОШИБКА: Отсутствует строка S* 0.0.0.0/0

    routes = [

        {"line": f"C        {lan_subnet} is directly connected, GigabitEthernet0/0"},

        {"line": f"L        {lan_ip}/32 is directly connected, GigabitEthernet0/0"},

        {"line": f"C        {wan_subnet} is directly connected, GigabitEthernet0/1"},

        {"line": f"L        {wan_ip}/32 is directly connected, GigabitEthernet0/1"},

        {"line": f"O IA     10.0.0.0/8 [110/50] via 10.10.0.2, 01:20:00, GigabitEthernet0/0"}

    ]

    

    return {

        "scenario": "missing_default_route",

        "interfaces": interfaces,

        "routes": routes,

        "expected_analysis": f"Маршрутизатор имеет активный внешний интерфейс GigabitEthernet0/1 с IP-адресом {wan_ip}, что указывает на его роль пограничного устройства. Однако в таблице маршрутизации полностью отсутствует маршрут по умолчанию (0.0.0.0/0). Без этого маршрута устройство не знает, куда отправлять трафик во внешние сети, что приведет к потере внешней связности.",

        "error_type": "Missing Default Route",

        "severity": "Critical"

    }


def generate_unreachable_nexthop() -> Dict:

    """Генерирует ошибку: статический маршрут с недостижимым Next-hop"""

    lan_subnet = "172.16.1.0/24"

    lan_ip = get_ip_from_subnet(lan_subnet, is_gateway=False)

    

    # ОШИБКА: Next-hop 172.16.2.254, но сети 172.16.2.0/24 нет в таблице

    bad_nexthop = "172.16.2.254"

    dest_network = "10.10.0.0/16"


    interfaces = [{"name": "GigabitEthernet0/0", "ip": lan_ip}]

    

    routes = [

        {"line": f"C        {lan_subnet} is directly connected, GigabitEthernet0/0"},

        {"line": f"L        {lan_ip}/32 is directly connected, GigabitEthernet0/0"},

        {"line": f"S        {dest_network} [1/0] via {bad_nexthop}"}

    ]

    

    return {

        "scenario": "unreachable_nexthop",

        "interfaces": interfaces,

        "routes": routes,

        "expected_analysis": f"В таблице маршрутизации присутствует статический маршрут к сети {dest_network} с Next-hop адресом {bad_nexthop}. Однако на данном маршрутизаторе нет ни одного интерфейса или маршрута, обеспечивающего связность с сетью 172.16.2.0/24. Маршрутизатор не может отправить пакет, так как не знает, как достичь указанного Next-hop (проблема рекурсивного поиска или опечатка в IP-адресе).",

        "error_type": "Unreachable Next-hop / Invalid Static Route",

        "severity": "High"

    }


# ==========================================

# 3. Форматирование в ChatML / JSONL

# ==========================================


def format_to_chatml(data: Dict) -> Dict:

    """Форматирует сценарий в структуру messages для fine-tuning"""

    prompt_text = (

        f"Проанализируй следующие данные с маршрутизатора:\n\n"

        f"# show ip interface brief\n{generate_interface_brief(data['interfaces'])}\n\n"

        f"# show ip route\n{generate_route_table(data['routes'])}"

    )

    

    if data['scenario'] == 'healthy':

        response_dict = {

            "analysis": data['expected_analysis'],

            "error_type": data['error_type'],

            "severity": data['severity'],

            "affected_prefixes": [],

            "recommendation": "Действий не требуется. Конфигурация соответствует лучшим практикам."

        }

    else:

        # Извлекаем префиксы из анализа для структурированного ответа

        affected = []

        if "10.0.0.0/8" in data['expected_analysis']: affected.append("10.0.0.0/8")

        if "0.0.0.0/0" in data['expected_analysis']: affected.append("0.0.0.0/0")

        if "10.10.0.0/16" in data['expected_analysis']: affected.append("10.10.0.0/16")

        

        recommendation = "Проверьте конфигурацию маршрутизации и исправьте выявленную несогласованность."

        if data['scenario'] == 'null0_blackhole':

            recommendation = "Проверьте конфигурацию агрегации маршрутов. Убедитесь, что конкретные подсети существуют и анонсируются, либо удалите команду суммаризации."

        elif data['scenario'] == 'missing_default_route':

            recommendation = "Добавьте статический маршрут по умолчанию (ip route 0.0.0.0 0.0.0.0 <next-hop>) или проверьте работу динамического протокола."

        elif data['scenario'] == 'unreachable_nexthop':

            recommendation = "Проверьте правильность указания IP-адреса Next-hop в статическом маршруте на предмет опечаток, либо добавьте маршрут до сети самого next-hop."


        response_dict = {

            "analysis": data['expected_analysis'],

            "error_type": data['error_type'],

            "severity": data['severity'],

            "affected_prefixes": affected,

            "recommendation": recommendation

        }


    return {

        "messages": [

            {

                "role": "system",

                "content": "Ты — эксперт по сетевой инженерии Cisco. Твоя задача — проанализировать вывод команд 'show ip interface brief' и 'show ip route', найти конфигурационные ошибки или аномалии и вернуть результат в строгом формате JSON."

            },

            {

                "role": "user",

                "content": prompt_text

            },

            {

                "role": "assistant",

                "content": json.dumps(response_dict, ensure_ascii=False)

            }

        ]

    }


# ==========================================

# 4. Главная функция генерации

# ==========================================


def generate_dataset(num_samples: int, output_file: str):

    """Генерирует датасет и сохраняет в JSONL"""

    generators = [

        (generate_healthy_edge_router, 0.30),       # 30% здоровых данных

        (generate_null0_blackhole, 0.25),           # 25% Null0 Blackhole

        (generate_missing_default_route, 0.25),     # 25% Missing Default Route

        (generate_unreachable_nexthop, 0.20)        # 20% Unreachable Next-hop

    ]

    

    with open(output_file, 'w', encoding='utf-8') as f:

        for _ in range(num_samples):

            # Выбор генератора на основе весов

            r = random.random()

            cumulative = 0

            chosen_generator = generators[0][0]

            for gen, weight in generators:

                cumulative += weight

                if r <= cumulative:

                    chosen_generator = gen

                    break

            

            # Генерация и запись

            sample_data = chosen_generator()

            chatml_sample = format_to_chatml(sample_data)

            

            # Запись в одну строку (стандарт JSONL)

            f.write(json.dumps(chatml_sample, ensure_ascii=False) + '\n')

            

    print(f" Датасет успешно сгенерирован: {num_samples} примеров сохранено в {output_file}")


if __name__ == "__main__":

    # Генерируем 1000 примеров для старта (можно увеличить до 5000+)

    generate_dataset(num_samples=1000, output_file="qwen_cisco_routing_dataset.jsonl")






Как использовать этот скрипт и развивать датасет:

Запуск: Просто сохраните код в файл generate_dataset.py и запустите: python generate_dataset.py. На выходе вы получите файл qwen_cisco_routing_dataset.jsonl.

Проверка формата: Откройте полученный файл и проверьте несколько строк. Каждая строка должна быть валидным JSON-объектом с ключом "messages".

Масштабирование (Следующие шаги):

Добавьте больше вариаций: В функции generate_healthy_edge_router можно добавить рандомизацию количества интерфейсов (от 2 до 8), чтобы модель училась игнорировать "шум" в больших таблицах.

Добавьте новые ошибки: Напишите функции по аналогии для:

Routing Loop / Recursive Routing (Next-hop указывает на собственный IP роутера или на интерфейс туннеля, через который должен идти трафик).

Suboptimal ECMP (Два пути к одной сети, но один через GigabitEthernet, другой через медленный Serial/Tunnel).

Administrative Distance Conflict (Маршрут RIP перекрывает маршрут OSPF из-за ручной настройки distance).

Интеграция с LLaMA-Factory: Этот .jsonl файл полностью совместим с форматом alpaca или sharegpt, который используется в популярных фреймворках для fine-tuning (например, в hiyouga/LLaMA-Factory). Вам нужно будет только создать файл dataset_info.json, указывающий на этот файл.



AI Prompts Examples

AI generated scenario






Comments

Popular posts from this blog

Учим с ИИ

Perplexity Answer

Генерирование МОДЕЛЕЙ