Build a Custom RAG Agent
Status: ACTIVE (pulled from docs.langchain.com) Source: https://docs.langchain.com/oss/python/langgraph/agentic-rag Timestamp: 2026-05-11
Build a retrieval-augmented generation agent that can decide when to use retriever tools.
Steps
1. Preprocess Documents
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
urls = [
"https://lilianweng.github.io/posts/2024-11-28-reward-hacking/",
"https://lilianweng.github.io/posts/2024-07-07-hallucination/",
]
docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=100, chunk_overlap=50
)
doc_splits = text_splitter.split_documents(docs_list)
2. Create Retriever Tool
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_openai import OpenAIEmbeddings
from langchain.tools import tool
vectorstore = InMemoryVectorStore.from_documents(
documents=doc_splits, embedding=OpenAIEmbeddings()
)
retriever = vectorstore.as_retriever()
@tool
def retrieve_blog_posts(query: str) -> str:
"""Search and return information about blog posts."""
docs = retriever.invoke(query)
return "\n\n".join([doc.page_content for doc in docs])
3. Generate Query Node
def generate_query_or_respond(state: MessagesState):
response = (
model.bind_tools([retrieve_blog_posts]).invoke(state["messages"])
)
return {"messages": [response]}
4. Grade Documents (Relevance Check)
class GradeDocuments(BaseModel):
binary_score: str = Field(description="'yes' if relevant, 'no' if not")
def grade_documents(state: MessagesState) -> Literal["generate_answer", "rewrite_question"]:
question = state["messages"][0].content
context = state["messages"][-1].content
prompt = GRADE_PROMPT.format(question=question, context=context)
response = grader_model.with_structured_output(GradeDocuments).invoke([{"role": "user", "content": prompt}])
if response.binary_score == "yes":
return "generate_answer"
return "rewrite_question"
5. Rewrite Question (Query Transformation)
def rewrite_question(state: MessagesState):
question = state["messages"][0].content
prompt = REWRITE_PROMPT.format(question=question)
response = model.invoke([{"role": "user", "content": prompt}])
return {"messages": [HumanMessage(content=response.content)]}
6. Generate Answer
def generate_answer(state: MessagesState):
question = state["messages"][0].content
context = state["messages"][-1].content
prompt = GENERATE_PROMPT.format(question=question, context=context)
response = model.invoke([{"role": "user", "content": prompt}])
return {"messages": [response]}
7. Assemble Graph
from langgraph.prebuilt import ToolNode
workflow = StateGraph(MessagesState)
workflow.add_node(generate_query_or_respond)
workflow.add_node("retrieve", ToolNode([retrieve_blog_posts]))
workflow.add_node(rewrite_question)
workflow.add_node(generate_answer)
workflow.add_edge(START, "generate_query_or_respond")
def route_on_tool_calls(state: MessagesState):
if getattr(state["messages"][-1], "tool_calls", None):
return "tools"
return END
workflow.add_conditional_edges("generate_query_or_respond", route_on_tool_calls, {
"tools": "retrieve", END: END,
})
workflow.add_conditional_edges("retrieve", grade_documents)
workflow.add_edge("generate_answer", END)
workflow.add_edge("rewrite_question", "generate_query_or_respond")
graph = workflow.compile()