The hype around "agentic AI" is real, but the frameworks that exist are often overengineered. They're designed for enterprise teams building complex multi-agent systems with observability dashboards, logging, and consensus protocols.
If you're a solo developer, you need something simpler: a framework that lets you define an agent's capabilities, give it a goal, and let it execute. Something that runs on your machine, doesn't require Kubernetes or a Vector database, and gets out of your way.
As of April 2026, you have real options. This guide walks through the frameworks that actually work for solo developers building production agents.
What Does "Agentic" Actually Mean?
Let's define the term because it's overloaded:
Not agentic: Chatbots, code generators, autocomplete. These react to input but don't plan across multiple steps.
Agentic: A system that can break "build a REST API" into subtasks, execute them, test the results, catch failures, and iterate.
In 2026, agentic systems are still early, but practical. You can build them. And they're faster than doing the same work manually.
Framework Comparison: The Essentials
| Framework | TypeScript Support | Learning Curve | Best For |
|---|---|---|---|
| LangGraph | ✓ Native | Medium | Complex agentic workflows, state management |
| Mastra | ✓ Native | Low | Quick agent prototypes, business automation |
| CrewAI | ✓ Via js-exec | Low | Multi-agent coordination, roleplaying |
| AutoGen (MS) | ✓ Via python | Medium | Enterprise multi-agent, conversation loops |
LangGraph: The Flexible Workhorse
What It Is
LangGraph is a framework for building stateful, multi-step AI workflows. You define nodes (steps), edges (transitions), and state. The framework manages execution, logging, and rollback.
Solo Developer Strengths
- State management out-of-the-box. You define your agent's state once, and it's preserved across steps.
- Visual debugging. Built-in visualization shows exactly what the agent is doing.
- Loop detection. The framework catches infinite loops and stops them.
- Runs locally. No cloud dependency (though it integrates with LangSmith for observability if you want).
Hello World Example
import { StateGraph, START, END } from '@langchain/langgraph';
import { ChatAnthropic } from '@langchain/anthropic';
interface AgentState {
messages: string[];
task: string;
}
const llm = new ChatAnthropic({ model: 'claude-3-5-sonnet' });
// Define the agent logic
async function agentNode(state: AgentState) {
const response = await llm.invoke([
{ role: 'user',
content: `Task: ${state.task}\n\nProgress so far: ${state.messages.join('\n')}` }
]);
return {
messages: [...state.messages, response.content]
};
}
// Define the workflow
const graph = new StateGraph(AgentState)
.addNode('agent', agentNode)
.addEdge(START, 'agent')
.addEdge('agent', END);
const app = graph.compile();
// Run it
const result = await app.invoke({
task: 'Write a TypeScript function to validate email addresses',
messages: []
});
console.log(result.messages);
That's the basics. Here's what makes it powerful: you can add loops, tool calls, conditional branching, and human-in-the-loop reviews all within the same framework.
Real Use Case: AI Code Review Agent
// Define state
interface ReviewState {
pullRequest: { title: string; diff: string };
reviewNotes: string[];
approved: boolean;
}
// Multi-step agent
const reviewGraph = new StateGraph(ReviewState)
.addNode('analyze', analyzeCodeNode) // Step 1: Read the PR
.addNode('lint', lintCheckNode) // Step 2: Check for issues
.addNode('test', testRunNode) // Step 3: Run tests
.addNode('decision', approvalNode) // Step 4: Make a decision
.addEdge(START, 'analyze')
.addEdge('analyze', 'lint')
.addEdge('lint', 'test')
.addEdge('test', 'decision')
.addEdge('decision', END);
// The agent will:
// 1. Analyze the code
// 2. Run linting
// 3. Run tests
// 4. Make a decision to approve or request changes
const app = reviewGraph.compile();
const result = await app.invoke({
pullRequest: prData,
reviewNotes: [],
approved: false
});
The beauty here: if step 3 (test run) fails, you can configure the agent to iterate. Loop back to analyze, propose a fix, run tests again, etc.
When LangGraph Shines
- You need complex state management across steps
- You want to add human approvals mid-workflow ("pause here, let a human decide")
- You're building agentic loops (think → act → observe → repeat)
LangGraph Gotchas
- Steeper learning curve (graph thinking takes time)
- Overhead for simple tasks (use Mastra if your agent is just "do one thing")
Mastra: The Lightweight Alternative
What It Is
Mastra is a newer TypeScript-first agent framework. It's smaller, faster to set up, and has a lower learning curve than LangGraph.
Solo Developer Strengths
- Minimal boilerplate. Define an agent in 10 lines of code.
- TypeScript-native. Built by and for TypeScript developers.
- Built-in tool integration. Use HTTP, files, shell commands as agent tools without extra setup.
- Fast iteration. Change your agent, reload, test. No complicated configuration.
Hello World Example
import { Agent } from '@mastra/core';
const agent = new Agent({
name: 'CodeGenerator',
model: 'claude-3-5-sonnet',
tools: [
{
name: 'write_file',
description: 'Write code to a file',
execute: async (path: string, content: string) => {
await fs.writeFile(path, content);
return `Written to ${path}`;
}
},
{
name: 'run_test',
description: 'Run tests on a file',
execute: async (path: string) => {
const result = await exec(`npm test ${path}`);
return result.stdout;
}
}
]
});
// Use the agent
const result = await agent.run({
prompt: 'Build a user authentication system in TypeScript with tests'
});
Done. The agent now knows about two tools (write files, run tests) and can use them to accomplish the goal.
When Mastra Shines
- You want fast iteration. Setup in minutes, not hours.
- Your agent is relatively simple (one goal, multiple tools)
- You're building a business automation or internal tool
- You want TypeScript all the way down
Mastra Limitations
- Less mature than LangGraph (smaller ecosystem)
- Limited state management out-of-the-box
CrewAI: The Multi-Agent Approach
What It Is
CrewAI is designed for multi-agent systems where agents have different roles and collaborate. Less common for solo developers, but powerful if you need it.
Use Case: Content Generation
// Define agents with roles
const researcher = new Agent({
role: 'Research Analyst',
goal: 'Find and synthesize information on the topic',
tools: [webSearch, documentRead]
});
const writer = new Agent({
role: 'Content Writer',
goal: 'Write engaging, accurate content',
tools: [fileWrite]
});
const editor = new Agent({
role: 'Editor',
goal: 'Ensure quality, coherence, correctness',
tools: [fileEdit]
});
// Define tasks
const tasks = [
{
agent: researcher,
description: 'Research AI agents in 2026'
},
{
agent: writer,
description: 'Write a blog post based on research'
},
{
agent: editor,
description: 'Edit and polish the post'
}
];
// Run the crew
const crew = new Crew({ agents: [researcher, writer, editor], tasks });
const result = await crew.run();
Now you have three agents working in sequence, each with their own role, tools, and decision-making process.
When CrewAI Shines
- You need multiple specialized agents
- Agents should interact and collaborate
- You want role-based thinking (researcher ≠ writer ≠ critic)
CrewAI for Solo Developers
Honest take: CrewAI adds complexity for solo developers. You're often better off with LangGraph or Mastra. But if you're automating a process that naturally breaks into roles (research → write → edit → publish), CrewAI's role-based approach can be elegant.
Which Framework Should You Actually Use?
Start with Mastra if:
• You want the fastest setup
• Your agent is simple (one goal, multiple tools)
• You're building internal automation
✓ Low friction
✗ Limited complexity
Start with LangGraph if:
• Your agent needs complex state
• You need loops and feedback
• You might scale to multiple agents later
✓ Scales well
✗ Steeper setup
Production Considerations
Error Handling & Resilience
Agents can fail. Your database might be down. An API might timeout. Here's how to handle it:
const agent = new Agent({
name: 'ResilientAgent',
model: 'claude-3-5-sonnet',
maxRetries: 3, // Retry failed tool calls
timeout: 30000, // 30-second timeout per step
tools: [
{
name: 'call_api',
execute: async (url: string) => {
try {
const response = await fetch(url, { timeout: 5000 });
if (!response.ok) throw new Error(`${response.status}`);
return response.json();
} catch (error) {
// Return structured error so agent can decide
return { error: error.message, retry: true };
}
}
}
]
});
// Agent will see { error: "Connection timeout", retry: true }
// and can decide to retry or try a different approach
Observability & Logging
You need to know what your agent did and why. Log everything:
const agent = new Agent({
name: 'LoggingAgent',
// ... config
onStep: (step) => {
console.log(`[${new Date().toISOString()}] Step: ${step.action}`);
console.log(`Input: ${JSON.stringify(step.input)}`);
console.log(`Output: ${JSON.stringify(step.output)}`);
}
});
// In production, send logs to a service:
import { sendToLoggingService } from './monitoring';
const agent = new Agent({
// ...
onStep: (step) => {
sendToLoggingService({
agent: 'MyAgent',
timestamp: Date.now(),
step: step.action,
input: step.input,
output: step.output
});
}
});
Testing Your Agent
Agents are non-deterministic. You can't unit test them like normal code. Instead, test the workflows:
describe('CodeGeneratorAgent', () => {
it('should generate valid TypeScript', async () => {
const agent = new Agent({ /* ... */ });
const result = await agent.run({
prompt: 'Generate a function that sums two numbers'
});
// Check the output is TypeScript
expect(result).toMatch(/function|const.*=.*/);
// Try to parse it
expect(() => {
new Function(result);
}).not.toThrow();
});
it('should handle failures gracefully', async () => {
// Test with a bad model
const agent = new Agent({ model: 'nonexistent-model' });
expect(async () => {
await agent.run({ prompt: 'test' });
}).rejects.toThrow();
});
});
Real Performance: What You'll Actually See
Mastra Agent (Simple: "Generate a function")
- Setup time: 5 minutes
- Execution time: 3-5 seconds (one LLM call)
- Success rate: ~90% (works on first try)
- Code quality: ~85% (usually works, needs minor tweaks)
LangGraph Agent (Complex: "Build a REST API, run tests, iterate until passing")
- Setup time: 30 minutes
- Execution time: 30-90 seconds (multiple steps, retries)
- Success rate: ~75% (succeeds most of the time)
- Code quality: ~92% (agent iterates until good)
The key insight: Simple agents are fast and reliable. Complex agents are slower but produce better results. Match the tool to the task.
Common Pitfalls & How to Avoid Them
Pitfall 1: Infinite Loops
Problem: Agent keeps doing the same thing, never makes progress.
Fix: Set a max step limit.
const agent = new Agent({
// ...
maxSteps: 10 // Stop after 10 steps no matter what
});
Pitfall 2: Context Window Explosion
Problem: Agent's context grows with every step, eventually runs out of tokens.
Fix: Summarize state periodically.
// In your agent's state management
if (state.messages.length > 20) {
// Summarize the first 10 messages
const summary = await llm.summarize(state.messages.slice(0, 10));
state.messages = [summary, ...state.messages.slice(10)];
}
Pitfall 3: Tool Hallucination
Problem: Agent calls a tool that doesn't exist or with wrong parameters.
Fix: Validate tool inputs in your framework (most frameworks do this, but double-check).
const agent = new Agent({
tools: [
{
name: 'write_file',
schema: {
path: { type: 'string', required: true },
content: { type: 'string', required: true }
},
execute: async (path, content) => {
// Validate inputs
if (!path || !content) throw new Error('Missing required params');
// ... rest of execution
}
}
]
});
Deployment: From Laptop to Production
Local Development (Mastra)
// 1. Define your agent
// 2. Test with: npm run dev
// 3. See logs in real time
Production (Deploy to Cloud)
// Deploy to Vercel / Railway / Fly.io
import { serve } from 'https://deno.land/std/http/server.ts';
const agent = new Agent({ /* ... */ });
serve({
'/agent': async (req) => {
const body = await req.json();
const result = await agent.run(body);
return new Response(JSON.stringify(result));
}
});
// Now your agent is callable via HTTP
// POST /agent with { prompt: "..." }
Scaling: Multiple Agents
Start with one agent. If you need more:
- Use LangGraph for multi-step workflows with one agent
- Use CrewAI for multiple specialized agents
- Add a message queue (Redis, Bull) if agents run long tasks
The 2026 Reality
Agentic systems are no longer experimental. They're practical, shipping in production, and adding real value.
For solo developers, the barrier to entry is lower than ever. Pick a framework (Mastra for speed, LangGraph for flexibility), define your agent's tools, and let it go. You'll be surprised how much it can handle.
The next year will see these frameworks mature further. But today? They're ready for work.
Learn how to structure prompts and context for agentic systems to actually work. Browse our templates →