Como melhorar os aplicativos LLM com a recuperação de documentos principais usando MongoDB e LangChain
Avalie esse Tutorial
Chunking, no contexto de aplicativos LLM, é o processo de dividir grandes pedaços de texto em segmentos ou blocos menores. O chunking é um componente importante de qualquer aplicação LLM que envolva a recuperação de dados de uma base de conhecimento, pois ele afeta a qualidade de tudo a jusante, da incorporação à recuperação e à própria geração.
No entanto, a principal preocupação com o chunking é que você fatalmente perde o contexto na tentativa de manter os chunks direcionados e focados para manter a qualidade da incorporação. Isso pode prejudicar a qualidade da geração, pois as informações necessárias para responder a uma pergunta específica podem se distribuir por vários blocos.
É aqui que uma técnica chamada recuperação de documento pai pode ajudar. Neste tutorial, vamos ver como essa técnica ajuda a reter os benefícios do chunking sem afetar a qualidade da geração. Especificamente, abordaremos o seguinte:
- O que é recuperação de documento pai e quando você deve usá-la?
- Como funciona a recuperação de documento pai no MongoDB
- Implementação da recuperação de documento pai usando a integração LangChain do MongoDB
- Usando a recuperação de documento pai em geração aumentada de recuperação (RAG) e fluxos de trabalho de agente
Ao dividir documentos para aplicativos LLM, geralmente há considerações conflitantes:
- Os chunks devem ser pequenos o suficiente para que as incorporações possam capturar com precisão seu significado, resultando em boa qualidade de recuperação.
- Os chunks devem ser grandes o suficiente para não espalhar o contexto em vários chunks, resultando em uma boa qualidade de geração.
Isso é difícil de conseguir usando estratégias simples que envolvem a definição de um único tamanho de chunk predefinido, por exemplo, token fixo com sobreposição ou recursivo com sobreposição. A recuperação do documento principal tem como objetivo encontrar um equilíbrio entre os dois requisitos,incorporando e armazenando pequenos blocos, mas identificando e buscando o documento de origem ou blocos maiores no momento da recuperação.
A principal vantagem desta técnica é que ela fornece um contexto mais completo ao LLM, resultando em respostas mais contextualizadas. Alguns casos de uso em que a expansão de contexto pode ser útil são os seguintes:
- Preparação de caso legal: Expandir uma resposta sobre uma cláusula de rescisão com informações sobre a resolução de conflitos e a lei aplicável a partir do mesmo documento.
- chatbots de documentação: respondendo a uma pergunta sobre autenticação de API com informações sobre expiração de token e mecanismos de atualização.
- Pesquisa científica: uma query sobre "resultados do experimento A" se expande para incluir métodos, hipoteses e limitações.
Neste tutorial, usaremos a integração LangChain do MongoDB que fornece uma API simples para a recuperação de documento pai, mas vamos ver o que acontece nos detalhes.
No momento da ingestão, os documentos são divisão em pequenos blocos, incorporados e armazenados em uma collection do MongoDB . Cada documento fragmentado tem um ID pai, que é um identificador exclusivo para o documento pai do qual o bloco veio. Os documentos pai também são armazenados na mesma coleção, com o
_id
campo correspondendo ao ID pai contido nos blocos de documento correspondentes. Uma representação visual deste processo é a seguinte:
No momento da recuperação, a query do usuário é incorporada e os blocos relevantes são recuperados usando pesquisa
$lookup
semântica. Uma operação no MongoDB, semelhante a uma junção externa esquerda, é executada para obter os documentos pai dos chunks recuperados da mesma collection. Os próprios blocos e quaisquer documentos pai duplicados são então descartados, e documentos pai exclusivos são passados para o LLM como contexto para responder à query do usuário. Tudo isso é alcançado usando a avançada framework de agregação do MongoDB. Uma representação visual do processo de recuperação e geração é a seguinte:
Neste tutorial, implementaremos a recuperação de documento pai usando a integração LangChain do MongoDB e veremos como usá-la em um aplicação RAG e um agente de IA . O Notebook Jupyter para este tutorial pode ser encontrado no Github em nosso repositório GenAI Showcase.
Vamos precisar das seguintes bibliotecas para este tutorial:
- conjuntos de dados: pacote Python para baixar conjuntos de dados do Abraçando a Face
- PyMongo: Driver Python para MongoDB
- langchain: pacote Python para os módulos principais do LangChain
- langgraph: pacote Python para orquestrar fluxos de trabalho LLM como gráficos
- langchain-mongodb: pacote Python para usar recursos MongoDB no LangChain
- langchain-openai: pacote Python para usar modelos OpenAI via LangChain
1 ! pip install -qU datasets pymongo langchain langgraph langchain-mongodb langchain-openai
Usaremos o OpenAI como fornecedor do modelo de incorporação e conclusão de chat. Para usar seus modelos, você precisa obter uma chave de API OpenAI e defini-la como uma variável de ambiente:
1 os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API Key:")
Usaremos o MongoDB para recuperação de documento pai. Mas, primeiro, você precisará de uma conta do MongoDB Atlas com um cluster de banco de dados de dados. Depois de fazer isso, você precisará obter a string de conexão para se conectar ao cluster. Siga estas etapas para configurar:
- Obtenha a stringde conexão para seu cluster de banco de dados.
Depois de obter a string de conexão , defina-a em seu código, instancie o cliente MongoDB e verifique se você consegue se conectar ao seu banco de dados de dados usando o
ping
comando.1 MONGODB_URI = getpass.getpass("Enter your MongoDB connection string:") 2 mongodb_client = MongoClient( 3 MONGODB_URI, appname="devrel.showcase.parent_doc_retrieval" 4 ) 5 mongodb_client.admin.command("ping")
Usaremos um snapshot da documentação oficial do MongoDB como conjunto de dados para nosso tutorial. Este conjunto de dados está disponível em Abraçando a Face. Para baixar este conjunto de dados, você precisará solicitar acesso a ele e criar um token de acesso do usuário. Siga as etapas aqui para configurar:
- Solicite acesso ao conjunto de dados. As solicitações são aprovadas automaticamente, então você deve ter acesso ao conjunto de dados instantaneamente.
Depois de obter o token de acesso, defina-o como uma variável de ambiente:
1 os.environ["HF_TOKEN"] = getpass.getpass("Enter your HF Access Token:")
Primeiro, vamos baixar o conjunto de dados MongoDB Docs do Abraçando o Face.
1 from datasets import load_dataset 2 import pandas as pd 3 4 data = load_dataset("mongodb-eai/docs", streaming=True, split="train") 5 data_head = data.take(1000) 6 df = pd.DataFrame(data_head)
Faremos o download do conjunto de dados no modo de streaming para baixar apenas um subconjunto do conjunto de dados em vez de baixar o conjunto de dados inteiro para o disco.
A maneira mais fácil de usar seus dados com funcionalidades do LangChain é convertendo-os em objetos de documento do LangChain (vamos nos referir a eles como " "documents neste tutorial). Esses objetos consistem em dois atributos — a saber,
page_content
e metadata
. page_content
, como o nome sugere, corresponde ao conteúdo do documento, e metadata
são informações básicas sobre os documentos que você pode personalizar ou serão extraídos automaticamente pelo LangChain.1 from langchain_core.documents import Document 2 3 docs = [] 4 metadata_fields = ["updated", "url", "title"] 5 for _, row in df.iterrows(): 6 content = row["body"] 7 metadata = row["metadata"] 8 for field in metadata_fields: 9 metadata[field] = row[field] 10 docs.append(Document(page_content=content, metadata=metadata))
No código acima, iteramos pelas linhas de nosso conjunto de dados Docs e criamos um documento LangChain por linha. De cada linha, extraímos o
body
campo como page_content
do documento. Também extraímos metadata
e alguns outros campos, como url
, title
, etc. como o metadata
atributo do documento.Um exemplo de um objeto de documento LangChain é o seguinte:
1 Document(page_content='# View Database Access History\n\n- This feature is not available for `M0` free clusters, `M2`, and `M5` clusters. To learn more, see Atlas M0 (Free Cluster), M2, and M5 Limits', metadata={'contentType': None, 'pageDescription': None, 'productName': 'MongoDB Atlas', 'tags': ['atlas', 'docs'], 'version': None, 'updated': {'$date': '2024-05-20T17:30:49.148Z'}, 'url': 'https://mongodb.com/pt-br/docs/atlas/access-tracking/', 'title': 'View Database Access History'})
Sempre que um recuperador de documento pai do MongoDB é instanciado usando o
from_connection_string
método, ele cria automaticamente uma instância MongoDBAtlasVectorSearch
do MongoDBDocStore
armazenamento de vetores e do armazenamento de documento. Quando os documentos são adicionados ao recuperador, o armazenamento de vetores do MongoDB Atlas os divide em blocos (documentos filhos), gera incorporações para os blocos e os ingere em uma coleção do MongoDB . O armazenamento de documento MongoDB ingere os documentos pai na mesma coleção.O MongoDB Atlas é uma plataforma unificada para dados vetoriais e operacionais. Isso permite que a mesma collection atue como vetor e armazenamento de documento . Na maioria dos outros casos, você precisaria usar uma plataforma como armazenamento de vetores e outra como armazenamento de documento .
Então, vamos primeiro especificar o modelo de incorporação, o banco de dados de dados e a collection nos quais fazer a ingestão de documentos, e definir uma função assistente para o agrupamento de documentos.
1 from langchain_mongodb.retrievers import ( 2 MongoDBAtlasParentDocumentRetriever, 3 ) 4 from langchain_text_splitters import RecursiveCharacterTextSplitter 5 from langchain_openai import OpenAIEmbeddings 6 7 embedding_model = OpenAIEmbeddings(model="text-embedding-3-small") 8 9 DB_NAME = "langchain" 10 COLLECTION_NAME = "parent_doc" 11 12 def get_splitter(chunk_size: int) -> RecursiveCharacterTextSplitter: 13 """ 14 Returns a token-based text splitter with overlap 15 16 Args: 17 chunk_size (_type_): Chunk size in number of tokens 18 19 Returns: 20 RecursiveCharacterTextSplitter: Recursive text splitter object 21 """ 22 return RecursiveCharacterTextSplitter.from_tiktoken_encoder( 23 encoding_name="cl100k_base", 24 chunk_size=chunk_size, 25 chunk_overlap=0.15 * chunk_size, 26 )
O código acima:
- Inicializa o modelo de incorporação. Estamos usando o do OpenAI.
text-embedding-3-small
- Especifica o banco de dados de dados (
DB_NAME
) e a collection (COLLECTION_NAME
) para consumir dados. - Define uma função chamada
get_splitter
para dividir documentos. A função usa umchunk_size
como argumento e retorna um objeto daRecursiveCharacterTextSplitter
classe. Usamos ofrom_tiktoken_encoder
método da classe, o que significa que os textos serão primeiro divisão por uma lista de caracteres e, em seguida, mesclados em tokens até que o especificadochunk_size
seja atingido. Também especificamos umchunk_overlap
correspondente a 15% doschunk_size
.
Agora, vamos instanciar o recuperador de documento pai do MongoDB :
1 parent_doc_retriever = MongoDBAtlasParentDocumentRetriever.from_connection_string( 2 connection_string=MONGODB_URI, 3 embedding_model=embedding_model, 4 child_splitter=get_splitter(200), 5 database_name=DB_NAME, 6 collection_name=COLLECTION_NAME, 7 text_key="page_content", 8 search_kwargs={"k": 10}, 9 )
O código acima utiliza o
from_connection_string
método com os seguintes argumentos para criar uma instância MongoDBParentDocumentRetriever
de:- connection_string: string de conexão para seu cluster MongoDB Atlas .
- embedding_model: modelo de incorporação para o armazenamento de vetores. Isso foi inicializado anteriormente.
- client_splitter: usa a
get_splitter
função para criar um divisor de texto para o agrupamento de documentos de acordo com o tamanho especificado do bloco, neste caso, 200 tokens. - database_name: o banco de banco de dados MongoDB para ingestão de documentos pai e filho.
- collection_name MongoDB collection na qual consumir documentos pai e filho.
- text_key: o campo nos documentos em partes que contém o texto bruto. Em nossos documentos, é
page_content
. - search_kwards: Argumentos adicionais para a pesquisa. Definiremos
k
10 como para recuperar os 10 principais blocos mais relevantes durante a execução da pesquisa semântica antes da recuperação do documento principal. - kvargs: Quaisquer argumentos adicionais para o recuperador de documento pai.
Você também pode passar
parent_splitter
como um argumento adicional para o from_connection_string
método. A ideia aqui é primeiro divisão os documentos brutos em grandes blocos e, em seguida, divisão -los em blocos menores. No momento da recuperação, em vez dos documentos pai completos, os blocos pai maiores são recuperados. Você pode instanciar um recuperador de chunks principal da seguinte maneira:1 parent_chunk_retriever = MongoDBAtlasParentDocumentRetriever.from_connection_string( 2 connection_string=MONGODB_URI, 3 embedding_model=embedding_model, 4 child_splitter=get_splitter(200), 5 parent_splitter=get_splitter(800), 6 database_name=DB_NAME, 7 collection_name=COLLECTION_NAME, 8 text_key="page_content", 9 search_kwargs={"k": 10}, 10 )
No exemplo acima, o recuperador criará blocos pai de tokens de tamanho 800 e blocos filhos de tokens de tamanho 200.
Usaremos o
parent_doc_retriever
para o resto do tutorial.Agora, vamos inserir documentos no MongoDB usando o recuperador. Iremos ingestão de documentos de forma assíncrona no MongoDB—isso é especialmente útil ao trabalhar com grandes conjuntos de dados, pois você pode processar simultaneamente vários chunks de dados, acelerando assim a ingestão de dados.
Vamos definir algumas funções assistente para a ingestão de dados.
1 import asyncio 2 from typing import Generator, List 3 4 BATCH_SIZE = 256 5 MAX_CONCURRENCY = 4 6 7 async def process_batch(batch: Generator, semaphore: asyncio.Semaphore) -> None: 8 """ 9 Ingest batches of documents into MongoDB 10 11 Args: 12 batch (Generator): Chunk of documents to ingest 13 semaphore (as): Asyncio semaphore 14 """ 15 async with semaphore: 16 await parent_doc_retriever.aadd_documents(batch) 17 print(f"Processed {len(batch)} documents")
O código acima:
- Define um tamanho de lote (
BATCH_SIZE
) que especifica o número de documentos a serem processados em uma única tarefa e um limite de simultaneidade (MAX_CONCURRENCY
) que indica o número máximo de tarefas que podem ser executadas simultaneamente. - Define uma função chamada
process_chunk
que executa um lote de documentos por meio doparent_doc_retriever
usando oaadd_documents
método. Como mencionado anteriormente, o chunk,parent_doc_retriever
incorporado e ingerido automaticamente os documentos por meio de seus armazenamentos de vetores e documento .
Em seguida, vamos definir uma função que cria os lotes de documento , em que cada lote consiste em
BATCH_SIZE
número de documentos:1 def get_batches(docs: List[Document], batch_size: int) -> Generator: 2 """ 3 Return batches of documents to ingest into MongoDB 4 5 Args: 6 docs (List[Document]): List of LangChain documents 7 batch_size (int): Batch size 8 9 Yields: 10 Generator: Batch of documents 11 """ 12 for i in range(0, len(docs), batch_size): 13 yield docs[i : i + batch_size]
Finalmente, vamos definir a função principal que orquestra a ingestão de dados:
1 async def process_docs(docs: List[Document]) -> List[None]: 2 """ 3 Asynchronously ingest LangChain documents into MongoDB 4 5 Args: 6 docs (List[Document]): List of LangChain documents 7 8 Returns: 9 List[None]: Results of the task executions 10 """ 11 semaphore = asyncio.Semaphore(MAX_CONCURRENCY) 12 batches = get_batches(docs, BATCH_SIZE) 13 14 tasks = [] 15 for batch in batches: 16 tasks.append(process_batch(batch, semaphore)) 17 # Gather results from all tasks 18 results = await asyncio.gather(*tasks) 19 return results
O código acima:
- Divide a lista de documentos para ingestão (
docs
) em lotes utilizando aget_batches
função definida anteriormente. - Cria uma tarefa para cada lote usando a
process_batch
função anterior, impondo o limite de simultaneidade usando um semáforo. - Usa
asyncio.gather
para executar tarefas simultaneamente e coletar seus resultados. Em nosso caso, as tarefas não retornam nada - elas apenas ingerem documentos no MongoDB.
Agora, vamos usar a
process_docs
função acima para inserir os documentos LangChain da etapa 4 em uma coleção MongoDB :1 collection = mongodb_client[DB_NAME][COLLECTION_NAME] 2 # Delete any existing documents from the collection 3 collection.delete_many({}) 4 print(f"Deletion complete.") 5 # Ingest LangChain documents into MongoDB 6 results = await process_docs(docs)
O código acima:
- Exclui quaisquer documentos existentes da coleção MongoDB para a qual queremos documentos ingeridos.
- Ingere os documentos de forma assíncrona (
docs
) no MongoDB usando aprocess_docs
função definida anteriormente.
Mesmo na recuperação do documento principal, a primeira etapa é recuperar os blocos filhos mais relevantes para a query do usuário usando a pesquisa semântica/vetorial. Para realizar a pesquisa vetorial no MongoDB Atlas, primeiro você precisa criar um índice de pesquisa vetorial:
1 from pymongo.operations import SearchIndexModel 2 from pymongo.errors import OperationFailure 3 4 VS_INDEX_NAME = "vector_index" 5 6 # Vector search index definition 7 model = SearchIndexModel( 8 definition={ 9 "fields": [ 10 { 11 "type": "vector", 12 "path": "embedding", 13 "numDimensions": 1536, 14 "similarity": "cosine", 15 } 16 ] 17 }, 18 name=VS_INDEX_NAME, 19 type="vectorSearch", 20 ) 21 22 # Check if the index already exists, if not create it 23 try: 24 collection.create_search_index(model=model) 25 print( 26 f"Successfully created index {VS_INDEX_NAME} for collection {COLLECTION_NAME}" 27 ) 28 except OperationFailure: 29 print( 30 f"Duplicate index {VS_INDEX_NAME} found for collection {COLLECTION_NAME}. Skipping index creation." 31 )
O código acima:
- Especifica o nome do índice de pesquisa vetorial (
VS_INDEX_NAME
). - Cria a definição do índice de pesquisa vetorial que contém o caminho para o campo de incorporações nos documentos (
path
), o número de dimensões de incorporação (numDimensions
) e a métrica de similaridade para encontrar os vizinhos mais próximos (similarity
). - Verifica se
VS_INDEX_NAME
existe um índice de pesquisa vetorial com o nome naCOLLECTION_NAME
collection. Caso contrário, somente então ele cria o índice de pesquisa vetorial.
Para reunir tudo isso, vamos ver como usar a recuperação de documento pai em fluxos de trabalho RAG e agentes.
1 from langchain_openai import ChatOpenAI 2 from langchain_core.prompts import ChatPromptTemplate 3 from langchain_core.runnables import RunnablePassthrough 4 from langchain_core.output_parsers import StrOutputParser 5 6 # Retrieve and parse documents 7 retrieve = { 8 "context": parent_doc_retriever 9 | (lambda docs: "\n\n".join([d.page_content for d in docs])), 10 "question": RunnablePassthrough(), 11 } 12 template = """Answer the question based only on the following context. If no context is provided, respond with I DON'T KNOW: \ 13 {context} 14 15 Question: {question} 16 """ 17 # Define the chat prompt 18 prompt = ChatPromptTemplate.from_template(template) 19 # Define the model to be used for chat completion 20 llm = ChatOpenAI(temperature=0, model="gpt-4o-2024-11-20") 21 # Parse output as a string 22 parse_output = StrOutputParser() 23 # Naive RAG chain 24 rag_chain = retrieve | prompt | llm | parse_output
O código acima cria um fluxo de trabalho RAG com recuperação de documento pai em LangChain. Em um alto nível, ele faz o seguinte:
- Reúne contexto para responder a perguntas usando o que criamos
parent_doc_retriever
na etapa 5 - Cria um modelo de prompt (
prompt
) com um prompt do sistema e espaços reservados para o contexto e a pergunta do usuário - Inicializa a conclusão do chat LLM (
llm
) a ser usada para gerar respostas - Cria um analisador de saída simples (
parse_output
) para analisar a saída LLM como uma string - Encadeia todos os componentes acima usando
|
a notação de pipe ( ) do LangChain para criar um fluxo de trabalho RAG simples (rag_chain
)
Um exemplo de resposta da cadeia RAG é o seguinte:
1 print(rag_chain.invoke("How do I improve slow queries in MongoDB?")) 2 3 To improve slow queries in MongoDB, you can follow these steps: 4 5 1. **Use the Performance Advisor**: 6 - The Performance Advisor monitors slow queries and suggests new indexes to improve query performance. 7 - Review the suggested indexes, especially those with high Impact scores and low Average Query Targeting scores, and create them if they align with your indexing strategies. 8 9 2. **Analyze Query Performance**: 10 - Use the **Query Profiler** to explore slow-running operations and their key performance statistics for up to the last 24 hours. 11 - Use the **Real-Time Performance Panel (RTPP)** to evaluate query execution times and the ratio of documents scanned to documents returned. 12 13 3. **Monitor Query Latency**: 14 - Use **Namespace Insights** to monitor collection-level query latency and view query latency metrics and statistics. 15 16 4. **Fix Inefficient Queries**: 17 - Address `Query Targeting` alerts by adding indexes to support inefficient queries. 18 - Use the `cursor.explain()` command to analyze query plans and identify inefficiencies. 19 20 5. **Follow Best Practices**: 21 - Create queries that are supported by existing indexes. 22 - Avoid large array fields in documents that are costly to search and index. 23 - Optimize and remove unused or inefficient indexes to balance read and write performance. 24 - Perform rolling index builds to minimize performance impact on replica sets and sharded clusters. 25 26 6. **Configure Slow Query Threshold**: 27 - Adjust the slow query threshold to identify slow queries more effectively. By default, Atlas dynamically adjusts this threshold, but you can set a fixed threshold of 100 milliseconds if needed. 28 ... 29 - Ensure queries are supported by indexes. 30 - Optimize queries involving `$lookup` or large array fields. 31 32 By implementing these steps, you can identify and resolve slow queries, improving overall query performance in MongoDB.
Observe a resposta muito detalhada, que não apenas inclui etapas para corrigir queries lentas, mas também maneiras de analisar e monitorar o desempenho da query e as práticas recomendadas para escrever queries MongoDB .
No contexto dos agentes de IA, você pode fornecer o recuperador de documento pai como uma das ferramentas que um agente pode usar. Vamos ver como criar um agente básico de chamada de ferramentas usando o LangGraph, uma estrutura do LangChain que permite orquestrar aplicativos LLM como gráficos.
Primeiro, vamos converter o
parent_doc_retriever
em uma ferramenta de agente . Em LangChain, criar ferramentas é tão simples quanto usar o @tool
planejador em uma função Python:1 from langchain.agents import tool 2 from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder 3 from typing import Annotated, Dict 4 from langgraph.graph.message import add_messages 5 from typing_extensions import TypedDict 6 from langgraph.prebuilt import ToolNode, tools_condition 7 from langgraph.graph import StateGraph, START, END 8 9 # Converting the retriever into an agent tool 10 11 def get_info_about_mongodb(user_query: str) -> str: 12 """ 13 Retrieve information about MongoDB. 14 15 Args: 16 user_query (str): The user's query string. 17 18 Returns: 19 str: The retrieved information formatted as a string. 20 """ 21 docs = parent_doc_retriever.invoke(user_query) 22 context = "\n\n".join([d.page_content for d in docs]) 23 return context 24 25 tools = [get_info_about_mongodb]
Em seguida, vamos definir o prompt para o agente e conceder a ele acesso à(s) ferramenta(s) definida(s) acima:
1 # Define the LLM to use as the brain of the agent 2 llm = ChatOpenAI(temperature=0, model="gpt-4o-2024-11-20") 3 # Agent prompt 4 prompt = ChatPromptTemplate.from_messages( 5 [ 6 ( 7 "You are a helpful AI assistant." 8 " You are provided with tools to answer questions about MongoDB." 9 " Think step-by-step and use these tools to get the information required to answer the user query." 10 " Do not re-run tools unless absolutely necessary." 11 " If you are not able to get enough information using the tools, reply with I DON'T KNOW." 12 " You have access to the following tools: {tool_names}." 13 ), 14 MessagesPlaceholder(variable_name="messages"), 15 ] 16 ) 17 # Partial the prompt with tool names 18 prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools])) 19 # Bind tools to LLM 20 llm_with_tools = prompt | llm.bind_tools(tools)
O código acima:
- Instancia o LLM (
llm
) que queremos usar como obrain " " do nosso agente. - Define o prompt (
prompt
) para o agente, com espaços reservados para os nomes das ferramentas e mensagens do usuário. - Vincula o LLM à(s) ferramenta(s) definida(s) anteriormente.
Agora, vamos orquestrar o agente usando o LangGraph. O LangGraph permite construir sistemas LLM como gráficos. Os nós do grafo são funções ou ferramentas para executar tarefas específicas, enquanto as bordas definem rotas entre os nós — eles podem ser fixos, condicionais ou até mesmo cíclicos. Cada grafo tem um estado que é uma estrutura de dados compartilhada que todos os nós podem acessar e fazer atualizações. Vamos em frente e definir o estado, os nós e as bordas do grafo do nosso agente:
1 # Define graph state 2 class GraphState(TypedDict): 3 messages: Annotated[list, add_messages] 4 5 def agent(state: GraphState) -> Dict[str, List]: 6 """ 7 Agent node 8 9 Args: 10 state (GraphState): Graph state 11 12 Returns: 13 Dict[str, List]: Updates to the graph state 14 """ 15 messages = state["messages"] 16 response = llm_with_tools.invoke(messages) 17 # We return a list, because this will get added to the existing list 18 return {"messages": [response]} 19 20 # Convert tools into a graph node 21 tool_node = ToolNode(tools) 22 23 # Parameterize the graph with the state 24 graph = StateGraph(GraphState) 25 # Add graph nodes 26 graph.add_node("agent", agent) 27 graph.add_node("tools", tool_node) 28 # Add graph edges 29 graph.add_edge(START, "agent") 30 graph.add_edge("tools", "agent") 31 graph.add_conditional_edges( 32 "agent", 33 tools_condition, 34 {"tools": "tools", END: END}, 35 ) 36 # Compile the graph 37 app = graph.compile() 38 39 # Execute the agent and view outputs 40 inputs = { 41 "messages": [ 42 ("user", "How do I improve slow queries in MongoDB?"), 43 ] 44 } 45 46 for output in app.stream(inputs): 47 for key, value in output.items(): 48 print(f"Node {key}:") 49 print(value) 50 print("---FINAL ANSWER---") 51 print(value["messages"][-1].content)
O código acima:
- Define o estado do grafo (
GraphState
). Em nosso gráfico, queremos apenas rastrear as entradas do usuário e as respostas LLM (messages
) no estado, mas você pode rastrear outros atributos personalizados. - Define o nó do agente , que é essencialmente uma função do Python (
agent
). Essa função lê as mensagens existentes do estado do gráfico, faz uma chamada para o LLM e anexa a resposta de volta ao estado do gráfico. - Converte a(s) ferramenta(s) definida(s) anteriormente em um nó usando a
ToolNode
classe. - Inicializa o gráfico (
StateGraph
), parametrizado pelo estado do gráfico. - Adiciona os nós e bordas ao gráfico. Observe a borda condicional que usa a função pré-criada do LangGraph
tools_condition
para rotear para o ToolNode se a última mensagem tiver chamadas de ferramentas. Caso contrário, ele roteia para oEND
nó . - Compila o gráfico utilizando o
compile()
método. - Executa o gráfico no modo streaming usando uma entrada de teste.
Neste tutorial, aprenderam sobre a recuperação de documento principais e como ela pode ajudar a superar as limitações do agrupamento no momento da geração, mantendo seus benefícios para a incorporação. Também destacamos alguns casos de uso em que essa técnica é particularmente útil. Por fim, vimos como a recuperação de documento principais funciona no MongoDB e a implementamos em fluxos de trabalho RAG e Agentic usando a integração LangChain do MongoDB.
Agora que você tem um bom entendimento dessa técnica, confira os tutoriais a seguir para explorar diferentes estratégias de agrupamento com a recuperação de documento principais ou avalie essa técnica de recuperação em relação a outras:
Como sempre, se você tiver mais perguntas ao criar seus aplicativos de IA, entre em contato conosco em nossos fóruns da comunidade de IA generativa.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.