초안 에이전트 설계 패턴 - 멀티 에이전트 협업

ailangchainlanggraphmulti-agentgeminitypescript
By sko X opus 4.19/20/20258 min read

멀티 에이전트 협업은 공통 목표를 향해 함께 작업하는 여러 전문 AI 에이전트를 오케스트레이션하여 복잡한 문제 해결을 가능하게 합니다. 단일 모놀리식 에이전트에 의존하는 대신, 이 패턴은 특정 기능과 도구를 가진 전문 에이전트들에게 작업을 분산하여 더 강력하고 확장 가능한 솔루션을 만들어냅니다.

멘탈 모델: 오케스트라 지휘자 아키텍처

멀티 에이전트 협업을 Next.js API 라우트에서 오케스트라를 지휘하는 것으로 생각해보세요. 각 에이전트는 자신만의 악기(도구/기능)를 가진 전문 연주자(바이올린 = 연구원, 드럼 = 데이터 분석가, 피아노 = 작가)입니다. 지휘자(슈퍼바이저 에이전트 또는 오케스트레이터)는 이러한 전문가들을 조율하여 교향곡(완전한 솔루션)을 만들어냅니다. Vercel의 서버리스 환경에서 이는 Redis나 Upstash의 공유 상태를 통해 협업하는 가벼운 전문 함수들을 실행하는 것을 의미하며, 메시지 큐를 통해 마이크로서비스가 통신하는 방식과 비슷하지만 각 노드에서 AI 기반 의사결정이 이루어집니다.

기본 예제: 슈퍼바이저-워커 패턴

연구 작업을 처리하기 위해 슈퍼바이저가 전문 워커들을 조정하는 간단한 멀티 에이전트 시스템을 구축해봅시다.

1. 에이전트 상태 및 타입 정의

// app/lib/agents/types.ts
import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";
import { z } from "zod";

// 에이전트 간 흐르는 상태 정의
export const AgentState = Annotation.Root({
  messages: Annotation<BaseMessage[]>({
    reducer: (x, y) => x.concat(y),
    default: () => []
  }),
  task: Annotation<string>(),
  researchData: Annotation<string>(),
  analysis: Annotation<string>(),
  finalOutput: Annotation<string>(),
  nextAgent: Annotation<string>()
});

export type AgentStateType = typeof AgentState.State;

// 에이전트 구성 스키마
export const AgentConfigSchema = z.object({
  name: z.string(),
  role: z.string(),
  temperature: z.number().default(0.7),
  maxTokens: z.number().default(1000)
});

적절한 TypeScript 타입과 함께 LangGraph의 Annotation 시스템을 사용하여 공유 상태 구조를 정의합니다.

2. 전문 워커 에이전트 생성

// app/lib/agents/workers.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
import { isEmpty, pick } from "es-toolkit";
import { AgentStateType } from "./types";

// 웹 검색 기능을 가진 연구 에이전트
export const researchAgent = async (state: AgentStateType) => {
  const model = new ChatGoogleGenerativeAI({
    model: "gemini-2.5-flash",
    temperature: 0.5,
    maxRetries: 2
  });

  const searchTool = new DynamicStructuredTool({
    name: "web_search",
    description: "웹에서 정보 검색",
    schema: z.object({
      query: z.string().describe("검색 쿼리")
    }),
    func: async ({ query }) => {
      // 모의 검색 - 실제 API 호출로 교체
      await new Promise(resolve => setTimeout(resolve, 100));
      return `다음 정보를 찾았습니다: ${query}`;
    }
  });

  const prompt = `당신은 연구 전문가입니다.
    작업: ${state.task}
    검색 도구를 사용하여 관련 정보를 수집하세요.`;

  const response = await model.invoke(prompt);

  return {
    messages: [response],
    researchData: response.content as string
  };
};

// 데이터 처리를 위한 분석 에이전트
export const analysisAgent = async (state: AgentStateType) => {
  const model = new ChatGoogleGenerativeAI({
    model: "gemini-2.5-flash",
    temperature: 0.3
  });

  const prompt = `당신은 데이터 분석가입니다.
    작업: ${state.task}
    연구 데이터: ${state.researchData}

    이 데이터를 분석하고 인사이트를 제공하세요.`;

  const response = await model.invoke(prompt);

  return {
    messages: [response],
    analysis: response.content as string
  };
};

// 최종 출력을 위한 작성자 에이전트
export const writerAgent = async (state: AgentStateType) => {
  const model = new ChatGoogleGenerativeAI({
    model: "gemini-2.5-pro",
    temperature: 0.7
  });

  const prompt = `당신은 전문 작가입니다.
    작업: ${state.task}
    연구: ${state.researchData}
    분석: ${state.analysis}

    포괄적이고 잘 구조화된 응답을 작성하세요.`;

  const response = await model.invoke(prompt);

  return {
    messages: [response],
    finalOutput: response.content as string
  };
};

각 워커 에이전트는 적절한 모델 설정과 도구를 갖춘 특정 작업에 전문화되어 있습니다.

3. 슈퍼바이저 구현

// app/lib/agents/supervisor.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { JsonOutputParser } from "@langchain/core/output_parsers";
import { AgentStateType } from "./types";
import { END } from "@langchain/langgraph";

export const supervisorAgent = async (state: AgentStateType) => {
  const model = new ChatGoogleGenerativeAI({
    model: "gemini-2.5-flash",
    temperature: 0
  });

  // 현재 상태를 기반으로 다음 에이전트 결정
  const systemPrompt = `당신은 에이전트 팀을 관리하는 슈퍼바이저입니다.
    현재 작업: ${state.task}

    사용 가능한 에이전트:
    - researcher: 정보 수집
    - analyst: 데이터 분석
    - writer: 최종 출력 생성

    완료된 내용 기반:
    - 연구 데이터 존재: ${!!state.researchData}
    - 분석 존재: ${!!state.analysis}
    - 최종 출력 존재: ${!!state.finalOutput}

    JSON으로 반환: { "nextAgent": "agent_name" 또는 "finish" }`;

  const parser = new JsonOutputParser();
  const response = await model.invoke(systemPrompt);
  const decision = await parser.parse(response.content as string);

  return {
    nextAgent: decision.nextAgent === "finish" ? END : decision.nextAgent,
    messages: [response]
  };
};

슈퍼바이저는 현재 상태에 따라 다음에 활성화할 에이전트를 결정합니다.

4. 멀티 에이전트 그래프 구축

// app/lib/agents/workflow.ts
import { StateGraph } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph";
import { START, END } from "@langchain/langgraph";
import { AgentState } from "./types";
import { supervisorAgent } from "./supervisor";
import { researchAgent, analysisAgent, writerAgent } from "./workers";

export function createMultiAgentWorkflow() {
  const workflow = new StateGraph(AgentState)
    // 각 에이전트의 노드 추가
    .addNode("supervisor", supervisorAgent)
    .addNode("researcher", researchAgent)
    .addNode("analyst", analysisAgent)
    .addNode("writer", writerAgent)

    // 플로우 정의
    .addEdge(START, "supervisor")
    .addConditionalEdges(
      "supervisor",
      // 슈퍼바이저 결정에 기반한 라우팅 함수
      (state) => state.nextAgent || END,
      {
        researcher: "researcher",
        analyst: "analyst",
        writer: "writer",
        [END]: END
      }
    )

    // 워커는 슈퍼바이저로 돌아감
    .addEdge("researcher", "supervisor")
    .addEdge("analyst", "supervisor")
    .addEdge("writer", "supervisor");

  // 대화 영속성을 위한 메모리와 함께 컴파일
  const memory = new MemorySaver();
  return workflow.compile({
    checkpointer: memory,
    recursionLimit: 10 // 무한 루프 방지
  });
}

LangGraph 워크플로우는 적절한 상태 관리로 에이전트 상호작용을 오케스트레이션합니다.

5. API 라우트 생성

// app/api/multi-agent/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createMultiAgentWorkflow } from "@/lib/agents/workflow";
import { HumanMessage } from "@langchain/core/messages";
import { pick } from "es-toolkit";

export const maxDuration = 300; // 장시간 실행 작업을 위한 5분

export async function POST(req: NextRequest) {
  try {
    const { task, threadId } = await req.json();

    // 워크플로우 초기화
    const workflow = createMultiAgentWorkflow();

    // 멀티 에이전트 협업 실행
    const result = await workflow.invoke(
      {
        task,
        messages: [new HumanMessage(task)]
      },
      {
        configurable: { thread_id: threadId || "default" },
        recursionLimit: 10
      }
    );

    // 관련 필드만 반환
    const response = pick(result, ["finalOutput", "analysis", "researchData"]);

    return NextResponse.json({
      success: true,
      result: response
    });
  } catch (error) {
    console.error("멀티 에이전트 오류:", error);
    return NextResponse.json(
      { error: "처리 실패" },
      { status: 500 }
    );
  }
}

API 라우트는 Vercel의 확장 타임아웃 지원으로 멀티 에이전트 오케스트레이션을 처리합니다.

6. React Query를 사용한 프론트엔드 통합

// app/components/MultiAgentInterface.tsx
"use client";

import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { debounce } from "es-toolkit";

interface MultiAgentResult {
  finalOutput: string;
  analysis: string;
  researchData: string;
}

async function runMultiAgentTask(task: string): Promise<MultiAgentResult> {
  const response = await fetch("/api/multi-agent", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ task, threadId: Date.now().toString() })
  });

  if (!response.ok) throw new Error("에이전트 실행 실패");
  const data = await response.json();
  return data.result;
}

export default function MultiAgentInterface() {
  const [task, setTask] = useState("");

  const mutation = useMutation({
    mutationFn: runMultiAgentTask,
    onSuccess: (data) => {
      console.log("작업 완료:", data);
    }
  });

  const handleSubmit = debounce((e: React.FormEvent) => {
    e.preventDefault();
    if (task.trim()) {
      mutation.mutate(task);
    }
  }, 500);

  return (
    <div className="card bg-base-100 shadow-xl">
      <div className="card-body">
        <h2 className="card-title">멀티 에이전트 작업 프로세서</h2>

        <form onSubmit={handleSubmit}>
          <textarea
            className="textarea textarea-bordered w-full"
            placeholder="작업을 설명하세요..."
            value={task}
            onChange={(e) => setTask(e.target.value)}
            rows={4}
          />

          <button
            type="submit"
            className="btn btn-primary mt-4"
            disabled={mutation.isPending}
          >
            {mutation.isPending ? (
              <span className="loading loading-spinner" />
            ) : (
              "작업 처리"
            )}
          </button>
        </form>

        {mutation.data && (
          <div className="mt-6 space-y-4">
            <div className="collapse collapse-arrow bg-base-200">
              <input type="checkbox" defaultChecked />
              <div className="collapse-title font-medium">
                최종 출력
              </div>
              <div className="collapse-content">
                <p>{mutation.data.finalOutput}</p>
              </div>
            </div>

            <div className="collapse collapse-arrow bg-base-200">
              <input type="checkbox" />
              <div className="collapse-title font-medium">
                분석
              </div>
              <div className="collapse-content">
                <p>{mutation.data.analysis}</p>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

React 컴포넌트는 적절한 로딩 상태와 결과 표시로 멀티 에이전트 상호작용을 관리합니다.

고급 예제: 토론을 통한 병렬 협업

이제 에이전트들이 병렬로 작업하고 합의에 도달하기 위해 토론하는 정교한 멀티 에이전트 시스템을 구현해봅시다.

1. 투표를 포함한 고급 상태 정의

// app/lib/agents/advanced-types.ts
import { Annotation } from "@langchain/langgraph";
import { z } from "zod";

export const DebateOpinion = z.object({
  agent: z.string(),
  opinion: z.string(),
  confidence: z.number().min(0).max(1),
  reasoning: z.string()
});

export const DebateState = Annotation.Root({
  question: Annotation<string>(),
  round: Annotation<number>({ default: () => 0 }),
  opinions: Annotation<z.infer<typeof DebateOpinion>[]>({
    reducer: (x, y) => [...x, ...y],
    default: () => []
  }),
  consensus: Annotation<string>(),
  hasConverged: Annotation<boolean>({ default: () => false })
});

export type DebateStateType = typeof DebateState.State;

고급 상태는 의견, 신뢰도 점수 및 합의 상태를 추적합니다.

2. 병렬 전문가 에이전트 구현

// app/lib/agents/expert-agents.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { DebateStateType, DebateOpinion } from "./advanced-types";
import { chunk, groupBy, maxBy } from "es-toolkit";

// 기본 전문가 에이전트 팩토리
function createExpertAgent(expertise: string, model: string = "gemini-2.5-flash") {
  return async (state: DebateStateType): Promise<Partial<DebateStateType>> => {
    const llm = new ChatGoogleGenerativeAI({
      model,
      temperature: 0.7,
      maxRetries: 2
    });

    // 이전 라운드에서 다른 에이전트의 의견 가져오기
    const otherOpinions = state.opinions
      .filter(op => op.agent !== expertise)
      .slice(-3); // 다른 사람의 마지막 3개 의견

    const prompt = `당신은 ${expertise}의 전문가입니다.
      질문: ${state.question}
      라운드: ${state.round}

      ${otherOpinions.length > 0 ? `
      다른 전문가의 의견:
      ${otherOpinions.map(op =>
        `${op.agent}: ${op.opinion} (신뢰도: ${op.confidence})`
      ).join('\n')}
      ` : ''}

      이유와 신뢰도(0-1)와 함께 의견을 제공하세요.
      형식: { "opinion": "...", "reasoning": "...", "confidence": 0.8 }`;

    const response = await llm.invoke(prompt);
    const parsed = JSON.parse(response.content as string);

    return {
      opinions: [{
        agent: expertise,
        opinion: parsed.opinion,
        reasoning: parsed.reasoning,
        confidence: parsed.confidence
      }]
    };
  };
}

// 전문 전문가 생성
export const technicalExpert = createExpertAgent("기술_분석");
export const businessExpert = createExpertAgent("비즈니스_전략");
export const userExpert = createExpertAgent("사용자_경험");
export const securityExpert = createExpertAgent("보안", "gemini-2.5-pro");

전문가 에이전트는 신뢰도 점수와 함께 전문적인 관점을 제공합니다.

3. 병렬 실행 코디네이터

// app/lib/agents/parallel-coordinator.ts
import { DebateStateType } from "./advanced-types";
import {
  technicalExpert,
  businessExpert,
  userExpert,
  securityExpert
} from "./expert-agents";
import { chunk, meanBy, partition } from "es-toolkit";

export async function parallelExpertAnalysis(
  state: DebateStateType
): Promise<Partial<DebateStateType>> {
  const experts = [
    technicalExpert,
    businessExpert,
    userExpert,
    securityExpert
  ];

  // 타임아웃과 함께 모든 전문가를 병렬 실행
  const expertPromises = experts.map(expert =>
    Promise.race([
      expert(state),
      new Promise<Partial<DebateStateType>>((_, reject) =>
        setTimeout(() => reject(new Error("타임아웃")), 10000)
      )
    ])
  );

  const results = await Promise.allSettled(expertPromises);

  // 성공한 의견 수집
  const newOpinions = results
    .filter(r => r.status === "fulfilled")
    .flatMap(r => (r as PromiseFulfilledResult<any>).value.opinions || []);

  // 수렴 확인
  const hasConverged = checkConvergence(newOpinions);

  return {
    opinions: newOpinions,
    round: state.round + 1,
    hasConverged
  };
}

function checkConvergence(opinions: any[]): boolean {
  if (opinions.length < 3) return false;

  // 유사성으로 의견 그룹화 (단순화)
  const groups = groupBy(opinions, op =>
    op.opinion.toLowerCase().includes("동의") ? "동의" : "비동의"
  );

  // 다수가 동의하는지 확인
  const largestGroup = Object.values(groups)
    .reduce((a, b) => a.length > b.length ? a : b, []);

  // 75% 이상 동의하고 평균 신뢰도가 0.7 이상이면 수렴
  const convergenceRatio = largestGroup.length / opinions.length;
  const avgConfidence = meanBy(largestGroup, op => op.confidence);

  return convergenceRatio > 0.75 && avgConfidence > 0.7;
}

코디네이터는 타임아웃 처리와 수렴 감지를 통해 병렬 실행을 관리합니다.

4. 투표를 통한 합의 구축

// app/lib/agents/consensus.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { DebateStateType } from "./advanced-types";
import { groupBy, maxBy, sortBy } from "es-toolkit";

export async function buildConsensus(
  state: DebateStateType
): Promise<Partial<DebateStateType>> {
  const model = new ChatGoogleGenerativeAI({
    model: "gemini-2.5-pro",
    temperature: 0.3
  });

  // 각 에이전트의 최근 의견 가져오기
  const recentOpinions = Object.values(
    groupBy(state.opinions, op => op.agent)
  ).map(group => maxBy(group, op => op.confidence));

  // 신뢰도로 정렬
  const sortedOpinions = sortBy(
    recentOpinions,
    op => -(op?.confidence || 0)
  );

  const prompt = `이 전문가 의견들을 합의로 종합하세요:
    질문: ${state.question}

    전문가 의견:
    ${sortedOpinions.map(op => `
      ${op?.agent} (신뢰도: ${op?.confidence}):
      의견: ${op?.opinion}
      이유: ${op?.reasoning}
    `).join('\n')}

    모든 전문가의 핵심 요점을 다루는 균형 잡힌 합의를 만드세요.
    신뢰도 점수로 의견에 가중치를 부여하세요.`;

  const response = await model.invoke(prompt);

  return {
    consensus: response.content as string,
    hasConverged: true
  };
}

// 교착 상태 해결을 위한 투표 메커니즘
export function majorityVote(opinions: any[]): string {
  const voteGroups = groupBy(opinions, op => op.opinion);
  const weightedVotes = Object.entries(voteGroups).map(([opinion, votes]) => ({
    opinion,
    totalWeight: votes.reduce((sum, v) => sum + v.confidence, 0)
  }));

  const winner = maxBy(weightedVotes, v => v.totalWeight);
  return winner?.opinion || "합의에 도달하지 못했습니다";
}

합의 구축은 신뢰도 가중치로 다양한 의견을 종합합니다.

5. 완전한 토론 워크플로우

// app/lib/agents/debate-workflow.ts
import { StateGraph, START, END } from "@langchain/langgraph";
import { DebateState } from "./advanced-types";
import { parallelExpertAnalysis } from "./parallel-coordinator";
import { buildConsensus } from "./consensus";

export function createDebateWorkflow() {
  const MAX_ROUNDS = 5;

  const workflow = new StateGraph(DebateState)
    .addNode("parallel_experts", parallelExpertAnalysis)
    .addNode("consensus", buildConsensus)

    // 병렬 전문가 분석으로 시작
    .addEdge(START, "parallel_experts")

    // 수렴에 기반한 조건부 라우팅
    .addConditionalEdges(
      "parallel_experts",
      (state) => {
        if (state.hasConverged || state.round >= MAX_ROUNDS) {
          return "consensus";
        }
        return "parallel_experts"; // 토론 계속
      },
      {
        parallel_experts: "parallel_experts",
        consensus: "consensus"
      }
    )

    .addEdge("consensus", END);

  return workflow.compile({
    recursionLimit: MAX_ROUNDS * 2
  });
}

워크플로우는 자동 수렴 감지와 함께 반복적인 토론을 구현합니다.

6. 실시간 업데이트를 위한 스트리밍 API

// app/api/debate/route.ts
import { NextRequest } from "next/server";
import { createDebateWorkflow } from "@/lib/agents/debate-workflow";

export const maxDuration = 300;

export async function POST(req: NextRequest) {
  const { question } = await req.json();

  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    async start(controller) {
      const workflow = createDebateWorkflow();

      try {
        // 이벤트가 발생할 때 스트리밍
        for await (const event of workflow.stream(
          { question, round: 0 },
          { streamMode: "values" }
        )) {
          const data = JSON.stringify({
            type: "update",
            round: event.round,
            opinions: event.opinions?.slice(-4), // 마지막 4개 의견
            hasConverged: event.hasConverged
          });

          controller.enqueue(
            encoder.encode(`data: ${data}\n\n`)
          );
        }

        // 최종 합의 전송
        const final = await workflow.invoke({ question, round: 0 });
        controller.enqueue(
          encoder.encode(`data: ${JSON.stringify({
            type: "complete",
            consensus: final.consensus
          })}\n\n`)
        );
      } catch (error) {
        controller.enqueue(
          encoder.encode(`data: ${JSON.stringify({
            type: "error",
            message: "토론 실패"
          })}\n\n`)
        );
      } finally {
        controller.close();
      }
    }
  });

  return new Response(stream, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      "Connection": "keep-alive"
    }
  });
}

스트리밍 API는 토론 과정 중 실시간 업데이트를 제공합니다.

7. 실시간 토론 시각화

// app/components/DebateVisualization.tsx
"use client";

import { useEffect, useState } from "react";
import { groupBy, sortBy } from "es-toolkit";

interface Opinion {
  agent: string;
  opinion: string;
  confidence: number;
  reasoning: string;
}

export default function DebateVisualization() {
  const [question, setQuestion] = useState("");
  const [isDebating, setIsDebating] = useState(false);
  const [rounds, setRounds] = useState<Opinion[][]>([]);
  const [consensus, setConsensus] = useState<string>("");

  const startDebate = async () => {
    setIsDebating(true);
    setRounds([]);
    setConsensus("");

    const response = await fetch("/api/debate", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ question })
    });

    const reader = response.body?.getReader();
    const decoder = new TextDecoder();

    while (reader) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = decoder.decode(value);
      const lines = chunk.split('\n');

      for (const line of lines) {
        if (line.startsWith('data: ')) {
          const data = JSON.parse(line.slice(6));

          if (data.type === "update" && data.opinions) {
            setRounds(prev => [...prev, data.opinions]);
          } else if (data.type === "complete") {
            setConsensus(data.consensus);
            setIsDebating(false);
          }
        }
      }
    }
  };

  return (
    <div className="container mx-auto p-4">
      <div className="card bg-base-100 shadow-xl mb-6">
        <div className="card-body">
          <h2 className="card-title">멀티 에이전트 토론 시스템</h2>

          <input
            type="text"
            placeholder="토론할 질문을 입력하세요..."
            className="input input-bordered w-full"
            value={question}
            onChange={(e) => setQuestion(e.target.value)}
          />

          <button
            className="btn btn-primary"
            onClick={startDebate}
            disabled={isDebating || !question}
          >
            {isDebating ? (
              <>
                <span className="loading loading-spinner" />
                토론...
              </>
            ) : (
              "토론 시작"
            )}
          </button>
        </div>
      </div>

      {/* 토론 라운드 시각화 */}
      {rounds.length > 0 && (
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
          {rounds[rounds.length - 1].map((opinion, idx) => (
            <div key={idx} className="card bg-base-200">
              <div className="card-body">
                <div className="flex justify-between items-center">
                  <h3 className="font-bold">{opinion.agent}</h3>
                  <div className="badge badge-primary">
                    {Math.round(opinion.confidence * 100)}% 확신
                  </div>
                </div>
                <p className="text-sm mt-2">{opinion.opinion}</p>
                <progress
                  className="progress progress-primary w-full"
                  value={opinion.confidence}
                  max="1"
                />
              </div>
            </div>
          ))}
        </div>
      )}

      {/* 합의 결과 */}
      {consensus && (
        <div className="alert alert-success">
          <div>
            <h3 className="font-bold">합의 도달:</h3>
            <p>{consensus}</p>
          </div>
        </div>
      )}
    </div>
  );
}

실시간 시각화는 신뢰도 표시기와 함께 토론 진행 상황을 보여줍니다.

결론

멀티 에이전트 협업은 전문 에이전트들 간에 작업을 분산하여 복잡한 문제 해결을 변혁합니다. 슈퍼바이저-워커 패턴은 고객 서비스 및 연구 작업에 이상적인 중앙 집중식 제어를 제공하며, 병렬 토론 시스템은 중요한 의사결정을 위한 다양한 관점 수집에 탁월합니다. 주요 고려사항에는 조정 오버헤드 관리(팀을 7개 에이전트 이하로 유지), 서버리스 환경을 위한 적절한 타임아웃 처리 구현, 합의 구축을 위한 신뢰도 가중 투표 사용이 포함됩니다. LangGraph의 상태형 오케스트레이션과 Vercel의 서버리스 인프라를 사용하면 정교한 조정 패턴을 통해 품질을 유지하면서 2-10배의 생산성 향상을 달성하는 프로덕션 준비가 된 멀티 에이전트 시스템을 구축할 수 있습니다.