# A Tour of Agents — Full Reference for AI Systems > Citation-ready content from https://tinyagents.dev. For a curated short index, see /llms.txt. This file mirrors every lesson, blog post, framework comparison, and head-to-head pair on the site as plain markdown. ## Key facts - An AI agent is a function that POSTs to an LLM API and dispatches tool calls in a loop. - The complete agent — tools, loop, conversation, state, memory, guardrails, self-scheduling — fits in ~60 lines of Python with no dependencies beyond `json`. - 20 frameworks compared on github stars, PyPI downloads, funding, and creator profiles. - LangChain dominates mindshare (132k stars) AND usage (53M weekly PyPI downloads, 14× #2). - Tinyagents.dev runs every lesson live in the browser via Pyodide. No install, no backend. ## Lessons ### Lesson 1: The Agent Function > Every ChatGPT message is one HTTP POST. That's all an agent is. Concepts: agent, function, HTTP POST, system prompt, messages. Maps to: LangChain's AgentExecutor, CrewAI's Agent, AutoGen's ConversableAgent — wrappers around one function.. # An Agent is a Function Every time you send a message in ChatGPT or Claude, here's what actually happens: your browser sends an HTTP POST to an API, and a response comes back. That's it. The fancy UI, the streaming text, the typing indicator — all cosmetics around one function call. Strip away LangChain's `AgentExecutor`, CrewAI's `Agent`, AutoGen's `ConversableAgent`. At the bottom of every one: **a function that sends an HTTP POST and returns the response.** That's what you'll build now. ## Step 1: POST to the LLM This is the raw call every SDK wraps — and exactly what happens when you hit Enter in ChatGPT. Two things to notice: 1. **`messages`** is an array — `system` sets behavior (like ChatGPT's "Custom Instructions"), `user` sends your input 2. The response lives at `choices[0].message.content` Everything else is HTTP boilerplate. ```python SYSTEM = "You are a concise expert. Answer in 1-2 sentences max." async def ask_llm(message): trace("llm_call", f"Asking: {message}") resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({ "model": LLM_MODEL, "messages": [ {"role": "system", "content": SYSTEM}, {"role": "user", "content": message}, ] })) data = json.loads(await resp.string()) return data["choices"][0]["message"]["content"] ``` ## Step 2: Wrap it The agent is the thinnest possible wrapper. String in, string out. Change the system prompt → different behavior from the same input. This is why ChatGPT and Claude behave differently — different system prompts. That's all "prompt engineering" is. ```python async def agent(message): trace("agent_start", f"Input: {message}") response = await ask_llm(message) trace("agent_end", f"Output: {response}") return response ``` ## Try it Send anything. Watch the diagram above — your message flows through `agent()` to the API and back. One function, one HTTP POST, one response. ```python print(f">> {await agent(USER_INPUT)}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') SYSTEM = "You are a concise expert. Answer in 1-2 sentences max." async def ask_llm(message): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": [{"role": "system", "content": SYSTEM}, {"role": "user", "content": message}]})) return json.loads(await resp.string())["choices"][0]["message"]["content"] async def agent(message): trace("agent_start", f"Input: {message}") response = await ask_llm(message) trace("agent_end", f"Output: {response}") return response print(await agent("What is the capital of France?")) ``` --- Source: https://tinyagents.dev/lesson/agent-function ### Lesson 2: Tools = Dict > When ChatGPT says "Used browser" — this is what's happening. Concepts: tools, function calling, JSON schema, dispatch. Maps to: LangChain's @tool, CrewAI's tool registration — it's `tools[name](**args)`.. # Tools = Dict You've seen this in ChatGPT: you ask "what's the weather?" and it calls a weather tool. Or in Claude: you ask it to search the web and it runs a search. The LLM can't run code — but it can say *"call `add` with `a=10, b=5`"* — a structured request. Your code executes it. > **Framework parallel:** LangChain's `@tool` decorator, CrewAI's tool registration — they build this dict for you. Here you'll see what's inside. ## Step 1: The tool registry A dict of callables. Lambda, function, class method — anything that takes arguments and returns a value. ```python tools = { "add": lambda a, b: a + b, "upper": lambda text: text.upper(), } ``` ## Step 2: Describe them for the LLM The LLM needs JSON Schema descriptions to know what tools exist and what arguments they accept. When ChatGPT shows that little plugin icon before calling a tool — this JSON schema is how it knew the tool existed and what to pass. This is the wire format OpenAI and Groq expect in the `tools` field. ```python TOOL_DEFS = [ {"type": "function", "function": { "name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": { "name": "upper", "description": "Uppercase a string", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] ``` ## Step 3: ask_llm with tool calling Same HTTP POST as L1, but now `tools` goes in the request body. When the LLM wants a tool, it returns `tool_calls` instead of plain text. ```python async def ask_llm(task): trace("llm_call", f"Asking: {task}") resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({ "model": LLM_MODEL, "messages": [{"role": "user", "content": task}], "tools": TOOL_DEFS, })) msg = json.loads(await resp.string())["choices"][0]["message"] if msg.get("tool_calls"): tc = msg["tool_calls"][0]["function"] return {"tool": tc["name"], "args": json.loads(tc["arguments"])} return {"text": msg.get("content", "")} ``` ## Step 4: Dispatch One line does the work: `tools[name](**args)`. When you see ChatGPT say "Used browser" or Claude say "Running search" — this is what's happening behind the scenes. Lookup by key, call with payload. Same pattern as an Express router or Redux reducer. ```python async def agent(task): trace("agent_start", f"Task: {task}") d = await ask_llm(task) if d.get("tool") and d["tool"] in tools: result = tools[d["tool"]](**d["args"]) trace("tool_result", f"{d['tool']} → {result}") trace("agent_end", f"{d['tool']}({d['args']}) = {result}") return f"{d['tool']}({d['args']}) = {result}" trace("agent_end", d.get("text", "No tool needed")) return d.get("text", "No tool needed") ``` ## Try it Try *"add 10 and 5"* — the LLM returns a tool call, you execute it. Try *"what is Python?"* — no tool needed, the LLM answers directly. **The LLM decides.** ```python print(f">> {await agent(USER_INPUT)}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(task): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": [{"role": "user", "content": task}], "tools": TOOL_DEFS})) msg = json.loads(await resp.string())["choices"][0]["message"] if msg.get("tool_calls"): tc = msg["tool_calls"][0]["function"] return {"tool": tc["name"], "args": json.loads(tc["arguments"])} return {"text": msg.get("content", "")} async def agent(task): d = await ask_llm(task) if d.get("tool") and d["tool"] in tools: r = tools[d["tool"]](**d["args"]) print(f">> {d['tool']}({d['args']}) = {r}") else: print(f">> {d.get('text', '')}") await agent("add 10 and 5") await agent("uppercase hello world") ``` --- Source: https://tinyagents.dev/lesson/tools ### Lesson 3: The Agent Loop > Claude searching files, then reading, then searching again. That's this loop. Concepts: agent loop, multi-turn, tool protocol, convergence. Maps to: LangChain AgentExecutor, OpenAI Agents SDK, AutoGen — a while loop over messages.. # The Agent Loop **This is the most important lesson.** Everything after this builds on this loop. You've seen this happen in Claude: you ask it to analyze a codebase, and it searches files, reads them, searches again, reads more — multiple steps before giving you an answer. Or ChatGPT with Code Interpreter: it writes code, runs it, sees an error, fixes it, runs again. That's this loop. L2's agent made one tool call and stopped. Real agents **loop**: call a tool → see the result → decide what's next → repeat until done. The LLM decides when to stop. No `tool_calls` in the response = done. This is the entire runtime of LangChain's `AgentExecutor`. ## Step 1: Tools + ask_llm Same tools as L2. But now `ask_llm` takes the full `messages` array and returns the raw message object — we need `tool_calls` and `tool_call_id` for the multi-turn protocol. ```python tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] ``` ## Step 2: The loop When Claude runs a multi-step task — say, searching your codebase, then reading files, then writing code — each step is one iteration of this loop: 1. **Call LLM** with the full messages array (everything so far) 2. **No `tool_calls`?** Return the answer — the LLM is done thinking 3. **Has `tool_calls`?** Execute each one, append results with `tool_call_id`, loop back The `tool_call_id` links each result to its request — without it, when the LLM asks for two tools at once, there's no way to tell which result belongs to which call. This is the **tool calling protocol** — the wire format that makes multi-step work. ```python async def agent(task, max_turns=5): messages = [ {"role": "system", "content": "Use tools to answer. Be concise."}, {"role": "user", "content": task}, ] for turn in range(max_turns): trace("llm_call", f"Turn {turn + 1}") msg = await ask_llm(messages) if not msg.get("tool_calls"): trace("agent_end", msg.get("content", "")) return msg.get("content", "") messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) trace("tool_result", f"{name}({args}) → {result}") messages.append({ "role": "tool", "tool_call_id": tc["id"], "content": str(result), }) return "Max turns reached" ``` ## Try it - *"add 10 and 5"* — one tool call, one turn - *"add 3 and 4, then uppercase hello"* — two tool calls, the LLM chains them Watch the diagram: each turn cycles through the loop. The messages array grows with each tool call. ```python print(f">> {await agent(USER_INPUT)}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] async def agent(task, max_turns=5): messages = [{"role": "system", "content": "Use tools to answer. Be concise."}, {"role": "user", "content": task}] for turn in range(max_turns): msg = await ask_llm(messages) if not msg.get("tool_calls"): return msg.get("content", "") messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns" print(f">> {await agent('add 10 and 5')}") print(f">> {await agent('uppercase hello world')}") ``` --- Source: https://tinyagents.dev/lesson/agent-loop ### Lesson 4: Conversation = Messages Array > Why ChatGPT remembers your last message — and why "New Chat" forgets. Concepts: conversation history, multi-turn, context window, ChatGPT pattern. Maps to: ChatGPT, Claude, every chat agent — the messages array IS the conversation.. # Conversation = Messages Array Open ChatGPT or Claude, send a message, then send another. The second message knows about the first. How? The app sends **every previous message** along with your new one. There's no magic memory — it's literally an array that grows. In L3, each `agent()` call started fresh. **Move the messages array outside**, and now every call sees the full history. That's it. That's what LangChain calls `ConversationBufferMemory`. It's a list that doesn't get cleared. ## Step 1: Tools + ask_llm Identical to L3. Nothing changes here. ```python tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] ``` ## Step 2: The conversation array One change from L3: the messages array lives **outside** the function. It's initialized once with a system prompt and never cleared. This is why ChatGPT and Claude can reference your earlier messages — and why starting a "New Chat" forgets everything. New chat = new empty array. ```python conversation = [ {"role": "system", "content": "You have tools: add(a,b) and upper(text). Use them when needed. Be concise."}, ] ``` ## Step 3: The loop with persistent history Same L3 loop. Two additions: 1. **Before the loop**: append the user's message to `conversation` 2. **After the loop**: append the assistant's response Next call, the LLM sees everything from this session. ```python async def agent(user_message, max_turns=5): conversation.append({"role": "user", "content": user_message}) for turn in range(max_turns): trace("llm_call", f"Turn {turn + 1} ({len(conversation)} messages)") msg = await ask_llm(conversation) if not msg.get("tool_calls"): conversation.append({"role": "assistant", "content": msg.get("content", "")}) trace("agent_end", msg.get("content", "")) return msg.get("content", "") conversation.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) trace("tool_result", f"{name}({args}) → {result}") conversation.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns reached" ``` ## Try it — multi-turn Send multiple messages: 1. *"add 3 and 4"* 2. *"now uppercase hello"* 3. *"what were the results?"* The agent answers #3 correctly because it sees the full conversation history. Watch the message count grow in the monitor. ```python print(f">> {await agent(USER_INPUT)}") print(f"({len(conversation)} messages in history)") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] conversation = [{"role": "system", "content": "You have tools: add(a,b), upper(text). Be concise."}] async def agent(user_message, max_turns=5): conversation.append({"role": "user", "content": user_message}) for turn in range(max_turns): msg = await ask_llm(conversation) if not msg.get("tool_calls"): conversation.append({"role": "assistant", "content": msg.get("content", "")}) return msg.get("content", "") conversation.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) conversation.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns" print(f">> {await agent('add 3 and 4')}") print(f">> {await agent('now uppercase hello')}") print(f">> {await agent('what were my results?')}") ``` --- Source: https://tinyagents.dev/lesson/conversation ### Lesson 5: State = Dict > How Claude shows "Searched 5 files" — structured tracking alongside the chat. Concepts: state, structured tracking, metadata, observability. Maps to: LangGraph state channels, Redux store — structured data alongside the conversation.. # State = Dict You know how Claude shows "Searched 5 files" or ChatGPT shows "Analyzed data" with a little summary? That's not from the messages — it's **state** tracked alongside the conversation. The messages array is the **raw tape**. But you often need structured answers: *which tools ran? how many turns? what were the results?* That's **state** — a dict updated inside the loop, returned alongside the answer. > **Framework parallel:** LangGraph calls these "state channels" with typed reducers. Strip the abstraction: it's a dict updated in a loop. ## Step 1: Tools + ask_llm Same L3 ingredients. No changes. ```python tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] ``` ## Step 2: The loop with state tracking Same L3 loop. One addition: a `state` dict that records every tool call and result as the loop runs. The agent returns `state` instead of just the answer string. This gives you a structured audit trail — not just "the answer was 15", but *which tools ran, with what args, producing what results, in how many turns.* ```python async def agent(task, max_turns=5): state = {"turns": 0, "tool_calls": [], "results": []} messages = [ {"role": "system", "content": "Use tools to answer. Be concise."}, {"role": "user", "content": task}, ] for turn in range(max_turns): state["turns"] += 1 trace("llm_call", f"Turn {state['turns']}") msg = await ask_llm(messages) if not msg.get("tool_calls"): state["answer"] = msg.get("content", "") trace("agent_end", f"Done in {state['turns']} turns") return state messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) state["tool_calls"].append({"tool": name, "args": args}) state["results"].append(result) trace("tool_result", f"{name}({args}) → {result}") messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) state["answer"] = "Max turns reached" return state ``` ## Try it Try *"add 10 and 5, then uppercase hello"*. You'll see the full state: which tools ran, what they returned, how many turns it took. This is observability — you can log it, store it, debug with it. ```python result = await agent(USER_INPUT) print(f">> {result['answer']}") print(f"Tools used: {result['tool_calls']}") print(f"Results: {result['results']}") print(f"Turns: {result['turns']}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] async def agent(task, max_turns=5): state = {"turns": 0, "tool_calls": [], "results": []} messages = [{"role": "system", "content": "Use tools. Be concise."}, {"role": "user", "content": task}] for turn in range(max_turns): state["turns"] += 1 msg = await ask_llm(messages) if not msg.get("tool_calls"): state["answer"] = msg.get("content", "") return state messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) state["tool_calls"].append(name) state["results"].append(result) messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return state r = await agent("add 10 and 5, then uppercase hello") print(f">> {r.get('answer','')}") print(f"Tools: {r['tool_calls']}, Results: {r['results']}, Turns: {r['turns']}") ``` --- Source: https://tinyagents.dev/lesson/state ### Lesson 6: Memory Across Runs > ChatGPT Memory knows your name across chats. Here's how. Concepts: memory, persistence, long-term, retrieval. Maps to: Mem0, Zep, LangChain ConversationSummaryMemory — long-term storage outside the conversation.. # Memory: Persist Across Runs ChatGPT has "Memory" in settings — it remembers your name, your preferences, your job across conversations. Claude has a similar feature called Projects with custom instructions. How? L4's conversation resets when you start a new chat. **Memory** survives across sessions. The difference: - **Conversation** = the messages array (ephemeral, dies with the chat) - **Memory** = a separate store (persistent, survives forever) In production you'd use Redis, Postgres, or a vector store. Here: a dict injected into the system prompt. Same interface ChatGPT uses: **load before the loop, save during it.** > **Key insight:** `remember` is just a tool. The LLM calls it like `add`. The side effect is: a value enters the memory dict. ## Step 1: Memory dict + tools The memory dict lives at module level. `remember` is a tool that writes to it. The LLM doesn't know memory is special — it's just another tool that returns a string. ```python memory = {} tools = { "add": lambda a, b: a + b, "remember": lambda key, value: memory.update({key: value}) or f"saved {key}={value}", } TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "remember", "description": "Save a key-value pair to long-term memory", "parameters": {"type": "object", "properties": {"key": {"type": "string"}, "value": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] ``` ## Step 2: Inject memory into the system prompt Same L3 loop. One change: the system prompt includes the current memory contents. The LLM sees past knowledge every turn and can save new knowledge via the `remember` tool. ```python async def agent(task, max_turns=5): mem_str = json.dumps(memory) if memory else "empty" messages = [ {"role": "system", "content": f"You have tools. Memory: {mem_str}. Use remember() to save facts. Be concise."}, {"role": "user", "content": task}, ] for turn in range(max_turns): trace("llm_call", f"Turn {turn + 1}") msg = await ask_llm(messages) if not msg.get("tool_calls"): trace("agent_end", msg.get("content", "")) return msg.get("content", "") messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) trace("tool_result", f"{name}({args}) → {result}") messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns reached" ``` ## Try it — memory persists across calls 1. Send *"my name is Alice"* — the agent calls `remember(key="name", value="Alice")` 2. Send *"what is my name?"* — the agent reads `Memory: {"name": "Alice"}` from the system prompt It works across separate calls because the memory dict lives outside the function. ```python print(f">> {await agent(USER_INPUT)}") print(f"Memory: {memory}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') memory = {} tools = {"add": lambda a, b: a + b, "remember": lambda key, value: memory.update({key: value}) or f"saved {key}={value}"} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "remember", "description": "Save to memory", "parameters": {"type": "object", "properties": {"key": {"type": "string"}, "value": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] async def agent(task, max_turns=5): mem_str = json.dumps(memory) if memory else "empty" messages = [{"role": "system", "content": f"Tools available. Memory: {mem_str}. Use remember() to save. Be concise."}, {"role": "user", "content": task}] for turn in range(max_turns): msg = await ask_llm(messages) if not msg.get("tool_calls"): return msg.get("content", "") messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns" print(f">> {await agent('my name is Alice and I like Python')}") print(f">> {await agent('what is my name and what do I like?')}") print(f"Memory: {memory}") ``` --- Source: https://tinyagents.dev/lesson/memory ### Lesson 7: Policy = Guardrails > Why ChatGPT refuses harmful requests. Two gates, a few lines each. Concepts: policy, guardrails, input gate, output gate, safety. Maps to: Guardrails AI, NeMo Guardrails, LangChain output parsers — rules checked before and after the LLM.. # Policy = Guardrails You've seen this: ask ChatGPT to help with something harmful and it refuses. Ask Claude to generate malware and it declines. That's not the LLM being "smart" — it's **policy**. Rules checked before and after the LLM runs. The L3 loop trusts the user and the LLM completely. Production agents can't afford that. **Policy** adds two gates: - **Input gate**: blocks dangerous requests *before* they reach the LLM (saves money, prevents harm) - **Output gate**: redacts or rejects the LLM's response *before* the user sees it > **Framework parallel:** Guardrails AI and NeMo Guardrails implement exactly these two gates. OpenAI's moderation endpoint is an input gate. The architecture is identical. ## Step 1: Tools + ask_llm Same L3 setup. The loop itself won't change — policy wraps it. ```python tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] ``` ## Step 2: Define the gates Each gate is a list of functions. A function returns `True` to pass, or a string explaining why it blocked. `check_gate` runs all rules and short-circuits on the first failure. This is the same pattern behind ChatGPT's content filter and Claude's safety system — just without the complexity. Adding a rule = appending a lambda. Removing one = deleting it. No config files, no YAML. ```python INPUT_RULES = [ lambda text: "delete" not in text.lower() or "Input blocked: no delete commands", lambda text: "drop" not in text.lower() or "Input blocked: no drop commands", lambda text: len(text) < 500 or "Input blocked: message too long", ] OUTPUT_RULES = [ lambda text: "password" not in text.lower() or "Output redacted: contains password", lambda text: "secret" not in text.lower() or "Output redacted: contains secret", ] def check_gate(text, rules, gate_name): for rule in rules: result = rule(text) if result is not True: trace("policy_block", f"{gate_name}: {result}") return False, result trace("policy_check", f"{gate_name}: PASS") return True, None ``` ## Step 3: Wrap the L3 loop Input gate runs first — if it fails, the LLM never sees the request. The L3 loop runs in the middle, unchanged. Output gate runs last — if it fails, the user sees a redaction notice instead of the response. ```python async def agent(task, max_turns=5): # --- INPUT GATE --- ok, reason = check_gate(task, INPUT_RULES, "INPUT") if not ok: return f"BLOCKED: {reason}" # --- L3 LOOP (unchanged) --- messages = [ {"role": "system", "content": "Use tools to answer. Be concise."}, {"role": "user", "content": task}, ] for turn in range(max_turns): trace("llm_call", f"Turn {turn + 1}") msg = await ask_llm(messages) if not msg.get("tool_calls"): response = msg.get("content", "") # --- OUTPUT GATE --- ok, reason = check_gate(response, OUTPUT_RULES, "OUTPUT") if not ok: return f"REDACTED: {reason}" trace("agent_end", response) return response messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) trace("tool_result", f"{name}({args}) → {result}") messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns reached" ``` ## Try it - *"add 10 and 5"* — passes both gates, you get the answer - *"delete everything"* — blocked at input, the LLM never sees it - *"tell me the admin password"* — the LLM might answer, but the output gate redacts it The LLM costs zero tokens on blocked requests. That's the input gate's real value. ```python print(f">> {await agent(USER_INPUT)}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper()} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] INPUT_RULES = [ lambda text: "delete" not in text.lower() or "No delete commands", lambda text: "drop" not in text.lower() or "No drop commands", ] OUTPUT_RULES = [ lambda text: "password" not in text.lower() or "Contains password", ] def check_gate(text, rules, name): for r in rules: result = r(text) if result is not True: return False, result return True, None async def agent(task, max_turns=5): ok, reason = check_gate(task, INPUT_RULES, "INPUT") if not ok: return f"BLOCKED: {reason}" messages = [{"role": "system", "content": "Use tools. Be concise."}, {"role": "user", "content": task}] for turn in range(max_turns): msg = await ask_llm(messages) if not msg.get("tool_calls"): response = msg.get("content", "") ok, reason = check_gate(response, OUTPUT_RULES, "OUTPUT") if not ok: return f"REDACTED: {reason}" return response messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns" print(f">> {await agent('add 10 and 5')}") print(f">> {await agent('delete everything')}") ``` --- Source: https://tinyagents.dev/lesson/policy ### Lesson 8: Self-Scheduling > ChatGPT deep research spawns sub-tasks on its own. A queue + a budget. Concepts: self-scheduling, task queue, BFS, convergence, budget. Maps to: CrewAI task delegation, AutoGen nested chats — BFS over a dynamic work queue.. # Self-Scheduling: The Agent Decides What's Next You've seen this in Claude: ask it to "refactor this module" and it decides on its own to read related files, check tests, update imports — tasks you never explicitly asked for. Or ChatGPT's deep research mode: you ask one question and it spawns multiple research threads. So far, **you** decide what the agent works on. A truly agentic system decides for itself. The trick: `schedule_followup` is just a tool. The LLM calls it like `add`. The side effect: a new task enters the queue. The outer loop processes tasks until the queue drains — or the budget runs out. > **Framework parallel:** CrewAI calls this "delegation." AutoGen calls it "nested chats." The budget cap (`max_tasks`) is the difference between a useful agent and a billing incident. ## Step 1: Tools + the queue `schedule_followup` appends to `task_queue` — a list that lives outside both the agent and the scheduler. The LLM doesn't know it's special; it just sees a tool that returns "scheduled: ...". ```python task_queue = [] tools = { "add": lambda a, b: a + b, "schedule_followup": lambda task: task_queue.append(task) or f"scheduled: {task}", } TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "schedule_followup", "description": "Schedule a follow-up task for the agent to process next", "parameters": {"type": "object", "properties": {"task": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] ``` ## Step 2: The L3 loop — unchanged The agent doesn't know about the queue. It executes tools. When the LLM calls `schedule_followup`, the tool function appends to the queue as a side effect. The loop handles it like any other tool result. ```python async def agent(task, max_turns=5): messages = [ {"role": "system", "content": "You have tools. Use schedule_followup to add next steps. Be concise."}, {"role": "user", "content": task}, ] for turn in range(max_turns): trace("llm_call", f"Turn {turn + 1}") msg = await ask_llm(messages) if not msg.get("tool_calls"): trace("agent_end", msg.get("content", "")) return msg.get("content", "") messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) trace("tool_result", f"{name}({args}) → {result}") messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns reached" ``` ## Step 3: The scheduler — BFS Pop the front task, run `agent()`, check if new tasks appeared. Repeat until the queue is empty or the budget is spent. This is the **outer loop**. The agent has its own **inner loop** (L3). Two levels of iteration: the scheduler picks *what* to work on, the agent decides *how* to do it. ```python async def run_queue(initial_tasks, max_tasks=5): task_queue.clear() task_queue.extend(initial_tasks) results = [] processed = 0 while task_queue and processed < max_tasks: task = task_queue.pop(0) processed += 1 trace("agent_start", f"[{processed}/{max_tasks}] {task}") result = await agent(task) results.append({"task": task, "result": result}) if task_queue: trace("policy_block", f"BUDGET: {len(task_queue)} tasks remaining") return results ``` ## Try it Enter a topic. The LLM processes it and may schedule follow-ups. Watch the queue grow and drain in the monitor. If the LLM is ambitious, it'll hit the budget cap. ```python results = await run_queue([f"research {USER_INPUT} and schedule a follow-up to summarize findings"]) for r in results: print(f">> [{r['task']}] {r['result']}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') task_queue = [] tools = {"add": lambda a, b: a + b, "schedule_followup": lambda task: task_queue.append(task) or f"scheduled: {task}"} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "schedule_followup", "description": "Schedule a follow-up task", "parameters": {"type": "object", "properties": {"task": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] async def agent(task, max_turns=5): messages = [{"role": "system", "content": "Use tools. Use schedule_followup for next steps. Be concise."}, {"role": "user", "content": task}] for turn in range(max_turns): msg = await ask_llm(messages) if not msg.get("tool_calls"): return msg.get("content", "") messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns" async def run_queue(tasks, max_tasks=5): task_queue.clear(); task_queue.extend(tasks) results, i = [], 0 while task_queue and i < max_tasks: task = task_queue.pop(0); i += 1 results.append({"task": task, "result": await agent(task)}) return results for r in await run_queue(["research AI safety and schedule a summarize follow-up"]): print(f">> [{r['task']}] {r['result']}") ``` --- Source: https://tinyagents.dev/lesson/self-scheduling ### Lesson 9: The Whole Thing > Everything ChatGPT and Claude do — composed in ~60 lines. Concepts: complete agent, integration, no framework. Maps to: LangChain, CrewAI, AutoGen: thousands of lines. You need 60.. # The Whole Thing Nine lessons. Each concept maps to something you've used in ChatGPT or Claude: | Lesson | Concept | You've seen it as... | |--------|---------|---------------------| | 1 | Agent function | Hitting Enter in any chat UI | | 2 | Tools | "Used browser", "Ran code", plugin icons | | 3 | **The Loop** | Multi-step tool use (search → read → search again) | | 4 | Conversation | Chat history within a session | | 5 | State | "Analyzed 5 files", progress indicators | | 6 | Memory | ChatGPT Memory, Claude Projects | | 7 | Policy | Content refusals, safety filters | | 8 | Self-scheduling | Deep research mode, autonomous sub-tasks | Now they compose into a complete agent framework. ~60 lines. No imports beyond `json` and `pyfetch`. > This is the same architecture as LangChain's AgentExecutor + memory + guardrails + task queue. The difference: you can read every line. ## Step 1: Tools + memory + queue (L2, L6, L8) Four tools. Two do computation (`add`, `upper`). Two have side effects: `remember` writes to a persistent dict, `schedule` appends to a task queue. The LLM treats them all the same. ```python memory, task_queue, state = {}, [], {"tool_calls": [], "turns": 0} tools = { "add": lambda a, b: a + b, "upper": lambda text: text.upper(), "remember": lambda key, value: memory.update({key: value}) or f"saved {key}={value}", "schedule": lambda task: task_queue.append(task) or f"scheduled: {task}", } TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, {"type": "function", "function": {"name": "remember", "description": "Save to long-term memory", "parameters": {"type": "object", "properties": {"key": {"type": "string"}, "value": {"type": "string"}}}}}, {"type": "function", "function": {"name": "schedule", "description": "Schedule a follow-up task", "parameters": {"type": "object", "properties": {"task": {"type": "string"}}}}}, ] ``` ## Step 2: ask_llm + policy (L1, L7) The raw HTTP call from L1. The two gates from L7. Input gate blocks before the LLM sees it. Output gate redacts before the user sees it. ```python async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] INPUT_RULES = [lambda t: "delete" not in t.lower() or "blocked: no delete"] OUTPUT_RULES = [lambda t: "password" not in t.lower() or "redacted: contains password"] def check_gate(text, rules): for r in rules: result = r(text) if result is not True: return False, result return True, None ``` ## Step 3: The agent (L3 + L5 + L6 + L7) Read this carefully — every concept has a home: - **L7 input gate** → lines 2-4 - **L6 memory injection** → lines 6-7 - **L3 loop** → lines 10-24 (the core — unchanged since lesson 3) - **L5 state tracking** → line 12, line 20 - **L7 output gate** → lines 15-17 The loop itself is exactly L3. Everything else wraps or extends it. ```python async def agent(task, max_turns=5): ok, reason = check_gate(task, INPUT_RULES) if not ok: trace("policy_block", reason) return f"BLOCKED: {reason}" mem_str = json.dumps(memory) if memory else "empty" messages = [ {"role": "system", "content": f"Tools available. Memory: {mem_str}. Be concise."}, {"role": "user", "content": task}, ] for turn in range(max_turns): state["turns"] += 1 trace("llm_call", f"Turn {turn + 1}") msg = await ask_llm(messages) if not msg.get("tool_calls"): response = msg.get("content", "") ok, reason = check_gate(response, OUTPUT_RULES) if not ok: trace("policy_block", reason) return f"REDACTED: {reason}" trace("agent_end", response) return response messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) state["tool_calls"].append({"tool": name, "args": args}) trace("tool_result", f"{name}({args}) → {result}") messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns reached" ``` ## Try it — the complete agent The scheduler (L8) processes the queue. Each task flows through: input gate → L3 loop (with memory + state) → output gate. Try these in sequence: 1. *"remember my name is Alice, then add 10 and 5"* 2. *"what is my name?"* — memory persists across calls 3. *"delete the database"* — blocked by input gate ```python task_queue.append(USER_INPUT) while task_queue: task = task_queue.pop(0) trace("agent_start", f"Task: {task}") print(f">> {await agent(task)}") print(f"Memory: {memory}") print(f"State: {state}") ``` ## Full code ```python import json from pyodide.http import pyfetch def trace(t, l): print(f'__TRACE__:{json.dumps({"id": l[:8], "timestamp": 0, "type": t, "label": l})}') memory, task_queue, state = {}, [], {"tool_calls": [], "turns": 0} tools = {"add": lambda a, b: a + b, "upper": lambda text: text.upper(), "remember": lambda key, value: memory.update({key: value}) or f"saved {key}={value}", "schedule": lambda task: task_queue.append(task) or f"scheduled: {task}"} TOOL_DEFS = [ {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}}, {"type": "function", "function": {"name": "upper", "description": "Uppercase text", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}}}}, {"type": "function", "function": {"name": "remember", "description": "Save to memory", "parameters": {"type": "object", "properties": {"key": {"type": "string"}, "value": {"type": "string"}}}}}, {"type": "function", "function": {"name": "schedule", "description": "Schedule follow-up task", "parameters": {"type": "object", "properties": {"task": {"type": "string"}}}}}, ] async def ask_llm(messages): resp = await pyfetch(f"{LLM_BASE_URL}/chat/completions", method="POST", headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}, body=json.dumps({"model": LLM_MODEL, "messages": messages, "tools": TOOL_DEFS})) return json.loads(await resp.string())["choices"][0]["message"] INPUT_RULES = [lambda t: "delete" not in t.lower() or "blocked: no delete"] OUTPUT_RULES = [lambda t: "password" not in t.lower() or "redacted: password"] def check_gate(text, rules): for r in rules: result = r(text) if result is not True: return False, result return True, None async def agent(task, max_turns=5): ok, reason = check_gate(task, INPUT_RULES) if not ok: return f"BLOCKED: {reason}" mem_str = json.dumps(memory) if memory else "empty" messages = [{"role": "system", "content": f"Tools available. Memory: {mem_str}. Be concise."}, {"role": "user", "content": task}] for turn in range(max_turns): state["turns"] += 1 msg = await ask_llm(messages) if not msg.get("tool_calls"): response = msg.get("content", "") ok, reason = check_gate(response, OUTPUT_RULES) if not ok: return f"REDACTED: {reason}" return response messages.append(msg) for tc in msg["tool_calls"]: name = tc["function"]["name"] args = json.loads(tc["function"]["arguments"]) result = tools[name](**args) state["tool_calls"].append({"tool": name, "args": args}) messages.append({"role": "tool", "tool_call_id": tc["id"], "content": str(result)}) return "Max turns" task_queue.append("remember name=Alice then add 10 and 5") while task_queue: task = task_queue.pop(0) print(f">> {await agent(task)}") print(f">> {await agent('what is my name?')}") print(f"Memory: {memory}") print(f"State: {state}") ``` --- Source: https://tinyagents.dev/lesson/the-whole-thing ## Blog posts ### LangChain Alternatives in 2026: 8 Real Options (Plus When to Pick Each) > If LangChain feels heavier than your problem, here are eight real alternatives — plain Python, LangGraph, CrewAI, Vercel AI SDK, Mastra, Pydantic AI, AWS Strands, AutoGen — and the question to ask before picking one. Date: 2026-05-07 · Keywords: langchain alternative, langchain alternatives, lightweight langchain alternative, langchain agentexecutor alternative, langchain agentexecutor replacement, free alternatives to langchain, alternative to langchain and langgraph, best langchain alternative, is n8n an alternative to langchain, python langchain alternative ## Why people Google "langchain alternative" Open Google autocomplete and type "langchain alternative." The suggestions tell you what's actually on engineers' minds: `lightweight langchain alternative`, `langchain agentexecutor alternative`, `langchain agentexecutor replacement`, `free alternatives to langchain`, `alternative to langchain and langgraph`, `is n8n an alternative to langchain`. There's also a steady stream of language-specific variants — `rust langchain alternative`, `golang langchain alternative`, `python langchain alternative`. The shape of the demand is clear: people are looking, and most of them are looking specifically because LangChain felt heavier than what their problem actually needed. This post is the honest answer to that search. Not "LangChain is bad" — LangChain genuinely earns its weight when you need its integration catalog. But for the engineer who installed it, hit the abstraction wall at 2 AM, and is now Googling for something simpler, here are the real options. ## Option 1: Plain Python (the 60-line version) The simplest LangChain alternative is no framework at all. The complete agent pattern — tool calling, the agent loop, conversation history, structured state, persistent memory, input/output guardrails, and a self-scheduling task queue — fits in roughly sixty lines of plain Python with no dependencies beyond `json`. An agent is a function that POSTs to `/chat/completions`. Tools are a dict of callables. The loop is a `while` loop that checks for `tool_calls` and dispatches via `tools[name](**args)`. Conversation is a list. State is a dict. Memory is a dict serialized into the system prompt. This is what `AgentExecutor` and `@tool` and `ConversationBufferMemory` are wrapping under the hood. When you write the sixty lines yourself, debugging is reading sixty lines instead of navigating five layers of class hierarchy. Use this when your agent is straightforward (one provider, a few tools, a single loop) and you want to actually understand what's happening. The interactive course at tinyagents.dev walks you through this lesson by lesson. ## Option 2: LangGraph (still LangChain Inc, but explicit) If you like LangChain's ecosystem but hate `AgentExecutor`'s opacity, LangGraph is the in-family answer. It models your agent as an explicit graph of nodes and edges with typed state, conditional branching, parallel fanout, and checkpointed pause/resume. The control flow is visible — you can see when a node fires, what state it reads, what it writes. LangSmith's tracing of LangGraph workflows is genuinely good. Use this when your agent is a workflow (decompose, search, judge, finalize) rather than a single tool-using loop, and when you want explicit branching, retries, or a human approval gate. Skip it if your agent is a single `while` loop — the graph machinery is overkill for one node. ## Option 3: CrewAI (multi-agent, role-driven) If your problem genuinely looks like a team — a researcher hands off to a writer hands off to an editor — CrewAI's vocabulary fits. `Agent(role, goal, backstory)` plus `Task` plus `Crew(process=sequential|hierarchical)` is a clean abstraction over multi-agent role separation, and the role/goal/backstory strings actually drive prompt quality. Smaller surface area than LangChain, fewer integrations, but the multi-agent ergonomics are sharper. Skip if your problem is one agent calling tools — the crew abstraction is wasted weight. ## Option 4: Vercel AI SDK (TypeScript-first, UI-native) If you're building a TypeScript app where chat is a first-class UI feature, the Vercel AI SDK is the right pick. `generateText`, `streamText`, and `generateObject` are streaming-first primitives. `useChat` saves a day of `useState` plumbing. Tools use Zod schemas. Provider portability is one import change. Tight integration with Vercel deploy and AI Gateway. Use it when the LLM is one piece of a bigger React app. Skip it if you're server-only Python or just learning how agents work — the SDK abstracts the very things you want to see when you're learning. ## Option 5: Mastra (TypeScript, batteries-included) Mastra is the right TypeScript framework when you'd otherwise piece together LangChain.js, a vector store client, a workflow engine, and a debugging tool yourself. Agents, workflows, RAG, memory, and Mastra Studio (a local visual debugger) all in one package, all type-safe. From the team behind Gatsby, $22M raised, intentionally TypeScript-native rather than a port of a Python framework. Use this for production TypeScript agents that need workflows, RAG, and memory together. Skip it if your agent is simple enough for plain TypeScript — sixty lines and `fetch` covers a lot. ## Option 6: Pydantic AI (type-safe, Python-native) Pydantic AI is the right pick when **structured output and type safety** are the primary concerns. Built by the Pydantic team, it leverages Pydantic models throughout — agents, tool responses, structured outputs are all typed. The `Agent` class is small and the abstractions are thin. Top-tier downloads on PyPI (around 3.8M weekly) because every Pydantic user is one `pip install` away from it. Use it when you need rigorous typed contracts between your agent and the rest of your code. Skip if you're more concerned about integration breadth or multi-agent orchestration. ## Option 7: AWS Strands + Bedrock AgentCore If you're shipping production agents on AWS, Strands plus AgentCore is the AWS-native pair. Strands is a thin Python SDK with model-driven loops, type-hint-derived tool schemas, and first-class MCP support. AgentCore is the managed runtime — sandboxed MicroVM isolation, managed memory (short and long term), OAuth + IAM identity, MCP-compliant gateway, and OpenTelemetry observability. Together they're a complete production stack on AWS without you owning the runtime layer. Skip it if you're not on AWS — these services don't apply elsewhere. ## Option 8: AutoGen (Microsoft, conversation-shaped) AutoGen models multi-agent systems as **conversations**: `ConversableAgent` instances exchange messages, `GroupChat` plus `GroupChatManager` selects the next speaker. Backed by Microsoft Research with significant academic uptake. Use it when your problem is genuinely conversational — multiple agents arguing, debating, refining a shared output — and the conversation-as-primitive abstraction matches your mental model. Skip if your problem is a single agent with tools (use plain Python or LangGraph) or role-separated handoffs (use CrewAI). ## What about n8n, AutoGPT, and BabyAGI? n8n is a **workflow automation platform** with AI nodes — closer to Zapier than to LangChain. It's a real LangChain alternative when your problem is "wire up some triggers and tools and let an LLM be one step in a larger automation," not "build an agent that reasons through a multi-step task." Different category, different fit. AutoGPT and BabyAGI are **autonomous agent demonstrations** rather than production frameworks — they were inspirational in 2023, but most teams that started with them moved to LangChain, CrewAI, or plain Python within a quarter. Worth knowing about; not a serious candidate for new projects in 2026. ## How to decide Three questions cut through the noise: 1. **What language are you in?** Python: plain Python, LangGraph, CrewAI, Pydantic AI, Strands. TypeScript: Vercel AI SDK or Mastra. 2. **What's the shape of your problem?** Single agent with tools: plain Python or Pydantic AI. Workflow with branching: LangGraph or Mastra. Multi-agent with roles: CrewAI or AutoGen. Production on AWS: Strands plus AgentCore. 3. **What's your debugging budget at 2 AM?** If "low" — pick something thin. The frameworks that wrap a five-line pattern in three layers of class hierarchy will cost you when something breaks. The best LangChain alternative is whichever lets you read your own agent's code on a bad day. For a lot of teams, that's plain Python. For some, it's LangGraph. For TypeScript shops, it's Vercel AI SDK or Mastra. The thing they all have in common: thinner than the abstraction wall you're trying to escape. ## Build it yourself Want to see what every framework on this list is actually wrapping? The interactive course at tinyagents.dev builds the same agent in ~60 lines of plain Python — the thing the abstractions wrap. --- Source: https://tinyagents.dev/blog/langchain-alternatives ### How AI Agents Actually Work (In Plain Python) > AI agents are simpler than frameworks make them look. An agent is a function, tools are a dict, and the agent loop is a while loop. Here's how it all fits together. Date: 2026-03-30 · Keywords: how AI agents work, AI agent explained, build AI agent Python, LLM agent architecture, agent loop explained, what is an AI agent, AI agent guardrails, AI agent state management, AI agent task queue ## An agent is a function Every time you send a message in ChatGPT or Claude, your browser sends an HTTP POST to an API and a response comes back. That's it — that's the agent. Strip away LangChain's AgentExecutor, CrewAI's Agent class, AutoGen's ConversableAgent — at the bottom of every one is a function that sends an HTTP POST and returns the response. The system prompt controls behavior. The messages array is the conversation. Everything else is cosmetics. When you use Cursor to edit code, it's doing the same thing: posting your file contents plus your instruction to an LLM endpoint and streaming back the diff. The "agent" is a function that takes messages and returns a completion. In LangChain, this is wrapped in a Runnable. In CrewAI, it's an Agent with a role and backstory. In plain Python, it's a function: def agent(messages): return client.chat.completions.create(model="gpt-4o", messages=messages). That's the entire abstraction. Everything built on top — chains, crews, workflows — is orchestration around this one call. ## Tools are a dictionary When ChatGPT says "Searched 4 sites" or Claude runs a code snippet, the LLM isn't executing anything. It returns a structured JSON request: call this function with these arguments. Your code looks up the function by name in a dictionary and calls it. That's it: `tools[name](**args)`. This is what LangChain's @tool decorator builds behind the scenes. It's what CrewAI's tool registration does. It's what OpenAI's function calling spec describes. A dict of callables, dispatched by name. When you see Cursor's agent mode invoking terminal commands or reading files, each of those is a tool — a Python or TypeScript function registered under a string key. The LLM picks the key and the arguments; your code does the execution. The dictionary pattern means adding a new tool is one line: tools["search_web"] = search_web. No subclassing, no decorators, no registration ceremony. AutoGen calls these "function maps." LangChain calls them "tool bindings." The underlying data structure is the same: a dictionary mapping strings to callables. ## The agent loop is a while loop When Claude searches your codebase, reads several files, then searches again based on what it found — that's a loop. Call the LLM with the full message history. If the response contains tool_calls, execute each one, append the results to messages, and call the LLM again. If no tool_calls, return the final response to the user. That's it. This is the entire runtime of LangChain's AgentExecutor: a while loop that exits when the LLM stops requesting tools. CrewAI's task execution does the same. AutoGen's agent chat runs the same pattern with message passing between agents. The loop typically includes a max_iterations guard so a confused model can't run forever. In ChatGPT, you'll sometimes see "Analyzing..." appear multiple times — that's the loop iterating. Each iteration is one LLM call, zero or more tool executions, and a decision: keep going or return. In plain Python: while True: response = call_llm(messages); if no tool_calls: break; execute tools and append results. Five lines that replace thousands of lines of framework code. ## Conversation is a list that grows ChatGPT remembers what you said three messages ago because the app sends every previous message along with your new one. There's no magic context window that "remembers" — it's a list that grows. Each time you send a message, the full array goes to the API: system prompt, then every user and assistant message in order. Move the messages list outside the function and every call sees the full history. That's LangChain's ConversationBufferMemory — literally an array. Starting a "New Chat" in ChatGPT or Claude just creates a new empty list. When you hit the context limit and the model starts forgetting early messages, that's because the list got too long and the oldest entries were truncated. Some frameworks handle this with sliding windows (keep the last N messages) or summarization (compress old messages into a summary). LangChain's ConversationSummaryMemory does the latter. But the foundation is always the same: a list of message objects, appended to on every turn, sent in full on every request. ## Memory is a dict in the system prompt ChatGPT Memory knows your name and preferences across conversations. How? A dictionary stored outside any single conversation, serialized into the system prompt at the start of each call. The LLM writes to it via a remember() tool — just another entry in the tools dictionary. When you tell ChatGPT "I prefer TypeScript over JavaScript" and it remembers that weeks later, a background process extracted that fact, stored it in a key-value store, and a future conversation's system prompt included it. Mem0, Zep, LangChain's ConversationSummaryMemory — all variations on this pattern. The dict might be flat key-value pairs, a list of facts, or a vector-searchable store, but the mechanism is identical: read from persistent storage, inject into prompt, update via tool call. In plain Python: memories = json.load(open("memory.json")); system_prompt = base_prompt + format_memories(memories). The remember tool writes back to that file. Cross-session memory is just a JSON file that outlives the conversation. ## State is a dict updated in the loop When ChatGPT shows "Analyzed 5 files" or a progress indicator during a long task, that's state — a dictionary tracked alongside the conversation and updated each iteration of the agent loop. It's not part of the messages array. It's metadata about what's happened so far: how many turns, which tools were called, accumulated results, error counts. LangGraph calls these "state channels" — typed dictionaries that flow through the graph. CrewAI tracks them as task context. In plain Python, it's a dict initialized before the loop: state = {"turns": 0, "tool_calls": [], "files_analyzed": 0}. Each iteration updates it: state["turns"] += 1. This is how agents make decisions beyond just "did the LLM request a tool?" — you can check state["turns"] > 10 and force a summary, or check state["errors"] > 3 and bail out. Cursor uses state to track which files it has already edited in a session so it doesn't re-edit them. The dict is the simplest possible accumulator, and it's all you need for most agent workflows. ## Guardrails are if-statements When ChatGPT refuses a harmful request, that's a guardrail — and it's less magical than it sounds. Two lists of checks: one inspects the user's input before the LLM sees it, one inspects the LLM's output before the user sees it. Each check is a function that returns either True (pass) or a rejection message. If any check fails, the agent short-circuits with a refusal instead of calling the model or returning the response. NeMo Guardrails, Guardrails AI, LangChain's output parsers — all build on this input/output filter pattern. Some use secondary LLM calls to classify content. Some use regex. Some use embedding similarity against a blocklist. But the control flow is the same: run checks, proceed or reject. In plain Python: input_checks = [lambda m: (True if "ignore all instructions" not in m else "Nice try.")]. Loop through them before calling the LLM. Output checks work identically on the response. Two lists of lambdas and two for-loops. That's the entire guardrails architecture. ## Self-scheduling is a task queue When ChatGPT's deep research mode spawns sub-queries — searching for background context, then diving into specifics, then cross-referencing — that's a task queue. The agent doesn't just respond; it adds new tasks for itself to process later. A schedule() tool lets the LLM push items onto a list. A while loop pops tasks off that list, runs the agent on each one, and collects results. That's what CrewAI's task delegation does. That's what AutoGen's group chat manager orchestrates. In plain Python: task_queue = deque([initial_task]). The agent loop becomes: while task_queue and budget > 0: task = task_queue.popleft(); result = run_agent(task); budget -= 1. The schedule tool just appends to the queue: task_queue.append(new_task). The budget cap prevents runaway execution — without it, a creative LLM could spawn infinite sub-tasks. This pattern turns a single-shot agent into a planner that decomposes problems. The queue is the only new data structure; everything else reuses the same loop, tools, and state from earlier. ## The whole thing is ~60 lines Agent function + tools dict + while loop + conversation list + state dict + memory file + input/output guardrails + task queue. Nine concepts. Every one is a plain data structure or control flow primitive you learned in your first year of programming. Composed together, they produce behavior identical to what you see in ChatGPT, Claude, and Cursor — tool use, multi-step reasoning, cross-session memory, content filtering, task decomposition. Every concept from LangChain, CrewAI, and AutoGen maps to one of these primitives. The total implementation is ~60 lines of plain Python. No imports beyond json and collections.deque. No framework required. Frameworks are useful when you need production infrastructure — retries, observability, deployment. But understanding what they abstract over takes less than an hour if you see the plain version first. That's the point: the architecture of AI agents is not complicated. The ecosystem makes it look complicated because complexity justifies the existence of the framework. Start with the 60 lines. Add abstractions only when you feel the pain they solve. ## Build it yourself Build it yourself in 9 interactive lessons — no install, runs in your browser. --- Source: https://tinyagents.dev/blog/how-ai-agents-work ### You Don't Need LangChain (Here's What It Actually Does) > LangChain's AgentExecutor, @tool, and ConversationBufferMemory map to a few lines of plain Python each. Here's the side-by-side breakdown. Date: 2026-03-30 · Keywords: LangChain alternative, do you need LangChain, LangChain vs plain Python, LangChain overhead, LangChain too complex, build without LangChain, LangGraph vs while loop, LangChain debugging ## The case against LangChain LangChain has over 200 classes spread across chains, agents, memory, retrievers, output parsers, and callbacks. The PyPI package pulls in dozens of dependencies. The documentation runs to hundreds of pages. And the core pattern underneath all of it — call an LLM, execute tools, loop — is a while loop with an HTTP POST. This isn't a takedown of LangChain. It's a calibration exercise. Every class in LangChain maps to something concrete in plain Python, usually something short. When you see the mapping, you can make an informed decision: does this abstraction save me time, or does it cost me clarity? The engineers who get the most out of LangChain are the ones who understand what it does before they import it. They know that AgentExecutor is a loop, that @tool is a registry entry, that ConversationBufferMemory is a list. They use LangChain because it saves them boilerplate on the parts they've already internalized — not because they need it to understand the concepts. If you've never built an agent from scratch, start there. You'll write maybe 60 lines of Python. Then, when you reach for LangChain, you'll know exactly which 60 lines it's replacing. ## AgentExecutor is a while loop LangChain's AgentExecutor is the central runtime for tool-using agents. Under the hood, it wraps LLMChain (which combines a PromptTemplate with an LLM call), an OutputParser (which extracts tool invocations from the response), and a ToolExecutor (which dispatches the calls). It manages iteration limits via max_iterations, handles parsing errors with handle_parsing_errors, and tracks intermediate steps in a list of AgentAction objects. In plain Python, the same thing is a while loop. Call the LLM API with your messages array. Check if the response contains tool_calls. If yes, execute each one, append the results as tool-role messages, and loop back. If no tool_calls, return the final response. The exit condition is identical: the LLM decides it's done by not requesting any more tools. The AgentExecutor adds real value in a few places: it handles edge cases like malformed tool calls, enforces iteration limits to prevent runaway loops, and provides callback hooks for logging. But the core loop — call, check, execute, append, repeat — is five lines of Python that any engineer can read and modify directly. Where LangChain's abstraction costs you is when the loop behavior isn't quite what you need. Want to inject a human approval step before certain tool calls? Want to retry with a different prompt if the LLM hallucinates a tool name? In AgentExecutor, you're subclassing and overriding. In plain Python, you're adding an if statement. ## @tool is a dict entry LangChain's @tool decorator does three things: it registers a Python function as a tool, generates a JSON schema from the function's type hints and docstring, and wraps the function so it can be called by the AgentExecutor's dispatch loop. Behind the scenes, it creates a StructuredTool instance with a name, description, and args_schema derived from the function signature. In plain Python, the equivalent is a dictionary and a list. The dictionary maps tool names to callables: tools = {"add": add, "search": search}. The list contains the JSON schema definitions that get sent to the LLM API in the tools parameter. You write the schema by hand — or, if you want to be clever, generate it from type hints with a 10-line helper function. The @tool decorator's main convenience is the automatic schema generation. If you have 20 tools with complex argument types, writing schemas by hand gets tedious. LangChain also handles type coercion — if the LLM returns a string "42" for an int parameter, @tool will cast it. In plain Python, you handle this yourself (or let it fail loudly, which is often preferable for debugging). For most agents with 3-5 tools, the manual approach takes about the same time and produces code that's transparent. You can see exactly what schema the LLM receives, which matters when the LLM misuses a tool — you debug the schema, not the decorator's introspection logic. For large tool registries with dozens of functions, LangChain's automation is genuinely helpful. ## ConversationBufferMemory is a list LangChain offers a family of memory classes that manage conversation history. ConversationBufferMemory stores every message. ConversationBufferWindowMemory keeps only the last k exchanges. ConversationSummaryMemory uses an LLM to compress old messages into a summary. ConversationSummaryBufferMemory combines both — recent messages in full, older ones summarized. Each plugs into the chain via a memory parameter. In plain Python, ConversationBufferMemory is a list. Literally: messages = []. You append user messages before the agent loop and assistant messages after. The list lives outside the function, so it persists across calls. That's the entire implementation of "memory" — a list that doesn't get cleared between turns. ConversationBufferWindowMemory is messages[-k:]. ConversationSummaryMemory is an extra LLM call that takes the oldest messages, asks the LLM to summarize them, and replaces them with a single system message. You can implement any of these patterns in 5-15 lines of Python, and you can see exactly what's happening to your context window. Where LangChain's memory classes genuinely help is when you need to swap strategies without changing your chain code. The unified interface means you can go from buffer to summary memory by changing one parameter. In plain Python, you'd refactor the message management logic. For applications that experiment heavily with memory strategies, the abstraction pays for itself. For most agents, a list with a length check is all you need. ## LangGraph vs a while loop LangGraph is LangChain's answer to stateful agent workflows. It models agent logic as a directed graph: you define nodes (functions), edges (transitions), and a shared state object with typed channels. A StateGraph compiles into a runnable that manages state transitions, supports branching, cycles, and even human-in-the-loop interrupts via checkpointers. In plain Python, the equivalent for most agent workflows is a while loop with a state dict. state = {"messages": [], "step": "plan"}. The loop body checks the current step, calls the appropriate function, updates state, and continues. For linear workflows — plan, then execute, then summarize — this is identical in behavior but dramatically simpler in code. LangGraph adds real value for complex branching workflows. If your agent needs to route to different subgraphs based on classification, run parallel tool chains that merge results, or checkpoint state for long-running workflows that survive process restarts — LangGraph's graph model is a genuine improvement over hand-rolled state machines. The typed channels catch state mutation bugs at compile time. But for the majority of agent use cases — a loop that calls tools until the LLM is done — LangGraph is a while loop with extra steps. The StateGraph definition, the node registration, the edge configuration, and the compilation step add ceremony without adding capability. If your agent's flow chart is a single loop with an exit condition, a while loop is the honest representation. Reach for LangGraph when your flow chart actually has branches. ## The debugging tax When something breaks in a LangChain agent at 2 AM, you debug through layers. The error traceback walks you through Chain, then AgentExecutor, then LLMChain, then PromptTemplate, then OutputParser, then maybe a CallbackManager. Each layer has its own state, its own error handling, and its own assumptions about what the layers above and below are doing. You're not debugging your logic — you're debugging the framework's interpretation of your logic. In plain Python, you read your 60 lines. The API call is right there. The tool dispatch is right there. The message array is right there. You can print any variable at any point without wondering if a callback will intercept it or if some middleware is transforming the data between layers. The abstraction cost isn't in performance — LangChain doesn't meaningfully slow down your agent. The cost is in comprehension. Every layer between you and the API call is a layer you have to understand when things go wrong. And things always go wrong in production: the LLM returns an unexpected format, a tool raises an exception, the context window fills up. The question is whether you can diagnose the problem in minutes or hours. This matters most for teams. A single developer who wrote the LangChain integration understands the layers. But when a different engineer gets paged at 2 AM, they're learning the framework's internals under pressure. Plain Python has no hidden layers — the code is the documentation. LangChain is worth the debugging tax when its features save you more time than its layers cost you. Be honest about that tradeoff for your team. ## When LangChain actually helps LangChain earns its complexity in specific, real scenarios. If you integrate with multiple LLM providers — OpenAI, Anthropic, Google, Cohere — and want a unified interface so you can swap models without rewriting API calls, LangChain's provider abstraction saves real work. Each provider has different request formats, streaming protocols, and error shapes. LangChain normalizes all of it. Production-grade RAG (retrieval-augmented generation) is another genuine strength. LangChain's vector store integrations cover Pinecone, Weaviate, Chroma, pgvector, and dozens more. The document loaders handle PDFs, HTML, CSVs, and Notion pages. Building this from scratch is hundreds of lines of glue code that LangChain has already written and battle-tested. LangSmith, LangChain's observability platform, is arguably the strongest reason to buy into the ecosystem. It traces every LLM call, tool execution, and chain step with latency and token counts. Building equivalent observability from scratch is a significant investment. If you need production tracing, LangSmith is hard to beat. For learning, prototyping, or building agents with a single LLM provider and a handful of tools — plain Python is clearer, faster to debug, and easier for your team to understand. The right question isn't "should I use LangChain?" It's "which specific LangChain features would save me more time than they cost in complexity?" If you can name them, use LangChain. If you can't, you probably don't need it yet. ## Build it yourself See every LangChain concept rebuilt from scratch in 9 interactive Python lessons. --- Source: https://tinyagents.dev/blog/langchain-vs-plain-python ### LLM Tool Calling Explained (With Python Code) > Tool calling lets LLMs use functions. The LLM returns a structured request, your code executes it. Here's exactly how it works with the OpenAI API. Date: 2026-03-30 · Keywords: LLM tool calling, function calling LLM, OpenAI tool calling, tool calling tutorial, how tool calling works, function calling Python ## What tool calling is LLMs generate text. That is all they do. They cannot run code, browse the web, query databases, or call APIs. But they can produce a structured JSON object that says "call the add function with a=10 and b=5." Your code receives that JSON, executes the actual function, and sends the result back to the LLM. The LLM sees the result and decides what to do next — call another function, or respond to the user. This is tool calling, also called function calling. The name varies by provider (OpenAI calls it "tool calling," Anthropic calls it "tool use," Google calls it "function calling"), but the mechanic is identical everywhere. The LLM never executes anything. It requests execution. Your code is the runtime. The mental model that helps most engineers: think of the LLM as a dispatcher. It looks at the user's request, looks at the available tools, and emits a structured dispatch command. Your application receives that command, routes it to the right function, runs it, and returns the output. The LLM is the brain; your code is the hands. This separation is what makes tool calling safe and auditable — you control every function the LLM can invoke, and you see every argument it passes before execution happens. ## The JSON schema contract Tool calling works because of a structured contract between you and the LLM. When you make an API request, you include a tools array that describes every function the LLM is allowed to call. Each tool definition follows a specific format: {"type": "function", "function": {"name": "add", "description": "Add two numbers", "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]}}}. The schema uses standard JSON Schema syntax — the same spec used in OpenAPI and many other places. The LLM reads these schemas and returns a JSON object that conforms to them. No parsing, no regex, no string extraction — structured in, structured out. This is why tool calling replaced the old approach of prompting the LLM to emit text in a particular format and then parsing it with regex or string splitting. That old approach was fragile. The LLM might add extra whitespace, wrap the output in markdown code fences, or rephrase the function name. With tool calling, the provider's API guarantees the response is valid JSON matching your schema. The description field matters more than most engineers expect. The LLM uses it to decide when to call the tool and what arguments to pass. A vague description like "does math" will produce worse results than "Adds two numbers and returns their sum. Use when the user asks for addition." Write descriptions as if you are writing docs for a junior developer who has never seen your codebase. ## How it works at the API level Here is the exact sequence of HTTP requests and responses. First, you send a chat completion request with two key fields: messages (the conversation so far) and tools (the array of function schemas). The LLM processes both and returns a response. If the LLM decides to call a tool, the response message has a tool_calls array instead of plain text content. Each entry in tool_calls contains an id (a unique identifier for this call), a function name, and a JSON string of arguments. Your code parses the arguments, executes the function, and makes a second API request. This request appends two new messages to the conversation: (1) the assistant's message containing the tool_calls (exactly as the API returned it), and (2) a message with role: "tool", the tool_call_id linking it to the original request, and the function's return value as content. The LLM receives this updated conversation and generates its next response — which might be a text reply to the user, or another tool call. The tool_call_id is critical. When the LLM requests multiple tools in parallel (which both OpenAI and Anthropic support), you need to match each result to the correct request. Without the ID, the LLM would not know which result belongs to which call. This is also why the assistant message with tool_calls must be included verbatim — the API uses it to maintain conversation coherence. ## The dispatch pattern Once you understand the API mechanics, the implementation pattern is almost anticlimactic. Tool dispatch is a dictionary lookup: `tools[name](**args)`. You define your functions in a Python dict, look up by the name the LLM returned, and call the function with the arguments the LLM provided. Three lines of code. This is the same pattern as an Express.js router mapping paths to handlers, a Redux reducer mapping action types to state transitions, or a command dispatcher mapping command names to handler functions. It is one of the oldest patterns in software engineering. LangChain's @tool decorator builds this dict for you. CrewAI's tool registration does the same thing. AutoGen's function map is the same thing. They all resolve to a dictionary of name-to-function mappings. The practical implication: you do not need a framework to do tool dispatch. A plain Python dict is sufficient for most use cases. Where frameworks add value is in the surrounding infrastructure — argument validation, error handling, retry logic, and observability. But the core dispatch? It is a dict lookup. Knowing this helps you evaluate whether a framework's tool abstraction is giving you something you need, or wrapping something trivial in unnecessary complexity. If your agent has three tools, a dict is fine. If it has thirty tools with complex validation rules and shared state, a framework's tool registry might earn its keep. ## Multi-step tool use The real power of tool calling is not a single function invocation — it is the loop. The LLM calls a tool, sees the result, reasons about it, and decides whether to call another tool or respond to the user. This is the agent loop: a while loop that runs until the LLM stops requesting tools. Consider a concrete example. A user asks "What is the weather in Tokyo and New York?" The LLM sees it has a get_weather tool. It emits two parallel tool calls: get_weather(city="Tokyo") and get_weather(city="New York"). Your code executes both, sends the results back. The LLM sees both results and composes a natural language response comparing the two cities. Three API round-trips: the initial request, the tool results, and the final response. Now consider a more complex case. A user asks "Find the cheapest flight from SFO to NRT next Tuesday." The LLM calls search_flights, gets a list of options, calls get_flight_details on the cheapest one, sees the result has a layover, calls get_layover_info, and finally responds with a complete summary. Each step depends on the previous result. The LLM is driving the control flow, but your code executes every step. Claude searching your codebase works exactly this way — search, read a file, search again, read another file, synthesize. The loop is the agent. Everything else is plumbing. ## Build it yourself Build tool calling from scratch in an interactive Python lesson — no install required. --- Source: https://tinyagents.dev/blog/what-is-tool-calling ### Build vs Buy: When to Use an AI Agent Framework > Should you use LangChain, CrewAI, or build from scratch? A practical decision framework based on what these tools actually do under the hood. Date: 2026-03-30 · Keywords: build vs buy AI agent, should I use LangChain, AI agent framework decision, build or buy LLM, LangChain worth it, CrewAI worth it, custom AI agent, agent framework overhead ## The build-vs-buy question for AI agents Every team building with LLMs faces this decision: do you use a framework like LangChain, CrewAI, or AutoGen — or write it yourself? The marketing says frameworks save weeks of development time. The Reddit threads say they add complexity that takes weeks to debug. Both are right, depending on context. The confusion exists because "AI agent framework" covers a huge range. Some frameworks are thin wrappers around API calls. Others are opinionated orchestration systems with their own execution model, memory layer, and deployment story. Choosing between them requires knowing what you are actually building. A single agent that calls GPT-4 with three tools and returns structured JSON is a fundamentally different engineering problem than a multi-agent system with shared memory, human-in-the-loop approval, and dynamic tool registration. The first is an afternoon of work. The second might justify a framework — or might justify a custom solution that fits your exact requirements without the framework's opinions about the parts you do not need. ## What frameworks actually give you Frameworks provide three categories of value. First: wiring. They connect your code to LLM providers, vector stores, and external APIs through unified interfaces. LangChain supports swapping between OpenAI, Anthropic, Google, and local models by changing one parameter. If you genuinely need multi-provider support — not "might need someday" but "we switch between providers based on cost and latency in production" — this saves real engineering time. The provider APIs are similar but not identical, and edge cases around streaming, tool calling formats, and error handling differ in ways that are tedious to abstract yourself. Second: patterns. Pre-built implementations of the agent loop, tool calling, memory management, and guardrails. This is where most teams get burned. The agent loop is a while loop. Tool calling is a dict lookup. Conversation memory is a list. These patterns are simple enough that the framework's abstraction often costs more than it saves — you spend time learning the framework's API for something you could write in twenty lines. Third: ecosystem. Observability tools like LangSmith, deployment platforms like LangServe, and community recipes. This is real value, but it creates lock-in. Once your traces live in LangSmith and your deployment runs on LangServe, switching costs are high. Evaluate whether you want that coupling before you adopt it. ## The hidden cost of framework abstractions LangChain's AgentExecutor is a while loop that calls the LLM, checks for tool requests, dispatches them, and repeats. Its ConversationBufferMemory is a Python list that appends messages. Its @tool decorator registers a function in a dictionary. These are not complex patterns. When you wrap them in framework abstractions, you inherit specific costs that compound over time. First, you get the framework's error messages instead of Python's. When a tool call fails inside AgentExecutor, the stack trace passes through multiple framework layers before reaching your code. Debugging goes from "read the traceback" to "search the docs for which internal class raised this exception." Second, you inherit the framework's update cycle. LangChain has shipped breaking changes in minor versions. Your agent that worked on Monday breaks on Wednesday because a dependency updated. Third, you inherit the framework's opinions about patterns you might want differently. Want to add custom retry logic between tool calls? You need to subclass or monkey-patch rather than editing your own while loop. The practical test: if you cannot explain what the framework is doing at the API level — what HTTP requests it sends, what JSON it constructs — you do not understand your own system. That is a liability in production. ## When to build from scratch Build from scratch when the thing you are building is simpler than the framework's simplest example. Your agent calls GPT-4 with three tools and returns structured JSON. You do not need fourteen pip install dependencies for that. You need the openai package, a dict of functions, and a while loop. Build from scratch when you are prototyping and need to move fast. Learning a framework's API, understanding its abstractions, and debugging its error messages is overhead that slows down the iteration cycle when you are still figuring out what your agent should do. Write the raw API calls, get the behavior right, then decide if a framework helps. Build from scratch when your team needs to understand and debug every line. In production, the agent that breaks at 2 AM needs to be fixable by whoever is on call. If that person has to read LangChain source code to understand why the agent is stuck in a loop, you have a problem. Build from scratch when you are building something the framework was not designed for. Custom control flow, non-standard memory patterns, or domain-specific tool orchestration often fight the framework's assumptions rather than benefiting from them. The core agent pattern — tool calling, the loop, conversation history, memory — is roughly sixty lines of Python. That is less code than most framework quickstart tutorials. ## When to use a framework Use a framework when the integrations justify the dependency. If you need a RAG pipeline with PDF chunking, embedding generation via OpenAI's API, storage in Pinecone, and retrieval with reranking via Cohere — LangChain's document loaders, text splitters, and retriever abstractions save you from writing and maintaining each integration yourself. That is four external services with four different APIs, authentication schemes, and error modes. The framework has already handled the edge cases. Use a framework when you need production-grade observability and your team does not want to build it. LangSmith gives you trace visualization, cost tracking, and evaluation harnesses out of the box. Building equivalent tooling yourself is a significant engineering investment. If observability is a hard requirement and you are not a platform team, this is real value. Use a framework when you are building complex multi-agent workflows. If you have agents that delegate to other agents, share state through a blackboard pattern, and need human-in-the-loop approval at specific steps — AutoGen and CrewAI have battle-tested implementations of these patterns. Reimplementing agent-to-agent communication, shared memory coordination, and workflow orchestration from scratch is a project in itself. Use a framework when your team already knows it and the abstractions match your mental model. Familiarity has real value. A team that ships with LangChain in a week beats a team that spends two weeks building from scratch, even if the custom solution is technically cleaner. ## The best approach: understand first, then decide The teams that succeed with AI agents — whether using frameworks or not — are the ones that understand the fundamentals before choosing their tools. If you know that an agent is a function that calls an LLM in a loop, that tools are a dictionary mapping names to functions, and that memory is a list of messages you manage yourself, you can evaluate any framework honestly. You will know what LangChain's AgentExecutor is doing because you have written the same while loop yourself. You will know whether CrewAI's task delegation adds value for your use case because you understand the underlying message-passing pattern. You will know when AutoGen's conversation patterns are worth the abstraction because you have implemented simpler versions by hand. This is not an argument against frameworks. It is an argument against adopting them before you understand what they abstract. The build-vs-buy decision is easy once you know what you are buying. If the framework saves you from writing and maintaining code you understand but do not want to own, that is a good trade. If the framework hides complexity you do not understand, that is technical debt disguised as productivity. The first step is always the same: understand the fundamentals, then decide. ## Build it yourself Understand the fundamentals first. Build every agent concept from scratch in 9 interactive Python lessons. --- Source: https://tinyagents.dev/blog/build-vs-buy-ai-agent-framework ### tinyagents.dev vs HuggingFace tiny-agents — Course or MCP Library? > Two different projects share the name. HuggingFace tiny-agents is an MCP library in JavaScript. tinyagents.dev (A Tour of Agents) is a Python course teaching agent fundamentals in 9 interactive lessons. Here's how to tell them apart and when to use each. Date: 2026-03-31 · Keywords: tinyagents.dev, tiny agents, huggingface tiny agents, huggingface tiny-agents, tinyagents python, tinyagents course, MCP agent, tiny agents vs, build agent from scratch, 50 lines agent, 60 lines agent, MCP tool calling, minimal AI agent, agent from scratch Python ## Did you mean the library or the course? If you searched for "tiny agents" you might have landed on either of two different projects. HuggingFace tiny-agents (huggingface.co/blog/tiny-agents) is a JavaScript MCP library you import into Node.js projects — roughly 50 lines, designed for shipping production agents. tinyagents.dev (this site, also called A Tour of Agents) is an interactive Python course — 9 browser-based lessons that teach agent internals in roughly 60 lines of plain Python. Same name, different artifacts. If you want a library to install and run, you want the HuggingFace one. If you want to understand what any agent framework actually does under the hood — including HuggingFace's — you want the course. Both believe the same thing: agents are simpler than frameworks make them look. Both prove it with line counts. The rest of this post explains where each fits. ## What HuggingFace Tiny Agents does Tiny Agents connects LLMs to MCP (Model Context Protocol) servers. MCP is an open standard for exposing tools to language models — think of it as a USB-C for AI tools. Instead of writing JSON schemas for each function, you point the agent at an MCP server and it auto-discovers available tools at startup. The library handles the full agent loop: send messages to the LLM, parse tool_calls from the response, route each call to the right MCP server, collect results, and loop back. It supports streaming, multi-turn conversations, and multiple tool servers simultaneously. Built in TypeScript, it runs on Node.js and is designed for production MCP workflows. The HuggingFace co-founder who built it described the motivation as making local tool calling "remarkably smooth" — and it delivers on that promise. ## What MCP changes about agent architecture Model Context Protocol matters because it standardizes the tool interface. Before MCP, every agent framework invented its own way to define and register tools — LangChain's @tool decorator, CrewAI's Tool class, AutoGen's register_for_llm(). MCP replaces all of these with a single protocol: tools are servers that expose JSON-RPC endpoints. Any MCP client (including Tiny Agents) can connect to any MCP server. This is why HuggingFace's 50-line agent can access filesystem tools, web search, databases, and more — without importing a library for each one. The agent doesn't know about file systems or HTTP. It knows about MCP. The plain Python equivalent: a tools dict where each value is a function. MCP adds network-level interoperability (tools can run anywhere), but the dispatch pattern is identical: look up the tool by name, call it with arguments, return the result. ## What tinyagents.dev (A Tour of Agents) teaches tinyagents.dev doesn't give you a library to import. It teaches you to build the same patterns from scratch, one concept at a time. Lesson 1: an agent is a function that sends an HTTP POST and returns the response. Lesson 2: tools are a dictionary of callables. Lesson 3: the agent loop is a while loop — call LLM, check for tool_calls, execute, repeat. Then conversation (a list), state (a dict), memory (a dict in the system prompt), guardrails (input/output gate functions), and self-scheduling (a task queue). By lesson 9, all eight concepts compose into roughly 60 lines of plain Python. No framework, no dependencies beyond json and pyfetch. Everything runs in your browser via Pyodide — Python compiled to WebAssembly. The goal isn't a package you install. It's knowledge you keep. When you've built the agent loop yourself, you understand what every framework does — including HuggingFace tiny-agents. ## Library vs understanding Use HuggingFace Tiny Agents when you need an MCP agent running in production today. It handles the wiring — MCP server discovery, tool routing, streaming, multi-turn state — so you can focus on your application logic. The library is well-designed and the MCP ecosystem is growing fast. Use A Tour of Agents when you want to understand what any agent framework does under the hood before committing to it. When you know that tool dispatch is a dict lookup, that the agent loop is a while loop, and that memory is a dict injected into the system prompt, you can evaluate Tiny Agents (or LangChain, or CrewAI) honestly. You'll know what the abstraction gives you and what it costs. These two projects aren't competitors. They're complementary. Tiny Agents answers the question "how do I ship this?" A Tour of Agents answers "why does this work?" The first helps you build. The second helps you debug, evaluate, and make architecture decisions. The engineers who do both are the ones who build systems that last. ## The minimal agent movement HuggingFace Tiny Agents, A Tour of Agents, Simon Willison's ReAct TIL, Anthropic's "building effective agents" guide — there's a pattern here. The best minds in AI keep arriving at the same conclusion: agents are simpler than frameworks make them look. The core is always the same: a function that calls an LLM, a way to dispatch tools, and a loop that repeats until the job is done. Everything else — memory, state, guardrails, scheduling — layers on top of that foundation. The frameworks that survive will be the ones that make this foundation accessible, not the ones that hide it. If you can't explain what your agent does in 60 lines, you might not understand what it does at all. ## The 60-line thesis Every agent framework wraps the same eight concepts. An agent is a function. Tools are a dict. The loop is a while loop. Conversation is a list that grows. State is a dict updated each turn. Memory is a dict injected into the system prompt. Guardrails are gate functions on input and output. Self-scheduling is a task queue. When you see the whole thing in 60 lines of plain Python, two things happen. First, the mystery disappears — agents aren't magic, they're a pattern. Second, framework evaluation becomes trivial — you know exactly what each abstraction replaces and can judge whether the tradeoff is worth it. That's the real value of building from scratch. Not to avoid frameworks forever, but to choose them wisely. ## Build it yourself Build the agent yourself — 9 interactive lessons, no install, runs in your browser. --- Source: https://tinyagents.dev/blog/tiny-agents-huggingface-vs-from-scratch ### Why 40% of AI Agent Projects Will Fail (And How to Avoid It) > Gartner predicts 40% of agentic AI projects will be cancelled by 2027. The root cause isn't the technology — it's the abstraction layer between engineers and what's actually happening. Date: 2026-04-03 · Keywords: why AI agent projects fail, AI agent failure rate, agentic AI fail, Gartner AI agents, AI agent project cancelled, LangChain debugging, agent framework problems ## The 40% cancellation prediction Gartner predicts that 40% of agentic AI projects started in 2025 will be cancelled or restructured by 2027. That is not a throwaway stat from a niche analyst — it is Gartner's top strategic prediction for AI. RAND Corporation puts it harsher: 80-90% of AI pilot projects fail before reaching production. TechRadar reports "more than half" of enterprise AI initiatives get shelved. The pattern is consistent across every research firm tracking the space. This is not a technology problem. LLMs work. Tool calling works. The APIs are stable and well-documented. OpenAI, Anthropic, and Google have all shipped reliable function-calling interfaces. The infrastructure is mature enough for production use. So why are projects failing at rates that would be alarming in any other software category? The answer is not in the models or the APIs. It is in the layer between the technology and the teams building on it — the abstraction layer that promises to make agent development faster but instead makes it opaque. The tooling meant to accelerate delivery is the primary source of the failures. ## The abstraction trap Here is how most AI agent projects start: a team installs LangChain or CrewAI because it is "the standard." The quickstart works in an afternoon. The demo impresses stakeholders. Then the agent goes to staging, hits real data, and breaks. The tool calls return unexpected formats. The loop gets stuck retrying a hallucinated function name. The memory fills up and the model starts ignoring the system prompt. Now the team needs to debug. But they cannot debug because they never learned what AgentExecutor actually does. They do not know that under the hood, it is a while loop that checks for tool_calls in the LLM response. They do not know that the memory is a list that grows until it exceeds the context window. They do not know that the tool dispatch is a dictionary lookup that fails silently when the key does not match. The framework is a black box. When something fails at 2 AM in production, the on-call engineer is reading framework documentation instead of understanding the five-line pattern underneath. The abstraction that was supposed to save time becomes the thing that costs the most time. ## What is actually under the hood Every AI agent — whether built with LangChain, CrewAI, AutoGen, or plain Python — composes the same eight concepts. An agent is a function that calls an LLM API. Tools are a dictionary mapping string names to callable functions. The agent loop is a while loop that calls the LLM, checks for tool requests, executes them, and repeats. Conversation is a list of messages that grows with each turn. State is a dictionary updated inside the loop to track progress. Memory is a dictionary serialized into the system prompt for cross-session persistence. Guardrails are if-statements that check inputs and outputs before proceeding. Self-scheduling is a task queue that lets the agent create sub-tasks for itself. Each of these maps directly to a data structure or control flow primitive that every engineer learned in their first year of programming. Lists, dicts, while loops, if-statements, and a deque. The total implementation — all eight concepts composed into a working agent with tools, memory, guardrails, and task decomposition — is roughly sixty lines of plain Python. No imports beyond json and collections.deque. No framework required. ## The debugging cost When an agent built on LangChain fails in production, here is the debugging path: you trace through Chain, then AgentExecutor, then LLMChain, then PromptTemplate, then OutputParser. Each layer has its own exception types, its own retry logic, its own configuration options. A single failed tool call passes through five layers of abstraction before you reach the actual error. When an agent built in sixty lines of Python fails, you read sixty lines of Python. The error is in the while loop, or the tool dispatch, or the message formatting. There is nowhere to hide. This matters more than most teams realize at the start. Research shows that LLM-based systems with 85% accuracy per step achieve only a 20% success rate over ten sequential steps — 0.85 to the power of 10. Agent workflows are inherently multi-step. Every step that can fail will fail eventually. You need to understand each step well enough to diagnose whether the failure is in the LLM output, the tool execution, the prompt construction, or the loop logic. Abstraction layers do not remove the failure modes. They just make them harder to find. ## The other failure modes Abstraction blindness is not the only reason agent projects fail. Gartner separately reports that 60% of AI projects are abandoned due to data quality issues — the agent works but the data it operates on is inconsistent, incomplete, or wrong. Plenty of projects fail because teams do not translate business logic into agent behavior correctly: the agent can call the tools but does not know when to call which tool in what order. Governance gaps kill projects too — no audit trail, no human-in-the-loop approval for high-stakes decisions, no way to explain why the agent did what it did. These are all real and serious failure modes. But the abstraction problem amplifies every one of them. When you do not understand the agent loop, you cannot diagnose whether a failure is a data problem, a tool problem, a prompt problem, or a loop problem. You cannot tell if the agent is stuck because the LLM is hallucinating tool names, because the tool returned bad data, or because the loop hit max iterations without completing. Every debugging session starts with the same question: what is actually happening? Abstractions you do not understand make that question harder to answer. ## What successful teams do differently The teams that ship AI agents to production and keep them running share a common trait: they understand the fundamentals before choosing their abstractions. They can draw the agent loop on a whiteboard — while True, call LLM, check tool_calls, execute, append, repeat. They know that memory is a dict, that tools are a dict, that conversation is a list. They know the exact HTTP request their agent sends to the LLM API and the exact JSON structure that comes back. Then they choose frameworks deliberately. They adopt LangChain for its retrieval integrations, not because it is the default. They use CrewAI when they genuinely need role-based multi-agent delegation, not because the quickstart was impressive. They use AutoGen when they need structured multi-agent communication patterns, not because it is from Microsoft. And when none of those fit — which is more often than framework marketing suggests — they build with plain Python because they know the core pattern is sixty lines. The framework decision becomes a build-vs-buy calculation based on specific integration needs, not a default choice driven by unfamiliarity with the underlying pattern. ## Start with 60 lines If your team is starting an AI agent project, do this first: build the agent from scratch. Not as a production system — as a learning exercise. Write the function that calls the LLM API. Build the tools dictionary. Implement the while loop. Add conversation history. Wire up memory. Add guardrails. Build the task queue. It takes about thirty minutes if you have an LLM API key. After that, you will understand what every framework does. You will know exactly what LangChain's AgentExecutor abstracts — and what it costs in debuggability. You will know whether CrewAI's multi-agent patterns solve a problem you actually have. You will be able to make an informed decision about whether to build or buy, and what specifically you are buying. The 40% of projects that Gartner predicts will fail are not failing because the technology is immature. They are failing because teams are building on abstractions they do not understand. The fix is simple: understand first, abstract later. Start with sixty lines. Then decide what you need on top of them. ## Build it yourself Build the agent from scratch in 30 minutes — then decide if you need a framework. --- Source: https://tinyagents.dev/blog/why-ai-agent-projects-fail ### The Agent Loop Explained: The 5-Line Pattern Behind Every AI Agent > The agent loop is a while loop. Call the LLM, check for tool calls, execute them, repeat. Here's the exact pattern that LangChain's AgentExecutor, CrewAI, and AutoGen all implement. Date: 2026-04-03 · Keywords: agent loop, agent loop explained, ReAct pattern, ReAct agent Python, LangChain AgentExecutor, AI agent while loop, tool calling loop, how AI agents work loop ## The loop in 5 lines Here is the entire agent loop, the pattern that powers every AI agent you have ever used: ```python while True: response = llm(messages, tools) if not response.tool_calls: break for tc in response.tool_calls: result = tools[tc.name](**tc.args) messages.append(tool_result(tc.id, result)) ``` Call the LLM with the conversation history and available tools. If the response contains no tool calls, the agent is done — break and return the response to the user. If there are tool calls, execute each one by looking up the function name in a dictionary and calling it with the provided arguments. Append each result to the messages list so the LLM can see what happened. Then loop back and call the LLM again with the updated history. That is it. Five lines of logic. Everything else — every framework, every orchestration library, every "agentic AI platform" — is built on top of this pattern. LangChain's AgentExecutor is this loop with error handling and callbacks. CrewAI's task execution is this loop with role-based prompts. AutoGen's conversation patterns are this loop distributed across multiple agents. The loop is the atom. Everything else is molecules. ## What you see when Claude searches your codebase When you ask Claude to find a bug in your project and it reads a file, then searches for related functions, then reads three more files, then finally explains the issue — you are watching the agent loop iterate. Each iteration follows the same pattern. Claude receives the conversation so far, including the results of any previous tool calls. It decides whether to use a tool or respond directly. If it chooses a tool — say, reading a file — the tool executes, the result is appended to the messages, and Claude is called again with the updated context. The "Analyzing..." indicator you see in ChatGPT while it processes a complex request is the same loop. Each time it appears and disappears, one iteration of the loop has completed: the model was called, it requested a tool (web search, code execution, file reading), the tool ran, and the result was fed back. When the model finally generates a text response without requesting any tools, the loop exits and you see the answer. Cursor's agent mode works identically — each file read, each terminal command, each edit is one iteration of the while loop. The agent decides, acts, observes, and decides again. ## The ReAct pattern The academic name for this loop is ReAct — Reasoning plus Acting. The paper by Yao et al. (2022) formalized the intuition: let the LLM reason about what to do, take an action, observe the result, and repeat. Think, Act, Observe, Repeat. The "Think" step is the LLM generating its internal reasoning about the current situation. The "Act" step is the LLM requesting a tool call. The "Observe" step is the tool result being appended to the conversation history. The "Repeat" is the while loop going back to the top. Our five-line while loop is a ReAct agent. The LLM reasons when it generates the response — it is deciding whether to call a tool and which one. It acts when it returns a tool_calls array. It observes when the tool results are added to messages and it sees them on the next iteration. The paper showed that interleaving reasoning and acting outperforms pure reasoning (chain-of-thought without tools) and pure acting (tool calls without reasoning). This is why modern LLMs generate text alongside their tool calls — the reasoning improves tool selection. The while loop captures all of this in five lines because the LLM handles the reasoning internally. ## What frameworks add on top The core loop is five lines. LangChain's AgentExecutor adds roughly two thousand lines on top. What do those lines do? First: max_iterations. A guard that stops the loop after N iterations so a confused model cannot run forever. In plain Python, this is "for i in range(max_turns)" instead of "while True." One line. Second: error handling. If a tool call fails, AgentExecutor catches the exception and feeds the error message back to the LLM so it can retry or choose a different approach. In plain Python, this is a try/except inside the tool dispatch. Three lines. Third: callbacks. Hooks that fire before and after each LLM call and each tool execution, used for logging, tracing, and observability. In plain Python, this is a print statement or a logging call. One line per hook point. Fourth: output parsing. Converting the LLM's response into a structured format. With modern function-calling APIs, the LLM returns structured JSON directly — output parsing is largely unnecessary. CrewAI adds role-based prompting and agent delegation. AutoGen adds multi-agent message routing. These are useful features for specific use cases. But none of them change the core pattern. They are all additions to the same while loop. ## Why the loop matters more than the framework Understanding the agent loop gives you four capabilities that no framework can provide. First: you can debug any agent, in any framework. When an agent gets stuck, it is always the loop. The LLM is either requesting the wrong tool, receiving confusing tool results, or looping without making progress. If you understand the loop, you know exactly where to look — check the messages array, check the tool dispatch, check the response. Second: you can build custom agents without a framework. Most agent use cases are simple enough that the five-line loop plus a tools dictionary is all you need. No dependencies, no version conflicts, no framework upgrade migrations. Third: you can evaluate frameworks honestly. When you understand that AgentExecutor is a while loop with error handling and callbacks, you can decide whether those additions are worth the dependency. Sometimes they are. Often they are not. Fourth: you can diagnose infinite loops. When an LLM gets stuck calling the same tool repeatedly with the same arguments, or hallucinating tool names that do not exist, or exceeding max iterations without completing — you know exactly what is happening because you understand the loop mechanics. The loop is the foundation. Everything else is furniture. ## Build it yourself A Tour of Agents is a free interactive course that teaches you to build the agent loop — and everything around it — from scratch in your browser. No install, no API key required (mock responses included), nine lessons that take you from "an agent is a function" through tools, the loop, conversation, state, memory, guardrails, and self-scheduling. Lesson 3 is specifically the agent loop. You will write the while loop, implement tool dispatch, handle the exit condition, and watch the agent iterate through a multi-step task in real time. By the end of that lesson, you will understand what every framework does at the core — because you will have built it yourself. The full course composes all nine concepts into a working agent in roughly sixty lines of Python. Every line is code you wrote and understand. From that foundation, you can evaluate whether LangChain, CrewAI, AutoGen, or plain Python is the right choice for your project. The decision becomes simple when you know what you are deciding between. The loop is where it all starts. ## Build it yourself Build the agent loop from scratch in your browser — Lesson 3 of A Tour of Agents. --- Source: https://tinyagents.dev/blog/what-is-the-agent-loop ### Every AI Agent Framework Ranked by Real Data (April 2026) > We pulled GitHub stars, forks, PyPI downloads, funding data, and creator profiles for 20 AI agent frameworks. Here's what the numbers actually say — and what they don't. Date: 2026-04-04 · Keywords: AI agent framework comparison, best AI agent framework 2026, LangChain vs CrewAI vs AutoGen, AI agent framework ranking, which AI agent framework, agent framework stars, agent framework downloads ## 944,617 stars and counting We pulled real data from the GitHub API and PyPI for every notable AI agent framework. The totals: 20 frameworks, 944,617 GitHub stars, 184,904 forks. Nearly a million stars across projects that all solve roughly the same problem — getting an LLM to use tools in a loop. The AI agent framework gold rush is real, and it is accelerating. In 2023, there were maybe five serious contenders. By April 2026, there are twenty, backed by some of the largest companies and most prolific investors in tech. Microsoft has two entries. Google shipped one. OpenAI and Anthropic each released their own SDKs. Stanford has one. Hugging Face has one. YC-backed startups have several. Every major AI lab and every well-funded startup has decided that the agent framework layer is strategic. The question is no longer "should I use an agent framework?" — it is "which of these twenty frameworks deserves my time?" This post answers that question with data, not opinions. We ranked every framework by GitHub stars, PyPI weekly downloads, funding, creator profile, and actual usage patterns. The numbers tell a story that the marketing pages do not. ## Stars lie. Downloads don't. The most important insight in this data is the gap between stars and downloads. AutoGPT has 183,111 GitHub stars — the most of any framework on this list. But it does not even appear in the PyPI top 12 for weekly downloads. It went viral in March 2023, collected mass attention, and then usage never caught up. Pydantic AI tells the opposite story: only 16,091 stars, but 3,836,560 weekly PyPI downloads — the second-highest of any framework. Why? Because it ships as part of the Pydantic ecosystem. Every team already using Pydantic for data validation gets agent capabilities for free. Stars measure hype. Downloads measure adoption. They are different things, and conflating them leads to bad framework decisions. LangChain is the only framework that dominates both metrics: 132,287 stars and 53,036,611 weekly downloads. That download number is not a typo — it is 14x more than the second-place framework. Whatever you think of LangChain's developer experience, the market has spoken. It is the default, and defaults are hard to displace. The lesson: never evaluate a framework by its GitHub stars alone. Check PyPI. Check npm. Check who is actually importing it into production code, not who clicked a button on GitHub. ## The Big Three: LangChain, CrewAI, AutoGen Three frameworks define the current landscape. LangChain is the incumbent. Founded by Harrison Chase, backed by Sequoia and Benchmark, with $50M raised, it has the deepest integration ecosystem: vector stores, retrievers, document loaders, output parsers. Its 132k stars and 53M weekly downloads make it the gravitational center of the space. If you need a production RAG pipeline today, LangChain is the path of least resistance. CrewAI is the challenger. Created by Joao Moura, YC-backed, it grew faster than any other framework in 2024. Its pitch is role-based multi-agent orchestration — you define agents with roles, goals, and backstories, then let them collaborate. At 47,976 stars and 1.37M weekly downloads, it owns the multi-agent narrative. AutoGen is Microsoft Research's bet. At 56,671 stars it outranks CrewAI on GitHub, but its 36,258 weekly PyPI downloads tell a different story. The 0.4 rewrite broke backward compatibility and fractured the community. Stars accumulated during the hype cycle; downloads reflect the aftermath. AutoGen is a cautionary tale about what happens when a major rewrite alienates early adopters. The Big Three together account for roughly 60M of the 63M weekly downloads tracked across all twenty frameworks. Everyone else is fighting over the remaining 5%. ## The new wave: Agno, DSPy, Smolagents, Pydantic AI Four frameworks represent the next generation, each with a genuinely distinct approach. Agno (formerly Phidata) at 39,153 stars positions itself as the lightweight alternative — fewer abstractions, faster startup, less magic. It has 361,453 weekly downloads and growing. DSPy from Stanford NLP, created by Omar Khattab, is the most intellectually novel framework on this list. Instead of writing prompts, you define input-output signatures and let DSPy compile optimized prompts automatically. At 33,423 stars and 1.19M weekly downloads, it has crossed from research project to production tool. Smolagents from Hugging Face takes a different angle entirely. Instead of generating JSON tool calls, agents write and execute Python code directly. At 26,425 stars and 112,610 weekly downloads, it is still early but the code-execution approach solves real problems with tool-call formatting that plague other frameworks. Pydantic AI is the sleeper hit. Samuel Colvin and the Pydantic team built it, and at 16,091 stars it looks modest. But 3,836,560 weekly downloads make it the second most-used framework by actual installation count. The secret: if you already depend on Pydantic — and most Python web developers do — Pydantic AI is zero additional dependency risk. Distribution through an existing ecosystem beats marketing every time. ## The corporate entries: OpenAI, Google, Anthropic, Microsoft Every major AI lab now has its own agent framework, all released within the last eighteen months. OpenAI Agents SDK launched in March 2025 and already has 20,558 stars. Google ADK (Agent Development Kit) shipped in April 2025 and hit 18,734 stars in under a year. Anthropic's Agent SDK sits at 3,122 stars. Microsoft's Semantic Kernel — the oldest corporate entry at 27,636 stars — supports C#, Python, and Java, making it the only polyglot framework on this list. The pattern is obvious: each lab optimizes for its own models. OpenAI Agents SDK integrates tightly with GPT-4o function calling. Google ADK is built for Gemini. Anthropic's SDK showcases Claude's tool use. Semantic Kernel has deep Azure OpenAI integration. This is not altruism. These frameworks are distribution channels for API revenue. The more developers build on OpenAI's agent SDK, the more locked in they are to OpenAI's models. The same applies to Google and Anthropic. If you are already committed to a single model provider for your production workload, the matching corporate SDK reduces friction. But if you want model portability — the ability to swap providers without rewriting your agent — the corporate SDKs are the wrong choice. They are optimized for lock-in by design. ## The OGs: Rasa, Haystack, n8n, BabyAGI, AutoGPT Five frameworks on this list predate the current hype cycle or defined it. Rasa, founded in 2016, is the oldest framework here by seven years. It was building conversational AI agents before GPT-3 existed. At 21,114 stars and 55,939 weekly downloads, it serves a specific niche — enterprise conversational agents with on-premise deployment requirements. Haystack from deepset, launched in 2019, pioneered the pipeline-first approach to NLP applications. At 24,698 stars and 161,209 weekly downloads, it remains the go-to for teams that think in directed acyclic graphs rather than agent loops. n8n is the wildcard. At 182,384 stars it is the second-highest on the entire list, but it is a visual workflow automation tool, not an agent framework in the traditional sense. Its AI capabilities were added on top of an existing low-code platform. BabyAGI, created by Yohei Nakajima in 2023, proved that a task-driven agent could be built in roughly 100 lines of Python. At 22,208 stars, it inspired an entire generation of agent builders. AutoGPT was the viral moment — 183,111 stars accumulated in weeks during March 2023. It showed the world what autonomous agents could look like, even if the execution was unreliable. These five shaped the landscape everyone else builds on. ## What none of them tell you Here is the uncomfortable truth that no framework's landing page will mention: every single framework on this list wraps the same eight patterns. A function that calls an LLM. A dictionary that maps tool names to callable functions. A while loop that repeats until the LLM stops requesting tools. A list of messages that grows with each turn. A dictionary for tracking state within a session. A dictionary for memory that persists across sessions. Input and output validation gates. A task queue for self-scheduling sub-tasks. That is it. The entire conceptual surface area of an AI agent, implemented from scratch, is roughly sixty lines of Python. No imports beyond json and collections.deque. The frameworks add real value through integrations — vector store connectors, document loaders, observability dashboards, managed deployment. That value is legitimate and worth paying for in production. But the core agent logic is identical across all twenty frameworks. Understanding those sixty lines gives you the ability to evaluate any framework honestly. You can read LangChain's AgentExecutor source and see the while loop. You can read CrewAI's delegation logic and see the tool dictionary. You can read AutoGen's conversation patterns and see the message list. The patterns are universal. The frameworks are wrappers. ## The funding map Money shapes the agent framework landscape as much as technology does. LangChain raised $50M from Sequoia Capital and Benchmark — the same firms that backed Google, Dropbox, and Uber. That money funds LangSmith, their observability and deployment platform, which is where the actual monetization happens. The framework is free; the tooling around it is the business. CrewAI is YC-backed, which means access to the YC network for distribution and a clear path to enterprise sales. Rasa raised over $30M and built Rasa Pro, an enterprise tier with analytics, RBAC, and on-premise deployment. deepset, the company behind Haystack, raised funding and built deepset Cloud. n8n raised capital and monetizes through n8n Cloud, a hosted version of their open-source workflow tool. The pattern is consistent: open-source framework as top-of-funnel, proprietary cloud platform as the business model. This matters for your framework decision because funded frameworks have longer runways — they will not disappear tomorrow. But they also face pressure to monetize, which means the free tier gets strategically limited over time. LangSmith's pricing, Rasa Pro's feature gating, and n8n Cloud's usage limits all follow this playbook. The framework you choose for free today may have a different cost structure in eighteen months. Factor that into your evaluation. ## Which one should you use? After analyzing all twenty frameworks, here is the honest decision matrix. If you need a production RAG pipeline with mature integrations, use LangChain or LlamaIndex — they have the deepest connector ecosystems and the most battle-tested retrieval patterns. If you need multi-agent collaboration with role-based delegation, use CrewAI — it owns that pattern and the community around it is active. If you are a Microsoft or Azure shop, use Semantic Kernel or AutoGen — they integrate natively with Azure OpenAI and the Microsoft ecosystem. If you are building on Google Cloud with Gemini, use Google ADK — it was built for that stack. If you are committed to OpenAI models only, the OpenAI Agents SDK reduces friction. If you care deeply about type safety and already use Pydantic, Pydantic AI is the natural choice. If you want a no-code approach, n8n AI lets non-engineers build agent workflows visually. If you want to understand what agents actually are before committing to a framework — and the data in this post should convince you that understanding matters — build one from scratch first. Sixty lines of Python. Nine lessons. No install required. Every framework on this list will make more sense after you have written the core patterns yourself. ## Or build your own The agent framework space has nearly a million GitHub stars spread across twenty projects. Hundreds of millions of dollars in venture funding. Corporate backing from Microsoft, Google, OpenAI, and Anthropic. And underneath all of it, the same sixty lines of Python. We built A Tour of Agents because we believe understanding comes before abstraction. Nine interactive lessons that run in your browser — no install, no API key required, no framework dependencies. You will build a working agent from scratch: the LLM function, the tool dictionary, the agent loop, conversation history, state management, memory, guardrails, and self-scheduling. By the end, you will have written every pattern that every framework on this list implements. You will understand what LangChain's 53 million weekly downloads are actually abstracting. You will know whether CrewAI's multi-agent patterns solve a problem you have or a problem you do not. You will be able to read any framework's source code and recognize the sixty lines underneath. The frameworks are not going away. Some of them are genuinely excellent for production use. But choosing one should be an informed decision based on specific integration needs — not a default driven by unfamiliarity with what is actually a simple pattern. Start with sixty lines. Then decide what you need on top. ## Build it yourself See all 20 frameworks reduced to 60 lines. 9 interactive lessons, no install, runs in your browser. --- Source: https://tinyagents.dev/blog/ai-agent-framework-ranking-2026 ## Framework comparisons (vs plain Python) ### LangChain vs Building from Scratch > Compare LangChain's abstractions to plain Python. See what AgentExecutor, @tool, ConversationBufferMemory, and output parsers actually do — in ~60 lines. LangChain is the most popular agent framework. It provides AgentExecutor, tool decorators, memory classes, and output parsers. But every one of these maps to a few lines of plain Python. Here's what each abstraction actually does. ## Concept map | Concept | LangChain | Plain Python | |---|---|---| | Agent | `AgentExecutor` with `LLMChain`, `PromptTemplate`, `OutputParser` | A function that POSTs to `/chat/completions` and returns the response | | Tools | `@tool` decorator, `StructuredTool`, `BaseTool` class hierarchy | A dict of callables: `tools = {"add": lambda a, b: a + b}` | | Agent Loop | `AgentExecutor.invoke()` with internal iteration | A `while` loop: call LLM, check for `tool_calls`, execute, repeat | | Conversation | `ConversationBufferMemory`, `ConversationSummaryMemory` | A `messages` list that persists outside the function | | State | LangGraph state channels with typed reducers | A dict updated inside the loop: `state["turns"] += 1` | | Memory | `VectorStoreRetrieverMemory`, `ConversationEntityMemory` | A dict injected into the system prompt, saved via a `remember()` tool | | Guardrails | `OutputParser`, `PydanticOutputParser`, custom validators | Two lists of `lambda` rules checked before and after the LLM call | ## What LangChain does LangChain provides a **unifying interface across LLM providers**, a class hierarchy for tools and memory, and orchestration via `AgentExecutor` and LangGraph. The core value proposition is **interchangeable components**: swap OpenAI for Anthropic by changing one class, plug in a vector store for retrieval, add memory without rewriting your loop. It also ships with dozens of integrations: - document loaders - text splitters - embedding models - vector stores These save you from writing boilerplate HTTP calls. For teams that need to compose many integrations quickly, this catalog is genuinely useful. The tradeoff is that you inherit a **large dependency tree** and a set of abstractions that sit between you and the actual API calls. ## The plain Python equivalent Every LangChain abstraction maps to a small piece of plain Python. `AgentExecutor` is a `while` loop that calls the LLM, checks for `tool_calls` in the response, executes the matching function from a `tools` dict, appends the result to a `messages` array, and repeats. Memory is a dict you inject into the system prompt. Output parsing is a function that validates the LLM's response before returning it. The entire agent — tool dispatch, conversation history, state tracking, guardrails — fits in about **60 lines of Python**. No base classes, no decorators, no chain composition. **Just a function, a dict, a list, and a loop.** When something breaks, you read your 60 lines instead of navigating a class hierarchy. ## When to use LangChain LangChain earns its complexity when you need **multiple integrations working together** — say, a RAG pipeline with a specific vector store, a document loader for PDFs, and a deployment target like LangServe. It also helps teams that want to swap LLM providers without rewriting business logic, or that need LangSmith's tracing and evaluation tooling. If you're building a product where the agent is one component among many integrations, LangChain's catalog saves real time. **LangGraph** adds value for complex multi-step workflows where you need conditional branching, parallel execution, and persistent state across nodes. The framework is worth it when the **integration surface area is large**. ## When plain Python is enough If your agent calls one LLM provider, uses a handful of tools, and runs a straightforward loop — **plain Python is simpler** to write, debug, and maintain. You don't need `AgentExecutor` to write a `while` loop. You don't need `ConversationBufferMemory` to append to a list. You don't need `@tool` to put a function in a dict. Most agents in production are simpler than their framework code suggests. Start with the plain version, add abstractions only when you hit a real pain point (not a hypothetical one), and you'll end up with code your team can actually read at 2 AM when something breaks. The 60-line version has **zero dependencies, zero magic, and zero surprises**. ## Verdict LangChain adds value when you need production integrations (vector stores, specific LLM providers, deployment tooling). But if you want to understand what's happening — or your use case is straightforward — the plain Python version is easier to debug, modify, and reason about. ## FAQ ### What does LangChain's AgentExecutor actually do? AgentExecutor is a while loop that calls the LLM, checks if the response contains tool_calls, executes matching tools from a registry, appends results to the messages array, and repeats until the LLM returns a final answer with no tool requests. ### Can I build an AI agent without LangChain? Yes. The core agent pattern is about 60 lines of Python: an HTTP POST to the LLM API, a dict of tool functions, and a while loop that dispatches tool calls. LangChain wraps these patterns in classes, but the underlying logic is simple enough to write directly. ### When should I use LangChain vs plain Python? Use LangChain when you need multiple LLM provider integrations, production vector store retrieval (RAG), or LangSmith observability. Use plain Python when your agent is straightforward, you want full debugging control, or you're learning how agents work. --- Source: https://tinyagents.dev/compare/langchain ### CrewAI vs Building from Scratch > Compare CrewAI's Agent, Task, and Crew abstractions to plain Python. See what crew orchestration, tool registration, and task delegation actually do. CrewAI organizes work into Agents, Tasks, and Crews. Each Agent has a role, goal, and tools. Tasks define work items. The Crew orchestrates execution. But strip away the abstractions and you'll find the same patterns. ## Concept map | Concept | CrewAI | Plain Python | |---|---|---| | Agent | `Agent(role, goal, backstory, tools, llm)` | A function with a system prompt and a `tools` dict | | Tools | Tool registration with `@tool` decorator, custom `Tool` classes | A dict: `tools[name](**args)` | | Agent Loop | Internal to `Agent` execution, hidden from user | A `while` loop over `messages` with `tool_calls` check | | Task Delegation | `Crew(agents, tasks, process=sequential/hierarchical)` | A task queue processed in a `while` loop with a budget cap | | Memory | `ShortTermMemory`, `LongTermMemory`, `EntityMemory` | A dict injected into the system prompt | | State | Task output passed between agents via `Crew` orchestration | A dict tracking tool calls and results | ## What CrewAI does CrewAI models multi-agent systems as a **crew of specialists**. Each `Agent` has a role (`"Senior Researcher"`), a goal (`"Find the best data sources"`), a backstory that shapes its behavior, and a set of tools it can use. `Tasks` define discrete units of work with expected outputs. The `Crew` orchestrates execution — sequentially, hierarchically, or with a custom process. CrewAI also provides memory systems: - `ShortTermMemory` - `LongTermMemory` - `EntityMemory` It also supports delegation, where one agent can hand off subtasks to another. The mental model is **a team of people collaborating on a project**. For prototyping multi-agent workflows where you want to reason about roles and responsibilities, it provides a clean vocabulary. ## The plain Python equivalent An `Agent` in CrewAI is a function with a system prompt that includes the role, goal, and backstory. The `tools` dict maps names to callables. Task delegation is a list of tasks processed in order — each task calls the assigned agent function with the task description appended to the `messages`. Hierarchical execution is a manager agent that decides which sub-agent to call next (just another tool choice). Memory is a dict injected into the system prompt. The entire crew pattern — multiple agents, task queue, delegation — is a `for` loop over tasks, where each iteration calls the right agent function. **No `Crew` class, no `process` kwarg.** Just functions calling functions with a shared `state` dict passed between them. ## When to use CrewAI CrewAI adds value when you **genuinely have multiple agents with distinct roles** and you want a clean way to define and orchestrate them. If you're building a content pipeline (researcher -> writer -> editor), a data analysis crew (collector -> analyst -> reporter), or any workflow where role separation matters for prompt quality, CrewAI's vocabulary helps. It also provides **guardrails around delegation** — agents can only delegate to agents in their crew, preventing runaway loops. For teams building multi-agent prototypes who want to iterate on roles and task definitions without writing orchestration code, CrewAI gets you to a working demo fast. ## When plain Python is enough Most "multi-agent" systems are **actually one agent calling different system prompts** depending on the task. If your agents share the same tools and differ only in their instructions, you don't need a framework — you need different system prompt strings. If your task execution is always sequential, a `for` loop is clearer than `Crew(process=sequential)`. The overhead of defining `Agent`, `Task`, and `Crew` objects adds ceremony without adding capability when the workflow is simple. Start with plain functions, add role separation through system prompts, and only reach for CrewAI when the **orchestration logic itself becomes the hard part** — not the prompts, not the tools, but the routing between agents. ## Verdict CrewAI shines for multi-agent setups where you want named roles ("researcher", "writer"). But the core mechanics — tool dispatch, the agent loop, task scheduling — are the same patterns you can build in plain Python. ## FAQ ### What is CrewAI and how does it work? CrewAI organizes AI agents into Crews with defined Agents (role, goal, tools) and Tasks (work items). The Crew orchestrates execution either sequentially or hierarchically. Under the hood, each Agent runs the same while loop pattern: call LLM, dispatch tools, repeat. ### Do I need CrewAI for multi-agent systems? Not necessarily. Most multi-agent systems are one agent function called with different system prompts. CrewAI adds value when you need complex orchestration, role-based delegation, or hierarchical task management. For simpler cases, plain Python functions with a task queue work fine. ### What is the difference between CrewAI and LangChain? LangChain focuses on single-agent tool use with broad integrations (vector stores, LLM providers). CrewAI focuses on multi-agent orchestration with named roles. LangChain is better for RAG pipelines; CrewAI is better for workflows where agents with different specialties collaborate. --- Source: https://tinyagents.dev/compare/crewai ### AutoGen vs Building from Scratch > Compare Microsoft AutoGen's ConversableAgent and nested chats to plain Python. See what multi-agent conversations and group chat actually do under the hood. AutoGen by Microsoft models agents as ConversableAgents that chat with each other. GroupChat coordinates multi-agent conversations. Nested chats handle sub-tasks. The underlying mechanics map directly to plain Python patterns. ## Concept map | Concept | AutoGen | Plain Python | |---|---|---| | Agent | `ConversableAgent` with `system_message`, `llm_config` | A function with a system prompt that POSTs to the LLM API | | Tools | `register_for_llm()` and `register_for_execution()` | A dict of callables + JSON schema descriptions | | Conversation | Two-agent chat with `initiate_chat()`, message history | A `messages` array that grows with each turn | | Multi-Agent | `GroupChat` with `GroupChatManager`, speaker selection | Multiple agent functions called in sequence on shared `messages` | | Nested Chats | `register_nested_chats()` for sub-task handling | A task queue (BFS) — agent schedules follow-ups via a tool | | Termination | `is_termination_msg` callback, `max_consecutive_auto_reply` | The `while` loop exits when no `tool_calls` or `max_turns` reached | ## What AutoGen does AutoGen's core abstraction is the `ConversableAgent` — an agent that can send and receive messages. Two agents chat by alternating turns on a shared message history. `GroupChat` extends this to N agents, with a `GroupChatManager` that selects the next speaker (round-robin, random, or LLM-based selection). **Nested chats** allow an agent to spin up a sub-conversation to handle a complex subtask before returning to the main thread. AutoGen also provides code execution sandboxes, letting agents write and run code as part of their conversation. The framework thinks in terms of **conversations, not chains or graphs**. This makes it natural for workflows where agents need to debate, critique, or iteratively refine outputs together. ## The plain Python equivalent A `ConversableAgent` is a function that takes a `messages` array, calls the LLM with a system prompt, and returns the assistant message. Two-agent chat is a `while` loop where you alternate between calling `agent_a(messages)` and `agent_b(messages)`, appending each response. `GroupChat` is the same loop but with a **speaker selection step** — either rotate through a list or ask the LLM "who should speak next?" and call that agent function. Nested chats are a function call within the loop: pause the main conversation, run a sub-loop with different agents, and inject the result back. Tool registration is adding functions to a `tools` dict with their JSON schemas. The conversation-as-primitive model is **just `messages` arrays passed between functions**. ## When to use AutoGen AutoGen earns its weight in **genuinely multi-agent scenarios** — code review where an author and reviewer iterate, research where a planner and executor collaborate, or any workflow where agents benefit from debating before producing output. The `GroupChat` abstraction with LLM-based speaker selection is non-trivial to implement well from scratch. AutoGen's code execution sandbox is also valuable if your agents need to write and test code. If you're building a system where the conversation structure itself is complex — nested sub-tasks, dynamic speaker selection, termination conditions based on content — AutoGen provides **tested implementations** of these patterns. Microsoft's backing also means active development and enterprise support. ## When plain Python is enough If your agents don't actually need to talk to each other — if it's really one agent with tools, or a pipeline where agent A's output feeds agent B's input — AutoGen's conversation model adds **indirection without benefit**. A single agent with a `tools` dict and a `while` loop covers most use cases. Even two-agent collaboration is often just two LLM calls in sequence: "Draft this" then "Review that." You don't need `initiate_chat()` and `is_termination_msg` callbacks for a two-step pipeline. The `GroupChatManager` is overkill when you know the execution order in advance. **Build the plain version first**, and reach for AutoGen only when you need dynamic, multi-turn, multi-agent conversations where the interaction pattern isn't predetermined. ## Verdict AutoGen excels at complex multi-agent workflows where agents need to debate or collaborate. For single-agent use cases or simple tool-calling agents, the plain Python version is significantly simpler. ## FAQ ### What is Microsoft AutoGen? AutoGen is Microsoft's multi-agent framework that models AI agents as ConversableAgents that chat with each other. It supports two-agent conversations, GroupChat with multiple agents, and nested chats for sub-tasks. The core mechanic is a messages array passed between agent functions. ### How does AutoGen compare to LangChain? AutoGen focuses on multi-agent conversations where agents debate and collaborate. LangChain focuses on single-agent tool use with broad integrations. AutoGen excels at complex multi-turn agent interactions; LangChain excels at RAG pipelines and provider-agnostic tooling. ### Can I build multi-agent systems without AutoGen? Yes. Multi-agent systems in plain Python are multiple agent functions called in sequence on shared messages. A GroupChat is a for-loop over agent functions. Nested chats are a task queue. AutoGen's value is in dynamic speaker selection and conversation management — patterns you rarely need for straightforward workflows. --- Source: https://tinyagents.dev/compare/autogen ### OpenAI Agents SDK vs Building from Scratch > Compare OpenAI's Agents SDK (formerly Swarm) to plain Python. See what Runner, handoffs, and guardrails do — it's the same agent loop you can build yourself. OpenAI's Agents SDK (evolved from Swarm) provides Agent, Runner, handoffs, and guardrails. It's intentionally minimal — closer to plain Python than most frameworks. Here's how the concepts map. ## Concept map | Concept | OpenAI Agents SDK | Plain Python | |---|---|---| | Agent | `Agent(name, instructions, model, tools)` | A function with a system prompt, model name, and `tools` dict | | Tools | Python functions with type hints, auto-converted to schemas | A dict of callables + manually written JSON schema | | Agent Loop | `Runner.run()` handles the loop internally | A `while` loop: call LLM, execute `tool_calls`, repeat | | Handoffs | `Handoff` between `Agent` objects for multi-agent routing | Call a different agent function based on the LLM's tool choice | | Guardrails | `InputGuardrail` and `OutputGuardrail` with tripwire pattern | Two lists of rule functions checked before and after the LLM | | Context | Typed context object passed through the agent lifecycle | A `state` dict updated inside the loop | ## What the OpenAI Agents SDK does The Agents SDK (formerly Swarm) is OpenAI's opinionated take on agent architecture. It provides **four primitives**: - `Agent` (system prompt + tools + model) - `Runner` (the agent loop) - handoffs (routing between agents) - guardrails (input/output validation) The key feature is **auto-schema generation** — write a Python function with type hints and the SDK converts it to a JSON tool schema automatically. `Runner.run()` handles the loop: call the model, check for tool calls, execute them, repeat. Handoffs let one agent transfer control to another by returning a special tool call. It's **deliberately thin**. OpenAI designed it as a reference implementation showing how agents should work with their API, not as a batteries-included framework. ## The plain Python equivalent The Agents SDK is **already close to plain Python**, which says something. `Agent` is a function that takes `messages` and returns a completion — the system prompt is the first message, tools are a dict. `Runner.run()` is a `while` loop: call `openai.chat.completions.create()`, check if the response has `tool_calls`, execute the matching functions from your `tools` dict, append results to `messages`, repeat until the model responds without `tool_calls`. Handoffs are an `if` statement: if the model calls a `"transfer_to_research"` tool, call the research agent function instead. Guardrails are two lists of validation functions — run the input rules before calling the LLM, run the output rules after. The **auto-schema generation** is the only piece that takes more than a few lines to replicate. ## When to use the Agents SDK The Agents SDK makes sense when you're **already committed to OpenAI's API** and want clean conventions without building them yourself. Auto-schema generation from type hints saves boilerplate and reduces bugs — you change the function signature and the schema updates automatically. The handoff pattern provides a clean multi-agent routing model that's well-tested. Guardrails with the **tripwire pattern** give you a standard way to validate inputs and outputs without ad-hoc `if` statements scattered through your code. If your team is building multiple agents on OpenAI and wants consistent patterns, the SDK provides a lightweight standard. It's also a good learning tool — read the source code to understand how agents work. ## When plain Python is enough Since the Agents SDK is **already minimal**, the gap between "with framework" and "without framework" is smaller than any other option on this page. The main reasons to skip it: - you use non-OpenAI models (the SDK is tightly coupled to OpenAI's API) - you want to understand every line of your agent loop (the **60-line version is fully transparent**) - you need custom behavior the SDK doesn't support (unusual termination logic, streaming with custom processing, non-standard tool patterns) Auto-schema generation is nice but not essential — writing JSON schemas by hand takes a few extra minutes and gives you full control. For a single-agent, single-provider setup, the plain Python version is **barely longer than the SDK version**. ## Verdict The Agents SDK is the thinnest framework on this list — it barely abstracts beyond what you'd write yourself. Use it when you want OpenAI's conventions and auto-schema generation. Skip it when you want full control or use non-OpenAI models. ## FAQ ### What is the OpenAI Agents SDK? The OpenAI Agents SDK (evolved from Swarm) provides Agent, Runner, handoffs, and guardrails for building AI agents with OpenAI models. It's the thinnest framework available — closer to plain Python than LangChain or CrewAI, with auto-schema generation from Python type hints. ### What is the difference between OpenAI Agents SDK and Swarm? Agents SDK is the production evolution of Swarm, which was an experimental framework. Both use the same patterns: Agent objects with instructions and tools, a Runner that handles the agent loop, and handoffs for multi-agent routing. Agents SDK adds guardrails and is officially supported. ### Should I use OpenAI Agents SDK or LangChain? Use the Agents SDK if you're committed to OpenAI models and want minimal abstraction with auto-schema generation. Use LangChain if you need multi-provider support, vector store integrations, or the LangSmith ecosystem. Use plain Python if you want full control and transparency. --- Source: https://tinyagents.dev/compare/openai-agents-sdk ### Agno (Phidata) vs Building from Scratch > Compare Agno's Agent class, tools, teams, and knowledge bases to plain Python. See what each abstraction actually does — in ~60 lines. Agno (formerly Phidata) is a lightweight Python framework for building agents. It gives you an Agent class with model, tools, instructions, knowledge, and storage — plus team-based multi-agent orchestration. It's closer to plain Python than LangChain, but still wraps patterns you can write yourself. Here's what each piece actually does. ## Concept map | Concept | Agno | Plain Python | |---|---|---| | Agent | `Agent(model=OpenAIChat(), instructions=[...])` class with `run()` method | A function that POSTs to `/chat/completions` and returns the response | | Tools | Function tools via `@tool` decorator or built-in toolkits (web search, SQL, etc.) | A dict of callables: `tools = {"search": search_web, "sql": run_query}` | | Agent Loop | `Agent.run()` handles tool dispatch internally, configurable via `show_tool_calls` | A `while` loop: call LLM, check for `tool_calls`, execute, repeat | | Memory / Knowledge | Knowledge bases (PDF, URL, vector DB) injected via `knowledge` param + built-in memory | A list of relevant chunks injected into the system prompt via a retrieval function | | Multi-Agent (Teams) | `Team` class with `agents` list, `mode` (sequential, parallel, coordinate), and shared memory | A function that calls agent functions in sequence or parallel, passing results between them | | Storage | `SqlAgentStorage`, `PostgresAgentStorage` for persisting sessions and state | `json.dump()` / `json.load()` to a file, or a simple DB insert | ## What Agno does Agno gives you a single `Agent` class that wires together an LLM, tools, instructions, knowledge bases, and storage. You configure an agent **declaratively** — pass in a model, a list of tools, and optional knowledge sources — and call `agent.run()`. It handles the tool-calling loop, injects knowledge into context, and persists conversation state. Agno also supports **multi-modal agents** (vision, audio) and team-based orchestration where multiple agents coordinate on tasks. The framework ships with built-in toolkits for common tasks: - web search - SQL queries - file operations Compared to LangChain, it's **lighter** — fewer abstractions, less indirection. The tradeoff is a smaller ecosystem and fewer third-party integrations. ## The plain Python equivalent Every Agno abstraction maps to plain Python. The `Agent` class is a function that POSTs to the LLM API, checks for `tool_calls`, dispatches them from a dict, and loops. Knowledge bases are a retrieval function that fetches relevant chunks and injects them into the system prompt. Memory is a `messages` list. Storage is `json.dump()`. Teams are a function that calls multiple agent functions and combines their outputs. The entire agent — with tools, knowledge retrieval, memory, and multi-agent coordination — fits in about **60 lines**. No base classes, no decorators. When something breaks, you debug your function, not a framework's internals. ## When to use Agno Agno earns its place when you want a **lightweight agent framework** that stays close to Python without the weight of LangChain. Its multi-modal support (vision, audio) is genuinely useful if you're building agents that process images or audio. Team-based orchestration saves time when you need multiple agents coordinating — sequential pipelines, parallel fan-out, or coordinator patterns. The built-in toolkits (web search, SQL, file I/O) eliminate boilerplate for common tasks. If you want structure without heavy abstraction, Agno is a **reasonable middle ground** between raw Python and a full framework. ## When plain Python is enough If your agent calls one LLM, uses a few custom tools, and doesn't need multi-modal input or team orchestration — **plain Python is simpler**. You don't need an `Agent` class to write a function. You don't need `SqlAgentStorage` to call `json.dump()`. You don't need a `Team` to call two functions in sequence. Agno is lightweight, but it's still a dependency that sits between you and the API. Start with the plain version. If you find yourself reimplementing Agno's patterns (especially multi-agent coordination or knowledge base management), then consider adopting it. **Don't add a framework to avoid writing a `while` loop.** ## Verdict Agno adds value when you want a batteries-included agent with minimal boilerplate — especially for multi-modal agents or team orchestration. But each of its abstractions maps to a small piece of plain Python. If your agent is straightforward, writing it directly gives you full control with zero framework overhead. ## FAQ ### What is Agno and how does it relate to Phidata? Agno is the new name for Phidata, a lightweight Python agent framework. It provides an Agent class with built-in support for tools, knowledge bases, memory, storage, and multi-agent teams. The rename happened in early 2025 — the core API stayed the same. ### How does Agno compare to LangChain? Agno is significantly lighter than LangChain — fewer abstractions, less indirection, closer to plain Python. LangChain has a larger ecosystem of integrations and LangSmith for observability. Agno is better for straightforward agents; LangChain is better when you need many third-party integrations working together. ### Is Agno better than Phidata? Agno is Phidata — it's the same project under a new name. If you're already using Phidata, upgrading to Agno gets you the latest features (improved multi-modal support, better team orchestration) with the same API patterns you already know. --- Source: https://tinyagents.dev/compare/agno ### LlamaIndex Agents vs Building from Scratch > Compare LlamaIndex's AgentRunner, ReActAgent, QueryEngine tools, and index-backed retrieval to plain Python. See what each abstraction actually does — in ~60 lines. LlamaIndex started as a RAG framework — connect your data, query it with an LLM. It now has a full agent system: AgentRunner, ReActAgent, FunctionTool, and the ability to use your indexes as tools. Its unique strength is treating your data as a callable tool. Here's what each piece actually does. ## Concept map | Concept | LlamaIndex | Plain Python | |---|---|---| | Agent | `AgentRunner` with `AgentWorker`, or `ReActAgent` for tool-calling agents | A function that POSTs to `/chat/completions` and returns the response | | Tools | `FunctionTool` for custom tools, `QueryEngineTool` to query an index as a tool | A dict of callables: `tools = {"search": search_fn, "calculate": calc_fn}` | | Agent Loop | `AgentRunner.chat()` manages step-by-step execution via `AgentWorker` tasks | A `while` loop: call LLM, check for `tool_calls`, execute, repeat | | RAG Integration | `VectorStoreIndex` + `QueryEngineTool` — the agent can query your data as a tool call | A tool function that embeds the query, searches a vector store, and returns top-k results | | Memory | `ChatMemoryBuffer` with token limit, or custom memory modules | A `messages` list with optional truncation: `messages = messages[-max_turns:]` | | Orchestration | `AgentRunner` step API for custom control flow, or multi-agent pipelines | Sequential function calls with results passed between them | ## What LlamaIndex agents do LlamaIndex's agent system builds on its core strength: **data indexing**. You create a `VectorStoreIndex` over your documents, wrap it in a `QueryEngineTool`, and hand it to a `ReActAgent`. The agent can then **query your data as a tool call** — the same way it might call a calculator or web search. `AgentRunner` manages the execution loop: it sends messages to the LLM, parses tool calls, dispatches them (including index queries), and accumulates results. `FunctionTool` lets you wrap any Python function as a tool. The unique value over other frameworks is the **tight integration between retrieval and agent reasoning** — your data becomes a first-class tool, not an afterthought bolted onto a generic agent loop. ## The plain Python equivalent The agent loop is the **same pattern as every other framework**: a `while` loop that calls the LLM, checks for `tool_calls`, dispatches from a dict, and repeats. What LlamaIndex adds is the **retrieval tool**. In plain Python, that's a function: embed the query with an API call, search your vector store (Pinecone, pgvector, FAISS — all have simple clients), return the top-k chunks as a string. You put that function in your `tools` dict alongside everything else. The agent doesn't know or care that one tool queries an index — it's just another callable. The total code is about **60 lines** for the agent loop plus 15-20 lines for the retrieval function. No `AgentRunner`, no `AgentWorker`, no `QueryEngineTool`. ## When to use LlamaIndex LlamaIndex earns its place when **retrieval is central to your agent** — not a side feature, but the core of what it does. If you're building an agent that queries multiple document collections, needs different retrieval strategies per collection, or benefits from LlamaIndex's document parsing (PDF, HTML, databases), the framework saves real work. The index-as-tool pattern is well-designed: **one line to turn any index into a tool** the agent can call. LlamaHub provides dozens of data connectors. If your problem is fundamentally `'let an agent reason over my data,'` LlamaIndex is purpose-built for that. It also integrates well with existing vector stores — Pinecone, Weaviate, Chroma, pgvector — without you writing the glue code. ## When plain Python is enough If your agent uses one or two data sources and a few tools, **plain Python is simpler**. You don't need `AgentRunner` to write a `while` loop. You don't need `QueryEngineTool` to write a function that searches a vector store — every vector store has a Python client with a `search` method. You don't need `FunctionTool` to put a function in a dict. LlamaIndex's value is **proportional to how much data infrastructure you need**. One vector store and five tools? Write it yourself. Ten document types, three retrieval strategies, and custom re-ranking? LlamaIndex will save you time. Match the tool to the complexity of the problem, not the other way around. ## Verdict LlamaIndex adds genuine value when your agent needs to query structured or unstructured data as part of its reasoning — that's the index-as-tool pattern, and it's well-executed. But if you're building a general-purpose agent that doesn't need RAG, the agent framework is overhead. The plain Python version of the agent loop is the same 60 lines either way. ## FAQ ### Can LlamaIndex be used for agents, not just RAG? Yes. LlamaIndex has a full agent system — ReActAgent for tool-calling agents, AgentRunner for custom control flow, and FunctionTool for wrapping any Python function. The unique angle is that your indexes (document collections) become tools the agent can call, so retrieval and reasoning happen in the same loop. ### How do LlamaIndex agents compare to LangChain agents? LlamaIndex agents are optimized for data-heavy use cases — the index-as-tool pattern is its core strength. LangChain agents are more general-purpose with a broader ecosystem of integrations. If your agent primarily reasons over documents, LlamaIndex has better abstractions. If you need diverse integrations (APIs, databases, deployment), LangChain has more options. ### Do I need LlamaIndex to build a RAG agent? No. A RAG agent is an agent loop (while loop + LLM + tool dispatch) where one of the tools is a retrieval function. That retrieval function embeds the query, searches a vector store, and returns results. Every vector store has a Python client. You can build the whole thing in ~80 lines without LlamaIndex — but LlamaIndex saves time when you have complex data pipelines. --- Source: https://tinyagents.dev/compare/llamaindex ### Semantic Kernel vs Building from Scratch > Compare Microsoft Semantic Kernel's Kernel, Plugins, and Planners to plain Python. See what ChatCompletionAgent, KernelFunction, and StepwisePlanner actually do — in ~60 lines. Semantic Kernel is Microsoft's enterprise SDK for building AI agents. It provides a Kernel orchestrator, Plugins with KernelFunctions, Planners for multi-step reasoning, and deep Azure OpenAI integration. But every one of these maps to the same primitives you can write yourself. ## Concept map | Concept | Semantic Kernel | Plain Python | |---|---|---| | Agent | `ChatCompletionAgent` with `Kernel`, instructions, and service config | A function that POSTs to `/chat/completions` with a system prompt | | Tools / Plugins | `KernelPlugin` with `@kernel_function` decorators, typed parameters | A dict of callables: `tools = {"search": lambda q: ...}` | | Planning | `StepwisePlanner`, `HandlebarsPlanner` for multi-step decomposition | A system prompt that says `'break this into steps'` — the LLM plans natively | | Memory | `SemanticTextMemory` with embeddings and vector stores | A dict injected into the system prompt, or a list searched with embeddings | | Orchestration | `Kernel.invoke()` with plugin resolution and filter pipeline | A `while` loop: call LLM, check for `tool_calls`, dispatch, repeat | | Multi-Language | C#, Python, Java SDKs with shared abstractions | The HTTP API is the same in every language — just POST JSON | ## What Semantic Kernel does Semantic Kernel is **Microsoft's SDK for building AI-powered applications**. The central object is the `Kernel` — it holds your AI service connections, plugins, and configuration. `Plugins` are collections of `KernelFunctions` (decorated Python/C# methods) that the LLM can call as tools. Planners like `StepwisePlanner` break complex goals into multi-step plans, choosing which plugins to invoke at each step. The SDK provides **deep integration with Azure OpenAI**, including managed identity auth, content filtering, and deployment management. It also ships memory connectors for vector stores (Azure AI Search, Qdrant, Pinecone) and supports **filters** — middleware that runs before and after each function invocation. For teams already on Azure with .NET backends, it fits naturally into the existing stack. ## The plain Python equivalent The `Kernel` is a config object that holds your API key and a dict of tools. A `KernelFunction` is a regular function in that dict. The `Planner` is a system prompt instruction — tell the LLM to break the task into steps and it will, no planner class needed. Memory is a list of strings you embed and search, or just a dict you inject into the prompt. Orchestration is the same `while` loop every agent uses: call the LLM, check if the response has `tool_calls`, look up the function in your `tools` dict, call it, append the result, repeat. The filter pipeline is a `try`/`except` around your function calls. The entire agent — including plugin dispatch, planning, and memory — is about **60 lines**. **No `Kernel` object, no plugin registry, no planner hierarchy.** ## When to use Semantic Kernel Semantic Kernel makes sense when you're **building on Microsoft's stack**. If your team writes C# and deploys to Azure, SK gives you managed identity auth, Azure OpenAI integration, and a familiar .NET programming model out of the box. The plugin system maps well to existing service classes — wrap your business logic in `KernelFunctions` and the agent can call it. For enterprise teams that need audit logging, content filtering, and deployment governance, SK's filter pipeline and Azure integration save real work. It also has **official support and LTS commitments** from Microsoft, which matters for procurement-heavy organizations. ## When plain Python is enough If you're **not on Azure and not writing C#**, most of Semantic Kernel's value proposition doesn't apply. The `Kernel` object adds indirection without adding capability — you still configure an API key, register functions, and run a loop. The `Planner` classes are the LLM planning with extra steps — modern models handle multi-step reasoning through their system prompt without needing a `StepwisePlanner`. If your agent calls one provider with a few tools, the plain Python version is faster to write, easier to debug, and has no SDK dependency to keep updated. **Start with the 60-line version.** If you find yourself wanting Azure-specific features or .NET interop, that's when SK earns its place. ## Verdict Semantic Kernel earns its complexity in enterprise environments with Azure OpenAI, .NET backends, and existing Microsoft infrastructure. But the core agent pattern — LLM call, tool dispatch, loop — is identical to what you can build in 60 lines of Python. ## FAQ ### What is Microsoft Semantic Kernel? Semantic Kernel is Microsoft's open-source SDK for building AI agents. It provides a Kernel orchestrator, Plugins (collections of functions the LLM can call), Planners for multi-step task decomposition, and deep Azure OpenAI integration. It's available in C#, Python, and Java. ### How does Semantic Kernel compare to LangChain? Both wrap the same core pattern (LLM call + tool dispatch + loop). LangChain has a broader integration catalog and larger community. Semantic Kernel has deeper Microsoft/Azure integration and multi-language support (C#, Python, Java). Choose based on your stack: Azure/.NET teams lean SK, everyone else typically starts with LangChain or plain code. ### Do I need Semantic Kernel to build AI agents with Azure OpenAI? No. Azure OpenAI exposes the same REST API as OpenAI — you can call it with any HTTP client. Semantic Kernel adds convenience for managed identity auth, content filtering config, and plugin management, but the underlying API calls are identical to what you'd write with requests or the openai Python package. --- Source: https://tinyagents.dev/compare/semantic-kernel ### Haystack vs Building from Scratch > Compare deepset Haystack's pipeline architecture and components to plain Python. See what Pipeline, Agent, Retrievers, and document stores actually do — in ~60 lines. Haystack by deepset is a framework for building NLP and LLM pipelines. It models everything as a directed graph of components — retrievers, generators, converters — connected through a Pipeline. But the patterns underneath are the same ones you can build with plain Python. ## Concept map | Concept | Haystack | Plain Python | |---|---|---| | Agent | `Agent` component with `ChatGenerator`, tool definitions, and message routing | A function that POSTs to `/chat/completions` and dispatches `tool_calls` | | Tools | `Tool` dataclass with function reference, name, description, parameters schema | A dict of callables: `tools = {"search": lambda q: ...}` | | Pipeline Architecture | `Pipeline()` with `add_component()` and `connect()` — a directed graph of typed components | A sequence of function calls: `output = step_b(step_a(input))` | | RAG / Retrieval | `DocumentStore` + `Retriever` + `PromptBuilder` + `Generator` wired in a `Pipeline` | Embed the query, search a list, inject matches into the prompt, call the LLM | | Memory | `ChatMessageStore` with `ConversationMemory` component in pipeline | A `messages` list that persists outside the function | | Deployment | Pipeline YAML serialization, `Hayhooks` REST server | A Python script behind FastAPI or any HTTP server | ## What Haystack does Haystack models NLP/LLM applications as **directed graphs of components**. You create a `Pipeline`, add components (retrievers, generators, converters, rankers), and connect their inputs and outputs. Each component is a Python class with a `@component` decorator and a `run()` method with typed inputs and outputs. The framework handles data routing between components, input validation, and pipeline serialization to YAML. Haystack ships with: - document stores (Elasticsearch, Qdrant, Pinecone, Weaviate) - embedding models - converters for PDFs and HTML - a `ChatGenerator` that wraps LLM API calls The `Agent` component adds tool-using capabilities on top. For RAG pipelines with multiple retrieval stages, re-ranking, and document processing, the component model provides **clear separation of concerns**. ## The plain Python equivalent A Haystack `Pipeline` is **a sequence of function calls**. The `Retriever` is a function that takes a query, embeds it, and searches a list of documents. The `Generator` is a function that calls the LLM API. The `PromptBuilder` is string formatting. Connecting components is passing the output of one function as the input to the next. The `Agent` component is the same `while` loop every agent framework uses — call the LLM, check for `tool_calls`, dispatch, append results, repeat. Document stores are a list you search with cosine similarity, or a database query. YAML serialization is saving your config to a file. The entire RAG pipeline — embed query, retrieve documents, build prompt, call LLM — fits in about **30 lines**. Add tool-using agent behavior and you're at 60. ## When to use Haystack Haystack shines for **document-heavy applications**. If you're building a RAG system with PDF ingestion, multiple retrieval strategies (sparse + dense), re-ranking, and you need to swap document stores without rewriting your pipeline, Haystack's component model saves real time. The typed input/output contracts between components **catch integration errors early**. Pipeline serialization lets you version and deploy pipelines as configuration rather than code. For teams building search and retrieval products — customer support knowledge bases, document Q&A, research assistants — where the retrieval pipeline is the core complexity, Haystack provides a mature foundation with production-tested components. ## When plain Python is enough If your application is an agent that calls an LLM with some tools, you **don't need a pipeline framework**. The `Pipeline`/`Component` abstraction adds value when you have many stages of data transformation — but most agents are a loop, not a graph. If your RAG setup is one retriever and one generator, writing `embed()` then `search()` then `call_llm()` is clearer than wiring three components through `Pipeline.connect()`. The YAML serialization is only useful if you need non-developers to modify pipelines. **Start with plain functions.** If you find yourself building complex retrieval pipelines with interchangeable components, that's when Haystack's graph model starts earning its overhead. ## Verdict Haystack earns its complexity when you're building RAG pipelines with multiple retrieval stages, document processing, and production deployment needs. But for straightforward agents with a few tools, the plain Python version is simpler to write and debug. ## FAQ ### What is Haystack AI by deepset? Haystack is an open-source framework by deepset for building NLP and LLM applications. It uses a pipeline architecture where components (retrievers, generators, converters) are connected as directed graphs. It's particularly strong for RAG (retrieval-augmented generation) applications with document stores and multi-stage retrieval. ### How does Haystack compare to LangChain? Both provide component abstractions for LLM applications. Haystack's pipeline model enforces typed connections between components, making data flow explicit. LangChain has a larger integration catalog and broader community. Haystack is stronger for structured retrieval pipelines; LangChain is more flexible for general agent workflows. Both wrap the same underlying API calls. ### Can I build AI agents with Haystack? Yes. Haystack 2.x includes an Agent component that supports tool-calling with any ChatGenerator. But the Agent component uses the same pattern as every other framework — call the LLM, check for tool_calls, dispatch functions, loop. If tool-using agents are your primary use case (not RAG), plain Python may be simpler than bringing in the full pipeline framework. --- Source: https://tinyagents.dev/compare/haystack ### Google ADK vs Building from Scratch > Compare Google's Agent Development Kit to plain Python. See what hierarchical agents, workflow agents, and Vertex AI integration actually do — in ~60 lines. Google's Agent Development Kit (ADK) is an open-source framework for building multi-agent systems. It provides hierarchical agent trees, workflow agents (Sequential, Parallel, Loop), tight Vertex AI integration, and a rich tool ecosystem. ADK 2.0 for Python GA'd in 2026 and Java 1.0.0 shipped the same year, so it's a multi-language SDK now rather than a Python-only one. Every one of these maps to plain Python primitives. ## Concept map | Concept | Google ADK | Plain Python | |---|---|---| | Agent | `LlmAgent` class with model, instructions, and `sub_agents` list | A function that POSTs to `/chat/completions` and returns the response | | Tools | `FunctionTool`, built-in tools (Search, Code Exec), third-party integrations | A dict of callables: `tools = {"search": search_web}` | | Agent Loop | `Runner.run()` with automatic tool dispatch and sub-agent delegation | A `while` loop: call LLM, check for `tool_calls`, execute, repeat | | Multi-Agent | Hierarchical agent tree with root agent delegating to specialized sub-agents | Functions calling other functions: `research = agent(prompt, tools=research_tools)` | | Workflows | `SequentialAgent`, `ParallelAgent`, `LoopAgent` workflow primitives | Sequential: call functions in order. Parallel: `asyncio.gather()`. Loop: `while` condition | | Session | Session and State service with typed channels and persistence | A dict passed between function calls: `state = {"turns": 0, "context": []}` | ## What Google ADK does Google ADK provides a **code-first framework** for building agents that can delegate work to other agents in a hierarchy. You define an `LlmAgent` with a model, instructions, tools, and optionally a list of sub-agents. The root agent decides when to hand off tasks to specialized children. ADK ships with workflow primitives: - `SequentialAgent` runs steps in order - `ParallelAgent` fans out concurrently - `LoopAgent` repeats until a condition is met The framework handles session management, state persistence, and streaming out of the box. It's **optimized for Gemini models and Vertex AI** but works with other providers. For teams already on Google Cloud, the deployment story is seamless: containerize your agent and deploy to Vertex AI Agent Engine or Cloud Run. ## The plain Python equivalent A hierarchical agent is **just functions calling other functions**. Your root agent calls the LLM, and if the response indicates a sub-task, you call a different function with its own system prompt and tool set. Workflow orchestration is equally straightforward: sequential is calling functions in order, parallel is `asyncio.gather()`, and looping is a `while` loop with a condition check. Session state is a dict you pass between calls and optionally serialize to disk or a database. The entire pattern — root agent, sub-agents, workflows, state — fits in about **80 lines of Python**. No class hierarchies, no `Runner` abstraction, no Agent Engine. When your agent misbehaves, you read your functions instead of tracing through framework internals. ## When to use Google ADK ADK makes sense when you're **building multi-agent systems on Google Cloud**. If your agents need to delegate to specialized sub-agents, run parallel workflows, and deploy to Vertex AI with managed scaling, ADK saves real engineering time. The built-in streaming support, bidirectional audio/video capabilities, and tight Gemini integration are genuinely useful for production applications. Teams that need agent observability, evaluation tooling, and enterprise deployment infrastructure will find ADK's opinionated approach faster than building from scratch. It also shines when you need the workflow primitives — sequential, parallel, loop — as **first-class composable units** rather than ad-hoc function calls. ## When plain Python is enough If your agent uses one model, a few tools, and a straightforward loop, you **don't need a hierarchical agent tree**. Most agents in practice are single-agent systems that call tools and return results. You don't need `SequentialAgent` to call three functions in order. You don't need `ParallelAgent` to use `asyncio.gather`. The ADK abstractions add value at scale — multiple agents, complex delegation logic, production deployment on Google Cloud. For learning, prototyping, or simple production agents, the plain Python version is easier to understand, debug, and modify. **Start with a function, a dict, and a loop.** Add ADK when you genuinely need multi-agent orchestration or Vertex AI deployment. ## Verdict ADK earns its complexity when you need multi-agent orchestration on Google Cloud with Vertex AI deployment. If you're using Gemini and need production-grade agent infrastructure, it's well-designed. For single-agent use cases or non-Google stacks, plain Python keeps things simpler. ## FAQ ### What is Google ADK (Agent Development Kit)? Google ADK is an open-source framework for building AI agents and multi-agent systems. It provides hierarchical agent trees where a root agent delegates to specialized sub-agents, workflow primitives (Sequential, Parallel, Loop), and tight integration with Gemini models and Vertex AI for deployment. ### How does Google ADK compare to LangChain? ADK focuses on multi-agent hierarchies and Google Cloud deployment, while LangChain focuses on composable chains and broad integration support. ADK is more opinionated about agent architecture (hierarchical delegation) and more tightly coupled to the Google ecosystem. LangChain is provider-agnostic but has a larger abstraction surface. ### Do I need Google ADK to build AI agents? No. The core patterns ADK provides — agent loops, tool dispatch, multi-agent delegation, workflows — map to plain Python: functions, dicts, asyncio.gather, and while loops. ADK adds value when you need production multi-agent orchestration on Google Cloud, but simple agents work fine without it. --- Source: https://tinyagents.dev/compare/google-adk ### Anthropic Agent SDK vs Building from Scratch > Compare the Anthropic Agent SDK (Claude Code as a library) to plain Python. See what built-in tools, MCP integration, and lifecycle hooks actually do — in ~60 lines. The Anthropic Agent SDK packages Claude Code's agent loop as a library. It provides built-in tools (file system, shell, web), deep MCP integration, and 18 lifecycle hooks for intercepting agent execution. But the underlying pattern is still a function, a dict, and a loop. ## Concept map | Concept | Anthropic Agent SDK | Plain Python | |---|---|---| | Agent | Claude agent with built-in tools, MCP servers, and system prompt | A function that POSTs to `/messages` and returns the response | | Tools | Built-in tools (`bash`, file read/write, web) + MCP server connections | A dict of callables: `tools = {"bash": run_command, "read": read_file}` | | Agent Loop | SDK's internal agentic loop with automatic tool dispatch | A `while` loop: call LLM, check for `tool_use` blocks, execute, repeat | | Sub-Agents | Agents invoke other agents as tools via the SDK | A function that calls another function: `result = research_agent(query)` | | Lifecycle Hooks | 18 hook events: pre/post tool call, message, error, etc. | `if`/`else` checks inside your loop: `if should_log: log(event)` | | MCP Integration | One-line MCP server config for Playwright, Slack, GitHub, etc. | HTTP client calls to each service: `requests.post(slack_url, payload)` | ## What the Anthropic Agent SDK does The Anthropic Agent SDK takes Claude Code — the coding agent used by hundreds of thousands of developers — and ships it as a Python and TypeScript library. You get the same agent loop, built-in tools (`bash` execution, file read/write, web search), and context management that Claude Code uses internally. The standout feature is **MCP (Model Context Protocol) integration**: connect Playwright, Slack, GitHub, databases, and hundreds of other servers with a single config line. The SDK also provides **18 lifecycle hooks** that let you intercept tool calls, messages, errors, and other events. This gives you fine-grained control over agent behavior without modifying the core loop. It's less a framework and more a **productized agent runtime**. ## The plain Python equivalent The agent loop is a `while` loop that POSTs to the `/messages` API, checks for `tool_use` blocks in the response, executes the matching function from a `tools` dict, appends the result to `messages`, and repeats. Built-in tools are just functions: - `bash` is `subprocess.run()` - file reading is `open().read()` - web search is an HTTP call to a search API MCP integration is HTTP client calls to each service — there's nothing magical about connecting to Slack or GitHub beyond knowing their API endpoints. Lifecycle hooks are `if`/`else` checks at specific points in your loop. The entire agent — tool dispatch, sub-agent delegation, logging — fits in about **60 lines**. The SDK's value isn't in the pattern (which is simple) but in the **pre-built tool implementations** and MCP plumbing. ## When to use the Anthropic Agent SDK The SDK earns its weight when you need an agent that **interacts with the real world** — reading codebases, running shell commands, browsing the web, connecting to third-party services via MCP. Writing reliable `bash` execution, file system access, and error handling from scratch is tedious. The SDK has **battle-tested implementations** of all of these. If you're building on Claude and want production-grade tool use with minimal setup, the SDK is the fastest path. The lifecycle hooks are genuinely useful for logging, guardrails, and cost tracking in production. Teams that already use MCP servers get instant access to a growing ecosystem of integrations without writing HTTP boilerplate. ## When plain Python is enough If your agent calls an LLM, uses a few custom tools, and doesn't need file system access or MCP connections, **plain Python is simpler**. You don't need the SDK to write a `while` loop that dispatches tool calls. The SDK's built-in tools are its main value — if you're not using them, you're importing complexity for no reason. For learning how agents work, the plain version is better: you see every HTTP call, every tool dispatch, every `messages` append. The SDK abstracts these away, which is great for production but counterproductive for understanding. **Start with 60 lines**, add the SDK when you need its built-in tools or MCP ecosystem. ## Verdict The Anthropic Agent SDK's real value is packaging Claude Code's battle-tested agent loop with built-in tools and MCP integration. If you want a production agent that reads files, runs commands, and connects to services, it saves significant plumbing. For understanding how agents work, the plain version is more instructive. ## FAQ ### What is the Anthropic Agent SDK? The Anthropic Agent SDK (formerly Claude Code SDK) packages Claude Code's agent loop as a Python and TypeScript library. It provides built-in tools for file system access, shell execution, and web browsing, plus deep MCP integration for connecting to third-party services like Slack, GitHub, and databases. ### How does the Anthropic Agent SDK compare to the OpenAI Agents SDK? The Anthropic SDK focuses on built-in tools and MCP integration — it's a productized runtime based on Claude Code. The OpenAI SDK focuses on agent orchestration with handoffs between agents and guardrails. Anthropic's approach is more tool-rich out of the box; OpenAI's is more focused on multi-agent coordination patterns. ### Do I need the Anthropic Agent SDK to build agents with Claude? No. You can build Claude agents with direct API calls to /messages. The SDK adds value through pre-built tools (bash, file I/O, web) and MCP integration. If your agent uses custom tools and doesn't need file system access or MCP, the plain API is simpler. --- Source: https://tinyagents.dev/compare/anthropic-sdk ### DSPy vs Building from Scratch > Compare DSPy's compiled prompting modules to plain Python. See what Signatures, Optimizers, ChainOfThought, and ReAct actually do — in ~60 lines. DSPy replaces hand-written prompts with compiled modules. You define Signatures (input/output types), compose them into pipelines, and let Optimizers auto-tune prompts based on a metric. For agents, DSPy provides a ReAct module. But under the hood, it's still prompts, functions, and a loop. ## Concept map | Concept | DSPy | Plain Python | |---|---|---| | Agent | `dspy.ReAct` module with signature and tools | A function that POSTs to `/chat/completions` with a system prompt | | Prompts | `dspy.Signature` defines input/output fields, compiled to optimized prompts | An f-string template: `prompt = f"Given {input}, return {output}"` | | Optimization | `dspy.BootstrapFewShot`, `MIPROv2` auto-tune prompts against a metric | Manual iteration: try different prompts, measure accuracy, pick the best one | | Tools | Tools passed to `ReAct` module as callable list | A dict of callables: `tools = {"search": search, "calc": calculate}` | | Chaining | `dspy.ChainOfThought`, `dspy.Module` with `forward()` composition | Function calls in sequence: `step1 = summarize(text); step2 = classify(step1)` | | Evaluation | `dspy.Evaluate` with metric functions and dev sets | A `for` loop over test cases: `scores = [metric(predict(x), y) for x, y in test_set]` | ## What DSPy does DSPy takes a **fundamentally different approach** from other agent frameworks. Instead of providing agent orchestration abstractions, it replaces the prompt engineering process itself. You define a `Signature` — a typed declaration of inputs and outputs like `"question -> answer"` — and DSPy compiles it into an optimized prompt. The framework provides modules like: - `ChainOfThought` (adds reasoning steps) - `ReAct` (adds tool use) - `ProgramOfThought` (generates code) The key innovation is **Optimizers**: algorithms like `BootstrapFewShot` and `MIPROv2` that automatically find the best instructions and few-shot examples by evaluating against a metric you define. This means prompts improve systematically rather than through trial-and-error. DSPy treats prompts as **a compilation target, not a hand-authored artifact**. ## The plain Python equivalent A `Signature` is an f-string template with named placeholders. `ChainOfThought` adds `"Let's think step by step"` to your prompt — literally **one line**. `ReAct` is the standard agent loop: call the LLM, parse tool calls, execute them, repeat. The real difference is **optimization**. In plain Python, you manually write prompts, test them against examples, adjust wording, and repeat. DSPy automates this cycle with search algorithms. The plain equivalent is a script that tries N prompt variants, scores each against a test set, and picks the winner. This is tedious but conceptually simple — a `for` loop over prompt templates with an accuracy check. The agent pattern itself (`function` + `dict` + `loop`) is **identical to every other framework**. ## When to use DSPy DSPy earns its complexity when **prompt quality directly impacts your product** and you have evaluation data to optimize against. If you're building a classification pipeline, a RAG system, or a multi-step reasoning chain where accuracy matters at scale, DSPy's optimizers can find prompts that outperform hand-written ones. It's **particularly valuable when you switch models** — instead of rewriting prompts for each provider, you re-run the optimizer. Teams with labeled datasets and clear metrics will get the most value. DSPy also shines for research workflows where you need reproducible, systematic prompt improvement rather than ad-hoc iteration. The `ReAct` module is competent for agentic tasks within this optimization framework. ## When plain Python is enough If your prompts **work well enough with manual tuning**, DSPy adds complexity without proportional benefit. Most agents don't need optimized prompts — they need good tool definitions and a reliable loop. If you're building a chatbot, a simple tool-calling agent, or a prototype, hand-written prompts are faster to write and easier to debug. DSPy's abstractions (`Signatures`, `Modules`, `Optimizers`) introduce a learning curve that only pays off when you have evaluation data and a clear quality metric. For one-off tasks or exploratory work, an f-string and a `for` loop are simpler. **Start with plain prompts**, measure quality, and reach for DSPy when manual iteration becomes the bottleneck. ## Verdict DSPy's real innovation is automated prompt optimization — replacing manual prompt engineering with algorithmic tuning. This is genuinely novel and valuable for production systems where prompt quality matters at scale. For simple agents or learning, hand-written prompts are easier to understand and modify. ## FAQ ### What is DSPy and how is it different from LangChain? DSPy is a Stanford NLP framework that replaces hand-written prompts with compiled modules. You define input/output Signatures and let Optimizers auto-tune prompts against a metric. LangChain focuses on agent orchestration and integrations. DSPy focuses on making prompts better algorithmically — they solve different problems. ### Can DSPy build AI agents? Yes. DSPy provides a ReAct module that implements the standard agent loop (reason, act, observe) with tool calling. However, DSPy's primary value is prompt optimization, not agent orchestration. The agent capabilities are a module within the broader framework, not the core focus. ### Do I need DSPy for prompt engineering? No. Most prompts work well enough with manual iteration. DSPy adds value when you have evaluation datasets, clear quality metrics, and need systematic prompt improvement at scale — especially when switching between LLM providers. For simple or prototype use cases, f-string templates are faster. --- Source: https://tinyagents.dev/compare/dspy ### Mastra vs Building from Scratch > Compare Mastra's TypeScript agent framework to plain code. See what agents, workflows, RAG, and Mastra Studio actually do — in ~60 lines. Mastra is a TypeScript-first framework for building AI agents, from the team behind Gatsby. It provides agents with tool calling, a workflow engine with steps and conditions, built-in RAG, memory systems, and Mastra Studio for visual debugging. Every piece maps to TypeScript you can write yourself. ## Concept map | Concept | Mastra | Plain Python | |---|---|---| | Agent | `new Agent({ model, instructions, tools })` with automatic tool dispatch | An `async` function that POSTs to `/chat/completions` and returns the response | | Tools | `createTool({ name, schema, execute })` with Zod validation | An object of functions: `const tools = { search: async (q) => fetch(url + q) }` | | Workflows | `Workflow` class with `.step()`, `.then()`, `.branch()` for orchestration | `async` function calls in sequence with `if`/`else` branching | | RAG | Built-in document syncing, chunking, embedding, and vector search | `fetch()` to embedding API, store in array, cosine similarity search | | Memory | Short-term thread memory + long-term vector memory across sessions | A `messages` array for short-term, a JSON file or DB query for long-term | | Studio | Mastra Studio: local GUI for testing agents, viewing traces, debugging | `console.log()` statements and a test script you run from the terminal | ## What Mastra does Mastra provides a **full-stack TypeScript framework** for building AI agents. You define agents with a model, system prompt, and tools — the framework handles the agent loop, tool dispatch, and response parsing. The workflow engine lets you compose multi-step processes with explicit steps, conditions, and error handling. Built-in RAG support covers the full pipeline: - document loading - chunking - embedding - vector storage - retrieval Memory spans both **short-term** (thread-scoped message history) and **long-term** (vector-based recall across sessions). Mastra Studio gives you a local browser-based GUI to test agents, inspect traces, and debug workflows visually. Created by the Gatsby team, it targets TypeScript developers who want a productive, type-safe agent development experience. ## The plain TypeScript equivalent An agent is an `async` function that POSTs to the LLM API, checks for `tool_calls` in the response, executes matching functions from a `tools` object, and loops. Workflows are `async` functions that call other `async` functions with `if`/`else` branching — no framework needed to run step A, then step B, then branch on a condition. RAG is three operations: call an embedding API, store vectors in an array (or database), and find the closest match with cosine similarity. Memory is a `messages` array you persist to a file or database. Studio is `console.log` and a test file. The entire agent — tools, memory, RAG retrieval — fits in about **60 lines of TypeScript**. No classes, no decorators, no build step. **Just functions, objects, and `fetch` calls.** ## When to use Mastra Mastra makes sense for **TypeScript teams building production agents** that need workflows, RAG, and memory in one cohesive package. If you'd otherwise piece together LangChain.js, a vector database client, a workflow engine, and a debugging tool, Mastra bundles all of these with a consistent API and type safety throughout. The workflow engine is particularly useful for multi-step business processes where you need explicit branching, error handling, and observability. Mastra Studio saves real debugging time compared to reading console logs. Teams that want to **stay in the TypeScript/Node.js ecosystem** without switching to Python will find Mastra more natural than porting Python-first frameworks. The Composio integration adds hundreds of third-party tool connections. ## When plain TypeScript is enough If your agent calls one LLM, uses a few tools, and doesn't need RAG or complex workflows, **plain TypeScript is simpler**. You don't need a workflow engine to call three functions in sequence. You don't need a framework to append `messages` to an array. Most agents are simpler than their framework usage suggests — a single function with a `while` loop handles the vast majority of use cases. For learning how agents work, the plain version teaches you what's actually happening at the API level. For prototyping, a **60-line script iterates faster** than setting up a framework. Reach for Mastra when you need its workflow engine, RAG pipeline, or Studio tooling — not before. ## Verdict Mastra is the best option for TypeScript teams that want a batteries-included agent framework without leaving the Node.js ecosystem. The workflow engine and Studio are genuinely productive. For simple agents or Python teams, the plain approach avoids an unnecessary dependency. ## FAQ ### What is Mastra and who created it? Mastra is a TypeScript-first AI agent framework created by the team behind Gatsby. It provides agents with tool calling, a workflow engine, built-in RAG, memory systems, and Mastra Studio for visual debugging. It launched from Y Combinator W25 and raised a $22M Series A led by Spark Capital in April 2026, bringing total funding to $35M. Relicensed from Elastic v2 to Apache 2.0 the same month. ### How does Mastra compare to LangChain? Mastra is TypeScript-native while LangChain started in Python (LangChain.js is a port). Mastra bundles workflows, RAG, and a visual debugger in one package. LangChain has broader integrations and a larger ecosystem. Choose Mastra for TypeScript-first development; choose LangChain for Python or maximum provider coverage. ### Do I need Mastra to build AI agents in TypeScript? No. The core agent pattern is about 60 lines of TypeScript: a fetch call to the LLM API, an object of tool functions, and a while loop. Mastra adds value when you need workflows, RAG, memory, and visual debugging in one cohesive TypeScript package. --- Source: https://tinyagents.dev/compare/mastra ### Rasa vs Building from Scratch > Compare Rasa's conversational AI framework to plain Python. See what NLU pipelines, dialogue management, CALM, and custom actions actually do — in ~60 lines. Rasa is an open-source framework for building conversational AI — chatbots and virtual assistants. It provides NLU pipelines, dialogue management, custom actions (tools), and the newer CALM architecture that combines LLMs with deterministic business logic. The underlying patterns are simpler than they appear. ## Concept map | Concept | Rasa | Plain Python | |---|---|---| | Agent | Rasa agent with NLU pipeline, dialogue policies, and action server | A function that classifies intent, looks up a response, and returns it | | NLU | NLU pipeline: tokenizer, featurizer, intent classifier, entity extractor | An LLM call with a prompt: `"Classify this message's intent: {message}"` | | Dialogue | Stories/Rules YAML + dialogue policies for conversation flow | A state machine: `if intent == 'greet': state = 'greeting'; respond()` | | Tools | Custom actions running on a separate action server via HTTP | Functions in a dict: `actions = {"check_order": lambda id: db.query(id)}` | | Slots | Typed slots for tracking entities and state across turns | A dict updated during conversation: `slots = {"order_id": "123"}` | | CALM | LLM for understanding + deterministic `Flows` for business logic | LLM parses user intent, `if`/`else` routes to the right handler function | ## What Rasa does Rasa provides a **complete framework for building conversational AI systems**. The traditional stack includes: - an **NLU pipeline** (intent classification and entity extraction) - **dialogue management** (stories and rules that define conversation flows) - an **action server** for custom business logic The newer **CALM architecture** separates language understanding (handled by LLMs) from business logic (handled by deterministic `Flows`), giving you LLM fluency without sacrificing reliability. Rasa focuses on enterprise requirements: on-premise deployment, data privacy, regulatory compliance, and deterministic behavior for critical business flows. You define your domain in YAML — intents, entities, slots, responses, actions — and Rasa trains a model that handles the conversation lifecycle. The framework is **battle-tested in production** across banking, telecom, and healthcare. ## The plain Python equivalent Intent classification is **one LLM call**: send the user's message with a prompt asking for the intent and entities, parse the JSON response. Dialogue management is a state machine — a dict tracking the current state and a series of `if`/`else` branches routing to the next step. Custom actions are functions you call based on the classified intent. Slot filling is updating a dict as entities are extracted. The entire conversational agent — intent handling, state tracking, tool dispatch, response generation — fits in about **60 lines**. The LLM handles the language understanding that Rasa's NLU pipeline was trained for, and your `if`/`else` logic handles the flows that Rasa's dialogue policies managed. **No YAML domain files, no training pipeline, no action server.** ## When to use Rasa Rasa earns its complexity in **enterprise conversational AI** where reliability and control matter more than flexibility. If you need deterministic behavior for regulated industries (banking, healthcare), on-premise deployment for data privacy, and auditable conversation flows, Rasa's architecture is designed for exactly this. The **CALM approach** — LLM understanding plus deterministic flows — is genuinely useful when you need natural language flexibility without giving up business logic guarantees. Teams building customer service bots, virtual assistants, or IVR systems at scale will find Rasa's tooling (training, testing, deployment, analytics) saves real time. Rasa Pro adds enterprise features like end-to-end testing, analytics dashboards, and SSO integration. ## When plain Python is enough If you're building a general-purpose agent, a simple Q&A bot, or an internal tool, **Rasa is overkill**. Modern LLMs handle intent classification, entity extraction, and response generation in a single API call — you don't need a separate NLU pipeline. For most chatbot use cases, a system prompt defining the bot's personality and rules, plus a `tools` dict for actions, is simpler and more flexible than YAML domain files and training pipelines. Rasa's value is in **controlled, predictable conversational flows** for enterprise production. If your bot doesn't need deterministic behavior or on-premise deployment, the plain Python version with an LLM is faster to build, easier to iterate, and more capable at handling unexpected inputs. ## Verdict Rasa is purpose-built for production conversational AI with enterprise requirements — on-premise deployment, regulatory compliance, deterministic business logic. For general-purpose agents or simple chatbots, an LLM with a system prompt and a few tools is faster to build and more flexible. ## FAQ ### What is Rasa and what is it used for? Rasa is an open-source framework for building conversational AI — chatbots, virtual assistants, and voice bots. It provides NLU (intent classification, entity extraction), dialogue management (conversation flow control), and custom actions (business logic). It's used primarily in enterprise settings for customer service, healthcare, and banking applications. ### How does Rasa compare to using LLMs directly? Rasa provides deterministic conversation flows with enterprise controls (on-premise, audit trails, compliance). LLMs with tool calling are more flexible and faster to prototype but less predictable. Rasa's CALM architecture combines both: LLMs for understanding, deterministic Flows for business logic. Choose Rasa for regulated, high-reliability chatbots; choose LLMs for general-purpose agents. ### Do I need Rasa to build a chatbot? No. A modern chatbot can be built with an LLM API call, a system prompt, and a tools dict for actions — about 60 lines of code. Rasa adds value for enterprise conversational AI with deterministic flows, on-premise deployment, and regulatory requirements. For simple or prototype chatbots, direct LLM usage is faster. --- Source: https://tinyagents.dev/compare/rasa ### Smolagents vs Building from Scratch > Compare HuggingFace's Smolagents to plain Python. See what CodeAgent, ToolCallingAgent, sandboxed execution, and the Hub integration actually do — in ~60 lines. Smolagents is HuggingFace's minimalist agent library. Its key idea: agents write Python code as their actions instead of emitting JSON tool calls. CodeAgent executes code in a sandbox, ToolCallingAgent uses traditional tool calling. The core logic fits in ~1,000 lines. Here's what each piece actually does. ## Concept map | Concept | Smolagents | Plain Python | |---|---|---| | Agent | `CodeAgent` or `ToolCallingAgent` with model and tools list | A function that POSTs to `/chat/completions` and returns the response | | Tools | `@tool` decorator or `Tool` class with name, description, and callable | A dict of callables: `tools = {"search": search, "calculate": calc}` | | Code Actions | `CodeAgent` writes Python code as its action, executed in sandbox | LLM returns code string, you run `exec(code, {"tools": tools})` | | Sandbox | E2B, Docker, Modal, or Pyodide sandbox for safe code execution | `subprocess.run()` in a Docker container, or restricted `exec()` with limited globals | | Agent Loop | Internal loop: think (LLM reasons), act (code/tool call), observe (result) | A `while` loop: call LLM, execute action, append observation, repeat | | Model Support | HuggingFace Hub models, OpenAI, Anthropic, local via LiteLLM | An HTTP POST to whichever provider's API you choose | ## What Smolagents does Smolagents takes a **distinct approach**: instead of having the LLM emit structured JSON tool calls, the `CodeAgent` has the LLM write Python code that performs the action directly. The model reasons about the task, writes code that calls available tools, and the framework executes that code in a sandboxed environment. This reduces the number of LLM calls by **about 30%** compared to traditional tool-calling agents, because the model can chain multiple operations in a single code block. The framework provides two agent types: - `CodeAgent` for the code-writing approach - `ToolCallingAgent` for traditional structured tool calls Sandbox options include E2B, Docker, Modal, and Pyodide for secure execution. The core library is deliberately minimal — **about 1,000 lines of logic** with few abstractions over raw Python. ## The plain Python equivalent A code agent is an LLM call that returns a Python code string, which you execute with `exec()` in a restricted namespace. You pass available tools as the namespace globals, the LLM writes code that calls them, and you capture the output. The sandbox is a Docker container or a restricted `exec()` with limited builtins. The agent loop is **identical to every other framework**: call the LLM, execute the action (code or tool call), append the result as an observation, repeat until done. Tool definitions are functions in a dict. Model support is an HTTP POST to an API endpoint. The entire code-agent pattern — including sandbox setup — fits in about **70 lines**. Smolagents wraps this cleanly, but the underlying mechanic is **straightforward `exec()` with safety guards**. ## When to use Smolagents Smolagents makes sense when you want a **lightweight agent library** without framework bloat. The code-agent approach is genuinely useful for tasks that benefit from chaining multiple tool calls in a single step — data analysis, multi-step calculations, or complex tool orchestration. If you're in the HuggingFace ecosystem and want to use Hub models alongside commercial APIs, Smolagents provides smooth integration. The sandbox options (E2B, Docker, Pyodide) make code execution **production-safe**. Teams that value simplicity and readability will appreciate the minimal codebase — you can read the entire framework source in an afternoon. For agents that need to do more than one thing per turn, the code approach outperforms traditional tool calling. ## When plain Python is enough If your agent uses **traditional tool calling** (one tool per turn), Smolagents' code-agent innovation doesn't help — a `while` loop with tool dispatch is simpler. If you're not using HuggingFace Hub models, the ecosystem integration adds no value. For single-tool-call agents, the plain version is fewer lines than importing a library. The code-agent approach also introduces **security considerations** (executing LLM-generated code) that you avoid entirely with structured tool calls. For learning how agents work, the 60-line plain version is more instructive than any library. **Start with a function, a dict, and a loop.** Reach for Smolagents when you specifically need code-as-action or want HuggingFace model access with minimal overhead. ## Verdict Smolagents lives up to its name — it's genuinely minimal and the code-agent approach is a real innovation that reduces LLM calls by ~30%. If you want a lightweight agent library with HuggingFace ecosystem access, it's excellent. For understanding the fundamentals, the plain version is even simpler. ## FAQ ### What is Smolagents and how does it differ from other agent frameworks? Smolagents is HuggingFace's minimalist agent library where agents write Python code as actions instead of emitting JSON tool calls. This code-agent approach reduces LLM calls by ~30% by chaining multiple operations in a single step. The core library is about 1,000 lines — deliberately minimal compared to frameworks like LangChain or CrewAI. ### Is Smolagents the same as HuggingFace Tiny Agents? No. Smolagents is HuggingFace's Python agent library for building code-writing and tool-calling agents. Tiny Agents is a separate HuggingFace project focused on lightweight MCP (Model Context Protocol) client agents. They share the HuggingFace ecosystem but solve different problems. ### Do I need Smolagents to build a code-writing agent? No. A code agent is an LLM call that returns Python code, executed via exec() with tools in the namespace. Smolagents wraps this pattern with sandbox support, model integrations, and a clean API. If you want just the pattern, it's about 70 lines of Python. Use Smolagents when you want production sandboxing and HuggingFace model access. --- Source: https://tinyagents.dev/compare/smolagents ### AutoGPT vs Building from Scratch > Compare AutoGPT's autonomous agent architecture to plain Python. See what goal decomposition, task execution, and plugin systems actually do — in ~60 lines. AutoGPT was one of the first autonomous agent projects, spawning 165k+ GitHub stars. It decomposes goals into task lists, executes them with web browsing and code execution, and loops until done. But the core pattern is a while loop with a prompt. ## Concept map | Concept | AutoGPT | Plain Python | |---|---|---| | Agent | AutoGPT `Agent` class with goal decomposition and self-prompting loop | A function that POSTs to `/chat/completions` with a system prompt containing the goal | | Tools | Plugin system with web browsing, file I/O, code execution, Google search | A dict of callables: `tools = {"search": search_web, "write_file": write_file}` | | Agent Loop | Autonomous loop: think → plan → act → observe → repeat until goal met | A `while` loop: call LLM, parse action, execute tool, append result, repeat | | Memory | Vector DB (Pinecone/local) for long-term memory, message history for short-term | A list for recent messages, a dict for facts injected into the system prompt | | Planning | GPT-4 generates multi-step plans, stores in task queue, revises on failure | Ask the LLM to return a JSON list of steps, iterate through them | | Self-Critique | Built-in self-evaluation prompt that critiques each action before executing | A second LLM call: `'Review this plan and list problems'` before acting | ## What AutoGPT does AutoGPT takes a high-level goal and **autonomously breaks it into subtasks**, executes them, and evaluates progress. The agent runs in a continuous loop: it thinks about what to do next, creates a plan, executes an action (web search, file write, code execution), observes the result, and decides whether to continue or revise. It stores results in a vector database for long-term memory and uses message history for short-term context. The plugin system lets you add capabilities like web browsing, Google search, and file management. With **165k+ GitHub stars**, it proved that LLMs could drive autonomous workflows — but it also revealed the fundamental challenge: **unbounded loops that burn tokens** without clear stopping criteria. ## The plain Python equivalent The core AutoGPT pattern is a `while` loop that calls an LLM with a goal-oriented system prompt, parses the response for an action to take, executes that action from a `tools` dict, appends the result to the message history, and repeats. Planning is just asking the LLM to return a JSON list of steps. Self-critique is a second LLM call that reviews the plan. Memory is a list of messages plus a dict of facts you inject into the prompt. The entire autonomous agent fits in about **60 lines** — the hard part was never the code, it was **designing prompts that keep the agent focused** and knowing when to stop. You get the same loop, minus the plugin system overhead. ## When to use AutoGPT AutoGPT makes sense when you want a **pre-built autonomous agent with batteries included** — web browsing, file management, code execution, and a plugin ecosystem. The AutoGPT Platform adds a visual builder, workflow management, and monitoring tools. If you need an agent that can handle open-ended research tasks, operate across multiple tools simultaneously, and you want a community of plugins to extend it, AutoGPT saves you from building that infrastructure. It is also useful as a **reference implementation** to study how autonomous agents handle planning, memory, and self-correction in practice. ## When plain Python is enough If your agent has a **bounded task** — answer a question, process a document, execute a defined workflow — you do not need an autonomous loop that decides its own stopping criteria. Most production agents are not autonomous; they run a fixed loop with clear exit conditions. A plain `while` loop with tool dispatch, a `messages` list, and a simple `state` dict handles this cleanly. You also avoid the **token-burning problem**: AutoGPT's unbounded loops can make dozens of LLM calls for simple tasks. Start with a bounded loop, add planning only if your task genuinely requires multi-step decomposition, and you will ship faster with lower API costs. ## Verdict AutoGPT pioneered the autonomous agent pattern, but most of its complexity comes from managing an unbounded loop — not from the core agent logic. For bounded tasks, a plain while loop with tool dispatch gives you the same capability with full control over when to stop. ## FAQ ### What is AutoGPT and how does it work? AutoGPT is an autonomous AI agent that takes a goal, breaks it into subtasks, and executes them in a loop using LLM calls, web browsing, file operations, and code execution. The core is a think-plan-act-observe cycle that repeats until the goal is met or the agent gets stuck. ### Can I build an autonomous agent without AutoGPT? Yes. The autonomous agent pattern is a while loop that calls an LLM, parses an action from the response, executes it from a tools dict, and appends the result to message history. AutoGPT wraps this in a plugin system and vector DB, but the core logic is about 60 lines of Python. ### Why does AutoGPT use so many API tokens? AutoGPT runs an unbounded autonomous loop — each iteration makes at least one LLM call, plus optional self-critique calls. For simple tasks, it can make 20-50 calls that a bounded agent would handle in 3-5. The token cost comes from the loop, not the agent logic itself. --- Source: https://tinyagents.dev/compare/autogpt ### BabyAGI vs Building from Scratch > Compare BabyAGI's task-driven agent loop to plain Python. See what task creation, prioritization, and vector memory actually do — in ~60 lines. BabyAGI popularized the task-driven autonomous agent in ~100 lines of Python. It runs three LLM-powered sub-agents — execution, task creation, and prioritization — in a loop, storing results in a vector database. The repo was archived by its author in September 2024 — it is a canonical pattern reference, not a maintained framework. The pattern still maps cleanly to plain Python, which is arguably why the author archived it. ## Concept map | Concept | BabyAGI | Plain Python | |---|---|---| | Agent | Three sub-agents: execution agent, task creation agent, prioritization agent | Three LLM calls with different system prompts inside one `while` loop | | Tools | Task execution via LLM completion with context from vector DB retrieval | A function that calls the LLM with the task description and relevant context | | Agent Loop | Pop task → execute → create new tasks → reprioritize → repeat | A `while` loop: pop from a list, call LLM, extend the list, sort, repeat | | Memory | Pinecone or Chroma vector DB storing task results as embeddings | A list of past results; optionally embed and search with a similarity function | | Task Queue | `Deque` of task dicts managed by the prioritization agent | A Python `list` of strings, sorted by a priority LLM call or simple heuristic | | Context Retrieval | Vector similarity search over stored results to build execution context | Search your `results` list for relevant entries, inject the top N into the prompt | ## What BabyAGI does BabyAGI runs a loop with **three LLM-powered steps**: - an **execution agent** takes the top task and produces a result, using context retrieved from a vector database of previous results - a **task creation agent** looks at the result and the objective to generate new tasks - a **prioritization agent** reorders the task list based on the objective The loop repeats until the task queue is empty or a limit is reached. Created by Yohei Nakajima in 2023, the original was about **100 lines of Python** — deliberately minimal to show that **the pattern, not the framework**, is what matters. It inspired dozens of agent frameworks and proved that task decomposition could be surprisingly simple. ## The plain Python equivalent The BabyAGI pattern translates directly to plain Python. A `while` loop pops tasks from a list. For each task, you make an LLM call with the task description and any relevant context from previous results. You append the result to a `results` list. Then you make a second LLM call asking for new tasks based on the result and objective, and extend your task list. Optionally, a third call reprioritizes — or you just sort by a simple heuristic. The vector database becomes a list you search with cosine similarity, or even just keyword matching for simple cases. The whole thing fits in **40-60 lines** without any external dependencies beyond an HTTP client. ## When to use BabyAGI BabyAGI is best used as a **learning tool and reference implementation**. If you want to understand how task-driven agents work — how tasks are created, prioritized, and executed in a loop — studying BabyAGI's source code is the fastest path. It also works as a starting point for research on autonomous agent behaviors, since the minimal codebase is easy to modify and experiment with. For exploratory tasks where you genuinely do not know the subtasks in advance — like researching a topic from scratch — the task creation loop can surface useful angles you would not have thought of manually. ## When plain Python is enough For most production use cases, you **know your tasks in advance** or can define them with a single LLM call. You do not need a prioritization agent to sort a list of three items. You do not need a vector database to store five results. The BabyAGI loop adds value when the task space is genuinely open-ended and you want the agent to discover subtasks dynamically — but most real-world agents execute a known workflow. Start with a simple `for` loop over your tasks, add dynamic task creation only if you need it, and skip the vector DB until your results list grows beyond what fits in a single prompt context window. ## Verdict BabyAGI proved that an autonomous agent can be elegantly simple — the original was ~100 lines. The value is in the pattern (task creation, execution, prioritization loop), not the framework. You can reimplement it in an afternoon and customize the stopping criteria that BabyAGI leaves open-ended. ## FAQ ### What is BabyAGI and how does it work? BabyAGI is a task-driven autonomous agent that runs a loop: execute the top task using an LLM, create new tasks based on the result, reprioritize the task list, and repeat. The original implementation was about 100 lines of Python, using OpenAI's API and a vector database for context retrieval. ### How is BabyAGI different from AutoGPT? BabyAGI focuses on task decomposition and prioritization with a minimal codebase (~100 lines). AutoGPT is a larger autonomous agent with web browsing, file operations, and a plugin system. BabyAGI is more of a pattern demonstration; AutoGPT is closer to a product with a full platform. ### Can I use BabyAGI in production? BabyAGI is better as a learning tool than a production framework. It lacks stopping criteria, error handling, and rate limiting. For production, take the pattern — task loop with creation and prioritization — and implement it with proper error handling, budget limits, and defined exit conditions. --- Source: https://tinyagents.dev/compare/babyagi ### Pydantic AI vs Building from Scratch > Compare Pydantic AI's type-safe agent framework to plain Python. See what typed tools, structured outputs, and model-agnostic agents actually do — in ~60 lines. Pydantic AI is a type-safe agent framework built by the Pydantic team. It brings Pydantic's validation philosophy to AI agents: typed tool definitions, structured outputs via Pydantic models, and compile-time checking. Version 2.0 shipped in June 2026 (v2.1 followed the same month) — a substantial API refresh after the September 2025 1.0. Every feature maps to a few lines of plain Python with manual validation. ## Concept map | Concept | Pydantic AI | Plain Python | |---|---|---| | Agent | `Agent()` class with typed `result_type`, system prompt, and `model` parameter | A function that POSTs to `/chat/completions` and validates the response shape | | Tools | `@agent.tool` decorator with typed parameters and Pydantic validation | A dict of callables with a wrapper that validates args: `json.loads` + `isinstance` checks | | Agent Loop | `agent.run()` handles the tool-call loop internally with typed dispatch | A `while` loop: call LLM, check for `tool_calls`, validate args, execute, repeat | | Structured Output | `result_type=MyModel` enforces Pydantic model on final LLM response | Parse the LLM response as JSON, pass to a validation function, retry on failure | | Model Switching | Swap `model='openai:gpt-4o'` to `model='anthropic:claude-sonnet'` in one line | Change the API endpoint URL and adjust the request/response format mapping | | Dependencies | `RunContext[DepsType]` injects typed dependencies into tools at runtime | Pass a `deps` dict to your agent function, tools access it via closure or argument | ## What Pydantic AI does Pydantic AI wraps the agent pattern in **Pydantic's type system**. You define an `Agent` with a `result_type` (a Pydantic model), register tools with typed parameters via decorators, and call `agent.run()` to execute the tool-call loop. The framework validates tool arguments against their type hints, validates the final response against your result model, and retries on validation failures. It supports **25+ model providers** through a unified interface, so switching from OpenAI to Anthropic is a one-line change. Dependencies are injected via typed `RunContext`, giving your tools access to databases, API clients, or configuration without global state. The real value is that your **IDE catches type errors before runtime**. ## The plain Python equivalent Type-safe tool dispatch in plain Python means **validating tool arguments before calling the function**. Parse the LLM's `tool_call` arguments as JSON, check types with `isinstance` or a simple schema, and raise on mismatch. Structured output is the same: parse the final response as JSON, validate against expected keys and types, retry if it fails. Model switching means swapping the API URL and adjusting the request format — a dict mapping provider names to endpoint configs. Dependency injection is passing a `deps` dict to your agent function that tools access via closure. The full typed agent is about **60 lines**, plus maybe 20 for validation helpers. No decorators, no base classes — **just functions with type checks**. ## When to use Pydantic AI Pydantic AI earns its place when **your codebase already uses Pydantic extensively** and you want the same validation guarantees in your agent layer. The typed tool definitions catch argument mismatches at write-time, not when a user triggers an edge case in production. Structured outputs via Pydantic models are particularly valuable for agents that feed data into typed downstream systems — if your agent produces a `CustomerRecord`, Pydantic ensures every field is present and correctly typed. The framework also shines for teams that frequently switch model providers; the **unified interface with 25+ providers** genuinely saves integration work. Logfire integration gives you observability out of the box. ## When plain Python is enough If your agent has a few tools with simple argument types and returns text responses, the **type-safety overhead may not pay off**. A dict of callables with basic `isinstance` checks handles most tool dispatch needs. Structured output via `json.loads` plus a validation function covers the common case. You do not need a framework to swap between two LLM providers — a simple `if`/`else` with different request formats works fine. **Start with plain Python validation**, add Pydantic models only when you have complex nested response types that benefit from schema validation. The framework's value scales with the complexity of your type requirements. ## Verdict Pydantic AI adds genuine value if you want compile-time type checking across your agent's tools, outputs, and dependencies. If you already use Pydantic in your stack, it fits naturally. But the core agent logic — loop, dispatch, validate — is still ~60 lines of Python you can own entirely. ## FAQ ### What makes Pydantic AI different from LangChain? Pydantic AI focuses on type safety — typed tool parameters, structured outputs via Pydantic models, and compile-time checking. LangChain focuses on integration breadth — hundreds of connectors for vector stores, document loaders, and LLM providers. Pydantic AI is narrower but catches more bugs at write-time. ### Do I need to know Pydantic to use Pydantic AI? Basic Pydantic knowledge helps — defining models with typed fields and understanding validation. But the agent API is straightforward: define an Agent with a result type, decorate tool functions, and call agent.run(). If you know Python type hints, you can learn the Pydantic parts as you go. ### Can Pydantic AI work with any LLM provider? Yes. Pydantic AI supports 25+ model providers through a unified interface. You specify the model as a string like 'openai:gpt-4o' or 'anthropic:claude-sonnet' and the framework handles the API differences. Switching providers is a one-line change. --- Source: https://tinyagents.dev/compare/pydantic-ai ### ControlFlow vs Building from Scratch > Compare ControlFlow's task-centric AI orchestration to plain Python. See what typed tasks, multi-agent flows, and Prefect integration actually do — in ~60 lines. ControlFlow by Prefect flips the typical agent framework: instead of defining agents that choose tasks, you define tasks and assign agents to them. Tasks have typed results via Pydantic, agents are interchangeable executors, and flows compose tasks into workflows. The core maps to functions with return types. ## Concept map | Concept | ControlFlow | Plain Python | |---|---|---| | Agent | `cf.Agent()` with name, model, instructions, and tool access | A function that calls the LLM with a specific system prompt and tool set | | Tools | Python functions passed to `Task()` or `Agent()` as tool lists | A dict of callables passed to your agent function | | Task | `cf.Task()` with `result_type`, `instructions`, `agents`, and `dependencies` | A function call that returns a typed result: `def classify(text: str) -> Category` | | Flow | `@cf.flow` decorator composing tasks with dependency resolution | A sequence of function calls, each using the previous result as input | | Multi-Agent | Multiple `cf.Agent()` instances assigned to different tasks in one flow | Multiple LLM calls with different system prompts in the same function | | Observability | Built-in Prefect integration for logging, retries, and monitoring | `print` statements, `try`/`except` blocks, and a logging library | ## What ControlFlow does ControlFlow **inverts the usual agent framework pattern**. Instead of creating an agent and letting it decide what to do, you define tasks with typed results, instructions, and dependencies — then assign agents to execute them. A `Task` specifies what you want (classify this text, extract these entities, summarize this document) and what the result should look like (a Pydantic model). An `Agent` is an **interchangeable executor** with a model, system prompt, and tool access. A `Flow` composes tasks with automatic dependency resolution: if task B depends on task A's result, ControlFlow runs them in order. Built on Prefect, it inherits production features like retries, logging, and monitoring dashboards. ## The plain Python equivalent A ControlFlow `Task` is a function with a typed return value. A `Flow` is a sequence of function calls where each uses the previous result. Multi-agent collaboration is calling different LLM functions with different prompts. Dependency resolution is just calling functions in the right order — something Python does naturally with sequential execution. The typed results become **Pydantic model validation** on the LLM's JSON output. The whole pattern is functions calling functions: define `classify()`, define `summarize()`, call them in order, pass results forward. About **60 lines** covers a multi-task workflow with typed outputs, no decorators or task objects needed. ## When to use ControlFlow ControlFlow earns its complexity when you have **genuinely complex orchestration needs**: many tasks with non-trivial dependencies, multiple specialized agents working on different parts of a workflow, and production requirements like retries, monitoring, and observability. The **Prefect integration** is the real differentiator — if your team already uses Prefect for data pipelines, adding AI tasks to existing workflows is natural. The task-centric model also helps teams think clearly about what they want from AI steps (typed results with defined schemas) rather than getting lost in prompt engineering. For workflows where tasks can run in parallel and dependency graphs matter, ControlFlow handles the scheduling. ## When plain Python is enough If your workflow is **linear** — do A, then B, then C — you do not need a task orchestrator. Sequential function calls with typed returns handle this cleanly. If you have one agent, you do not need multi-agent assignment. If your tasks always succeed on the first try, you do not need Prefect's retry infrastructure. Most AI workflows in practice are simple pipelines: take input, call LLM, validate output, return result. Start with plain functions, add Pydantic validation on the outputs, and reach for ControlFlow only when you need parallel task execution, complex dependency graphs, or integration with an existing Prefect deployment. The framework adds value at **orchestration scale, not at single-agent scale**. ## Verdict ControlFlow's task-centric model is a genuinely different way to think about agent orchestration — define what you want, not how to get it. The Prefect integration adds real production value. But if your workflow is linear and your tasks are simple, plain function composition does the same job with less ceremony. ## FAQ ### How is ControlFlow different from LangChain? ControlFlow is task-centric — you define typed tasks and assign agents to them. LangChain is agent-centric — you define agents with tools and let them decide what to do. ControlFlow gives you more control over what gets executed and in what order, while LangChain gives agents more autonomy. ### Do I need Prefect to use ControlFlow? ControlFlow is built on Prefect and uses it for orchestration, logging, and monitoring. While you can use ControlFlow without deploying Prefect infrastructure, the full value comes from the integration — retries, dashboards, scheduling, and production monitoring that Prefect provides. ### Can ControlFlow handle multi-agent workflows? Yes. You define multiple Agent instances with different models, prompts, and tools, then assign them to different tasks in a flow. ControlFlow handles the routing — each task runs with its assigned agent, and results flow between tasks via typed dependencies. --- Source: https://tinyagents.dev/compare/controlflow ### CAMEL AI vs Building from Scratch > Compare CAMEL AI's multi-agent role-playing framework to plain Python. See what inception prompting, role assignment, and agent societies actually do — in ~60 lines. CAMEL AI pioneered role-playing multi-agent conversations in a 2023 NeurIPS paper. Two agents — an instructor and an assistant — collaborate through inception prompting to solve tasks. The framework scales to societies of agents, but the core pattern is two LLM calls in a loop with different system prompts. ## Concept map | Concept | CAMEL AI | Plain Python | |---|---|---| | Agent | `ChatAgent` with `role_name`, `role_type`, and `system_message` for behavior | A function that calls the LLM with a role-specific system prompt | | Tools | Tool modules registered on agents with OpenAI-compatible function schemas | A dict of callables with JSON schema descriptions for the LLM | | Role-Playing | `RolePlaying` session with `user_agent`, `assistant_agent`, and inception prompting | Two LLM calls per turn: one with `'You are the instructor'` prompt, one with `'You are the assistant'` | | Inception Prompting | System prompts that embed the task, roles, and constraints to prevent drift | A detailed system prompt that says: `'You are X. Your task is Y. Always respond as X.'` | | Society | Multi-agent societies with role assignment, communication, and voting | A loop over N agents, each with a different system prompt, sharing a `messages` list | | Task Decomposition | AI Society that splits tasks into subtasks assigned to specialist role pairs | One LLM call to decompose the task, then iterate subtasks through agent pairs | ## What CAMEL AI does CAMEL AI implements multi-agent collaboration through **role-playing**. The core idea from the NeurIPS 2023 paper: assign two agents complementary roles (instructor and assistant), give each an **inception prompt** that embeds the task and behavioral constraints, and let them converse to solve a problem. The instructor breaks the task into steps and gives instructions; the assistant executes and reports back. This back-and-forth reduces hallucination because each agent **checks the other's work**. The framework scales beyond pairs to societies of agents — communities that debate, vote, and collaborate. The research team has simulated up to **one million agents** studying emergent behaviors and scaling laws in complex multi-agent environments. ## The plain Python equivalent Role-playing in plain Python is **two LLM calls per turn** with different system prompts. The instructor call gets a prompt like `'You are a project manager. Break this task into steps and give the next instruction.'` The assistant call gets `'You are a developer. Execute the instruction and report the result.'` Both share a `messages` list so each sees what the other said. Inception prompting is just a detailed system prompt that prevents role drift — include the task, the role, and behavioral constraints. A society of agents is a `for` loop over N agents with different prompts, each appending to a shared conversation. The entire multi-agent debate pattern fits in about **50 lines**. The insight is in the **prompting technique, not the code**. ## When to use CAMEL AI CAMEL AI is valuable for **research on multi-agent systems** — studying how agent societies form consensus, how role assignment affects output quality, and how communication patterns scale. If you are running experiments on agent collaboration, the framework provides built-in infrastructure for role-playing sessions, conversation logging, and society simulation. It also helps when you need structured multi-agent debate for production tasks: having an analyst and a critic review each other's work genuinely improves output quality on complex reasoning tasks. The academic origin means the framework is **well-documented in papers** with reproducible experimental setups. ## When plain Python is enough If you want two agents to check each other's work, you **do not need a framework** — two LLM calls with different system prompts in a loop gives you multi-agent debate. If you are building a product, not running research, the society infrastructure is overkill. Most production use cases need at most two or three agent perspectives, not a simulated society. The inception prompting technique works in any framework or in plain Python: write a detailed system prompt that embeds the role, task, and constraints. **Start with two-agent debate in plain Python**, measure whether it actually improves your output quality, and reach for CAMEL AI only if you need the research infrastructure for systematic experiments. ## Verdict CAMEL AI's research contribution — role-playing and inception prompting — is a genuinely useful technique for reducing hallucination through multi-agent debate. But the technique is the value, not the framework. Two LLM calls with different system prompts give you the same pattern in plain Python. ## FAQ ### What is CAMEL AI's role-playing approach? CAMEL AI assigns two agents complementary roles — an instructor who breaks tasks into steps and gives directions, and an assistant who executes and reports back. Inception prompting embeds the task and role constraints into system prompts to keep agents on track. The back-and-forth debate reduces hallucination through mutual checking. ### How is CAMEL AI different from CrewAI? CAMEL AI is research-focused with academic origins (NeurIPS 2023). It emphasizes role-playing conversations and inception prompting as techniques. CrewAI is production-focused with sequential and parallel task execution. CAMEL AI is for studying multi-agent behaviors; CrewAI is for building multi-agent workflows. ### Does multi-agent role-playing actually improve results? Research shows that multi-agent debate reduces hallucination on complex reasoning tasks because agents check each other's work. The improvement is most noticeable on tasks requiring analysis, critique, or multi-step reasoning. For simple tasks like text classification, a single agent is usually sufficient. --- Source: https://tinyagents.dev/compare/camel-ai ### n8n AI vs Building from Scratch > Compare n8n's visual AI workflow builder to plain Python. See what AI agent nodes, 500+ integrations, and no-code tool calling actually do — in ~60 lines of code. n8n is a workflow automation platform that added AI agent capabilities with native LangChain integration. You build agents by dragging nodes in a visual canvas — LLM nodes, tool nodes, memory nodes — and connecting them. Every node maps to a function call you could write in Python. ## Concept map | Concept | n8n AI | Plain Python | |---|---|---| | Agent | AI Agent node with model, tools, and memory connected via canvas wires | A function that POSTs to `/chat/completions` and dispatches tool calls | | Tools | Tool nodes (HTTP Request, Code, database) wired into the agent node | A dict of callables: `tools = {"query_db": run_sql, "call_api": http_get}` | | Agent Loop | Agent node internally loops: call LLM → detect tool use → run tool → repeat | A `while` loop: call LLM, check for `tool_calls`, execute, append result, repeat | | Memory | Memory node (window buffer, vector store) connected to agent node | A `messages` list persisted to a file or database between runs | | Integrations | 500+ pre-built nodes for Slack, Gmail, Notion, databases, APIs | HTTP requests to each service's API with auth headers from environment variables | | Orchestration | Visual workflow canvas with triggers, conditionals, and parallel branches | A Python script with `if`/`else`, `for` loops, and `asyncio.gather` for parallel calls | ## What n8n AI does n8n is a **workflow automation platform** — think Zapier, but self-hostable and open-source. In 2025-2026, it added native AI capabilities: an AI Agent node that runs a tool-calling loop, LLM nodes for any provider, tool nodes that let the agent call external services, and memory nodes for conversation persistence. You build agents by **dragging nodes onto a canvas** and connecting them with wires. The agent node internally runs the same LLM-tool-call loop every agent framework uses, but you configure it visually instead of writing code. With **500+ integration nodes** — Slack, Gmail, Notion, PostgreSQL, HTTP — the agent can interact with any service without writing API code. You can inspect every execution step in the UI. ## The plain Python equivalent Every n8n node maps to **a function call**. The AI Agent node is a `while` loop that calls the LLM, checks for `tool_calls`, executes the matching function, and repeats. A Slack tool node is an HTTP POST to Slack's API with a bot token. A database tool node is a SQL query with a connection string. Memory is a `messages` list saved to a file or database. The visual canvas with conditional branches becomes `if`/`else` statements. Parallel execution becomes `asyncio.gather`. The entire agent with three integrations is about **60 lines of Python**. What you lose is the visual builder, the pre-built auth handling for 500+ services, and the execution inspection UI. ## When to use n8n AI n8n AI is the right tool when **your team thinks in workflows, not code**. If you need an AI agent that monitors a Gmail inbox, classifies emails, creates Notion pages, and posts Slack notifications — n8n connects all of those with drag-and-drop nodes. The **self-hosting option** gives you full data sovereignty, which matters for teams handling sensitive data. The visual execution log lets non-engineers debug failed workflows by clicking through each step. For teams that already use n8n for automation, adding AI agents to existing workflows is natural — the agent node plugs into the same canvas alongside all your existing automation nodes. The **500+ integrations** are pre-built with authentication handling. ## When plain Python is enough If your agent calls **one or two APIs** and runs a straightforward tool-call loop, the visual builder adds overhead without proportional value. You can write an HTTP POST to Slack in three lines — you do not need a node for it. If your team is comfortable with Python, code gives you more flexibility than a visual canvas: custom error handling, complex data transformations, and version control with git. The no-code approach **trades flexibility for accessibility**. Start with plain Python if your agent is code-first, your integrations are few, and your team can write HTTP requests. Reach for n8n when the integration count grows, when non-engineers need to modify workflows, or when visual debugging and self-hosted deployment matter. ## Verdict n8n AI is the right choice when your team builds automations visually, needs 500+ integrations out of the box, and wants to self-host. But the AI agent logic inside each node is the same loop you would write in Python — the value is in the integration catalog and visual builder, not the agent pattern. ## FAQ ### Can n8n AI agents use custom tools? Yes. You can add Code nodes (JavaScript or Python), HTTP Request nodes, or database query nodes as tools for the AI Agent node. The agent calls them during its tool-calling loop just like any other integration. You can also create custom n8n nodes for specialized tools. ### How does n8n AI compare to LangChain? n8n is a visual workflow platform with AI capabilities added on top. LangChain is a code-first agent framework. n8n is better for teams that build automations visually and need many integrations. LangChain is better for developers who want full programmatic control over agent behavior. ### Is n8n AI free to use? n8n's core is open-source and self-hostable for free. The cloud-hosted version has a free tier with limited executions. AI agent features are available in both self-hosted and cloud versions — you bring your own LLM API keys. --- Source: https://tinyagents.dev/compare/n8n-ai ### LangGraph vs Building from Scratch > Compare LangGraph's stateful agent workflows to plain Python. See what nodes, edges, state channels, and checkpointing actually do — in ~60 lines. LangGraph is LangChain's stateful workflow framework — a graph of nodes (functions) connected by edges with shared state. It adds checkpointing, conditional branching, parallel fanout, and human-in-the-loop pauses on top of the core agent loop. LangGraph 1.0 GA'd in October 2025 alongside LangChain 1.0 — the API is now stability-focused, no breaking changes on the roadmap. Each piece maps to a few lines of Python you can write yourself. ## Concept map | Concept | LangGraph | Plain Python | |---|---|---| | Agent | A `StateGraph` with nodes, edges, and a typed `State` channel | A function that calls an LLM and updates a dict | | Tools | `ToolNode(tools)` paired with a conditional edge for routing | A dict of callables: `tools[name](**args)` dispatched in a loop | | Loop | `add_conditional_edges` from a node back to itself until a `END` condition | A `while` loop with an `if` to exit | | State | Typed `State` channels with reducers (`Annotated[list, add_messages]`) | A dict you mutate in place: `state['messages'].append(...)` | | Checkpointing | `MemorySaver` / `PostgresSaver` persists state per `thread_id` | `json.dump(state, open(f'{thread_id}.json', 'w'))` | | Human-in-loop | `interrupt_before` / `interrupt_after` pauses execution for review | `input()` (CLI) or a queued message in a DB | | Parallel fanout | Multiple edges from one node + reducers merge results | `asyncio.gather()` over agent calls, then merge dicts | ## What LangGraph does LangGraph models an agent as a **directed graph of nodes**. Each node is a Python function that reads from and writes to a shared `State` object. Edges connect nodes; conditional edges route based on the state. The runtime walks the graph, calling nodes, merging their state updates via reducers (e.g., `add_messages` appends instead of overwriting), and stopping at `END`. The value-add over plain LangChain `AgentExecutor` is **explicit control flow and persistence**: - conditional branching between nodes - parallel fanout with automatic merge - checkpointing with `MemorySaver` or `PostgresSaver` so a workflow can pause and resume - `interrupt_before` / `interrupt_after` for human-in-the-loop review - time-travel debugging via state history This is what production agent workflows actually need — multi-step pipelines, retries, approval gates, async fanout. LangGraph Platform (hosted) adds deployment, monitoring, and tracing on top. ## The plain Python equivalent A `StateGraph` is a `while` loop over a `state` dict. Each node is a function `def node(state) -> dict` that returns its update; you merge with `state.update(node(state))`. Conditional edges are `if`/`elif` checking a state field. Reducers like `add_messages` are `state['messages'].extend(new_msgs)`. Checkpointing is `json.dump(state, open(f'{thread_id}.json', 'w'))` after each step. Human-in-loop is `input()` or a DB-backed message queue. Parallel fanout is `asyncio.gather(node_a(state), node_b(state))` followed by `state.update({**a, **b})`. The full pattern — multi-step workflow, conditional branching, parallel fanout, checkpointing, human pause/resume — fits in about **80 lines of plain Python** (a bit more than the single-agent 60 because workflows are inherently more complex). No `Annotated[list, add_messages]` typing dance, no `interrupt_before` decorator, no separate `MemorySaver` class. ## When to use LangGraph LangGraph is the right call when your agent is a **multi-step workflow with real branching** — a research pipeline that decomposes a query, searches in parallel, judges results, and either finalizes or re-searches. Or any flow that benefits from **checkpointed pause/resume**: long-running approvals, async waits for external systems, or recovering from crashes mid-execution. It also makes sense when you're already in the LangChain ecosystem and want to graduate from `AgentExecutor` to something more controllable without rewriting your tools or memory layer. LangSmith's tracing of LangGraph workflows is genuinely good — you see node-by-node execution with state diffs, which is hard to replicate by hand. ## When plain Python is enough If your agent is a **single-loop tool-using agent** — call LLM, dispatch tools, append results, repeat — LangGraph is overkill. You don't need a graph for one node. You don't need typed state channels for one dict. You don't need `interrupt_before` if you don't have human review. The 60-line plain Python version handles this case faster, with less to learn and less to debug. The time to adopt LangGraph is when you've outgrown the loop and find yourself reaching for state machines, parallel fanout, or checkpointing — not before. Reaching for it preemptively burns weeks on framework concepts that your actual problem doesn't require. ## Verdict LangGraph earns its weight when your agent is a *workflow* — explicit branches, checkpoints, parallel branches, or a human approval gate. For a single-agent loop, the graph machinery is overkill and a plain `while` loop is faster to write, debug, and ship. ## FAQ ### What is LangGraph and how does it differ from LangChain? LangGraph is LangChain Inc's stateful workflow framework. LangChain (core) provides the building blocks — tools, memory, models, output parsers. LangGraph composes those blocks into a graph with typed state, conditional edges, checkpointing, and human-in-the-loop. Use LangChain for components; use LangGraph when you need explicit multi-step workflows or persistence. ### Can I build a multi-step agent without LangGraph? Yes. The pattern is a while loop over a `state` dict where each iteration either calls an LLM, dispatches a tool, branches via `if`, or finalizes. Checkpointing is `json.dump(state, ...)` after each step. Human-in-loop is `input()` or a queue. The full workflow pattern fits in ~80 lines of plain Python. ### When should I use LangGraph over plain LangChain AgentExecutor? Use LangGraph when you need explicit branching between nodes, parallel fanout with state merging, checkpointed pause/resume, or human-in-the-loop interrupts. AgentExecutor is fine for a single-loop tool-using agent. LangGraph adds value at the *workflow* layer above that. --- Source: https://tinyagents.dev/compare/langgraph ### Vercel AI SDK vs Building from Scratch > Compare Vercel AI SDK's TypeScript abstractions to plain code. See what generateText, streamText, tool, and useChat actually do — in ~60 lines. The Vercel AI SDK is a TypeScript-first toolkit for building LLM apps. It standardizes provider APIs (OpenAI, Anthropic, Google, etc.) behind one shape, ships streaming-first primitives (`generateText`, `streamText`, `generateObject`), defines tools with Zod schemas, and includes React hooks (`useChat`, `useCompletion`) for client UIs. Each layer maps to a few lines of TypeScript you can write yourself. ## Concept map | Concept | Vercel AI SDK | Plain Python | |---|---|---| | Agent | `generateText({ model, tools, maxSteps })` runs the loop and returns final text | An `async` function that POSTs to `/chat/completions` and dispatches tools | | Tools | `tool({ description, parameters: z.object(...), execute })` | An object of functions with parameter validation by hand or via Zod | | Streaming | `streamText` returns a `ReadableStream` of deltas with built-in parsing | `fetch` with `stream: true` and `for await (const chunk of res.body)` | | Structured output | `generateObject({ schema })` returns parsed/validated objects | `response_format: { type: 'json_schema' }` + `JSON.parse` + Zod parse | | UI hook | `useChat()` returns `{ messages, input, handleSubmit, isLoading }` | `useState` for messages, `fetch` to your endpoint, append on response | | Provider swap | Change one import: `openai('gpt-4o')` → `anthropic('claude-3-5-sonnet')` | Change one URL string in the `fetch` call | ## What Vercel AI SDK does The Vercel AI SDK does three things at once: it **normalizes LLM provider APIs** behind a single interface (`generateText`, `streamText`), it **handles the agent loop with tools** (`maxSteps`, automatic tool dispatch via Zod schemas), and it **bridges to React UIs** with `useChat` and `useCompletion` hooks that handle streaming, optimistic updates, and error states. The streaming-first design is genuinely useful — `streamText` returns a typed stream of deltas, `streamUI` returns a stream of React components from the server. `generateObject` adds JSON-schema-validated structured output. Provider portability means swapping OpenAI for Anthropic for Google is one line. Tight integration with Vercel's hosting and AI Gateway is the main moat — you get observability, rate limiting, and BYOK out of the box on Vercel. ## The plain TypeScript equivalent An agent is an `async` function that POSTs to `/chat/completions`, checks `response.tool_calls`, calls matching functions from a `tools` object, appends the results, and loops. Streaming is `fetch(url, { ...body, stream: true })` with `for await (const chunk of res.body)`. Structured output is `response_format: { type: 'json_schema' }` plus `JSON.parse` and a Zod parse if you want runtime validation. `useChat` is a `useState` plus a `handleSubmit` that POSTs to your endpoint and `setMessages` on response. Provider-swap is one URL string. The full agent + streaming + tools pattern fits in **~60 lines of TypeScript** with no dependencies beyond the standard `fetch` and `zod`. The SDK saves you the boilerplate when your app needs all of it; it doesn't save you anything if you only need part of it. ## When to use Vercel AI SDK Reach for the AI SDK when you're building a **TypeScript React app where chat is a first-class feature** — a chatbot UI, an inline AI feature in a SaaS dashboard, a v0-style generator. The `useChat` hook genuinely saves a day of `useState` plumbing. Streaming-first primitives mean you don't write parser code. Provider portability is a real lever if you A/B between models in production. It's also the natural choice if you're already on Vercel — the AI Gateway, observability, and OIDC token piping integrate without configuration. For Next.js apps specifically, the React Server Component streaming primitives (`streamUI`) are unique and powerful. ## When plain TypeScript is enough If your agent is server-only, used by a different frontend, or doesn't need streaming, the AI SDK adds dependencies you won't use. A 60-line `fetch`-based agent is simpler to debug and exposes the actual API contract — which matters when you're learning or when something breaks. For Python teams, the AI SDK doesn't apply (it's TypeScript-only). For learning how agents work under the hood, the SDK abstracts the very things you want to see — the raw HTTP, the tool dispatch loop, the streaming protocol. Build it from scratch first, then reach for the SDK when the React UI plumbing becomes the bottleneck. ## Verdict Vercel AI SDK is the right pick for **TypeScript apps where the LLM is one piece of a bigger React app** — you get streaming primitives, provider-portable tool calling, and `useChat` hooks all in one package. For a server-side agent or a learning exercise, the plain `fetch` version is simpler and shows you what's happening on the wire. ## FAQ ### What is the Vercel AI SDK and what does it do? The Vercel AI SDK is a TypeScript toolkit for building LLM apps. It provides a unified interface across LLM providers (OpenAI, Anthropic, Google), streaming-first primitives (generateText, streamText), schema-validated tool calling, structured output via Zod, and React hooks (useChat, useCompletion) for client UIs. It works on any host but integrates tightly with Vercel deploy and AI Gateway. ### Vercel AI SDK vs LangChain — which should I use? Choose Vercel AI SDK if you're building a TypeScript/Next.js app where chat is a first-class UI feature — useChat alone saves significant boilerplate. Choose LangChain if you need broad Python integrations, RAG pipelines, or a larger ecosystem. They overlap on tool calling and provider portability; Vercel is sharper on UI streaming, LangChain is sharper on integrations. ### Do I need the Vercel AI SDK to build a TypeScript agent? No. A complete tool-using agent in TypeScript is about 60 lines: a fetch call to the LLM API, an object of tool functions, and a while loop dispatching tool_calls. The SDK adds value when you also need streaming, structured output, useChat hooks, and provider swapping — not before. --- Source: https://tinyagents.dev/compare/vercel-ai-sdk ### AWS Strands Agents vs Building from Scratch > Compare AWS Strands' model-driven agent SDK to plain Python. See what Agent, tools, MCP servers, and the agent loop actually do — in ~60 lines. AWS Strands Agents is a lightweight, model-driven Python SDK for building agents released by AWS in May 2025. It provides an `Agent` class, `@tool` decorator, native MCP server support, multi-agent primitives (graphs, swarms), and integration with AWS Bedrock AgentCore for hosted deployment. The runtime is intentionally minimal — the model drives the loop. ## Concept map | Concept | AWS Strands Agents | Plain Python | |---|---|---| | Agent | `Agent(model, tools, system_prompt)` with the model running its own tool-call loop | A function that POSTs to an LLM and dispatches tool calls in a `while` loop | | Tools | `@tool` decorator on Python functions; type hints become the schema | A dict of callables: `tools[name](**args)` with a JSON schema next to it | | Loop | Implicit — the model decides when to call tools and when to stop | Explicit `while` loop checking for `tool_calls` in the response | | Multi-agent | `Graph`, `Swarm`, agents-as-tools, and a workflow primitive | Python functions calling each other with shared dicts | | MCP | First-class MCP server + client support out of the box | Implement the JSON-RPC over stdio handshake yourself (~50 lines) | | Deploy | Bedrock AgentCore for hosted runtime, observability, identity | Any Python host — FastAPI, AWS Lambda, a Cloud Run container | ## What AWS Strands does Strands inverts the usual abstraction: instead of the **framework** orchestrating the loop with explicit nodes and edges (LangGraph) or a `Crew` (CrewAI), the **model** drives the loop. You call `agent('user message')` and the SDK handles the underlying provider call, the tool-call dispatch, and the iteration until the model stops requesting tools. Tools are `@tool`-decorated Python functions; type hints become the JSON schema automatically. The distinguishing pieces are **first-class MCP support** (run an agent as an MCP server, consume MCP servers as tools) and **multi-agent primitives** that match real shapes: `Graph` for explicit DAG-style orchestration, `Swarm` for autonomous collaboration with handoff, and the agents-as-tools pattern. Production-side, AWS Bedrock AgentCore provides hosted runtime, identity, observability, and gateway features — Strands is what you build with locally; AgentCore is where it runs. ## The plain Python equivalent An agent is a function that calls the LLM API, checks for `tool_calls` in the response, looks up the tool by name in a dict, calls it with the parsed arguments, appends the result to messages, and calls the LLM again. Strands' `@tool` decorator with type-hint-derived schema is `inspect.signature(fn)` plus a small JSON schema generator (about 15 lines of Python). MCP server/client support is the JSON-RPC-over-stdio handshake (about 50 lines if you're writing it from scratch, since most of the protocol is small). Multi-agent `Swarm` is N agent functions with a shared `state` dict and a `handoff()` tool that picks which agent runs next. The full pattern — single agent, multi-agent handoff, tool dispatch, MCP support — fits in **~80 lines of plain Python** if you skip the deploy-on-AgentCore piece. ## When to use AWS Strands Strands is the right choice when you're **deploying agents on AWS** and want a thin SDK that doesn't fight Bedrock. The local SDK feels like the plain-Python pattern with the boilerplate removed — type-hint-driven schemas, model-driven loop, no class hierarchy to learn. AgentCore on the deploy side handles identity, observability, and runtime concerns that you'd otherwise build yourself. It's also a strong choice if **MCP is central to your design** — Strands treats MCP as a first-class citizen rather than an integration. If you're publishing tools as MCP servers or consuming MCP-exposed APIs, the ergonomics are better than most alternatives. The multi-agent primitives (`Swarm`, agents-as-tools) are usable but more recent than CrewAI's role-based abstraction; choose based on whether your problem looks like a swarm or a crew. ## When plain Python is enough Strands' philosophy is closest to plain Python of any major framework — the loop is implicit, tools are decorated functions, the SDK gets out of the way. If you're not on AWS and not using AgentCore, the value-add over a hand-written 60-line agent is small: you're saving ~20 lines of tool-schema generation and an `inspect`-based decorator. For learning, plain Python wins because you see the loop happen. For non-AWS production, plain Python plus a small `tool()` helper gets you to the same place with one fewer dependency to track. Reach for Strands when AWS Bedrock AgentCore deploy + MCP-first design is the goal — not before. ## Verdict AWS Strands fits AWS-heavy teams that want a thin SDK, native MCP, and a hosted runtime via Bedrock AgentCore. The model-driven design is genuinely lighter than LangChain — but for teams not on AWS, plain Python is closer to what Strands is doing than any other framework on this list. ## FAQ ### What is AWS Strands Agents? AWS Strands Agents is a lightweight, model-driven Python SDK for building agents, released by AWS in May 2025 under Apache-2.0. It provides an Agent class, @tool decorator, native MCP server/client support, multi-agent primitives (Graph, Swarm, agents-as-tools), and pairs with AWS Bedrock AgentCore for hosted deployment, identity, and observability. ### How is AWS Strands different from LangChain? Strands is model-driven — the model decides when to call tools and when to stop, and the SDK gets out of the way. LangChain has explicit AgentExecutor orchestration and a much larger integration catalog. Strands ships first-class MCP support and pairs natively with Bedrock AgentCore for AWS deployment. Choose Strands for AWS-deployed, MCP-centric agents; choose LangChain for broader integrations. ### Do I need AWS Strands to build agents on AWS? No. Plain Python with a `fetch` to Bedrock's API works the same way. Strands removes ~20 lines of tool-schema boilerplate and adds first-class MCP support and tight Bedrock AgentCore integration. Use it when those things matter; skip it when they don't. --- Source: https://tinyagents.dev/compare/aws-strands ### AWS Bedrock AgentCore vs Building from Scratch > Compare AWS Bedrock AgentCore (managed agent runtime) to plain Python. See what Runtime, Memory, Identity, and Gateway actually do — and what it costs to build the same yourself. Bedrock AgentCore is AWS's managed runtime for production agents, launched in July 2025. It is *not* a framework you import — it is the platform that runs your agent code (Strands, LangGraph, CrewAI, or anything else). It packages five managed services: Runtime (sandboxed execution), Memory (short + long term), Identity (OAuth + secrets), Gateway (tools as APIs), and Observability (traces, metrics). ## Concept map | Concept | AWS Bedrock AgentCore | Plain Python | |---|---|---| | Runtime | Sandboxed, low-latency container per session, up to 8h, MicroVM-isolated | AWS Lambda or Fargate or a Cloud Run container — you wire timeouts and isolation | | Memory | Managed short-term + long-term memory with semantic recall and namespacing | DynamoDB or Postgres + an embedding API + a `vectors` table you maintain | | Identity | OAuth flows, AWS IAM, Secrets Manager integration, per-user credential vending | Roll your own with Auth0, Cognito, or Secrets Manager + a token vending lambda | | Gateway | Turn any API or Lambda into an MCP-compliant tool with one config | Implement the MCP server protocol per tool (~50 lines each) | | Observability | OpenTelemetry traces, per-step LLM call costs, error grouping in CloudWatch | OTel SDK + a backend (Honeycomb / Grafana / Datadog) you run yourself | | Browser | Managed isolated browser tool for agent web actions | Playwright in a container with auto-shutdown after N minutes | ## What AgentCore does AgentCore is **the managed half** of AWS's agent stack (the framework half is Strands, but AgentCore runs anything). It provides five services that production agents need but that aren't fun to build: - **Runtime**: a sandboxed, MicroVM-isolated container per session, supporting long-running agents (up to 8 hours) with low cold-start latency. Sessions are isolated from each other so one user's agent can't see another's state. - **Memory**: short-term (in-session) and long-term (cross-session, vector-recalled) memory with namespacing per user. - **Identity**: OAuth 2.0 inbound, AWS IAM and Secrets Manager outbound, per-user credential vending. Solves the "how does the agent authenticate to Slack on this user's behalf" problem. - **Gateway**: turn any API endpoint or Lambda into an MCP-compliant tool. Removes the per-tool MCP server boilerplate. - **Observability**: OpenTelemetry traces of each LLM call, per-step cost attribution, error grouping. Hooks into CloudWatch. It's intentionally **framework-agnostic** — you point AgentCore at your agent code (Strands, LangGraph, plain Python) and it provides the runtime layer underneath. ## The plain Python equivalent Each AgentCore service maps to **infrastructure work you'd otherwise own**: Runtime is Lambda or Fargate plus session isolation. Memory is DynamoDB or Postgres plus an embedding API plus a vector table. Identity is Cognito or Auth0 plus Secrets Manager plus a token-vending Lambda. Gateway is per-tool MCP server implementation (the protocol is small but adds up across many tools). Observability is the OTel SDK plus a Honeycomb or Datadog backend you operate. For a single-agent prototype, all of this is overkill — you don't need MicroVM isolation for one user, you don't need long-term memory for an MVP, you don't need identity vending if there's no user-specific OAuth. The full set of capabilities lives on the **production-at-scale** side of the line. If your agent is `python agent.py` on a laptop, building any of this from scratch is wasted effort. ## When to use AgentCore AgentCore makes sense when you're **shipping agents to many users on AWS** and the operational concerns (isolation, identity, observability, memory at scale) become real problems. The MicroVM isolation is genuinely hard to reproduce — you cannot trivially run untrusted user-driven agent code in shared containers safely. The OAuth + Secrets Manager identity flow alone saves weeks if your agent needs to act as a user against external services. It's also a sensible choice if you're already standardized on AWS and want to consolidate: one platform for the agent runtime, the memory store, the auth boundary, and the observability pipeline. Pricing is pay-as-you-go, which is friendly for prototypes-to-production transitions. ## When plain Python is enough If you're not on AWS, AgentCore doesn't apply — it's an AWS service, full stop. If you're prototyping or running a single-user agent, AgentCore's services solve problems you don't have yet. Plain Python on a laptop or a small Lambda is the right place to start. Most agents in early production are simpler than their deploy story suggests. The time to graduate to AgentCore (or any managed agent runtime) is when you've **felt the pain** — a session leaked state to another user, an OAuth token was hardcoded, observability is `print()`. Adopt it then. Building on top of AgentCore from day one is fine if you're certain that's the destination, but it removes your ability to see and modify what's happening at the runtime layer, which is where most novel agent bugs live. ## Verdict AgentCore is for **production AWS deployments** where you want to skip the runtime, memory, identity, and observability work and pay AWS to do it instead. It is framework-agnostic — bring Strands, LangGraph, CrewAI, or your own. For non-AWS teams, prototypes, or anything where you want to see what the agent is doing, plain Python on Lambda or a container is simpler. ## FAQ ### What is AWS Bedrock AgentCore? AgentCore is AWS's managed runtime for production agents, launched July 2025. It is framework-agnostic — you bring your agent code (Strands, LangGraph, CrewAI, or plain Python) and AgentCore provides Runtime (sandboxed execution), Memory (short + long term), Identity (OAuth + secrets), Gateway (APIs as MCP tools), and Observability (traces + cost attribution). Pay-as-you-go on AWS. ### AgentCore vs Strands — which do I use? Both. Strands is the SDK you write your agent code with; AgentCore is the platform that runs it. They're designed to pair. You can also run AgentCore with LangGraph, CrewAI, or plain Python — Strands isn't required. Choose Strands as the framework if you want a thin Python SDK; choose AgentCore as the runtime if you're deploying on AWS and want managed isolation, memory, identity, and observability. ### Do I need AgentCore to run agents in production? No. Plain Python on Lambda, Fargate, or a Cloud Run container works fine for many production agents. AgentCore earns its place when the operational layer becomes real — multi-user session isolation, OAuth-based external API access, long-term memory across sessions, observability across hundreds of agent invocations. For single-user, single-tenant, single-purpose agents, the managed services are overkill. --- Source: https://tinyagents.dev/compare/aws-agentcore ### Eve vs Building from Scratch > Compare Vercel Eve — the open-source "Next.js for agents" — to plain code. See what agents-as-directories, Vercel Workflow SDK, and Sandbox actually do — in ~60 lines. Eve is Vercel's open-source TypeScript agent framework, launched June 17 2026. An agent is a directory: `agent.ts`, `instructions.md`, `tools/`, `skills/`, `subagents/`, `channels/`, `schedules/`. It runs on the Vercel Workflow SDK (durable execution), Vercel Sandbox (isolated code exec), and AI Gateway (multi-model routing). Positioned as "Next.js, for agents" — same convention-over-config philosophy, applied to agent apps. ## Concept map | Concept | Eve | Plain Python | |---|---|---| | Agent | A directory with `agent.ts` + `instructions.md` + subfolders — the framework wires them together | A function that POSTs to `/messages` and dispatches tools from a dict | | Tools | Each file in `tools/` exports one tool; schema comes from a Zod export | A dict of callables: `const tools = { search: async (q) => fetch(url + q) }` | | Durability | Vercel Workflow SDK checkpoints every step so a crashed agent resumes where it left off | Serialize state to JSON after each step; on restart, load the last snapshot | | Sub-agents | Each `subagents/*.ts` becomes a callable sub-agent the parent can hand off to | A function that calls another function with a different system prompt | | Sandboxed exec | Vercel Sandbox runs untrusted code in isolated micro-VMs, one API call away | A subprocess or a container you manage yourself | | Schedules | `schedules/*.ts` exports a cron expression + handler; Vercel runs it | A cron job on your host or a scheduled worker | ## What Eve does Eve is a **convention-over-configuration TypeScript agent framework**. An agent is a directory, and each subfolder maps to a specific capability: `tools/` for callable functions, `skills/` for reusable prompts, `subagents/` for hand-offs, `channels/` for input surfaces (Slack, email, HTTP), `schedules/` for cron jobs. Vercel calls this "Next.js for agents," and the analogy holds — you get filesystem-based routing but for agent capabilities instead of pages. Under the hood it composes three Vercel primitives: the **Workflow SDK** (durable, checkpointed execution), **Vercel Sandbox** (isolated micro-VMs for running untrusted or LLM-generated code), and **AI Gateway** (unified provider routing, observability, BYOK). This is the real value — three services that agent apps typically need to wire together are one install away. ## The plain TypeScript equivalent A directory-shaped agent is a folder you scan at startup. Read `tools/*.ts`, import each, build a dict. Read `subagents/*.ts`, wrap each as a function. That's ~30 lines of Node.js filesystem code. Durable execution is the harder piece. The naive version: serialize agent state (messages, tool history) to JSON or Postgres after each step, load on restart. Fine for most cases. Vercel Workflow SDK does this cleanly with automatic replay, which is genuinely useful if your agents run for hours. Sandboxed exec is `child_process.spawn` in a container, or a call to a Sandbox-as-a-service. Model routing is a switch statement across providers. The full pattern — tool dispatch, sub-agent hand-off, checkpointing, cron schedules — fits in about **80 lines of TypeScript** if you don't need micro-VM isolation. Eve's runtime pieces are what you're paying for, not the conventions. ## When to use Eve Eve is the right pick when **your agent needs durable execution, sandboxed code exec, and provider portability, and you're OK deploying on Vercel** (or willing to run its runtime pieces yourself). Long-running agent workflows that need to survive crashes, LLM-generated code that shouldn't run in your main process, and multi-model routing all come for free. If your team already ships on Vercel and uses Next.js, the mental model is instantly familiar. The convention-based structure also pays off on multi-agent codebases where you want a consistent way for engineers to add new tools or sub-agents without inventing conventions per project. ## When plain TypeScript is enough If your agent is a single tool-using loop with no crash-recovery needs, Eve is over-engineered. You don't need a Workflow SDK to append to an array. You don't need a Sandbox to call `fetch`. A 60-line `fetch`-based agent handles most production workloads and stays legible. Eve's abstractions pay off at the workflow layer — durable long-running agents, sandboxed exec, sub-agent hand-offs. Below that layer, the conventions add cognitive overhead without saving code. Start with a function and a loop; adopt Eve when durability or sandboxing becomes the actual bottleneck. ## Verdict Eve earns its keep when you want durable execution, sandboxed code exec, and multi-model routing without wiring three separate services. If you're already on Vercel, it composes; if not, the runtime pieces are the value and they don't travel. For a single-loop tool-using agent, plain TypeScript ships faster. ## FAQ ### What is Eve and how is it different from the Vercel AI SDK? Eve is a full agent framework — durable execution via Vercel Workflow SDK, sandboxed code exec, sub-agents, schedules — released June 17 2026. The Vercel AI SDK is a lower-level toolkit for LLM calls, streaming, and tool schemas. Eve uses the AI SDK underneath. If you're building a single-page chat UI, use the AI SDK; if you're building a durable long-running agent app, use Eve. ### Do I have to deploy Eve on Vercel? No — Eve is open-source (github.com/vercel/eve) and the framework itself runs anywhere Node runs. But its main value comes from the Vercel Workflow SDK, Sandbox, and AI Gateway. If you don't use those, you're paying convention cost without the runtime benefits, and a plain agent is probably lighter. ### How does Eve compare to LangGraph? Both target durable, stateful agent workflows. LangGraph models the workflow as a graph of nodes with typed state and reducers — Python-first, LangChain ecosystem. Eve models it as a directory of files with Vercel-native runtime primitives — TypeScript-first, Vercel ecosystem. Same problem, different language stacks and different mental models. --- Source: https://tinyagents.dev/compare/eve ### Flue vs Building from Scratch > Compare Flue — the Astro creator's declarative TypeScript agent framework on Cloudflare — to plain code. See what createAgent, Durable Streams, and Pi actually do — in ~60 lines. Flue is a declarative TypeScript agent framework from Fred K. Schott and the Astro team, now at Cloudflare. Announced May 1 2026, 1.0 Beta on June 16 2026. It's built on the Pi harness (the same runtime that powers OpenClaw) and Cloudflare's Agents SDK, deploys to Cloudflare Durable Objects out of the box, and also runs on Node, GitHub Actions, and GitLab CI. Agents are defined with `createAgent()` + valibot tool schemas; state is a Durable Stream you can replay. ## Concept map | Concept | Flue | Plain Python | |---|---|---| | Agent | `createAgent({ model, instructions, tools })` — declarative config, framework runs the loop | A function that POSTs to `/messages` and dispatches tools from a dict | | Tools | Registered with valibot schemas: `{ name, description, schema, execute }` | A dict of callables with a JSON schema built by hand or generated from types | | State | Durable Streams — replayable, checkpointed event log stored in Cloudflare Durable Objects | Append every step to an array or a database table; replay by re-reading in order | | Deployment | One config controls deploys to Cloudflare, Node, GitHub Actions, or GitLab CI | A container or a serverless function you deploy per environment | | Runtime | The Pi harness — same runtime as OpenClaw, so agents share tooling with that ecosystem | Whatever process manager and error handling you assemble yourself | | Cloudflare-native | Durable Objects give per-agent persistence and locking without an external DB | Redis, Postgres, or a file-based lock — pick one, wire it, maintain it | ## What Flue does Flue is **declarative-first**: you describe the agent (`createAgent({ model, instructions, tools })`) and the framework owns the loop. Tools register with valibot schemas — a smaller, faster-typed alternative to Zod — and state is stored as a **Durable Stream**: a replayable event log persisted in Cloudflare Durable Objects, so an agent can crash and pick back up. What makes it distinct from other TypeScript agent frameworks is the **cross-runtime deployment story**. The same agent config deploys to Cloudflare Durable Objects (its home turf), plain Node, GitHub Actions, or GitLab CI — useful when the same logic runs both in production and inside a CI pipeline. It's built on the **Pi harness**, the same runtime powering OpenClaw, so agents in that ecosystem share tooling and observability. ## The plain TypeScript equivalent A declarative agent is just a config object plus a dispatcher. Read the config, register the tools in a dict, run the loop. Valibot schemas are Zod-shaped and interchangeable — pick either. Durable Streams are a checkpointed event log. Naive version: append `{ step, state, timestamp }` to Postgres or a file after each iteration; on restart, replay in order. Cloudflare Durable Objects give you the same replayability with per-agent locking and no external DB, which is real value if you're already on Cloudflare Workers. Cross-runtime deployment is a build script that targets `wrangler` vs `node` vs a GitHub Action YAML. The declarative agent + event-log state + multi-target build fits in about **80 lines of TypeScript** if you're not paying for Durable Object locking. ## When to use Flue Flue is the pick when the deploy target is **Cloudflare Workers + Durable Objects**, and you want an agent framework that treats that as the primary runtime rather than a bolt-on. Durable Objects give per-agent persistence and locking for free — that's a real primitive to build on, and Flue exposes it directly. It's also the right call when the same agent needs to run in **both production and CI** — e.g., a research agent that runs on user demand in a Worker and also nightly in a GitHub Action. The cross-runtime config saves you from maintaining two builds. Teams building on the Pi/OpenClaw stack get tooling reuse across their agent workloads. ## When plain TypeScript is enough If your agent runs in one place, doesn't need replayable state, and doesn't touch Cloudflare, Flue's runtime story doesn't pay off. You're paying for conventions and a valibot dependency without using the Durable Object piece. A 60-line `fetch`-based agent with a JSON file for state handles the majority of production workloads without another framework in the tree. Reach for Flue when Durable Objects, Pi tooling, or cross-runtime deploy is the actual constraint — not before. ## Verdict Flue is the natural choice when the deploy target is Cloudflare and you want a TypeScript-first, declarative agent framework tuned for Durable Objects. Its cross-runtime story (Cloudflare + Node + CI) is genuinely useful if agents run in more than one place. For a single-agent loop that doesn't need persistence, plain TypeScript is simpler. ## FAQ ### What is Flue and who made it? Flue is a declarative TypeScript agent framework announced May 1 2026, 1.0 Beta on June 16 2026. It was built by Fred K. Schott and the Astro core team after they joined Cloudflare in January 2026. It runs on the Pi harness and Cloudflare's Agents SDK, and deploys to Cloudflare Durable Objects, Node, GitHub Actions, or GitLab CI. ### How does Flue compare to Mastra? Both are TypeScript-first agent frameworks. Mastra is deploy-agnostic and ships its own Studio debugger + RAG pipeline + workflow engine; it's positioned as a full toolkit. Flue is thinner and Cloudflare-native — its main leverage is Durable Objects for per-agent state and locking, plus a cross-runtime deploy story that includes CI environments. Choose Mastra for batteries-included TypeScript agents; choose Flue when Cloudflare is the target. ### Do I need Cloudflare to use Flue? No — Flue also runs on Node, GitHub Actions, and GitLab CI. But its most distinctive value is the Durable Objects integration, which only works on Cloudflare. Without Cloudflare, you get a competent declarative TypeScript agent framework, but you're leaving the biggest lever on the table. --- Source: https://tinyagents.dev/compare/flue ## Head-to-head framework comparisons ### CrewAI vs LangChain: Which Agent Framework to Use? > Compare CrewAI and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs LangChain, head to head CrewAI models work as a team: `Agent(role, goal, backstory)` objects bound to `Task` items, scheduled by a `Crew` with `process=sequential` or `hierarchical`. LangChain models work as a chain: an `AgentExecutor` wrapping an `LLMChain`, `PromptTemplate`, and `OutputParser`, with one agent driving a tool registry. CrewAI thinks in **roles**; LangChain thinks in **components**. LangChain's surface area is huge — document loaders, vector stores, embedding models, retrievers, plus `LangSmith` for tracing and `LangServe` for deployment. CrewAI ships a tighter core: `@tool`, `Crew`, three memory classes (`ShortTermMemory`, `LongTermMemory`, `EntityMemory`), and first-class MCP support. If you need fourteen vector store integrations on day one, LangChain has them. If you don't, CrewAI's smaller dependency tree is less to maintain. LangChain fits **single-agent, integration-heavy** workflows — RAG pipelines, multi-provider swaps, anything where the LLM is one node in a bigger graph (use `LangGraph` for branching state). CrewAI fits **multi-agent role separation** — a researcher hands off to a writer hands off to an editor, each with its own `backstory` and `tools` list. The deciding question: is your hard problem the integrations around the agent, or the orchestration between agents? ## Pick CrewAI if Pick crewai if your project lives or dies on agents with distinct roles handing work to each other. - **Named role separation**: You want a `"Senior Researcher"` and a `"Technical Writer"` with different `backstory` strings, and those labels actually drive prompt quality — not just decoration. - **Sequential or hierarchical task flow**: Your work decomposes cleanly into `Task` items, and `Crew(process=hierarchical)` with a manager agent matches how you'd assign the work to humans. - **Delegation guardrails**: You want agents that hand off subtasks but can only delegate within their crew, keeping loops bounded without you writing a scheduler. ## Pick LangChain if Pick langchain if your project lives or dies on a wide integration surface around a single agent. - **RAG and retrieval depth**: You need vector stores, document loaders, text splitters, and embedding models that already compose — the `langchain` + `langchain-community` catalog is the reason to be here. - **Provider portability**: You want to swap OpenAI for Anthropic or a self-hosted model by changing one class, and you'll pay the `AgentExecutor` abstraction cost to keep that lever. - **LangSmith observability**: Production tracing, evals, and prompt versioning matter to your team, and you'd rather adopt the platform than build it. `LangGraph` for branching workflows is the bonus. ## What both add Both frameworks insert a class hierarchy between you and the actual `/chat/completions` call. CrewAI gives you `Agent`, `Task`, `Crew`; LangChain gives you `AgentExecutor`, `LLMChain`, `PromptTemplate`, `OutputParser`. When a tool call misfires, the stack trace runs through framework code you didn't write. The dependency footprint is real — LangChain's transitive deps are heavy with constant version churn, CrewAI is leaner but still ships memory classes and a process scheduler. Either way, expect a few days of ramp-up before a new teammate ships confidently. --- Source: https://tinyagents.dev/vs/crewai-vs-langchain ### AutoGen vs LangChain: Which Agent Framework to Use? > Compare AutoGen and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AutoGen vs LangChain, head to head AutoGen's primitive is the conversation: `ConversableAgent` instances exchange messages, and `GroupChat` + `GroupChatManager` decide who speaks next via round-robin, random, or LLM-based selection. LangChain's primitive is the single-agent loop: `AgentExecutor` wraps `LLMChain`, `PromptTemplate`, and `OutputParser` to dispatch `@tool`-decorated functions on one agent's behalf. AutoGen models the *interaction between agents*; LangChain models *one agent's interaction with the world*. LangChain has roughly 2.3× the GitHub stars (132k vs 57k), VC backing from Sequoia and Benchmark, and a commercial stack — `LangSmith` for tracing, `LangServe` for deployment, `LangGraph` for typed workflows. AutoGen ships from Microsoft Research with a tighter scope: chat orchestration plus a code-execution sandbox, no SaaS tier on top. LangChain's catalog covers vector stores, document loaders, embedding models, and text splitters across dozens of providers; AutoGen leaves most of that integration work to you. Use AutoGen when the *conversation structure* is the hard part — author/reviewer iteration, planner/executor pairs, dynamic speaker selection, sub-tasks via `register_nested_chats()`, or agents that need to write and run code mid-thread. Use LangChain when the *integration surface* is the hard part — RAG with `VectorStoreRetrieverMemory`, swapping LLM providers without rewriting business logic, or wiring `LangGraph` state channels across multi-step workflows with conditional branching. The two frameworks rarely solve the same problem; the fastest way to choose is to ask whether your bottleneck is *who talks to whom* or *what plugs into what*. ## Pick AutoGen if Pick autogen if your project lives or dies on agents debating, critiquing, or collaborating with each other. - **Multi-agent orchestration**: `GroupChat` with `GroupChatManager` and LLM-based speaker selection is non-trivial to build well. AutoGen's tested implementation earns its keep when speaker order isn't predetermined. - **Nested sub-tasks**: `register_nested_chats()` lets an agent pause its main thread, run a sub-conversation, and inject the result back. Useful when one turn needs its own multi-turn loop. - **Code-writing agents**: AutoGen ships a code-execution sandbox so agents can write and run code as part of the conversation. LangChain has no first-class equivalent. ## Pick LangChain if Pick langchain if your project lives or dies on integrations, provider portability, or production tooling. - **Integration catalog**: vector stores, document loaders, embedding models, and text splitters across dozens of providers. AutoGen's scope is narrower and you'll write most of that glue yourself. - **Provider swapping**: change one class to move from OpenAI to Anthropic without touching business logic. Worth real money when procurement or pricing forces a switch mid-project. - **Observability and workflows**: `LangSmith` for tracing and evals, `LangGraph` for typed state channels with conditional branching and parallel nodes. AutoGen offers neither at this level of polish. ## What both add Both frameworks put a class hierarchy between your code and the LLM API. `ConversableAgent` and `AgentExecutor` are runtime objects you debug *through*, not functions you read top-to-bottom — stack traces get longer, and the surface area for version-pinning conflicts grows with every integration you pull in. Both also impose ramp-up cost on every new engineer. The team has to learn `register_for_llm` / `register_for_execution` or `@tool` / `StructuredTool` / `BaseTool` before shipping a one-tool agent. That's a fair trade when the abstractions earn their keep, and pure tax when they don't. --- Source: https://tinyagents.dev/vs/autogen-vs-langchain ### LangChain vs OpenAI Agents SDK: Which Agent Framework to Use? > Compare LangChain and OpenAI Agents SDK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs OpenAI Agents SDK, head to head LangChain is a **class hierarchy** — `AgentExecutor`, `LLMChain`, `PromptTemplate`, `BaseTool`, `ConversationBufferMemory` — built to abstract over any LLM provider. The Agents SDK is **four primitives** (`Agent`, `Runner`, handoffs, guardrails) intentionally coupled to OpenAI's chat completions API. LangChain inverts control: you compose objects and `AgentExecutor.invoke()` runs the loop. The SDK does the same with `Runner.run()`, but the surface area is small enough you can read the source in an afternoon. LangChain ships **dozens of integrations** — document loaders, text splitters, embedding models, vector stores — plus LangSmith for tracing and LangServe for deployment. That catalog is the real product. The Agents SDK ships almost nothing beyond the core loop. **Auto-schema generation** from Python type hints is the one ergonomic win — change a function signature, the JSON tool schema updates. No vector store wrappers, no retriever classes, no memory hierarchy. Use LangChain when your agent is **one node in a larger pipeline** with retrieval, multiple providers, and observability needs — or when you want **LangGraph** for branching, parallelism, and durable state across nodes. Use the Agents SDK when you're committed to OpenAI and want **multi-agent routing** via `Handoff` plus the `InputGuardrail`/`OutputGuardrail` tripwire pattern as a standard. It's a reference implementation, not a batteries-included framework — which is the point. ## Pick LangChain if Pick langchain if your project lives or dies on the breadth of integrations and provider portability. - **Multi-provider swap**: Change one class to move from OpenAI to Anthropic to a local model. If your contract or roadmap requires provider-agnostic code, `BaseChatModel` pays for itself. - **RAG with batteries included**: Document loaders, text splitters, embeddings, and vector store wrappers are already written. You compose, you don't plumb HTTP. - **LangGraph + LangSmith**: Conditional branching, parallel execution, durable state across nodes, plus production tracing and eval. Worth the dependency tree when workflows get genuinely complex. ## Pick OpenAI Agents SDK if Pick openai-agents-sdk if you're committed to OpenAI and want the thinnest possible standard on top of `chat.completions`. - **Auto-schema generation**: Type-hinted Python functions become tool schemas with no manual JSON. Change the signature, the schema follows — fewer drift bugs. - **Handoffs as a primitive**: `Handoff` between `Agent` objects gives you a tested multi-agent routing pattern instead of ad-hoc dispatch logic scattered through your codebase. - **Guardrail tripwires**: `InputGuardrail` and `OutputGuardrail` standardize input/output validation. Cleaner than `if` statements sprinkled around the loop, and the pattern is consistent across agents. ## What both add Both frameworks add a **dependency tree and a vocabulary** your team has to learn before shipping. LangChain's is large and changes often; the Agents SDK's is small but ties you to OpenAI's API surface and its release cadence. Both also put **abstractions between you and the wire**. When a tool call misfires or a prompt regresses, you debug through `AgentExecutor` internals or `Runner` lifecycle hooks instead of the actual HTTP request. That ramp-up is fine for large teams; it's overhead for a single-agent, single-provider build. --- Source: https://tinyagents.dev/vs/langchain-vs-openai-agents-sdk ### Agno vs LangChain: Which Agent Framework to Use? > Compare Agno and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Agno vs LangChain, head to head Agno is a single `Agent` class you configure declaratively — pass a `model`, `tools`, `instructions`, and optional `knowledge`, then call `agent.run()`. LangChain splits the same job across `AgentExecutor`, `LLMChain`, `PromptTemplate`, and an `OutputParser`, with `LangGraph` layered on top for stateful branching. Agno hides the loop behind one method; LangChain exposes it as composable primitives you wire together. LangChain is the larger surface area by an order of magnitude — 132k stars, document loaders, vector stores, embedding adapters, plus `LangSmith` for tracing and `LangServe` for deployment. Agno's catalog is narrower: built-in toolkits for web search, SQL, and file I/O, plus first-class **multi-modal** (vision, audio) support that LangChain treats as a model-config detail. If you need a specific Pinecone/Weaviate/Cohere integration today, LangChain almost certainly has it; Agno expects you to bring more of your own glue. Use Agno when the agent itself is the product — especially **multi-modal pipelines** or `Team`-based orchestration with `mode="coordinate"` across a few agents sharing memory. Use LangChain when the agent is one node in a larger integration graph: RAG over a managed vector DB, swappable providers behind one interface, or `LangGraph` workflows with typed state channels and conditional edges. Agno optimizes for a clean single-agent or small-team build; LangChain optimizes for plugging into an existing stack. ## Pick Agno if Pick agno if your project lives or dies on lightweight, multi-modal agents without LangChain's class hierarchy. - **Multi-modal is core**: Vision and audio agents are first-class in Agno's `Agent` class, not a bolted-on model parameter. Useful when you're processing images, audio, or mixed inputs as primary content. - **Team orchestration without LangGraph**: The `Team` class with `mode="sequential"`, `"parallel"`, or `"coordinate"` and shared memory covers most multi-agent patterns in a few lines of config. - **Built-in toolkits cover your stack**: Web search, SQL, and file ops ship in-box, and `SqlAgentStorage`/`PostgresAgentStorage` handle session persistence without extra plumbing. ## Pick LangChain if Pick langchain if your project lives or dies on integration breadth and production tooling around the agent. - **Provider and store interchangeability**: Swapping OpenAI for Anthropic, or Pinecone for Weaviate, is a class change — useful when procurement, latency, or cost forces you to move between vendors. - **`LangSmith` and `LangServe` matter**: Tracing, eval datasets, and a deployment target ship as a coherent product. Hard to replicate quickly in Agno. - **`LangGraph` for complex workflows**: Typed state channels, conditional edges, and parallel nodes are worth the abstraction tax once you have branching logic, human-in-the-loop steps, or long-running stateful runs. ## What both add Both frameworks pull in a non-trivial dependency tree and a vocabulary your team has to learn — `Agent`/`Team`/`knowledge` in Agno, `AgentExecutor`/`LLMChain`/`OutputParser`/`LangGraph` in LangChain. Upgrades occasionally change semantics (Phidata → Agno 2.0, LangChain's repeated agent-API rewrites), and stack traces tend to land inside framework internals rather than your code. Both also abstract the actual `/chat/completions` call, which makes provider-specific behavior — tool-call formats, streaming quirks, prompt-cache headers — harder to inspect when something misbehaves in production. --- Source: https://tinyagents.dev/vs/agno-vs-langchain ### LangChain vs LlamaIndex: Which Agent Framework to Use? > Compare LangChain and LlamaIndex side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs LlamaIndex, head to head LangChain is a **general-purpose orchestration layer** — `AgentExecutor`, `@tool`, `ConversationBufferMemory`, and `OutputParser` are designed to be swappable across providers and use cases. LlamaIndex starts from data: `VectorStoreIndex` is the primitive, and `ReActAgent` + `QueryEngineTool` exist to let an agent reason over that index. LangChain's loop is provider-agnostic plumbing. LlamaIndex's loop is built around **retrieval as a first-class tool call**. LangChain ships the larger catalog: dozens of LLM providers, document loaders, vector stores, plus **LangSmith** for tracing and **LangServe** for deployment. It has Sequoia/Benchmark backing and ~3.4M weekly npm downloads. LlamaIndex is narrower but deeper on data — **LlamaHub** connectors, document parsers (PDF, HTML, SQL), and pluggable vector stores (Pinecone, Weaviate, pgvector). If you measure ecosystem by integrations-per-domain, LangChain wins on breadth, LlamaIndex on retrieval depth. If your agent juggles APIs, databases, and multiple LLM providers, LangChain's `AgentExecutor` + integration catalog is the better fit — and **LangGraph** handles branching workflows across nodes. If your agent's job is to reason over your documents, LlamaIndex's index-as-tool pattern is purpose-built: one `QueryEngineTool` line and the agent can query a collection alongside any other `FunctionTool`. Picking the wrong one means fighting the framework's center of gravity. ## Pick LangChain if Pick langchain if your project lives or dies on the breadth of integrations and orchestration tooling around the agent. - **Multi-provider, multi-integration surface**: You're swapping between OpenAI and Anthropic, plugging into 3+ vector stores, or composing document loaders with custom chains. The class hierarchy pays off when integration count is high. - **Production observability matters**: You want `LangSmith` traces, eval runs, and prompt versioning without building it yourself. - **Branching workflows beyond a single loop**: `LangGraph` state channels, conditional edges, and parallel nodes are worth the abstraction tax for genuinely multi-step pipelines. ## Pick LlamaIndex if Pick llamaindex if your agent's core job is reasoning over your data, not orchestrating arbitrary tools. - **Retrieval is the product**: Multiple document collections, varied retrieval strategies, re-ranking — `VectorStoreIndex` + `QueryEngineTool` turns each index into a callable tool in one line. - **Heavy document ingestion**: PDFs, HTML, SQL, Notion, Slack — **LlamaHub** connectors and parsers save weeks of glue code over hand-rolled pipelines. - **Index-aware agent reasoning**: You want `ReActAgent` to decide *which* index to query, not just whether to retrieve. The framework treats data sources as peer tools, which is hard to replicate cleanly by hand. ## What both add Both frameworks pull in **large dependency trees** and put a class hierarchy between you and the actual `/chat/completions` call. `AgentExecutor`, `AgentRunner`, `FunctionTool`, `@tool` — each is a layer to learn, debug through, and keep pinned across version bumps. Breaking changes in either framework have shipped quarterly. Ramp-up is real: new engineers learn the framework's vocabulary before they learn your agent. If your loop is a `while` with three tools and one provider, that overhead buys you very little — and shows up every time you read a stack trace at 2 AM. --- Source: https://tinyagents.dev/vs/langchain-vs-llamaindex ### Google ADK vs LangChain: Which Agent Framework to Use? > Compare Google ADK and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Google ADK vs LangChain, head to head ADK is **opinionated about agent topology**: you build a tree of `LlmAgent` nodes with `sub_agents`, then orchestrate them with `SequentialAgent`, `ParallelAgent`, and `LoopAgent` workflow primitives. LangChain is **opinionated about composition**: `AgentExecutor` runs a single ReAct-style loop over a `@tool` registry, and LangGraph adds typed state channels when you need branching. Where ADK assumes hierarchical delegation as the default, LangChain assumes a flat agent with pluggable parts — `PromptTemplate`, `OutputParser`, `ConversationBufferMemory` — that you wire together yourself. LangChain has the larger surface area: 132k stars since 2022, thousands of integrations (document loaders, vector stores, embeddings), and `LangSmith` for tracing. Provider-agnostic by design — swapping OpenAI for Anthropic is a class change. ADK is younger (April 2025, 18k stars) and **leans toward Gemini and Vertex AI**. Other providers work, but the deployment story (Vertex AI Agent Engine, Cloud Run, bidirectional audio/video, Session/State services) is built for Google Cloud customers. The integration catalog is thinner; the cloud integration is deeper. Use ADK when the system is **multi-agent by design** — a root coordinator delegating to specialized children, parallel fan-out via `ParallelAgent`, and managed deployment on Vertex AI. Use LangChain when the agent itself is simple but the **integration surface is wide**: RAG over a specific vector store, multi-provider support, and `LangSmith` observability. ADK optimizes for orchestration topology; LangChain optimizes for connector breadth. ## Pick Google ADK if Pick google-adk if your project lives or dies on multi-agent orchestration deployed on Google Cloud. - **Hierarchical delegation is the architecture**: You actually need a root `LlmAgent` routing to specialized `sub_agents`, not just a single agent with a few tools. ADK's tree model and `Runner.run()` dispatch are the point. - **Vertex AI is your deployment target**: You want managed scaling, Agent Engine, and built-in streaming with bidirectional audio/video. The Gemini and Google Cloud coupling is a feature, not a tax. - **Workflow primitives as first-class units**: `SequentialAgent`, `ParallelAgent`, and `LoopAgent` map directly onto how your team thinks about pipelines, and you want them composable rather than encoded as ad-hoc function calls. ## Pick LangChain if Pick langchain if your project lives or dies on stitching many integrations together across providers. - **Provider-agnostic by requirement**: You need to swap OpenAI, Anthropic, Cohere, or local models behind one interface, and you don't want each swap to touch business logic. LangChain's class hierarchy pays off here. - **RAG and connector breadth**: Document loaders, text splitters, embedding models, and vector stores are core to the product. The integration catalog saves weeks of glue code. - **`LangSmith` and `LangGraph` are on the roadmap**: You want production tracing, eval datasets, and conditional state-machine workflows with typed reducers — not just a single agent loop. ## What both add Both frameworks pull in a substantial dependency tree and a vocabulary your team has to learn before reading any agent code. `LlmAgent` plus `Runner` plus `SequentialAgent` is a different mental model from `AgentExecutor` plus `@tool` plus `ConversationBufferMemory`, but in both cases the actual LLM call sits behind several layers of indirection. That indirection is the cost: stack traces get longer, debugging means stepping through framework internals, and version upgrades occasionally break public APIs. If your agent is a single loop with a handful of tools, you may be paying for orchestration and integrations you'll never call. --- Source: https://tinyagents.dev/vs/google-adk-vs-langchain ### Anthropic Agent SDK vs LangChain: Which Agent Framework to Use? > Compare Anthropic Agent SDK and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Anthropic Agent SDK vs LangChain, head to head These two aren't really in the same category, even though people search for them as if they are. The Anthropic Agent SDK is the runtime that powers Claude Code, repackaged as a library. You import `Agent`, hand it tools, and call `run`. The loop is opinionated, the bash/file/web tools are pre-built, and there are 18 lifecycle hooks for the things you'd otherwise reach in and override. It's a working agent in five lines, and it only speaks Claude. LangChain is a kit of parts. `AgentExecutor` wraps an LLM, `@tool` decorates your functions, `ConversationBufferMemory` (or its current replacement) handles history, an `OutputParser` rescues malformed responses, and you compose those into whatever shape you want. It runs against any model the integration list covers, which is most of them. The cleanest way to think about this: the Anthropic SDK is "Claude with extension points." LangChain is "a way to build any agent that has to talk to several things." If your agent is a Claude assistant that touches files, runs shell, and hits a few MCP servers, the SDK is shorter and the integrations are already wired. If your agent has to swap providers, do RAG over a specific vector store, or live as one node inside a larger pipeline, LangChain pays for its weight. The MCP question is real. Anthropic's catalog is MCP-shaped — one line of config to Playwright, Slack, GitHub, or any of the registry servers — but the loop is Claude-only. LangChain has the wider catalog (document loaders, embeddings, vector stores, plus `LangSmith` for tracing and `LangServe` for deployment), but you'll often write the MCP bridge yourself if you want it. ## Pick Anthropic Agent SDK if Pick the Anthropic SDK when the agent has to actually touch things, and you've already chosen Claude. - The built-in `bash`, file, and web tools save you from writing safe `subprocess.run` wrappers and a sandbox. - MCP is your integration story — Playwright, Slack, GitHub, databases — and you want one-line config, not per-service HTTP clients. - You need lifecycle hooks for cost tracking, audit logs, or pre/post tool-call guardrails without forking the loop. - You're never going to swap models, so the provider lock-in doesn't bother you. ## Pick LangChain if Pick LangChain when the agent is the small part of something larger. - You need to swap OpenAI for Anthropic for an open model without rewriting the agent — the LLM abstraction earns its keep here. - The actual work is RAG: document loaders, splitters, embeddings, vector stores wired together. The catalog beats writing each one by hand. - `LangGraph` is the right tool for what you're building — typed state, conditional branches, parallel nodes — and you want `LangSmith` traces in production. - You're shipping in a polyglot stack where Python is one of several services, and you want one library that works across them. ## What both add Both come with a vocabulary your team has to learn before they ship anything. LangChain has `AgentExecutor`, `BaseTool`, `OutputParser`, plus LangGraph's state model. The Anthropic SDK has hooks, MCP server configs, and runtime opinions about what a loop should do. When something breaks, you're stepping through framework internals either way. Both also assume you want their catalog. MCP servers for Anthropic, vector stores and loaders for LangChain. If your agent talks to one model and three internal functions, most of that surface area is overhead you'll never use — which is the case the bare `/lesson/agent-function` walk-through makes. ## Same task in Anthropic Agent SDK and LangChain Here is the same job in both: an agent that takes a question, searches the web when it needs to, and answers in plain text. ### Anthropic Agent SDK ```python from anthropic.agents import Agent from anthropic.agents.tools import web_search agent = Agent( model="claude-sonnet-4-5", system="You are a research assistant. Search the web when you need current information.", tools=[web_search], ) result = agent.run("What's the GitHub star count of LangChain today?") print(result.text) ``` The runtime owns the loop. `web_search` is a stock tool with sandboxing and retries already wired. The lifecycle hooks attach if you want `pre_tool_call` cost tracking or output guardrails — none of that is in this snippet, but you don't write the plumbing for it either. ### LangChain (with LangGraph) ```python from langchain_anthropic import ChatAnthropic from langchain_community.tools.tavily_search import TavilySearchResults from langgraph.prebuilt import create_react_agent model = ChatAnthropic(model="claude-sonnet-4-5") search = TavilySearchResults(max_results=3) agent = create_react_agent(model, tools=[search]) result = agent.invoke({ "messages": [("user", "What's the GitHub star count of LangChain today?")] }) print(result["messages"][-1].content) ``` You bring the search provider (Tavily, SerpAPI, Brave — your call). `create_react_agent` is LangGraph's prebuilt loop. `invoke` returns the full state dict, so you fish out the last message. Swap `ChatAnthropic` for `ChatOpenAI` and nothing else changes — that portability is what the extra setup is buying you. ### Side by side | | Anthropic SDK | LangChain | |---|---|---| | Models supported | Claude only | Any provider in the catalog | | Search tool | Built-in (`web_search`) | BYO (Tavily / SerpAPI / Brave) | | Loop primitive | `agent.run()` (runtime-driven) | `create_react_agent(...).invoke()` (LangGraph) | | Lines of code | ~5 | ~10 | | Extension points | 18 lifecycle hooks | LangChain callbacks + LangGraph state edits | For a tool-using single-Claude agent, the SDK is shorter and the search is free. For anything that needs to swap providers or wire RAG behind the search, LangChain pulls ahead — and `create_react_agent` keeps the line count from getting silly. ## Migrating between Anthropic Agent SDK and LangChain LangChain to the Anthropic SDK happens when a team has committed to Claude and wants the framework weight gone. The swap is mostly mechanical. Replace `ChatAnthropic` plus `@tool`-decorated functions with `Agent(model=..., tools=[...])`. `AgentExecutor`, `PromptTemplate`, and `OutputParser` all disappear. Most LangChain tools are already plain Python functions, so they port without ceremony. Two things to plan for. LangSmith doesn't have a turnkey equivalent in the SDK yet — the hooks let you build a thin tracer, but you're writing it. And `ConversationBufferMemory` gets replaced by passing the message history into `agent.run` directly; the SDK doesn't impose a memory class. Anthropic SDK to LangChain is the reverse motion, usually triggered by needing multiple providers or RAG that's heavier than what MCP servers cover. Tools wrap as plain functions or `@tool`-decorated callables and pass into `create_react_agent`. The loop semantics are similar — neither framework iterates differently. The SDK's `web_search` becomes `TavilySearchResults` (or your search of choice), and now you're managing a search-provider key. Lifecycle hooks become `BaseCallbackHandler` subclasses or LangGraph node interceptors. Budget a day or two for that retrofit. In both directions, the agent's business logic — which tools, how to compose them, how to validate output — stays the same. What changes is the runtime layer and the integration catalog around it. --- Source: https://tinyagents.dev/vs/anthropic-sdk-vs-langchain ### DSPy vs LangChain: Which Agent Framework to Use? > Compare DSPy and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## DSPy vs LangChain, head to head DSPy treats prompts as a **compilation target**: you declare a `Signature` like `"question -> answer"`, wrap it in `ChainOfThought` or `ReAct`, and let `BootstrapFewShot` or `MIPROv2` search for the best instructions and few-shot examples against a metric. LangChain treats agents as **orchestration**: `AgentExecutor` runs the reason-act-observe loop, `@tool` decorates your functions, and `ConversationBufferMemory` carries history — prompts are hand-authored strings you pass through. One framework asks "what should this prompt be?" The other asks "how do these pieces wire together?" LangChain's surface area is enormous — dozens of `BaseLLM` providers, document loaders, vector stores, output parsers, plus `LangGraph` for stateful workflows and `LangSmith` for tracing and evals. DSPy's surface is small and academic: a handful of modules (`Predict`, `ChainOfThought`, `ReAct`, `ProgramOfThought`), a few optimizers, and `dspy.Evaluate`. If your agent is one node in a sprawling integration graph, LangChain has the catalog. If your agent is a focused pipeline you want to tune against data, DSPy stays out of the way. LangChain's `AgentExecutor` is the shortest path to **product engineering** — chatbots, RAG, tool-calling agents that need to ship next sprint. DSPy is the shortest path to **measurable accuracy** — classification chains, multi-hop QA, anything where you have a labeled dev set and a metric like F1 or exact-match. Switching LLM providers is roughly equivalent effort in both: swap a class in LangChain, re-run the optimizer in DSPy. The real split is whether your bottleneck is wiring components or improving prompt quality. ## Pick DSPy if Pick DSPy if your project lives or dies on prompt accuracy and you have data to optimize against. - **Labeled dev set in hand**: You already have input/output examples and a metric like exact-match, F1, or a custom judge. `dspy.Evaluate` plus `BootstrapFewShot` turns that into systematic prompt improvement instead of vibes. - **Multi-model portability**: You ship across GPT-4, Claude, and open-weights models, and rewriting prompts per provider is the actual bottleneck. Re-running the optimizer beats hand-tuning each model. - **Research-grade reproducibility**: You need pipelines you can re-derive months later. `Signature` plus `Module.forward()` makes the pipeline declarative and version-controllable in a way f-strings never are. ## Pick LangChain if Pick LangChain if you're integrating many moving parts and the agent is one of them. - **Heterogeneous integrations**: You need a Pinecone retriever, an unstructured PDF loader, a Confluence connector, and a Slack tool in the same app. The catalog of `BaseRetriever`, `DocumentLoader`, and `BaseTool` implementations is the real product. - **LangSmith and LangServe in the loop**: Your team already runs traces, evals, and deployments through LangChain's commercial tooling. Staying inside the framework keeps that pipeline intact. - **LangGraph for stateful workflows**: You need conditional branching, parallel nodes, and persistent state across steps. `StateGraph` with typed reducers is the framework's strongest piece for non-linear agents. ## What both add Both frameworks pull in a meaningful dependency tree and a vocabulary your team has to learn before being productive. DSPy expects you to think in `Signatures`, `Modules`, and metrics; LangChain expects chains, executors, and parsers. Debugging a misbehaving agent means stepping through someone else's abstractions, not your own logic. Both also defer real decisions to the framework — DSPy decides what your prompt becomes after compilation, LangChain decides how iteration happens inside `AgentExecutor`. That trade is worth it when the defaults match your problem, and a tax when they don't. --- Source: https://tinyagents.dev/vs/dspy-vs-langchain ### LangChain vs Mastra: Which Agent Framework to Use? > Compare LangChain and Mastra side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs Mastra, head to head LangChain and Mastra solve the same problem in two different languages, and most of the decision comes down to that fact. LangChain is Python-first and built out of classes. `AgentExecutor` wraps an `LLMChain`, which wraps a `PromptTemplate` and an `OutputParser`. Memory injects as a class. Tools are subclasses of `BaseTool` (or functions you decorate with `@tool`). When you want to swap a vector store or an LLM provider, you change one class — that's the abstraction earning its weight. Mastra is TypeScript-native and flatter. `new Agent({ model, instructions, tools })` is the whole construction. Tools come from `createTool({ name, schema, execute })` with Zod for input validation, so the schema lives in the type system instead of being inferred from a docstring. There's no `AgentExecutor` to wrap or `OutputParser` to subclass — the runtime owns the dispatch. The ecosystem gap is real. LangChain has ~132k GitHub stars, ~3.4M weekly npm downloads, and integrations for every vector store, document loader, and embedding model you've heard of, plus `LangSmith` for hosted observability and `LangServe` for deployment. Mastra is younger — 22k stars, 244k weekly downloads, YC W25, built by ex-Gatsby people — and ships its own `Mastra Studio` debugger, a built-in RAG pipeline, and a Composio bridge for third-party tools. The catalog is narrower but it's also more cohesive. The honest decision is mostly about your stack. If your team writes Python and your data tooling assumes pandas, LangChain.js exists but trails the Python release on features and you'll feel it. If your team writes TypeScript and lives in Node, Mastra was built for you and you don't have to choose between a Python sidecar and a half-finished port. Past that, there are a few real differences: **Workflow engine.** Mastra ships `Workflow.step().then().branch()` in core. LangChain's equivalent is `LangGraph`, a separate package with a graph DSL that's more powerful but also more to learn. **Observability.** LangSmith is hosted and broadly used in production. Mastra Studio is local-first, which is sharper in the dev loop but means you're wiring OTel for production tracing yourself. **Provider portability.** LangChain's `ChatAnthropic | ChatOpenAI` swap is one import. Mastra uses `@ai-sdk/*` per-provider packages — same idea, more explicit install steps. ## Pick LangChain if Pick LangChain when Python is the stack and the integration list is what you're paying for. - Your team writes Python, your data team uses pandas, your ML pipeline already speaks it. - You need several vector stores, document loaders for PDF/CSV/HTML, multiple embeddings, and the option to swap LLM vendors behind one interface. - LangSmith earns its keep — hosted tracing, eval suites, dataset-driven regression tests on agent runs, none of which you want to build. - The agent is one node in a larger Python service, not the whole product. ## Pick Mastra if Pick Mastra when the team is TypeScript-native and wants the agent, the workflow, the RAG, and the debugger from one install. - TypeScript end to end matters: typed tools via `createTool` with Zod, agents that compile, no Python sidecar to deploy. - The workflow needs explicit branching and you don't want to learn LangGraph's graph DSL to get it. - Mastra Studio is the part that sells the team — clicking through agent runs locally beats grepping `console.log` during iteration. - The product is a chat-shaped feature in a Node app and you'd rather not introduce a second language. ## What both add Both add a real dependency tree and a layer of abstraction between you and the model call. The class hierarchies, decorators, and config objects all carry learning overhead that's harder to justify on smaller projects, and onboarding new engineers means walking them through framework concepts before they touch business logic. You also inherit each project's release cadence and breaking changes. LangChain has rewritten its core APIs more than once. Mastra is younger and still moving quickly. Pin your versions either way. ## Same task in LangChain and Mastra Here is the same agent in both: it has a calculator tool the model can call when it needs to do math. ### LangChain (Python) ```python from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langgraph.prebuilt import create_react_agent @tool def calculator(expression: str) -> str: """Evaluate a math expression like '23 * 47'.""" return str(eval(expression)) model = ChatOpenAI(model="gpt-4o") agent = create_react_agent(model, tools=[calculator]) result = agent.invoke({ "messages": [("user", "What is 23 multiplied by 47?")] }) print(result["messages"][-1].content) ``` The `@tool` decorator builds the JSON schema from the docstring and the type hints. `create_react_agent` runs the loop. `invoke` returns the full state dict, so you reach in for the last message. ### Mastra (TypeScript) ```ts import { Agent } from "@mastra/core/agent"; import { createTool } from "@mastra/core/tools"; import { openai } from "@ai-sdk/openai"; import { z } from "zod"; const calculator = createTool({ id: "calculator", description: "Evaluate a math expression like '23 * 47'.", inputSchema: z.object({ expression: z.string() }), execute: async ({ context }) => ({ result: String(eval(context.expression)), }), }); const agent = new Agent({ name: "math-agent", instructions: "You can do math by calling the calculator tool.", model: openai("gpt-4o"), tools: { calculator }, }); const result = await agent.generate("What is 23 multiplied by 47?"); console.log(result.text); ``` Mastra wants the schema explicit (Zod, no docstring inference) and returns `{ text }` directly. The shape feels closer to a Vercel SDK `generateText` call than to LangChain's state-based `invoke`. ### Side by side | | LangChain | Mastra | |---|---|---| | Language | Python (LangChain.js exists, trails) | TypeScript-native | | Tool schema source | Docstring + type hints (auto) | Zod schema (explicit) | | Loop primitive | `create_react_agent(...).invoke()` returns state | `agent.generate()` returns text | | Result shape | Full message-history dict | `{ text, toolCalls, ... }` | | Production debugging | LangSmith (hosted, broad) | Mastra Studio (local, TS-aware) | | Workflows | LangGraph (separate package, graph DSL) | Built-in `Workflow` with `.step().then().branch()` | For TypeScript teams shipping a chat-shaped feature, Mastra reads cleaner. For Python teams doing RAG, multi-provider routing, or anything in LangChain's catalog, LangChain pulls ahead. Language choice is usually the deciding axis. Porting a working codebase from one to the other is more expensive than living with either framework's ergonomics. ## Migrating between LangChain and Mastra LangChain to Mastra happens when a team is rewriting in TypeScript or spinning up a new TS service. Tools port cleanly: the `@tool` decorator becomes `createTool({ inputSchema: z.object(...), execute })`. The docstring becomes the `description` field. `ConversationBufferMemory` becomes Mastra's thread memory — per-thread message history — and the abstractions are close enough that the migration is mostly type definitions. LangGraph workflows become Mastra's `Workflow` class with `.step()` and `.branch()`. The mental model is similar but the API surface is smaller. The thing to plan for is provider portability. LangChain's one-import swap (`ChatAnthropic | ChatOpenAI | ChatGoogleGenerativeAI`) is more convenient than Mastra's per-provider `@ai-sdk/*` packages. Same idea, more install steps. LangSmith traces don't transfer either; you switch to Mastra Studio for dev and wire OTel for production. Sharper dev loop, less mature production observability. Mastra to LangChain is the move when a team is consolidating into Python or needs the deeper integration catalog. Zod schemas become Pydantic models or `@tool`-decorated docstrings; pick `@tool` for terse code and Pydantic when you want shared models with the rest of your Python codebase. Mastra's `Workflow` becomes a LangGraph `StateGraph` — each step is a node, each branch is a conditional edge. The thing to watch in this direction is TypeScript-only integrations. If your Mastra app uses Vercel KV, Cloudflare Workers, or browser-native APIs, those don't have Python analogues. You may end up keeping some pieces in TypeScript even after the migration. In both directions, the agent's business logic — which tools, when to call them, how to validate output — stays the same. The work is in the surrounding plumbing. --- Source: https://tinyagents.dev/vs/langchain-vs-mastra ### LangChain vs Rasa: Which Agent Framework to Use? > Compare LangChain and Rasa side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs Rasa, head to head These two frameworks don't really compete. They get compared because both have "build a bot" energy, but they're built for different jobs and different buyers. LangChain is a Python (and JS) library you import. You compose `AgentExecutor`, `@tool`, and a retriever, then run it. The bot is whatever the LLM decides to do that turn. It's flexible by default and deterministic only if you write the determinism yourself. Rasa is a product. You declare intents, slots, and stories in YAML, train models, and run an action server alongside a dialogue engine. The bot does what your flow says. The newer `CALM` layer lets an LLM choose between `Flows` you've already defined, but the flows are still authored, not improvised. If you put both in front of the same product manager, LangChain is the prototype that ships in a week and surprises you for the next year. Rasa is the system that takes a quarter to stand up and behaves the same in month 18 as it did on day one. ### Where the on-prem question decides everything If the bot has to live behind a firewall, this comparison is usually over before it starts. Rasa runs fully on-prem out of the box. NLU server, action server, dialogue engine — you host all three. `Rasa Pro` lets you point CALM at a self-hosted LLM (Llama, Mistral, anything behind a private endpoint) so no user message leaves the data perimeter. Transcripts stay in your database. For banking (PCI, FFIEC), healthcare (HIPAA), or telecom (lawful intercept), this is the floor regulators expect, and Rasa's per-turn logs (intent, confidence, policy decision) line up with what auditors want to see. LangChain doesn't care where you run it, which sounds the same but isn't. The Python process is easy to self-host. The default integrations are not: OpenAI, Anthropic, Pinecone, LangSmith — all hosted, all cross-border, all sending your data somewhere. Going fully on-prem with LangChain means swapping in vLLM or Ollama for the model, Weaviate or pgvector for retrieval, and rolling your own audit log. It's all possible. It's all on you to assemble. Rule of thumb that holds up: if a compliance officer needs to read your stack diagram before launch, Rasa saves you weeks. If you're shipping in a normal SaaS posture, LangChain is faster. ### How the two companies make money LangChain the library is MIT-licensed and free forever. The money is in `LangSmith` (hosted tracing and evals — free tier, then per-trace and per-seat) and `LangGraph Platform` (hosted runtime for stateful agents). You can self-host the tracing layer, but most teams pay for LangSmith because building it yourself is a side project nobody finishes. Rasa splits Apache-licensed `Rasa Open Source` from `Rasa Pro`, which is the paid SKU with CALM, end-to-end testing, SSO, audit logs, and an analytics UI. Pricing isn't on the website — it's quoted per deployment. If you want to put a credit card down on a docs page, that's a LangChain motion. If you want an MSA, a named CSM, and a renewal cycle, Rasa Pro is built for procurement. ## Pick LangChain if Pick LangChain when the work is reasoning and retrieval, not a scripted conversation. - You want `AgentExecutor`-shaped behavior: model decides what to call, you supply the tools, the loop runs until it doesn't need to. - You need `LangGraph` for branching state machines or `LangSmith` for trace-level debugging in prod. - Your integration list looks like "OpenAI plus Pinecone plus a Postgres" and you don't want to write adapters. - Hosted models are acceptable. No one is going to ask for an air-gap diagram. ## Pick Rasa if Pick Rasa when the conversation is the contract. - The flow is "collect order ID, then check status, then offer refund" and skipping a step is a bug, not a feature. - You're in a regulated industry where shipping a transcript to OpenAI's API is a non-starter. - The buyer is enterprise IT, not a developer with a credit card. SSO, RBAC, audit logs, on-prem are all hard requirements. - You already have a Rasa team and an action server in production, and replacing that with an LLM agent is rewriting a working system. ## What both add Both come with vocabulary you'll have to teach every new engineer. LangChain has `AgentExecutor`, `BaseTool`, `Runnable`, `LCEL`, plus whichever memory class is currently in fashion. Rasa has intents, entities, slots, stories, rules, policies, and a separate action server process. Either way, the framework's mental model is a thing your team is learning before they're shipping. The dependency footprint is real too. LangChain's optional-integrations tree is wide; Rasa ships training infrastructure and a second process to manage. If the actual job is "one LLM call and two tools," both feel like carrying a workbench to drive a nail. ## Migrating between LangChain and Rasa Rasa to LangChain happens when the bot has outgrown scripted flows. Usually product wants open-ended Q&A, document summarization, or anything where the user's next utterance isn't predictable. You throw out the stories and intents, rebuild around an agent loop with retrieval, and lose the deterministic guarantees on the way out. Plan compensating controls before launch: output filters, deterministic tool wrappers, a real eval suite. Otherwise you're shipping a downgrade in reliability for an upgrade in surface area. LangChain to Rasa is rarer and almost always has one cause: a compliance or procurement bar the LangChain stack can't clear without months of custom plumbing. The migration itself is less terrifying than it sounds. The agent's *actual* flow is usually narrower than the code suggests, so it re-encodes into `Flows` plus a few action-server endpoints. The model calls move behind CALM. The hard part is replacing whatever the LLM was papering over — intent gaps, edge cases, fallback prompts — with explicit business rules you have to write down. In both directions, the migration is mostly a clarification exercise. You find out what the bot is actually supposed to do, often for the first time. --- Source: https://tinyagents.dev/vs/langchain-vs-rasa ### AutoGPT vs LangChain: Which Agent Framework to Use? > Compare AutoGPT and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AutoGPT vs LangChain, head to head AutoGPT is a **finished autonomous agent** — you give it a goal, its `Agent` class decomposes it, runs a think-plan-act-observe loop, and decides on its own when to stop. LangChain is a **toolkit of primitives** — `AgentExecutor`, `@tool`, `ConversationBufferMemory`, `OutputParser` — that you compose into whatever loop you want. One is an application; the other is a kit. AutoGPT picks the control flow for you; LangChain hands you the parts and expects you to wire them. AutoGPT ships with a fixed set of capabilities — web browsing, file I/O, code execution, Google search — plus a plugin system and the AutoGPT Platform's visual builder. The integration surface is **deep on autonomous behaviors**, narrow on everything else. LangChain ships hundreds of integrations: document loaders, text splitters, embedding models, vector stores, every major LLM provider, and adjacent products like `LangSmith` (tracing), `LangServe` (deployment), and `LangGraph` (stateful workflows). It is **broad on plumbing**, opinion-light on agent behavior. Use AutoGPT when the task is open-ended and you want the agent to figure out its own subtasks — research, exploration, multi-step automation where you cannot enumerate steps upfront. Expect dozens of LLM calls per run. Use LangChain when the task is bounded but the **integration count is high** — RAG over a specific vector store, swapping providers, production tracing via `LangSmith`, or branching state machines via `LangGraph`. You write the loop; the framework supplies the connectors. ## Pick AutoGPT if Pick autogpt if your project lives or dies on autonomous, open-ended task execution rather than a fixed workflow. - **Goal-driven research and automation**: You hand the agent a high-level objective and want it to decompose, plan, and revise on failure without you enumerating steps. The built-in self-critique loop earns its keep here. - **Batteries-included capabilities**: You need web browsing, file management, and code execution working out of the box, plus a plugin ecosystem to extend further. Building this stack from scratch is real work. - **Reference implementation for autonomy**: You want to study how an unbounded `think → plan → act → observe` loop handles memory and self-correction in practice before designing your own. ## Pick LangChain if Pick langchain if your project lives or dies on integration breadth and production tooling around a loop you control. - **Multi-provider, multi-store stacks**: You're swapping between OpenAI, Anthropic, and others, or wiring a specific vector store into a RAG pipeline. The class hierarchy behind `AgentExecutor` and the retriever ecosystem saves real glue code. - **Observability and deployment**: You need `LangSmith` traces, evaluation harnesses, or `LangServe` endpoints. These are first-class and not easy to bolt on later. - **Stateful, branching workflows**: Your agent needs `LangGraph` state channels with typed reducers, conditional edges, and parallel nodes — beyond what a single `while` loop expresses cleanly. ## What both add Both frameworks pull in a sizable dependency tree and a layer of abstraction between you and the actual `/chat/completions` call. AutoGPT couples you to its autonomous-loop control flow and plugin contract; LangChain couples you to its class hierarchy — `BaseTool`, `LLMChain`, memory subclasses — and the breaking changes that come with a fast-moving framework. The ramp-up cost is real. Debugging means tracing through framework internals, not just your code, and you inherit upgrade work every time the abstractions shift. Worth paying when you're using the breadth; expensive when you only need a loop and a tool dict. --- Source: https://tinyagents.dev/vs/autogpt-vs-langchain ### BabyAGI vs LangChain: Which Agent Framework to Use? > Compare BabyAGI and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## BabyAGI vs LangChain, head to head BabyAGI is a **single-purpose loop**: an execution agent, a task creation agent, and a prioritization agent passing a `deque` of task dicts between three LLM calls. LangChain is a **component framework**: `AgentExecutor` orchestrates an `LLMChain` over a registry of `@tool`-decorated callables, with `OutputParser` and `ConversationBufferMemory` plugged in around it. One is a pattern you read top-to-bottom in a single file; the other is a class hierarchy you compose. BabyAGI is ~100 lines, one author, MIT, last meaningful update early 2026 — there is no plugin catalog, no commercial backer, no observability layer. LangChain ships with document loaders, text splitters, embedding models, and vector store adapters, plus `LangSmith` for tracing and `LangServe` for deployment, backed by Sequoia and Benchmark. If you want a `PydanticOutputParser` or a Pinecone retriever wired up, LangChain has it; BabyAGI expects you to glue Pinecone or Chroma in yourself. BabyAGI fits **open-ended task discovery** — you give it an objective, it invents subtasks, reprioritizes, and keeps going until you stop it. LangChain fits **defined workflows with known tools** — `AgentExecutor.invoke()` runs a reason-act-observe loop where the tools are fixed and the exit condition is a final answer. BabyAGI leaves stopping criteria open; LangChain expects you to know when you're done. Use BabyAGI when the subtasks are unknown at design time and exploration itself is the product. Use LangChain when the agent is one component in a larger product and you need integrations — RAG, providers, deployment — to come for free rather than be hand-rolled. ## Pick BabyAGI if Pick babyagi if your project lives or dies on **dynamic task decomposition** against an open-ended objective. - **Subtasks are unknown upfront**: The task creation agent earns its keep when you genuinely cannot enumerate steps in advance — research, exploration, or open investigation where the next move depends on the last result. - **You want a hackable reference**: ~100 lines in one file means you can fork it, swap the prioritization heuristic, change the vector store, and understand every line in an afternoon. - **Stopping criteria are yours to define**: BabyAGI deliberately leaves exit conditions open, which is a feature when you want full control over budget, depth, and termination logic. ## Pick LangChain if Pick langchain if your project lives or dies on **integration breadth** across providers, stores, and deployment. - **Multiple integrations in one pipeline**: Document loaders, text splitters, embedding models, and vector stores already wired through a common interface — useful when a RAG stack is half your codebase. - **Provider portability matters**: Swap OpenAI for Anthropic by changing one class instead of rewriting request shapes, retry logic, and response parsing across your codebase. - **You need observability and deployment**: `LangSmith` traces, evals, and `LangServe` endpoints are the kind of infrastructure you would otherwise build twice — worth the dependency tree if a team depends on it. ## What both add Both frameworks pull in dependency trees and conceptual overhead that may exceed what your agent actually does. BabyAGI assumes a vector DB and three LLM roles; LangChain assumes a class hierarchy of `AgentExecutor`, `BaseTool`, memory adapters, and output parsers. If your real workload is a handful of tools and a fixed sequence, both are heavier than the problem. Ramp-up is the second cost. Reading BabyAGI's loop is fast, but extending it past the demo means rebuilding stopping criteria and error handling. LangChain's surface area is larger — `LangGraph` state channels, `Runnable` composition, retriever variants — and onboarding a teammate to debug it at 2 AM is not free. --- Source: https://tinyagents.dev/vs/babyagi-vs-langchain ### LangChain vs Pydantic AI: Which Agent Framework to Use? > Compare LangChain and Pydantic AI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs Pydantic AI, head to head LangChain is an **integration catalog wrapped in class hierarchies** — `AgentExecutor`, `LLMChain`, `PromptTemplate`, `ConversationBufferMemory` — composed via inheritance and chain composition. Pydantic AI is a **type-system-first wrapper** built around `Agent(result_type=MyModel)`, `@agent.tool` decorators with typed parameters, and `RunContext[DepsType]` for dependency injection. LangChain validates loosely (`OutputParser`, `PydanticOutputParser` bolted on after the call). Pydantic AI validates upfront — tool args check against type hints, final responses check against your Pydantic model, and the framework retries on mismatch. LangChain has **132k stars, $50M raised, and a commercial stack** — LangSmith for tracing, LangServe for deployment, LangGraph for stateful workflows. The integration surface is huge: document loaders, text splitters, vector stores, dozens of provider adapters. Pydantic AI is **smaller (16k stars, no VC backing)** but inherits Pydantic's mindshare — every Python team that already validates with `BaseModel` gets a familiar API. Both ship 25+ provider support; LangChain's catalog is wider, Pydantic AI's is narrower but more uniform. Use `LangChain` when the **agent is one node in a larger integration graph** — RAG with a specific vector store, PDF loaders, multi-step LangGraph workflows with conditional branching and persistent state. The framework earns its weight when you're composing many connectors. Use `Pydantic AI` when the **agent's output flows into typed downstream systems** — a `CustomerRecord` that has to be valid, tool arguments that must match a schema before execution. It's the better pick when correctness matters more than connector breadth. ## Pick LangChain if Pick langchain if your project lives or dies on the breadth of integrations you can wire together quickly. - **You need the catalog**: Document loaders, text splitters, embedding models, and vector stores ship in the box. Building a RAG pipeline against Pinecone or pgvector is a few imports, not a week of glue code. - **You want LangGraph for complex flows**: Conditional branching, parallel execution, and state channels with typed reducers cover workflows that a plain `while` loop would turn into spaghetti. - **LangSmith is a hard requirement**: Production tracing, evaluation datasets, and prompt versioning are the commercial layer your team has already standardized on. ## Pick Pydantic AI if Pick pydantic-ai if your stack already runs on Pydantic and you want the same validation guarantees inside your agent. - **Typed outputs feed typed systems**: `result_type=MyModel` enforces the shape of every final response, so downstream code never branches on missing fields or string-typed numbers. - **Tool dispatch must be safe**: `@agent.tool` validates arguments against type hints before execution — bad LLM-generated args fail loudly instead of silently corrupting state. - **You swap models often**: Changing `model='openai:gpt-4o'` to `model='anthropic:claude-sonnet'` is one line, with `RunContext[DepsType]` keeping database clients and config injected the same way across providers. ## What both add Both frameworks pull in a **dependency tree and a vocabulary you have to learn before you ship anything**. LangChain's class hierarchy (`BaseTool`, `StructuredTool`, chain composition) and Pydantic AI's typed `Agent` / `RunContext` lifecycle each sit between your code and the actual `/chat/completions` POST. That's fine when the abstractions pay rent — RAG pipelines, typed downstream systems, multi-provider deployments. It's overhead when your agent is a loop, a tool dict, and a messages list. Read your own call sites before committing: if you'd write thin wrappers around either framework anyway, you're paying for ceremony you won't use. --- Source: https://tinyagents.dev/vs/langchain-vs-pydantic-ai ### ControlFlow vs LangChain: Which Agent Framework to Use? > Compare ControlFlow and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## ControlFlow vs LangChain, head to head ControlFlow inverts the usual loop: you declare a `cf.Task()` with a Pydantic `result_type` and dependencies, then assign one or more `cf.Agent()` instances to execute it. LangChain keeps the agent in the driver's seat — `AgentExecutor.invoke()` runs a ReAct loop where the LLM decides which `@tool` to call next. ControlFlow is *tell me what you want*; LangChain is *tell the agent and let it figure out how*. LangChain ships a much wider catalog — document loaders, text splitters, embeddings, vector stores, plus paid surfaces like `LangSmith` for tracing and `LangServe` for deployment. ControlFlow inherits its production layer from Prefect: retries, scheduling, and run dashboards come from the data-orchestration side. If your team already runs Prefect for ETL, ControlFlow plugs in naturally; otherwise LangChain has more boxed integrations on day one. LangChain is built for open-ended agents that reason-act-observe their way to an answer, with `LangGraph` extending that into branching state machines. ControlFlow is built for structured workflows with typed boundaries — classify, then extract, then summarize, with each step's output validated before the next step sees it. Pick ControlFlow when the shape of the work is closer to a typed DAG and you want explicit `dependencies` between steps; pick LangChain when the agent itself should decide the order, the tool, and when to stop. ## Pick ControlFlow if Pick controlflow if your project lives or dies on structured task orchestration with typed outputs, not on letting an LLM choose its own next step. - **Typed task results**: `cf.Task(result_type=Category)` validates the LLM's JSON against a Pydantic model before any downstream task sees it. Malformed outputs fail at the task boundary, not three steps later. - **Prefect-native ops**: Retries, scheduling, and run dashboards come for free when ControlFlow runs inside an existing Prefect flow. Worth it if your team already lives in Prefect for data pipelines. - **Per-task agent assignment**: Different `cf.Agent()` instances — different model, system prompt, tools — can be wired to different tasks in the same `@cf.flow`, with dependency resolution handling the order. ## Pick LangChain if Pick langchain if your project lives or dies on the integration catalog — providers, vector stores, retrievers, and observability already wired up. - **Provider portability**: Swap OpenAI for Anthropic by changing one class. The unified `ChatModel` interface absorbs provider differences your business logic shouldn't care about. - **RAG infrastructure**: Document loaders, text splitters, embeddings, and vector store wrappers cover the boring parts of retrieval. You skip writing chunking logic and connector code. - **LangSmith and LangGraph**: Tracing, eval datasets, and stateful graph workflows are first-party. If you need conditional branching with persistent state across nodes, `LangGraph` is the path of least resistance. ## What both add Both frameworks ask you to learn their abstractions before you ship anything. `AgentExecutor` and `cf.Task` each sit between you and the actual `/chat/completions` call, which turns a five-line bug into a stack trace through three or four library files. Both also pull real dependency weight — LangChain's split packages (`langchain-core`, `langchain-openai`, integration extras) and ControlFlow's Prefect runtime each add install time, version pinning, and a class hierarchy to onboard new engineers into. Worth it if you use enough of the surface; expensive if you don't. --- Source: https://tinyagents.dev/vs/controlflow-vs-langchain ### CAMEL AI vs LangChain: Which Agent Framework to Use? > Compare CAMEL AI and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CAMEL AI vs LangChain, head to head ### Paradigm CAMEL AI is a **research framework** built around role-playing: a `RolePlaying` session pairs a `user_agent` and `assistant_agent`, each with `role_name`, `role_type`, and an inception prompt that locks behavior. LangChain is a **general agent toolkit**: `AgentExecutor` runs a reason-act-observe loop, `@tool` decorates callables, and `ConversationBufferMemory` handles history. CAMEL constrains you to multi-agent debate; LangChain hands you composable primitives for any agent shape. ### Ecosystem CAMEL ships at ~16k stars from KAUST researchers, with a NeurIPS 2023 paper and the OWL automation sister project as its anchor artifacts. LangChain sits at ~132k stars, ~3.4M weekly npm pulls, $50M raised across A and B, and commercial surface in **LangSmith** (tracing) and **LangServe** (deployment). Integration catalogs are not comparable — LangChain has document loaders, vector stores, and dozens of provider adapters; CAMEL has role-playing infrastructure. ### Use case Reach for CAMEL when the **interaction pattern itself** is the work — agent societies, instructor/assistant debate, or studying how `inception_prompt` shapes emergent behavior across many agents. Reach for LangChain when the **integration surface** is the work — a single agent that needs RAG against a specific vector store, swaps OpenAI for Anthropic by changing one class, and exports traces to LangSmith. CAMEL is opinionated about *how agents talk*; LangChain is opinionated about *what agents can plug into*. ## Pick CAMEL AI if Pick CAMEL AI if your project lives or dies on multi-agent collaboration as a first-class primitive. - **Role-playing is the design**: You want `RolePlaying` sessions with structured `user_agent`/`assistant_agent` pairs and `inception_prompt` constraints baked in, not bolted on top of a single-agent loop. - **Research and reproducibility**: You're running experiments on agent societies, voting, or scaling laws and want NeurIPS-grade infrastructure with logged conversations and citeable methodology. - **Debate-driven quality**: Your task benefits from analyst/critic mutual checking — complex reasoning, plan critique, or anything where one agent grading another measurably reduces hallucination. ## Pick LangChain if Pick LangChain if your project lives or dies on the integration surface around the LLM call. - **Provider and store portability**: You need to swap OpenAI for Anthropic or Pinecone for pgvector by changing a class, not rewriting tool dispatch and prompt assembly. - **RAG and ingestion plumbing**: Document loaders, text splitters, embedding adapters, and `VectorStoreRetrieverMemory` save weeks versus building loaders and chunkers per source. - **Production observability**: You want **LangSmith** traces, eval datasets, and **LangServe** deployment — or **LangGraph** for branching workflows with typed state channels and persistent checkpoints. ## What both add Both frameworks pull in dependency trees and class hierarchies that sit between you and the actual `/chat/completions` POST. CAMEL drags in society/role infrastructure even for two-agent cases; LangChain drags in `AgentExecutor`, `OutputParser`, and memory classes even when a `messages` list would do. Ramp-up is non-trivial. New engineers learn `ChatAgent`/`RolePlaying` semantics or `AgentExecutor`/`@tool`/`LCEL` conventions before they can debug a misfire — and when something breaks at 2 AM, you're stepping through framework internals instead of your own loop. ## Same task in CAMEL AI and LangChain Here is the **same task** in both frameworks: a research agent that takes a question, searches the web, reads the top result, and produces a one-paragraph answer. CAMEL's `RolePlaying` shape splits this across two agents (researcher + writer); LangChain runs it as a single tool-using agent. ### CAMEL AI (Python, RolePlaying) ```python from camel.agents import RolePlaying from camel.types import ModelType, TaskType session = RolePlaying( assistant_role_name="Research Writer", user_role_name="Research Coordinator", task_prompt="Search the web and write a one-paragraph answer.", with_task_specify=True, assistant_agent_kwargs={"model_type": ModelType.GPT_4O}, user_agent_kwargs={"model_type": ModelType.GPT_4O}, task_type=TaskType.AI_SOCIETY, ) # Drives the conversation between the two agents until completion: input_msg = session.init_chat() for _ in range(10): assistant_response, user_response = session.step(input_msg) if assistant_response.terminated or user_response.terminated: break input_msg = assistant_response.msg print(assistant_response.msg.content) ``` You define **two roles** that talk to each other. The framework formalizes turn-taking, role separation, and termination conditions. Web search is a tool wired into either agent's tool list (omitted here for brevity). ### LangChain (Python, single agent) ```python from langchain_openai import ChatOpenAI from langchain_community.tools.tavily_search import TavilySearchResults from langgraph.prebuilt import create_react_agent model = ChatOpenAI(model="gpt-4o") search = TavilySearchResults(max_results=3) agent = create_react_agent( model, tools=[search], ) result = agent.invoke({ "messages": [ ("system", "Search the web and write a one-paragraph answer."), ("user", "What is the GitHub star count of LangChain?"), ] }) print(result["messages"][-1].content) ``` One agent. The model decides when to search and when to summarize. No role separation, no turn-taking — `create_react_agent` runs the loop until the model stops calling tools. ### What you actually choose between | | CAMEL AI | LangChain | |---|---|---| | Mental model | Agents talking to agents | One agent calling tools | | Best for | Multi-agent simulations, role studies, "AI society" workflows | Single-agent RAG, multi-provider routing, integration-heavy pipelines | | Verbosity for simple tasks | High (two roles needed) | Low (one agent, prebuilt loop) | | Ecosystem breadth | Smaller, research-focused | Largest in Python, broad SaaS catalog | | Native multi-agent | First-class `RolePlaying`, `Society` | Built on top via LangGraph | If the *problem itself* is multi-agent — two roles negotiating, an evaluator critiquing a writer, a debate format — CAMEL's vocabulary fits. For a single agent doing tools and retrieval, LangChain (or LangGraph) is shorter and has more integrations. ## Migrating between CAMEL AI and LangChain **From LangChain → CAMEL AI** (your agent grew into a multi-role workflow): - A single LangChain agent becomes one `ChatAgent` (or one of the two roles in a `RolePlaying` session). The tools port directly — both frameworks accept Python callables with type hints. - `AgentExecutor`'s loop is replaced by CAMEL's `session.step()` calls. You drive the iteration explicitly rather than handing it to the framework. - LangChain memory (`ConversationBufferMemory`) becomes CAMEL's per-agent message history. Cross-agent memory is the `Society` abstraction. - `LangSmith` tracing has no direct equivalent. CAMEL is research-first; production observability is something you'll bolt on (OTel + a tracing backend). - **Watch out for**: tool output handling. CAMEL formats tool results into the next message differently than LangChain's `ToolMessage`. Test the round-trip on every tool you migrate. **From CAMEL AI → LangChain** (multi-agent overhead is hurting more than helping): - Most CAMEL `RolePlaying` sessions can collapse into a single LangChain agent with a richer system prompt. The roles often turn out to be one prompt with multiple personas, not two separate agents. - If you genuinely need two agents, use `LangGraph` with two nodes and a conditional edge for handoff. The mental model is closer to CAMEL's role separation than `AgentExecutor` is. - The `Society` and `AI_SOCIETY` task types don't have LangChain analogues — these are CAMEL-specific. You're rebuilding that scaffolding by hand if you need it. - Tool integrations port cleanly; both frameworks accept Python callables. - **Watch out for**: termination logic. CAMEL has explicit termination signals built into the role conversation. In LangChain, you control termination via `max_iterations` on the agent or by checking for a specific tool call. The semantics are different — re-test edge cases. CAMEL is the right choice when the *problem* is genuinely multi-agent. When it isn't, the role infrastructure is overhead. --- Source: https://tinyagents.dev/vs/camel-ai-vs-langchain ### LangChain vs n8n AI: Which Agent Framework to Use? > Compare LangChain and n8n AI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs n8n AI, head to head LangChain and n8n AI both let an LLM call tools, and that's about where the similarity stops. LangChain is code. You write Python, compose `AgentExecutor` (or `create_react_agent` from LangGraph), decorate your functions with `@tool`, and the agent runs as part of whatever service you deploy. Provider swapping, custom retrievers, and conditional state machines all live in code you can grep, unit-test, and ship through CI. n8n is a canvas. You drag an `AI Agent` node onto a workflow, wire `Tool` nodes and a `Memory` node into its inputs, and the same reason-act-observe loop runs inside that node. The 500+ pre-built nodes (Slack, Gmail, Notion, Postgres, HubSpot, Sheets, and on) come with auth wired into n8n's credential vault, which is the actual reason most people pick it. Underneath, both run the same loop. The choice is who's expected to maintain it. LangChain assumes engineers. n8n assumes ops people and engineers sharing the same workflow, and it's optimized so an operator can click into a failed run, see exactly which tool call broke, and rerun a single step without opening a debugger. A few practical differences worth pricing in: **Authoring surface.** LangChain is a `.py` file in your repo. n8n is a visual graph that exports to JSON nobody reads by hand. **Deployment.** LangChain you host yourself (FastAPI, Lambda, Cloud Run). n8n is either self-hosted (Docker, one process) or n8n Cloud. **Version control.** Git diffs of LangChain code read normally. Git diffs of n8n workflow JSON are noisy — order changes, position changes, ID changes — and most teams accept that the canvas is the source of truth, not the JSON. **Observability.** LangChain has `LangSmith` for hosted tracing. n8n has a per-execution log built into the UI that non-engineers can read. The fastest decision rule: if the bottleneck is engineering velocity on novel agent logic, LangChain. If the bottleneck is letting ops modify the flow without a deploy, n8n. They're not really competitors — they overlap on the AI Agent node and almost nowhere else. ## Pick LangChain if Pick LangChain when the agent is part of a service engineers own end to end. - The work is RAG with specific embeddings, a chosen vector store, and reranking — `VectorStoreRetrieverMemory` and the retriever interface earn their weight. - You need to swap providers (OpenAI to Anthropic to Bedrock) without rebuilding the workflow. - `LangGraph` is the right shape — conditional branches, parallel nodes, typed reducers — and you want that logic in code you can unit-test. - Nobody outside engineering needs to edit the flow. ## Pick n8n AI if Pick n8n AI when the agent's job is to move data between SaaS tools and the ops team is the buyer. - Slack, Gmail, Notion, HubSpot, Sheets — the integration list is the value, and you'd rather not write OAuth flows for any of them. - The execution log needs to be readable by someone who isn't an engineer. - You already run n8n for non-AI workflows and the agent fits next to existing triggers. - A prompt change should happen in a UI, not a deploy. ## What both add Both come with a real dependency footprint and a vocabulary your team has to learn. `AgentExecutor` and chain composition on one side, the node-and-wire model plus credential system on the other. Upgrades track each project's release cadence, not yours. Both also sit between you and the model API. When a tool call misfires or a prompt drifts, you debug through the abstractions before reaching the underlying HTTP call. That's fine when you're using the catalog. It's friction when you aren't. ## Same task in LangChain and n8n AI These solve different shapes of problem, so the "same task" looks pretty different in each. Here's a Slack message that gets summarized and replied to, in both. ### LangChain (Python, in code) ```python from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langgraph.prebuilt import create_react_agent from slack_sdk import WebClient slack = WebClient(token=SLACK_BOT_TOKEN) @tool def post_slack_reply(channel: str, thread_ts: str, text: str) -> str: """Post a reply in a Slack thread.""" slack.chat_postMessage(channel=channel, thread_ts=thread_ts, text=text) return "posted" model = ChatOpenAI(model="gpt-4o") agent = create_react_agent(model, tools=[post_slack_reply]) # Triggered by your own webhook handler: def handle_slack_event(event): user_msg = event["text"] agent.invoke({ "messages": [ ("system", "Summarize the user's request and post a useful Slack reply."), ("user", f"channel={event['channel']} thread_ts={event['ts']} message={user_msg}"), ] }) ``` You own the webhook server, Slack auth, and deployment. LangChain owns the LLM call and tool dispatch. That's it. ### n8n AI (canvas + JSON) In n8n's UI the same workflow is four nodes wired together: 1. `Slack Trigger` (event: `message`) — auth handled by n8n's credential vault. 2. `AI Agent` node — model: OpenAI gpt-4o, system prompt as text, tools: a single connected `Slack: Post Message` action. 3. `Slack: Post Message` (`channel`, `thread_ts`, `text`) — same auth as the trigger. 4. (Implicit) the `AI Agent` node calls `Post Message` when the model decides to. The exported JSON is what n8n persists, but almost nobody writes it by hand. Slack credentials, retries, error branches, rate-limiting — all framework-provided. That's the value. ### Side by side | | LangChain | n8n AI | |---|---|---| | Authoring surface | Code (`.py`) | Visual canvas + node config | | Slack auth | You wire it | Built-in credential vault | | Deployment | You host (FastAPI / Lambda / Cloud Run) | n8n self-hosted or n8n Cloud | | Version control | Git diffs are readable | JSON exports — diffs are noisy | | Non-engineer access | None — it's Python | Operators can edit nodes | | LLM-as-tool flexibility | Anything you can write in Python | Anything n8n exposes as a node | | Debugging | Stack traces + LangSmith | Per-node execution log in the UI | For engineering teams shipping novel agent logic, LangChain wins. For teams where ops needs to read and edit the flow, n8n wins. ## Migrating between LangChain and n8n AI LangChain to n8n is the move when the agent has to land somewhere ops can edit it. Most common SaaS tools (Slack, Gmail, HubSpot, Notion, Sheets) are pre-built nodes, so you stop maintaining auth and HTTP clients. Your `@tool` decorators don't port directly — n8n wants each tool wired to a real node, and custom logic ends up in a `Code` node (JavaScript or Python). What you lose: LangSmith traces (n8n's per-execution log is the closest replacement, useful but UI-bound and not exportable). What gets noisier: prompt iteration, since changing a prompt is clicking into the AI Agent node and editing a text field instead of editing code. Slower for engineers, faster for non-engineers — match your team. n8n to LangChain is the reverse, usually triggered by the agent outgrowing the canvas. Each SaaS node becomes an SDK call (`slack_sdk`, `googleapiclient`, `hubspot`). `Code` nodes become inline Python functions. `If` nodes become `add_conditional_edges` in LangGraph. The non-trivial part is credentials: they migrate from n8n's vault to a real secrets manager (AWS Secrets Manager, Doppler, Vault), and you inherit the deployment work n8n was hiding — the FastAPI server, the Slack event subscription, the retry queue. LangGraph's `MemorySaver` or `PostgresSaver` replaces n8n's per-execution state. One subtle gotcha in this direction: error branches. n8n has explicit `Error Trigger` nodes. LangChain agents recover through `try/except` plus the loop's natural retry. Different model — rewrite the recovery logic, don't translate it. The real decision isn't really framework. It's whether the agent lives in code or on a canvas. That choice tracks your team's split between engineers and operators more than any technical axis. --- Source: https://tinyagents.dev/vs/langchain-vs-n8n-ai ### LangChain vs LangGraph: Which Agent Framework to Use? > Compare LangChain and LangGraph side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs LangGraph, head to head LangChain is a **component library**: `AgentExecutor`, `LLMChain`, `@tool`, `ConversationBufferMemory`, `OutputParser` — wire them together and the executor runs an internal reason-act loop. LangGraph is a **state-machine runtime**: you declare a `StateGraph` of nodes and edges over a typed `State` channel, and the runtime walks the graph, merging updates via reducers like `add_messages`. LangChain hides the loop inside `AgentExecutor.invoke()`. LangGraph makes control flow explicit — every transition is an edge you wrote, every branch is a `add_conditional_edges` call. Both ship from LangChain Inc, share LangSmith for tracing, and reuse the same tool and model abstractions. LangChain's draw is breadth: document loaders, text splitters, embeddings, dozens of vector stores, provider wrappers. LangGraph's draw is depth on workflow primitives the executor lacks — `MemorySaver` / `PostgresSaver` checkpointing per `thread_id`, `interrupt_before` / `interrupt_after` for approvals, parallel fanout with reducer-merged state, and time-travel debugging via state history. In practice teams pair them: LangChain for tools and integrations, LangGraph for orchestration. Use LangChain when the agent is a **single reason-act loop** plus a pile of integrations — RAG over a vector store, swap OpenAI for Anthropic by changing one class, ship via LangServe. `AgentExecutor` covers that shape directly. Use LangGraph when the agent is a **workflow**: branch on intermediate results, fan out parallel sub-agents, pause for human approval, resume after a crash. The graph, typed channels, and checkpointer exist precisely because `AgentExecutor`'s opaque internal loop can't express any of that cleanly. ## Pick LangChain if Pick langchain if your project lives or dies on the breadth of integrations and a single agent loop is enough. - **Integration catalog over orchestration**: You need PDF loaders, text splitters, embeddings, and a specific vector store wired up yesterday. `AgentExecutor` plus the integration list is faster than building a graph. - **Provider portability**: You want to swap OpenAI for Anthropic by changing one class, and keep `@tool`-decorated functions and `ConversationBufferMemory` untouched. - **Single reason-act loop**: One LLM, a handful of tools, no branching, no human gate. `AgentExecutor.invoke()` is the right level of abstraction. ## Pick LangGraph if Pick langgraph if your agent is a workflow with explicit branches, persistence, or human approval gates. - **Multi-step branching**: A research flow that decomposes a query, searches in parallel, judges results, and either finalizes or re-searches. `add_conditional_edges` and reducer-merged `State` channels are built for this. - **Checkpointed pause/resume**: Long-running runs that must survive crashes or wait on external systems. `MemorySaver` / `PostgresSaver` per `thread_id` gives you this without rolling your own persistence. - **Human-in-the-loop**: Approval gates between steps. `interrupt_before` / `interrupt_after` pause the graph and let a reviewer inspect or edit state before resuming. ## What both add Both pull in a **large dependency tree** and a layered class hierarchy that sits between your code and the actual `/chat/completions` POST. Concepts like `Annotated[list, add_messages]`, `StructuredTool`, and reducer semantics are real ramp-up cost — and a stack trace from `AgentExecutor` or a `StateGraph` walker is several frames removed from the line that broke. You also inherit a **release cadence you don't control**. Breaking changes in `langchain-core`, `langgraph`, or an integration package land on their schedule, and version pinning across the ecosystem is its own ongoing tax. --- Source: https://tinyagents.dev/vs/langchain-vs-langgraph ### LangChain vs Vercel AI SDK: Which Agent Framework to Use? > Compare LangChain and Vercel AI SDK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangChain vs Vercel AI SDK, head to head LangChain is a Python-first class hierarchy: `AgentExecutor` orchestrates `LLMChain` + `PromptTemplate` + `OutputParser`, tools extend `BaseTool` or wear `@tool`, and memory is its own class tree (`ConversationBufferMemory`, `VectorStoreRetrieverMemory`). Vercel AI SDK is a TypeScript function library: `generateText({ model, tools, maxSteps })` runs the loop, `tool({ parameters: z.object(...), execute })` defines a tool inline with Zod, and `streamText` returns a typed `ReadableStream`. One asks you to compose classes; the other asks you to call functions. LangChain's pull is its catalog — document loaders, text splitters, embeddings, dozens of vector stores — plus `LangSmith` for tracing and `LangServe` for deploy. The AI SDK's pull is the React surface: `useChat`, `useCompletion`, and `streamUI` for RSC streaming, plus provider-portable model imports (`openai('gpt-4o')` → `anthropic('claude-3-5-sonnet')`) and tight Vercel hosting/AI Gateway integration. LangChain wins on backend integrations; the AI SDK wins on frontend plumbing and streaming protocols. If the agent sits behind a RAG pipeline, talks to Pinecone, ingests PDFs, and needs `LangSmith` traces, LangChain's catalog saves real time. If the agent is the chat box inside a Next.js app and you need token-by-token UI updates, `useChat` + `streamText` save a day of `useState` plumbing you'd otherwise write. LangChain assumes Python and a backend; the AI SDK assumes TypeScript and a UI. ## Pick LangChain if Pick langchain if your project lives or dies on the Python integration catalog and production observability. - **Multi-integration RAG**: You're wiring document loaders, text splitters, embeddings, and a specific vector store. The catalog is the product — replicating it by hand is a quarter of work. - **LangSmith observability**: You need trace-level debugging, eval datasets, and prompt versioning across a team. `LangSmith` is the strongest commercial tooling in this space. - **LangGraph workflows**: You have conditional branching, parallel nodes, and persistent state across steps. `LangGraph` state channels are designed for this; `generateText` is not. ## Pick Vercel AI SDK if Pick vercel-ai-sdk if your agent ships inside a TypeScript React app and streaming UX is the point. - **`useChat` is on the critical path**: The chat box is a first-class feature. `useChat` handles messages, optimistic updates, and streaming state — that's a day of `useState` plumbing you skip. - **RSC + `streamUI`**: You're on Next.js App Router and want to stream React components from the server. No other library handles this cleanly. - **Provider A/B in production**: You swap between `openai('gpt-4o')` and `anthropic('claude-3-5-sonnet')` to compare quality or cost. One import change, no rewrite of tool definitions. ## What both add Both frameworks add a dependency tree and a layer of abstraction between your code and the actual `/chat/completions` payload. When something misbehaves — a tool argument doesn't parse, a stream stalls, a token budget blows up — you're debugging through `AgentExecutor` internals or `streamText` chunk handlers instead of the raw HTTP. Both also encourage you to adopt the whole package even when you only need one piece. If you want tool calling but not streaming, or streaming but not `useChat`, you still inherit the full surface area, the version churn, and the ramp-up cost for new hires. --- Source: https://tinyagents.dev/vs/langchain-vs-vercel-ai-sdk ### AWS Strands Agents vs LangChain: Which Agent Framework to Use? > Compare AWS Strands Agents and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AWS Strands Agents vs LangChain, head to head Strands is model-driven — the model decides when to call tools, when to stop, and the SDK dispatches. LangChain wraps the loop in `AgentExecutor` with `LLMChain`, `PromptTemplate`, and `OutputParser` — explicit orchestration via a class hierarchy. Strands' `Agent(model, tools, system_prompt)` and `@tool` decorator hide one layer between you and the provider call; LangChain's `AgentExecutor.invoke()` hides several, and you'll meet most of them when you debug a tool that fires twice or a parser that drops a field. LangChain has five years of integration catalog — document loaders, text splitters, embedding models, vector stores, dozens of LLM providers, plus LangSmith for tracing and LangServe for deployment. Strands launched May 2025 from AWS with first-class MCP server/client support and tight Bedrock AgentCore integration for hosted runtime, identity, and observability. LangChain's surface is broader and provider-neutral; Strands is narrower and AWS-shaped, with MCP carrying the weight of what would otherwise be the integration catalog. Pick Strands if your deploy target is Bedrock AgentCore or MCP is the integration story — publishing tools as MCP servers, consuming MCP-exposed APIs. Pick LangChain if you need RAG over a specific vector store, multiple providers behind one interface, or LangSmith's evaluation tooling. For multi-agent shapes, Strands offers `Graph`, `Swarm`, and agents-as-tools; LangChain pushes you to LangGraph for anything beyond `AgentExecutor`. The decision is mostly deployment surface and integration breadth, not core agent capability. ## Pick AWS Strands Agents if Pick aws-strands if your project lives or dies on AWS deployment and MCP-first design. - **Bedrock AgentCore is your runtime**: Strands pairs natively with AgentCore for hosted runtime, identity, and observability. The local SDK is what you build with; AgentCore is where it runs in production. - **MCP is a first-class citizen**: Run an `Agent` as an MCP server, consume MCP servers as tools, no glue code. Better ergonomics than retrofitting MCP onto frameworks designed before the spec existed. - **Model-driven loop over explicit orchestration**: The `@tool` decorator with type-hint-derived schemas and an implicit loop means less framework code between you and the model on each turn. ## Pick LangChain if Pick langchain if your project lives or dies on integration breadth and provider portability. - **The integration catalog is the moat**: Document loaders, text splitters, embedding models, vector stores — dozens of each. If your agent is one component in a larger RAG or data pipeline, the catalog saves real time. - **LangSmith for tracing and evaluation**: Provider-neutral observability built for `AgentExecutor` runs and LangGraph nodes. Strands defers to AgentCore for the same job; LangSmith works wherever your code runs. - **LangGraph for complex workflows**: Conditional branching, parallel execution, typed state channels with reducers. When `Graph` and `Swarm` shapes don't fit, LangGraph's explicit DAG with persistent state will. ## What both add Both frameworks add a dependency tree and a layer of abstraction between your code and the LLM API. Strands is thinner than LangChain's class hierarchy, but you still inherit the SDK's behavior on retries, schema generation, and error surfaces — and you'll read its source the first time something behaves unexpectedly. Both also encode opinions about how multi-agent systems compose (`Graph`/`Swarm` in Strands, LangGraph nodes in LangChain). If your problem doesn't match those shapes, you work around the abstraction rather than with it. Ramp-up cost is real on either, even when the headline API looks small. --- Source: https://tinyagents.dev/vs/aws-strands-vs-langchain ### AWS Bedrock AgentCore vs LangChain: Which Agent Framework to Use? > Compare AWS Bedrock AgentCore and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AWS Bedrock AgentCore vs LangChain, head to head AgentCore is a **managed runtime platform** — closed-source SaaS that ships five services (`Runtime`, `Memory`, `Identity`, `Gateway`, `Observability`) and runs *your* agent code inside them. LangChain is an **open-source Python library** you import, with class hierarchies like `AgentExecutor`, `@tool`, `ConversationBufferMemory`, and `PydanticOutputParser`. They aren't really peers. LangChain code can *run on* AgentCore — AgentCore is framework-agnostic. The real question is which layer you adopt: orchestration code (LangChain) or the platform underneath it (AgentCore). LangChain has **132k stars, MIT, and a deep integration catalog** — dozens of vector stores, document loaders, embedding models, plus `LangSmith` for traces and `LangServe` for deployment. Provider swap is a one-class change. AgentCore is **AWS-only and proprietary**, with deep ties to CloudWatch, IAM, Secrets Manager, and Lambda/Fargate. It compensates with services LangChain doesn't try to give you: per-session MicroVM isolation, OAuth inbound + per-user credential vending outbound, and `Gateway` to expose any Lambda or API as an MCP-compliant tool. LangChain solves the **"how do I write the agent"** problem — orchestrating an LLM call, a tool registry, memory, and an output parser. AgentCore solves the **"how do I run the agent for many users on AWS without leaking state or hardcoding tokens"** problem. If you only ever pick one, pick the layer where your pain lives. Most teams that hit production scale end up with both: LangChain (or LangGraph) writing the loop, AgentCore hosting it. ## Pick AWS Bedrock AgentCore if Pick aws-agentcore if your project lives or dies on shipping multi-tenant agents on AWS without rebuilding the operational layer yourself. - **MicroVM session isolation**: each session runs in an isolated container for up to 8h. Reproducing this for untrusted user-driven agent code is genuinely hard, and `Runtime` removes that work. - **OAuth + per-user credential vending**: `Identity` ties inbound OAuth to AWS Secrets Manager so the agent can act as a specific user against Slack, GitHub, or Salesforce without you writing a token broker. - **Managed memory and observability**: short-term plus long-term semantic recall via `Memory`, OTel traces with per-step cost attribution wired to CloudWatch — no DynamoDB-plus-vector-table plumbing. ## Pick LangChain if Pick langchain if your project lives or dies on integration breadth and provider portability inside the agent code itself. - **Provider swapping**: change `ChatOpenAI` to `ChatAnthropic` without rewriting the loop. Useful when you're benchmarking models or hedging against a single vendor. - **Integration catalog**: vector stores, document loaders, text splitters, embedding models — dozens of pre-built components save real time on RAG and ingestion pipelines. - **LangGraph for branching workflows**: typed state channels, conditional edges, parallel nodes, and persistent state. Earns its weight when `AgentExecutor`'s linear loop is too thin for the workflow you actually have. ## What both add Both put a layer between you and the LLM HTTP call. LangChain hides it behind `AgentExecutor`, `LLMChain`, and a class hierarchy that takes a week to internalize. AgentCore hides the runtime — cold starts, networking, session boundaries — behind a control plane you don't get to read. They also pull you toward their gravity wells: LangChain into a large dependency tree and frequent breaking releases, AgentCore into AWS-only deployment and proprietary pricing. Neither is wrong, but both decisions are easier to make than to reverse once integrations and IAM policies pile up. --- Source: https://tinyagents.dev/vs/aws-agentcore-vs-langchain ### Flue vs LangChain: Which Agent Framework to Use? > Compare Flue and LangChain side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Flue vs LangChain, head to head Flue and LangChain live in different languages, different runtimes, and different theories of what an agent framework is even for — but they both end up on the shortlist when someone asks "what should I use to build an agent." LangChain is the incumbent. Python-first, class-heavy, five years of accumulated integrations (~132k GitHub stars, ~3.4M weekly downloads), and paid-for by LangSmith (hosted observability) and LangServe (deployment). `AgentExecutor` wraps `LLMChain` wraps `PromptTemplate` + `OutputParser`; tools are `@tool` decorators or `BaseTool` subclasses; memory injects as a class. When you swap a vector store, you change one class — that's the abstraction earning its cost. LangGraph handles the workflow layer above the loop; 1.0 GA'd October 2025. Flue is the new arrival. Announced May 1 2026 by Fred K. Schott and the Astro core team (now at Cloudflare), 1.0 beta on June 16 2026. TypeScript-native, declarative-first: `createAgent({ model, instructions, tools })` is the whole construction, and state is a Durable Stream — a replayable event log stored in Cloudflare Durable Objects. Deploys to Cloudflare, plain Node, GitHub Actions, and GitLab CI from one config. Built on the Pi harness (same runtime as OpenClaw). The tradeoff is real. LangChain trades bulk and a class hierarchy for the deepest integration catalog in the space. Flue trades ecosystem depth for a thinner API, native Cloudflare persistence, and a genuinely unusual cross-runtime deploy story. Language is usually the tiebreaker — Python teams pick LangChain, TypeScript teams pick Flue (or Mastra, or Eve). ## Pick Flue if Pick Flue when the team is TypeScript-native and Cloudflare Durable Objects is a real primitive to build on. - The deploy target is Cloudflare Workers + Durable Objects; per-agent state + locking is essentially free. - The same agent needs to run in production AND in CI (nightly bots, GitHub Actions research agents) — the cross-runtime config is rare and useful. - Declarative feels cleaner than class hierarchies for how your team writes code. - Your team is on the Pi/OpenClaw stack and shared tooling matters. ## Pick LangChain if Pick LangChain when Python is the stack and the integration matrix is what you're paying for. - Your team writes Python and your data tooling assumes pandas; LangChain.js exists but trails on features. - You need several vector stores, document loaders for PDF/CSV/HTML, multiple embeddings, and the option to swap LLM vendors behind one interface. - LangSmith earns its keep — hosted tracing, eval suites, dataset-driven regression tests — and you don't want to build any of that. - LangGraph is where you'd graduate to for workflows; the ecosystem holds together. ## What both add Both add a real dependency tree and a vocabulary your team has to learn before they ship anything. LangChain's surface is much bigger — that's the tradeoff for the integration catalog — and Flue's is thinner but tied to Cloudflare in ways that don't travel cleanly. Both also have release-cadence risk. LangChain has rewritten its core APIs more than once (though 1.0 GA'd in Oct 2025 with stability as the point). Flue is a 1.0 beta as of mid-2026; the API is likely to move. Pin your versions either way. --- Source: https://tinyagents.dev/vs/flue-vs-langchain ### AutoGen vs CrewAI: Which Agent Framework to Use? > Compare AutoGen and CrewAI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AutoGen vs CrewAI, head to head AutoGen treats agents as **chat participants**: `ConversableAgent` instances exchange messages via `initiate_chat()`, with `GroupChat` and a `GroupChatManager` picking the next speaker. CrewAI treats agents as **roles on a team**: `Agent(role, goal, backstory)` instances execute `Task` objects under a `Crew(process=sequential|hierarchical)`. AutoGen's primitive is the conversation; CrewAI's primitive is the work item. AutoGen is a **Microsoft Research project** (CC-BY-4.0, 56k+ stars) with a v0.4 rewrite focused on scale, plus a code-execution sandbox baked in. CrewAI is **MIT-licensed**, founder-led by João Moura with $18M behind an enterprise platform, and ships first-class **MCP integration** plus three memory tiers (`ShortTermMemory`, `LongTermMemory`, `EntityMemory`) out of the box. Both list MCP support; CrewAI's docs are more opinionated about it. Use AutoGen when the **interaction pattern is the hard part** — agents debating, critiquing, or running nested chats via `register_nested_chats()`, with termination governed by `is_termination_msg` callbacks. Use CrewAI when the **role separation is the hard part** — a researcher → writer → editor pipeline where you want named agents, typed task outputs, and `process=hierarchical` doing the routing. AutoGen rewards dynamic, unpredictable speaker selection; CrewAI rewards predictable pipelines where roles and deliverables are known up front. ## Pick AutoGen if Pick AutoGen if your project lives or dies on agents actually talking to each other rather than running in a fixed pipeline. - **Dynamic speaker selection**: `GroupChatManager` with LLM-based routing handles workflows where you don't know in advance who should speak next — debate, critique, planner/executor handoffs. - **Nested sub-conversations**: `register_nested_chats()` lets an agent pause the main thread, run a focused sub-chat, and inject the result back. Building this cleanly yourself is real work. - **Code-writing agents**: AutoGen's built-in code execution sandbox is the path of least resistance when agents need to write, run, and iterate on code as part of the conversation. ## Pick CrewAI if Pick CrewAI if your project lives or dies on clear role separation across a known sequence of tasks. - **Named roles drive prompts**: `role`, `goal`, and `backstory` give you a clean vocabulary for shaping per-agent system prompts — useful for researcher → writer → editor style pipelines. - **Typed task outputs**: `Task` objects with expected outputs and `Crew(process=sequential|hierarchical)` give you predictable orchestration without writing the routing loop yourself. - **Memory tiers built in**: `ShortTermMemory`, `LongTermMemory`, and `EntityMemory` are wired up by default, plus delegation guardrails that keep agents from calling outside the crew. ## What both add Both frameworks bring a real dependency footprint and a vocabulary you have to learn before you can ship: `ConversableAgent`, `GroupChatManager`, `is_termination_msg` on one side; `Agent`, `Task`, `Crew`, `process` on the other. Upgrades aren't free either — AutoGen's v0.4 rewrite reshaped the API surface, and CrewAI's enterprise pivot has moved abstractions around. Both also hide the agent loop. When a tool call misfires or a speaker selection goes sideways, you debug through the framework's abstractions instead of the raw `messages` array — fine in steady state, painful when something is wrong in production. --- Source: https://tinyagents.dev/vs/autogen-vs-crewai ### Agno vs CrewAI: Which Agent Framework to Use? > Compare Agno and CrewAI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Agno vs CrewAI, head to head Agno and CrewAI both want to be the framework you reach for, but they have opposite opinions about what an agent looks like. Agno is one class. You write `Agent(model=..., tools=..., instructions=..., knowledge=...)`, call `agent.run()`, and you're done. If you need a second agent, you wrap it in a `Team`. Most of the time you don't. CrewAI insists you build a small org chart. There's an `Agent` (with a `role`, a `goal`, and a `backstory`), a `Task` (the thing it does), and a `Crew` (the orchestrator that runs Tasks in `sequential` or `hierarchical` mode). Even a one-agent script needs the Crew/Task wrapping. The opinion is that this ceremony pays off when you actually have multiple specialists routing work between each other. The honest split: if your workload is "one capable agent that occasionally asks a helper for help," Agno stays out of your way. If it's "researcher hands off to writer hands off to editor," CrewAI's roles and backstories give the prompts somewhere to live. A few practical differences worth knowing before you commit: **Multi-modal.** Agno takes vision and audio inputs natively. CrewAI doesn't — you bolt that on yourself. **Memory.** Agno has one knob (`knowledge`). CrewAI splits memory into `ShortTermMemory`, `LongTermMemory`, and `EntityMemory`, which is either useful structure or three things to tune depending on your problem. **Persistence.** Agno ships `SqlAgentStorage` and `PostgresAgentStorage`. CrewAI has memory backends but the integration story is less direct. **Runtime.** Both have a hosted offering — Agno's `AgentOS`, CrewAI's enterprise platform — for teams that don't want to self-host. If you genuinely can't decide, the tiebreaker is whether you're building one agent with side helpers (Agno) or multiple peer agents trading work (CrewAI). Most apps turn out to be the first one. ## Pick Agno if Pick Agno when the work is one strong agent doing most of the job. - Vision or audio inputs are a hard requirement, not a maybe. - You want `tools=[web_search, sql, file_ops]` to just work without writing wrappers. - Sessions need to survive a server restart — `SqlAgentStorage` or Postgres, no custom schema. - You'd rather have one class to debug than three. ## Pick CrewAI if Pick CrewAI when the orchestration is the actual product. - Distinct agents need distinct system prompts — a researcher and a writer aren't the same persona with two tool sets. - Routing between agents is non-trivial: hierarchical delegation, scoped permissions, guardrails on who can call whom. - You want memory shaped on three axes (short-term, long-term, entity) instead of one undifferentiated context blob. - The team thinks in roles and tasks already, and the abstraction matches their mental model. ## What both add Both sit between you and the model API. That means when something misbehaves inside `agent.run()` or `crew.kickoff()`, you're reading framework source, not your own code. Agno's single class is lighter than CrewAI's Agent + Task + Crew trio, but both hide the tool-calling loop you'd otherwise own end to end. You also inherit each project's opinions about memory, storage, and tool registration. If your real workload is one LLM call, two tools, and a list of messages, you're paying ramp-up cost for abstractions that aren't earning their weight yet — and the `/lesson/agent-function` walkthrough below shows the version of that with no framework at all. --- Source: https://tinyagents.dev/vs/agno-vs-crewai ### CrewAI vs LlamaIndex: Which Agent Framework to Use? > Compare CrewAI and LlamaIndex side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs LlamaIndex, head to head CrewAI models work as a **team of specialists**: each `Agent` carries a `role`, `goal`, and `backstory`, and a `Crew` runs `Task` objects sequentially or hierarchically. LlamaIndex models work as **an agent reasoning over indexed data**: `AgentRunner` drives the loop, `ReActAgent` handles tool-calling, and `QueryEngineTool` turns any `VectorStoreIndex` into a callable. The two frameworks barely overlap conceptually. CrewAI's primitive is the role; LlamaIndex's primitive is the index. CrewAI gives you orchestration primitives — `Process.sequential`, `Process.hierarchical`, delegation guardrails, `ShortTermMemory`/`LongTermMemory`/`EntityMemory` — plus `@tool` for custom callables. There's no built-in retrieval story; you bring your own RAG. LlamaIndex gives you data infrastructure — LlamaHub connectors, document parsers, `VectorStoreIndex`, integrations with Pinecone, Weaviate, pgvector, Chroma — plus `FunctionTool` and `ChatMemoryBuffer`. Multi-agent coordination is thinner; it's a single-agent-with-good-tools story, not a crew story. Use CrewAI when the hard part is **routing between agents with distinct responsibilities** — a researcher hands off to a writer hands off to an editor, and you want named roles in the prompts and a `Crew` to enforce delegation rules. Use LlamaIndex when the hard part is **letting one agent reason over your documents** — multiple collections, custom retrieval per source, re-ranking, or non-trivial parsing. If your project is RAG-shaped, LlamaIndex; if your project is org-chart-shaped, CrewAI. Picking the wrong one means writing the other framework's strengths from scratch. ## Pick CrewAI if Pick crewai if your project lives or dies on coordinating multiple agents with distinct responsibilities. - **Named roles drive prompt quality**: When `role`/`goal`/`backstory` for a `"Senior Researcher"` vs a `"Technical Editor"` produces materially different outputs, CrewAI's vocabulary is doing real work. - **Delegation needs guardrails**: `Crew(process=hierarchical)` keeps a manager agent from spawning runaway sub-agents, and agents can only delegate within their `Crew`. - **Sequential pipelines with handoffs**: Researcher → writer → editor, or collector → analyst → reporter, where `Task` outputs feed the next agent's context cleanly without you writing the wiring. ## Pick LlamaIndex if Pick llamaindex if your agent's main job is reasoning over your own data. - **Index-as-tool is the core pattern**: `QueryEngineTool` wraps a `VectorStoreIndex` in one line, and `ReActAgent` calls it like any other tool — retrieval and reasoning live in the same loop. - **Multiple data sources, multiple strategies**: Different collections with different retrievers, re-rankers, or hybrid search — LlamaIndex's abstractions hold up where hand-rolled glue gets messy. - **You need the data plumbing**: LlamaHub connectors, PDF/HTML/SQL parsers, and integrations with Pinecone, Weaviate, Chroma, and pgvector save real days of work. ## What both add Both frameworks pull in dependency trees — CrewAI brings memory modules and orchestration machinery; LlamaIndex brings indexing, parsers, and a sprawling integration surface. Upgrades occasionally rename classes (`AgentRunner`/`AgentWorker` evolution, `Crew` process kwargs), and stack traces cross several layers of abstraction before reaching your code. The ramp-up cost is real. Engineers need to learn `Agent`/`Task`/`Crew` semantics or `AgentRunner`/`QueryEngineTool`/`FunctionTool` semantics before they can debug a tool that won't fire. If your workflow is a `while` loop with three tools, that learning curve buys you very little. --- Source: https://tinyagents.dev/vs/crewai-vs-llamaindex ### CrewAI vs Haystack: Which Agent Framework to Use? > Compare CrewAI and Haystack side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs Haystack, head to head CrewAI models work as a **team metaphor** — `Agent(role, goal, backstory)` instances assigned to `Task` objects, orchestrated by a `Crew` with `process=sequential` or `hierarchical`. Haystack models work as a **typed dataflow graph** — a `Pipeline` of `@component` classes wired with `add_component()` and `connect()`, where each `run()` method has typed inputs and outputs. One reasons about *who* does the work; the other reasons about *how data flows* between stages. CrewAI hides the agent loop inside `Agent` execution. Haystack exposes every retrieval and generation step as a node you can inspect. Haystack ships a wide catalog for retrieval — `DocumentStore` integrations for Elasticsearch, Qdrant, Pinecone, Weaviate, plus PDF/HTML converters, rankers, and `PromptBuilder`. Pipelines serialize to YAML and deploy through `Hayhooks`. CrewAI's catalog is thinner on retrieval and richer on agent collaboration — `@tool` decorators, `ShortTermMemory` / `LongTermMemory` / `EntityMemory`, and built-in delegation between agents in the same `Crew`. Haystack has the older codebase (since 2019) and document-Q&A heritage; CrewAI is newer and focused on multi-agent coordination. Use CrewAI when the hard part is **routing work across roles** — a researcher hands off to a writer hands off to an editor, and you want named agents with distinct system prompts and bounded delegation. Use Haystack when the hard part is **retrieval** — chunking documents, combining sparse and dense retrievers, re-ranking, and swapping vector stores without rewriting glue code. A `Crew` with sequential tasks looks like a degenerate Haystack pipeline; a Haystack `Agent` component with one `ChatGenerator` looks like a degenerate `Crew` of one. They overlap in the middle and diverge at the edges. ## Pick CrewAI if Pick crewai if your project lives or dies on coordinating multiple specialist agents with distinct prompts. - **Named roles drive prompt quality**: When `role`, `goal`, and `backstory` produce measurably better outputs than a single mega-prompt, CrewAI's vocabulary makes that structure first-class instead of buried in string templates. - **Delegation needs guardrails**: A `Crew` constrains which agents can hand off to which, preventing runaway loops that ad-hoc agent-calling-agent code tends to produce. - **Hierarchical orchestration is the work**: If you actually need a manager agent routing subtasks via `process=hierarchical`, CrewAI gives you that out of the box rather than building a router by hand. ## Pick Haystack if Pick haystack if your project lives or dies on the retrieval pipeline, not the agent loop. - **Multi-stage retrieval is core**: Sparse plus dense retrievers, re-rankers, and `PromptBuilder` chained through `Pipeline.connect()` — Haystack's typed component contracts catch wiring errors before runtime. - **Document stores need to be swappable**: Moving between Elasticsearch, Qdrant, Pinecone, or Weaviate is a config change, not a rewrite — useful when infra decisions outlive your code. - **Pipelines as configuration**: YAML serialization plus `Hayhooks` lets ops or non-developers version and deploy pipelines without touching Python, which matters in regulated or cross-functional teams. ## What both add Both frameworks bring nontrivial dependency trees and a vocabulary your team has to learn before reading any code — `Agent`/`Task`/`Crew` in one, `Pipeline`/`@component`/`connect()` in the other. Upgrades between minor versions have historically broken component signatures and orchestration semantics in both projects, so pinning matters. Debugging gets harder when the loop is hidden. CrewAI buries the agent loop inside `Agent` execution; Haystack routes data through typed connections you didn't write. When something goes wrong, you're tracing through framework internals before you reach your prompt or your tool. --- Source: https://tinyagents.dev/vs/crewai-vs-haystack ### Anthropic Agent SDK vs CrewAI: Which Agent Framework to Use? > Compare Anthropic Agent SDK and CrewAI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Anthropic Agent SDK vs CrewAI, head to head The Anthropic Agent SDK is a **single-agent runtime** lifted from Claude Code: one loop, built-in `bash`/file/web tools, and 18 lifecycle hooks for intercepting `pre/post tool call`, message, and error events. CrewAI is a **multi-agent orchestrator** built around `Agent(role, goal, backstory)`, `Task`, and `Crew(process=sequential|hierarchical)` — the loop is hidden inside each `Agent` and the abstractions push you toward role separation. Both speak MCP, but the gravity is different. The Anthropic SDK ships MCP as a **first-class config line** plus production-grade `bash` and file I/O — you get Claude Code's actual tool implementations, not reference code. CrewAI's MCP support exists, but its real ecosystem is the catalog of `@tool`-decorated integrations and the `ShortTermMemory` / `LongTermMemory` / `EntityMemory` stack. Picking SDK locks you to Claude; CrewAI is LLM-agnostic via LiteLLM. Use the SDK when **one agent needs to touch the real world** — read a repo, run shell commands, drive Playwright via MCP — and you want the hooks for guardrails, logging, and cost tracking. Use CrewAI when the work decomposes into **named specialists handing off artifacts** (researcher → writer → editor) and you want `Crew` to handle sequential or hierarchical routing. The SDK has no real notion of multi-agent handoff beyond "agent calls agent as a tool"; CrewAI has no equivalent to the SDK's batteries-included tool runtime. If you find yourself wanting both — production tool execution *and* role-based delegation — you're past what either gives you cleanly and into custom orchestration. ## Pick Anthropic Agent SDK if Pick anthropic-sdk if your project lives or dies on a single Claude agent reliably touching files, shells, and external services. - **Built-in tool runtime**: You need production-grade `bash`, file read/write, and web search without writing `subprocess` wrappers, sandboxing, or retry logic yourself. These are the same implementations Claude Code ships to hundreds of thousands of developers. - **MCP-first integrations**: Your roadmap depends on Playwright, Slack, GitHub, or database MCP servers, and you want one-line config instead of HTTP boilerplate per service. - **Lifecycle hook control**: You need to intercept `pre/post tool call`, message, and error events for guardrails, audit logs, or cost tracking without forking the loop. ## Pick CrewAI if Pick crewai if your problem is genuinely multi-agent and the hard part is routing work between specialists. - **Role-based decomposition**: Your workflow naturally splits into named roles like `"Senior Researcher"` → `"Writer"` → `"Editor"`, and `Agent(role, goal, backstory)` makes prompt iteration cleaner than juggling system-prompt strings. - **Sequential or hierarchical orchestration**: You want `Crew(process=...)` to handle the routing, including a manager agent delegating to sub-agents, instead of writing the task queue yourself. - **LLM-agnostic + memory tiers**: You're not committed to Claude and want LiteLLM-backed model swaps, plus `ShortTermMemory`, `LongTermMemory`, and `EntityMemory` without designing your own context store. ## What both add Both frameworks add a dependency, a vocabulary, and an opinion you'll inherit. The SDK couples you to Claude and to whatever Anthropic decides the agent loop should look like next quarter; CrewAI couples you to its `Agent`/`Task`/`Crew` mental model and its memory abstractions, even when your real workload is a sequential `for` loop with one LLM call per step. Both also obscure the actual HTTP traffic and tool dispatch behind their loops. That's fine in production, but it means debugging a misbehaving agent — wrong tool called, runaway cost, weird `tool_use` parsing — requires learning the framework's internals before you can learn your own bug. --- Source: https://tinyagents.dev/vs/anthropic-sdk-vs-crewai ### CrewAI vs Mastra: Which Agent Framework to Use? > Compare CrewAI and Mastra side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs Mastra, head to head CrewAI models work as a `Crew` of `Agent`s with `role`, `goal`, `backstory`, processing a `Task` queue with `process=sequential` or `hierarchical`. Mastra models work as `new Agent({ model, instructions, tools })` plus a separate `Workflow` class with `.step()`, `.then()`, `.branch()`. CrewAI's mental model is a team of named specialists; Mastra's is a typed pipeline with explicit control flow. CrewAI is Python-first, ~48k stars, with `@tool` decorators and custom `Tool` classes — and lands in the same stack as Pandas, notebooks, and Python LLM SDKs. Mastra is TypeScript-first, ~23k stars but ~244k weekly npm downloads, using `createTool({ schema, execute })` with Zod for compile-time validation. Both ship first-class MCP support, so the real ecosystem question is language: Python data tooling vs. Node services. CrewAI fits workflows where role separation drives prompt quality — `researcher` → `writer` → `editor` — and where `Crew(process="hierarchical")` does the routing with built-in delegation guardrails. Mastra fits a single agent (or a few) that needs `Workflow` branching, document chunking + vector retrieval, and thread/long-term memory inside one Node service, with Mastra Studio for trace inspection. Pick CrewAI when the *interaction between agents* is the hard part. Pick Mastra when one agent needs workflows, RAG, and observability bundled — and you don't want to leave TypeScript. ## Pick CrewAI if Pick crewai if your project lives or dies on coordinating multiple specialist agents with named roles. - **Role-based prompt separation**: You want `Agent(role="Senior Researcher", goal=..., backstory=...)` so each LLM call gets a tailored system prompt. CrewAI's vocabulary makes that clean instead of ad hoc string concatenation. - **Hierarchical task delegation**: `Crew(process="hierarchical")` gives you a manager agent that routes subtasks, with guardrails preventing delegation outside the crew. Useful when one agent legitimately needs to hand off work. - **Python data and ML stacks**: Your team already runs Pandas, notebooks, and Python LLM SDKs. CrewAI's `ShortTermMemory`/`LongTermMemory`/`EntityMemory` plugs into the same toolchain without an API boundary. ## Pick Mastra if Pick mastra if your project lives or dies on shipping a typed agent inside a Node.js service. - **TypeScript end-to-end**: `createTool({ name, schema, execute })` with Zod gives compile-time validation across tool inputs and outputs. No Python sidecar, no JSON contract drifting between services. - **Workflows, RAG, and memory in one package**: `.step().then().branch()` plus document loading, chunking, embedding, and vector retrieval — instead of stitching LangChain.js, a vector client, and a workflow lib together yourself. - **Mastra Studio for debugging**: A local GUI for traces, agent runs, and workflow steps beats `console.log` when you're iterating on a multi-step `Workflow` and need to see where a branch fired. ## What both add Both frameworks pull in real dependency weight and a vocabulary your team has to learn before shipping. CrewAI's `Agent`/`Task`/`Crew`/`Process` and Mastra's `Agent`/`Workflow`/`Step` each become the API surface you reason about — and the thing you fight when runtime behavior diverges from the abstraction. Both also hide the agent loop. When tool dispatch misbehaves or token usage spikes, you end up reading framework source instead of your own code, and minor version bumps can shift execution semantics under you without warning. --- Source: https://tinyagents.dev/vs/crewai-vs-mastra ### CrewAI vs Pydantic AI: Which Agent Framework to Use? > Compare CrewAI and Pydantic AI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs Pydantic AI, head to head CrewAI models work as a team metaphor: `Agent(role, goal, backstory)` plus `Task` plus `Crew(process=sequential|hierarchical)`. Pydantic AI models work as a typed function call: `Agent` with a `result_type` Pydantic model, `@agent.tool` decorators with typed parameters, and `RunContext[DepsType]` for dependency injection. One treats agents as personas you orchestrate; the other treats them as schemas you validate. CrewAI optimizes for prompt-level role separation, Pydantic AI for compile-time type checks on tool args and outputs. CrewAI is the bigger community by raw numbers — ~48k GitHub stars vs ~16k — and ships memory primitives (`ShortTermMemory`, `LongTermMemory`, `EntityMemory`) plus first-class MCP support. Pydantic AI is younger (June 2024) but inherits the Pydantic user base and pairs natively with Logfire for tracing. Both are MIT, both are Python-only. CrewAI leans toward plug-and-play multi-agent demos; Pydantic AI leans toward integration into existing typed Python codebases (FastAPI, SQLModel, Pydantic-heavy stacks). Use CrewAI when you actually have multiple specialists collaborating — researcher → writer → editor — and the orchestration between roles is the hard part. The `process=hierarchical` flag and built-in delegation guardrails matter when one agent needs to route work to another. Use Pydantic AI when a single agent's outputs feed into typed downstream systems and a malformed `tool_call` is a production bug. Its 25+ provider abstraction (`model='openai:gpt-4o'` → `model='anthropic:claude-sonnet'`) also matters more than CrewAI's role vocabulary if you swap models often. ## Pick CrewAI if Pick crewai if your project lives or dies on coordinating multiple agents with distinct roles and handoffs. - **Role-driven prompt design**: You want `role`, `goal`, and `backstory` as first-class fields so prompt engineering for a `"Senior Researcher"` vs a `"Copy Editor"` stays organized as the crew grows. - **Hierarchical or sequential orchestration**: `Crew(process=hierarchical)` with manager-driven delegation is closer to your workflow than a hand-rolled task queue, and you want delegation guardrails out of the box. - **Built-in memory tiers**: You need `ShortTermMemory`, `LongTermMemory`, and `EntityMemory` distinctions without designing your own retrieval layer. ## Pick Pydantic AI if Pick pydantic-ai if your project lives or dies on typed contracts between the agent and the rest of your code. - **Structured outputs into typed systems**: `result_type=CustomerRecord` enforces a Pydantic model on the final response, so downstream code never sees a malformed dict from the LLM. - **Existing Pydantic / FastAPI stack**: Your team already writes Pydantic models everywhere; `@agent.tool` with typed params and `RunContext[DepsType]` slots into that style without a new mental model. - **Frequent model switching**: You toggle between `openai:gpt-4o`, `anthropic:claude-sonnet`, and others often enough that the unified provider interface plus Logfire tracing pays for itself. ## What both add Both frameworks add a class hierarchy you have to learn before you can ship: `Agent` / `Task` / `Crew` / `process` in CrewAI, `Agent` / `result_type` / `@agent.tool` / `RunContext` in Pydantic AI. That ramp-up is real, and so is the lock-in — once tools are decorated and crews are wired, ripping the abstraction out is non-trivial. Both also hide the loop. The actual mechanics — call LLM, dispatch tool, validate, repeat — happen inside `agent.run()` or `Crew.kickoff()`, which makes debugging tool-call failures or token budgets harder than reading a 30-line `while` loop. --- Source: https://tinyagents.dev/vs/crewai-vs-pydantic-ai ### CrewAI vs LangGraph: Which Agent Framework to Use? > Compare CrewAI and LangGraph side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs LangGraph, head to head CrewAI thinks in **people**: an `Agent` carries a `role`, `goal`, and `backstory`, and a `Crew` runs a list of `Task` objects sequentially or hierarchically. LangGraph thinks in **graphs**: a `StateGraph` of node functions wired by edges, with a typed `State` channel and reducers like `Annotated[list, add_messages]` controlling how updates merge. CrewAI hides the agent loop inside `Agent` execution; LangGraph forces you to draw it as `add_conditional_edges` from a node back to itself until `END`. CrewAI is its own stack — the open-source `crewai` package plus a hosted enterprise platform from João Moura's company. LangGraph ships under LangChain Inc (Sequoia/Benchmark, $50M raised across A and B) and inherits the LangChain world: existing `@tool` definitions, memory classes, and node-by-node tracing in LangSmith. If you already wrote tools for LangChain, LangGraph plugs in; CrewAI asks you to redefine them with its own `@tool` decorator and custom `Tool` subclasses. Pick CrewAI when the natural model is "researcher hands off to writer hands off to editor" — named specialists, mostly linear delegation, prompt quality driven by clear role separation. Pick LangGraph when the natural model is a workflow with real branching: parallel fanout merged through reducers, `interrupt_before` for a human approval gate, `MemorySaver` or `PostgresSaver` for crash-safe pause/resume. CrewAI optimizes for *who is doing the work*; LangGraph optimizes for *what state the work is in*. ## Pick CrewAI if Pick CrewAI if your project lives or dies on cleanly separated agent roles collaborating toward a shared output. - **Role-driven pipelines**: Content flows like researcher → writer → editor, or data crews like collector → analyst → reporter. The `role`/`goal`/`backstory` triple is genuinely useful prompt scaffolding when you have three or more specialists. - **Hierarchical delegation out of the box**: `Crew(process="hierarchical")` gives you a manager agent that routes subtasks across a fixed roster without you writing the router. - **Time-to-demo matters more than control**: You want a working multi-agent prototype today. CrewAI's vocabulary reads well to non-engineer stakeholders and hides the loop entirely. ## Pick LangGraph if Pick LangGraph if your agent is a stateful workflow that needs explicit control over branches, retries, and pauses. - **Conditional branching and parallel fanout**: `add_conditional_edges` plus typed reducers handle "search three sources in parallel, judge results, re-route" without you hand-rolling the merge logic. - **Checkpointed pause/resume**: `MemorySaver` or `PostgresSaver` per `thread_id` keeps long-running workflows alive across crashes, approvals, and async waits — hard to retrofit later. - **Human-in-the-loop plus observability**: `interrupt_before` / `interrupt_after` for review gates, and LangSmith's node-by-node traces with state diffs are worth the graph ceremony when you need to debug what step four actually saw. ## What both add Both frameworks ship abstractions you'll need to internalize before you ship anything: CrewAI's `Agent` / `Task` / `Crew` / `Process` vocabulary, LangGraph's `StateGraph`, reducers, and `Annotated` typing dance. Each is a few days of ramp-up before the mental model clicks, plus a transitive dependency tree (LangChain core under LangGraph; CrewAI's own stack on the other side). Both also pull you toward a specific metaphor — crew of specialists or directed state machine — that can outlive its usefulness. Once your wiring matches the framework's shape, migrating out is a project of its own, so it's worth pressure-testing whether your real problem fits the metaphor before you commit. --- Source: https://tinyagents.dev/vs/crewai-vs-langgraph ### CrewAI vs Vercel AI SDK: Which Agent Framework to Use? > Compare CrewAI and Vercel AI SDK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## CrewAI vs Vercel AI SDK, head to head CrewAI is a Python multi-agent orchestrator: you declare `Agent(role, goal, backstory, tools, llm)` instances, wrap work in `Task` objects, and hand them to a `Crew(process=sequential|hierarchical)` that routes execution. Vercel AI SDK is a TypeScript toolkit aimed at a single agent loop — `generateText({ model, tools, maxSteps })` runs tool dispatch, `streamText` returns a `ReadableStream` of deltas, and `tool({ parameters: z.object(...), execute })` defines the callable surface. CrewAI thinks in **teams of specialists**; the AI SDK thinks in **one model call you stream to a UI**. CrewAI lives in the Python data/ML stack — it leans on `ShortTermMemory`, `LongTermMemory`, `EntityMemory`, and a first-class MCP integration for tools. Vercel AI SDK lives in the JS/React stack — it ships `useChat`, `useCompletion`, `streamUI` for RSC, Zod schemas for `generateObject`, and one-line provider swaps (`openai('gpt-4o')` → `anthropic('claude-3-5-sonnet')`). They do not really overlap on runtime: one is a Python orchestration layer, the other is a TypeScript LLM client + React hook bundle. Use CrewAI when the workflow itself has named roles — researcher → writer → editor — and you want `Crew` to handle delegation and task ordering for you. Use Vercel AI SDK when chat or generation is a **UI feature in a Next.js app** and `useChat` plus streaming is the actual hard part. CrewAI optimizes for **multi-agent prompt routing on the server**; Vercel AI SDK optimizes for **single-agent streaming into a browser**. They rarely compete for the same slot in a stack. ## Pick CrewAI if Pick crewai if your project lives or dies on coordinating multiple specialist agents on the server. - **Named roles drive prompt quality**: When `role`, `goal`, and `backstory` per `Agent` materially change output — content pipelines, research crews, analyst → reporter handoffs — CrewAI's vocabulary maps directly to how you already think. - **Delegation needs guardrails**: `Crew` constrains who can hand off to whom and `process=hierarchical` gives you a manager agent without writing a router. Useful when runaway loops are a real risk. - **You're already in Python**: Memory primitives (`ShortTermMemory`, `EntityMemory`) and MCP tool integration plug into the rest of a Python data stack without a language hop. ## Pick Vercel AI SDK if Pick vercel-ai-sdk if your project lives or dies on a TypeScript app with chat or generation in the UI. - **`useChat` is the hook you'd otherwise write**: Messages state, optimistic updates, streaming parsing, error handling — a day of `useState` plumbing collapses into one hook for a Next.js or React app. - **Streaming is non-negotiable**: `streamText` and `streamUI` give you typed deltas and RSC component streams without writing SSE parsers, which matters for chatbots, inline AI, and v0-style generators. - **Provider portability is a real lever**: Swapping `openai('gpt-4o')` for `anthropic('claude-3-5-sonnet')` is one import change, and the AI Gateway adds observability and BYOK on Vercel without config. ## What both add Both frameworks add an abstraction layer between your code and the raw `chat/completions` request. With CrewAI you take on `Agent`, `Task`, `Crew`, three memory classes, and a process kwarg; with Vercel AI SDK you take on `generateText`, `streamText`, `tool`, `useChat`, and a Zod dependency. If you only need part of either surface, the rest is dead weight in your bundle or your mental model. Version churn is real on both sides — APIs shift, kwargs get renamed, and debugging means reading framework source to see what request actually went out. Worth it when the abstractions match your workload; expensive when they don't. --- Source: https://tinyagents.dev/vs/crewai-vs-vercel-ai-sdk ### AWS Strands Agents vs CrewAI: Which Agent Framework to Use? > Compare AWS Strands Agents and CrewAI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AWS Strands Agents vs CrewAI, head to head Strands is **model-driven**: you instantiate `Agent(model, tools, system_prompt)` and the model decides when to call tools and when to stop. CrewAI is **role-driven**: each `Agent(role, goal, backstory)` is shaped by prompt-level identity, and a `Crew` schedules `Task` objects across them. Strands hides the loop; CrewAI hides the orchestration around the loop. CrewAI has the larger footprint — ~48k stars, MIT, shipping since October 2023, with an enterprise platform and $18M behind it. Strands is newer (May 2025, ~4.2k stars) but ships from AWS with native ties to Bedrock AgentCore for hosted runtime, identity, and observability. Both treat MCP as first-class; Strands wires it directly into the `Agent` surface, CrewAI exposes MCP servers as a tool source via its `@tool` layer. Strands fits **single-agent or `Swarm`-of-equals** designs where the model leads, MCP is central, and deployment lands on AWS. CrewAI fits **named-specialist workflows** — researcher → writer → editor, collector → analyst → reporter — where role separation drives prompt quality and `Crew(process=hierarchical)` gives a manager agent control over delegation. Pick on shape: a model-led loop with optional handoff, or a predefined team of roles working a task list. ## Pick AWS Strands Agents if Pick aws-strands if your project lives or dies on AWS Bedrock deployment and MCP-first design. - **AWS Bedrock AgentCore deploy**: hosted runtime, identity, and observability come bundled. If you're already in AWS, the deploy story is shorter than rolling your own on Lambda or ECS. - **First-class MCP**: run an `Agent` as an MCP server or consume MCP servers as tools without writing the JSON-RPC-over-stdio handshake yourself. - **Thin, model-driven SDK**: `@tool` decorator with type-hint-derived schemas and an implicit loop. No `Crew`, no `Task`, no DAG to define before you can ship. ## Pick CrewAI if Pick crewai if your workflow is genuinely a team of named specialists collaborating on a multi-step output. - **Role-based agent design**: `role`, `goal`, and `backstory` shape distinct personas, which matters when "researcher" and "writer" need different tones inside the same pipeline. - **`Crew` orchestration with delegation guardrails**: `process=sequential` or `hierarchical` plus scoped delegation keeps agents from handing off outside the crew or looping. - **Built-in memory tiers**: `ShortTermMemory`, `LongTermMemory`, and `EntityMemory` cover the common patterns without you wiring a vector store yourself. ## What both add Both frameworks hide the agent loop. Strands lets the model drive iteration inside the SDK; CrewAI buries it inside `Agent` execution under the `Crew`. When a tool gets called wrong or the loop runs hot, you debug through framework internals instead of your own code. Both also add dependency weight you may not need. CrewAI brings task orchestration, three memory subsystems, and a `Crew` runtime; Strands pulls Bedrock-friendly defaults and MCP plumbing. Pin versions carefully — both repos move fast, and breaking changes have shown up in minor releases. --- Source: https://tinyagents.dev/vs/aws-strands-vs-crewai ### AutoGen vs Haystack: Which Agent Framework to Use? > Compare AutoGen and Haystack side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AutoGen vs Haystack, head to head AutoGen treats every actor as a `ConversableAgent` and frames the whole system as a chat — `initiate_chat()`, `GroupChat`, `GroupChatManager`, and `register_nested_chats()` for sub-tasks. Haystack treats the system as a typed DAG: a `Pipeline` of `@component` classes wired through `add_component()` and `connect()`, with `Agent` being just one node alongside `Retriever`, `PromptBuilder`, and `ChatGenerator`. In short: AutoGen's primitive is **a message between agents**, Haystack's is **a tensor of typed I/O between components**. AutoGen ships speaker-selection strategies, code-execution sandboxes, and `is_termination_msg` callbacks — its center of gravity is *agent choreography*. Haystack ships `DocumentStore` integrations (Elasticsearch, Qdrant, Pinecone, Weaviate), PDF/HTML converters, embedders, rankers, and `Hayhooks` for REST deployment — its center of gravity is *retrieval and document processing*. Both wrap the same `/chat/completions` call underneath, but the surface area you import is completely different: AutoGen pulls in conversation orchestration; Haystack pulls in an indexing stack. Use AutoGen when the hard part is **multiple agents arguing toward an answer** — author/reviewer loops, planner/executor splits, debate-style refinement where the next speaker isn't known statically. Use Haystack when the hard part is **getting the right documents into the prompt** — hybrid sparse+dense retrieval, re-ranking, multi-format ingestion, swappable vector stores. A single tool-calling agent fits both frameworks awkwardly: AutoGen wraps it in a chat that has nobody to chat with; Haystack wraps it in a `Pipeline` that has nothing to pipe. ## Pick AutoGen if Pick AutoGen if your project lives or dies on multiple agents talking to each other with non-trivial turn-taking logic. - **Dynamic speaker selection**: You need `GroupChatManager` to pick the next agent via LLM routing, round-robin, or custom logic — not a hardcoded sequence. - **Nested sub-conversations**: An agent should pause the main thread, spin up a sub-chat via `register_nested_chats()`, and inject the result back without you hand-rolling a task queue. - **Sandboxed code execution**: Your agents write and run Python as part of the conversation, and you'd rather use AutoGen's executor than build container isolation yourself. ## Pick Haystack if Pick Haystack if your project lives or dies on the retrieval pipeline, not the agent loop. - **Multi-stage RAG**: You want sparse + dense retrievers, a re-ranker, and a `PromptBuilder` wired through `Pipeline.connect()` with typed contracts catching mismatches at build time. - **Document store portability**: You need to swap Elasticsearch for Qdrant or Weaviate without rewriting the pipeline — the `DocumentStore` interface is doing real work for you. - **Config-as-deployment**: YAML serialization plus `Hayhooks` lets ops or non-developers version pipelines and deploy them as REST endpoints without touching Python. ## What both add Both frameworks bring a worldview before they bring features. AutoGen wants you to think in `ConversableAgent` and `GroupChat`; Haystack wants you to think in `@component` classes with typed `run()` signatures. If your actual problem is a single agent calling three tools in a loop, you're paying for orchestration or graph machinery that has nothing to do. There's also the dependency tail: Microsoft's agent stack on one side, deepset's retrieval stack with optional vector DB clients on the other. Onboarding a new engineer means teaching the framework's mental model before they touch your business logic — fine when the abstractions earn it, friction when they don't. --- Source: https://tinyagents.dev/vs/autogen-vs-haystack ### AutoGen vs Google ADK: Which Agent Framework to Use? > Compare AutoGen and Google ADK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AutoGen vs Google ADK, head to head AutoGen models cooperation as conversation. Two agents alternate via `initiate_chat()`, and N agents share a transcript through `GroupChat` with a `GroupChatManager` picking the next speaker — round-robin, random, or LLM-selected. ADK models it as hierarchy: an `LlmAgent` holds a `sub_agents` list and delegates downward, with `SequentialAgent`, `ParallelAgent`, and `LoopAgent` as explicit workflow primitives. AutoGen's structure emerges turn by turn from the transcript; ADK's structure is declared statically before the run. AutoGen has the older, larger footprint — ~57k stars since August 2023, Microsoft Research backing, and a built-in code execution sandbox for agents that need to write and run code. ADK is newer (April 2025, ~19k stars) but ships with first-class Vertex AI deployment, Gemini-tuned defaults, managed `Session`/`State` services, and bidirectional audio/video streaming. If you live on Google Cloud and Gemini, ADK has the cleaner deployment story; otherwise AutoGen has more community examples, a longer track record, and a published research paper. Pick AutoGen when the interaction pattern itself is the value — agents debating, critiquing, or iteratively refining each other's work, where you don't know in advance who should speak next. `register_nested_chats()` is genuinely hard to reproduce well. Pick ADK when you know the topology up front and want it expressed as a tree plus workflow primitives, then containerized to Vertex AI Agent Engine or Cloud Run. AutoGen rewards dynamic, emergent collaboration; ADK rewards structured orchestration backed by a managed cloud runtime. ## Pick AutoGen if Pick AutoGen if your project lives or dies on agents talking to each other dynamically. - **Multi-agent debate is the workflow**: Code review with author/reviewer cycles, planner/executor pairs, or any setup where agents critique and revise each other. `GroupChat` with LLM-based speaker selection is the payoff. - **Sub-conversations branch off the main thread**: `register_nested_chats()` lets an agent spin up a focused sub-discussion and inject the result back. Hand-rolling this with proper `is_termination_msg` handling is tedious. - **Agents need to write and run code**: AutoGen's code execution sandbox is built in. If your agents iterate on generated scripts, that's table stakes you don't have to reinvent. ## Pick Google ADK if Pick Google ADK if your agents need to ship to Vertex AI with hierarchy and workflows declared up front. - **You're already on Google Cloud**: `LlmAgent` + Gemini + Vertex AI Agent Engine is one stack. Deployment, observability, and managed scaling come without glue code. - **Workflow primitives map to your problem**: `SequentialAgent`, `ParallelAgent`, and `LoopAgent` are first-class. If your spec reads like "do A, then B and C in parallel, then loop until done," ADK encodes it directly. - **Hierarchical delegation is real**: A root agent routing to specialized sub-agents — research, writing, review — fits the `sub_agents` model cleanly, with `Session`/`State` carrying context between them. ## What both add Both frameworks bring substantial surface area: class hierarchies, `Runner`/`GroupChatManager` orchestration, session services, callbacks for termination and speaker selection. That's worth the import when you genuinely need multi-agent orchestration — but it's a lot of API to ramp up on for a single-agent system that just calls a few tools. You also inherit the framework's release cadence. AutoGen v0.4 was a major rewrite that broke the v0.2 API; ADK is barely a year old and still expanding its primitives. Pinning versions and reading changelogs becomes part of the maintenance budget. --- Source: https://tinyagents.dev/vs/autogen-vs-google-adk ### AutoGen vs LangGraph: Which Agent Framework to Use? > Compare AutoGen and LangGraph side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AutoGen vs LangGraph, head to head AutoGen models agents as `ConversableAgent` instances that **chat with each other** — a `GroupChat` plus `GroupChatManager` picks the next speaker, and `register_nested_chats()` spawns sub-conversations. LangGraph models agents as a `StateGraph` of nodes and edges over a typed `State` channel, with conditional edges and reducers like `Annotated[list, add_messages]`. One thinks in **dialogue**, the other in **state machines**. AutoGen's primitive is the message; LangGraph's primitive is the node transition. AutoGen ships from Microsoft Research (CC-BY-4.0, ~57k stars) with a code execution sandbox and v0.4 rewrite for scale. LangGraph ships from LangChain Inc (MIT, ~19k stars) and plugs into the LangChain tool/memory layer plus LangSmith tracing and the hosted LangGraph Platform. LangGraph has the deeper **production story** — `PostgresSaver` checkpoints, time-travel debugging, listed users like Replit and Klarna. AutoGen's story is research-flavored multi-agent patterns with enterprise backing. Use AutoGen when the interesting part is **agents debating** — author/reviewer loops, planner/executor pairs, dynamic speaker selection where you don't know the order in advance. Use LangGraph when the interesting part is the **workflow shape** — explicit branches, parallel fanout, checkpointed pause/resume, `interrupt_before` for human approval. A two-agent critique loop is awkward in LangGraph (you'd model speakers as nodes). A long-running approval pipeline with retries is awkward in AutoGen (no first-class checkpointing). Pick the one whose primitive matches your problem shape. ## Pick AutoGen if Pick autogen if your project lives or dies on multiple agents talking to each other in patterns you can't pre-script. - **Dynamic speaker selection**: `GroupChatManager` with LLM-based routing handles "who should respond?" decisions that would be tedious to hand-roll across a 5-agent debate. - **Nested sub-conversations**: `register_nested_chats()` lets one agent spin up a side conversation to resolve a subtask, then return — useful for planner/executor or author/critic loops. - **Code-writing agents**: AutoGen's built-in execution sandbox is a real time-saver if your agents need to write, run, and iterate on code as part of the conversation. ## Pick LangGraph if Pick langgraph if your agent is a workflow with explicit branches, persistence, or human gates — not just a tool-calling loop. - **Checkpointed pause/resume**: `MemorySaver` and `PostgresSaver` per `thread_id` survive crashes and long async waits — non-trivial to bolt on later. - **Human-in-the-loop review**: `interrupt_before` and `interrupt_after` give you approval gates with state inspection; LangSmith shows node-by-node diffs. - **Parallel fanout with merge**: multiple edges from one node plus reducers handle research-style "search 5 sources in parallel, judge, decide" flows where you'd otherwise wire `asyncio.gather` and merge logic by hand. ## What both add Both frameworks bring real conceptual surface area. AutoGen wants you to think in `ConversableAgent`, `initiate_chat`, `is_termination_msg`, and speaker selection policies. LangGraph wants you to think in nodes, edges, typed state channels, reducers, and checkpointers. That's a week of ramp-up before you ship anything that wasn't already a few hundred lines of code. Both also pull in transitive dependencies and lock you into a runtime model that's hard to back out of once your tools, memory, and tracing are wired through it. Worth paying when the abstraction matches your problem; expensive when it doesn't. --- Source: https://tinyagents.dev/vs/autogen-vs-langgraph ### Anthropic Agent SDK vs OpenAI Agents SDK: Which Agent Framework to Use? > Compare Anthropic Agent SDK and OpenAI Agents SDK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Anthropic Agent SDK vs OpenAI Agents SDK, head to head The Anthropic SDK ships Claude Code as a runtime — `bash`, file I/O, web tools, and MCP servers come pre-wired, and you intercept behavior through 18 lifecycle hooks. The OpenAI Agents SDK ships four primitives — `Agent`, `Runner`, `Handoff`, `InputGuardrail`/`OutputGuardrail` — and expects you to bring your own tools as typed Python functions. One is a **productized agent**; the other is a **thin convention layer**. Anthropic gives you a working filesystem-aware coder out of the box. OpenAI gives you `Runner.run()` and gets out of the way. Anthropic's gravity is **MCP**: one-line config connects Playwright, Slack, GitHub, and a growing server registry. OpenAI's gravity is **schema ergonomics**: type hints on a Python function become a tool schema automatically, and `Handoff` between `Agent` objects gives you a clean multi-agent routing pattern without writing the dispatcher. Both are MCP-aware now, but Anthropic treats MCP as a first-class entry point while OpenAI treats it as one transport among many. Model lock-in cuts both ways — neither SDK is realistically portable to the other provider without rewriting the loop. Use the Anthropic SDK when the agent's job is **acting on a machine** — running shell commands, editing files, hitting MCP servers — and you'd rather not reimplement Claude Code's tool implementations. Use the OpenAI Agents SDK when the agent's job is **routing between specialists** — a triage agent handing off to a research agent handing off to a writer — with `InputGuardrail` tripwires gating each step. If your agent is one model, a few custom tools, and a loop, the OpenAI SDK is closer to what you'd write anyway. If your agent needs `bash` and a Playwright browser by Tuesday, Anthropic saves you the plumbing. ## Pick Anthropic Agent SDK if Pick anthropic-sdk if your project lives or dies on real-world tool execution and MCP integrations rather than orchestration logic. - **You need `bash`, file I/O, and web on day one**: The built-in tools are battle-tested from Claude Code itself. Reimplementing reliable shell execution, sandboxing, and file diffing is weeks of work you can skip. - **MCP is your integration story**: One-line config for Playwright, Slack, GitHub, and Postgres beats writing HTTP clients per service. The MCP registry keeps growing without you shipping new code. - **You need production observability**: The 18 lifecycle hooks (`pre_tool_use`, `post_tool_use`, `on_error`, etc.) give you clean injection points for logging, cost tracking, and guardrails without forking the loop. ## Pick OpenAI Agents SDK if Pick openai-agents-sdk if your project lives or dies on multi-agent routing and minimal abstraction over OpenAI's API. - **Handoffs are your core pattern**: `Handoff` between `Agent` objects gives you triage → specialist routing without writing the dispatcher. The pattern composes cleanly as you add more agents. - **You want auto-schema from type hints**: Decorate a Python function, get a JSON tool schema for free. Signature changes propagate to the schema automatically, which kills a real class of drift bugs. - **You want the thinnest possible framework**: `Runner.run()` plus `InputGuardrail`/`OutputGuardrail` tripwires is roughly what you'd write yourself. If your team is standardizing on OpenAI, this gives you conventions without weight. ## What both add Both SDKs lock you into a provider's conventions and version cadence. Anthropic's hooks API and MCP wiring are wider surface area than most teams need; the OpenAI SDK's `Runner` and `Handoff` types add an indirection between you and `chat.completions.create()` that has to be debugged when traces go sideways. Neither is portable. Swapping models means rewriting the loop, the tool format, and the lifecycle integration. If your agent is one model, a `tools` dict, and a `while` loop, you're paying ramp-up and dependency cost for primitives you're not using yet. --- Source: https://tinyagents.dev/vs/anthropic-sdk-vs-openai-agents-sdk ### Agno vs Mastra: Which Agent Framework to Use? > Compare Agno and Mastra side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Agno vs Mastra, head to head Agno is Python-first and class-driven: you instantiate `Agent(model=..., tools=[...], knowledge=...)` and call `.run()`, with `Team` wrapping multiple agents in `sequential`, `parallel`, or `coordinate` modes. Mastra is TypeScript-first and composition-driven: `new Agent({ model, instructions, tools })` plus a separate `Workflow` class with `.step()`, `.then()`, and `.branch()` for explicit control flow. The split is sharper than it looks — Agno bundles orchestration into the agent (teams share memory, dispatch is internal), while Mastra deliberately separates the agent loop from the workflow engine so branching and error handling live outside the LLM call. Agno rides Python's ML stack — vector DBs, PDF loaders, and SQL toolkits drop in as `knowledge` sources or built-in tools. Mastra rides the Node ecosystem — `createTool` uses Zod for schema validation, and the Composio integration adds hundreds of third-party connectors without leaving TypeScript. For observability, Agno leans on `show_tool_calls` and `SqlAgentStorage`/`PostgresAgentStorage` for session persistence. Mastra ships **Mastra Studio**, a local browser GUI for trace inspection — a meaningfully different debugging experience than reading stdout. Use Agno when your agent is multi-modal (vision, audio) or when you need several agents coordinating through a `Team` with shared memory — that's where its abstractions earn their weight. Use Mastra when your bottleneck is a multi-step business process with explicit branching and retries, and your team already lives in Node. Agno's knowledge bases are tighter for RAG-heavy Python agents; Mastra's workflow engine plus Studio is tighter for TypeScript teams shipping production pipelines with non-trivial control flow. ## Pick Agno if Pick agno if your project is Python-native and you want one class to wire model, tools, knowledge, and memory together. - **Multi-modal agents**: Agno's first-class support for vision and audio inputs through the `Agent` class avoids the glue code you'd write against raw provider SDKs. - **Team orchestration with shared memory**: The `Team` class with `sequential`, `parallel`, and `coordinate` modes is more direct than building your own coordinator, especially when agents need to read each other's state. - **Knowledge-heavy agents**: Passing PDFs, URLs, or a vector DB through the `knowledge` param skips the chunk-embed-store boilerplate when RAG is the core of your agent. ## Pick Mastra if Pick mastra if your stack is TypeScript and your agent is really a multi-step workflow with branching, retries, and observability needs. - **Workflow-shaped problems**: The `Workflow` class with `.step()`, `.then()`, and `.branch()` keeps control flow out of the LLM loop — useful when half your logic is deterministic business rules. - **Type-safe tool definitions**: `createTool` with Zod schemas catches bad arguments at compile time and gives the model a clean signature without hand-written JSON Schema. - **Visual debugging via Mastra Studio**: Inspecting traces in a local GUI beats `console.log` once your agent has more than a couple of tool calls per turn. ## What both add Both frameworks introduce a class hierarchy and a configuration surface you'll need to learn before shipping anything — `Agent`, `Team`, `Toolkit`, `Storage` on one side; `Agent`, `Workflow`, `createTool`, Studio on the other. Upgrades cut both ways: you inherit improvements, but breaking changes (Agno's v2 redesign, Mastra's pre-1.0 churn) land on your timeline, not yours. Both also pull in transitive dependencies for features you may not use — built-in toolkits, vector store adapters, embedding clients. If your agent is a single LLM call with two tools, that surface area is overhead you're paying without a return. --- Source: https://tinyagents.dev/vs/agno-vs-mastra ### Mastra vs Pydantic AI: Which Agent Framework to Use? > Compare Mastra and Pydantic AI side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Mastra vs Pydantic AI, head to head Mastra is a **TypeScript-first runtime**: `new Agent({ model, instructions, tools })` plus a `Workflow` class with `.step()`, `.then()`, `.branch()` for orchestration. Pydantic AI is a **Python type-system wrapper**: an `Agent` with a `result_type` Pydantic model, `@agent.tool` decorators, and `RunContext[DepsType]` for typed dependency injection. Mastra optimizes for end-to-end coverage — agents, workflows, RAG, memory, Studio. Pydantic AI optimizes for one thing — making tool args, outputs, and deps validate at write-time. Mastra ships RAG (chunking, embedding, vector search), short-term thread memory plus long-term vector memory, and **Mastra Studio** for local trace inspection. It assumes a Node/TypeScript stack and bundles Composio for third-party tool connections. Pydantic AI ships a **unified interface across 25+ model providers** (`openai:gpt-4o` to `anthropic:claude-sonnet` is a one-line swap), Logfire for observability, and not much else. There is no built-in workflow engine, no RAG pipeline, no GUI debugger — those are your problem. Use Mastra when the agent is one piece of a larger TypeScript app and you need branching workflows, document retrieval, and a debugging GUI in one cohesive package. The `Workflow.branch()` primitive and Studio traces pay off when business logic spans many steps. Use Pydantic AI when the agent feeds typed data into downstream Python systems and you want the LLM's tool calls and final output to validate against `BaseModel` schemas. If your agent produces a `CustomerRecord`, Pydantic AI catches a missing field before it reaches your database — Mastra leaves that to you. ## Pick Mastra if Pick mastra if your project is TypeScript-end-to-end and the agent is one slice of a multi-step application. - **Workflow orchestration is core**: You need explicit `.step()`, `.then()`, `.branch()` semantics with error handling and observability, not just a `while` loop around tool calls. - **RAG and memory are bundled requirements**: Document chunking, embedding, vector search, and cross-session memory should come from one package with a consistent API rather than four glued-together libraries. - **Visual debugging matters to your team**: Mastra Studio's trace viewer and agent playground replace a real chunk of `console.log` archaeology when workflows get non-trivial. ## Pick Pydantic AI if Pick pydantic-ai if your codebase already runs on Pydantic and the agent's outputs feed typed Python systems. - **Structured output is non-negotiable**: `result_type=MyModel` enforces a Pydantic schema on the final response and retries on validation failure, which matters when the agent writes to a typed database or API. - **Model provider churn is real**: Switching `model='openai:gpt-4o'` to `model='anthropic:claude-sonnet'` across 25+ providers is one line, not a request/response refactor. - **Typed dependency injection is wanted**: `RunContext[DepsType]` passes DB clients, configs, or auth into tools without globals, with the IDE catching mismatches at write-time. ## What both add Both frameworks ship abstractions you may not need. Mastra's `Workflow` class, Studio, and bundled RAG are real surface area to learn, version-pin, and debug when something inside the framework misbehaves. Pydantic AI's decorators, `RunContext` generics, and retry-on-validation behavior add indirection between the LLM call and your code. Both lock you into their agent loop. When the model returns something unexpected, you debug through framework internals before reaching your own logic — and upgrade cycles, breaking API changes, and provider compatibility become their schedule, not yours. --- Source: https://tinyagents.dev/vs/mastra-vs-pydantic-ai ### LangGraph vs Mastra: Which Agent Framework to Use? > Compare LangGraph and Mastra side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## LangGraph vs Mastra, head to head LangGraph models an agent as a `StateGraph` — typed `State` channels, `ToolNode`, conditional edges, and reducers like `Annotated[list, add_messages]`. Mastra models it as an `Agent` object plus a `Workflow` class with `.step()`, `.then()`, `.branch()` chained imperatively. One is a graph runtime; the other is a pipeline builder with `createTool` and Zod schemas wired in. LangGraph is **Python-first**, sits inside the LangChain ecosystem, and ships `MemorySaver` / `PostgresSaver` plus LangSmith tracing. Mastra is **TypeScript-first** from the Gatsby team, bundling RAG (chunking, embedding, vector search), thread memory, and Mastra Studio as a local debug GUI. Picking one is partly picking a language: there is no real Python story for Mastra and no first-class TS story for LangGraph. LangGraph wins when control flow is the hard part — `interrupt_before` for human review, parallel fanout with reducer merges, checkpointed pause/resume across `thread_id`s. Mastra wins when the hard part is **assembling** an agent: model + tools + RAG + memory + a visual trace viewer in one Node.js package. LangGraph asks you to think in graphs; Mastra asks you to think in steps and `createTool` definitions. ## Pick LangGraph if Pick langgraph if your project lives or dies on explicit, inspectable control flow over a long-running workflow. - **Human-in-the-loop gates**: You need `interrupt_before` / `interrupt_after` to pause for review and resume from a checkpoint, not a homegrown queue. - **Checkpointed multi-step workflows**: `PostgresSaver` per `thread_id` lets a graph crash, resume, and time-travel through state diffs — Mastra has no equivalent persistence primitive. - **You're already in LangChain / Python**: Tools, retrievers, and LangSmith tracing carry over directly; graduating from `AgentExecutor` to `StateGraph` is a smaller jump than porting to TypeScript. ## Pick Mastra if Pick mastra if your team writes TypeScript and wants agent + RAG + memory + debugger in one install. - **TypeScript-native stack**: `new Agent({ model, instructions, tools })` and `createTool` with Zod schemas keep you in Node.js — no Python sidecar, no LangChain.js port lag. - **Batteries-included RAG and memory**: Document syncing, chunking, embedding, vector search, plus short-term thread memory and long-term vector recall ship in the box instead of being glued together. - **Mastra Studio for debugging**: A local GUI for traces and step inspection beats `console.log` archaeology when workflows have branches and tool calls stacked deep. ## What both add Both frameworks pull in real surface area. LangGraph means typed state channels, reducers, and a graph mental model on top of LangChain itself. Mastra means a `Workflow` class, `createTool` wrappers, and a Studio process — fine when you use them, dead weight when your agent is one LLM call and three tools. Both also bind you to a vendor's idea of an agent loop. Upgrades, breaking changes, and tracing formats are theirs to define. If your workload is a single tool-using loop without branching, checkpointing, or RAG, most of what you're paying for sits unused. --- Source: https://tinyagents.dev/vs/langgraph-vs-mastra ### Mastra vs Vercel AI SDK: Which Agent Framework to Use? > Compare Mastra and Vercel AI SDK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Mastra vs Vercel AI SDK, head to head Mastra is a **batteries-included framework**: `new Agent({ model, instructions, tools })`, a `Workflow` class with `.step()`/`.then()`/`.branch()`, built-in RAG, and dual-tier memory (thread + vector). Vercel AI SDK is a **primitives toolkit**: `generateText`, `streamText`, `generateObject`, and `tool({ parameters: z.object(...), execute })` — you compose, it doesn't orchestrate. The split shows up in the agent loop. Mastra owns the loop end-to-end with workflow steps and conditions as first-class objects. The AI SDK gives you `maxSteps` on `generateText` and gets out of the way — there's no `Workflow` class, no `.branch()`, no Studio. Vercel AI SDK is the **larger ecosystem by an order of magnitude** — 2.4M weekly npm downloads vs Mastra's 244k, used by v0.dev, Cursor, and Sourcegraph. Provider portability is built in: swap `openai('gpt-4o')` for `anthropic('claude-3-5-sonnet')` in one import. React hooks (`useChat`, `useCompletion`, `streamUI`) are unique to the SDK. Mastra's ecosystem is younger but **more vertically integrated**. You get RAG (chunking, embedding, vector search), memory across sessions, and Mastra Studio for local trace inspection in one install. The Composio integration adds hundreds of third-party tools. Use Vercel AI SDK when **chat is a UI feature** in a Next.js or React app — `useChat` alone saves a day of `useState` plumbing, and `streamUI` is the only way to stream React components from a server. Use Mastra when you're building a **server-side agent platform** with multi-step workflows, RAG, and observability needs. The SDK is sharper on the wire and the browser; Mastra is sharper on the orchestration layer behind it. ## Pick Mastra if Pick mastra if your project lives or dies on multi-step orchestration with RAG and memory baked in. - **You need a `Workflow` engine, not just a loop**: explicit `.step()`, `.then()`, `.branch()` with error handling beats hand-rolled `if`/`else` chains once you cross three or four steps. - **RAG is in scope, not a future maybe**: Mastra ships document syncing, chunking, embedding, and vector search as one pipeline — you're not gluing Pinecone, an embedding client, and a chunker yourself. - **You want Mastra Studio for debugging**: a local GUI for traces and agent runs is meaningfully faster than `console.log` once workflows get non-trivial. ## Pick Vercel AI SDK if Pick vercel-ai-sdk if your product is a TypeScript app where streaming chat or inline AI is a first-class UI surface. - **`useChat` is the killer feature**: messages, input, `handleSubmit`, and `isLoading` wired up correctly out of the box — the boilerplate it replaces is real, not imagined. - **You A/B between providers in production**: one-import swaps from `openai()` to `anthropic()` to `google()` matter when you're tuning cost and quality on live traffic. - **You're on Vercel or shipping RSC**: AI Gateway, observability, BYOK, and `streamUI` for streaming React components from the server are zero-config there and nowhere else. ## What both add Both frameworks add a dependency surface you'll carry forever: version pins, breaking changes between majors, and behavior that's defined by the framework rather than your code. Mastra's `Workflow` class and Vercel's `generateText` both abstract the agent loop, which is fine until you need to debug *why* a tool fired twice or a step branched wrong. Ramp-up isn't free either. Both have their own mental model — Mastra's steps and conditions, Vercel's `maxSteps` and stream protocols — and neither maps cleanly to the raw HTTP that's actually happening. If your agent is one LLM call, three tools, and a `while` loop, you're paying framework tax for features you won't use. --- Source: https://tinyagents.dev/vs/mastra-vs-vercel-ai-sdk ### Eve vs Mastra: Which Agent Framework to Use? > Compare Eve and Mastra side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Eve vs Mastra, head to head Eve and Mastra are both TypeScript-native agent frameworks aimed at production, but they were shaped by different constraints — and that shows up in what each one bundles by default. Mastra is **deploy-agnostic and batteries-included**. `new Agent({ model, instructions, tools })` is one line. Tools use `createTool` with Zod schemas. Workflows are a first-class primitive: `Workflow.step().then().branch()`. RAG is built in (chunking, embedding, vector store, retrieval). Memory is short-term-per-thread and long-term-across-sessions. **Mastra Studio** is a local browser-based debugger for stepping through agent runs. Series A of $22M in April 2026, relicensed to Apache 2.0 the same month. Eve is **convention-driven and Vercel-runtime-coupled**. An agent is a directory: `agent.ts`, `instructions.md`, `tools/`, `skills/`, `subagents/`, `channels/`, `schedules/`. The framework wires the files together. Under the hood it composes the Vercel Workflow SDK (durable execution), Vercel Sandbox (isolated code exec), and AI Gateway (multi-model routing). The runtime primitives are the real value — the convention is the wrapper. The honest tradeoff: Mastra gives you agent + workflow + RAG + debugger in one install, and it runs wherever Node runs. Eve gives you agent + durable execution + sandboxed exec + provider routing, and its full power is on Vercel. If Studio + built-in RAG + deploy-anywhere is what you need, pick Mastra. If Vercel is your target and durable execution + sandboxed exec are the reasons you're reaching for a framework, pick Eve. ## Pick Eve if Pick Eve when Vercel is the deploy target and the Workflow SDK + Sandbox are the actual leverage. - Long-running or crash-prone agents; the checkpointed workflow layer means the agent resumes cleanly after a restart. - You're running LLM-generated code — Vercel Sandbox is one API call and you don't have to run micro-VMs yourself. - You already ship on Vercel and want agents to inherit the same deploy story as Next.js. - The filesystem-shaped convention (`tools/`, `skills/`, `subagents/`) matches how your team scales. ## Pick Mastra if Pick Mastra when you want a batteries-included TypeScript agent framework you can deploy anywhere. - Built-in RAG is genuinely useful — chunking, embedding, vector store, retrieval, all one install. - Mastra Studio is a real productivity lever for iterating on agents locally; visual traces beat `console.log`. - Deploy target is TBD or heterogeneous — Mastra runs on Node, doesn't care about the platform. - Workflow branching (`Workflow.step().then().branch()`) matches your control flow needs without a graph DSL. ## What both add Both are actively evolving TypeScript-native frameworks — Mastra just took Series A, Eve is weeks old. Version churn is likely on both; pin your dependencies. Both also add a **framework layer between your code and the model API**. When something goes wrong at the HTTP level, you're reading framework source before you see the actual request. That's the same tax every framework charges — and worth paying when the abstractions do real work for you, and expensive when you're using 20% of the surface. --- Source: https://tinyagents.dev/vs/eve-vs-mastra ### Flue vs Mastra: Which Agent Framework to Use? > Compare Flue and Mastra side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Flue vs Mastra, head to head Flue and Mastra are both TypeScript-native agent frameworks with production ambitions, and the decision between them isn't language or paradigm — both are declarative-ish, both use per-tool schemas, both target teams that don't want a Python sidecar. It's what each one bundles by default, and where each one wants to run. Mastra is **deploy-agnostic and batteries-included**. `new Agent({ model, instructions, tools })` is one line. Tools register with `createTool` + Zod. Workflows are first-class: `Workflow.step().then().branch()`. RAG is built in — chunking, embedding, vector store, retrieval. Memory spans short-term (per-thread) and long-term (vector). **Mastra Studio** is a local browser-based debugger. Series A of $22M in April 2026 from Spark Capital; relicensed to Apache 2.0 the same month. Flue is **Cloudflare-native and thinner**. `createAgent({ model, instructions, tools })` is also one line, but state lives in **Durable Streams** — a replayable event log stored in Cloudflare Durable Objects, giving per-agent persistence and locking without an external database. Tools use valibot instead of Zod (smaller runtime, faster). Built on the Pi harness (same runtime as OpenClaw). One config deploys to Cloudflare, Node, GitHub Actions, and GitLab CI — the cross-runtime story is genuinely unusual. The honest split is by deploy target and surface area. **Mastra gives you more out of the box** (Studio, RAG, workflow engine) and runs anywhere Node runs. **Flue gives you Cloudflare Durable Objects as first-class state** and a lighter framework surface, but its main leverage is only there on Cloudflare. ## Pick Flue if Pick Flue when Cloudflare Durable Objects earn their keep. - Per-agent state + locking via Durable Objects is a real primitive; you'd otherwise wire Redis + Postgres + a queue. - Same agent needs to run in production AND in CI (nightly bots, GitHub Actions research agents) — the cross-runtime config matters. - You want a thinner framework: valibot instead of Zod, no Studio, no built-in RAG, less surface to learn. - Your team is on the Pi/OpenClaw stack and shared tooling matters. ## Pick Mastra if Pick Mastra when deploy target is TBD or heterogeneous, and you want the batteries. - Built-in RAG (chunking, embedding, vector store, retrieval) saves real integration work. - Mastra Studio is a productivity lever — visual traces beat `console.log` when iterating on tool behavior. - Workflow branching (`Workflow.step().then().branch()`) covers the orchestration case without pulling in a graph DSL. - You want the option to move deploy targets later without rewriting; Mastra runs anywhere Node runs. ## What both add Both are TypeScript-first and both add a real framework layer between your code and the model API. When something misbehaves at the HTTP level, you'll be reading framework source before you see the actual request. That's the tax; whether it's worth it depends on how much of each framework's surface you actually use. Both are also evolving quickly. Flue is a 1.0 beta as of mid-2026 — API churn likely. Mastra just took Series A and is moving fast on the platform side. Pin your dependencies either way. --- Source: https://tinyagents.dev/vs/flue-vs-mastra ### Eve vs LangGraph: Which Agent Framework to Use? > Compare Eve and LangGraph side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Eve vs LangGraph, head to head Eve and LangGraph both target the same problem — **durable, stateful, multi-step agent workflows** — and both are the answer their respective ecosystems arrived at. Everything past that is language and mental model. LangGraph is Python-first and models the workflow as a **graph of nodes with typed state channels**. `StateGraph` holds nodes (functions), edges (conditional or unconditional), and a shared `State` object with reducers like `add_messages` that append instead of overwriting. It shipped 1.0 GA in October 2025 alongside LangChain 1.0 — API stability is now the point. Persistence comes from `MemorySaver` / `PostgresSaver` checkpointers; human-in-the-loop from `interrupt_before` / `interrupt_after`; parallel fanout from multiple edges + reducers. Eve is TypeScript-first and models the agent as a **directory of files**. `agent.ts` + `instructions.md` at the root; `tools/`, `skills/`, `subagents/`, `channels/`, `schedules/` as subfolders. Durability comes from the Vercel Workflow SDK — the runtime checkpoints every step, so an agent crashed mid-execution resumes on next invocation. Sub-agent hand-off is a call into a file under `subagents/`. Sandboxed code exec is a Vercel Sandbox API call. The abstraction shapes are legitimately different: LangGraph asks you to think in state machines and typed reducers; Eve asks you to think in filesystems and conventions. Neither is "better" — they're built for different populations. Python teams already reasoning about `Annotated[list, add_messages]` will pick LangGraph; TypeScript teams already reasoning about `app/` directories will pick Eve. ## Pick Eve if Pick Eve when the team is TypeScript-native and durable execution + sandboxed exec are the actual constraints. - Vercel is the deploy target; Workflow SDK + Sandbox + AI Gateway ship together and you'd otherwise glue three services. - You want filesystem-shaped conventions — a new engineer knows where tools go without reading a wiki. - The agent runs LLM-generated code and Sandbox is the real leverage. - Persistence + retry + sub-agent hand-off is what you need; you don't need typed state reducers or a graph DSL. ## Pick LangGraph if Pick LangGraph when Python is the stack and the workflow is genuinely graph-shaped. - Your workflow has real branching, parallel fanout with merge, or human approval gates — the graph DSL earns its cost. - LangSmith tracing pays for itself; you want node-by-node execution traces with state diffs in production. - You're already in the LangChain ecosystem and graduating from `AgentExecutor` without rewriting your tool + memory layer. - Typed state channels with reducers match how your team already thinks about state — the graph reads like a state machine to you. ## What both add Both add a real runtime — LangGraph's checkpointer + graph traversal, Eve's Workflow SDK + Sandbox — that's harder to justify on single-loop agents. If your agent is one model call in a while loop, either one is over-engineered. Both also lock you into an ecosystem: LangGraph into LangChain + LangSmith for the observability payoff, Eve into Vercel for the Workflow SDK + Sandbox payoff. That's fine when the payoff is the reason you picked the framework, and expensive when you're paying the tax without using the leverage. --- Source: https://tinyagents.dev/vs/eve-vs-langgraph ### Eve vs Vercel AI SDK: Which Agent Framework to Use? > Compare Eve and Vercel AI SDK side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Eve vs Vercel AI SDK, head to head This is the one comparison where "vs" is misleading. Eve and the Vercel AI SDK are **from the same company, built to compose, and solve different layers of the problem**. Reading them as competitors leads to picking wrong. The **Vercel AI SDK** is the low-level toolkit. `generateText`, `streamText`, `generateObject` normalize provider APIs behind one shape. Tools use `tool({ description, parameters: z.object(...), execute })`. React hooks (`useChat`, `useCompletion`) handle the streaming + optimistic-update plumbing for chat UIs. The mental model is "a better `fetch` for LLM calls with tools and UI baked in." **Eve** is the higher-level agent framework, launched June 17 2026. An agent is a directory (`agent.ts`, `instructions.md`, `tools/`, `skills/`, `subagents/`, `channels/`, `schedules/`), and Eve composes three Vercel runtime primitives under the hood: the Workflow SDK for durable execution, Vercel Sandbox for isolated code exec, and — critically — **the AI SDK itself for the model calls**. Eve doesn't replace the AI SDK; it uses it. The decision is about how much surface you need. If you're adding an inline AI feature to a Next.js SaaS dashboard, the AI SDK is the whole answer — `useChat` alone saves a day of state plumbing. If you're building a long-running agent app with multiple sub-agents, scheduled jobs, sandboxed code exec, and durable crash recovery, Eve is the answer, and the AI SDK is a piece of it. ## Pick Eve if Pick Eve when the surface is a full agent app, not a chat feature inside another app. - Multiple tools, sub-agents, or scheduled jobs make a directory convention feel earned rather than heavy. - Durable execution — surviving a crash mid-workflow — is a real requirement, not a nice-to-have. - LLM-generated code needs to run in isolation; Vercel Sandbox is the reason you're here. - You'd otherwise be gluing the AI SDK to a workflow queue, a code sandbox service, and a cron scheduler yourself. ## Pick Vercel AI SDK if Pick the Vercel AI SDK when the LLM is a feature inside a bigger app, not the whole product. - Building a chat UI, an AI dashboard widget, or an inline generation feature in an existing React/Next.js app. - Streaming-first primitives + `useChat` are what you're paying for; a full agent framework is overkill. - You want provider portability (`openai('gpt-4o')` → `anthropic('claude-3-5-sonnet')` is one import) without inheriting an agent runtime. - A 60-line `fetch`-based agent would work too; the AI SDK just removes the boilerplate for the parts that repeat. ## What both add Because they're both Vercel products, both benefit from Vercel-hosted deploys (AI Gateway, observability, BYOK), and both are Vercel-flavored in ways that show up when you deploy elsewhere. Neither is a hard lock-in — the AI SDK runs anywhere Node runs, Eve is open source — but the payoff is skewed toward the Vercel platform in both cases. The honest read: **most teams don't need to pick between them**. If you know the answer is "a chat feature," you're on the AI SDK. If you know the answer is "an agent app," you're on Eve — and you're using the AI SDK underneath it whether you notice or not. --- Source: https://tinyagents.dev/vs/eve-vs-vercel-ai-sdk ### AWS Bedrock AgentCore vs AWS Strands Agents: Which Agent Framework to Use? > Compare AWS Bedrock AgentCore and AWS Strands Agents side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## AWS Bedrock AgentCore vs AWS Strands Agents, head to head AgentCore is the **runtime layer** — a closed-source AWS SaaS bundling Runtime, Memory, Identity, Gateway, and Observability. You don't import it; you point it at agent code and it executes inside MicroVM-isolated sessions. Strands is the **SDK layer** — an Apache-2.0 Python package with `Agent(model, tools, system_prompt)`, the `@tool` decorator, and multi-agent primitives like `Graph` and `Swarm`. They sit at different layers of the stack: Strands is what you write; AgentCore is where it runs. AgentCore is **framework-agnostic** — it runs Strands, LangGraph, CrewAI, or hand-rolled Python equally. Strands has its own ecosystem story: native MCP server/client support, type-hint-driven tool schemas, and multi-agent primitives shipped in the SDK. The two are designed by AWS to pair, but neither requires the other — you can run LangGraph on AgentCore, or run Strands on a bare Lambda. The GitHub picture matches: Strands lives at `strands-agents/sdk-python` (~4.2k stars, Apache-2.0); AgentCore is proprietary AWS infrastructure with no source repo. The choice isn't really A vs B — it's which **layer you're picking for**. AgentCore earns its place when operational concerns are real: multi-tenant session isolation, per-user OAuth credential vending, OTel traces with cost attribution, long-term memory at scale. Strands earns its place when you want a thin SDK that doesn't fight Bedrock and treats MCP as a first-class citizen. Production AWS deployments often use both; teams off AWS use neither. ## Pick AWS Bedrock AgentCore if Pick aws-agentcore if your project lives or dies on running multi-tenant agents safely on AWS. - **MicroVM-isolated sessions**: AgentCore runs each session in its own container for up to 8 hours, isolated from other users' state. Reproducing that on shared Lambda or Fargate is genuinely hard. - **OAuth + Secrets Manager identity**: Per-user credential vending for agents acting as a user against Slack, GitHub, or other external APIs. Building it from Cognito plus a token-vending Lambda is weeks of work. - **Observability and cost attribution**: OTel traces, per-step LLM cost, error grouping in CloudWatch — wired up by default rather than assembled from a Honeycomb or Datadog backend you operate. ## Pick AWS Strands Agents if Pick aws-strands if your project lives or dies on writing clean agent code that targets Bedrock and MCP. - **Model-driven loop**: `Agent(model, tools, system_prompt)` lets the model decide when to call tools and when to stop. No `AgentExecutor`, no graph nodes — closer to how the underlying provider API actually works. - **`@tool` decorator with type-hint schemas**: Decorate a Python function, type hints become the JSON schema. Skips the ~15 lines of `inspect`-based schema generation you'd write per tool. - **First-class MCP**: Run an agent as an MCP server, consume MCP servers as tools, no JSON-RPC-over-stdio handshake to implement. Better ergonomics than LangChain's `AgentExecutor` or CrewAI's `Crew` for MCP-centric designs. ## What both add Both lock you to AWS in ways worth naming. Strands works against any provider, but the ergonomic story assumes Bedrock and AgentCore — examples, deploy guides, and observability hooks are AWS-flavored. AgentCore is an AWS service end-to-end; you pay for Runtime, Memory, and Gateway as managed line items rather than infrastructure you own. Both also add abstractions you might not need yet. Strands' `Graph` and `Swarm` primitives are useful for genuine multi-agent shapes but premature for single-agent prototypes. AgentCore's five managed services solve problems — session isolation, OAuth vending, long-term memory — that single-tenant agents never encounter. --- Source: https://tinyagents.dev/vs/aws-agentcore-vs-aws-strands ### Eve vs Flue: Which Agent Framework to Use? > Compare Eve and Flue side by side. See what each framework abstracts and how the same patterns look in ~60 lines of plain Python. ## Eve vs Flue, head to head Eve and Flue are the two TypeScript agent frameworks that launched within six weeks of each other in mid-2026 — Flue from the Astro team on May 1, Eve from Vercel on June 17 — and the temptation to lump them together as "the new TS agent frameworks" hides a real difference in what each one is actually shaped by. Eve is **convention-driven**: an agent is a directory. `agent.ts`, `instructions.md`, `tools/`, `skills/`, `subagents/`, `channels/`, `schedules/` — each subfolder maps to a capability, and the framework wires them together. Vercel calls it "Next.js for agents" and the analogy holds. Under the hood, Eve composes three Vercel primitives: the Workflow SDK for durable execution, Sandbox for isolated code exec, and AI Gateway for provider routing. The runtime is the value. Flue is **declarative**: `createAgent({ model, instructions, tools })` describes the agent, valibot schemas type the tools, and state lives in a Durable Stream — a replayable event log stored in Cloudflare Durable Objects. It's built on the Pi harness (same runtime as OpenClaw) and deploys not just to Cloudflare, but also to plain Node, GitHub Actions, and GitLab CI from one config. The cross-runtime story is unusual — most frameworks pick a home; Flue picks four. The honest split is by deploy target and abstraction shape. If your agents live on Vercel and you want a Next.js-shaped mental model with a filesystem full of tools, Eve fits. If your agents live on Cloudflare (or need to run in CI as well as in production), and you want one config object rather than a directory, Flue fits. ## Pick Eve if Pick Eve when the deploy target is Vercel and the "Next.js for agents" convention matches how your team already thinks. - You want durable execution, sandboxed exec, and AI Gateway routing bundled — not three separate services to wire. - A filesystem of tools + sub-agents + schedules matches how your team scales code review across engineers. - You already run Next.js on Vercel and want the same deploy story for agents. - The Vercel Sandbox is the actual leverage — you're running LLM-generated code and can't put it in your main process. ## Pick Flue if Pick Flue when Cloudflare is the target and Durable Objects earn their keep. - Per-agent state + locking via Durable Objects is a real primitive; Flue exposes it as first-class rather than glued on. - The same agent needs to run in production AND in CI (nightly research agents, GitHub Actions bots) — Flue's cross-runtime deploy is genuinely rare. - You want a thinner framework: one `createAgent` config, valibot tool schemas, no directory convention to internalize. - Your team is on the Pi/OpenClaw stack and wants shared tooling. ## What both add Both are young — Eve is weeks old at time of writing, Flue is a 1.0 beta. API churn is likely. Pin versions and treat both like early production dependencies, not stable ones. Both are also **runtime-coupled** in a way most agent frameworks aren't. Eve's real value is Vercel Workflow SDK + Sandbox + AI Gateway; without those, you're paying convention overhead for little runtime benefit. Flue's real value is Cloudflare Durable Objects; without Cloudflare, you get a competent declarative TS agent framework but not the differentiator. If your deploy target isn't Vercel or Cloudflare, either you're picking based on abstraction shape alone, or you should look at Mastra (deploy-agnostic) or plain code instead. --- Source: https://tinyagents.dev/vs/eve-vs-flue