Yandex Cloud
Поиск
Связаться с экспертомПопробовать бесплатно
  • Кейсы
  • Документация
  • Блог
  • Все сервисы
    • Cloud Interconnect
    • Cloud Backup
    • Compute Cloud
    • Object Storage
    • Managed Service for Kubernetes®
    • Managed Service for PostgreSQL
    • Managed Service for ClickHouse®
    • Monium
    • Cloud CDN
    • Network Load Balancer
    • Virtual Private Cloud
    • Cloud DNS
    • Application Load Balancer
    • Yandex Cloud Router
    • Managed Service for MySQL®
    • Managed Service for Valkey™
    • Managed Service for Apache Spark™
    • Managed Service for OpenSearch
    • Managed Service for Apache Kafka®
    • Data Transfer
    • Yandex MPP Analytics Engine for PostgreSQL
    • Managed Service for YDB
    • SpeechKit
    • Yandex Identity Hub
    • Key Management Service
    • Certificate Manager
    • Yandex Lockbox
    • Audit Trails
    • Container Registry
    • Managed Service for Prometheus®
    • Message Queue
    • Identity and Access Management
    • Yandex Cloud Console
    • Resource Manager
    • Yandex Cloud Billing
    • Cloud Apps
    • Yandex AI Studio
    • Yandex BareMetal
    • Smart Web Security
    • Security Deck
    • Yandex Cloud Video
    • Stackland
    • Yandex Managed Service for Apache Airflow®
    • Data Processing
    • Yandex MetaData Hub
    • Yandex WebSQL
    • DataLens
    • Yandex Search API
    • SpeechSense
    • DataSphere
    • Vision OCR
    • Translate
    • Cloud Registry
    • SmartCaptcha
    • Cloud Desktop
    • Yandex SIEM
    • SourceCraft Code Assistant
    • Managed Service for GitLab
    • Cloud Functions
    • API Gateway
    • Yandex Cloud Postbox
    • IoT Core
    • Serverless Containers
    • Cloud Notification Service
    • Yandex Query
  • Статус работы сервисов
  • Marketplace
    • Доступны в регионе
    • Инфраструктура и сеть
    • Платформа данных
    • Искусственный интеллект
    • Безопасность
    • Инструменты DevOps
    • Бессерверные вычисления
    • Управление ресурсами
  • Все решения
    • По отраслям
    • По типу задач
    • Экономика платформы
    • Безопасность
    • Техническая поддержка
    • Каталог партнёров
    • Обучение и сертификация
    • Облако для стартапов
    • Облако для крупного бизнеса
    • Центр технологий для общества
    • Партнёрская программа
    • Облако для фрилансеров
    • Обучение и сертификация
    • Блог
    • Документация
    • Мероприятия и вебинары
    • Контакты, чаты и сообщества
    • Идеи
    • Калькулятор цен
    • Тарифы
  • Кейсы
  • Документация
  • Блог
Создавайте контент и получайте гранты!Готовы написать своё руководство? Участвуйте в контент-программе и получайте гранты на работу с облачными сервисами!
Подробнее о программе
Проект Яндекса
© 2026 ТОО «Облачные Сервисы Казахстан»
Monium
KZ
  • Начало работы
  • Обзор
    • SLO-мониторинг веб-сервиса
    • Интеграция LiteLLM Proxy с Yandex AI Studio и Monium Traces
  • Управление доступом
  • Правила тарификации
  • Справочник Terraform
  • История изменений
  • Обучающие курсы

В этой статье:

  • Необходимые платные ресурсы
  • Порядок действий
  • Перед началом работы
  • Подготовьте окружение
  • Создайте API-ключи
  • Разверните и настройте экземпляр LiteLLM Proxy
  • Отправьте запросы к модели и проверьте трейсы
  • Создайте и запустите тестовое приложение
  • Найдите и просмотрите трейсы в Monium
  • Решение проблем
  • Как удалить созданные ресурсы
  1. Практические руководства
  2. Интеграция LiteLLM Proxy с Yandex AI Studio и Monium Traces

Интеграция LiteLLM Proxy с Yandex AI Studio и Monium Traces

Статья создана
Yandex Cloud
Обновлена 14 октября 2026 г.
Открыть в Markdown
  • Необходимые платные ресурсы
  • Порядок действий
  • Перед началом работы
    • Подготовьте окружение
    • Создайте API-ключи
  • Разверните и настройте экземпляр LiteLLM Proxy
  • Отправьте запросы к модели и проверьте трейсы
    • Создайте и запустите тестовое приложение
    • Найдите и просмотрите трейсы в Monium
    • Решение проблем
  • Как удалить созданные ресурсы

LiteLLM Proxy — прокси-сервер для работы с LLM-провайдерами через единый интерфейс, совместимый с OpenAI API.

LiteLLM Proxy удобен для работы с несколькими моделями, поскольку позволяет централизованно управлять доступом приложений, ограничивать частоту запросов, учитывать расходы и формировать трейсы для анализа выполнения запросов.

Отслеживать выполнение запросов можно через систему распределенной трассировки Monium Traces. Трейс объединяет данные об отдельных операциях: их последовательности, длительности и ошибках.

Трейсы помогают:

  • Определить, на каком этапе, в каком сервисе или компоненте приложения возник сбой или задержка.
  • Анализировать промпты, ответы модели и расход токенов. Например, какой контекст получила модель, сколько времени заняла генерация и сколько токенов использовано в запросе и ответе.

Интеграция LiteLLM Proxy с системой трассировки позволяет собирать эти данные на стороне прокси-сервера без добавления кода отправки телеметрии в приложение.

В этом руководстве вы развернете LiteLLM Proxy и настроите передачу трейсов в Monium Traces с помощью встроенной интеграции OpenTelemetry v2. Затем вы запустите тестовое приложение, которое имитирует диалог пользователя с моделью DeepSeek V4 Flash в Yandex AI Studio через LiteLLM Proxy, и просмотрите полученные трейсы.

Необходимые платные ресурсыНеобходимые платные ресурсы

В стоимость поддержки решения входят:

  • Вычислительные ресурсы виртуальной машины и диск (тарифы Yandex Compute Cloud).
  • Публичный IP-адрес и исходящий трафик (тарифы Yandex Virtual Private Cloud).
  • Генерация текста (тарифы Yandex AI Studio).
  • Запись трейсов в Monium Traces (тарифы Yandex Monium).

Порядок действийПорядок действий

  1. Подготовьте облако к работе.
  2. Разверните и настройте экземпляр LiteLLM Proxy.
  3. Отправьте запросы к модели и проверьте трейсы.

Если вы развернули систему только для знакомства с решением, после завершения работы можно удалить созданные ресурсы.

Перед началом работыПеред началом работы

Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:

  1. Перейдите в консоль управления, затем войдите в Yandex Cloud или зарегистрируйтесь.
  2. На странице Yandex Cloud Billing убедитесь, что у вас подключен платежный аккаунт и он находится в статусе ACTIVE или TRIAL_ACTIVE. Если платежного аккаунта нет, создайте его и привяжите к нему облако.

Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака.

Подготовьте окружениеПодготовьте окружение

Создайте виртуальную машину из публичного образа Ubuntu 24.04 LTS.

Важно

При создании ВМ укажите объем оперативной памяти не менее 4 ГБ.

Создайте API-ключиСоздайте API-ключи

Чтобы LiteLLM Proxy мог обращаться к модели Yandex AI Studio и передавать трейсы в Monium Traces, создайте два сервисных аккаунта с API-ключами — по одному для каждого сервиса:

Консоль управления
  1. Создайте сервисный аккаунт для доступа LiteLLM Proxy к Yandex AI Studio:

    1. В консоли управления выберите каталог, в котором вы будете создавать инфраструктуру.
    2. Перейдите в сервис Identity and Access Management.
    3. Нажмите кнопку Создать сервисный аккаунт.
    4. В открывшемся окне введите имя сервисного аккаунта litellm-ai-studio-sa.
    5. В поле Роли в каталоге нажмите кнопку Добавить роль и выберите роль ai.languageModels.user.
    6. Нажмите кнопку Создать.
  2. Создайте API-ключ для доступа LiteLLM Proxy к Yandex AI Studio:

    1. В открывшемся списке сервисных аккаунтов выберите сервисный аккаунт litellm-ai-studio-sa.
    2. В верхней части страницы нажмите значок и выберите Создать новый ключ → Создать API-ключ.
    3. В открывшемся окне выберите область действия yc.ai.languageModels.execute и нажмите кнопку Создать.
    4. Сохраните секретную часть API-ключа. После закрытия диалогового окна она будет недоступна.
  3. Аналогично создайте сервисный аккаунт и API-ключ для доступа LiteLLM Proxy к Yandex Monium со следующими параметрами:

    • Имя сервисного аккаунта — litellm-monium-sa.
    • Роль — monium.traces.writer.
    • Область действия API-ключа — yc.monium.traces.write.

Разверните и настройте экземпляр LiteLLM ProxyРазверните и настройте экземпляр LiteLLM Proxy

Чтобы развернуть LiteLLM Proxy на созданной виртуальной машине, установите Docker, подготовьте файлы конфигурации и запустите контейнер:

  1. Подключитесь к виртуальной машине по SSH или через OS Login в зависимости от способа подключения, выбранного при ее создании.

  2. Установите и настройте Docker:

    1. Установите необходимые зависимости:

      sudo apt update
      sudo apt install ca-certificates curl gnupg
      
    2. Установите GPG-ключ Docker:

      sudo install -m 0755 -d /etc/apt/keyrings
      sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
      sudo chmod a+r /etc/apt/keyrings/docker.asc
      
    3. Добавьте репозиторий Docker в список источников пакетов apt:

      echo \
        "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
        $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
        sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      
    4. Установите Docker:

      sudo apt update
      sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
      
    5. Добавьте текущего пользователя в группу docker и запустите новую оболочку, чтобы применить изменения:

      sudo usermod -aG docker $USER
      newgrp docker
      
    6. Убедитесь, что Docker установлен и работает:

      docker run hello-world
      

      Результат:

      Unable to find image 'hello-world:latest' locally
      latest: Pulling from library/hello-world
      4f55086f7dd0: Pull complete 
      d5e71e642bf5: Download complete 
      Digest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe89********
      Status: Downloaded newer image for hello-world:latest
      
      Hello from Docker!
      This message shows that your installation appears to be working correctly.
      ...
      
  3. В домашней директории создайте директорию для проекта Docker Compose и перейдите в нее:

    mkdir ~/litellm-proxy && cd ~/litellm-proxy
    
  4. Создайте файлы, необходимые для работы проекта:

    touch .env
    touch config.yaml
    touch docker-compose.yaml
    
  5. В текстовом редакторе откройте файл .env:

    nano .env
    
  6. Добавьте в файл .env переменные окружения, необходимые для работы проекта:

    YANDEX_AI_STUDIO_API_KEY=<секретная_часть_ключа_ai_studio>
    MONIUM_API_KEY=<секретная_часть_ключа_monium>
    MONIUM_PROJECT=folder__<идентификатор_каталога>
    LITELLM_MASTER_KEY=sk-local-litellm
    

    Где:

    • YANDEX_AI_STUDIO_API_KEY — секретная часть API-ключа сервисного аккаунта litellm-ai-studio-sa.

    • MONIUM_API_KEY — секретная часть API-ключа сервисного аккаунта litellm-monium-sa.

    • MONIUM_PROJECT — идентификатор проекта Monium. Он состоит из префикса folder__ и идентификатора каталога, в котором созданы ВМ и сервисные аккаунты. Например: folder__b1gt6g8ht345********.

    • LITELLM_MASTER_KEY — ключ для доступа приложений к LiteLLM Proxy. Вы задаете его в файле .env, чтобы прокси проверял ключ в поступающих запросах. Для выполнения руководства оставьте значение sk-local-litellm: оно уже предусмотрено в коде тестового приложения agent_example.py, которое вы создадите на следующем этапе.

      Совет

      Для рабочей среды сгенерируйте случайную строку командой openssl rand -hex 32 и добавьте к ней префикс sk-.

      Если вы изменили LITELLM_MASTER_KEY в .env после запуска контейнера, пересоздайте контейнер командой docker compose up -d --force-recreate. При использовании собственного ключа перед запуском тестового приложения agent_example.py задайте такое же значение в переменной окружения LITELLM_PROXY_KEY в терминале, из которого будете запускать приложение:

      export LITELLM_PROXY_KEY='<ключ_из_файла_env>'
      

      Библиотека openai в тестовом приложении автоматически передаст этот ключ в заголовке Authorization со схемой Bearer. Значение в заголовке должно совпадать с ключом, заданным для прокси в .env.

  7. В текстовом редакторе откройте файл config.yaml:

    nano config.yaml
    
  8. Добавьте в файл config.yaml конфигурацию LiteLLM Proxy:

    # LiteLLM proxy: Yandex AI Studio (OpenAI-compatible) → traces to Yandex Monium.
    # Model URI: gpt://<идентификатор_каталога>/deepseek-v4-flash
    
    model_list:
      - model_name: deepseek-v4-flash
        litellm_params:
          model: openai/gpt://<идентификатор_каталога>/deepseek-v4-flash
          api_base: https://ai.api.cloud.yandex.net/v1
          api_key: os.environ/YANDEX_AI_STUDIO_API_KEY
    
    litellm_settings:
      drop_params: true
      set_verbose: false
      # Both this callback and LITELLM_OTEL_V2=true are required.
      callbacks:
        - otel
    
    general_settings:
      master_key: os.environ/LITELLM_MASTER_KEY
    

    Где model — идентификатор модели в LiteLLM: префикс openai/ и URI модели Yandex AI Studio, содержащий идентификатор каталога. Например: openai/gpt://b1gt6g8ht345********/deepseek-v4-flash.

    Префикс os.environ/ указывает, что значение параметра нужно получить из соответствующей переменной окружения.

  9. В текстовом редакторе откройте файл docker-compose.yaml:

    nano docker-compose.yaml
    
  10. Добавьте в файл docker-compose.yaml конфигурацию проекта Docker Compose:

    services:
      litellm:
        image: docker.litellm.ai/berriai/litellm:main-latest
        container_name: litellm-proxy
        restart: unless-stopped
        # Host network: on this class of host net.ipv4.ip_forward=0 and bridge NAT drops connections.
        network_mode: host
        volumes:
          - ./config.yaml:/app/config.yaml:ro
        environment:
          - LITELLM_OTEL_V2=true
          - OTEL_EXPORTER=otlp_http
          - OTEL_ENDPOINT=https://ingest.monium.yacloudkz.tech/otlp
          - OTEL_SERVICE_NAME=litellm-proxy
          - OTEL_ENVIRONMENT_NAME=local
          - OTEL_HEADERS=Authorization=Api-Key ${MONIUM_API_KEY:?MONIUM_API_KEY},x-monium-project=${MONIUM_PROJECT:?MONIUM_PROJECT},x-monium-cluster=${MONIUM_CLUSTER:-default},x-monium-service=litellm-proxy
          - OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=span_only
          - YANDEX_AI_STUDIO_API_KEY=${YANDEX_AI_STUDIO_API_KEY:?YANDEX_AI_STUDIO_API_KEY}
          - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY:-sk-local-litellm}
        command:
          - --config
          - /app/config.yaml
          - --port
          - "4000"
          - --host
          - "0.0.0.0"
    

    Параметры конфигурации:

    • environment — переменные окружения:

      • OTEL_EXPORTER=otlp_http — задает отправку трейсов по протоколу OTLP через HTTP.
      • OTEL_ENDPOINT=https://ingest.monium.yacloudkz.tech/otlp — задает базовый адрес эндпоинта. LiteLLM Proxy добавляет к нему /v1/traces и отправляет трейсы на https://ingest.monium.yacloudkz.tech/otlp/v1/traces.
      • x-monium-service=litellm-proxy — имя, которое будет присвоено сервису в интерфейсе Monium.
      • OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=span_only — включает запись текстов запросов и ответов модели в спаны.
    • network_mode: host — запускает контейнер в сети хоста.

      Это позволяет контейнеру обращаться к Yandex AI Studio и Yandex Monium, даже если на хосте отключена пересылка IP-пакетов (net.ipv4.ip_forward = 0). LiteLLM Proxy принимает подключения на порте 4000 виртуальной машины.

  11. Запустите контейнер:

    docker compose up -d
    

    Результат:

    [+] up 27/27
    ✔ Image docker.litellm.ai/berriai/litellm:main-latest Pulled                                                      82.4s
    ✔ Container litellm-proxy                             Started                                                     43.1s
    
  12. Чтобы проверить, что LiteLLM Proxy готов принимать запросы, выполните команду в терминале виртуальной машины:

    for _ in $(seq 1 30); do
      curl -sf http://127.0.0.1:4000/health/liveliness >/dev/null && echo "proxy is up" && break
      sleep 1
    done
    

    Результат:

    proxy is up
    

Отправьте запросы к модели и проверьте трейсыОтправьте запросы к модели и проверьте трейсы

Чтобы проверить передачу трейсов, создайте и запустите тестовое приложение на той же ВМ, где работает LiteLLM Proxy. Приложение отправит запросы к модели через прокси, а прокси передаст трейсы в Monium Traces. Затем найдите эти трейсы в интерфейсе Monium и просмотрите данные о выполнении запросов.

Создайте и запустите тестовое приложениеСоздайте и запустите тестовое приложение

Тестовое приложение на языке Python имитирует диалог покупателя с агентом службы поддержки. Для отправки запросов к модели через LiteLLM Proxy оно использует библиотеку openai. Установите ее в отдельное виртуальное окружение, чтобы изолировать зависимости приложения.

Чтобы создать и запустить приложение:

  1. Перейдите в директорию ~/litellm-proxy, созданную при развертывании LiteLLM Proxy на виртуальной машине:

    cd ~/litellm-proxy
    
  2. Установите Python и модуль venv для работы с виртуальными окружениями:

    sudo apt install python3 python3.12-venv
    
  3. Создайте и активируйте виртуальное окружение litellm-venv:

    python3 -m venv ~/litellm-venv
    source ~/litellm-venv/bin/activate
    
  4. Установите библиотеку openai:

    pip install 'openai==2.54.0'
    
  5. Создайте и откройте в текстовом редакторе файл тестового приложения agent_example.py:

    touch agent_example.py
    nano agent_example.py
    
  6. Добавьте в файл agent_example.py следующий код:

    Код приложения
    """Customer support agent that talks to a local LiteLLM proxy only.
    
    No client-side telemetry: tracing is handled by the proxy (OTEL v2 → Monium).
    """
    
    from __future__ import annotations
    
    import argparse
    import json
    import os
    import random
    import sys
    
    from openai import OpenAI
    
    DEFAULT_PROXY_BASE = "http://127.0.0.1:4000/v1"
    DEFAULT_PROXY_KEY = "sk-local-litellm"
    DEFAULT_MODEL = "deepseek-v4-flash"
    
    
    class KnowledgeBase:
        """Simple knowledge base for customer support."""
    
        def __init__(self) -> None:
            self.knowledge = {
                "shipping": {
                    "standard": "Standard shipping takes 5-7 business days and costs $5.99",
                    "express": "Express shipping takes 2-3 business days and costs $12.99",
                    "overnight": "Overnight shipping delivers next business day and costs $24.99",
                },
                "returns": {
                    "policy": "You can return items within 30 days of purchase for a full refund",
                    "process": "To return an item, go to your account, find the order, and click 'Return Item'",
                    "condition": "Items must be in original condition with tags attached",
                },
                "account": {
                    "password_reset": "Click 'Forgot Password' on the login page and follow the email instructions",
                    "update_info": "You can update your account information in the 'My Account' section",
                    "delete_account": "Contact customer service to delete your account permanently",
                },
                "products": {
                    "warranty": "All products come with a 1-year manufacturer warranty",
                    "availability": "Check product availability on the product page",
                    "specifications": "Product specifications are listed in the product description",
                },
            }
    
        def search(self, query: str) -> str:
            query_lower = query.lower()
    
            if any(
                word in query_lower for word in ["ship", "delivery", "shipping", "deliver"]
            ):
                if "express" in query_lower or "fast" in query_lower:
                    return self.knowledge["shipping"]["express"]
                if "overnight" in query_lower or "next day" in query_lower:
                    return self.knowledge["shipping"]["overnight"]
                return self.knowledge["shipping"]["standard"]
    
            if any(word in query_lower for word in ["return", "refund", "exchange"]):
                if "how" in query_lower or "process" in query_lower:
                    return self.knowledge["returns"]["process"]
                if "condition" in query_lower:
                    return self.knowledge["returns"]["condition"]
                return self.knowledge["returns"]["policy"]
    
            if any(
                word in query_lower for word in ["account", "password", "login", "profile"]
            ):
                if "password" in query_lower or "reset" in query_lower:
                    return self.knowledge["account"]["password_reset"]
                if "update" in query_lower or "change" in query_lower:
                    return self.knowledge["account"]["update_info"]
                if "delete" in query_lower:
                    return self.knowledge["account"]["delete_account"]
    
            if any(
                word in query_lower for word in ["product", "warranty", "available", "spec"]
            ):
                if "warranty" in query_lower:
                    return self.knowledge["products"]["warranty"]
                if "available" in query_lower or "stock" in query_lower:
                    return self.knowledge["products"]["availability"]
                return self.knowledge["products"]["specifications"]
    
            return (
                "I couldn't find specific information about that. "
                "Let me help you with a general response."
            )
    
    
    class CustomerSupportAgent:
        """Customer support agent using an OpenAI-compatible LiteLLM proxy."""
    
        def __init__(
            self,
            *,
            base_url: str,
            api_key: str,
            model: str,
        ) -> None:
            self.client = OpenAI(base_url=base_url, api_key=api_key)
            self.model = model
            self.knowledge_base = KnowledgeBase()
            self.conversation_history: list[dict] = []
            self.system_prompt = """
    You are a helpful and friendly customer support agent. Your role is to:
    1. Assist customers with their questions and concerns
    2. Use the available tools whenever they help answer the question
    3. Be polite, professional, and empathetic
    4. If you don't know something, admit it and offer to escalate to a human agent
    5. Keep responses concise but helpful
    
    Always maintain a positive and solution-oriented attitude.
    Prefer calling tools for shipping, returns, order status, refunds, accounts, and products
    instead of inventing details.
    """.strip()
    
        def search_knowledge_base(self, query: str) -> str:
            return self.knowledge_base.search(query)
    
        def get_order_status(self, order_id: str) -> str:
            statuses = ["Processing", "Shipped", "Out for Delivery", "Delivered"]
            return f"Order {order_id} status: {random.choice(statuses)}"
    
        def process_refund(self, order_id: str, reason: str) -> str:
            return (
                f"Refund request for order {order_id} has been submitted. "
                f"Reason: {reason}. You'll receive confirmation within 24 hours."
            )
    
        def get_available_tools(self) -> list[dict]:
            return [
                {
                    "type": "function",
                    "function": {
                        "name": "search_knowledge_base",
                        "description": (
                            "Search the knowledge base for shipping, returns, "
                            "accounts, and products"
                        ),
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "query": {
                                    "type": "string",
                                    "description": "The customer's question or topic",
                                }
                            },
                            "required": ["query"],
                        },
                    },
                },
                {
                    "type": "function",
                    "function": {
                        "name": "get_order_status",
                        "description": "Get the current status of a customer's order",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "order_id": {
                                    "type": "string",
                                    "description": "The order ID to check",
                                }
                            },
                            "required": ["order_id"],
                        },
                    },
                },
                {
                    "type": "function",
                    "function": {
                        "name": "process_refund",
                        "description": "Process a refund request for a customer",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "order_id": {
                                    "type": "string",
                                    "description": "The order ID to refund",
                                },
                                "reason": {
                                    "type": "string",
                                    "description": "Reason for the refund",
                                },
                            },
                            "required": ["order_id", "reason"],
                        },
                    },
                },
            ]
    
        def execute_tool_call(self, tool_call) -> str:
            function_name = tool_call.function.name
            arguments = json.loads(tool_call.function.arguments)
    
            if function_name == "search_knowledge_base":
                return self.search_knowledge_base(arguments["query"])
            if function_name == "get_order_status":
                return self.get_order_status(arguments["order_id"])
            if function_name == "process_refund":
                return self.process_refund(arguments["order_id"], arguments["reason"])
            return f"Unknown tool: {function_name}"
    
        def chat(self, user_message: str) -> str:
            self.conversation_history.append({"role": "user", "content": user_message})
            messages = [
                {"role": "system", "content": self.system_prompt}
            ] + self.conversation_history
    
            try:
                response = self.client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    tools=self.get_available_tools(),
                    tool_choice="auto",
                    temperature=0.4,
                    max_tokens=700,
                )
                assistant_message = response.choices[0].message
    
                # Allow a few tool rounds so one user turn can expand into several LLM calls.
                for _ in range(4):
                    if not assistant_message.tool_calls:
                        break
    
                    self.conversation_history.append(
                        {
                            "role": "assistant",
                            "content": assistant_message.content,
                            "tool_calls": [
                                {
                                    "id": tool_call.id,
                                    "type": "function",
                                    "function": {
                                        "name": tool_call.function.name,
                                        "arguments": tool_call.function.arguments,
                                    },
                                }
                                for tool_call in assistant_message.tool_calls
                            ],
                        }
                    )
    
                    for tool_call in assistant_message.tool_calls:
                        tool_result = self.execute_tool_call(tool_call)
                        print(f"  [tool] {tool_call.function.name} -> {tool_result}")
                        self.conversation_history.append(
                            {
                                "role": "tool",
                                "tool_call_id": tool_call.id,
                                "content": tool_result,
                            }
                        )
    
                    messages = [
                        {"role": "system", "content": self.system_prompt}
                    ] + self.conversation_history
                    response = self.client.chat.completions.create(
                        model=self.model,
                        messages=messages,
                        tools=self.get_available_tools(),
                        tool_choice="auto",
                        temperature=0.4,
                        max_tokens=700,
                    )
                    assistant_message = response.choices[0].message
    
                response_content = assistant_message.content or ""
                self.conversation_history.append(
                    {"role": "assistant", "content": response_content}
                )
                return response_content
    
            except Exception as exc:  # noqa: BLE001 - surface proxy/LLM errors to the chat
                return f"I apologize, but I'm experiencing technical difficulties. Error: {exc}"
    
        def reset_conversation(self) -> None:
            self.conversation_history = []
    
    
    DEMO_TURNS = [
        "Hi! I need express shipping info for an order I'm about to place.",
        "Also, what is the status of my existing order ORD-10482?",
        "The item arrived damaged. What is your return policy and how do I start a return?",
        "Please process a refund for order ORD-10482 because the item arrived damaged.",
        "One last thing: how long is the product warranty, and how do I reset my account password?",
    ]
    
    
    def run_demo(agent: CustomerSupportAgent) -> int:
        print("Customer Support Agent (LiteLLM proxy demo)")
        print("=" * 60)
        print(f"Proxy model: {agent.model}")
        print(f"Turns: {len(DEMO_TURNS)}\n")
    
        for idx, user_input in enumerate(DEMO_TURNS, start=1):
            print(f"[{idx}/{len(DEMO_TURNS)}] You: {user_input}")
            response = agent.chat(user_input)
            print(f"Agent: {response}\n")
    
        print("Demo finished.")
        return 0
    
    
    def run_interactive(agent: CustomerSupportAgent) -> int:
        print("Customer Support Agent")
        print("=" * 50)
        print("Type 'quit' to exit or 'reset' to start a new conversation.\n")
    
        while True:
            try:
                user_input = input("\nYou: ").strip()
            except (EOFError, KeyboardInterrupt):
                print(
                    "\nAgent: Thank you for contacting customer support. Have a great day!"
                )
                return 0
    
            if user_input.lower() in {"quit", "exit", "bye"}:
                print(
                    "\nAgent: Thank you for contacting customer support. Have a great day!"
                )
                return 0
            if user_input.lower() == "reset":
                agent.reset_conversation()
                print("\nAgent: Conversation reset. How can I help you today?")
                continue
            if not user_input:
                continue
    
            print(f"\nAgent: {agent.chat(user_input)}")
    
    
    def build_agent_from_env() -> CustomerSupportAgent:
        return CustomerSupportAgent(
            base_url=os.getenv("LITELLM_PROXY_BASE", DEFAULT_PROXY_BASE),
            api_key=os.getenv("LITELLM_PROXY_KEY", DEFAULT_PROXY_KEY),
            model=os.getenv("LITELLM_MODEL", DEFAULT_MODEL),
        )
    
    
    def main(argv: list[str] | None = None) -> int:
        parser = argparse.ArgumentParser(description=__doc__)
        parser.add_argument(
            "--interactive",
            action="store_true",
            help="Run an interactive REPL instead of the multi-step demo",
        )
        args = parser.parse_args(argv)
    
        agent = build_agent_from_env()
        if args.interactive:
            return run_interactive(agent)
        return run_demo(agent)
    
    
    if __name__ == "__main__":
        sys.exit(main())
    

    Приложение последовательно отправляет модели через локальный LiteLLM Proxy пять сообщений покупателя: вопрос о доставке, вопрос о статусе заказа, вопрос о правилах возврата товара, просьбу вернуть деньги и сообщение с двумя вопросами — о гарантии и сбросе пароля.

    Для подготовки ответа модель может запросить вызов инструмента: поиск в локальной базе знаний, проверку статуса заказа или возврат денег. Приложение выполняет этот вызов и передает результат модели через прокси в следующем запросе. Проверка статуса заказа и возврат денег имитируются в коде приложения.

    Число запросов зависит от того, какие инструменты вызовет модель. Например, при проверке статуса заказа первый запрос позволяет модели выбрать инструмент, а второй передает ей результат проверки для подготовки ответа покупателю. Для каждого запроса LiteLLM Proxy формирует отдельный трейс и передает его в Monium Traces. Само приложение телеметрию не отправляет.

  7. Запустите тестовое приложение:

    python3 agent_example.py
    

    Результат:

    Customer Support Agent (LiteLLM proxy demo)
    ============================================================
    Proxy model: deepseek-v4-flash
    Turns: 5
    ...
    [1/5] You: Hi! I need express shipping info for an order I'm about to place.
      [tool] search_knowledge_base -> Express shipping takes 2-3 business days and costs $12.99
    Agent: Great news! Here's the express shipping information:
    ...
    ...
    Demo finished.
    

Найдите и просмотрите трейсы в MoniumНайдите и просмотрите трейсы в Monium

Чтобы найти трейсы запросов тестового приложения:

Интерфейс Monium
  1. Перейдите в сервис Monium.

  2. На панели слева выберите LLM-мониторинг.

  3. Выберите на шкале времени в верхней части экрана период, когда вы запускали тестовое приложение.

  4. В правом верхнем углу строки запроса нажмите значок и введите фильтр:

    {project = "folder__<идентификатор_каталога>", service = "litellm-proxy"}
    

    Где <идентификатор_каталога> — идентификатор каталога, в котором вы ранее создали сервисные аккаунты.

  5. Нажмите кнопку Выполнить запрос.

В результатах поиска появятся трейсы запросов тестового приложения. Чтобы просмотреть все операции в трейсе, нажмите на строку с ним, затем — Показать полный трейс.

В открывшемся трейсе будут представлены следующие спаны:

  • POST /v1/chat/completions — HTTP-запрос к LiteLLM Proxy.
  • auth /v1/chat/completions — проверка ключа доступа к LiteLLM Proxy.
  • chat gpt://<идентификатор_каталога>/deepseek-v4-flash — вызов модели. Спан содержит имя модели, количество входных и выходных токенов, тексты сообщений.

Если модель запросила вызовы инструментов, спан модели содержит их имена (search_knowledge_base, get_order_status или process_refund) и аргументы вызовов.

Приложение сохраняет историю диалога и передает ее в последующих запросах, поэтому контекст в следующих трейсах становится длиннее.

Подробнее о просмотре трейсов в разделе Просмотр и анализ LLM-трейсов.

Решение проблемРешение проблем

Проблема

Решение

Ошибка соединения (Error: Connection error)

  • На последнем шаге развертывания LiteLLM Proxy дождитесь сообщения proxy is up.
  • Убедитесь, что порт 4000 виртуальной машины не занят другим приложением.

Yandex AI Studio возвращает код HTTP 401 или 403

  • Убедитесь, что переменная YANDEX_AI_STUDIO_API_KEY содержит только ключ, без Bearer, Api-Key и других лишних символов.

  • Убедитесь, что в config.yaml:

    • model (идентификатор модели в LiteLLM) — openai/gpt://<идентификатор_каталога>/deepseek-v4-flash;
    • api_base (адрес) — https://ai.api.cloud.yandex.net/v1.

Тестовое приложение работает, но трейсы не поступают в Monium

Убедитесь, что:

  • значение переменной MONIUM_PROJECT — folder__<идентификатор_каталога> (с двумя подчеркиваниями);
  • в файле docker-compose.yaml в заголовке Authorization между Api-Key и ключом есть пробел (не символ %20);
  • сервисному аккаунту litellm-monium-sa назначена роль monium.traces.writer на каталог, указанный в переменной MONIUM_PROJECT;
  • для API-ключа этого сервисного аккаунта задана область действия yc.monium.traces.write;
  • в интерфейсе Monium указан фильтр {project = "folder__<идентификатор_каталога>", service = "litellm-proxy"} и выбран период, когда вы запускали тестовое приложение.

Нет сетевого соединения у контейнера (Connection reset)

Выполните команду sysctl net.ipv4.ip_forward.

Если результат net.ipv4.ip_forward = 0, в файле docker-compose.yaml оставьте строку network_mode: host.

После внесения изменений выполните docker compose up -d --force-recreate.

Спаны не содержат текста сообщений

Убедитесь, что файл docker-compose.yaml содержит переменную OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=span_only.

После внесения изменений выполните docker compose up -d --force-recreate.

Как удалить созданные ресурсыКак удалить созданные ресурсы

Если вы развернули систему только в тестовых целях, после завершения работы можно удалить созданные ресурсы, чтобы избежать дальнейших расходов:

  1. Удалите виртуальную машину с LiteLLM Proxy.
  2. Если диск не был удален вместе с виртуальной машиной, удалите его.
  3. Если вы зарезервировали для виртуальной машины статический публичный IP-адрес, удалите его.

Если сервисные аккаунты litellm-ai-studio-sa и litellm-monium-sa больше не нужны, удалите их.

Область действия — параметр API-ключа, который ограничивает применение ключа определенными операциями в дополнение к правам доступа сервисного аккаунта. Подробнее в разделе API-ключи с ограничениями области и срока действия.

Спан — базовый элемент распределенной трассировки, отдельная операция в вашей системе. Например, HTTP-запрос, вызов базы данных или выполнение функции. Подробнее читайте в разделе Спан.

Была ли статья полезна?

Предыдущая
SLO-мониторинг веб-сервиса
Следующая
Обзор
Создавайте контент и получайте гранты!Готовы написать своё руководство? Участвуйте в контент-программе и получайте гранты на работу с облачными сервисами!
Подробнее о программе
Проект Яндекса
© 2026 ТОО «Облачные Сервисы Казахстан»