Главная / Статьи / Позвольте Близнецам выбрать источник: FAISS, Tavily и прямые ответы в LangGraph

Позвольте Близнецам выбрать источник: FAISS, Tavily и прямые ответы в LangGraph

Создайте небольшой рабочий процесс LangGraph, в котором Gemini направляет каждый вопрос в базу знаний FAISS, на поиск в интернете с помощью Tavily или на прямой ответ, с использованием механизма направления запросов к структурированным результатам.

3771 слов

Большинство прототипов систем ответов на вопросы обрабатывают каждый запрос через одну и ту же фиксированную цепочку операций, несмотря на то, что вопросы различаются по своим требованиям: некоторые зависят от внутренних документов компании, другие — от данных, которые меняются ежедневно, а некоторые требуют лишь общих знаний. В этом руководстве создается компактный Python-пайплайн, в котором языковая модель сначала анализирует каждый вопрос и направляет его туда, куда он нужен: в хранилище векторов FAISS для внутренних знаний, в Tavily для актуальных результатов из Интернета или напрямую в Gemini. К концу вы поймёте, как состояния, узлы и условные связи взаимодействуют в LangGraph, почему структурированный вывод делает маршрутизатор LLM надёжным и где этот пример, предназначенный для обучения, требует доработок перед использованием с реальными пользователями. Если вам нужен более полный каталог форм графов, прочитайте статью о маршрутизации, распространении данных и пяти шаблонах LangGraph.

«Шаблоны критики и одобрения в LangGraph» дополняют этот практический пример реализации.

Три вопроса, три разных источника

Представьте трех пользователей. Первый спрашивает о политике отпусков компании; на этот вопрос могут ответить только внутренние документы. Второй хочет узнать погоду сегодня в Уттаракханде; ни один внутренний документ не содержит этой информации, поэтому системе приходится искать её в Интернете. Третий спрашивает, что такое RAG; модель уже знает ответ, и получение дополнительной информации лишь увеличит задержку.

Поэтому целевая архитектура предусматривает использование маршрутизатора на базе Gemini перед тремя ветвями, которые все сходятся к одному шагу генерации ответа:

User Question
                               |
                               v
                       +----------------+
                       |   AI Router    |
                       |    (Gemini)    |
                       +----------------+
                         /      |      \
                        /       |       \
                       v        v        v
                    FAISS    Tavily    Gemini
                 Internal DB  Web      Direct
                       \        |        /
                        \       |       /
                         v      v       v
                       +----------------+
                       | Generate Answer|
                       |     Gemini     |
                       +----------------+
                               |
                               v
                            Answer

Маршрутизатор является сердцем данной конструкции. Соблазнительным упрощением является сопоставление ключевых слов, как показано в приведённом ниже псевдокоде, где определённые слова в вопросе выбирают соответствующую ветвь:

if "weather" in question:
    use_tavily()
elif "leave" in question:
    use_faiss()
else:
    use_gemini()

Здесь принятие решения передается модели Gemini. Поскольку модель интерпретирует вопрос, а не ищет в нем ключевые слова, такой подход получил название «агентный».

Генеративный ИИ, агенты и агентные рабочие процессы

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

Простой генеративный ИИ

В самом простом случае запрос передается модели, и она возвращает то, что сгенерировала:

User Question
      |
      v
     LLM
      |
      v
    Answer

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

Агенты, вызывающие инструменты

Агент расширяет возможности модели с помощью таких инструментов, как база данных, поисковая система или внешний API, и позволяет ей решать, стоит ли использовать один из них перед ответом:

User Question
      |
      v
     LLM
      |
      +------> Database
      |
      +------> Web Search
      |
      +------> API
      |
      v
    Answer

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

Рабочие процессы с агентами

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

Question
   |
   v
Router
   |
   +----> FAISS
   |
   +----> Tavily
   |
   +----> Gemini

Разработчик определяет три возможных варианта действий; модель лишь выбирает один из них для каждого поступающего вопроса. Ей не предоставляется полный контроль над приложением, а лишь меню разрешенных действий. «Рабочий процесс маршрутизации с агентами» — это наиболее точное название такой схемы.

Настройка проекта

Вам понадобится Python 3.10 или новее, ключ API для Google Gemini и ключ API для Tavily. Установите библиотеки сразу:

pip install -U langchain langchain-google-genai langchain-community langgraph faiss-cpu tavily-python python-dotenv pydantic

Храните оба ключа в файле .env, а не в исходном коде:

GOOGLE_API_KEY=your_google_api_key
TAVILY_API_KEY=your_tavily_api_key

Импорты включают инструменты для определения типов, Pydantic для схем маршрутизации, обёртки LangChain для работы с документами и FAISS, классы чата и генерации эмбеддингов Gemini, примитивы графов LangGraph и клиент Tavily:

import os
from typing import List, Literal
from typing_extensions import TypedDict
from dotenv import load_dotenv
from pydantic import BaseModel
from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_google_genai import (
    ChatGoogleGenerativeAI,
    GoogleGenerativeAIEmbeddings,
)
from langgraph.graph import StateGraph, START, END
from tavily import TavilyClient

Следующим шагом, показанным в виде однострочного фрагмента кода, является простое загрузка переменных окружения:

Load the environment variables:

При использовании параметра override=True значения из файла .env имеют приоритет над переменными, уже заданными в вашей оболочке:

load_dotenv(override=True)

Настройка Gemini для маршрутизации, ответов и генерации эмбеддингов

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

model = ChatGoogleGenerativeAI(
    model="gemini-3.6-flash",
    max_tokens=None,
    timeout=None,
    max_retries=2,
    api_key=os.getenv("GOOGLE_API_KEY"),
)

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

Эмбеддинги — это отдельная категория данных, обрабатываемая специальной моделью. Gemini Embedding преобразует каждый документ в числовой вектор:

embeddings = GoogleGenerativeAIEmbeddings(
    model="models/gemini-embedding-001",
    google_api_key=os.getenv("GOOGLE_API_KEY"),
)

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

Небольшая внутренняя база знаний в FAISS

Чтобы сосредоточить внимание на рабочем процессе, база знаний содержит всего три кратких документа: политику возврата средств на 30 дней, право на 20 оплачиваемых каникулярных дней с требованием уведомления за семь дней и часы обслуживания в рабочие дни. В реальной системе эти тексты могли бы находиться в руководствах, PDF-файлах, страницах Notion, заявках на поддержку, внутренних документах или строках базы данных.

documents = [
    Document(
        page_content="""
        Our company provides a 30-day refund policy.
        Customers can request a refund within 30 days of purchase.
        """
    ),
    Document(
        page_content="""
        Employees receive 20 paid vacation days per year.
        Vacation requests must be submitted at least 7 days in advance.
        """
    ),
    Document(
        page_content="""
        The company provides technical support from Monday to Friday,
        9 AM to 6 PM IST.
        """
    ),
]

Для создания магазина и его оформления в виде инструмента поиска требуется два вызова. Установка значения k в 3 приводит к получению трех ближайших документов; в рамках этого упрощенного набора данных каждый документ возвращается в порядке убывания сходства:

vector_store = FAISS.from_documents(
    documents,
    embeddings,
)

retriever = vector_store.as_retriever(
    search_kwargs={"k": 3}
)

FAISS не понимает языка; это индекс для поиска ближайших соседей. Модель вкладки преобразует поступающий вопрос в вектор, после чего FAISS возвращает сохраненные документы, векторы которых находятся ближе всего к этому вектору.

Добавление клиента Tavily для получения актуальной информации

Для поиска в Интернете требуется лишь клиентская инстанция, созданная с использованием второго API-ключа:

tavily_client = TavilyClient(
    api_key=os.getenv("TAVILY_API_KEY")
)

Проектирование общего состояния

Состояние — это центральная идея LangGraph: типизированный словарь, который передается по графу, из которого читают данные и вносят свой вклад все узлы. Для этого процесса необходимы вопрос, все полученные документы, результаты Tavily, выбранный источник и окончательный ответ:

class AgentState(TypedDict):
    question: str
    documents: List[Document]
    tavily_response: str
    source: str
    answer: str

Концептуально это похоже на общий запись, которую может видеть каждый шаг:

AgentState
                    |
        +-----------+-----------+
        |           |           |
    question    documents    source
                    |
             tavily_response
                    |
                  answer

Узел получает текущее состояние, использует те поля, которые его интересуют, и возвращает обновления; LangGraph объединяет эти обновления перед тем, как передать состояние следующему узлу.

Узел поиска FAISS

Этот узел берет вопрос из состояния, обрабатывает его с помощью механизма поиска и сохраняет соответствующие документы:

def retrieve_from_faiss(state : AgentState) -> AgentState:
    question = state['question']

    """ Fetch the details from the FAISS vector database
    """

    result = retriever.invoke(question)

    return {**state, "documents": result}

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

"What is our company leave policy?"

Для такого ввода механизм поиска должен выдать документ о отпуске:

Employees receive 20 paid vacation days per year.
Vacation requests must be submitted at least 7 days in advance.

Узел поиска Tavily

Узел веб-поиска отправляет вопрос в Tavily с параметром search_depth, установленным в значение advanced, затем извлекает поле content из каждого результата:

def search_with_tavily(state: AgentState) -> AgentState:
    question = state['question']
    """ Using the Tavily to search the web and
      fetch the latest information about user query
    """

    response = tavilyClient.search(
        query=question,
        search_depth='advanced'
    )

    contents = [result["content"] for result in response["results"]]
    return {**state, "tavilyResponse":contents}

Эти фрагменты становятся контекстом, который Gemini будет использовать при формировании ответа. Обратите внимание, что узел возвращает список строк; если вы предпочитаете единый блок текста, объедините элементы перед сохранением, чтобы тип поля соответствовал типу str, заданному в состоянии.

Обеспечение надежности маршрутизатора с помощью структурированного вывода

Примитивный маршрутизатор просит модель ответить одним из трех простых слов:

Return only:
faiss
tavily
gemini

Модели языка не всегда соблюдают инструкции по форматированию. Возможно, вы получите целое предложение в ответ:

I would choose tavily.

Или немного иной вариант формулировки:

The best option is: tavily

Любой из этих ответов нарушает структуру графа, которой требуется точная строка для выбора ребра. Структурированный вывод устраняет эту проблему. Сначала опишите допустимые варианты решений с помощью модели Pydantic, у которой единственное поле ограничено тремя конкретными значениями:

class RouteDecision(BaseModel):
    source: Literal["faiss", "tavily", "gemini"]

Затем создайте модель маршрутизатора, которая должна возвращать экземпляр этой схемы. Метод json_schema требует от поставщика ограничивать генерацию согласно схеме, а не полагаться только на формулировку запроса:

router_model = model.with_structured_output(
    RouteDecision,
    method="json_schema",
)

Результат вывода проверяется по типу Literal, поэтому недопустимое значение выдается как ошибка, вместо того чтобы направить работу графа в неопределенное русло.

Написание узла принятия решений

В инструкции к маршрутизации описаны все варианты: faiss — для вопросов о корпоративной политике и других внутренних знаниях, tavily — для любой информации, требующей актуальных данных или информации с веб-ресурсов, а gemini — для общих знаний, не требующих ни того, ни другого:

def decide_source(state:AgentState)-> AgentState:
    question = state["question"]
    prompt = f"""
    Decide the best source for answering this question.

    Choose exactly one:

    faiss:
    Use when the question can be answered using our internal
    knowledge base.related to company policy and all

    tavily:
    Use when the question requires current, recent, or web-based
    information.

    gemini:
    Use when the question is general knowledge and does not
    require our internal documents or current web information.

    Question:
    {question}

    Return only one word:
    faiss, tavily, or gemini
    """

    response = model.invoke(prompt)
    # return response

    return {**state,"source":response.text}

Внимательно посмотрите на последние строки. В текущем виде узел всё ещё вызывает простой model и сохраняет response.text, что соответствует описанному выше нестабильному подходу с использованием свободного текста. Чтобы воспользоваться преимуществами схемы, вместо этого вызовите router_model.invoke(prompt) и сохраните атрибут source результата. Благодаря этой замене маршрутизатор возвращает объект с определённым типом, а не произвольный текст:

RouteDecision(source="faiss")

Указание LangGraph следующего шага

У условных рёбер требуется функция, которая указывает, какую ветвь следует выбрать. Эта функция не принимает никаких решений сама; она читает выбор, уже сохранённый маршрутизатором в состоянии, и возвращает его обратно в LangGraph:

def route_source(
    state: AgentState,
) -> Literal["faiss", "tavily", "gemini"]:
    return state["source"]

Один узел ответа на каждый маршрут

Все три ветви завершаются на одном и том же этапе генерации. Система анализирует source и формирует соответствующий запрос: полученные документы объединяются в контекст для FAISS, фрагменты поиска используются в качестве ссылочного материала для Tavily, а сам вопрос — для прямых ответов:

# Generate the answer for the user
def generateAnswer(state:AgentState) -> AgentState:
    source = state["source"]
    question = state['question']
    documents = state['documents']
    tavilyResponse = state['tavilyResponse']

    if source == "faiss":
        context = "\n\n".join([doc.page_content for doc in documents])
        prompt = f"""Based on the following context answer the question below
           Context:
           {context}

        Question:
        {question}
           """
    elif source == "tavily":
        prompt = f""" Based on the following search result , use this as an reference and provdie the
         answer to the below question

         Context:
         {tavilyResponse}

         Question:
         {question}
        """
    else:
        prompt = f" Answer the following question : {question}"
    response = model.invoke(prompt)
    answer = response.content

    return {**state, "answer":answer}

Каждый ответ генерируется одной и той же моделью Gemini. Единственное, что меняется, — это контекст, предоставляемый модели; в этом и заключается основная идея подхода RAG: лучшие входные данные, а не другая модель, обеспечивают более достоверные ответы.

Сохранение единообразия названий при сборе фрагментов

В этих фрагментах сочетаются два стиля названий, и несоответствия приведут к ошибкам, если скопировать их вместе без изменений. В состоянии объявлено tavily_response, в то время как узлы читают и записывают tavilyResponse; клиент создается как tavily_client, но вызывается как tavilyClient; функция ответа определена как generateAnswer, но регистрируется как generate_answer. Выберите одну конвенцию и применяйте ее везде перед запуском графа.

Сборка графа

На этом этапе компоненты — это узел принятия решения плюс три возможных продолжения:

decide_source
      |
      +----> faiss
      |
      +----> tavily
      |
      +----> gemini

Здесь есть нюанс. Маршрут gemini вовсе не является шагом поиска; он означает «пропустить поиск и сразу дать ответ». Поэтому вместо создания пустого узла для него этот ответвление может напрямую указывать на общий узел генерации.

Начните с создания графа для типов состояний:

workflow = StateGraph(AgentState)

Зарегистрируйте четыре узла:

workflow.add_node("decide", decide_source)
workflow.add_node("faiss", retrieve_from_faiss)
workflow.add_node("tavily", search_with_tavily)
workflow.add_node("generate", generate_answer)

Сделайте узел принятия решений точкой входа:

workflow.add_edge(START, "decide")

Подключите условные ребра. Маппинг преобразует каждое значение, которое может вернуть функция маршрутизации, в имя узла — именно туда gemini направляется непосредственно в функцию generate:

workflow.add_conditional_edges(
    "decide",
    route_source,
    {
        "faiss": "faiss",
        "tavily": "tavily",
        "gemini": "generate",
    },
)

Затем ветви по получению данных и поиску должны перейти к генерации ответа:

workflow.add_edge("faiss", "generate")
workflow.add_edge("tavily", "generate")

Генерация является последним шагом перед завершением графа:

workflow.add_edge("generate", END)

Компиляция превращает определение в запускаемое приложение:

app = workflow.compile()

Готовый граф

Полный процесс от начала до конца:

START
                           |
                           v
                    +--------------+
                    |    decide    |
                    |    source    |
                    +--------------+
                     /      |      \
                    /       |       \
                   v        v        v
               +------+ +--------+ +---------+
               |FAISS | | Tavily | | Generate|
               |      | |        | | directly|
               +------+ +--------+ +---------+
                   \        |         /
                    \       |        /
                     v      v       v
                    +----------------+
                    |    generate    |
                    |     answer     |
                    +----------------+
                            |
                            v
                           END

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

Попробовать три варианта

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

def ask_question(question: str):
    initial_state = {
        "question":question,
        "documents":[],
        "tavilyResponse":"",
        "source":""
    }

    result = app.invoke(initial_state)
    return result

Вопрос, требующий использования веба

Вопрос о погоде следует отправлять на Tavily:

result = ask_question(
    "What is the current weather in Uttarakhand?"
)

Вывести как выбранный исходный материал, так и сгенерированный ответ:

print("Source:", result["source"])
print("Answer:", result["answer"])

Ожидаемый путь:

Source: tavily

Текущие условия существуют только в веб-версии.

Вопрос об общих знаниях

Далее задайте вопрос о Retrieval Augmented Generation:

result = ask_question(
    "What is Retrieval Augmented Generation?"
)

Вывести результат таким же образом:

print("Source:", result["source"])
print("Answer:", result["answer"])

Роутер должен полностью пропустить этап получения данных:

Source: gemini

Модель может самостоятельно объяснить эту концепцию.

Вопрос о внутренней политике

Наконец, вопрос о политике отпусков:

result = ask_question(
    "What is our company leave policy?"
)

И те же самые операторы вывода:

print("Source:", result["source"])
print("Answer:", result["answer"])

На этот раз внутренняя база знаний должна дать правильный ответ:

Source: faiss

Маршрутизация с использованием LLM основана на вероятностях, поэтому следует рассматривать эти результаты как вероятные, а не как гарантированные.

Почему семантическая маршрутизация превосходит правила на основе ключевых слов

Вот снова альтернатива, заданная в коде жестко:

if "weather" in question:
    use_tavily()
elif "leave" in question:
    use_faiss()
else:
    use_gemini()

Подобные правила быстро теряют актуальность. Представьте ситуацию, когда пользователь спрашивает, открыт ли офис в субботу. В этом предложении не упоминается никакая политика, однако ответ может находиться во внутренней документации. Маршрутизатор, основанный на модели, может понять намерение пользователя; список ключевых слов этого сделать не может, если только кто-то заранее не предусмотрит все возможные формулировки.

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

SQL Database
Internal API
CRM
Customer Support System
Documentation
Web Search

Недостатком является дополнительный вызов модели с соответствующими затратами и задержкой перед началом реальной работы.

Является ли это действительно ИИ-агентом?

Более точно будет назвать его небольшой агентским рабочим процессом, чем автономным агентом. Доступные действия заранее фиксированы:

FAISS
Tavily
Direct Gemini

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

delete a database
send an email
call an arbitrary API

Архитектура представляет собой цепочку ответственностей:

Developer defines possible actions
              |
              v
        LLM chooses action
              |
              v
        LangGraph executes
              |
              v
           Result

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

Роль каждого компонента

LangGraph: движок рабочих процессов

LangGraph определяет структуру приложения: состояние, узлы, рёбра, условное маршрутизирование и порядок выполнения. В общем случае каждый шаг следует одному и тому же шаблону:

State
  |
  v
Node
  |
  v
Updated State
  |
  v
Conditional Edge
  |
  +----> Node A
  |
  +----> Node B
  |
  +----> Node C

Граф из небольших шагов легче тестировать и наблюдать, чем одна обширная функция.

FAISS: слой поиска

FAISS обеспечивает часть системы, отвечающую за RAG. В упрощённой форме:

Company Documents
       |
       v
   Embeddings
       |
       v
     FAISS
       |
       v
Similar Documents
       |
       v
     Gemini
       |
       v
     Answer

При реальной разработке перед ним добавляется конвейер ввода данных:

Documents
   |
   v
Load
   |
   v
Split into chunks
   |
   v
Generate embeddings
   |
   v
Store vectors
   |
   v
Retrieve relevant chunks
   |
   v
Generate answer

В примере опущены этапы загрузки и разбиения на части, чтобы сосредоточиться на рабочем процессе; в реальных руководствах они оба необходимы.

Tavily: поиск в интернете в режиме реального времени

Tavily обрабатывает информацию, которая меняется со временем:

User Question
      |
      v
   Router
      |
      v
    Tavily
      |
      v
 Search Results
      |
      v
    Gemini
      |
      v
    Answer

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

Шаблон, который стоит запомнить

Компоненты могут быть взаимозаменяемыми. Основная идея заключается в маршрутизации: соответствовании каждому запросу наиболее подходящей способности для его обработки.

Internal Knowledge
        |
        +------ FAISS
Current Information
        |
        +------ Tavily
General Knowledge
        |
        +------ Gemini

Этот выбор способности для каждого запроса встречается практически во всех серьезных агентских приложениях.

Как двигаться дальше

Добавить больше инструментов

Маршрутизатор может выбирать среди гораздо большего количества бэкендов:

SQL Database
REST APIs
CRM
Email
Calendar
Internal Documentation

Усилить процесс поиска

Три документа представляют собой лишь пример, а не базу знаний. Настоящая система RAG требует механизмов загрузки документов, их разбиения на части, метаданных, более эффективных стратегий поиска, переранжирования результатов, указания источников и механизмов контроля доступа, чтобы пользователи получали только ту информацию, которая им разрешена.

Проверка решений о маршрутизации

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

Router
   |
   v
Validator
   |
   +---- valid ----> Tool
   |
   +---- invalid --> Fallback

Это становится всё более важным по мере увеличения количества инструментов.

Обработка сбоев

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

Router
   |
   v
Tavily
   |
   X
Search failed
   |
   v
Fallback

В производственных системах необходимы попытки повтора и резервные варианты, такие как прямой ответ с четким предупреждением. Статья о создании устойчивых графов агентов с механизмами повторных попыток и резервов подробнее рассматривает эту тему.

Сделайте процесс прозрачным

Когда ответ неверен, необходимо знать, на каком этапе произошла ошибка:

Wrong route?
      |
      v
Bad retrieval?
      |
      v
Bad search results?
      |
      v
Bad generation?

Логирование выбранного исходного данных и промежуточных результатов позволяет ответить на этот вопрос.

Введите циклы

Текущий граф принимает решение только один раз:

Question
   |
   v
Router
   |
   v
Tool
   |
   v
Answer

Более способный агент анализирует полученную информацию и решает, стоит ли действовать снова:

Question
   |
   v
Reason
   |
   v
Tool
   |
   v
Evaluate Result
   |
   +---- Need more information?
   |            |
   |            v
   |          Tool
   |            |
   +------------+
   |
   v
Final Answer

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

Основные выводы

Вся система отвечает на один вопрос: как приложению с ИИ следует решать, откуда брать ответ? Готовый рабочий процесс выглядит следующим образом:

User Question
                   |
                   v
                Gemini
                Router
                   |
        +----------+----------+
        |          |          |
        v          v          v
      FAISS      Tavily    Gemini
   Internal DB    Web      Direct
        |          |          |
        +----------+----------+
                   |
                   v
                Gemini
              Final Answer
  • Определите возможности и границы в графе; позвольте модели выбирать среди них во время выполнения.
  • Используйте структурированный вывод для принятия решений о маршрутизации и убедитесь, что узел принятия решений действительно вызывает структурированную модель.
  • Используйте один узел генерации ответов и меняйте только контекст, который вы ему передаете.
  • Рассматривайте маршрутизацию как компонент, подлежащий тестированию: записывайте решения и проверяйте их на примерах типичных вопросов.
  • Добавьте проверку данных, резервные варианты и механизмы отслеживания до внедрения новых инструментов, поскольку каждый новый инструмент увеличивает количество способов сбоя запроса.
  • На этой основе тот же принцип естественным образом распространяется на SQL-базы данных, API, оперативную память, утверждение человеком, узлы оценки, повторные попытки и многокомпонентные схемы. Граф расширяется, но принцип остается неизменным: предоставьте модели полезные возможности, определите способы их использования и позвольте ей самой выбрать наиболее подходящий вариант для выполнения задачи.

    Связанные материалы