Skip to content
Wróć do bloga
Open Source

Ta sama aplikacja czatowa, 4 frameworki: Pydantic AI vs LangChain vs LangGraph vs CrewAI (porownanie kodu)

Vstorm · · 5 min czytania
Spis treści

Wszyscy maja opinie o frameworkach AI. Niewielu pokazuje kod.

Utrzymujemy full-stack-ai-agent-template — produkcyjny szablon dla aplikacji AI/LLM z FastAPI, Next.js i ponad 75 opcjami konfiguracji. Jedna z tych opcji to framework AI. Wybierasz sposrod Pydantic AI, LangChain, LangGraph lub CrewAI podczas konfiguracji, a szablon generuje dokladnie te sama aplikacje czatowa z tym samym API, schematem bazy danych, streamingiem WebSocket i frontendem. Zmienia sie tylko warstwa AI.

To dalo nam unikalna mozliwosc: kontrolowane porownanie. Ta sama funkcjonalnosc, te same testy, ten sam deployment — cztery implementacje.

Konfiguracja

Kazdy wygenerowany projekt ma te sama strukture:

  • FastAPI backend z endpointem WebSocket do streamingu
  • Next.js frontend z interfejsem czatu
  • PostgreSQL do przechowywania konwersacji
  • JWT uwierzytelnianie dla polaczen WebSocket
  • Jeden plik agenta w app/agents/ obslugujacy logike AI

Agent musi przyjac wiadomosc uzytkownika i historie konwersacji, obslugiwac wywolania narzedzi, zwrocic odpowiedz jako (output_text, tool_events, context) i obslugiwac streaming do dostarczania tokenow w czasie rzeczywistym.

Pydantic AI (~160 linii)

Najbardziej zwiezla implementacja. Pelne typy generyczne z Agent[Deps, str], typowane wstrzykiwanie zaleznosci przez RunContext[Deps] i natywne async.

from pydantic_ai import Agent, RunContext
from pydantic_ai.settings import ModelSettings
@dataclass
class Deps:
user_id: str | None = None
user_name: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
class AssistantAgent:
def _create_agent(self) -> Agent[Deps, str]:
model = OpenAIChatModel(
self.model_name,
provider=OpenAIProvider(api_key=settings.OPENAI_API_KEY),
)
agent = Agent[Deps, str](
model=model,
model_settings=ModelSettings(temperature=self.temperature),
system_prompt=self.system_prompt,
)
self._register_tools(agent)
return agent
def _register_tools(self, agent: Agent[Deps, str]) -> None:
@agent.tool
async def current_datetime(ctx: RunContext[Deps]) -> str:
"""Get the current date and time."""
return get_current_datetime()
async def run(self, user_input, history=None, deps=None):
result = await self.agent.run(
user_input, deps=agent_deps, message_history=model_history
)
return result.output, tool_events, agent_deps

Kluczowe cechy: Generyki Agent[Deps, str] — IDE zna typ wyjscia. RunContext[Deps] w narzedziach daje typowany dostep do zaleznosci. Narzedzia rejestrowane dekoratorem @agent.tool. Natywne async z agent.run() i agent.iter() do streamingu.

LangChain (~170 linii)

Podobny wzorzec wrappera z samodzielnym dekoratorem @tool i konwersja wiadomosci:

from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI
@tool
def current_datetime() -> str:
"""Get the current date and time."""
return get_current_datetime()
class LangChainAssistant:
def _create_agent(self):
model = ChatOpenAI(
model=self.model_name,
temperature=self.temperature,
api_key=settings.OPENAI_API_KEY,
)
return create_agent(model=model, tools=self._tools, system_prompt=self.system_prompt)
async def run(self, user_input, history=None, context=None):
messages = self._convert_history(history)
messages.append(HumanMessage(content=user_input))
result = self.agent.invoke({"messages": messages})
return output, tool_events, agent_context

Kluczowe cechy: Narzedzia jako funkcje na poziomie modulu z @tool. create_agent() buduje wstepnie skonfigurowany graf. Wymaga _convert_history() do translacji miedzy standardowymi dict a HumanMessage/AIMessage. Streaming przez agent.astream(stream_mode=["messages", "updates"]).

LangGraph (~280 linii)

Jawny graf stanow z wezlami i krawedziami warunkowymi — budowanie calej petli agenta recznie:

from langgraph.graph import END, START, StateGraph
from langgraph.checkpoint.memory import MemorySaver
class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
class LangGraphAssistant:
def _agent_node(self, state: AgentState):
model = self._create_model()
messages = [SystemMessage(content=self.system_prompt), *state["messages"]]
response = model.invoke(messages)
return {"messages": [response]}
def _tools_node(self, state: AgentState):
last_message = state["messages"][-1]
tool_results = []
for tool_call in last_message.tool_calls:
tool_fn = TOOLS_BY_NAME.get(tool_call["name"])
result = tool_fn.invoke(tool_call["args"])
tool_results.append(ToolMessage(content=str(result), tool_call_id=tool_call["id"]))
return {"messages": tool_results}
def _should_continue(self, state) -> Literal["tools", "__end__"]:
if state["messages"][-1].tool_calls:
return "tools"
return "__end__"
def _build_graph(self):
workflow = StateGraph(AgentState)
workflow.add_node("agent", self._agent_node)
workflow.add_node("tools", self._tools_node)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", self._should_continue)
workflow.add_edge("tools", "agent")
return workflow.compile(checkpointer=MemorySaver())

Kluczowe cechy: StateGraph z AgentState do jawnego zarzadzania stanem. Dwa wezly (agent, tools) polaczone krawedziami warunkowymi. _should_continue kieruje do narzedzi lub konca. MemorySaver do pamieci konwersacji. Okolo 75% wiecej kodu niz Pydantic AI, ale pelna kontrola nad kazdym krokiem.

CrewAI (~420 linii)

Fundamentalnie inny — wieloagentowe zespoly z rolami, celami i historiami:

from crewai import Agent, Crew, Process, Task
class CrewAIAssistant:
def _default_config(self):
return CrewConfig(
agents=[
AgentConfig(role="Research Analyst", goal="Gather and analyze info"),
AgentConfig(role="Content Writer", goal="Create clear responses"),
],
tasks=[
TaskConfig(description="Research query: {user_input}", agent_role="Research Analyst"),
TaskConfig(description="Write response", agent_role="Content Writer",
context_from=["Research Analyst"]),
],
)
def _build_crew(self):
return Crew(agents=[...], tasks=[...], process=Process.sequential)
async def run(self, user_input, history=None, context=None):
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, lambda: self.crew.kickoff(inputs=inputs))
return output, task_results, crew_context

Kluczowe cechy: Wieloagentowy domyslnie — Research Analyst + Content Writer pracujacy jako zespol. Agent(role=..., goal=..., backstory=...) do konfiguracji jezykiem naturalnym. Synchroniczny pod spodem — wymaga run_in_executor dla async. Event bus (crewai_event_bus) do streamingu przez watek w tle + kolejke. Ponad dwukrotnie wiecej kodu, ale orkiestracja wieloagentowa od razu.

Tabela porownawcza

MetrykaPydantic AILangChainLangGraphCrewAI
Linie kodu~160~170~280~420
Bezpieczenstwo typowPelne generykiTypedDictTypedDictModele Pydantic
Wsparcie asyncNatywneNatywneNatywneSync (executor)
Streamingagent.iter()astream()astream()Event bus + watek
Skladnia narzedzi@agent.tool@toolbind_tools()Konfiguracja
ArchitekturaPojedynczy agentAgent (abstrakcja)Jawny grafWieloagentowa zaloga
Najlepszy doTypowane agentySzybkie prototypyZlozone przeplywyZespoly agentow

Kiedy uzyc ktorego

Pydantic AI — typowane pojedyncze agenty, wsparcie IDE, ekosystem Pydantic.

LangChain — najwiekszy ekosystem integracji, szybkie prototypowanie, znajomosc w zespole.

LangGraph — zlozone wieloetapowe rozumowanie, warunkowe rozgalezianie, human-in-the-loop.

CrewAI — wieloagentowa wspolpraca, role-based persony, hierarchiczna delegacja zadan.

Wyprobuj wszystkie cztery

full-stack-ai-agent-template pozwala wygenerowac ten sam projekt z dowolnym z tych czterech frameworkow. To samo API, ten sam frontend, ta sama baza danych, te same testy, ten sam Docker.

Konfigurator webowy — wybierz framework w kroku 4, pobierz jako ZIP.

CLI: pip install fastapi-fullstack && fastapi-fullstack init

Udostępnij artykuł

Powiązane artykuły

Gotowy, żeby wdrożyć swoją aplikację AI?

Wybierz frameworki, wygeneruj projekt gotowy do produkcji i wdróż. 75+ opcji, jedna komenda, zero długu konfiguracyjnego.

Potrzebujesz pomocy przy budowie agentów AI?