Мета-контроллер
В многоагентной команде вы, возможно, заметили некоторую жесткость. Мы жестко задали последовательность: News -> Technical -> Financial -> Writer.
А что, если пользователю нужен только технический анализ? Наша система все равно будет тратить время и деньги на запуск других аналитических инструментов.
В архитектуре Meta-Controller используется интеллектуальный диспетчер. Единственная задача этого агента-контроллера — проанализировать запрос пользователя и определить, какой специалист лучше всего подходит для выполнения этой задачи.
В системах RAG или агентных системах Мета-контроллер является центральной нервной системой. Это своего рода «входная дверь», которая направляет входящие запросы в нужный отдел.
Простейшая версия контроллера Meta работает следующим образом:


Инструмент, использующий рабочий процесс (создан пользователем)
Фарид Хан
)
Получение запроса: Агент получает запрос от пользователя.
Решение: Агент анализирует запрос и рассматривает доступные инструменты. Затем он решает, необходим ли тот или иной инструмент для точного ответа на вопрос.
Действие: Если требуется инструмент, агент формирует вызов этого инструмента, например, конкретной функции с правильными аргументами.
Наблюдение: Система выполняет вызов инструмента, и результат («наблюдение») отправляется обратно агенту.
Синтез: Агент берет выходные данные инструмента, объединяет их со своими собственными рассуждениями и генерирует окончательный, обоснованный ответ для пользователя.
Для этого нам нужно предоставить нашему агенту инструмент. Для этого мы будем использовать TavilySearchResultsинструмент, который дает нашему агенту доступ к поиску в интернете. Самая важная часть здесь — описание . LLM читает это описание на естественном языке, чтобы понять, что делает инструмент и когда его следует использовать, поэтому сделать его ясным и точным — ключ к успеху.
# Инициализируем инструмент. Мы можем установить максимальное количество результатов, чтобы контекст был кратким.
search_tool = TavilySearchResults(max_results= 2 )
# Крайне важно дать инструменту четкое имя и описание для агента.
search_tool.name = "web_search"
search_tool.description = "Инструмент, который можно использовать для поиска в интернете актуальной информации по любой теме, включая новости, события и текущие события."
tools = [search_tool]
Теперь, когда у нас есть функциональный инструмент, мы можем создать агента, который научится им пользоваться. Состояние агента, использующего инструмент, довольно простое: это всего лишь список сообщений, отслеживающий всю историю разговора.
class AgentState ( TypedDict ):
messages: Annotated[ list [AnyMessage], add_messages]
Далее нам необходимо сообщить LLM о предоставленных нам инструментах. Это критически важный шаг. Мы используем .bind_tools()метод, который, по сути, внедряет название и описание инструмента в системную подсказку LLM, позволяя ему самому решать, когда вызывать этот инструмент.
llm = ChatNebius(model= "meta-llama/Meta-Llama-3.1-8B-Instruct" , temperature= 0 )
# Привязываем инструменты к LLM, делая его совместимым с инструментами
llm_with_tools = llm.bind_tools(tools)
Теперь мы можем определить рабочий процесс нашего агента с помощью LangGraph. Нам нужны два основных узла: agent_node«мозг», который вызывает LLM для принятия решения о дальнейших действиях, и tool_node«руки», которые фактически выполняют инструмент.
def agent_node ( state: AgentState ):
"""Первичный узел, который вызывает LLM для принятия решения о следующем действии."""
console. print ( "--- АГЕНТ: Размышляет... ---" )
response = llm_with_tools.invoke(state[ "messages" ])
return { "messages" : [response]}
# ToolNode — это предварительно созданный узел из LangGraph, который выполняет инструменты
tool_node = ToolNode(tools)
После agent_nodeвыполнения заданий нам нужен маршрутизатор, чтобы определить, куда двигаться дальше. Если последнее сообщение агента содержит tool_callsатрибут, это означает, что он хочет использовать инструмент, поэтому мы направляем его к этому инструменту tool_node. В противном случае это означает, что у агента есть окончательный ответ, и мы можем завершить рабочий процесс.
def router_function ( state: AgentState ) -> str :
"""Проверяет последнее сообщение агента, чтобы решить, какой будет следующий шаг."""
last_message = state[ "messages" ][- 1 ]
if last_message.tool_calls:
# Агент запросил вызов инструмента
console. print ( "--- МАРШРУТИЗАТОР: Решение — вызвать инструмент. ---" )
return "call_tool"
else :
# Агент предоставил окончательный ответ
console. print ( "--- МАРШРУТИЗАТОР: Решение — завершить. ---" )
return "__end__"
Хорошо, у нас есть все необходимые элементы. Давайте соединим их в граф. Ключевым моментом здесь является условное ребро, которое использует наш механизм router_functionдля создания основного цикла рассуждений агента: agent-> router-> tool-> agent.
graph_builder = StateGraph(AgentState)
# Добавить узлы
graph_builder.add_node( "agent" , agent_node)
graph_builder.add_node( "call_tool" , tool_node)
# Установить точку входа
graph_builder.set_entry_point( "agent" )
# Добавить условный маршрутизатор
graph_builder.add_conditional_edges(
"agent" ,
router_function,
)
# Добавить ребро от узла инструмента обратно к агенту, чтобы завершить цикл
graph_builder.add_edge( "call_tool" , "agent" )
# Скомпилировать граф
tool_agent_app = graph_builder. compile ()

Мета-контроллер (создан пользователем)
Фарид Хан
)
Теперь давайте протестируем наш диспетчер с помощью различных подсказок. Каждая из них предназначена для проверки того, отправляет ли диспетчер запрос нужному специалисту.
# Тест 1: Следует перенаправить на специалиста широкого профиля
run_agent( "Здравствуйте, как дела сегодня?" )
# Тест 2: Следует перенаправить на исследователя
run_agent( "Какие были последние финансовые результаты NVIDIA?" )
# Тест 3: Следует перенаправить на программиста
run_agent( "Можете ли вы написать мне функцию на Python для вычисления n-го числа Фибоначчи?" )
На графике выходных данных видно, что контроллер каждый раз принимает взвешенные решения.
--- 🧠 Мета-контроллер анализирует запрос ---
[жёлтый]Решение о маршрутизации:[/жёлтый] Отправить [жирный]Специалисту[/жирный]. [курсив]Причина: Запрос пользователя — простое приветствие...[/курсив]
Окончательный ответ: Здравствуйте! Чем я могу вам сегодня помочь?
--- 🧠 Мета-контроллер анализирует запрос ---
[жёлтый]Решение о маршрутизации:[/жёлтый] Отправить [жирный]Исследователю[/жирный]. [курсив]Причина: Пользователь спрашивает о недавнем событии...[/курсив]
Окончательный ответ: Последние финансовые результаты NVIDIA... были исключительно сильными. Они сообщили о выручке в размере 26,04 миллиарда долларов...
--- 🧠 Мета-контроллер анализирует запрос ---
[жёлтый]Решение о маршрутизации:[/жёлтый] Отправить [жирный]Программисту[/жирный]. [курсив]Причина: Пользователь явно запрашивает функцию Python...[/курсив]
Окончательный ответ:
```python
def fibonacci(n):
# ... (код) ...
``` {data-source-line="130"}
Система работает безупречно. Приветствие отправляется специалисту широкого профиля, запрос новостей — исследователю, а запрос кода — программисту. Контроллер корректно распределяет задачи в зависимости от их содержания.
Для формализации этого вопроса мы будем использовать LLM в качестве судьи, который оценивает только один параметр: корректность маршрутизации.
class RoutingEvaluation ( BaseModel ):
"""Схема для оценки решения метаконтроллера о маршрутизации.""
routing_correctness_score: int = Field(description= "Оценка от 1 до 10, показывающая, выбрал ли контроллер наиболее подходящего специалиста для запроса." )
justification: str = Field(description= "Краткое обоснование оценки." )
На вопрос типа «Какова столица Франции?» оценка судьи будет следующей:
--- Оценка маршрутизации метаконтроллера ---
{
'routing_correctness_score': 8.5,
'justification': "Контроллер правильно определил запрос пользователя как фактический запрос, требующий актуальной информации, и направил его агенту 'Исследователь'. Это был оптимальный выбор, поскольку у специалиста широкого профиля могли быть устаревшие знания."
}
Этот результат показывает, что наш контроллер не просто прокладывает маршруты, а грамотно распределяет задачи.
Это делает системы искусственного интеллекта масштабируемыми и простыми в обслуживании, поскольку новые навыки можно добавлять, подключая специалистов и обновляя контроллер.
