--- id: ai-langgraph-agent-frameworks title: Agent Frameworks — LangGraph / Mastra / CrewAI category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ai, agent, framework, vibe-coding] tech_stack: { language: "TS / Python", applicable_to: ["Backend"] } applied_in: [] aliases: [LangGraph, LangChain, Mastra, CrewAI, AutoGen, agent state, multi-agent] --- # Agent Frameworks > Agent 구현 framework. **LangGraph (state machine), Mastra (TS modern), CrewAI (multi-agent), AutoGen (Microsoft)**. 자체 implementation 도 좋음 — overkill 주의. ## 📖 핵심 개념 - State graph: node + edge. - State persistence: checkpoint. - Streaming: 매 step 응답. - Human-in-the-loop: 중간 confirm. ## 💻 코드 패턴 ### LangGraph (Python / JS) ```ts import { StateGraph, END, MemorySaver } from '@langchain/langgraph'; import { ChatAnthropic } from '@langchain/anthropic'; import { z } from 'zod'; const State = z.object({ messages: z.array(z.any()), toolResults: z.record(z.string()).optional(), }); const llm = new ChatAnthropic({ model: 'claude-opus-4-7' }); const graph = new StateGraph(State) .addNode('agent', async (state) => { const response = await llm.invoke(state.messages); return { messages: [...state.messages, response] }; }) .addNode('tools', async (state) => { const last = state.messages.at(-1); const results: Record = {}; for (const call of last.tool_calls ?? []) { results[call.id] = await executeTool(call.name, call.args); } return { messages: [...state.messages, ...Object.entries(results).map(([id, content]) => ({ role: 'tool', tool_call_id: id, content }))], toolResults: results, }; }) .addEdge('__start__', 'agent') .addConditionalEdges('agent', (state) => { const last = state.messages.at(-1); return last.tool_calls?.length > 0 ? 'tools' : END; }) .addEdge('tools', 'agent') .compile({ checkpointer: new MemorySaver() }); // 실행 (streaming) const stream = await graph.stream( { messages: [{ role: 'user', content: '...' }] }, { configurable: { thread_id: 'session-1' } } ); for await (const chunk of stream) { console.log(chunk); } ``` ### Mastra (TS modern) ```ts import { Mastra } from '@mastra/core'; const mastra = new Mastra({ agents: { weatherAgent: new Agent({ name: 'Weather', instructions: 'Help users with weather questions.', model: openai('gpt-4o'), tools: { getWeather: weatherTool }, }), }, workflows: { customerSupport: workflow, }, }); const result = await mastra.agents.weatherAgent.generate('Tokyo weather?'); ``` → TS-first, modern, evals + observability built-in. ### CrewAI (multi-agent, Python) ```python from crewai import Agent, Task, Crew researcher = Agent( role='Senior Researcher', goal='Discover latest AI trends', backstory='You are an expert AI researcher...', tools=[search_tool], ) writer = Agent( role='Tech Writer', goal='Write engaging articles', ) task1 = Task(description='Research 2026 AI trends', agent=researcher) task2 = Task(description='Write article based on research', agent=writer) crew = Crew(agents=[researcher, writer], tasks=[task1, task2]) result = crew.kickoff() ``` → Role-based multi-agent. 빠른 시작 but 정밀 제어 어려움. ### Vercel AI SDK (modern, simple) ```ts import { generateText, tool } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const result = await generateText({ model: openai('gpt-4o'), tools: { getWeather: tool({ description: 'Get weather', parameters: z.object({ city: z.string() }), execute: async ({ city }) => fetchWeather(city), }), }, maxSteps: 5, // tool loop prompt: 'Tokyo weather?', }); console.log(result.text, result.toolCalls); ``` → 단순 use case 강력. ### State 영속 (LangGraph) ```ts import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres'; const checkpointer = PostgresSaver.fromConnString('postgresql://...'); const graph = ...compile({ checkpointer }); // 같은 thread_id = 이어서 await graph.invoke(input, { configurable: { thread_id: userId } }); ``` ### Human-in-the-loop ```ts const graph = new StateGraph(State) .addNode('plan', planNode) .addNode('confirm', confirmNode) // 사용자 승인 대기 .addNode('execute', executeNode) .addEdge('plan', 'confirm') .addConditionalEdges('confirm', (state) => state.approved ? 'execute' : END ) .compile({ interrupt_before: ['execute'], // 항상 멈춤 }); // 1. Plan 까지 실행 const state = await graph.invoke(input, config); // 2. UI 가 사용자 confirm if (await askUser(state.plan)) { // 3. 이어서 실행 await graph.invoke(null, { ...config, recursionLimit: 10 }); } ``` ### Streaming + tools ```ts const stream = await graph.streamEvents(input, { ...config, version: 'v2', }); for await (const event of stream) { if (event.event === 'on_chat_model_stream') { process.stdout.write(event.data.chunk.content); } if (event.event === 'on_tool_start') { console.log('\nTool:', event.name); } } ``` ### 자체 implementation (가벼운) ```ts class Agent { private messages: Message[] = []; private maxIters = 10; constructor( private llm: LLM, private tools: Tool[], private systemPrompt: string, ) {} async run(userMsg: string): Promise { this.messages.push({ role: 'user', content: userMsg }); for (let i = 0; i < this.maxIters; i++) { const r = await this.llm.chat({ system: this.systemPrompt, messages: this.messages, tools: this.tools, }); this.messages.push({ role: 'assistant', content: r.content }); if (r.stopReason === 'end_turn') return r.text; if (r.toolCalls) { const results = await Promise.all( r.toolCalls.map(call => this.executeTool(call)) ); this.messages.push(...results.map(toToolResult)); } } throw new Error('max iters'); } } ``` → 위 [[AI_Function_Calling_Deep]] 가 baseline. ### Observability ```ts // LangSmith / Langfuse / Helicone import { LangSmithTracer } from 'langsmith'; const tracer = new LangSmithTracer({ projectName: 'my-agent', }); await graph.invoke(input, { callbacks: [tracer], }); ``` → 매 LLM call + tool call 추적. ### Memory systems ```ts // Short-term: conversation messages // Long-term: vector DB import { MemoryClient } from '@mem0/sdk'; const memory = new MemoryClient(); // Save await memory.add(userId, 'User prefers dark mode and minimal UI'); // Retrieve relevant const memories = await memory.search(userId, currentQuery); // Inject to system prompt const system = `${baseSystem}\n\nRelevant context:\n${memories.join('\n')}`; ``` ### Eval (위 LLM Eval 문서) ```ts import { evalDataset, exactMatch, llmJudge } from 'mastra/evals'; await evalDataset({ agent: weatherAgent, cases: [ { input: 'Tokyo weather?', expected: { contains: 'Tokyo' } }, ], metrics: [exactMatch, llmJudge('helpful')], }); ``` ### 비교 ``` LangGraph: + 가장 강력 / production-ready + State machine 명시 - Python 우월 (TS 제한) Mastra: + Modern TS-first + Eval / observability built-in - 새로움 (검증 적음) CrewAI: + Multi-agent simple - 정밀 제어 어려움 Vercel AI SDK: + 단순 / TS 친화 - Multi-step 제한적 자체: + 정확 제어 - 모든 거 직접 ``` ## 🤔 의사결정 기준 | 상황 | 추천 | |---|---| | 단순 1-2 step | Vercel AI SDK | | Multi-step / state | LangGraph (Python) / Mastra (TS) | | Multi-agent | CrewAI / AutoGen | | Production / 큰 규모 | LangGraph + LangSmith | | Quick prototype | Vercel AI SDK | | 완전 제어 | 자체 | ## ❌ 안티패턴 - **Framework 선택 전 use case 명시 X**: overkill / 부족. - **State persistence 없음 + long task**: crash 시 잃음. - **Max iter 없음**: 무한 / 비용 폭발. - **HITL 없음 + dangerous tool**: 실수 책임. - **Multi-agent 가 single agent 대체**: 단일 가 충분 자주. - **Memory 모든 거 inject**: context 폭발. retrieval. - **Eval 없음**: 향상 측정 X. ## 🤖 LLM 활용 힌트 - 단순 = Vercel AI SDK. - Production state = LangGraph / Mastra. - Multi-agent overkill 자주. - HITL + state persistence 필수. ## 🔗 관련 문서 - [[AI_Agentic_Patterns]] - [[AI_Function_Calling_Deep]] - [[AI_LLM_Eval_Patterns]]