LlamaIndex Plus LangGraph: Czysty wzorzec dla agentów RAG
Jak stworzyć bazę wiedzy za pomocą LlamaIndex i udostępnić ją agentowi LangGraph jako narzędzie, z wyjaśnieniem funkcji trwałości danych, Chromy oraz trybu wyłącznie odtwarzania informacji.
Źródła: RAG Without the Guesswork: A Standardized LangGraph + LlamaIndex Pattern.
Punkt ślepy i podział pracy
Agent LangGraph zna tylko to, co jego model LLM poznał podczas szkolenia. Pytania dotyczące wewnętrznej polityki cenowej, raportu finansowego z poprzedniego kwartału lub podręcznika ukończonego w zeszłym tygodniu spotykają się albo z przyznaniem się do niewiedzy, albo, co gorsza, z pewnym siebie zmyśleniem.
Retrieval-Augmented Generation (RAG) zamienia tę lukę. Podczas wyszukiwania informacji pobiera istotne fragmenty z twoich własnych dokumentów i przekazuje je modelowi LLM jako kontekst przed udzieleniem odpowiedzi, dzięki czemu model czyta zamiast zgadywać. RAG to funkcjonalność dodawana do pojedynczego agenta. Nie jest to nowy wzorzec orkiestracji, więc nadzorcy, podgrafy oraz funkcja Send nie odgrywają w tym procesie żadnej roli. Wystarczy jedynie pewna znajomość struktury agenta LangGraph oraz sposobu dostarczania mu narzędzi.
Możliwe jest ręczne budowanie łańcucha przetwarzania w LangGraph: należy napisać własny narzędzie do dzielenia tekstu na fragmenty, funkcje do generowania wektorów reprezentacji, indeks wektorowy oraz kod do wyszukiwania. Wiele zespołów tak robi, choć to sporo pracy przy problemie, który jest już rozwiązany. LlamaIndex został stworzony po to, by przejąć tę pracę. Jest zdecydowanie najbardziej rozwiniętą i popularną opcją dla obszaru danych w aplikacjach AI, obejmującą ładowanie danych, dzielenie ich na fragmenty, generowanie wektorów reprezentacji, tworzenie indeksów oraz wyszukiwanie. Zadania związane z orkiestracją, takie jak zarządzanie stanem, przepływ kontrolny i rozumowanie wieloetapowe, należą do LangGraph.
Z tego podziału wynika struktura, którą stosują większość systemów RAG w produkcji: LlamaIndex tworzy i udostępnia bazę wiedzy, natomiast LangGraph traktuje ją jako narzędzie i decyduje, kiedy go wykorzystać.
Model mentalny LlamaIndex
Traktuj LlamaIndex jako bibliotekarza dla twojego LLM. LLM przypomina wybitnego asystenta badawczego, który przeczytał dużą część Internetu, ale nigdy nie otworzył plików twojej firmy, a nikt nie może kazać mu zapamiętać 10 000-stronicowego segregatora. Dlatego interweniuje bibliotekarz: czyta każdy dokument, rozbija go na małe, wyszukiwalne karty indeksowe (kawałki), przechowuje te karty w miejscu, gdzie szybko można znaleźć dane tematyczne (indeks wektorowy) oraz przywołuje tylko 3 do 5 najbardziej istotnych kart, gdy asystent o coś pyta. Asystent nigdy niczego nie zapamiętuje. Przychodzą kilka fragmentów, a rozumowanie odbywa się na ich podstawie.
Proces w pięciu etapach
Każdy system RAG, niezależnie od użytego frameworku, przechodzi przez te same pięć etapów, a LlamaIndex oferuje przejrzystą abstrakcję dla każdego z nich.
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
Pierwsze cztery etapy odbywają się raz, podczas tworzenia bazy wiedzy, i nazywane są indeksowaniem. Piąty etap występuje za każdym razem, gdy użytkownik zadaje pytanie, i nazywa się go szukaniem. Związek pomiędzy czasem indeksowania a czasem wyszukiwania jest prawdopodobnie najważniejszym modelem myślowym w RAG i warto go pamiętać.
Słownictwo
Documentto surowy fragment treści źródłowej, taki jak jeden plik PDF, jedna strona internetowa lub jedna wiersz w bazie danych wraz z jej metadanymi. Jest to nieprzetworzona, pełnowymiarowa jednostka.Nodeto fragment wycięty zDocument. Modele LLM i modele embedding działają lepiej na skoncentrowanych, krótkich fragmentach tekstu niż na całych dokumentach, dlatego LlamaIndex dzieli każdyDocumentna wiele obiektówNode. Node’y przekazują metadane z powrotem do swojego rodzica, dzięki czemu każdy fragment można prześledzić do jego źródła.SimpleDirectoryReaderto standardowy loader: wystarczy wskazać mu folder, a on odczyta pliki PDF, tekstowe, pliki Word oraz inne formaty, tworząc po jednym lub kilku obiektachDocumentna każdy plik.VectorStoreIndexto główna klasa do tworzenia indeksów. Na podstawie obiektówDocumentlubNodewłącza je do struktury i tworzy indeks wektorowy nadający się do wyszukiwania, albo w pamięci, albo w zewnętrznej bazie danych wektorowych, jeśli taka jest skonfigurowana.Settingsto globalny obiekt konfiguracji. Model LLM oraz model embedding są na nim ustawiane raz, a każdy komponent korzysta z tych ustawień, więc nie muszą być przekazywane przy każdym wywołaniu.QueryEngineto element, do którego faktycznie kierujemy pytania. Powstaje poprzez metodę.as_query_engine()na indeksie, a jedna wywołanie zawiera zapytanie, przeprowadza wyszukiwanie podobieństw i dostarcza uzyskany kontekst do LLM w celu uzyskania ostatecznej odpowiedzi.Retrieverpowstaje poprzez metodę.as_retriever()na indeksie i zajmuje się wyłącznie krokiem wyszukiwania. Zwraca surowe, pasujące obiektyNodei nie generuje żadnej odpowiedzi, co nadaje się do przypadków, gdy pobieranie danych i generowanie odpowiedzi powinny być oddzielnymi, kontrolowalnymi krokami. To dokładnie sytuacja, która występuje po włączeniu LangGraph.
Proces jest najpierw budowany samodzielnie, bez użycia LangGraph.
Pracujący pipeline w około dziesięciu liniach
Najpierw następuje instalacja.
# 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 dostarczany jest celowo w postaci wielu małych pakietów. llama-index-core zawiera logikę frameworku, natomiast wybór modelu LLM, modelu embeddingu, bazy danych wektorowej oraz odczytującego pliki narzędzia znajduje się w osobnym pakiecie, który instaluje się tylko w razie potrzeby. Dzięki temu instalacje pozostają lekkie.
Niewielki plik konfiguracyjny ustala globalne ustawienia domyślne.
# ── 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
Co do chunk_size: zbyt duże kawałki zawierają tak dużo tekstu, że jego pobieranie staje się nieprecyzyjne, ponieważ pobrany kawałek może być istotny jedynie w 10%. Zbyt małe kawałki pomijają otaczający kontekst, który model językowy potrzebuje, aby zrozumieć dany fakt. Powszechną i rozsądną wartością jest start od 512 tokenów przy nakładaniu się o 50 tokenów, a następnie dostosowanie tego parametru do charakteru dokumentów.
Następnie sama baza wiedzy:
# ── 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)
Około dziesięć linijek kodu wystarcza, aby stworzyć działający system RAG – to swego rodzaju „hello world” tego frameworku oraz dobrzy przykład tego, dlaczego istnieje. Wykonanie tego ręcznie oznaczałoby konieczność stworzenia specjalnego narzędzia do dzielenia tekstu, pętli wywołującej model embeddingowy, funkcji wyszukiwania najbliższych sąsiadów oraz szablonu promptu.
Zachowywanie indeksu
Podczas tworzenia indeksu każdy fragment tekstu przechodzi przez model embeddingowy, co wiąże się z kosztami finansowymi i czasowymi; dlatego ponowne tworzenie indeksu przy każdym uruchomieniu aplikacji jest marnotrawstwem zasobów. Zapisanie indeksu na dysku i jego późniejsze załadowanie pozwala uniknąć tego problemu.
# ── 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() przechowuje dane wektorowe, metadane węzłów oraz bazę dokumentów w lokalnej folderze, natomiast load_index_from_storage() odtwarza identyczny indeks z tych plików bez ponownego embedowania i bez żadnych wywołań API. Opiera się na tej samej zasadzie – budowanie raz, przechowywanie długoterminowe, tanie ładowanie – co checkpointer w LangGraph.
Prawdziwa baza danych wektorowych
Indeks w pamięci operacyjnej nadaje się do prototypów. W warunkach produkcyjnych zwykle potrzebna jest dedykowana baza danych wektorowych zaprojektowana do przechowywania milionów wektorów, jednoczesnych odczytów oraz filtrowania metadanych na dużą skalę. Przykładem może być 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)
Ta metoda przypomina mechanizmy checkpointingu. Wymienia się pojedynczy obiekt (przechowywanie w pamięci dla ChromaVectorStore, podobnie jak MemorySaver ustępuje miejsca SqliteSaver), a silnik zapytań oraz logika wyszukiwania pozostają bez zmian. Pinecone, Qdrant, Weaviate oraz większość innych baz danych wektorowych obsługiwanych przez LlamaIndex działają w ten sam sposób.
Łączenie dwóch frameworków
Oto sedno sprawy. Frameworky używają różnych języków: QueryEngine z LlamaIndex nie jest narzędziem LangGraph, ponieważ agent LangGraph oczekuje narzędzi stworzonych przy użyciu dekoratora @tool z LangChain.
Most jest prosty. Należy zdefiniować zwykłą funkcję w Pythonie, dodać do niej tag @tool, a w jej ciele sprawić, by wywoływano query_engine.query(...). To jest cała integracja. LangGraph nie musi nic wiedzieć o wewnętrznej strukturze LlamaIndex, ponieważ widzi narzędzie, które przyjmuje ciąg znaków i zwraca ciąg znaków, tak jak każde inne narzędzie.
# ── 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)
Opis dokumentu ma takie samo znaczenie jak w przypadku każdego narzędzia. LLM agenta czyta go, aby zdecydować, kiedy użyć tego narzędzia, zamiast odpowiadać na podstawie własnej wiedzy lub wywoływać inne narzędzie.
Pełny agent
Oto pełny szablon agenta z narzędziem RAG opartym na LlamaIndex włączonym do modułu narzędzi. Nic innego się nie zmienia.
# ============================================================
# 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")
Co dzieje się przy jednej zapytaniu
Śledzenie pojedynczej prośby uwyraźnia podział zadań.
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
Granica między tymi narzędziami jest wyraźna. LangGraph nigdy nie ma do czynienia z Node, embeddingiem ani wektorem, lecz jedynie z narzędziem, które zwraca tekst – niczym nie różni się to od search_web czy calculate. LlamaIndex z kolei nie ma do czynienia z messages, state ani thread_id, lecz jedynie z ciągiem zapytań wpływającym do systemu oraz odpowiedzią wychodzącą z niego. Każde z tych narzędzi wykorzystuje swoje mocne strony, a połączenie między nimi stanowi pojedyncza wywołanie funkcji.
Tryb tylko Retriever dla większej kontroli
Wzorzec query_engine.query() to najprostsza forma integracji i pasuje do większości przypadków. Czasami przydaje się większa kontrola, na przykład gdy agent LangGraph powinien sam napisać ostateczną odpowiedź, korzystając z istniejącego promptu systemowego oraz kontekstu rozmowy, a nie z wewnętrznego elementu Settings.llm LlamaIndex, który generuje oddzielną, niepowiązaną odpowiedź.
W takim przypadku użyj Retriever zamiast QueryEngine. Wykonuje on jedynie krok wyszukiwania i zwraca surowe fragmenty, a to, co nastąpi dalej, zależy od agenta.
# ── 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)
W tym ustawieniu własne wywołanie LLM przez agent_node, wykorzystujące llm_with_tools wraz z pełną historią rozmowy i promptem systemowym, odczytuje surowe fragmenty i zapisuje odpowiedź. Daje to rzeczywiste korzyści: agent może określić, z jakiego dokumentu pochodzi fakt, może łączyć pobrane fragmenty z wcześniejszą rozmową, a użytkownicy słyszą spójny głos, a nie dwa LLM o różnych stylach.
Który wybrać
Wybierz QueryEngine w przypadku najprostszej integracji, gdy wystarczy, aby LLM z LlamaIndex generował odpowiedź opartą na wyszukiwaniu w postaci samodzielnej ciągu znaków. Pasuje to do większości chatbotów i asystentów.
Wybierz Retriever, gdy potrzebne są źródłowe cytaty, gdy główny model językowy agenta powinien zachować spójny ton i styl rozumowania zarówno w przypadku odzyskanych, jak i niodzyskanych odpowiedzi, lub gdy odzyskane fragmenty muszą zostać połączone z wynikami innych narzędzi przed sformułowaniem ostatecznej odpowiedzi.
Odnośnik do słów kluczowych
Ładowanie i indeksowanie. Document to załadowany źródło, jeden plik lub jedna strona. Node to jego fragment, jednostka dostępna do pobrania, z metadanymi wskazującymi na jej pochodzenie. SimpleDirectoryReader("./path").load_data() ładuje wszystkie obsługiwane pliki w folderze jako obiekty Document. Settings to globalny singleton konfiguracji, a Settings.llm, Settings.embed_model oraz Settings.chunk_size są ustawiane raz dla wszystkich komponentów. VectorStoreIndex.from_documents(documents) dzieli na fragmenty, wstawia dane i przechowuje je w jednej operacji.
Zapytanie. Funkcja index.as_query_engine(similarity_top_k=3) zwraca obiekt QueryEngine, który pobiera najlepsze k elementów oraz generuje syntetyczną odpowiedź. Funkcja index.as_retriever(similarity_top_k=3) zwraca obiekt Retriever, który pobiera jedynie te najlepsze elementy, co nadaje się do sytuacji, gdy syntezę wykonuje własny model językowy. Funkcja query_engine.query("...") uruchamia cały proces pobierania danych i ich przetwarzania, zwracając obiekt Response, natomiast str(response) wydobywa tekst z tej odpowiedzi. Funkcja retriever.retrieve("...") wykonuje wyłącznie czynność pobierania danych i zwraca obiekty Node, które zawierają pola .text oraz .metadata.
Trwałość. Funkcja index.storage_context.persist(persist_dir="./storage") zapisuje indeks, dzięki czemu po ponownym uruchomieniu nie trzeba go ponownie wgrywać. Funkcja StorageContext.from_defaults(persist_dir="./storage") w połączeniu z load_index_from_storage(...) ładuje go ponownie. ChromaVectorStore, PineconeVectorStore oraz QdrantVectorStore to zewnętrzne backendy, które zastępują domyślny magazyn w pamięci, w taki sam sposób jak mechanizmy checkpointingu w LangGraph.
Most. Cała integracja polega na funkcji @tool, której ciało wywołuje query_engine.query(...): LangGraph postrzega zwykły narzędzie, a LlamaIndex pozostaje zamknięty w swojej strukturze. Istnieją również dwie oddzielne konfiguracje LLM. Settings.llm to wewnętrzny model LlamaIndex, używany wyłącznie w silniku zapytań, natomiast llm w LangGraph agenta zajmuje się głównym rozumowaniem. Obie te części działają niezależnie i nie dzielą się żadną historią ani stanem.
Czy to jest dla ciebie konieczne?
- Jeśli agent potrzebuje jedynie wiedzy ogólnej, umiejętności matematycznych lub możliwości wyszukiwania w sieci w czasie rzeczywistym, LlamaIndex nie jest wymagany – wystarczy podstawowy szablon z standardowymi narzędziami.
- Jeśli agent musi odpowiadać na podstawie ustalonej grupy dokumentów, takich jak polityki, instrukcje lub raporty, które rzadko się zmieniają, użycie
QueryEnginejako narzędzia jest dokładnie tym, czego potrzeba.
Retriever.interrupt() z LangGraph i umieść wywołanie wyszukiwania w węźle, który czeka na zatwierdzenie, tak jak w przypadku każdego narzędzia wrażliwego.Dwa frameworki i jedna spójność
Podstawowa lekcja, jaką można wyciągnąć, jest taka, że zapewnienie agentowi wiedzy z rzeczywistego świata nie stanowi problemu LangGraph. To problem danymi, a odpowiednim narzędziem do jego rozwiązania jest framework do obsługi danych. Złożony system wielu agentów to w rzeczywistości kilka grafów pojedynczych agentów połączonych ze sobą, a dostęp do wiedzy opiera się na tym samym zasadzie modułowości.
LlamaIndex zajmuje się indeksowaniem i wyszukiwaniem, natomiast LangGraph odpowiada za rozumowanie i koordynację działań. Całość jest połączona za pomocą funkcji ozdobionej tagiem @tool. Nie jest to ograniczenie, lecz właściwy poziom powiązań, ponieważ każdy framework funkcjonuje w swojej sferze, a obie strony mogą być aktualizowane niezależnie. Baza danych wektorowych można zamienić bez ingerencji w graf, a wzorzec nadzorczy można dodać bez wpływu na logikę wyszukiwania.
Literatura pokrewna
- MCP dla agentów LangGraph, krok po kroku: serwery, mechanizmy transportu i most adapterów — Poznaj protokół Model Context Protocol, od ról hosta, klienta i serwera po serwer FastMCP, różnice między stdio a HTTP, adaptery langchain-mcp oraz typowe problemy w produkcji w LangGraph.
- Stwórz ShopBot: agenta obsługi klienta w LangGraph z pamięcią, narzędziami i możliwością zatwierdzania zwrotów — Buduj agenta obsługi klienta w LangGraph moduł po modułu: stan, narzędzia, węzły, routowanie, podsumowania oraz mechanizm przerwania czekający na zatwierdzenie zwrotu.