Как управлять dependency между промптами (один промпт вызывает другой)?

Краткий тезис

Управление зависимостями промптов — это ключевой элемент архитектуры Agentic RAG, где один промпт может вызывать другой как под-агент или использовать его содержимое на этапе компиляции. Систематический подход включает явное описание зависимостей (типа include и call), версионирование промптов, построение DAG зависимостей с проверкой на циклы и использование Prompt Registry с автоматическим отслеживанием изменений. Без этого каскадные изменения в одном промпте могут незаметно сломать поведение всей системы агентов.


1. Термин: dependency (зависимость) между промптами

Dependency (зависимость) возникает, когда выход одного промпта (шаблона или инструкции для LLM) используется как вход для другого, или когда один промпт динамически вызывает другой в процессе выполнения. В RAG|Agentic RAG агенты часто состоят из нескольких шагов, каждый из которых управляется отдельным промптом.

Почему это важно

  • Одиночный промпт редко решает сложную задачу — нужна композиция.
  • Изменение в родительском промпте может повлиять на все дочерние.
  • Необходимо обеспечить целостность и повторяемость поведения системы.

Термин «Промпт» — это шаблон инструкции для LLM, который может содержать переменные, примеры (few-shot) и ссылки на другие промпты.


2. Типы зависимостей: include vs call

Черновик выделяет два основных типа:

2.1 include: — статическая вставка (compile time)

include: — механизм, при котором содержимое одного промпта подставляется в другой на этапе компиляции шаблона. Это похоже на импорт модулей в коде.

Пример синтаксиса (Prompt Template):

# main_prompt.j2
Ты  аналитик документов.
Ответь на вопрос на основе следующего контекста:
{% include 'context_prompt.j2' %}

Характеристики

  • Зависимость разрешается до запуска (статическая).
  • Изменение вложенного промпта автоматически меняет результат главного.
  • Нет дополнительных вызовов LLM — всё уже в одном контексте.
  • Удобно для общих частей (стили, системные инструкции, списки правил).

2.2 call: — динамический вызов (runtime)

call: — механизм, при котором один промпт (агент) во время исполнения решает вызвать другой промпт как под-агента и передаёт ему управление. Это уже агентный паттерн.

Пример (псевдокод агента):

if need_more_info:
    response = call_prompt("search_agent_prompt", query=user_query)
    # используем response в текущем промпте

Характеристики

  • Зависимость разрешается динамически, в зависимости от промежуточного выхода LLM.
  • Вызов может быть последовательным или условным.
  • Требует фреймворка для агентов (LangChain, Semantic Kernel, AutoGen).
  • Изменение вызываемого промпта может изменить логику ветвления вызывающего.
Характеристикаinclude: (compile time)call: (runtime)
Момент разрешенияДо запускаВо время выполнения
Вызов LLMНет (подстановка текста)Да (отдельный вызов)
Влияние измененийПрямое (всегда подставляется новая версия)Косвенное (зависит от логики вызова)
Контроль версийПроще (достаточно тестировать итоговый промпт)Сложнее (нужно тестировать цепочки)

Термин «Compile time» — этап сборки шаблона промпта, когда все include раскрываются. «Runtime» — момент фактического выполнения промпта LLM.


3. Версионирование промптов и каскадные изменения

Если промпт B изменился, нужно протестировать промпт A, который его вызывает или включает. Это каскадное тестирование.

Пример проблемы

  • Промпт summarizer (версия 1.2) содержал инструкцию: «Суммируй не более 50 слов».
  • Промпт report_generator использует include: summarizer и ожидает краткие саммари.
  • После обновления summarizer (версия 1.3) инструкция стала «Дай полный пересказ».
  • report_generator сломается — ответ станет слишком длинным.

Решение

  1. Использовать семантическое версионирование (semver) промптов: X.Y.Z — мажорное изменение (ломает совместимость) → обязательно тестировать всех потребителей.
  2. Хранить манифест зависимостей для каждого промпта (какие промпты он включает/вызывает с диапазонами версий).
  3. Автоматизировать regression-тестирование при изменении любого промпта в графе.

Термин «Семантическое версионирование» — система нумерации, где изменение мажорной версии (X) означает обратно несовместимые изменения, минорной (Y) — добавление функциональности с обратной совместимостью, патч (Z) — исправления багов.


4. DAG зависимостей и проверка на циклы

DAG (Directed Acyclic Graph) — направленный ациклический граф, в котором вершины — промпты, а рёбра — зависимости (include или call).

Почему ацикличность

  • Циклическая зависимость (A -> B -> A) приведёт к бесконечной рекурсии (для call) или конфликту включений (для include).

Пример проверки на циклы (топологическая сортировка):

# Упрощённый код проверки DAG на циклы с помощью DFS
def has_cycle(graph, node, visited, rec_stack):
    visited.add(node)
    rec_stack.add(node)
    for neighbor in graph.get(node, []):
        if neighbor not in visited:
            if has_cycle(graph, neighbor, visited, rec_stack):
                return True
        elif neighbor in rec_stack:
            return True
    rec_stack.discard(node)
    return False

graph = {
    'A': ['B'],
    'B': ['C'],
    'C': ['A']  # цикл!
}
visited = set()
print(has_cycle(graph, 'A', visited, set()))  # True

Рекомендации

  • При добавлении или изменении зависимости проверять, не образуется ли цикл.
  • Использовать готовые библиотеки (NetworkX) для построения и валидации DAG.
  • Для call допускается цикл с условием остановки (например, max_depth), но это усложняет отладку.

Термин «Топологическая сортировка» — упорядочивание вершин DAG так, чтобы все рёбра шли от более ранних к более поздним. Если сортировка невозможна → есть цикл.


5. Инструменты: Prompt Registry с dependency tracking

Prompt Registry (реестр промптов) — централизованный сервис (или модуль), который хранит версии промптов, их зависимости и метаданные.

Ключевые функции

  • Хранение шаблонов и их версий (в базе данных или Git-репозитории).
  • Отслеживание зависимостей: для каждого промпта список include и call с указанием диапазона версий.
  • Автоматическое оповещение при изменении зависимого промпта (например, через CI/CD пайплайн).
  • Предотвращение несовместимых обновлений (blocker при попытке обновить промпт, если какая-то зависимость нарушена).

Пример структуры записи в реестре

{
  "prompt_id": "summarizer",
  "version": "1.2.0",
  "content": "Суммируй документ не более 50 слов.",
  "dependencies": {
    "include": [],
    "call": []
  }
}
{
  "prompt_id": "report_generator",
  "version": "2.0.0",
  "dependencies": {
    "include": ["summarizer@^1.0.0"],
    "call": ["search_agent@>=1.0 <2.0"]
  }
}

Термин «^1.0.0» — диапазон версий (семантическое версионирование): ^ означает «мажорная версия 1, любой минор и патч».

Популярные реализации: Prompt Flow (Microsoft), W&B Prompt Registry, LangChain Hub, Dify Prompt Management.


6. Тестирование зависимостей: тесты на интеграцию и регрессию

Для управления dependency недостаточно просто хранить граф — нужно автоматически проверять, что изменения не сломали систему.

Типы тестов

Тип тестаЧто проверяетКогда запускать
Unit-тест промптаФормат вывода, наличие переменных, отсутствие синтаксических ошибокПри каждом изменении промпта
Интеграционный тестЦепочка A -> B -> C: конечный ответ корректенПри изменении любого промпта в цепочке
Тест на регрессиюСтарые сценарии (golden датасет) не сломалисьПериодически / перед релизом
Тест на циклыDAG остаётся ацикличнымПри изменении зависимостей (CI/CD)

Пример интеграционного теста (Python + pytest):

def test_report_generation():
    # Arrange: загружаем промпты из реестра
    registry = PromptRegistry()
    report_prompt = registry.get("report_generator", version="2.0.0")
    
    # Act: генерируем ответ с помощью агента
    agent = Agent(prompt=report_prompt)
    result = agent.run(input="Какой курс доллара?")
    
    # Assert: проверяем ключевые аспекты
    assert len(result) > 0
    assert "доллар" in result.lower()

Термин «Golden датасет» — набор входных данных и эталонных ответов, используемый для регрессионного тестирования промптов.


7. Управление зависимостями в lifecycle агента

Рассмотрим жизненный цикл промпта-агента в конвейере:

  1. Разработка — создание нового промпта с явными зависимостями (include/call).
  2. Версионирование — фиксация версии и зависимостей в реестре.
  3. Тестирование — прогон unit-тестов и интеграционных тестов для всей группы зависимых промптов.
  4. Развёртывание — деплой новой версии промпта только если все зависимости удовлетворены и тесты пройдены.
  5. Мониторинг — отслеживание метрик (доля отказов, время ответа) для обнаружения регрессий.

Рекомендация хранить зависимости в коде (YAML/JSON рядом с промптами) и автоматически проверять их через CI.


8. Расширенный пример: Agentic RAG с dependency management

Представим систему вопросно-ответного агента, где:

# router_prompt v1.0
Ты  маршрутизатор. Если вопрос фактографический  вызови search_agent_prompt,
иначе  summarise_agent_prompt.
Вызываемые промпты: {call: [search_agent_prompt^1.0, summarise_agent_prompt^1.0]}
# search_agent_prompt v1.0
Сначала перепиши запрос, вызвав rewrite_prompt.
Затем выполни поиск по документам.
Зависимости: {call: [rewrite_prompt^1.0]}
# rewrite_prompt v1.0
Перепиши запрос в формате для поиска.
Зависимости: {}

DAG зависимостей

router_prompt → search_agent_prompt → rewrite_prompt
router_prompt → summarise_agent_prompt → format_prompt

Ацикличен, можно выполнять топологическую сортировку.


Пет-проект для закрепления

Задача Разработать небольшой реестр промптов с отслеживанием зависимостей и автоматической проверкой на циклы.

Инструменты Python, NetworkX (для DAG), JSON/YAML для хранения, pytest для тестов.

Шаги:

  1. Определить структуру данных Prompt с полями: id, version, content, dependencies (список объектов с типом include/call и версией).
  2. Реализовать класс PromptRegistry, который умеет:
    • добавлять/обновлять промпты;
    • проверять, не нарушена ли совместимость версий при добавлении;
    • строить граф зависимостей и проверять его на ацикличность (с помощью NetworkX).
  3. Написать CLI-утилиту, которая загружает все промпты из папки и выдаёт отчёт: наличие циклов, неудовлетворённые зависимости.
  4. Добавить интеграционный тест: при изменении промпта B тестируется промпт A (который его включает/вызывает).

Ожидаемый результат Инструмент, который предотвращает каскадные поломки и автоматически подсвечивает проблемы при изменении промптов в многокомпонентной системе агентов.


Связь с другими вопросами

ВопросТема
800Основы архитектуры Agentic RAG
803Цепочки вызовов агентов
805Управление состоянием в агентных системах
808Версионирование промптов
810Дебаггинг и трассировка агентов
730Общие принципы композиции LLM-вызовов

Навигация