Главная / Статьи

LlamaIndex Plus LangGraph: чистая модель для агентов RAG

Как создать базу знаний с помощью LlamaIndex и сделать её доступной агенту LangGraph в качестве инструмента, с объяснением функций сохранения данных, использования Chroma и режима работы только с поисковиком.

Издатель: REACTAPP.TOP14 мин чтения3211 слов

Источники: RAG Without the Guesswork: A Standardized LangGraph + LlamaIndex Pattern.

Слепая зона и разделение труда

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

Retrieval-Augmented Generation (RAG) устраняет этот недостаток. При получении запроса система извлекает соответствующие фрагменты из ваших собственных документов и передает их LLM в качестве контекста перед тем, как тот даст ответ, так что модель читает информацию, а не догадывается. RAG — это функция, добавляемая к отдельному агенту. Это не новый паттерн оркестрации, поэтому консультанты, подграфы и команда Send не участвуют в этом процессе. Достаточно лишь иметь базовое представление о структуре агента LangGraph и о том, как предоставить ему инструменты.

В LangGraph возможно вручную создавать всю необходимую инфраструктуру: нужно написать собственный инструмент для разбиения данных на части, код для генерации эмбеддингов, а также код для индексации и поиска векторов. Многие команды так и делают, хотя это требует значительных усилий для решения проблемы, которая уже решена. LlamaIndex был создан для того, чтобы взять на себя всю эту работу. Он, безусловно, является наиболее зрелым и популярным решением для обработки данных в AI-приложениях — он обеспечивает загрузку данных, их разбиение на части, генерацию эмбеддингов, индексацию и поиск. Что касается организации процессов, таких как управление состоянием, контроль потока выполнения и многократные логические операции, то это задачи LangGraph.

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

Ментальная модель LlamaIndex

Представьте LlamaIndex как библиотекаря для вашего большого языкового моделя. Большой языковой модель похож на талантливого исследовательского ассистента, который прочитал огромное количество материалов из Интернета, но никогда не открывал файлы вашей компании, и никто не может попросить его запомнить том в 10 000 страниц. Поэтому на сцену выходит библиотекарь: он читает каждый документ, разбивает его на небольшие куски, подлежащие поиску (индексные карточки), хранит эти карточки там, где поиск по теме происходит быстро (векторный индекс), и получает только 3–5 наиболее релевантных карточек каждый раз, когда ассистент задаёт вопрос. Ассистент ничего не запоминает — приходят лишь несколько фрагментов, и рассуждения проводятся на их основе.

Пятиэтапная процедура

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

1. LOAD       → Read raw files (PDF, Word, web pages, databases) into Documents
2. CHUNK      → Split Documents into small, retrievable Nodes
3. EMBED      → Convert each Node's text into a vector (a list of numbers
                representing meaning)
4. STORE      → Save those vectors in a Vector Index for fast lookup
5. RETRIEVE   → At query time, embed the user's question, find the most
                  similar Nodes, and return them as context

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

Лексикон

  • Документ — это необработанный фрагмент исходного контента, такой как один PDF, одна веб-страница или одна строка базы данных вместе с её метаданными. Это непроцессированная, полноразмерная единица.
  • Узел — это фрагмент, вырезанный из Документа. Модели больших языков и модели эмбеддингов работают лучше с сосредоточенными, небольшими по объёму текстами, чем с целыми документами, поэтому LlamaIndex разделяет каждый Документ на множество объектов Узел. Узлы передают метаданные своему родителю, что позволяет отследить происхождение любого фрагмента.
  • SimpleDirectoryReader — это стандартный загрузчик: указав ему папку, он читает PDF-файлы, текстовые документы, файлы Word и другие форматы, создавая по каждому файлу один или несколько объектов Document.
  • VectorStoreIndex — основной класс для индексации. Получив объекты Document или Node, он встраивает их и формирует поисковый векторный индекс либо в памяти, либо во внешней векторной базе данных, если таковая настроена.
  • Settings — это объект глобальной конфигурации. На нем один раз задаются параметры LLM и модели встраивания, и все компоненты берут их из этого объекта, поэтому их не нужно передавать при каждом вызове.
  • QueryEngine — это тот компонент, которому вы фактически задаёте вопросы. Он формируется с помощью метода .as_query_engine() у индекса, и один вызов включает запрос, выполняет поиск по сходству и передаёт полученный контекст в LLM для получения окончательного ответа.
  • Retriever формируется с помощью метода .as_retriever() у индекса и обрабатывает только этап поиска. Он возвращает сырые объекты Node, соответствующие запросу, и не генерирует ответа, что подходит для случаев, когда поиск и генерация должны быть отдельными, контролируемыми шагами. Именно такая ситуация возникает при использовании LangGraph.
  • Сначала создаётся сама цепочка обработки, без участия LangGraph.

    Рабочая цепочка обработки примерно из десяти строк

    Сначала происходит установка.

    # Core package + OpenAI LLM and embedding integrations (the common starting setup)
    pip install llama-index-core llama-index-llms-openai llama-index-embeddings-openai
    
    # Readers for common file types (PDF, Word, etc.)
    pip install llama-index-readers-file pypdf
    

    LlamaIndex преднамеренно поставляется в виде множества небольших пакетов. llama-index-core содержит логику фреймворка, в то время как выбор языка больших моделей, модели эмбеддингов, векторной базы данных и читателя файлов расположен в отдельных пакетах, которые устанавливаются только при необходимости. Это позволяет сделать установку более легкой.

    Небольшой файл конфигурации задаёт глобальные настройки по умолчанию.

    # ── llamaindex_config.py ─────────────────────────────────────
    import os
    from llama_index.core import Settings
    from llama_index.llms.openai import OpenAI
    from llama_index.embeddings.openai import OpenAIEmbedding
    
    # Settings is global - configure once, used everywhere in LlamaIndex
    Settings.llm = OpenAI(
        model="gpt-4o-mini",       # Used for generating final answers from retrieved context
        temperature=0.1,            # Low temperature: factual, not creative
    )
    Settings.embed_model = OpenAIEmbedding(
        model="text-embedding-3-small",   # Used to convert text into vectors
    )
    # Controls how documents are split into Nodes (chunks)
    Settings.chunk_size = 512        # Max tokens per chunk
    Settings.chunk_overlap = 50      # Overlap between consecutive chunks, to preserve context across boundaries
    

    Что касается chunk_size: слишком большие куски содержат столько текста, что извлечение информации становится нечетким, поскольку полученный кусок может быть релевантен лишь на 10%. Слишком маленькие куски удаляют окружающий контекст, необходимый большой языковой модели для понимания факта. Распространенным и разумным выбором является начальное значение в 512 токенов с перекрытием 50 токенов, после чего вносятся корректировки в зависимости от документов.

    Затем сама база знаний:

    # ── build_knowledge_base.py ──────────────────────────────────
    from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
    
    # ── LOAD: Read all files in a folder into Document objects ──
    documents = SimpleDirectoryReader("./data").load_data()
    print(f"Loaded {len(documents)} documents")
    # ── CHUNK + EMBED + STORE: all three happen inside this one call ──
    # VectorStoreIndex automatically:
    #   1. Splits each Document into Nodes (using Settings.chunk_size)
    #   2. Embeds each Node (using Settings.embed_model)
    #   3. Stores the vectors in an in-memory index
    index = VectorStoreIndex.from_documents(documents, show_progress=True)
    # ── RETRIEVE + GENERATE: ask a question ──────────────────────
    query_engine = index.as_query_engine(
        similarity_top_k=3,   # Retrieve the 3 most relevant chunks for each query
    )
    response = query_engine.query("What is our refund policy for enterprise customers?")
    print(response)
    

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

    Хранение индекса

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

    # ── Save the index after building it ─────────────────────────
    index.storage_context.persist(persist_dir="./storage")
    
    # ── Load it back later without re-embedding anything ──────────
    from llama_index.core import StorageContext, load_index_from_storage
    storage_context = StorageContext.from_defaults(persist_dir="./storage")
    index = load_index_from_storage(storage_context)
    

    storage_context.persist() сохраняет векторные данные, метаданные узлов и хранилище документов в локальной папке, а load_index_from_storage() воссоздаёт идентичный индекс из этих файлов без повторной обработки данных и без вызовов API. Этот подход следует той же концепции однократной обработки, долгосрочного хранения и недорогой перезагрузки, что и у чекпоинтеров LangGraph.

    Настоящая векторная база данных

    Индекс в памяти подходит для прототипов. В производственных условиях обычно требуется специализированная векторная база данных, предназначенная для обработки миллионов векторов, одновременных операций чтения и фильтрации метаданных в крупном масштабе. Примером может служить Chroma.

    # ── Using Chroma as a persistent, production-grade vector store ──
    # pip install llama-index-vector-stores-chroma chromadb
    
    import chromadb
    from llama_index.vector_stores.chroma import ChromaVectorStore
    from llama_index.core import StorageContext, VectorStoreIndex
    chroma_client = chromadb.PersistentClient(path="./chroma_db")
    chroma_collection = chroma_client.get_or_create_collection("my_knowledge_base")
    vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
    storage_context = StorageContext.from_defaults(vector_store=vector_store)
    # Build the index directly into Chroma
    index = VectorStoreIndex.from_documents(
        documents,
        storage_context=storage_context
    )
    # Later, in a different process, reconnect without re-indexing:
    index = VectorStoreIndex.from_vector_store(vector_store=vector_store)
    

    Этот механизм замены похож на функции контрольных точек. Заменяется лишь один объект (хранилище в памяти для ChromaVectorStore, так же как MemorySaver сменяется на SqliteSaver), в то время как движок запросов и логика поиска остаются прежними. Pinecone, Qdrant, Weaviate и большинство других векторных баз данных, поддерживаемых LlamaIndex, работают аналогичным образом.

    Связь двух фреймворков

    Вот суть проблемы. Фреймворки используют разные языки: QueryEngine от LlamaIndex не является инструментом LangGraph, поскольку агент LangGraph ожидает инструменты, созданные с использованием декоратора @tool от LangChain.

    Механизм интеграции прост. Необходимо определить обычную функцию на Python, добавить к ней аннотацию @tool, и в её теле вызвать query_engine.query(...). Это и всё, что требуется для интеграции. LangGraph не должен ничего знать о внутреннем устройстве LlamaIndex, поскольку он видит лишь инструмент, принимающий строку и возвращающий строку, как и любой другой инструмент.

    # ── MODULE 3: TOOLS (LlamaIndex-backed) ─────────────────────
    from langchain_core.tools import tool
    
    # The query_engine built in Part B - created once, at startup
    # (In a real app, you'd load this from persisted storage, not rebuild it every time)
    @tool
    def search_knowledge_base(query: str) -> str:
        """Search the internal knowledge base for company policies, product
        documentation, and internal procedures. Use this whenever the user asks
        a question that might be answered by internal company documents rather
        than general knowledge.
    
        Args:
            query: A natural-language question to search for.
    
        Returns:
            A synthesized answer based on the most relevant retrieved documents.
        """
        response = query_engine.query(query)
        return str(response)
    

    Документация имеет такое же значение, как и для любого инструмента. ЯЗЫК ИИ агента читает её, чтобы решить, когда вызвать этот инструмент, вместо того чтобы отвечать на основе собственных знаний или вызывать другой инструмент.

    Полный агент

    Вот полная шаблонная структура агента с инструментом RAG, основанным на LlamaIndex, включённым в модуль инструментов. Больше ничего не меняется.

    # ============================================================
    # LANGGRAPH + LLAMAINDEX RAG AGENT — COMPLETE TEMPLATE
    # Extends: Part 1 (core structure)
    # ============================================================
    
    # ── MODULE 1: IMPORTS & CONFIGURATION ───────────────────────
    import os
    from typing import Literal
    # LangChain / LangGraph imports (the orchestration layer)
    from langchain_openai import ChatOpenAI
    from langchain_core.messages import HumanMessage, SystemMessage, BaseMessage
    from langchain_core.tools import tool
    from langgraph.graph import StateGraph, MessagesState, START, END
    from langgraph.prebuilt import ToolNode
    from langgraph.checkpoint.memory import MemorySaver
    # LlamaIndex imports (the retrieval / data layer)
    from llama_index.core import (1
        Settings, SimpleDirectoryReader, VectorStoreIndex,
        StorageContext, load_index_from_storage
    )
    from llama_index.llms.openai import OpenAI as LlamaOpenAI
    from llama_index.embeddings.openai import OpenAIEmbedding
    # LangGraph's chat model - used by the agent's reasoning
    llm = ChatOpenAI(model="gpt-4o", temperature=0)
    # LlamaIndex's model config - used internally by the query engine
    # Note: these are SEPARATE from the LangGraph llm above. Each framework
    # manages its own model instances; they don't share state.
    Settings.llm = LlamaOpenAI(model="gpt-4o-mini", temperature=0.1)
    Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
    Settings.chunk_size = 512
    
    # ── MODULE 2: STATE ──────────────────────────────────────────
    class State(MessagesState):
        pass  # messages field inherited; extend if your agent needs more
    
    # ── MODULE 3: TOOLS (RAG-backed) ─────────────────────────────
    # Build or load the LlamaIndex knowledge base ONCE, at startup
    PERSIST_DIR = "./storage"
    if os.path.exists(PERSIST_DIR):
        # Reload existing index - no re-embedding, fast startup
        storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR)
        index = load_index_from_storage(storage_context)
    else:
        # First run - build the index and persist it
        documents = SimpleDirectoryReader("./data").load_data()
        index = VectorStoreIndex.from_documents(documents, show_progress=True)
        index.storage_context.persist(persist_dir=PERSIST_DIR)
    query_engine = index.as_query_engine(similarity_top_k=3)
    
    @tool
    def search_knowledge_base(query: str) -> str:
        """Search internal company documents for policies, product specs,
        procedures, and other domain-specific information. Use this for any
        question that requires knowledge specific to this organization rather
        than general world knowledge."""
        response = query_engine.query(query)
        return str(response)
    
    tools = [search_knowledge_base]
    llm_with_tools = llm.bind_tools(tools)
    tool_node = ToolNode(tools)
    
    # ── MODULE 4: NODES ──────────────────────────────────────────
    def agent_node(state: State) -> dict:
        """The reasoning node. Decides whether to answer directly or
        search the knowledge base first."""
        system_prompt = SystemMessage(content=(
            "You are a helpful assistant with access to an internal knowledge base. "
            "Use the search_knowledge_base tool when the user asks about company-specific "
            "information. For general questions, answer directly."
        ))
        messages = [system_prompt] + state["messages"]
        response = llm_with_tools.invoke(messages)
        return {"messages": [response]}
    
    # ── MODULE 5: ROUTING ────────────────────────────────────────
    def should_continue(state: State) -> Literal["tools", "__end__"]:
        last_message = state["messages"][-1]
        if hasattr(last_message, "tool_calls") and last_message.tool_calls:
            return "tools"
        return "__end__"
    
    # ── MODULE 6: GRAPH ASSEMBLY ─────────────────────────────────
    graph_builder = StateGraph(State)
    graph_builder.add_node("agent", agent_node)
    graph_builder.add_node("tools", tool_node)
    graph_builder.add_edge(START, "agent")
    graph_builder.add_conditional_edges(
        "agent", should_continue,
        {"tools": "tools", "__end__": END}
    )
    graph_builder.add_edge("tools", "agent")
    graph = graph_builder.compile(checkpointer=MemorySaver())
    
    # ── MODULE 7: ENTRYPOINT ──────────────────────────────────────
    if __name__ == "__main__":
        config = {"configurable": {"thread_id": "session-001"}}
    
        print("RAG agent ready. Ask about your documents, or anything else.\n")
    
        while True:
            user_text = input("You: ").strip()
            if not user_text or user_text.lower() == "exit":
                break
    
            response = graph.invoke(
                {"messages": [HumanMessage(content=user_text)]},
                config=config
            )
            print(f"Agent: {response['messages'][-1].content}\n")
    

    Что происходит при одном запросе

    Прослеживание одного запроса помогает ясно увидеть разделение обязанностей.

    User: "What's our policy on remote work?"
            ↓
    [agent_node] — LangGraph's LLM reads the message, recognizes this needs
                   internal info, decides to call search_knowledge_base
            ↓
    [tools] — ToolNode executes search_knowledge_base("What's our policy on remote work?")
            ↓
            Inside the tool: query_engine.query(...) runs —
            this is 100% LlamaIndex, invisible to LangGraph:
              1. Embeds the query
              2. Searches the vector index for the 3 closest chunks
              3. Feeds those chunks + the question to Settings.llm
              4. Returns a synthesized answer string
            ↓
    [agent_node] — LangGraph's LLM receives the tool's string result,
                   and crafts the final response shown to the user
            ↓
    Response to user
    

    Границы взаимодействия четко определены. LangGraph никогда не сталкивается с элементами типа Node, эмбеддингами или векторами — он использует только инструменты, возвращающие текст, что не отличается от функций search_web или calculate. LlamaIndex также не имеет дела с понятиями messages, state или thread_id; здесь просто поступает запрос, а затем выводится ответ. Каждая платформа использует свои сильные стороны, причем соединение между ними осуществляется через один вызов функции.

    Режим только получателя данных для большего контроля

    Схема query_engine.query() представляет собой самый простой способ интеграции и подходит для большинства случаев. Однако иногда требуется больший контроль, например, когда собственный большой языковой модуль агента LangGraph должен составить окончательный ответ, используя существующий системный промпт и контекст разговора, а не внутренние настройки LlamaIndex типа Settings.llm, которые генерируют отдельный, несвязанный с контекстом ответ.

    В таком случае используйте Retriever вместо QueryEngine. Он выполняет только шаг поиска и возвращает сырые фрагменты данных, а дальнейшие действия зависят от агента.

    # ── Retriever-only tool: returns raw chunks, not a synthesized answer ──
    retriever = index.as_retriever(similarity_top_k=3)
    @tool
    def retrieve_documents(query: str) -> str:
        """Retrieve relevant document excerpts from the internal knowledge base.
        Returns raw excerpts for you to read and reason over yourself -
        use this when you need to cite specific sources or combine information
        from multiple documents."""
    
        nodes = retriever.retrieve(query)
    
        # Format each retrieved chunk with its source for transparency
        formatted_chunks = []
        for i, node in enumerate(nodes):
            source = node.metadata.get("file_name", "unknown source")
            formatted_chunks.append(f"[Excerpt {i+1} from {source}]\n{node.text}")
    
        return "\n\n---\n\n".join(formatted_chunks)
    

    Благодаря такой конфигурации собственный вызов LLM у agent_node, использующий llm_with_tools вместе с полной историей разговора и системным промптом, считывает необработанные отрывки и формирует ответ. Это приносит реальные преимущества: агент может указать, из какого документа взята информация, он может смешивать полученные отрывки с предыдущими сообщениями, а пользователи слышат единый голос, а не два LLM с разными стилями.

    Какой вариант выбрать

    Выбирайте QueryEngine для самой простой интеграции, когда достаточно, чтобы LLM от LlamaIndex генерировал ответ на основе поиска в виде самодостаточной строки. Это подходит для большинства чат-ботов и помощников.

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

    Справочник ключевых слов

    Загрузка и индексация. Document — это загруженный источник, один файл или одна страница. Node — это его фрагмент, единица, доступная для извлечения, с метаданными, указывающими на его источник. Метод SimpleDirectoryReader("./path").load_data() загружает все поддерживаемые файлы в папке в виде объектов Document. Settings — это глобальный объект конфигурации типа singleton, а параметры Settings.llm, Settings.embed_model и Settings.chunk_size устанавливаются один раз для всех компонентов. Метод VectorStoreIndex.from_documents(documents) выполняет разбиение на фрагменты, встраивание информации и хранение за один вызов.

    Запросы. Метод index.as_query_engine(similarity_top_k=3) возвращает объект QueryEngine, который загружает k лучших фрагментов и также формирует синтезированный ответ. Метод index.as_retriever(similarity_top_k=3) возвращает объект Retriever, который загружает только k лучших фрагментов, что подходит для сценариев, где синтез выполняется собственным LLM. Вызов query_engine.query("...") запускает полный процесс сначала получения данных, затем их обработки, и возвращает объект Response, а операция str(response) извлекает текст. Вызов retriever.retrieve("...") выполняет только операцию поиска и возвращает объекты Node, содержащие поля .text и .metadata.

    Надежность хранения. Функция index.storage_context.persist(persist_dir="./storage") сохраняет индекс, что позволяет избежать его повторной генерации при перезапуске. С помощью StorageContext.from_defaults(persist_dir="./storage") в сочетании с load_index_from_storage(...) происходит его повторная загрузка. ChromaVectorStore, PineconeVectorStore и QdrantVectorStore — это внешние бэкенды, заменяющие стандартный внутренний хранилище, и функционируют по тому же принципу, что и механизмы сохранения состояния в LangGraph.

    Мост. Вся интеграция заключается в функции @tool, тело которой вызывает query_engine.query(...): LangGraph воспринимает её как обычный инструмент, а LlamaIndex остаётся закрытым модулем. Также существуют два отдельных конфигурации больших языковых моделей. Settings.llm — это внутренняя модель LlamaIndex, используемая только внутри движка запросов, в то время как llm в LangGraph агента отвечает за основные операции вывода выводов. Они работают независимо и не делятся ни историей, ни состоянием.

    Вам это нужно?

    • Если агенту требуется только общие знания, математика или поиск в интернете, LlamaIndex не нужен, достаточно базовой шаблона с стандартными инструментами.
    • Если необходимо отвечать на вопросы на основе фиксированного набора документов, таких как политики, руководства или отчёты, которые редко меняются, правильным решением будет использование QueryEngine в качестве инструмента.
  • Если необходимо цитировать источники или сочетать полученные факты с логическими рассуждениями в ходе многократных диалогов, используйте шаблон Retriever.
  • Если база знаний велика (100 000 и более документов), обновления происходят часто или необходимо обслуживать множество одновременных пользователей, вместо индекса в памяти используйте внешнюю векторную базу данных, такую как Chroma, Pinecone или Qdrant.
  • Если сам процесс поиска должен быть подлежащим проверке и возможным к приостановке, например, когда требования к соответствию законодательству предусматривают человеческое утверждение того, что ищется, соедините его с функцией interrupt() от LangGraph и разместите вызов поиска в узле, ожидающем утверждения, как и в случае с любым чувствительным инструментом.
  • Два фреймворка и одна связь

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

    LlamaIndex отвечает за индексацию и поиск, а LangGraph — за рассуждения и координацию действий. Вся связь между ними осуществляется с помощью функции, оформленной с использованием тега @tool. Это не ограничение, а оптимальный уровень взаимосвязи, поскольку каждая система работает в своей сфере, и их можно обновлять независимо друг от друга. Векторную базу данных можно заменить, не затрагивая граф, а паттерн контроллера можно добавить, не меняя логику поиска.

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

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