2026년 AI Repo GitHub 톱: Agent 예제와 함께 상세 설명

이 글에는 짧은 영상이 있습니다:YouTubeTikTok

1. 개요

2026년 GitHub의 트렌딩 순위는 단 하나의 키워드로 지배당합니다: agent. 더 이상은 ’한 문장 답하고 끝’인 챗봇이 아니라, 이들 저장소는 자동으로 계획을 세우고, 도구를 호출하고, 추론 사이클을 반복하며, 다른 에이전트와 협력하여 복잡한 작업을 완료할 수 있는 AI 시스템을 구축합니다. 이 글에서는 가장 인기 있는 저장소들 뒤의 아키텍처 패턴을 설명하고, 배울 만한 도구 그룹을 살펴보며, 더 중요하게는 Java/Spring Boot로 Tool-Calling Agent를 직접 작성해서 내부 메커니즘을 이해하도록 합니다. 단순히 ’pip install’하고 어떻게 작동하는지 모르고 사용하는 것 대신에요.

2. AI repo 2026에서 “agent”가 가장 인기 있는 키워드인 이유

이전까지 가장 흔한 AI 애플리케이션은 RAG(Retrieval-Augmented Generation)였습니다: 질문을 받고, 관련 문서를 찾고, 프롬프트에 집어넣고, LLM을 한 번 호출하고, 답변을 제공합니다. 이 모델은 단순하지만 명확한 한계가 있습니다: LLM은 ’말하기’만 할 수 있고 ’하기’는 못 합니다. 스스로 API를 호출해서 일정을 예약할 수 없고, 스스로 계좌 잔액을 확인하는 스크립트를 실행할 수 없으며, 어떤 추가 정보가 필요한지 스스로 결정할 수 없습니다.

Agent가 바로 이 부분을 해결합니다: LLM을 한 번 호출하는 대신, 시스템은 루프를 실행합니다 — LLM이 현재 상태를 관찰하고, 행동(도구 호출, 추가 질문, 또는 최종 답변)을 결정하고, 행동을 실행하고, 결과를 관찰하고, 반복합니다. 이것이 바로 multi-agent orchestration framework, 모델 제공업체의 공식 agent SDK, RAG 워크플로우 라이브러리 등이 모두 동일한 아키텍처로 수렴하는 이유입니다: 관찰 – 결정 – 행동 루프.

3. 모든 agent framework의 기반이 되는 아키텍처 패턴

이름은 다르지만, 2026년의 대부분 agent 저장소는 4가지 패턴 위에 구축되어 있습니다. 이 4가지 패턴을 이해하면, 어떤 프레임워크의 소스 코드를 읽어도 친숙하게 느껴질 것입니다.

3.1. ReAct 루프 (Reason + Act)

개념: LLM이 즉시 답변하지 않고, ’사고 → 행동 → 관찰’의 시퀀스를 생성하여 여러 번 반복하다가 최종 답변에 충분한 정보가 모이면 멈춥니다.

Thought: ORD-001 주문 상태를 알아야 답변할 수 있습니다.
Action: get_order_status(orderId="ORD-001")
Observation: 배송 중
Thought: 정보가 충분합니다, 답변할 수 있습니다.
Final Answer: 당신의 주문은 배송 중입니다.

3.2. Tool calling / Function calling

LLM은 스스로 코드를 실행하지 않습니다 — 함수 호출 의도(함수 이름 + JSON 형식의 매개변수)만 생성합니다. 런타임(agent framework)이 실제 함수를 실행하고 결과를 LLM에 전달합니다. 이것은 중요한 보안 경계입니다: LLM은 ’제안’하고, 당신의 코드가 ’실행할지 말지 결정’합니다.

3.3. Multi-agent orchestration

단일 에이전트는 ’역할 과부하’에 걸리기 쉽습니다(계획을 세우면서 코드를 작성하고 검토해야 함). 많은 저장소는 작업을 여러 전문화된 에이전트(planner, executor, reviewer)에 분산시킨 다음, ’오케스트레이터’를 사용하여 그들 사이의 데이터 흐름을 조정합니다 — microservice 모델과 비슷하지만 ‘AI 직원’ 사이에서입니다.

3.4. Memory & state

에이전트는 대화 이력, 호출한 도구의 결과, 그리고 진행 상태(완료한 단계, 남은 단계)를 기억해야 합니다. 이 부분은 보통 선형 메시지 시퀀스보다는 상태 그래프(state machine)로 모델링되어서 에이전트가 되돌아가거나, 분기하거나, 인간 확인을 기다리며 중간에 멈출 수 있게 합니다(human-in-the-loop).

4. 2026년 배워야 할 가치 있는 AI agent 저장소 그룹들

이 섹션은 단순히 저장소 이름을 나열하는 대신 아키텍처 역할에 따라 그룹화합니다, 왜냐하면 그것이 올바른 도구를 올바른 작업에 선택하기 위해 이해해야 할 것이기 때문입니다.

4.1. Orchestration framework 그룹

이러한 유형의 프레임워크는 ’골격’을 제공하여 단계(노드)의 그래프와 그 사이의 조건 가장자리(edge)를 정의할 수 있습니다 — 예를 들어 에이전트 A가 답변한 후, 추가 정보가 필요하면 에이전트 B로 전환하고, 충분하면 끝냅니다. 복잡한 흐름 제어, 오류 발생 시 재개할 수 있는 체크포인트 지원, 그리고 그래프를 시각화하여 디버깅하는 데 강합니다.

참고: 이 그룹은 단순한 LLM API 호출보다 훨씬 가파른 학습곡선(learning curve)을 가집니다. 사용 사례가 단순히 ’한 문장 물어보고 한 문장 답변’이라면, 전체 그래프 엔진을 가져오지 마세요 — 운영 및 디버깅 비용이 그럴 가치가 없습니다.

4.2. 공식 agent SDK 그룹

대형 모델 제공자(OpenAI, Anthropic)는 자체 agent SDK를 출시하며, tool calling 메커니즘, 세션 관리, 그리고 에이전트 무한 실행을 방지하기 위한 반복 단계 제한이 기본 제공됩니다. 장점은 자신들의 모델과 최적의 호환성, 최소한의 보일러플레이트입니다. 단점은 그 SDK에 lock-in되면 다른 모델로 전환하기 어렵다는 것입니다.

4.3. RAG + Agent workflow 그룹

이들은 전통적인 RAG에서 출발한 라이브러리로, 에이전트 기능을 확장합니다: retrieve-후-생성을 한 번만 하는 것이 아니라, 에이전트가 다시 검색해야 하는지, 어디서 검색할지 스스로 결정하게 합니다. 복잡도가 높은 내부 문서(knowledge base) 기반의 질문-답변 시스템에 가장 적합합니다.

세 그룹의 공통점: 모두 3절의 동일한 4가지 패턴을 노출합니다. 따라서 깊이 있게 이해하려면, 최선의 방법은 최소한의 에이전트 루프를 직접 작성하는 것입니다 — 다음 섹션에서 정확히 그것을 할 것입니다.

5. 실습: Java/Spring Boot로 Tool-Calling Agent 직접 구축하기

목표: 주문 상태 조회 질문에 답할 수 있는 작은 에이전트를 구축합니다. 에이전트는 질문에서 주문 코드를 자동으로 식별하고, 조회 ’도구’를 호출한 다음, 답변을 종합합니다 — 정확히 3.1절에서 설명한 ReAct 루프입니다.

5.1. 의존성

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

참고: 이 예제에서는 실제 LLM을 호출하지 않아서 API 키 없이 바로 실행할 수 있습니다. 5.2절에서 LlmClient 인터페이스는 실제 프로덕션 환경에서 Spring AI(spring-ai-openai-spring-boot-starter) 또는 Anthropic SDK를 연결하는 지점입니다.

5.2. Tool 선언

Tool은 에이전트가 호출할 수 있는 가장 작은 액션 단위입니다. LLM이 선택할 수 있도록 이름이, LLM이 목적을 이해할 수 있도록 설명이, 그리고 실행할 함수가 필요합니다.

public interface AgentTool {
    String name();
    String description();
    String execute(Map<String, String> args);
}
public class OrderStatusTool implements AgentTool {

    private final Map<String, String> orderStatuses = Map.of(
            "ORD-001", "배송 중",
            "ORD-002", "배송 완료"
    );

    @Override
    public String name() {
        return "get_order_status";
    }

    @Override
    public String description() {
        return "주문 ID에 따른 주문 상태를 조회합니다. 예: ORD-001";
    }

    @Override
    public String execute(Map<String, String> args) {
        String orderId = args.get("orderId");
        return orderStatuses.getOrDefault(orderId, "찾을 수 없음");
    }
}

execute는 key-value 형태의 매개변수(LLM이 생성한 JSON을 모방)를 받고, 텍스트 형태의 observation을 반환합니다 — 위의 ReAct 예제에서의 Action/Observation과 정확히 같습니다.

5.3. 다음 단계를 결정하는 ‘Brain’

프로덕션 환경에서 이 부분은 도구 목록을 설명하는 시스템 프롬프트를 포함한 실제 LLM(OpenAI, Claude…)으로의 호출입니다. 여기서는 네트워크 호출 없이 결정 로직을 설명하기 위해 간단한 규칙 기반 클라이언트를 작성합니다.

public record LlmDecision(DecisionType type, String toolName,
                           Map<String, String> args, String finalAnswer) {
    public enum DecisionType { TOOL_CALL, FINAL_ANSWER }
}
public interface LlmClient {
    LlmDecision decideNextStep(String userQuestion, List<String> observations);
}
public class SimpleOrderLlmClient implements LlmClient {

    private static final Pattern ORDER_PATTERN = Pattern.compile("ORD-\\d+");

    @Override
    public LlmDecision decideNextStep(String question, List<String> observations) {
        if (observations.isEmpty()) {
            Matcher matcher = ORDER_PATTERN.matcher(question);
            if (!matcher.find()) {
                return new LlmDecision(LlmDecision.DecisionType.FINAL_ANSWER, null, null,
                        "주문번호를 입력해주세요. 형식: ORD-xxx");
            }
            Map<String, String> args = Map.of("orderId", matcher.group());
            return new LlmDecision(LlmDecision.DecisionType.TOOL_CALL,
                    "get_order_status", args, null);
        }

        String lastObservation = observations.get(observations.size() - 1);
        String status = lastObservation.split("->")[1].trim();
        return new LlmDecision(LlmDecision.DecisionType.FINAL_ANSWER, null, null,
                "당신의 주문 상태는: " + status);
    }
}

첫 번째 라운드(아직 observation이 없을 때), 질문에 주문번호가 있으면 클라이언트는 TOOL_CALL을 결정합니다. 두 번째 라운드(이미 도구의 observation이 있을 때), 이를 FINAL_ANSWER로 종합합니다. 이것이 실제 LLM이 도구를 받았을 때의 행동 방식입니다.

5.4. Agent 루프

이것은 모든 에이전트 프레임워크의 핵심입니다: 관찰 – 결정 – 행동을 반복하되, 무한 루프(runaway agent)를 방지하기 위해 단계 수를 제한합니다.

public class ReactAgent {

    private static final int MAX_STEPS = 5;

    private final LlmClient llmClient;
    private final Map<String, AgentTool> tools;

    public ReactAgent(LlmClient llmClient, List<AgentTool> toolList) {
        this.llmClient = llmClient;
        this.tools = toolList.stream()
                .collect(Collectors.toMap(AgentTool::name, t -> t));
    }

    public String run(String question) {
        List<String> observations = new ArrayList<>();

        for (int step = 0; step < MAX_STEPS; step++) {
            LlmDecision decision = llmClient.decideNextStep(question, observations);

            if (decision.type() == LlmDecision.DecisionType.FINAL_ANSWER) {
                return decision.finalAnswer();
            }

            AgentTool tool = tools.get(decision.toolName());
            if (tool == null) {
                throw new IllegalStateException(
                        "에이전트가 존재하지 않는 도구를 호출했습니다: " + decision.toolName());
            }

            String result = tool.execute(decision.args());
            observations.add(tool.name() + " -> " + result);
        }

        throw new IllegalStateException("에이전트가 허용된 단계 수를 초과했습니다 (" + MAX_STEPS + ")");
    }
}

MAX_STEPS 제한은 생각보다 중요합니다: LLM이 계속 도구를 호출하기로 결정하되 절대 FINAL_ANSWER를 내지 않으면(잘못된 프롬프트나 모델 혼란으로 인해), 에이전트는 무한 실행되어 API 비용이 들고 시스템이 과부하됩니다. 진지한 에이전트 SDK는 모두 이 메커니즘을 가지고 있습니다.

5.5. Spring Boot에 통합

@Configuration
public class AgentConfig {

    @Bean
    public ReactAgent reactAgent() {
        return new ReactAgent(new SimpleOrderLlmClient(), List.of(new OrderStatusTool()));
    }
}
@RestController
@RequestMapping("/api/agent")
public class AgentController {

    private final ReactAgent reactAgent;

    public AgentController(ReactAgent reactAgent) {
        this.reactAgent = reactAgent;
    }

    @PostMapping("/ask")
    public String ask(@RequestBody String question) {
        return reactAgent.run(question);
    }
}

시도해보세요:

curl -X POST http://localhost:8080/api/agent/ask \
  -H "Content-Type: text/plain" \
  -d "ORD-001 배송 상태를 확인해주세요"
당신의 주문 상태는: 배송 중

6. Agent의 동작에 대한 테스트 작성

에이전트는 ‘비선형’ 성질을 가집니다(실제 LLM을 사용할 때 비결정적). 따라서 가장 중요한 테스트는 루프의 end-to-end 동작을 확인하는 것이지, 내부 각 단계를 테스트하는 것이 아닙니다.

class ReactAgentTest {

    @Test
    void shouldReturnOrderStatus_whenOrderIdPresentInQuestion() {
        ReactAgent agent = new ReactAgent(
                new SimpleOrderLlmClient(), List.of(new OrderStatusTool()));

        String answer = agent.run("ORD-001 배송 상태를 확인해주세요");

        assertThat(answer).contains("배송 중");
    }

    @Test
    void shouldAskForOrderId_whenQuestionMissingOrderId() {
        ReactAgent agent = new ReactAgent(
                new SimpleOrderLlmClient(), List.of(new OrderStatusTool()));

        String answer = agent.run("내 주문은 어떻게 됐나요?");

        assertThat(answer).contains("주문번호");
    }

    @Test
    void shouldThrow_whenToolNameUnknown() {
        LlmClient brokenClient = (question, observations) ->
                new LlmDecision(LlmDecision.DecisionType.TOOL_CALL, "no_such_tool", Map.of(), null);
        ReactAgent agent = new ReactAgent(brokenClient, List.of(new OrderStatusTool()));

        assertThatThrownBy(() -> agent.run("아무거나"))
                .isInstanceOf(IllegalStateException.class)
                .hasMessageContaining("존재하지 않는");
    }
}

세 번째 테스트는 매우 현실적인 상황을 시뮬레이션합니다: LLM이 레지스트리에 존재하지 않는 도구 이름을 ‘환각’(hallucinate)합니다. 에이전트 런타임은 조용히 무시하거나 이해하기 어려운 오류로 크래시하는 대신, 명확한 예외로 이를 차단해야 합니다.

7. 흔한 오류 & agent framework를 사용하면 안 될 때

흔한 오류:

  • 반복 단계 수를 제한하지 않음 → LLM이 절대 멈추기로 결정하지 않으면 에이전트가 무한 실행됩니다. 항상 MAX_STEPS 또는 전체 타임아웃을 설정하세요.
  • LLM이 생성한 매개변수를 100% 신뢰함 → LLM은 잘못된 형식의 orderId나 존재하지 않는 값을 생성할 수 있습니다. 도구는 자체 검증을 해야 하며, 에이전트의 입력을 안전하다고 가정하면 안 됩니다. 클라이언트의 데이터를 신뢰하지 않는 것과 같습니다.
  • human-in-the-loop 확인 단계 없이 에이전트에게 민감한 데이터 쓰기/삭제 권한 부여 → LLM의 한 가지 잘못된 결정이 프로덕션 데이터를 실수로 삭제할 수 있습니다.
  • 너무 많은 도구를 하나의 에이전트에 넣음 → 목록이 너무 길고 설명(description)이 충분히 명확하고 구분되지 않으면 LLM이 잘못된 도구를 선택하기 쉬워집니다.

agent framework를 사용하면 안 될 때:

  • 문제가 LLM을 한 번만 호출하면 되고, 도구도 필요 없고, 반복도 필요 없음 — 직접 chat completion API를 사용하세요. 오케스트레이션 그래프를 가져와서 복잡하게 하지 마세요.
  • 시스템이 절대적으로 결정론적(deterministic) 결과를 요구합니다. 예: 세금 계산, 급여 계산 — LLM 기반 에이전트는 100% 일관성을 보장하지 않으므로, 순수 비즈니스 로직을 사용하세요.
  • 운영 비용(각 루프 단계는 LLM 호출 한 번, 토큰 소비)을 제대로 계산하지 않았음 — 다단계 에이전트는 스케일할 때 생각보다 훨씬 비쌀 수 있습니다.

8. 결론

2026년의 주목할 만한 AI agent 저장소들은 ’마법’이 아닙니다 — 모두 4가지 익숙한 패턴을 중심으로 합니다: ReAct 루프, tool calling, multi-agent orchestration, 그리고 memory/state. 5절의 최소한의 ReactAgent를 직접 작성할 수 있으면, 어떤 큰 프레임워크의 소스 코드를 읽어도 훨씬 예측하기 쉬워집니다. 왜냐하면 본질적으로 그들은 정확히 그 루프의 복잡한 버전일 뿐이기 때문입니다. 프로덕션에 가져올 저장소를 선택하기 전에, 스스로에게 물어보세요: 당신의 문제가 정말로 관찰 — 결정 — 행동 루프가 필요한가요, 아니면 단순한 LLM 호출 한 번으로 충분한가요?

AI agentLangGraphSpring AILLMGitHub