Course Content
CrewAI Multi-Agents
9 sections · 53 lessons
How would you combine CrewAI with LangGraph or RAG systems?
What you need to know
Pattern 1: a crew inside a LangGraph node
1from typing import TypedDict2from langgraph.graph import StateGraph, START, END3from langgraph.checkpoint.memory import InMemorySaver4from langgraph.types import interrupt56class State(TypedDict):7 claim_id: str8 draft: dict9 approved: bool1011def draft_node(state: State) -> dict:12 out = ClaimsCrew().crew().kickoff(inputs={"claim_id": state["claim_id"]})13 return {"draft": out.pydantic.model_dump()}1415def approve_node(state: State) -> dict:16 answer = interrupt({"draft": state["draft"]}) # pause for a person17 return {"approved": answer == "approve"}1819g = StateGraph(State)20g.add_node("draft", draft_node)21g.add_node("approve", approve_node)22g.add_edge(START, "draft")23g.add_edge("draft", "approve")24g.add_edge("approve", END)25app = g.compile(checkpointer=InMemorySaver()) # Postgres in productionLangGraph saves state after each node, so if the approval takes two days or the server restarts, the crew does not run again. The crew returns a Pydantic model, and the node stores it as a plain dict in the graph state.
If you prefer one framework, a CrewAI Flow with @persist and @human_feedback plays the same outer role.
Pattern 2: RAG as a tool
CrewAI's knowledge_sources are quick to set up, but you get less control over how documents are split, filtered and ranked. For serious retrieval, write a tool:
1from crewai.tools import BaseTool2from pydantic import BaseModel, Field34class PolicySearchInput(BaseModel):5 query: str = Field(description="What to find in the policy wording")6 policy_id: str78class PolicySearchTool(BaseTool):9 name: str = "policy_search"10 description: str = "Search one customer's policy wording. Returns clauses with IDs."11 args_schema: type[BaseModel] = PolicySearchInput1213 def _run(self, query: str, policy_id: str) -> str:14 hits = retriever.search(query, filter={"policy_id": policy_id}, k=5)15 return "\n".join(f"[{h.clause_id}] {h.text[:400]}" for h in hits)Then give it only to the policy agent, add sources: list[str] to that task's output_pydantic, and use a guardrail that checks every cited clause ID was actually returned by the tool.
knowledge_sources
- Set up in a few lines
- CrewAI chooses chunking and embedding
- Good for small, stable reference text
Retrieval as a tool
- You own chunking, filters and reranking
- Retrieval can be tested on its own
- Good for large, changing or per-customer data
A real-life example
An insurer's settlement system is a LangGraph graph: collect documents, run the claims-review crew, wait for manager approval above ₹2 lakh, then pay out. The crew's policy agent uses a policy_search tool over 40,000 policy documents in a vector store, filtered by policy_id so it never reads another customer's policy.
Before the tool had the filter, the agent sometimes cited a clause from a different product's policy. The citation guardrail caught 3% of drafts with a clause ID that did not come from the tool. After adding the filter and a reranker, that fell to 0.2%. Because retrieval is a separate tool, the team measures it on its own (the right clause in the top 5 for 96% of test questions) without running the crew at all.
Follow-up questions to expect
- "Why not run LangGraph inside CrewAI instead?" — You can wrap a graph as a tool, but the outer layer should own durability and approvals, so graph-outside is more common.
- "When are
knowledge_sourcesenough?" — Small, stable reference material, like a 20-page style guide or product FAQ, where default chunking works. - "How do you stop a crew from running twice after a graph resume?" — The checkpointer saves the node's output, so a completed node is not rerun; keep the crew's tools idempotent anyway.