Loading the catalog…
Loading the catalog…
0. 들어가며 개인적으로 사내 에이전트 기반 제품 개발 프로젝트를 리드할 때에는 오픈소스 프레임워크를 사용하지는 않았습니다. 당시 검토한 오픈소스 프레임워크는 제품에 필요한 범위에 비해 방대하고 무거웠기 때문에 필요한 개념만 취해서 팀에서 직접 구현하는 식이었습니다. 그러다가 26년 기준 에이전트 프레임워크의 세계는 어떻게 변했을지, 각 프레임워크별 차이가 무엇이고 어떤 설계 사상과 기준이 반영돼 있을지 코드 수준에서 알아보고 싶어졌습니다. 그래서 Codex의 도움을 받아 주요 오픈소스 프레임워크의 실제 실행 경로를 따라가 봤습니다. 오늘 글에서는 기능 지원 여부를 나열하기보다, 각 프레임워크가 ‘Agent를 실행한다’는 문제를 어떻게 다르게 풀고 있는지를 중심으로 살펴보겠습니다. Framework 코드에서 드러나는 핵심 구조 LangGraph 상태를 가진 Graph Runtime 이 실행을 주도 OpenAI Agents SDK Agent와 Runner를 중심으로 한 명확한 Agent Loop Google ADK Agent를 Workflow를 구성하는 Node 로 통합 Microsoft Agent Framework Agent 실행에 Context·Session·Workflow 를 함께 결합 PydanticAI Agent 내부를 Typed State Machine / Graph 로 구현 CrewAI Role·Task 기반 구조에 Flow / Planning Runtime 을 결합 smolagents ReAct Loop가 직접 드러나며 CodeAgent를 주요 특징으로 하는 최소 추상화 Strands Agent Loop에서 Runtime / Harness 영역으로 확장 1. 다 지원한다는데, 왜 Agent Framework는 이렇게 많이 필요한가? 주요 Agent Framework의 기능을 표로 정리해보면 생각보다 큰 차이가 없어 보이는데요. Tool Calling, Memory나 State, Multi-Agent, Human-in-the-loop, Persistence 같은 기능은 이제 대부분의 프레임워크가 지원합니다. Framework Tool Memory / State Multi-Agent HITL Persistence LangGraph O O O O O OpenAI Agents SDK O O O O O Google ADK O O O O O Microsoft Agent Framework O O O O O PydanticAI O O O O O CrewAI O O O O O smolagents O O O 제한적 제한적 Strands O O O O O 이 표만 보면 어떤 프레임워크를 사용해도 크게 다르지 않아 보입니다. 문제는 같은 기능 이름이 실제로 같은 동작을 의미하지 않는다는 것 입니다. 예를 들어 Multi-Agent 는 어떤 프레임워크에서는 다른 Agent를 Tool처럼 호출하고 결과만 돌려받는 것을 의미하지만, 다른 프레임워크에서는 대화의 주도권 자체를 넘기는 Handoff이거나 Graph의 다른 Node나 Subgraph로 이동하는 것을 의미합니다. Persistence 역시 대화 내역을 저장하는 것부터 중단된 Agent Run을 이어가는 것, 실행 중이던 Graph의 어느 단계까지 완료됐는지를 복구하는 것까지 범위가 다릅니다. 그래서 Tool 지원 O , Multi-Agent 지원 O 같은 기능표에서 한 단계 더 내려가 실제 코드를 살펴보았습니다. 가장 작은 실행 단위가 무엇인지, 다음 행동을 Model과 Framework 중 누가 결정하는지, 무엇을 State로 저장하고 어디까지 복구하는지 등등 핵심 개념들을 코드 수준에서 따라가보니 프레임워크별 차이가 훨씬 선명하게 드러났습니다. 2. Loop: Agent 실행의 가장 기본적인 형태 Agent를 가장 단순하게 구현하면 Model을 호출하고, 필요한 Action을 실행하고, 그 결과를 다시 Model에 넣는 과정을 종료 조건이 충족될 때까지 반복 하게 되는데, 이런 실행 구조를 보통 Agent Loop 라고 부릅니다. OpenAI Agents SDK, Google ADK, Strands 등을 보면 형태는 다르지만 내부에 이런 반복 구조가 존재합니다. 차이는 '한 번'의 단위를 어떻게 정할 것인가 , 그리고 반복을 계속할지 누가 결정하는가 입니다. 가장 직접적인 형태는 smolagents 에서 볼 수 있습니다. smolagents 는 2024년 12월 31일 Hugging Face가 처음 공개한 오픈소스 Agent 라이브러리로, 기존 transformers.agents 의 후속 프로젝트입니다. Hugging Face는 공개 당시부터 가능한 한 적은 추상화로 Agent를 구현 하는 것을 주요 방향으로 제시했습니다. ( Hugging Face의 smolagents 최초 공개 글 ) 그럼 실제 코드를 열어볼까요? agents.py 의 MultiStepAgent._run_stream() 을 보면 Loop의 종료 조건을 알 수 있습니다. self.step_number = 0 ...(중간 생략) self.step_number = 1 returned_final_answer = False while not returned_final_answer and self.step_number <= max_steps: ... Source: Hugging Face smolagents · src/smolagents/agents.py · MultiStepAgent._run_stream() · Apache-2.0 · 2026-10-01 main 기준 · 코드 원문 MultiStepAgent 의 중심에는 ActionStep을 반복하는 ReAct Loop가 있습니다. 여기에 필요에 따라 Action Step 사이에 별도의 PlanningStep을 실행할 수 있고, 각 Step에는 callback, error handling, final-answer validation 같은 실행 제어도 붙습니다. 핵심 Control Flow가 직접적으로 드러난다는 점에서 다른 프레임워크보다 구조를 읽기 쉽습니다. _step_stream() 의 정의를 볼까요? def _step_stream( self, memory_step: ActionStep ) -> Generator[ChatMessageStreamDelta | ToolCall | ToolOutput | ActionOutput]: """ Perform one step in the ReAct framework: the agent thinks, acts, and observes the result. Yields ChatMessageStreamDelta during the run if streaming is enabled. At the end, yields either None if the step is not final, or the final answer. """ Source: Hugging Face smolagents · src/smolagents/agents.py · MultiStepAgent._step_stream() · Apache-2.0 · 2026-10-01 main 기준 · 코드 원문 주석을 번역해보면 다음과 같습니다. "ReAct 프레임워크의 한 단계를 수행합니다. 에이전트는 사고하고(think), 행동한 뒤(act), 그 결과를 관찰합니다(observe). 스트리밍이 활성화된 경우 실행 중에 ChatMessageStreamDelta를 반환합니다. 단계가 끝나면 최종 단계가 아닌 경우 None을 반환하고, 최종 단계인 경우 최종 답변을 반환합니다." 구조를 간단히 도식화해보면 다음과 같습니다. Model이 현재까지의 Context를 보고 다음 Action을 결정하고, Framework가 그 Action을 실행합니다. 실행 결과인 Observation은 다시 Memory에 쌓이고 다음 Model 호출의 입력이 됩니다. Final Answer가 나올 때까지 이 과정이 반복됩니다. 실제로 보면 크게 ToolCallingAgent와 CodeAgent로 나뉘는데요. 각 Step에서는 모델이 다음 Action을 생성합니다. ToolCallingAgent에서는 Tool Call이 Action이 됩니다. CodeAgent에서는 실행 가능한 Python 코드가 Action이 됩니다. 실행 결과는 Observation으로 AgentMemory에 남고 다음 Step의 Model Input에 다시 포함됩니다. 이렇게 Core Loop만 놓고 보면 구조가 단순해보이죠. 하지만 다른 Framework를 더 뜯어보면, 이 기본 Loop의 골격 위에 무엇을 추가하느냐에 따라 구조가 많이 달라집니다. OpenAI Agents SDK: Loop를 Turn 단위의 상태 전이로 관리한다 OpenAI Agents SDK로 넘어가보겠습니다. 기본적인 실행 원리는 smolagents 와 크게 다르지 않습니다. Runner.run() 의 docstring을 보면, Agent를 Final Output이 만들어질 때까지 Loop로 실행한다 고 직접 설명합니다. """ Run a workflow starting at the given agent. The agent will run in a loop until a final output is generated. The loop runs like so: 1. The agent is invoked with the given input. 2. If there is a final output, the loop terminates. 3. If there's a handoff, we run the loop again, with the new agent. 4. Else, we run tool calls (if any), and re-run the loop. """ 여기서 Turn은 “한 번의 AI invocation과 그 과정에서 발생하는 Tool Call”을 묶은 단위 로 정의하고 있습니다. max_turns: The maximum number of turns to run the agent for. A turn is defined as one AI invocation (including any tool calls that might occur). Source: OpenAI Agents SDK · src/agents/run.py · Runner.run() · MIT · 2026-10-01 main 기준 코드 원문 — run.py 실제 상위 실행부를 보면 Runner 안에는 큰 while True 가 있고, Loop가 돌 때마다 current_turn 을 하나씩 증가시킨 뒤 run_single_turn() 을 호출합니다. max_turns 도 바로 이 카운터를 기준으로 검사하고 있습니다. while True: ... current_turn += 1 ... turn_result = await run_single_turn(...) Source: OpenAI Agents SDK · src/agents/run.py · AgentRunner.run() 내부 main loop · MIT · 2026-10-01 main 기준 코드 원문 — run.py 도식으로 그려볼까요? 여기서 중요한 함수가 run_single_turn() 입니다. 코드 주석을 보면 “Run a single non-streaming turn of the agent loop.” 라고 정의합니다. 한 Turn 안에서는 먼저 현재 Agent의 System Prompt와 Prompt 설정을 가져오고, 사용할 Handoff와 Tool 목록, Output Schema를 결정합니다. 이전 Turn까지 생성된 Item도 현재 입력과 합쳐 Model Input을 만듭니다. 그리고 실제 Model 호출은 get_new_response() 로 넘어갑니다. new_response = await get_new_response(...) ... return await get_single_step_result_from_response(...) Source: OpenAI Agents SDK · src/agents/run_internal/run_loop.py · run_single_turn() · MIT · 2026-10-01 main 기준 코드 원문 — run_loop.py 이 두 호출 사이가 OpenAI Agents SDK의 한 Turn을 이해하는 데 중요합니다. get_new_response() 는 말 그대로 현재 Turn의 Model 호출을 담당합니다. 현재 Agent와 RunConfig에서 Model과 Model Settings를 가져온 다음 model.get_response() 를 호출하는데, 이때 단순한 Prompt만 넘기는 것이 아니라 현재 사용할 Tool, Output Schema, Handoff 목록도 함께 전달하는 것입니다. 현재까지 단계를 요약해볼까요? 흥미로운 부분은 그 다음입니다. Model이 돌려준 ModelResponse 를 Runner가 곧바로 검사해서 “Tool을 실행할지, Handoff할지, 끝낼지”를 결정하지 않습니다. 먼저 get_single_step_result_from_response() 로 넘기고, 여기서 process_model_response() 를 호출해 Provider가 반환한 Raw Output을 SDK가 이해할 수 있는 실행 단위로 변환 합니다. processed_response = process_model_response(...) ... return await execute_tools_and_side_effects(...) Source: OpenAI Agents SDK · src/agents/run_internal/turn_resolution.py · get_single_step_result_from_response() · MIT · 2026-10-01 main 기준 코드 원문 — turn_resolution.py Run Item Lifecycle 이 중간 단계가 필요한 이유는 Model의 출력이 하나의 종류가 아니기 때문입니다. 한 번의 Model 호출에서는 일반적인 Assistant Message뿐 아니라 Function Tool Call, Handoff를 나타내는 Tool Call, Computer Action, Shell Call, Approval이 필요한 호출 등이 함께 나올 수 있습니다. OpenAI Agents SDK는 이 Provider Output을 두 종류의 내부 표현으로 나눕니다. 첫 번째는 RunItem 입니다. 이것은 해당 Turn에서 “무슨 일이 발생했는가”를 표현하는 공개적인 기록 같은 것입니다. 이후 RunResult , Streaming Event, Session History, Trace 등에 사용됩니다. 두 번째는 실제로 SDK가 실행해야 할 내부 Tool Run Record 입니다. 예를 들어 Function Tool Call이라면 SDK가 실제로 호출할 Python Tool 객체, Call ID, Routing 정보, Approval 상태 등을 함께 보존하는 것입니다. 공식 Runner 내부 문서를 보면 이 구분이 명시되어 있습니다. process_model_response() 는 ModelResponse.output 을 public RunItem 과 internal executable tool-run record로 변환해 ProcessedResponse 에 담는다 고 설명합니다. process_model_response() converts recognized output into public RunItem objects and internal executable tool-run records in ProcessedResponse . 출처: https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/turn_resolution.py ProcessedResponse 는 이 둘을 한데 모아 현재 Turn에서 무엇이 생성됐고, 그중 실제로 무엇을 실행해야 하는지 를 정리한 중간 표현입니다. 예를 들어 모델이 다음과 같은 응답을 냈다고 가정해보겠습니다. - 일반 메시지 - get_weather() 호출 - transfer_to_support_agent() 호출 Provider 입장에서는 모두 하나의 Model Response 안에 들어 있는 Output Item입니다. 하지만 Runner 입장에서는 성격이 전혀 다릅니다. 일반 메시지는 결과와 History에 남길 RunItem get_weather() 는 실제 Function Tool을 찾아 실행해야 하는 작업 transfer_to_support_agent() 는 단순 Function Tool이 아니라 현재 Agent를 교체할 수 있는 Handoff 후보 그래서 process_model_response() 단계는 말하자면 “Model이 무엇을 출력했는가”를 “SDK가 무엇을 해야 하는가”로 번역 하는 것이지요. 공식 문서에서도 이 처리 흐름을 다음 순서로 설명합니다. Provider adapter가 ModelResponse.output 을 반환하면, process_model_response() 가 이를 ProcessedResponse 로 바꾸고, 이후 Tool 실행과 Handoff 처리가 Output Item을 추가한 뒤 SingleStepResult.next_step 을 결정합니다. "Tool execution and handoffs add output items and choose a SingleStepResult.next_step ." 출처: https://github.com/openai/openai-agents-python/blob/main/.agents/references/run-item-lifecycle.md 여기서 execute_tools_and_side_effects() 가 다음 단계입니다. process_model_response() 가 무엇을 실행해야 하는지 발견하고 구조화하는 단계 라면, 이 함수는 실제 Side Effect를 발생시키고 그 결과를 바탕으로 Control Flow를 결정합니다. 이 구분은 의도적으로 설계된 것입니다. OpenAI Agents SDK의 Tool Execution 문서는 process_model_response() 가 실행할 수 있는 작업을 찾아내는 단계와, 실제로 지금 실행해도 되는 작업을 결정하는 단계를 분리한다고 설명합니다. Approval이 필요한 호출인지, 이미 중단됐다가 재개된 실행인지, 동일한 Tool Call을 다시 실행하면 안 되는 상황인지 등을 Tool 실행 전에 별도로 판단하기 위해서입니다. “process_model_response() discovers executable work, but tool_planning.py decides which work may run now. ” 출처: https://github.com/openai/openai-agents-python/blob/main/.agents/references/tool-execution-lifecycle.md 같은 문서에서는 이 단계를 더 명확하게 다음처럼 설명합니다. “Keep discovery, approval partitioning, and invocation as separate phases.” 또 재개된 실행에서 이미 완료한 Tool Call을 다시 실행하지 않아야 한다는 점도 명시합니다. “A resumed interruption must execute unresolved or newly approved work without rediscovering or rerunning completed calls.” 예를 들어 Human Approval이 필요한 Tool이라면 Model이 Tool Call을 생성했다고 해서 바로 실행하면 안 됩니다. 이 구조 덕분에 Model Output의 해석과 실제 실행을 분리할 수 있습니다. 특히 이후에 등장하는 Approval, Tool deduplication, interruption/resume, Handoff 같은 기능은 이 분리가 있어야 안정적으로 처리할 수 있습니다. 여기까지 처리된 뒤에야 execute_tools_and_side_effects() 가 실제 Tool을 실행하고 Handoff나 Approval을 처리한 다음, 결과를 SingleStepResult.next_st
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
2026 - 오픈소스 에이전트 프레임워크 비교하기 (1) - Agent Loop. 0. 들어가며 개인적으로 사내 에이전트 기반 제품 개발 프로젝트를 리드할 때에는 오픈소스 프레임워크를 사용하지는 않았습니다. 당시 검토한 오픈소스 프레임워크는 제품에 필요한 범위에 비해 방대하고 무거웠기 때문에 필요한 개념만 취해서 팀에서 직접 구현하는 식이었습니다. 그러다가 26년 기준 에이전트 프레임워크의 세계는 어떻게 변했을지, 각 프레임워크별 차이가 무엇이고 어떤 설계 사상과 기준이 반영돼 있을지 코드 수준에서 알아보고 싶어졌습니다. 그래서 Codex의 도움을 받아 주요 오픈소스 프레임워크의 실제 실행 경로를 따라가 봤습니다. 오늘 글에서는 기능 지원 여부를 나열하기보다, 각 프레임워크가 ‘Agent를 실행한다’는…
Open source