# Agents-Course

## Docs

- [에이전틱 프레임워크 소개](https://huggingface.co/learn/agents-course/ko/unit2/introduction.md)
- [셀프 체크 (채점되지 않음) [[quiz2]]](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/quiz2.md)
- [LlamaIndex에서 에이전트 워크플로우 만들기](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/workflows.md)
- [결론](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/conclusion.md)
- [LlamaIndex에서 툴 사용하기](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/tools.md)
- [목차](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/README.md)
- [LlamaIndex에서 에이전트 사용하기](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/agents.md)
- [LlamaIndex 소개](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/introduction.md)
- [미니 퀴즈 (채점되지 않음) [[quiz1]]](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/quiz1.md)
- [LlamaIndex의 구성 요소란 무엇인가요?](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/components.md)
- [LlamaHub 소개](https://huggingface.co/learn/agents-course/ko/unit2/llama-index/llama-hub.md)
- [멀티 에이전트 시스템](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/multi_agent_systems.md)
- [smolagents로 비전 에이전트 만들기](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/vision_agents.md)
- [에이전틱 RAG 시스템 구축하기](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/retrieval_agents.md)
- [소규모 퀴즈(비채점) [[quiz2]]](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/quiz2.md)
- [코드를 사용하는 에이전트 만들기](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/code_agents.md)
- [결론](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/conclusion.md)
- [도구 (Tools)](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/tools.md)
- [시험 시간!](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/final_quiz.md)
- [`smolagents` 소개](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/introduction.md)
- [smolagents를 써야 하는 이유](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/why_use_smolagents.md)
- [소규모 퀴즈(비채점) [[quiz1]]](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/quiz1.md)
- [액션을 코드 스니펫 또는 JSON 블롭으로 작성하기](https://huggingface.co/learn/agents-course/ko/unit2/smolagents/tool_calling_agents.md)
- [`LangGraph`란 무엇인가요? [[what-is-langgraph]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/when_to_use_langgraph.md)
- [LangGraph의 구성 요소[[building-blocks-of-langgraph]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/building_blocks.md)
- [나만의 첫 LangGraph 만들기[[building-your-first-langgraph]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/first_graph.md)
- [결론[[conclusion]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/conclusion.md)
- [문서 분석 그래프[[document-analysis-graph]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/document_analysis_agent.md)
- [`LangGraph` 소개[[introduction-to-langgraph]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/introduction.md)
- [LangGraph 이해도 테스트 [[test-your-understanding-of-langgraph]]](https://huggingface.co/learn/agents-course/ko/unit2/langgraph/quiz1.md)
- [마지막 Unit에 오신 것을 환영합니다[[introduction]]](https://huggingface.co/learn/agents-course/ko/unit4/introduction.md)
- [함수 호출이란 무엇인가[[what-is-function-calling]]](https://huggingface.co/learn/agents-course/ko/bonus-unit1/what-is-function-calling.md)
- [소개[[introduction]]](https://huggingface.co/learn/agents-course/ko/bonus-unit1/introduction.md)
- [Unit 1 퀴즈 [[unit-1-quiz]]](https://huggingface.co/learn/agents-course/ko/unit1/final-quiz.md)
- [LLM이란?[[what-are-llms]]](https://huggingface.co/learn/agents-course/ko/unit1/what-are-llms.md)
- [액션: 에이전트가 환경과 상호작용할 수 있게 하기 [[actions-enabling-the-agent-to-engage-with-its-environment]]](https://huggingface.co/learn/agents-course/ko/unit1/actions.md)
- [셀프 체크! (업데이트됨) [[quiz2]]](https://huggingface.co/learn/agents-course/ko/unit1/quiz2.md)
- [결론 [[conclusion]]](https://huggingface.co/learn/agents-course/ko/unit1/conclusion.md)
- [도구(Tool)란? [[what-are-tools]]](https://huggingface.co/learn/agents-course/ko/unit1/tools.md)
- [메시지와 특수 토큰 [[messages-and-special-tokens]]](https://huggingface.co/learn/agents-course/ko/unit1/messages-and-special-tokens.md)
- [smolagents로 첫 번째 에이전트 만들기 [[lets-create-our-first-agent-using-smolagents]]](https://huggingface.co/learn/agents-course/ko/unit1/tutorial.md)
- [에이전트란? [[what-is-an-agent]]](https://huggingface.co/learn/agents-course/ko/unit1/what-are-agents.md)
- [컨텐츠 목록](https://huggingface.co/learn/agents-course/ko/unit1/README.md)
- [사고: AI 에이전트의 내부 추론과 Re-Act 방식 [[thought-internal-reasoning-and-the-re-act-approach]]](https://huggingface.co/learn/agents-course/ko/unit1/thoughts.md)
- [관찰: 피드백을 통합하여 성찰하고 적응하기 [[observe-integrating-feedback-to-reflect-and-adapt]]](https://huggingface.co/learn/agents-course/ko/unit1/observations.md)
- [에이전트 소개 [[introduction-to-agents]]](https://huggingface.co/learn/agents-course/ko/unit1/introduction.md)
- [사고-행동-관찰 주기를 통해 AI 에이전트 이해하기 [[understanding-ai-agents-through-the-thought-action-observation-cycle]]](https://huggingface.co/learn/agents-course/ko/unit1/agent-steps-and-structure.md)
- [Q1: 에이전트(Agent)란? [[q1-what-is-an-agent]]](https://huggingface.co/learn/agents-course/ko/unit1/quiz1.md)
- [더미 에이전트 라이브러리 [[dummy-agent-library]]](https://huggingface.co/learn/agents-course/ko/unit1/dummy-agent-library.md)
- [온보딩(Onboarding): 첫 걸음 내딛기 ⛵](https://huggingface.co/learn/agents-course/ko/unit0/onboarding.md)
- [AI 에이전트 코스에 오신걸 환영합니다 🤗 [[introduction]]](https://huggingface.co/learn/agents-course/ko/unit0/introduction.md)
- [(선택 섹션) Discord 101 [[discord-101]]](https://huggingface.co/learn/agents-course/ko/unit0/discord101.md)

### 에이전틱 프레임워크 소개
https://huggingface.co/learn/agents-course/ko/unit2/introduction.md

# 에이전틱 프레임워크 소개

Unit 2에 오신 것을 환영합니다! **이번 유닛에서는 다양한 에이전틱 프레임워크**를 살펴보고, 이를 활용해 강력한 에이전트 기반 애플리케이션을 만드는 방법을 배웁니다.

이번 유닛에서 다룰 내용은 다음과 같습니다:

- 2.1: [smolagents](https://huggingface.co/docs/smolagents/en/index)  
- 2.2: [LlamaIndex](https://www.llamaindex.ai/)
- 2.3: [LangGraph](https://www.langchain.com/langgraph)

함께 시작해봅시다! 🕵

## 에이전틱 프레임워크는 언제 필요할까?

에이전틱 프레임워크는 **LLM 기반 애플리케이션을 만들 때 항상 필요한 것은 아닙니다.** 프레임워크는 특정 작업을 효율적으로 해결할 수 있도록 워크플로우에 유연성을 제공하지만, 항상 필수적인 것은 아닙니다.

때로는 **미리 정의된 워크플로우만으로도** 사용자 요청을 충분히 처리할 수 있으며, 굳이 에이전틱 프레임워크가 필요하지 않을 수 있습니다. 에이전트 구조가 단순하다면, 프롬프트 체이닝 등 일반 코드만으로도 충분합니다. 이 경우 개발자는 **추상화 없이 시스템을 완전히 이해하고 제어**할 수 있다는 장점이 있습니다.

하지만 워크플로우가 복잡해져 LLM이 함수를 호출하거나, 여러 에이전트를 활용해야 하는 경우에는 이러한 추상화가 도움이 되기 시작합니다.

이러한 점을 고려하면, 다음과 같은 기능이 필요함을 알 수 있습니다:

* 시스템을 구동할 *LLM 엔진*
* 에이전트가 접근할 수 있는 *도구 목록*  
* LLM 출력에서 도구 호출을 추출하는 *파서*
* 파서와 동기화된 *시스템 프롬프트*
* *메모리 시스템*
* LLM의 실수를 제어할 *에러 로깅 및 재시도 메커니즘*

이러한 주제들이 각 프레임워크에서 어떻게 해결되는지, `smolagents`, `LlamaIndex`, `LangGraph`를 통해 살펴볼 예정입니다.

## 에이전틱 프레임워크별 유닛 안내

| 프레임워크  | 설명 | 유닛 저자 |
|------------|----------------|----------------|
| [smolagents](./smolagents/introduction) | Hugging Face에서 개발한 에이전트 프레임워크 | Sergio Paniego - [HF](https://huggingface.co/sergiopaniego) - [X](https://x.com/sergiopaniego) - [Linkedin](https://www.linkedin.com/in/sergio-paniego-blanco) |
| [Llama-Index](./llama-index/introduction) | 컨텍스트 증강 AI 에이전트를 프로덕션에 배포할 수 있는 엔드-투-엔드 툴링 | David Berenstein - [HF](https://huggingface.co/davidberenstein1957) - [X](https://x.com/davidberenstei) - [Linkedin](https://www.linkedin.com/in/davidberenstein) |
| [LangGraph](./langgraph/introduction) | 상태 기반 에이전트 오케스트레이션을 지원하는 프레임워크 | Joffrey THOMAS - [HF](https://huggingface.co/Jofthomas) - [X](https://x.com/Jthmas404) - [Linkedin](https://www.linkedin.com/in/joffrey-thomas) |

### 셀프 체크 (채점되지 않음) [[quiz2]]
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/quiz2.md

# 셀프 체크 (채점되지 않음) [[quiz2]]

뭐?! 또 다른 퀴즈? 알겠습니다, 알겠습니다... 😅 하지만 이 짧은, 채점되지 않는 퀴즈는 **방금 배운 핵심 개념을 강화하는 데 도움이** 되기 위해 여기 있습니다.

이 퀴즈는 효과적인 AI 에이전트를 구축하기 위한 필수 구성 요소인 에이전트 워크플로우와 상호작용을 다룹니다.

### Q1: LlamaIndex에서 AgentWorkflow의 목적은 무엇인가요?

---

### Q2: 워크플로우의 상태를 추적하는 데 사용되는 객체는 무엇인가요?

---

### Q3: 에이전트가 이전 상호작용을 기억하게 하려면 어떤 메서드를 사용해야 하나요?

---

### Q4: 에이전트 RAG의 주요 특징은 무엇인가요?

---

이해하셨나요? 훌륭합니다! 이제 **단원에 대한 간단한 요약을 해보겠습니다!**

### LlamaIndex에서 에이전트 워크플로우 만들기
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/workflows.md

# LlamaIndex에서 에이전트 워크플로우 만들기

LlamaIndex의 워크플로우는 코드를 순차적이고 관리 가능한 단계로 구성하는 구조화된 방법을 제공합니다.

이러한 워크플로우는 `Events`에 의해 트리거되는 `Steps`를 정의하여 생성되며, 이들 자체가 추가 단계를 트리거하기 위해 `Events`를 발생시킵니다.
RAG 작업을 위한 LlamaIndex 워크플로우를 보여주는 알프레드를 살펴보겠습니다.

![워크플로우 개요](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/workflows.png)

**워크플로우는 몇 가지 주요 이점을 제공합니다:**

- 코드를 개별 단계로 명확하게 구성
- 유연한 제어 흐름을 위한 이벤트 기반 아키텍처
- 단계 간 타입 안전 통신
- 내장 상태 관리
- 간단하고 복잡한 에이전트 상호작용 모두 지원

추측하셨을 수 있듯이, **워크플로우는 전체 워크플로우에 대한 제어를 유지하면서 에이전트의 자율성 사이에서 훌륭한 균형을 제공합니다.**

그럼 우리만의 워크플로우를 만드는 방법을 배워보겠습니다!

## 워크플로우 만들기

이 노트북을 따라가며 Google Colab에서 코드를 실행해볼 수 있습니다.

### 기본 워크플로우 생성

워크플로우 패키지 설치
LlamaHub 섹션에서 소개된 대로, 다음 명령어로 워크플로우 패키지를 설치할 수 있습니다:

```python
pip install llama-index-utils-workflow
```

`Workflow`를 상속하는 클래스를 정의하고 함수에 `@step` 데코레이터를 사용하여 단일 단계 워크플로우를 만들 수 있습니다.
또한 워크플로우의 시작과 끝을 나타내는 특별한 이벤트인 `StartEvent`와 `StopEvent`를 추가해야 합니다.

```python
from llama_index.core.workflow import StartEvent, StopEvent, Workflow, step

class MyWorkflow(Workflow):
    @step
    async def my_step(self, ev: StartEvent) -> StopEvent:
        # 여기서 무언가 수행
        return StopEvent(result="안녕하세요, 세계!")

w = MyWorkflow(timeout=10, verbose=False)
result = await w.run()
```

보시다시피, 이제 `w.run()`을 호출하여 워크플로우를 실행할 수 있습니다.

### 여러 단계 연결하기

여러 단계를 연결하기 위해 **단계 간에 데이터를 전달하는 사용자 정의 이벤트를 만듭니다.**
이를 위해 단계 간에 전달되고 첫 번째 단계의 출력을 두 번째 단계로 전송하는 `Event`를 추가해야 합니다.

```python
from llama_index.core.workflow import Event

class ProcessingEvent(Event):
    intermediate_result: str

class MultiStepWorkflow(Workflow):
    @step
    async def step_one(self, ev: StartEvent) -> ProcessingEvent:
        # 초기 데이터 처리
        return ProcessingEvent(intermediate_result="1단계 완료")

    @step
    async def step_two(self, ev: ProcessingEvent) -> StopEvent:
        # 중간 결과 사용
        final_result = f"처리 완료: {ev.intermediate_result}"
        return StopEvent(result=final_result)

w = MultiStepWorkflow(timeout=10, verbose=False)
result = await w.run()
result
```

타입 힌팅은 워크플로우가 올바르게 실행되도록 보장하므로 여기서 중요합니다. 조금 더 복잡하게 만들어보겠습니다!

### 루프와 분기

타입 힌팅은 워크플로우의 가장 강력한 부분입니다. 더 복잡한 워크플로우를 용이하게 하기 위해 분기, 루프, 조인을 만들 수 있게 해주기 때문입니다.

합집합 연산자 `|`를 사용하여 **루프를 만드는** 예제를 보여드리겠습니다.
아래 예제에서 `LoopEvent`가 단계의 입력으로 사용되고 출력으로도 반환될 수 있음을 볼 수 있습니다.

```python
from llama_index.core.workflow import Event
import random

class ProcessingEvent(Event):
    intermediate_result: str

class LoopEvent(Event):
    loop_output: str

class MultiStepWorkflow(Workflow):
    @step
    async def step_one(self, ev: StartEvent | LoopEvent) -> ProcessingEvent | LoopEvent:
        if random.randint(0, 1) == 0:
            print("나쁜 일이 발생했습니다")
            return LoopEvent(loop_output="1단계로 돌아갑니다.")
        else:
            print("좋은 일이 발생했습니다")
            return ProcessingEvent(intermediate_result="첫 번째 단계 완료.")

    @step
    async def step_two(self, ev: ProcessingEvent) -> StopEvent:
        # 중간 결과 사용
        final_result = f"처리 완료: {ev.intermediate_result}"
        return StopEvent(result=final_result)

w = MultiStepWorkflow(verbose=False)
result = await w.run()
result
```

### 워크플로우 그리기

워크플로우를 그릴 수도 있습니다. `draw_all_possible_flows` 함수를 사용하여 워크플로우를 그려보겠습니다. 이는 워크플로우를 HTML 파일에 저장합니다.

```python
from llama_index.utils.workflow import draw_all_possible_flows

w = ... # 이전 섹션에서 정의된 대로
draw_all_possible_flows(w, "flow.html")
```

![워크플로우 그리기](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/workflow-draw.png)

강의에서 다룰 마지막 멋진 트릭이 있는데, 그것은 워크플로우에 상태를 추가하는 능력입니다.

### 상태 관리

상태 관리는 워크플로우의 상태를 추적하고 싶을 때 유용하므로, 모든 단계가 동일한 상태에 접근할 수 있습니다.
단계 함수의 매개변수 위에 `Context` 타입 힌팅을 사용하여 이를 수행할 수 있습니다.

```python
from llama_index.core.workflow import Context, StartEvent, StopEvent

@step
async def query(self, ctx: Context, ev: StartEvent) -> StopEvent:
    # 컨텍스트에 쿼리 저장
    await ctx.store.set("query", "프랑스의 수도는 무엇인가요?")

    # 컨텍스트와 이벤트로 무언가 수행
    val = ...

    # 컨텍스트에서 쿼리 검색
    query = await ctx.store.get("query")

    return StopEvent(result=val)
```

훌륭합니다! 이제 LlamaIndex에서 기본 워크플로우를 만드는 방법을 알게 되었습니다!

워크플로우에는 더 복잡한 뉘앙스가 있으며, LlamaIndex 문서에서 배울 수 있습니다.

하지만 `AgentWorkflow` 클래스에 의존하는 워크플로우를 만드는 다른 방법이 있습니다. 이를 사용하여 다중 에이전트 워크플로우를 만드는 방법을 살펴보겠습니다.

## 다중 에이전트 워크플로우로 워크플로우 자동화

수동 워크플로우 생성 대신 **`AgentWorkflow` 클래스를 사용하여 다중 에이전트 워크플로우를 만들** 수 있습니다.
`AgentWorkflow`는 워크플로우 에이전트를 사용하여 전문화된 기능을 기반으로 협업하고 서로에게 작업을 넘길 수 있는 하나 이상의 에이전트 시스템을 만들 수 있게 해줍니다.
이를 통해 서로 다른 에이전트가 작업의 서로 다른 측면을 처리하는 복잡한 에이전트 시스템을 구축할 수 있습니다.
`llama_index.core.agent`에서 클래스를 가져오는 대신, `llama_index.core.agent.workflow`에서 에이전트 클래스를 가져올 것입니다.
하나의 에이전트는 `AgentWorkflow` 생성자에서 루트 에이전트로 지정되어야 합니다.
사용자 메시지가 들어오면 먼저 루트 에이전트로 라우팅됩니다.

각 에이전트는 다음을 수행할 수 있습니다:

- 툴을 사용하여 요청을 직접 처리
- 작업에 더 적합한 다른 에이전트에게 작업 넘기기
- 사용자에게 응답 반환

다중 에이전트 워크플로우를 만드는 방법을 살펴보겠습니다.

```python
from llama_index.core.agent.workflow import AgentWorkflow, ReActAgent
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# 몇 가지 툴 정의
def add(a: int, b: int) -> int:
    """두 숫자를 더합니다."""
    return a + b

def multiply(a: int, b: int) -> int:
    """두 숫자를 곱합니다."""
    return a * b

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# FunctionTool 없이 함수를 직접 전달할 수 있습니다 -- 함수/독스트링이 이름/설명을 위해 파싱됩니다
multiply_agent = ReActAgent(
    name="multiply_agent",
    description="두 정수를 곱할 수 있습니다",
    system_prompt="숫자를 곱하는 툴을 사용할 수 있는 도움이 되는 어시스턴트입니다.",
    tools=[multiply],
    llm=llm,
)

addition_agent = ReActAgent(
    name="add_agent",
    description="두 정수를 더할 수 있습니다",
    system_prompt="숫자를 더하는 툴을 사용할 수 있는 도움이 되는 어시스턴트입니다.",
    tools=[add],
    llm=llm,
)

# 워크플로우 생성
workflow = AgentWorkflow(
    agents=[multiply_agent, addition_agent],
    root_agent="multiply_agent",
)

# 시스템 실행
response = await workflow.run(user_msg="5와 3을 더할 수 있나요?")
```

에이전트 툴은 앞서 언급한 워크플로우 상태도 수정할 수 있습니다. 워크플로우를 시작하기 전에 모든 에이전트가 사용할 수 있는 초기 상태 딕셔너리를 제공할 수 있습니다.
상태는 워크플로우 컨텍스트의 상태 키에 저장됩니다. 각 새로운 사용자 메시지를 보강하는 state_prompt에 주입됩니다.

이전 예제를 수정하여 함수 호출을 세는 카운터를 주입해보겠습니다:

```python
from llama_index.core.workflow import Context

# 몇 가지 툴 정의
async def add(ctx: Context, a: int, b: int) -> int:
    """두 숫자를 더합니다."""
    # 카운트 업데이트
    cur_state = await ctx.store.get("state")
    cur_state["num_fn_calls"] += 1
    await ctx.store.set("state", cur_state)

    return a + b

async def multiply(ctx: Context, a: int, b: int) -> int:
    """두 숫자를 곱합니다."""
    # 카운트 업데이트
    cur_state = await ctx.store.get("state")
    cur_state["num_fn_calls"] += 1
    await ctx.store.set("state", cur_state)

    return a * b

...

workflow = AgentWorkflow(
    agents=[multiply_agent, addition_agent],
    root_agent="multiply_agent",
    initial_state={"num_fn_calls": 0},
    state_prompt="현재 상태: {state}. 사용자 메시지: {msg}",
)

# 컨텍스트와 함께 워크플로우 실행
ctx = Context(workflow)
response = await workflow.run(user_msg="5와 3을 더할 수 있나요?", ctx=ctx)

# 상태를 꺼내서 검사
state = await ctx.store.get("state")
print(state["num_fn_calls"])
```

축하합니다! 이제 LlamaIndex에서 에이전트의 기본을 마스터했습니다! 🎉

지식을 확고히 하기 위해 마지막 퀴즈를 계속해보겠습니다! 🚀

### 결론
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/conclusion.md

# 결론

2단원의 `llama-index` 모듈을 완료하신 것을 축하합니다! 🥳

방금 `llama-index`의 기본을 마스터하고 자신만의 에이전트 워크플로우를 구축하는 방법을 보셨습니다!
이제 `llama-index` 기술을 갖추셨으니, 관심 있는 작업을 해결할 검색 엔진을 만들기 시작할 수 있습니다.

이 단원의 다음 모듈에서는 **LangGraph로 에이전트를 구축하는 방법**을 배우게 됩니다.

마지막으로, **강의에 대한 여러분의 생각과 개선 방법**을 듣고 싶습니다.
피드백이 있으시면 👉 [이 양식을 작성해주세요](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### 계속 배우고, 멋지게 유지하세요! 🤗

### LlamaIndex에서 툴 사용하기
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/tools.md

# LlamaIndex에서 툴 사용하기

**명확한 툴 세트를 정의하는 것이 성능에 중요합니다.** [1단원](../../unit1/tools)에서 논의했듯이, 명확한 툴 인터페이스는 LLM이 사용하기 쉽습니다.
인간 엔지니어를 위한 소프트웨어 API 인터페이스와 마찬가지로, 툴이 어떻게 작동하는지 이해하기 쉽다면 더 많은 것을 얻을 수 있습니다.

**LlamaIndex에는 네 가지 주요 유형의 툴**이 있습니다:

![툴](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/tools.png)

1. `FunctionTool`: 모든 Python 함수를 에이전트가 사용할 수 있는 툴로 변환합니다. 함수가 어떻게 작동하는지 자동으로 파악합니다.
2. `QueryEngineTool`: 에이전트가 쿼리 엔진을 사용할 수 있게 하는 툴입니다. 에이전트는 쿼리 엔진 위에 구축되므로 다른 에이전트도 툴로 사용할 수 있습니다.
3. `Toolspecs`: 커뮤니티에서 만든 툴 세트로, 종종 Gmail과 같은 특정 서비스를 위한 툴을 포함합니다.
4. `Utility Tools`: 다른 툴에서 나오는 대량의 데이터를 처리하는 데 도움이 되는 특별한 툴입니다.

아래에서 각각에 대해 더 자세히 살펴보겠습니다.

## FunctionTool 만들기

이 노트북을 따라가며 Google Colab에서 코드를 실행해볼 수 있습니다.

FunctionTool은 모든 Python 함수를 래핑하고 에이전트가 사용할 수 있게 만드는 간단한 방법을 제공합니다.
동기 또는 비동기 함수를 툴에 전달할 수 있으며, 선택적으로 `name`과 `description` 매개변수도 함께 전달할 수 있습니다.
이름과 설명은 특히 중요합니다. 에이전트가 언제, 어떻게 툴을 효과적으로 사용할지 이해하는 데 도움이 되기 때문입니다.
아래에서 FunctionTool을 만드는 방법을 살펴보고 호출해보겠습니다.

```python
from llama_index.core.tools import FunctionTool

def get_weather(location: str) -> str:
    """주어진 위치의 날씨를 가져오는 데 유용합니다."""
    print(f"{location}의 날씨를 가져오는 중")
    return f"{location}의 날씨는 맑습니다"

tool = FunctionTool.from_defaults(
    get_weather,
    name="my_weather_tool",
    description="주어진 위치의 날씨를 가져오는 데 유용합니다.",
)
tool.call("뉴욕")
```

함수 호출이 있는 에이전트나 LLM을 사용할 때, 선택된 툴(그 툴에 대해 작성된 인수)은 툴의 목적과 인수에 대한 툴 이름과 설명에 크게 의존합니다. 함수 호출 가이드에서 함수 호출에 대해 더 알아보세요.

## QueryEngineTool 만들기

이전 단원에서 정의한 `QueryEngine`은 `QueryEngineTool` 클래스를 사용하여 쉽게 툴로 변환할 수 있습니다.
아래 예제에서 `QueryEngine`에서 `QueryEngineTool`을 만드는 방법을 살펴보겠습니다.

```python
from llama_index.core import VectorStoreIndex
from llama_index.core.tools import QueryEngineTool
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore

embed_model = HuggingFaceEmbedding("BAAI/bge-small-en-v1.5")

db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)

index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(llm=llm)
tool = QueryEngineTool.from_defaults(query_engine, name="some useful name", description="some useful description")
```

## Toolspecs 만들기

`ToolSpecs`를 조화롭게 함께 작동하는 툴 모음으로 생각하세요 - 잘 정리된 전문가 툴킷처럼요.
정비사의 툴킷이 차량 수리를 위해 함께 작동하는 보완적인 툴들을 포함하는 것처럼, `ToolSpec`은 특정 목적을 위해 관련된 툴들을 결합합니다.
예를 들어, 회계 에이전트의 `ToolSpec`은 스프레드시트 기능, 이메일 기능, 계산 툴을 우아하게 통합하여 재무 작업을 정확하고 효율적으로 처리할 수 있습니다.

Google Toolspec 설치
LlamaHub 섹션에서 소개된 대로, 다음 명령어로 Google toolspec을 설치할 수 있습니다:

```python
pip install llama-index-tools-google
```

이제 toolspec을 로드하고 툴 리스트로 변환할 수 있습니다.

```python
from llama_index.tools.google import GmailToolSpec

tool_spec = GmailToolSpec()
tool_spec_list = tool_spec.to_tool_list()
```

툴에 대한 더 자세한 보기를 얻기 위해 각 툴의 `metadata`를 살펴볼 수 있습니다.

```python
[(tool.metadata.name, tool.metadata.description) for tool in tool_spec_list]
```

### LlamaIndex의 모델 컨텍스트 프로토콜 (MCP)

LlamaIndex는 [LlamaHub의 ToolSpec](https://llamahub.ai/l/tools/llama-index-tools-mcp?from=)을 통해 MCP 툴 사용도 허용합니다.
단순히 MCP 서버를 실행하고 다음 구현을 통해 사용을 시작할 수 있습니다.

MCP에 대해 더 깊이 알고 싶다면 [무료 MCP 강의](https://huggingface.co/learn/mcp-course/)를 확인할 수 있습니다.

MCP Toolspec 설치
LlamaHub 섹션에서 소개된 대로, 다음 명령어로 MCP toolspec을 설치할 수 있습니다:

```python
pip install llama-index-tools-mcp
```

```python
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec

# 127.0.0.1:8000에서 실행 중인 mcp 서버가 있다고 가정하거나, mcp 클라이언트를 사용하여 자체 mcp 서버에 연결할 수 있습니다.
mcp_client = BasicMCPClient("http://127.0.0.1:8000/sse")
mcp_tool = McpToolSpec(client=mcp_client)

# 에이전트 가져오기
agent = await get_agent(mcp_tool)

# 에이전트 컨텍스트 생성
agent_context = Context(agent)
```

## 유틸리티 툴

종종 API를 직접 쿼리하는 것은 **과도한 양의 데이터를 반환할 수 있으며**, 그 중 일부는 관련이 없거나, LLM의 컨텍스트 윈도우를 넘치거나, 사용하는 토큰 수를 불필요하게 증가시킬 수 있습니다.
아래에서 두 가지 주요 유틸리티 툴을 살펴보겠습니다.

1. `OnDemandToolLoader`: 이 툴은 기존 LlamaIndex 데이터 로더(BaseReader 클래스)를 에이전트가 사용할 수 있는 툴로 변환합니다. 툴은 데이터 로더에서 `load_data`를 트리거하는 데 필요한 모든 매개변수와 자연어 쿼리 문자열로 호출할 수 있습니다. 실행 중에 먼저 데이터 로더에서 데이터를 로드하고, 인덱싱하고(예를 들어 벡터 스토어로), 그런 다음 '온디맨드'로 쿼리합니다. 이 세 단계 모두가 단일 툴 호출에서 발생합니다.
2. `LoadAndSearchToolSpec`: LoadAndSearchToolSpec은 기존 툴을 입력으로 받습니다. 툴 스펙으로서 `to_tool_list`를 구현하며, 해당 함수가 호출되면 두 개의 툴이 반환됩니다: 로딩 툴과 검색 툴입니다. 로드 툴 실행은 기본 툴을 호출한 다음 출력을 인덱싱합니다(기본적으로 벡터 인덱스로). 검색 툴 실행은 쿼리 문자열을 입력으로 받아 기본 인덱스를 호출합니다.

toolspecs와 유틸리티 툴은 LlamaHub에서 찾을 수 있습니다

이제 LlamaIndex에서 에이전트와 툴의 기본을 이해했으니, LlamaIndex를 사용하여 **구성 가능하고 관리 가능한 워크플로우를 만드는** 방법을 살펴보겠습니다!

### 목차
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/README.md

# 목차

이 LlamaIndex 프레임 개요는 강의 2단원의 일부입니다. hf.co/learn에서 LlamaIndex에 대한 2단원에 접근할 수 있습니다 👉 여기

| 제목 | 설명 |
| --- | --- |
| [소개](introduction.mdx) | LlamaIndex 소개 |
| [LlamaHub](llama-hub.mdx) | LlamaHub: 통합, 에이전트, 툴의 레지스트리 |
| [구성 요소](components.mdx) | 구성 요소: 워크플로우의 빌딩 블록 |
| [툴](tools.mdx) | 툴: LlamaIndex에서 툴을 구축하는 방법 |
| [퀴즈 1](quiz1.mdx) | 퀴즈 1 |
| [에이전트](agents.mdx) | 에이전트: LlamaIndex에서 에이전트를 구축하는 방법 |
| [워크플로우](workflows.mdx) | 워크플로우: 순서대로 실행되는 구성 요소로 만들어진 단계, 이벤트의 시퀀스 |
| [퀴즈 2](quiz2.mdx) | 퀴즈 2 |
| [결론](conclusion.mdx) | 결론 |

### LlamaIndex에서 에이전트 사용하기
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/agents.md

# LlamaIndex에서 에이전트 사용하기

이전에 나왔던 도움이 되는 집사 에이전트 알프레드를 기억하시나요? 이제 그가 업그레이드를 받을 차례입니다!
이제 LlamaIndex에서 사용할 수 있는 툴들을 이해했으니, 알프레드에게 더 나은 서비스를 제공할 수 있는 새로운 기능들을 부여할 수 있습니다.

하지만 계속하기 전에, 알프레드 같은 에이전트가 어떻게 작동하는지 다시 한번 상기해봅시다.
1단원에서 우리는 다음을 배웠습니다:

> 에이전트는 사용자가 정의한 목표를 달성하기 위해 AI 모델을 활용하여 환경과 상호작용하는 시스템입니다. 작업을 수행하기 위해 추론, 계획, 액션 실행(종종 외부 툴을 통해)을 결합합니다.

LlamaIndex는 **세 가지 주요 유형의 추론 에이전트**를 지원합니다:

![에이전트](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agents.png)

1. `Function Calling Agents` - 특정 함수를 호출할 수 있는 AI 모델과 함께 작동합니다.
2. `ReAct Agents` - 채팅이나 텍스트 엔드포인트를 수행하는 모든 AI와 함께 작동할 수 있으며, 복잡한 추론 작업을 처리합니다.
3. `Advanced Custom Agents` - 더 복잡한 작업과 워크플로우를 처리하기 위해 더 복잡한 방법을 사용합니다.

고급 에이전트에 대한 더 많은 정보는 BaseWorkflowAgent에서 찾을 수 있습니다.

## 에이전트 초기화

이 노트북을 따라가며 Google Colab에서 코드를 실행해볼 수 있습니다.

에이전트를 만들기 위해, 먼저 **기능을 정의하는 함수/툴 세트**를 제공합니다.
몇 가지 기본 툴로 에이전트를 만드는 방법을 살펴보겠습니다. 현재로서는 에이전트가 자동으로 함수 호출 API(사용 가능한 경우) 또는 표준 ReAct 에이전트 루프를 사용합니다.

툴/함수 API를 지원하는 LLM들은 비교적 최신이지만, 특정 프롬프팅을 피하고 제공된 스키마를 기반으로 LLM이 툴 호출을 생성할 수 있게 하여 툴을 호출하는 강력한 방법을 제공합니다.

ReAct 에이전트는 또한 복잡한 추론 작업에 능숙하며, 채팅이나 텍스트 완성 기능이 있는 모든 LLM과 함께 작동할 수 있습니다. 더 상세하며, 수행하는 특정 액션의 추론 과정을 보여줍니다.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.core.tools import FunctionTool

# 샘플 툴 정의 -- 타입 어노테이션, 함수 이름, 독스트링이 모두 파싱된 스키마에 포함됩니다!
def multiply(a: int, b: int) -> int:
    """두 정수를 곱하고 결과 정수를 반환합니다"""
    return a * b

# llm 초기화
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# 에이전트 초기화
agent = AgentWorkflow.from_tools_or_functions(
    [FunctionTool.from_defaults(multiply)],
    llm=llm
)
```

**에이전트는 기본적으로 상태가 없으며**, 과거 상호작용을 기억하는 것은 `Context` 객체를 사용하여 선택사항입니다.
이는 여러 메시지에 걸쳐 맥락을 유지하는 챗봇이나 시간이 지남에 따라 진행 상황을 추적해야 하는 작업 관리자처럼 이전 상호작용을 기억해야 하는 에이전트를 사용하고 싶을 때 유용할 수 있습니다.

```python
# 상태 없음
response = await agent.run("2 곱하기 2는 무엇인가요?")

# 상태 기억
from llama_index.core.workflow import Context

ctx = Context(agent)

response = await agent.run("내 이름은 Bob입니다.", ctx=ctx)
response = await agent.run("내 이름이 뭐였지?", ctx=ctx)
```

`LlamaIndex`의 에이전트들이 Python의 `await` 연산자를 사용하기 때문에 비동기라는 것을 알 수 있습니다. Python의 비동기 코드가 처음이거나 복습이 필요하다면, [훌륭한 비동기 가이드](https://docs.llamaindex.ai/en/stable/getting_started/async_python/)가 있습니다.

이제 기본을 마스터했으니, 에이전트에서 더 복잡한 툴을 사용하는 방법을 살펴보겠습니다.

## QueryEngineTools로 RAG 에이전트 만들기

**에이전트 RAG는 에이전트를 사용하여 데이터에 대한 질문에 답하는 강력한 방법입니다.** 알프레드가 질문에 답하는 데 도움이 되도록 다양한 툴을 전달할 수 있습니다.
하지만 문서 위에서 자동으로 질문에 답하는 대신, 알프레드는 질문에 답하기 위해 다른 툴이나 흐름을 사용할지 결정할 수 있습니다.

![에이전트 RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agentic-rag.png)

에이전트를 위해 **`QueryEngine`을 툴로 래핑**하는 것은 쉽습니다.
이렇게 할 때, **이름과 설명을 정의**해야 합니다. LLM은 이 정보를 사용하여 툴을 올바르게 사용합니다.
[구성 요소 섹션](components)에서 만든 `QueryEngine`을 사용하여 `QueryEngineTool`을 로드하는 방법을 살펴보겠습니다.

```python
from llama_index.core.tools import QueryEngineTool

query_engine = index.as_query_engine(llm=llm, similarity_top_k=3) # LlamaIndex의 구성 요소 섹션에서 보여진 대로

query_engine_tool = QueryEngineTool.from_defaults(
    query_engine=query_engine,
    name="name",
    description="a specific description",
    return_direct=False,
)
query_engine_agent = AgentWorkflow.from_tools_or_functions(
    [query_engine_tool],
    llm=llm,
    system_prompt="You are a helpful assistant that has access to a database containing persona descriptions. "
)
```

## 다중 에이전트 시스템 만들기

`AgentWorkflow` 클래스는 다중 에이전트 시스템도 직접 지원합니다. 각 에이전트에 이름과 설명을 부여함으로써, 시스템은 단일 활성 발화자를 유지하며, 각 에이전트는 다른 에이전트에게 작업을 넘길 수 있는 능력을 가집니다.

각 에이전트의 범위를 좁힘으로써, 사용자 메시지에 응답할 때 일반적인 정확도를 높이는 데 도움이 될 수 있습니다.

**LlamaIndex의 에이전트들은 더 복잡하고 사용자 정의 시나리오를 위해 다른 에이전트의 툴로도 직접 사용될 수 있습니다.**

```python
from llama_index.core.agent.workflow import (
    AgentWorkflow,
    FunctionAgent,
    ReActAgent,
)

# 몇 가지 툴 정의
def add(a: int, b: int) -> int:
    """두 숫자를 더합니다."""
    return a + b

def subtract(a: int, b: int) -> int:
    """두 숫자를 뺍니다."""
    return a - b

# 에이전트 설정 생성
# 참고: 여기서 FunctionAgent 또는 ReActAgent를 사용할 수 있습니다.
# FunctionAgent는 함수 호출 API가 있는 LLM용입니다.
# ReActAgent는 모든 LLM용입니다.
calculator_agent = ReActAgent(
    name="calculator",
    description="기본 산술 연산을 수행합니다",
    system_prompt="당신은 계산기 어시스턴트입니다. 모든 수학 연산에 툴을 사용하세요.",
    tools=[add, subtract],
    llm=llm,
)

query_agent = ReActAgent(
    name="info_lookup",
    description="XYZ에 대한 정보를 찾습니다",
    system_prompt="XYZ에 대한 정보를 답하기 위해 RAG 시스템을 쿼리하는 툴을 사용하세요",
    tools=[query_engine_tool],
    llm=llm
)

# 워크플로우 생성 및 실행
agent = AgentWorkflow(
    agents=[calculator_agent, query_agent], root_agent="calculator"
)

# 시스템 실행
response = await agent.run(user_msg="5와 3을 더할 수 있나요?")
```

아직 충분히 배우지 못했나요? AgentWorkflow 기본 소개 또는 에이전트 학습 가이드에서 LlamaIndex의 에이전트와 툴에 대해 더 많은 것을 발견할 수 있으며, 스트리밍, 맥락 직렬화, 사람 개입에 대해 더 읽을 수 있습니다!

이제 LlamaIndex에서 에이전트와 툴의 기본을 이해했으니, LlamaIndex를 사용하여 **구성 가능하고 관리 가능한 워크플로우를 만드는** 방법을 살펴보겠습니다!

### LlamaIndex 소개
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/introduction.md

# LlamaIndex 소개

[LlamaIndex](https://www.llamaindex.ai/) 툴킷을 사용하여 LLM 기반 에이전트를 구축하는 방법을 배우는 이 모듈에 오신 것을 환영합니다.

LlamaIndex는 **인덱스와 워크플로우를 사용하여 데이터 위에서 LLM 기반 에이전트를 만드는 완전한 툴킷**입니다. 이 강의에서는 LlamaIndex에서 에이전트를 구축하는 데 도움이 되는 세 가지 주요 부분에 집중할 것입니다: **구성 요소**, **에이전트와 툴**, 그리고 **워크플로우**입니다.

![LlamaIndex](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/thumbnail.png)

LlamaIndex의 이러한 핵심 부분들과 에이전트에 어떻게 도움이 되는지 살펴보겠습니다:

- **구성 요소**: LlamaIndex에서 사용하는 기본 빌딩 블록입니다. 프롬프트, 모델, 데이터베이스 등이 포함됩니다. 구성 요소는 종종 LlamaIndex를 다른 툴과 라이브러리와 연결하는 데 도움이 됩니다.
- **툴**: 검색, 계산, 외부 서비스 접근과 같은 특정 기능을 제공하는 구성 요소입니다. 에이전트가 작업을 수행할 수 있게 하는 빌딩 블록입니다.
- **에이전트**: 툴을 사용하고 결정을 내릴 수 있는 자율 구성 요소입니다. 복잡한 목표를 달성하기 위해 툴 사용을 조정합니다.
- **워크플로우**: 로직을 함께 처리하는 단계별 프로세스입니다. 워크플로우 또는 에이전트 워크플로우는 명시적인 에이전트 사용 없이 에이전트 행동을 구조화하는 방법입니다.

## LlamaIndex를 특별하게 만드는 것은 무엇인가요?

LlamaIndex가 smolagents와 같은 다른 프레임워크와 유사한 일부 작업을 수행하지만, 몇 가지 주요 이점이 있습니다:

- **명확한 워크플로우 시스템**: 워크플로우는 이벤트 기반 및 비동기 우선 구문을 사용하여 에이전트가 단계별로 어떻게 결정을 내려야 하는지 분해하는 데 도움이 됩니다. 이는 로직을 명확하게 구성하고 구성하는 데 도움이 됩니다.
- **LlamaParse를 통한 고급 문서 파싱**: LlamaParse는 LlamaIndex를 위해 특별히 만들어졌으므로 통합이 원활하지만, 유료 기능입니다.
- **많은 즉시 사용 가능한 구성 요소**: LlamaIndex는 오랫동안 존재해왔으므로 많은 다른 프레임워크와 함께 작동합니다. 이는 LLM, 검색기, 인덱스 등과 같은 많은 테스트되고 신뢰할 수 있는 구성 요소가 있음을 의미합니다.
- **LlamaHub**: LlamaIndex 내에서 사용할 수 있는 이러한 구성 요소, 에이전트, 툴의 수백 개 레지스트리입니다.

이러한 모든 개념은 유용한 에이전트를 만들기 위해 다양한 시나리오에서 필요합니다.
다음 섹션에서는 이러한 개념들을 각각 자세히 살펴볼 것입니다.
개념을 마스터한 후, 우리의 학습을 사용하여 **에이전트 알프레드와 함께 적용된 사용 사례를 만들** 것입니다!

LlamaIndex를 직접 다루는 것이 흥미롭지 않나요? 그럼 무엇을 기다리고 있나요? **LlamaHub를 사용하여 필요한 통합을 찾고 설치하는** 것으로 시작해봅시다! 🚀

### 미니 퀴즈 (채점되지 않음) [[quiz1]]
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/quiz1.md

# 미니 퀴즈 (채점되지 않음) [[quiz1]]

지금까지 LlamaIndex에서 사용되는 주요 구성 요소와 툴에 대해 논의했습니다.
**자신을 테스트하는 것**이 학습하는 가장 좋은 방법이고 [능력의 착각을 피하는](https://www.coursera.org/lecture/learning-how-to-learn/illusions-of-competence-BuFzf) 방법이므로 짧은 퀴즈를 만들어보겠습니다.
이것은 **지식을 강화해야 할 부분을 찾는** 데 도움이 될 것입니다.

이것은 선택사항 퀴즈이며 채점되지 않습니다.

### Q1: QueryEngine이란 무엇인가요?
다음 중 QueryEngine 구성 요소를 가장 잘 설명하는 것은 무엇인가요?

---

### Q2: FunctionTools의 목적은 무엇인가요?
에이전트에게 FunctionTools가 중요한 이유는 무엇인가요?

---

### Q3: LlamaIndex의 Toolspecs란 무엇인가요?
Toolspecs의 주요 목적은 무엇인가요?

---

### Q4: 툴을 만들기 위해 필요한 것은 무엇인가요?
툴을 만들 때 포함해야 하는 정보는 무엇인가요?

---

이 퀴즈를 완료하신 것을 축하합니다! 🥳 일부 요소를 놓쳤다면, 지식을 강화하기 위해 챕터를 다시 읽어보세요. 통과하셨다면, 이러한 구성 요소로 더 깊이 구축할 준비가 되었습니다!

### LlamaIndex의 구성 요소란 무엇인가요?
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/components.md

# LlamaIndex의 구성 요소란 무엇인가요?

1단원에서 나왔던 도움이 되는 집사 에이전트 알프레드를 기억하시나요?
효과적으로 우리를 도우려면, 알프레드는 우리의 요청을 이해하고 **작업을 완료하는 데 도움이 되는 관련 정보를 준비하고, 찾고, 사용**할 수 있어야 합니다.
이것이 LlamaIndex의 구성 요소가 들어오는 곳입니다.

LlamaIndex에는 많은 구성 요소가 있지만, **우리는 특별히 `QueryEngine` 구성 요소에 집중할 것입니다.**
왜냐고요? 에이전트의 검색 증강 생성(RAG) 툴로 사용할 수 있기 때문입니다.

그럼 RAG란 무엇일까요? LLM은 일반적인 지식을 배우기 위해 엄청난 양의 데이터로 훈련됩니다.
하지만 관련 있고 최신 데이터로 훈련되지 않았을 수 있습니다.
RAG는 데이터에서 관련 정보를 찾고 검색하여 LLM에게 제공함으로써 이 문제를 해결합니다.

![RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/rag.png)

이제 알프레드가 어떻게 작동하는지 생각해보세요:

1. 알프레드에게 저녁 파티 계획을 도와달라고 요청합니다
2. 알프레드는 캘린더, 식이 선호도, 과거 성공한 메뉴를 확인해야 합니다
3. `QueryEngine`이 알프레드가 이 정보를 찾고 저녁 파티 계획에 사용하도록 도와줍니다

이것이 `QueryEngine`을 LlamaIndex에서 **에이전트 RAG 워크플로우를 구축하기 위한 핵심 구성 요소**로 만듭니다.
알프레드가 도움이 되기 위해 가정 정보를 검색해야 하는 것처럼, 모든 에이전트는 관련 데이터를 찾고 이해할 수 있는 방법이 필요합니다.
`QueryEngine`은 정확히 이 기능을 제공합니다.

이제 구성 요소에 대해 조금 더 깊이 들어가서 **구성 요소를 결합하여 RAG 파이프라인을 만드는** 방법을 살펴보겠습니다.

## 구성 요소를 사용하여 RAG 파이프라인 만들기

이 노트북을 따라가며 Google Colab에서 코드를 실행해볼 수 있습니다.

RAG 내에는 5가지 주요 단계가 있으며, 이는 구축하는 대부분의 더 큰 어플리케이션의 일부가 됩니다:

1. **로딩**: 데이터가 있는 곳(텍스트 파일, PDF, 다른 웹사이트, 데이터베이스, API)에서 워크플로우로 데이터를 가져오는 것을 의미합니다. LlamaHub는 선택할 수 있는 수백 개의 통합을 제공합니다.
2. **인덱싱**: 데이터를 쿼리할 수 있는 데이터 구조를 만드는 것을 의미합니다. LLM의 경우, 이는 거의 항상 벡터 임베딩을 만드는 것을 의미합니다. 이는 데이터 의미의 수치적 표현입니다. 인덱싱은 속성을 기반으로 맥락적으로 관련된 데이터를 정확하게 찾기 쉽게 만드는 수많은 다른 메타데이터 전략을 의미할 수도 있습니다.
3. **저장**: 데이터가 인덱싱되면 다시 인덱싱할 필요가 없도록 인덱스와 다른 메타데이터를 저장하고 싶을 것입니다.
4. **쿼리**: 주어진 인덱싱 전략에 대해 하위 쿼리, 다단계 쿼리, 하이브리드 전략을 포함하여 LLM과 LlamaIndex 데이터 구조를 사용하여 쿼리할 수 있는 많은 방법이 있습니다.
5. **평가**: 모든 흐름에서 중요한 단계는 다른 전략에 비해 얼마나 효과적인지, 또는 변경사항을 만들 때 확인하는 것입니다. 평가는 쿼리에 대한 응답이 얼마나 정확하고, 신뢰할 수 있고, 빠른지에 대한 객관적인 측정을 제공합니다.

다음으로, 구성 요소를 사용하여 이러한 단계들을 재현하는 방법을 살펴보겠습니다.

### 문서 로딩 및 임베딩

앞서 언급했듯이, LlamaIndex는 자체 데이터 위에서 작동할 수 있지만, **데이터에 접근하기 전에 로드해야 합니다.**
LlamaIndex에 데이터를 로드하는 세 가지 주요 방법이 있습니다:

1. `SimpleDirectoryReader`: 로컬 디렉토리에서 다양한 파일 유형을 위한 내장 로더입니다.
2. `LlamaParse`: PDF 파싱을 위한 LlamaIndex의 공식 툴인 LlamaParse로, 관리형 API로 사용 가능합니다.
3. `LlamaHub`: 모든 소스에서 데이터를 수집하기 위한 수백 개의 데이터 로딩 라이브러리 레지스트리입니다.

더 복잡한 데이터 소스를 위해 LlamaHub 로더와 LlamaParse 파서에 익숙해지세요.

**데이터를 로드하는 가장 간단한 방법은 `SimpleDirectoryReader`를 사용하는 것입니다.**
이 다재다능한 구성 요소는 폴더에서 다양한 파일 유형을 로드하고 LlamaIndex가 작업할 수 있는 `Document` 객체로 변환할 수 있습니다.
`SimpleDirectoryReader`를 사용하여 폴더에서 데이터를 로드하는 방법을 살펴보겠습니다.

```python
from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()
```

문서를 로드한 후, `Node` 객체라는 더 작은 조각들로 나누어야 합니다.
`Node`는 AI가 작업하기 더 쉬운 원본 문서의 텍스트 청크일 뿐이며, 여전히 원본 `Document` 객체에 대한 참조를 가지고 있습니다.

`IngestionPipeline`은 두 가지 주요 변환을 통해 이러한 노드를 만드는 데 도움이 됩니다.
1. `SentenceSplitter`는 자연스러운 문장 경계에서 문서를 분할하여 관리 가능한 청크로 나눕니다.
2. `HuggingFaceEmbedding`은 각 청크를 수치적 임베딩으로 변환합니다 - AI가 효율적으로 처리할 수 있는 방식으로 의미적 의미를 포착하는 벡터 표현입니다.

이 과정은 검색과 분석에 더 유용한 방식으로 문서를 구성하는 데 도움이 됩니다.

```python
from llama_index.core import Document
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline

# 변환과 함께 파이프라인 생성
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_overlap=0),
        HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"),
    ]
)

nodes = await pipeline.arun(documents=[Document.example()])
```

### 문서 저장 및 인덱싱

`Node` 객체를 만든 후, 검색 가능하게 만들기 위해 인덱싱해야 하지만, 그 전에 데이터를 저장할 곳이 필요합니다.

수집 파이프라인을 사용하고 있으므로, 파이프라인에 벡터 스토어를 직접 연결하여 채울 수 있습니다.
이 경우, 문서를 저장하기 위해 `Chroma`를 사용할 것입니다.

ChromaDB 설치

LlamaHub 섹션에서 소개된 대로, 다음 명령어로 ChromaDB 벡터 스토어를 설치할 수 있습니다:

```bash
pip install llama-index-vector-stores-chroma
```

```python
import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore

db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"),
    ],
    vector_store=vector_store,
)
```

다양한 벡터 스토어에 대한 개요는 LlamaIndex 문서에서 찾을 수 있습니다.

이것이 벡터 임베딩이 들어오는 곳입니다 - 쿼리와 노드를 같은 벡터 공간에 임베딩함으로써 관련 매치를 찾을 수 있습니다.
`VectorStoreIndex`는 일관성을 보장하기 위해 수집 중에 사용한 것과 같은 임베딩 모델을 사용하여 이를 처리합니다.

벡터 스토어와 임베딩에서 이 인덱스를 만드는 방법을 살펴보겠습니다:

```python
from llama_index.core import VectorStoreIndex
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)
```

모든 정보는 `ChromaVectorStore` 객체와 전달된 디렉토리 경로 내에서 자동으로 지속됩니다.

훌륭합니다! 이제 인덱스를 쉽게 저장하고 로드할 수 있으니, 다양한 방법으로 쿼리하는 방법을 탐구해보겠습니다.

### 프롬프트와 LLM으로 VectorStoreIndex 쿼리하기

인덱스를 쿼리하기 전에, 쿼리 인터페이스로 변환해야 합니다. 가장 일반적인 변환 옵션은 다음과 같습니다:

- `as_retriever`: 기본 문서 검색용으로, 유사도 점수가 있는 `NodeWithScore` 객체 리스트를 반환합니다
- `as_query_engine`: 단일 질문-답변 상호작용용으로, 작성된 응답을 반환합니다
- `as_chat_engine`: 여러 메시지에 걸쳐 메모리를 유지하는 대화형 상호작용용으로, 채팅 기록과 인덱싱된 맥락을 사용하여 작성된 응답을 반환합니다

에이전트와 같은 상호작용에 더 일반적이므로 쿼리 엔진에 집중하겠습니다.
응답에 사용할 LLM도 쿼리 엔진에 전달합니다.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(
    llm=llm,
    response_mode="tree_summarize",
)
query_engine.query("인생의 의미는 무엇인가요?")
# 인생의 의미는 42입니다
```

### 응답 처리

내부적으로, 쿼리 엔진은 질문에 답하기 위해 LLM만 사용하는 것이 아니라 응답을 처리하기 위한 전략으로 `ResponseSynthesizer`도 사용합니다.
다시 한번, 이것은 완전히 사용자 정의 가능하지만 즉시 잘 작동하는 세 가지 주요 전략이 있습니다:

- `refine`: 검색된 각 텍스트 청크를 순차적으로 거쳐 답변을 생성하고 개선합니다. 이는 Node/검색된 청크당 별도의 LLM 호출을 만듭니다.
- `compact` (기본값): 개선과 유사하지만 미리 청크를 연결하여 더 적은 LLM 호출을 만듭니다.
- `tree_summarize`: 검색된 각 텍스트 청크를 거쳐 답변의 트리 구조를 만들어 상세한 답변을 생성합니다.

저수준 구성 API로 쿼리 워크플로우를 세밀하게 제어하세요. 이 API는 쿼리 프로세스의 모든 단계를 사용자 정의하고 미세 조정하여 정확한 요구사항에 맞출 수 있게 해주며, 워크플로우와도 잘 맞습니다.

언어 모델이 항상 예측 가능한 방식으로 수행되지 않을 수 있으므로, 우리가 얻는 답변이 항상 정확하다고 확신할 수 없습니다. **답변의 품질을 평가**함으로써 이를 처리할 수 있습니다.

### 평가 및 관찰 가능성

LlamaIndex는 **응답 품질을 평가하기 위한 내장 평가 도구**를 제공합니다.
이러한 평가자는 LLM을 활용하여 다양한 차원에서 응답을 분석합니다.
사용 가능한 세 가지 주요 평가자를 살펴보겠습니다:

- `FaithfulnessEvaluator`: 답변이 맥락에 의해 지원되는지 확인하여 답변의 신뢰성을 평가합니다.
- `AnswerRelevancyEvaluator`: 답변이 질문과 관련이 있는지 확인하여 답변의 관련성을 평가합니다.
- `CorrectnessEvaluator`: 답변이 정확한지 확인하여 답변의 정확성을 평가합니다.

에이전트 관찰 가능성과 평가에 대해 더 배우고 싶나요? 보너스 단원 2로 여정을 계속하세요.

```python
from llama_index.core.evaluation import FaithfulnessEvaluator

query_engine = # 이전 섹션에서
llm = # 이전 섹션에서

# 인덱스 쿼리
evaluator = FaithfulnessEvaluator(llm=llm)
response = query_engine.query(
    "미국 혁명에서 뉴욕시에서 어떤 전투가 일어났나요?"
)
eval_result = evaluator.evaluate_response(response=response)
eval_result.passing
```

직접적인 평가 없이도 **관찰 가능성을 통해 시스템이 어떻게 수행되고 있는지에 대한 통찰을 얻을 수 있습니다.**
이는 더 복잡한 워크플로우를 구축하고 각 구성 요소가 어떻게 수행되고 있는지 이해하고 싶을 때 특히 유용합니다.

LlamaTrace 설치

LlamaHub 섹션에서 소개된 대로, 다음 명령어로 Arize Phoenix의 LlamaTrace 콜백을 설치할 수 있습니다:

```bash
pip install -U llama-index-callbacks-arize-phoenix
```

또한 LlamaTrace API 키를 `PHOENIX_API_KEY` 환경 변수로 설정해야 합니다. 다음을 통해 얻을 수 있습니다:
- [LlamaTrace](https://llamatrace.com/login)에서 계정 생성
- 계정 설정에서 API 키 생성
- 아래 코드에서 추적을 활성화하기 위해 API 키 사용

```python
import llama_index
import os

PHOENIX_API_KEY = ""
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
llama_index.core.set_global_handler(
    "arize_phoenix",
    endpoint="https://llamatrace.com/v1/traces"
)
```

구성 요소와 사용 방법에 대해 더 배우고 싶나요? 구성 요소 가이드 또는 RAG 가이드로 여정을 계속하세요.

구성 요소를 사용하여 `QueryEngine`을 만드는 방법을 보았습니다. 이제 **`QueryEngine`을 에이전트의 툴로 사용하는** 방법을 살펴보겠습니다!

### LlamaHub 소개
https://huggingface.co/learn/agents-course/ko/unit2/llama-index/llama-hub.md

# LlamaHub 소개

**LlamaHub는 LlamaIndex 내에서 사용할 수 있는 수백 개의 통합, 에이전트, 툴의 레지스트리입니다.**

![LlamaHub](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/llama-hub.png)

이 강의에서 다양한 통합을 사용할 것이므로, 먼저 LlamaHub와 그것이 어떻게 우리를 도울 수 있는지 살펴보겠습니다.

필요한 구성 요소의 의존성을 찾고 설치하는 방법을 살펴보겠습니다.

## 설치

LlamaIndex 설치 지침은 [LlamaHub](https://llamahub.ai/)에서 잘 구조화된 **개요로 제공**됩니다.
처음에는 조금 압도적일 수 있지만, 대부분의 **설치 명령어는 일반적으로 기억하기 쉬운 형식**을 따릅니다:

```bash
pip install llama-index-{component-type}-{framework-name}
```

[Hugging Face 추론 API 통합](https://llamahub.ai/l/llms/llama-index-llms-huggingface-api?from=llms)을 사용하여 LLM과 임베딩 구성 요소의 의존성을 설치해보겠습니다.

```bash
pip install llama-index-llms-huggingface-api llama-index-embeddings-huggingface
```

## 사용법

설치되면 사용 패턴을 볼 수 있습니다. 가져오기 경로가 설치 명령어를 따르는 것을 알 수 있을 것입니다!
아래에서 **LLM 구성 요소를 위한 Hugging Face 추론 API 사용**의 예를 볼 수 있습니다.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
import os
from dotenv import load_dotenv

# .env 파일 로드
load_dotenv()

# 환경 변수에서 HF_TOKEN 검색
hf_token = os.getenv("HF_TOKEN")

llm = HuggingFaceInferenceAPI(
    model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
    temperature=0.7,
    max_tokens=100,
    token=hf_token,
)

response = llm.complete("안녕하세요, 어떻게 지내시나요?")
print(response)
# 잘 지내고 있습니다, 오늘 어떻게 도와드릴까요?
```

훌륭합니다, 이제 필요한 구성 요소의 통합을 찾고, 설치하고, 사용하는 방법을 알게 되었습니다.
**구성 요소에 대해 더 깊이 들어가서** 우리만의 에이전트를 구축하는 데 어떻게 사용할 수 있는지 살펴보겠습니다.

### 멀티 에이전트 시스템
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/multi_agent_systems.md

# 멀티 에이전트 시스템

멀티 에이전트 시스템은 **특화된 에이전트들이 협력하여 복잡한 작업을 해결**할 수 있게 해주며, 모듈성, 확장성, 견고성을 높여줍니다. 단일 에이전트에 의존하는 대신, 각기 다른 능력을 가진 에이전트들이 작업을 분담합니다.

**smolagents**에서는 다양한 에이전트를 결합해 파이썬 코드 생성, 외부 도구 호출, 웹 검색 등 다양한 작업을 수행할 수 있습니다. 이러한 에이전트들을 조율함으로써 강력한 워크플로우를 만들 수 있습니다.

일반적인 구성 예시:
- **매니저 에이전트**: 작업 분배 담당
- **코드 인터프리터 에이전트**: 코드 실행 담당
- **웹 검색 에이전트**: 정보 검색 담당

아래 다이어그램은 **매니저 에이전트**가 **코드 인터프리터 도구**와 **웹 검색 에이전트**를 조율하는 간단한 멀티 에이전트 아키텍처를 보여줍니다. 웹 검색 에이전트는 `DuckDuckGoSearchTool`과 `VisitWebpageTool` 같은 도구를 활용해 정보를 수집합니다.

## 멀티 에이전트 시스템 실전 예시

멀티 에이전트 시스템은 여러 특화 에이전트가 **오케스트레이터 에이전트**의 조율 아래 협력하는 구조입니다. 이 방식은 역할이 다른 에이전트들에게 작업을 분산시켜 복잡한 워크플로우를 구현할 수 있게 해줍니다.

예를 들어, **멀티 에이전트 RAG 시스템**은 다음을 통합할 수 있습니다:
- **웹 에이전트**: 인터넷 브라우징 담당
- **리트리버 에이전트**: 지식 베이스 정보 검색 담당
- **이미지 생성 에이전트**: 시각 자료 생성 담당

이 모든 에이전트는 오케스트레이터가 작업 분배와 상호작용을 관리합니다.

## 멀티 에이전트 계층 구조로 복잡한 문제 해결하기

이 노트북을 따라가며 코드를 직접 실행해볼 수 있습니다.

리셉션이 다가오고 있습니다! 여러분의 도움으로 Alfred는 거의 모든 준비를 마쳤습니다.

하지만 문제가 생겼습니다: 배트모빌이 사라졌습니다. Alfred는 대체 차량을 빨리 찾아야 합니다.

다행히 브루스 웨인의 삶을 다룬 바이오픽 영화가 몇 편 제작되었으니, Alfred는 영화 세트장에 남겨진 차량을 찾아 현대식으로 개조할 수 있을지도 모릅니다. 물론 자율주행 기능도 필수겠죠.

하지만 이 차량들은 전 세계 촬영지 어디에나 있을 수 있습니다.

그래서 Alfred는 여러분의 도움이 필요합니다. 이 문제를 해결할 수 있는 에이전트를 만들어볼까요?

> 👉 전 세계 배트맨 촬영지를 모두 찾아, 고담(40.7128° N, 74.0060° W)까지 화물선(비행기)으로 이동하는 데 걸리는 시간을 계산하고, 이를 지도에 표시하세요. 이동 시간에 따라 색상을 다르게 하고, 슈퍼카 공장도 같은 방식으로 표시하세요.

함께 만들어봅시다!

이 예시에는 추가 패키지가 필요하니 먼저 설치해 주세요:

```bash
pip install 'smolagents[litellm]' plotly geopandas shapely kaleido -q
```

### 먼저 화물기 이동 시간 계산 도구를 만듭니다.

```python
import math
from typing import Optional, Tuple

from smolagents import tool

@tool
def calculate_cargo_travel_time(
    origin_coords: Tuple[float, float],
    destination_coords: Tuple[float, float],
    cruising_speed_kmh: Optional[float] = 750.0,  # 화물기 평균 속도
) -> float:
    """
    두 지점(위도, 경도) 간 화물기 이동 시간을 대권거리로 계산합니다.

    Args:
        origin_coords: 출발지 (위도, 경도) 튜플
        destination_coords: 도착지 (위도, 경도) 튜플
        cruising_speed_kmh: 순항 속도(km/h, 기본값 750)

    Returns:
        float: 예상 이동 시간(시간 단위)

    Example:
        >>> # 시카고(41.8781° N, 87.6298° W) → 시드니(33.8688° S, 151.2093° E)
        >>> result = calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093))
    """

    def to_radians(degrees: float) -> float:
        return degrees * (math.pi / 180)

    # 좌표 변환
    lat1, lon1 = map(to_radians, origin_coords)
    lat2, lon2 = map(to_radians, destination_coords)

    # 지구 반지름(km)
    EARTH_RADIUS_KM = 6371.0

    # haversine 공식으로 대권거리 계산
    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = (
        math.sin(dlat / 2) ** 2
        + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
    )
    c = 2 * math.asin(math.sqrt(a))
    distance = EARTH_RADIUS_KM * c

    # 우회 경로, 관제 등 감안해 10% 추가
    actual_distance = distance * 1.1

    # 비행 시간 계산(이착륙 1시간 추가)
    flight_time = (actual_distance / cruising_speed_kmh) + 1.0

    return round(flight_time, 2)

print(calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093)))
```

### 에이전트 설정하기

모델 제공자는 Together AI를 사용합니다. GoogleSearchTool은 [Serper API](https://serper.dev)를 사용하므로, 환경 변수 `SERPAPI_API_KEY`(provider="serpapi") 또는 `SERPER_API_KEY`(provider=serper)를 설정해야 합니다.

Serp API가 없다면 `DuckDuckGoSearchTool`을 사용할 수 있지만, 속도 제한에 유의하세요.

```python
import os
from PIL import Image
from smolagents import CodeAgent, GoogleSearchTool, InferenceClientModel, VisitWebpageTool

model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct", provider="together")
```

간단한 에이전트를 만들어 기본 리포트를 받아봅시다.

```python
task = """전 세계 배트맨 촬영지를 모두 찾아, 고담(40.7128° N, 74.0060° W)까지 화물기로 이동하는 시간을 계산해 pandas 데이터프레임으로 반환하세요. 슈퍼카 공장도 같은 방식으로 포함하세요."""
```

```python
agent = CodeAgent(
    model=model,
    tools=[GoogleSearchTool("serper"), VisitWebpageTool(), calculate_cargo_travel_time],
    additional_authorized_imports=["pandas"],
    max_steps=20,
)
```

```python
result = agent.run(task)
```

```python
result
```

실행 결과 예시:

```python
|  | Location                                             | Travel Time to Gotham (hours) |
|--|------------------------------------------------------|------------------------------|
| 0  | Necropolis Cemetery, Glasgow, Scotland, UK         | 8.60                         |
| 1  | St. George's Hall, Liverpool, England, UK         | 8.81                         |
| 2  | Two Temple Place, London, England, UK             | 9.17                         |
| 3  | Wollaton Hall, Nottingham, England, UK           | 9.00                         |
| 4  | Knebworth House, Knebworth, Hertfordshire, UK    | 9.15                         |
| 5  | Acton Lane Power Station, Acton Lane, Acton, UK  | 9.16                         |
| 6  | Queensboro Bridge, New York City, USA            | 1.01                         |
| 7  | Wall Street, New York City, USA                  | 1.00                         |
| 8  | Mehrangarh Fort, Jodhpur, Rajasthan, India       | 18.34                        |
| 9  | Turda Gorge, Turda, Romania                      | 11.89                        |
| 10 | Chicago, USA                                     | 2.68                         |
| 11 | Hong Kong, China                                 | 19.99                        |
| 12 | Cardington Studios, Northamptonshire, UK        | 9.10                         |
| 13 | Warner Bros. Leavesden Studios, Hertfordshire, UK | 9.13                         |
| 14 | Westwood, Los Angeles, CA, USA                  | 6.79                         |
| 15 | Woking, UK (McLaren)                             | 9.13                         |
```

계획 단계(planning step)를 추가하고 프롬프트를 보강하면 더 나은 결과를 얻을 수 있습니다.

계획 단계는 에이전트가 다음 단계를 미리 생각하고 계획할 수 있게 해줍니다.

```python
agent.planning_interval = 4

detailed_report = agent.run(f"""
여러분은 전문 분석가입니다. 여러 웹사이트를 방문해 종합 리포트를 작성합니다.
for 루프를 활용해 여러 쿼리를 한 번에 검색하세요.
각 데이터 포인트마다 소스 URL을 방문해 수치를 확인하세요.

{task}
""")

print(detailed_report)
```

```python
detailed_report
```

실행 결과 예시:

```python
|  | Location                                         | Travel Time (hours) |
|--|--------------------------------------------------|---------------------|
| 0  | Bridge of Sighs, Glasgow Necropolis, Glasgow, UK | 8.6                 |
| 1  | Wishart Street, Glasgow, Scotland, UK         | 8.6                 |
```

이처럼 프롬프트와 계획 기능만으로도 훨씬 간결한 리포트를 얻을 수 있습니다!

모델의 컨텍스트 윈도우가 빠르게 채워집니다. **따라서 상세 검색 결과와 다른 결과를 결합하도록 요청하면 느려지고 토큰·비용이 급증할 수 있습니다.**

➡️ 시스템 구조를 개선해야 합니다.

### ✌️ 두 에이전트로 작업 분할하기

멀티 에이전트 구조는 서로 다른 하위 작업의 메모리를 분리해 두 가지 큰 이점을 제공합니다:
- 각 에이전트가 핵심 작업에 집중해 성능이 향상됩니다.
- 메모리 분리로 각 단계의 입력 토큰 수가 줄어, 지연과 비용이 감소합니다.

웹 검색 전담 에이전트와 이를 관리하는 매니저 에이전트로 팀을 만들어봅시다.

매니저 에이전트는 최종 리포트 작성을 위해 추가 import(`plotly`, `geopandas`, `shapely`)가 필요합니다.

```python
model = InferenceClientModel(
    "Qwen/Qwen2.5-Coder-32B-Instruct", provider="together", max_tokens=8096
)

web_agent = CodeAgent(
    model=model,
    tools=[
        GoogleSearchTool(provider="serper"),
        VisitWebpageTool(),
        calculate_cargo_travel_time,
    ],
    name="web_agent",
    description="웹에서 정보를 찾는 역할",
    verbosity_level=0,
    max_steps=10,
)
```

매니저 에이전트는 더 강력한 모델 [DeepSeek-R1](https://huggingface.co/deepseek-ai/DeepSeek-R1)을 사용하고, `planning_interval`도 추가합니다.

```python
from smolagents.utils import encode_image_base64, make_image_url
from smolagents import OpenAIServerModel

def check_reasoning_and_plot(final_answer, agent_memory):
    multimodal_model = OpenAIServerModel("gpt-4o", max_tokens=8096)
    filepath = "saved_map.png"
    assert os.path.exists(filepath), "saved_map.png 파일이 저장되어야 합니다!"
    image = Image.open(filepath)
    prompt = (
        f"다음은 사용자 요청과 에이전트 단계입니다: {agent_memory.get_succinct_steps()}. 아래는 생성된 지도입니다."
        "추론 과정과 지도가 요청을 올바르게 해결하는지 확인하세요."
        "먼저 이유를 나열한 뒤, 최종 결정(PASS/FAIL)을 작성하세요."
        "너무 엄격할 필요는 없습니다. 대부분 해결했다면 PASS입니다."
        "지도는 px.scatter_map으로 만들어야 PASS입니다."
    )
    messages = [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": prompt,
                },
                {
                    "type": "image_url",
                    "image_url": {"url": make_image_url(encode_image_base64(image))},
                },
            ],
        }
    ]
    output = multimodal_model(messages).content
    print("피드백: ", output)
    if "FAIL" in output:
        raise Exception(output)
    return True

manager_agent = CodeAgent(
    model=InferenceClientModel("deepseek-ai/DeepSeek-R1", provider="together", max_tokens=8096),
    tools=[calculate_cargo_travel_time],
    managed_agents=[web_agent],
    additional_authorized_imports=[
        "geopandas",
        "plotly",
        "shapely",
        "json",
        "pandas",
        "numpy",
    ],
    planning_interval=5,
    verbosity_level=2,
    final_answer_checks=[check_reasoning_and_plot],
    max_steps=15,
)
```

이 팀의 구조를 시각화해봅시다:

```python
manager_agent.visualize()
```

아래와 같이 구조와 도구 사용 관계를 한눈에 볼 수 있습니다:

```python
CodeAgent | deepseek-ai/DeepSeek-R1
├── ✅ Authorized imports: ['geopandas', 'plotly', 'shapely', 'json', 'pandas', 'numpy']
├── 🛠️ Tools:
│   ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
│   ┃ Name                        ┃ Description                           ┃ Arguments                             ┃
│   ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│   │ calculate_cargo_travel_time │ 두 지점 간 화물기 이동 시간 계산         │ origin_coords (`array`): 출발지 좌표   │
│   │                             │                                       │ destination_coords (`array`): 도착지   │
│   │                             │                                       │ cruising_speed_kmh (`number`): 속도    │
│   │ final_answer                │ 문제에 대한 최종 답변 제공              │ answer (`any`): 최종 답변              │
│   └─────────────────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
└── 🤖 Managed agents:
    └── web_agent | CodeAgent | Qwen/Qwen2.5-Coder-32B-Instruct
        ├── ✅ Authorized imports: []
        ├── 📝 Description: 웹에서 정보를 찾는 역할
        └── 🛠️ Tools:
            ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
            ┃ Name                        ┃ Description                       ┃ Arguments                         ┃
            ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
            │ web_search                  │ 구글 웹 검색 수행                  │ query (`string`): 검색어           │
            │                             │                                   │ filter_year (`integer`): 연도 제한 │
            │ visit_webpage               │ 웹페이지 방문 및 내용 읽기         │ url (`string`): 방문할 URL         │
            │ calculate_cargo_travel_time │ 두 지점 간 화물기 이동 시간 계산   │ origin_coords (`array`): 출발지     │
            │                             │                                   │ destination_coords (`array`): 도착지│
            │                             │                                   │ cruising_speed_kmh (`number`): 속도 │
            │ final_answer                │ 문제에 대한 최종 답변 제공         │ answer (`any`): 최종 답변           │
            └─────────────────────────────┴───────────────────────────────────┴───────────────────────────────────┘
```

```python
manager_agent.run("""
전 세계 배트맨 촬영지를 모두 찾아, 고담(40.7128° N, 74.0060° W)까지 화물기로 이동하는 시간을 계산하세요. 슈퍼카 공장도 같은 방식으로 포함해 총 6개 이상 지점을 지도에 산점도로 표시하고, 이동 시간에 따라 색상을 다르게 하세요. 결과 이미지는 saved_map.png로 저장하세요!

지도 예시:
import plotly.express as px
df = px.data.carshare()
fig = px.scatter_map(df, lat="centroid_lat", lon="centroid_lon", text="name", color="peak_hour", size=100,
     color_continuous_scale=px.colors.sequential.Magma, size_max=15, zoom=1)
fig.show()
fig.write_image("saved_image.png")
final_answer(fig)

문자열을 코드로 처리하지 마세요. 문자열이 있으면 print로 출력하세요.
""")
```

제 실행에서는 매니저 에이전트가 웹 에이전트에게 `1. 배트맨 촬영지 검색`, `2. 슈퍼카 공장 찾기`로 작업을 분배한 뒤, 결과를 집계해 지도를 그렸습니다.

에이전트 상태에서 지도를 직접 확인해봅시다:

```python
manager_agent.python_executor.state["fig"]
```

아래와 같이 지도가 출력됩니다:

![멀티에이전트 시스템 예시 출력 지도](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/smolagents/output_map.png)

## 참고 자료

- [멀티 에이전트 시스템](https://huggingface.co/docs/smolagents/main/en/examples/multiagents) – 멀티 에이전트 시스템 개요
- [Agentic RAG란?](https://weaviate.io/blog/what-is-agentic-rag) – Agentic RAG 소개
- [Multi-Agent RAG System 🤖🤝🤖 레시피](https://huggingface.co/learn/cookbook/multiagent_rag_system) – 단계별 구축 가이드

### smolagents로 비전 에이전트 만들기
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/vision_agents.md

# smolagents로 비전 에이전트 만들기

이 섹션의 예제는 강력한 VLM(비전-언어 모델) API가 필요합니다. 우리는 GPT-4o API로 테스트했습니다.  
하지만 Why use smolagents에서는 smolagents와 Hugging Face에서 지원하는 대체 솔루션도 다루고 있습니다. 다른 옵션을 탐색하고 싶다면 해당 섹션을 참고하세요.

에이전트에 시각적 능력을 부여하는 것은 텍스트 처리 이상의 실제 문제를 해결하는 데 매우 중요합니다. 웹 브라우징이나 문서 이해 등 많은 실제 과제는 풍부한 시각 정보를 분석해야 합니다. 다행히도, `smolagents`는 비전-언어 모델(VLM)을 내장 지원하여 에이전트가 이미지를 효과적으로 처리하고 해석할 수 있게 해줍니다.  

이 예시에서, Wayne Manor의 집사 Alfred는 파티에 참석하는 손님들의 신원을 확인하는 임무를 맡았습니다. Alfred는 모든 손님을 알지 못할 수 있으므로, 손님의 외모에 대한 시각 정보를 검색해 신원을 확인하는 에이전트를 사용할 수 있습니다. 이를 통해 Alfred는 입장 허가 여부를 현명하게 결정할 수 있습니다. 예시를 함께 만들어봅시다!

## 에이전트 실행 시작 시 이미지 제공하기

코드는 이 노트북에서 따라할 수 있습니다. Google Colab에서 실행할 수 있습니다.

이 방식에서는 이미지를 에이전트 실행 시점에 `task_images`로 전달합니다. 에이전트는 이 이미지를 전체 실행 과정에서 활용합니다.  

예를 들어, Alfred는 이전 파티에서 손님들의 이름과 함께 저장된 이미지 데이터셋을 가지고 있습니다. 새로운 방문객의 이미지를 받아, 기존 데이터셋과 비교해 입장 허가 여부를 결정할 수 있습니다.  

이번에는 한 손님이 입장하려고 하는데, Alfred는 이 손님이 Wonder Woman으로 위장한 Joker일 수 있다고 의심합니다. Alfred는 신원을 확인해 원치 않는 인물이 입장하지 못하도록 해야 합니다.  

예시를 만들어봅시다. 우선 이미지를 불러옵니다. 여기서는 위키피디아 이미지를 사용하지만, 실제로는 다양한 활용이 가능합니다!

```python
from PIL import Image
import requests
from io import BytesIO

image_urls = [
    "https://upload.wikimedia.org/wikipedia/commons/e/e8/The_Joker_at_Wax_Museum_Plus.jpg", # Joker 이미지
    "https://upload.wikimedia.org/wikipedia/en/9/98/Joker_%28DC_Comics_character%29.jpg" # Joker 이미지
]

images = []
for url in image_urls:
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 
    }
    response = requests.get(url,headers=headers)
    image = Image.open(BytesIO(response.content)).convert("RGB")
    images.append(image)
```

이제 이미지를 준비했으니, 에이전트가 손님이 Wonder Woman인지 Joker인지 판별해줍니다.

```python
from smolagents import CodeAgent, OpenAIServerModel

model = OpenAIServerModel(model_id="gpt-4o")

# 에이전트 인스턴스화
agent = CodeAgent(
    tools=[],
    model=model,
    max_steps=20,
    verbosity_level=2
)

response = agent.run(
    """
    이 사진 속 만화 캐릭터의 의상과 메이크업을 묘사해 주세요.
    손님이 The Joker인지 Wonder Woman인지 알려주세요.
    """,
    images=images
)
```

실행 결과는 다음과 같습니다(실행 환경에 따라 다를 수 있습니다):

```python
    {
        'Costume and Makeup - First Image': (
            '보라색 코트와 노란색 셔츠 위에 보라색 실크 크라바트 또는 타이.',
            '흰색 얼굴 분장, 과장된 이목구비, 진한 눈썹, 파란 아이섀도, 빨간 입술로 넓은 미소.'
        ),
        'Costume and Makeup - Second Image': (
            '꽃이 달린 어두운 정장, 손에 트럼프 카드를 들고 있음.',
            '창백한 피부, 초록색 머리, 과장된 미소의 빨간 입술.'
        ),
        'Character Identity': '이 캐릭터는 만화에서 묘사된 The Joker와 유사합니다.'
    }
```

이처럼, 결과를 통해 손님이 다른 인물로 위장했음을 알 수 있으므로, Alfred는 Joker의 입장을 막을 수 있습니다!

## 동적 검색으로 이미지 제공하기

코드는 이 Python 파일에서 따라할 수 있습니다.

이전 방식도 유용하지만, 데이터베이스에 손님이 없는 경우에는 외부에서 이미지를 동적으로 검색해야 할 수 있습니다. 예를 들어, 웹을 탐색하며 손님에 대한 이미지를 찾아 신원을 확인하는 방식입니다.

이 방식에서는 에이전트 실행 중에 이미지를 동적으로 메모리에 추가합니다. smolagents의 에이전트는 `MultiStepAgent` 클래스를 기반으로 하며, 이는 ReAct 프레임워크의 추상화입니다. 이 클래스는 다양한 변수와 지식을 여러 단계에 걸쳐 기록하는 구조적 사이클로 동작합니다:

1. **SystemPromptStep:** 시스템 프롬프트 저장
2. **TaskStep:** 사용자 쿼리 및 입력 기록
3. **ActionStep:** 에이전트의 액션 및 결과 로그 기록

이 구조적 접근법 덕분에, 에이전트는 시각 정보를 동적으로 통합하고, 변화하는 작업에 적응적으로 대응할 수 있습니다. 아래 다이어그램은 이러한 동적 워크플로우와 각 단계가 에이전트 라이프사이클에서 어떻게 통합되는지 보여줍니다. 브라우징 중에는 에이전트가 스크린샷을 찍어 `observation_images`에 저장할 수 있습니다.

![Dynamic image retrieval](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/smolagents-can-see/diagram_adding_vlms_smolagents.png)

이제 필요성을 이해했으니, 전체 예시를 만들어봅시다. 이번에는 Alfred가 손님 신원 확인 과정을 완전히 통제하고 싶어 하므로, 웹을 탐색하며 정보를 찾는 것이 적합합니다. 이를 위해 Selenium과 Helium 등 브라우저 자동화 도구가 필요합니다. 아래 명령어로 필요한 패키지를 설치하세요:

```bash
pip install "smolagents[all]" helium selenium python-dotenv
```

에이전트에는 웹 브라우징을 위한 `search_item_ctrl_f`, `go_back`, `close_popups` 등 도구가 필요합니다. 이 도구들은 실제 사용자가 웹을 탐색하는 것처럼 동작하게 해줍니다.

```python
@tool
def search_item_ctrl_f(text: str, nth_result: int = 1) -> str:
    """
    현재 페이지에서 Ctrl + F로 텍스트를 검색하고 n번째 결과로 이동합니다.
    Args:
        text: 검색할 텍스트
        nth_result: 몇 번째 결과로 이동할지(기본값: 1)
    """
    elements = driver.find_elements(By.XPATH, f"//*[contains(text(), '{text}')]")
    if nth_result > len(elements):
        raise Exception(f"{nth_result}번째 결과를 찾을 수 없습니다. (총 {len(elements)}개 일치)")
    result = f"'{text}'에 대해 {len(elements)}개 일치."
    elem = elements[nth_result - 1]
    driver.execute_script("arguments[0].scrollIntoView(true);", elem)
    result += f"{len(elements)}개 중 {nth_result}번째 요소로 포커스 이동"
    return result

@tool
def go_back() -> None:
    """이전 페이지로 이동합니다."""
    driver.back()

@tool
def close_popups() -> str:
    """
    페이지의 모달 또는 팝업을 닫습니다. 팝업 창을 닫을 때 사용하세요! 쿠키 배너에는 동작하지 않습니다.
    """
    webdriver.ActionChains(driver).send_keys(Keys.ESCAPE).perform()
```

또한, 스크린샷 저장 기능도 필요합니다. 이 기능은 브라우저의 스크린샷을 찍어 `step_log.observations_images = [image.copy()]`에 저장합니다. 이를 통해 에이전트가 탐색 중 이미지를 동적으로 저장하고 활용할 수 있습니다.

```python
def save_screenshot(step_log: ActionStep, agent: CodeAgent) -> None:
    sleep(1.0)  # 자바스크립트 애니메이션 대기
    driver = helium.get_driver()
    current_step = step_log.step_number
    if driver is not None:
        for step_logs in agent.logs:  # 이전 스크린샷 정리
            if isinstance(step_log, ActionStep) and step_log.step_number 

최종 출력 예시는 다음과 같습니다:

```python
Final answer: Wonder Woman은 일반적으로 빨간색과 금색의 상의, 파란색 별무늬 하의, 금색 티아라, 은색 팔찌, 황금 올가미를 착용한 모습으로 묘사됩니다. 그녀는 Themyscira의 공주이자, 인간 세계에서는 Diana Prince로 알려져 있습니다.
```

이렇게 해서, 파티 입장 손님 신원 확인을 위한 비전 에이전트를 완성했습니다! 이제 Alfred는 올바른 손님만 입장할 수 있도록 완벽하게 준비할 수 있습니다. Wayne Manor에서 멋진 파티를 즐기세요!

## 추가 자료

- [We just gave sight to smolagents](https://huggingface.co/blog/smolagents-can-see) - 비전 에이전트 기능을 소개하는 블로그
- [Web Browser Automation with Agents 🤖🌐](https://huggingface.co/docs/smolagents/examples/web_browser) - 웹 브라우저를 활용한 에이전트 예시
- [Web Browser Vision Agent Example](https://github.com/huggingface/smolagents/blob/main/src/smolagents/vision_web_browser.py) - 웹 브라우저 비전 에이전트 예시

### 에이전틱 RAG 시스템 구축하기
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/retrieval_agents.md

# 에이전틱 RAG 시스템 구축하기

코드는 이 노트북에서 따라할 수 있습니다. Google Colab에서 실행할 수 있습니다.

Retrieval Augmented Generation(RAG) 시스템은 데이터 검색과 생성 모델의 능력을 결합하여 상황에 맞는 답변을 제공합니다. 예를 들어, 사용자의 질문이 검색 엔진에 전달되고, 검색된 결과가 질문과 함께 모델에 제공됩니다. 모델은 질문과 검색된 정보를 바탕으로 답변을 생성합니다.

에이전틱 RAG(검색 증강 생성)는 **자율 에이전트와 동적 지식 검색**을 결합하여 기존 RAG 시스템을 확장합니다.

기존 RAG 시스템은 LLM이 검색된 데이터를 바탕으로 답변을 생성하지만, 에이전틱 RAG는 **검색과 생성 과정을 지능적으로 제어**하여 효율성과 정확성을 높입니다.

기존 RAG 시스템의 주요 한계는 **단일 검색 단계에 의존**하고, 사용자의 질문과 직접적인 의미적 유사성에만 집중하여 관련 정보를 놓칠 수 있다는 점입니다.

에이전틱 RAG는 에이전트가 검색 쿼리를 자율적으로 생성하고, 검색 결과를 비판적으로 평가하며, 여러 번의 검색 단계를 거쳐 더 맞춤화되고 포괄적인 출력을 생성할 수 있도록 합니다.

## DuckDuckGo를 활용한 기본 검색

이제 DuckDuckGo를 사용해 웹을 검색하는 간단한 에이전트를 만들어봅시다. 이 에이전트는 정보를 검색하고, 답변을 종합하여 질문에 답합니다. 에이전틱 RAG를 활용하면 Alfred의 에이전트는 다음과 같은 일을 할 수 있습니다:

* 최신 슈퍼히어로 파티 트렌드 검색
* 결과를 고급스러운 요소로 세분화
* 정보를 종합하여 완성된 계획 제안

Alfred의 에이전트가 이를 어떻게 수행하는지 살펴봅시다:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

# 검색 도구 초기화
search_tool = DuckDuckGoSearchTool()

# 모델 초기화
model = InferenceClientModel()

agent = CodeAgent(
    model=model,
    tools=[search_tool],
)

# 예시 사용법
response = agent.run(
    "Search for luxury superhero-themed party ideas, including decorations, entertainment, and catering."
)
print(response)
```

에이전트의 동작 과정:

1. **요청 분석:** Alfred의 에이전트는 쿼리의 핵심 요소(고급 슈퍼히어로 테마 파티, 장식, 엔터테인먼트, 케이터링 등)를 파악합니다.
2. **검색 수행:** DuckDuckGo를 활용해 Alfred의 고급스러운 취향에 맞는 최신 정보를 검색합니다.
3. **정보 종합:** 검색 결과를 바탕으로 파티의 모든 측면을 아우르는 실행 가능한 계획을 만듭니다.
4. **정보 저장:** 검색된 정보를 저장해, 이후 파티 준비 시 효율적으로 재활용할 수 있습니다.

## 맞춤형 지식 베이스 도구

특정 작업에는 맞춤형 지식 베이스가 매우 유용합니다. 벡터 데이터베이스에 저장된 기술 문서나 전문 지식을 쿼리하는 도구를 만들어봅시다. 의미적 검색을 통해 Alfred의 요구에 가장 적합한 정보를 찾을 수 있습니다.

벡터 데이터베이스는 텍스트나 기타 데이터를 머신러닝 모델로 임베딩(숫자 벡터)하여 저장합니다. 이를 통해 의미적으로 유사한 정보를 고차원 공간에서 찾을 수 있습니다.

이 접근법은 사전 정의된 지식과 의미적 검색을 결합해 상황에 맞는 솔루션을 제공합니다. Alfred는 전문 지식에 접근해 파티의 모든 세부 사항을 완벽하게 준비할 수 있습니다.

아래 예시에서는 BM25 검색기를 사용해 맞춤형 지식 베이스에서 파티 아이디어를 검색하는 도구를 만듭니다. `RecursiveCharacterTextSplitter`로 문서를 작은 청크로 나눠 검색 효율을 높입니다.

```python
from langchain.docstore.document import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from smolagents import Tool
from langchain_community.retrievers import BM25Retriever
from smolagents import CodeAgent, InferenceClientModel

class PartyPlanningRetrieverTool(Tool):
    name = "party_planning_retriever"
    description = "Alfred의 슈퍼히어로 테마 파티를 위한 아이디어를 의미적 검색으로 찾아줍니다."
    inputs = {
        "query": {
            "type": "string",
            "description": "파티 준비나 슈퍼히어로 테마와 관련된 쿼리입니다.",
        }
    }
    output_type = "string"

    def __init__(self, docs, **kwargs):
        super().__init__(**kwargs)
        self.retriever = BM25Retriever.from_documents(
            docs, k=5  # 상위 5개 문서 검색
        )

    def forward(self, query: str) -> str:
        assert isinstance(query, str), "검색 쿼리는 문자열이어야 합니다."

        docs = self.retriever.invoke(
            query,
        )
        return "\n검색된 아이디어:\n" + "".join(
            [
                f"\n\n===== 아이디어 {str(i)} =====\n" + doc.page_content
                for i, doc in enumerate(docs)
            ]
        )

# 파티 준비에 관한 지식 베이스 예시
party_ideas = [
    {"text": "고급 장식(금색 장식, 벨벳 커튼 등)이 있는 슈퍼히어로 테마 가면무도회.", "source": "Party Ideas 1"},
    {"text": "배트맨, 원더우먼 등 슈퍼히어로 테마 음악을 연주할 전문 DJ 고용.", "source": "Entertainment Ideas"},
    {"text": "케이터링 메뉴에 슈퍼히어로 이름을 붙인 요리 제공(예: '헐크의 그린 스무디', '아이언맨의 파워 스테이크').", "source": "Catering Ideas"},
    {"text": "장소 곳곳에 슈퍼히어로 로고와 고담 등 도시 프로젝션 장식.", "source": "Decoration Ideas"},
    {"text": "VR을 활용한 슈퍼히어로 시뮬레이션, 테마 게임 등 인터랙티브 체험 제공.", "source": "Entertainment Ideas"}
]

source_docs = [
    Document(page_content=doc["text"], metadata={"source": doc["source"]})
    for doc in party_ideas
]

# 문서를 작은 청크로 분할
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    add_start_index=True,
    strip_whitespace=True,
    separators=["\n\n", "\n", ".", " ", ""],
)
docs_processed = text_splitter.split_documents(source_docs)

# 검색 도구 생성
party_planning_retriever = PartyPlanningRetrieverTool(docs_processed)

# 에이전트 초기화
agent = CodeAgent(tools=[party_planning_retriever], model=InferenceClientModel())

# 예시 사용법
response = agent.run(
    "Find ideas for a luxury superhero-themed party, including entertainment, catering, and decoration options."
)

print(response)
```

이 향상된 에이전트는 다음을 수행할 수 있습니다:
1. 먼저 문서에서 관련 정보 검색
2. 지식 베이스의 인사이트 결합
3. 대화 맥락을 메모리에 유지

## 고도화된 검색 기능

에이전틱 RAG 시스템을 구축할 때, 에이전트는 다음과 같은 고급 전략을 사용할 수 있습니다:

1. **쿼리 재구성(Query Reformulation):** 원본 쿼리 대신, 문서에 더 잘 맞는 최적화된 검색어 생성
2. **쿼리 분해(Query Decomposition):** 여러 정보를 포함한 쿼리를 분해해 각각 검색
3. **쿼리 확장(Query Expansion):** 쿼리를 다양한 표현으로 확장해 여러 방식으로 검색
4. **재정렬(Reranking):** Cross-Encoder 등으로 검색 결과와 쿼리의 의미적 관련성 점수 부여
5. **다단계 검색(Multi-Step Retrieval):** 초기 결과를 바탕으로 추가 검색 반복
6. **소스 통합(Source Integration):** 웹 검색, 로컬 문서 등 다양한 소스 결합
7. **결과 검증(Result Validation):** 검색된 내용의 적합성과 정확성 평가 후 답변에 포함

효과적인 에이전틱 RAG 시스템을 위해서는 여러 측면을 신중히 고려해야 합니다. 에이전트는 **쿼리 유형과 맥락에 따라 적절한 도구를 선택**해야 하며, 메모리 시스템을 통해 대화 이력을 관리하고 중복 검색을 방지해야 합니다. 또한, 예비 전략을 마련해 주요 검색 방법이 실패해도 가치를 제공할 수 있어야 하며, 검증 단계를 통해 검색 결과의 정확성과 적합성을 보장해야 합니다.

## 참고 자료

- [Agentic RAG: turbocharge your RAG with query reformulation and self-query! 🚀](https://huggingface.co/learn/cookbook/agent_rag) - smolagents로 에이전틱 RAG 시스템을 개발하는 레시피

### 소규모 퀴즈(비채점) [[quiz2]]
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/quiz2.md

# 소규모 퀴즈(비채점) [[quiz2]]

*Code Agents*, *Tool Calling Agents*, *Tools* 섹션에 대한 이해도를 테스트해봅시다. 이 퀴즈는 선택 사항이며 점수화되지 않습니다.

---

### Q1: smolagents에서 `@tool` 데코레이터로 도구를 만드는 것과 `Tool` 서브클래스를 만드는 것의 핵심 차이는?
두 방식의 도구 정의 차이를 가장 잘 설명하는 문장을 고르세요.

@tool 데코레이터는 리트리벌 기반 도구에만 필수이고, Tool 서브클래스는 텍스트 생성 작업에만 사용된다",
    explain: "두 방식 모두 어떤 유형의 도구에도 사용할 수 있습니다.",
  },
  {
    text: "@tool 데코레이터는 간단한 함수형 도구에 권장되고, Tool 서브클래스는 복잡한 기능이나 커스텀 메타데이터가 필요할 때 더 유연하다",
    explain: "정답입니다. 데코레이터 방식이 더 간단하고, 서브클래싱은 맞춤 동작에 적합합니다.",
    correct: true
  },
  {
    text: "@tool은 멀티 에이전트 시스템에서만 쓸 수 있고, Tool 서브클래스는 단일 에이전트에만 쓸 수 있다",
    explain: "모든 에이전트(단일/멀티)에서 두 방식 모두 사용할 수 있습니다.",
  },
  {
    text: "@tool로 함수에 데코레이터를 달면 docstring이 필요 없고, 서브클래스는 docstring을 포함하면 안 된다",
    explain: "두 방식 모두 명확한 docstring이 필요합니다.",
  }
]}
/>

---

### Q2: CodeAgent는 ReAct(Reason + Act) 접근법으로 다단계 작업을 어떻게 처리하나요?
CodeAgent가 일련의 단계를 실행해 문제를 푸는 방식을 올바르게 설명한 문장을 고르세요.

---

### Q3: 도구를 Hugging Face Hub에 공유할 때의 주요 장점은?
개발자가 커스텀 도구를 업로드·공유하는 가장 큰 이유를 고르세요.

---

### Q4: ToolCallingAgent와 CodeAgent의 액션 실행 방식 차이에 대해 올바른 설명은?
ToolCallingAgent의 동작 방식을 가장 잘 설명한 문장을 고르세요.

---

### Q5: smolagents 기본 도구 상자에는 무엇이 포함되어 있고, 왜 사용할까요?
기본 도구 상자의 목적과 내용을 가장 잘 설명한 문장을 고르세요.

---

퀴즈를 모두 마쳤습니다! 🎉 어려웠던 문제는 *Code Agents*, *Tool Calling Agents*, *Tools* 섹션을 복습해보세요. 모두 맞췄다면, robust한 smolagents 애플리케이션을 만들 준비가 된 것입니다!

### 코드를 사용하는 에이전트 만들기
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/code_agents.md

# 코드를 사용하는 에이전트 만들기

코드 에이전트는 `smolagents`의 기본 에이전트 유형입니다. 이들은 파이썬 도구 호출을 생성하여 작업을 수행하며, 효율적이고 표현력 있으며 정확한 액션 표현을 달성합니다.

이러한 간소화된 접근 방식은 필요한 액션 수를 줄이고, 복잡한 작업을 단순화하며, 기존 코드 함수의 재사용을 가능하게 합니다. `smolagents`는 약 1,000줄의 코드로 구현된 경량 프레임워크를 제공합니다.

![코드 vs JSON 액션](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)
위 이미지는 논문 [Executable Code Actions Elicit Better LLM Agents](https://huggingface.co/papers/2402.01030)에서 가져왔습니다.

코드 에이전트가 왜 효과적인지 더 알고 싶다면, smolagents 문서의 이 가이드를 참고하세요.

## 왜 코드 에이전트인가요?

다단계 에이전트 프로세스에서 LLM은 일반적으로 외부 도구 호출을 포함하는 액션을 작성하고 실행합니다. 기존 방식은 도구 이름과 인자를 문자열로 지정하는 JSON 형식을 사용하며, **시스템이 어떤 도구를 실행할지 파싱해야 합니다**.

하지만 연구 결과에 따르면, **도구 호출 LLM은 직접 코드를 사용할 때 더 효과적으로 작동합니다**. 이것이 `smolagents`의 핵심 원칙이며, 위의 논문 이미지에서도 확인할 수 있습니다.

코드로 액션을 작성하면 다음과 같은 주요 장점이 있습니다:

* **조합성**: 액션을 쉽게 결합하고 재사용할 수 있습니다.
* **객체 관리**: 이미지와 같은 복잡한 구조를 직접 다룰 수 있습니다.
* **범용성**: 계산적으로 가능한 모든 작업을 표현할 수 있습니다.
* **LLM에 자연스러움**: 고품질 코드는 이미 LLM의 학습 데이터에 풍부하게 포함되어 있습니다.

## 코드 에이전트는 어떻게 동작하나요?

![https://huggingface.co/docs/smolagents/conceptual_guides/react에서 가져옴](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/smolagents/codeagent_docs.png)

위 다이어그램은 `CodeAgent.run()`이 어떻게 동작하는지 보여줍니다. 이는 1단원에서 언급한 ReAct 프레임워크를 따릅니다. `smolagents`에서 에이전트의 주요 추상화는 `MultiStepAgent`이며, 이는 핵심 빌딩 블록 역할을 합니다. `CodeAgent`는 특별한 종류의 `MultiStepAgent`입니다.

`CodeAgent`는 다음과 같은 단계로 동작합니다. 기존 변수와 지식은 실행 로그에 저장되어 에이전트의 컨텍스트로 활용됩니다:

1. 시스템 프롬프트는 `SystemPromptStep`에 저장되고, 사용자 쿼리는 `TaskStep`에 기록됩니다.

2. 그 다음, 다음과 같은 while 루프가 실행됩니다:

    2.1 `agent.write_memory_to_messages()` 메서드는 에이전트의 로그를 LLM이 읽을 수 있는 [채팅 메시지](https://huggingface.co/docs/transformers/main/en/chat_templating) 리스트로 변환합니다.
    
    2.2 이 메시지들은 `Model`로 전송되어 응답을 생성합니다.
    
    2.3 응답에서 액션을 추출하는데, 코드 에이전트이므로 파이썬 코드 조각이어야 합니다.
    
    2.4 액션이 실행됩니다.
    
    2.5 결과가 `ActionStep`에 메모리로 기록됩니다.

각 단계가 끝날 때, 에이전트에 함수 호출(`agent.step_callback`)이 포함되어 있다면 실행됩니다.

## 예시로 알아보기

이 노트북을 따라가며 코드를 직접 실행해볼 수 있습니다.

Alfred는 웨인 가문의 저택에서 파티를 준비하고 있습니다. 모든 것이 순조롭게 진행되도록 여러분의 도움이 필요합니다. 지금까지 배운 다단계 `CodeAgent`의 동작 방식을 적용해봅시다.

아직 `smolagents`를 설치하지 않았다면, 다음 명령어로 설치하세요:

```bash
pip install smolagents -U
```

Hugging Face Hub에 로그인하여 Serverless Inference API를 사용할 수 있도록 합니다.

```python
from huggingface_hub import login

login()
```

### `smolagents`로 파티 플레이리스트 선택하기

음악은 파티의 필수 요소입니다! Alfred는 플레이리스트를 고르는 데 도움이 필요합니다. 다행히도 `smolagents`를 사용하면 웹 검색 도구를 에이전트에 추가하여 이를 해결할 수 있습니다.

모델로는 Hugging Face의 [Serverless Inference API](https://huggingface.co/docs/api-inference/index)에 접근할 수 있는 `InferenceClientModel`을 사용합니다. 기본 모델은 "Qwen/Qwen2.5-Coder-32B-Instruct"이며, 빠른 추론이 가능합니다. 허브에서 호환되는 다른 모델도 선택할 수 있습니다.

에이전트 실행은 매우 간단합니다:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())

agent.run("웨인 저택에서 열리는 파티에 어울리는 최고의 음악 추천을 검색해줘.")
```

이 예시를 실행하면 **워크플로우 단계의 실행 추적**이 출력됩니다. 또한 다음과 같은 파이썬 코드가 표시됩니다:

```python
 ─ 실행된 파싱 코드: ─────────────────────────────────────────────────────────────── 
  results = web_search(query="배트맨 파티에 어울리는 최고의 음악")                                                       
  print(results)                                                                                                    
 ───────────────────────────────────────────────────────────────────────────────────── 
```

몇 단계 후, Alfred가 사용할 수 있는 플레이리스트가 생성됩니다! 🎵

### 맞춤형 도구로 메뉴 준비하기

플레이리스트를 골랐다면, 이제 손님을 위한 메뉴를 준비해야 합니다. Alfred는 여기서도 `smolagents`의 도움을 받을 수 있습니다. 아래 예시에서는 `@tool` 데코레이터를 사용해 맞춤형 함수를 도구로 등록합니다. 도구 생성에 대해서는 이후에 더 자세히 다루니, 지금은 코드를 실행해보세요.

아래 예시처럼, `@tool` 데코레이터로 도구를 만들고 `tools` 리스트에 추가합니다.

```python
from smolagents import CodeAgent, tool, InferenceClientModel

# 파티 상황에 맞는 메뉴를 추천하는 도구
@tool
def suggest_menu(occasion: str) -> str:
    """
    상황에 따라 메뉴를 추천합니다.
    Args:
        occasion (str): 파티 유형. 허용 값:
                        - "casual": 캐주얼 파티 메뉴
                        - "formal": 포멀 파티 메뉴
                        - "superhero": 슈퍼히어로 파티 메뉴
                        - "custom": 맞춤 메뉴
    """
    if occasion == "casual":
        return "피자, 스낵, 음료."
    elif occasion == "formal":
        return "3코스 디너, 와인, 디저트."
    elif occasion == "superhero":
        return "고에너지, 건강식 뷔페."
    else:
        return "집사만을 위한 맞춤 메뉴."

# Alfred가 파티 메뉴를 준비합니다
agent = CodeAgent(tools=[suggest_menu], model=InferenceClientModel())

# 파티를 위한 포멀 메뉴 준비
agent.run("파티를 위한 포멀 메뉴를 준비해줘.")
```

에이전트는 몇 단계 실행 후 답을 찾습니다. docstring에 허용 값을 명확히 적으면 에이전트가 `occasion` 인자 값을 올바르게 사용하도록 유도할 수 있습니다.

메뉴 준비 완료! 🥗

### 에이전트 내에서 파이썬 import 사용하기

플레이리스트와 메뉴가 준비되었으니, 이제 남은 것은 준비 시간 계산입니다!

Alfred는 모든 준비를 지금 시작하면 언제 파티가 준비될지 계산해야 합니다. 필요하다면 다른 슈퍼히어로의 도움도 받을 수 있겠죠.

`smolagents`는 파이썬 코드 조각을 작성하고 실행하는 에이전트에 특화되어 있으며, 보안을 위해 샌드박스 실행을 제공합니다.

**코드 실행에는 엄격한 보안 조치가 적용됩니다** — 기본적으로 허용된 목록 외의 import는 차단됩니다. 하지만 `additional_authorized_imports`에 문자열로 추가하면 import를 허용할 수 있습니다.
자세한 보안 실행 방법은 공식 [가이드](https://huggingface.co/docs/smolagents/tutorials/secure_code_execution)를 참고하세요.

아래 예시에서는 `datetime` 모듈 import를 허용합니다.

```python
from smolagents import CodeAgent, InferenceClientModel
import numpy as np
import time
import datetime

agent = CodeAgent(tools=[], model=InferenceClientModel(), additional_authorized_imports=['datetime'])

agent.run(
    """
    Alfred가 파티를 준비해야 합니다. 작업 목록:
    1. 음료 준비 - 30분
    2. 저택 장식 - 60분
    3. 메뉴 세팅 - 45분
    4. 음악 및 플레이리스트 준비 - 45분

    지금 바로 시작하면 파티 준비가 언제 끝날까요?
    """
)
```

이 예시들은 코드 에이전트로 할 수 있는 일의 시작에 불과합니다. 더 많은 내용은 [smolagents 문서](https://huggingface.co/docs/smolagents)에서 확인하세요.

요약하자면, `smolagents`는 파이썬 코드 조각을 작성하고 실행하는 에이전트에 특화되어 있으며, 보안 샌드박스 실행을 지원합니다. 로컬 및 API 기반 언어 모델 모두 지원하여 다양한 개발 환경에 적합합니다.

### 우리만의 파티 준비 에이전트를 허브에 공유하기

우리만의 Alfred 에이전트를 커뮤니티에 공유할 수 있다면 정말 멋지지 않을까요? 누구나 허브에서 쉽게 다운로드해 사용할 수 있습니다! `smolagents`는 완성된 에이전트를 커뮤니티에 공유하고, 다른 사람의 에이전트를 즉시 다운로드할 수 있도록 지원합니다. 방법은 다음과 같이 간단합니다:

```python
# 사용자명과 저장소명을 변경하세요
agent.push_to_hub('sergiopaniego/AlfredAgent')
```

다시 다운로드하려면 아래 코드를 사용하세요:

```python
# 사용자명과 저장소명을 변경하세요
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)

alfred_agent.run("웨인 저택에서 열리는 파티에 어울리는 최고의 플레이리스트를 추천해줘. 파티 테마는 '빌런 가면무도회'야")  
```

공유된 에이전트는 Hugging Face Spaces에서도 바로 사용할 수 있습니다. [여기](https://huggingface.co/spaces/davidberenstein1957/smolagents-and-tools)에서 다양한 에이전트를 탐색해보세요.

예를 들어, _AlfredAgent_는 [여기](https://huggingface.co/spaces/sergiopaniego/AlfredAgent)에서 바로 사용할 수 있습니다. 아래에서 직접 체험해보세요:

Alfred가 어떻게 이런 에이전트를 만들었는지 궁금하다면, 여러 도구를 통합해 아래와 같이 에이전트를 생성할 수 있습니다. 도구에 대해서는 이후에 자세히 다루니, 지금은 구조만 참고하세요:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, Tool, tool, VisitWebpageTool

@tool
def suggest_menu(occasion: str) -> str:
    """
    상황에 따라 메뉴를 추천합니다.
    Args:
        occasion: 파티 유형
    """
    if occasion == "casual":
        return "피자, 스낵, 음료."
    elif occasion == "formal":
        return "3코스 디너, 와인, 디저트."
    elif occasion == "superhero":
        return "고에너지, 건강식 뷔페."
    else:
        return "집사만을 위한 맞춤 메뉴."

@tool
def catering_service_tool(query: str) -> str:
    """
    이 도구는 고담시에서 평점이 가장 높은 케이터링 서비스를 반환합니다.
    
    Args:
        query: 케이터링 서비스 검색어
    """
    # 예시 케이터링 서비스와 평점
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }
    
    # 평점이 가장 높은 서비스 찾기(검색어 필터링 시뮬레이션)
    best_service = max(services, key=services.get)
    
    return best_service

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    이 도구는 카테고리에 따라 창의적인 슈퍼히어로 테마 파티 아이디어를 제안합니다.
    고유한 파티 테마 아이디어를 반환합니다."""
    
    inputs = {
        "category": {
            "type": "string",
            "description": "슈퍼히어로 파티 유형(예: 'classic heroes', 'villain masquerade', 'futuristic Gotham')",
        }
    }
    
    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Justice League Gala: 손님들이 DC 영웅으로 분장하고 '크립토나이트 펀치' 같은 테마 칵테일을 즐깁니다.",
            "villain masquerade": "Gotham Rogues' Ball: 손님들이 배트맨 빌런으로 분장하는 신비로운 가면무도회.",
            "futuristic Gotham": "Neo-Gotham Night: 배트맨 비욘드에서 영감을 받은 사이버펑크 스타일 파티, 네온 장식과 미래형 소품.",
        }
        
        return themes.get(category.lower(), "테마 파티 아이디어를 찾을 수 없습니다. 'classic heroes', 'villain masquerade', 'futuristic Gotham' 중에서 시도해보세요.")

# Alfred가 파티 메뉴를 준비합니다
agent = CodeAgent(
    tools=[
        DuckDuckGoSearchTool(), 
        VisitWebpageTool(),
        suggest_menu,
        catering_service_tool,
        SuperheroPartyThemeTool(),
	FinalAnswerTool()
    ], 
    model=InferenceClientModel(),
    max_steps=10,
    verbosity_level=2
)

agent.run("웨인 저택에서 열리는 파티에 어울리는 최고의 플레이리스트를 추천해줘. 파티 테마는 '빌런 가면무도회'야")
```

이처럼 여러 도구를 결합해 `CodeAgent`를 만들면, 커뮤니티와 공유할 수 있는 궁극의 파티 플래너가 완성됩니다! 🎉

이제 여러분 차례입니다: 자신만의 에이전트를 만들어 허브에 공유해보세요! 🕵️‍♂️💡

여러분의 에이전트 프로젝트를 공유하고 싶다면, Hugging Face Hub에 space를 만들고 agents-course 태그를 달아주세요. 여러분의 작품을 기다리고 있습니다!

### OpenTelemetry와 Langfuse로 파티 준비 에이전트 추적하기 📡

Alfred는 파티 준비 에이전트를 다듬으면서 실행을 디버깅하는 데 지쳐가고 있습니다. 에이전트는 본질적으로 예측 불가능하고, 동작을 추적하기 어렵기 때문입니다. 하지만 궁극의 파티 준비 에이전트를 만들어 배포하려면, 미래의 모니터링과 분석을 위한 강력한 추적 기능이 필요합니다.

여기서도 `smolagents`가 해결책을 제공합니다! [OpenTelemetry](https://opentelemetry.io/) 표준을 채택해 에이전트 실행을 계측하고, [Langfuse](https://langfuse.com/) 및 `SmolagentsInstrumentor`를 통해 동작을 쉽게 추적·분석할 수 있습니다.

설정 방법은 다음과 같이 간단합니다!

먼저, 필요한 의존성을 설치합니다:

```bash
pip install opentelemetry-sdk opentelemetry-exporter-otlp openinference-instrumentation-smolagents
```

Alfred는 이미 Langfuse 계정을 만들고 API 키를 준비했습니다. 아직 계정이 없다면 [여기](https://cloud.langfuse.com/)에서 가입하거나 [대안](https://huggingface.co/docs/smolagents/tutorials/inspect_runs)을 참고하세요.

API 키를 아래와 같이 환경 변수로 설정합니다:

```python
import os
import base64

LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_AUTH=base64.b64encode(f"{LANGFUSE_PUBLIC_KEY}:{LANGFUSE_SECRET_KEY}".encode()).decode()

os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://cloud.langfuse.com/api/public/otel" # EU 데이터 리전
# os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://us.cloud.langfuse.com/api/public/otel" # US 데이터 리전
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
```

마지막으로, `SmolagentsInstrumentor`를 초기화해 추적을 시작합니다.

```python
from opentelemetry.sdk.trace import TracerProvider

from openinference.instrumentation.smolagents import SmolagentsInstrumentor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor

trace_provider = TracerProvider()
trace_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter()))

SmolagentsInstrumentor().instrument(tracer_provider=trace_provider)
```

이제 Alfred는 연결 완료! 🔌 `smolagents`의 실행이 Langfuse에 기록되어, 에이전트의 동작을 완벽하게 추적할 수 있습니다. 이 설정으로 이전 실행을 다시 확인하고, 파티 준비 에이전트를 더욱 개선할 수 있습니다.

에이전트 추적 및 평가 데이터 활용법은 보너스 2단원에서 더 자세히 다룹니다.

```python
from smolagents import CodeAgent, InferenceClientModel

agent = CodeAgent(tools=[], model=InferenceClientModel())
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)
alfred_agent.run("웨인 저택에서 열리는 파티에 어울리는 최고의 플레이리스트를 추천해줘. 파티 테마는 '빌런 가면무도회'야")  
```

Alfred는 이제 [여기](https://cloud.langfuse.com/project/cm7bq0abj025rad078ak3luwi/traces/995fc019255528e4f48cf6770b0ce27b?timestamp=2025-02-19T10%3A28%3A36.929Z)에서 로그를 확인하고 분석할 수 있습니다.

실행 중에 작은 오류가 발생했습니다. 로그에서 오류를 찾아볼 수 있나요? 에이전트가 어떻게 오류를 처리하고 유효한 답을 반환하는지 추적해보세요. 여기에서 오류를 직접 확인할 수 있습니다. 물론 오류는 이미 수정되었으며, 자세한 내용은 이 이슈에서 확인할 수 있습니다.

한편, [추천 플레이리스트](https://open.spotify.com/playlist/0gZMMHjuxMrrybQ7wTMTpw)가 파티 준비 분위기를 완성해줍니다. 멋지죠? 🎶

---

이제 첫 번째 코드 에이전트를 만들었으니, **이제 smolagents에서 제공하는 두 번째 에이전트 유형인 Tool Calling Agent 만드는 법**을 배워봅시다.

## 참고 자료

- [smolagents 블로그](https://huggingface.co/blog/smolagents) - smolagents 및 코드 인터랙션 소개
- [smolagents: 좋은 에이전트 만들기](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - 신뢰성 높은 에이전트 구축을 위한 베스트 프랙티스
- [Anthropic: 효과적인 에이전트 만들기](https://www.anthropic.com/research/building-effective-agents) - 에이전트 설계 원칙
- [OpenTelemetry로 실행 공유하기](https://huggingface.co/docs/smolagents/tutorials/inspect_runs) - 에이전트 추적 설정 방법

### 결론
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/conclusion.md

# 결론

`smolagents` 모듈(2단원)을 마친 것을 축하합니다 🥳

이제 `smolagents`의 기본을 마스터했고, 나만의 에이전트도 만들어보았습니다! 이제 여러분은 관심 있는 문제를 해결하는 에이전트를 직접 만들 수 있는 역량을 갖추었습니다.

다음 모듈에서는 **LlamaIndex로 에이전트 만드는 법**을 배울 예정입니다.

마지막으로, **이 과정에 대한 여러분의 의견과 개선점**을 듣고 싶습니다. 피드백이 있다면 👉 [이 양식](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)을 작성해 주세요.

### 계속 배우고, 멋진 하루 보내세요 🤗

### 도구 (Tools)
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/tools.md

# 도구 (Tools)

[unit 1](https://huggingface.co/learn/agents-course/unit1/tools)에서 살펴본 것처럼, 에이전트는 다양한 작업을 수행하기 위해 도구를 사용합니다. `smolagents`에서 도구는 **에이전트 시스템 내에서 LLM이 호출할 수 있는 함수**로 취급됩니다.

도구와 상호작용하려면 LLM이 다음과 같은 **인터페이스 설명**을 필요로 합니다:

- **이름**: 도구의 이름
- **도구 설명**: 도구가 수행하는 작업
- **입력 타입 및 설명**: 도구가 받는 인자
- **출력 타입**: 도구가 반환하는 값

예를 들어, Alfred가 Wayne Manor에서 파티를 준비할 때, 케이터링 서비스 검색부터 파티 테마 아이디어 찾기까지 다양한 정보를 수집해야 합니다. 아래는 간단한 검색 도구 인터페이스 예시입니다:

- **이름:** `web_search`
- **도구 설명:** 특정 쿼리로 웹을 검색합니다
- **입력:** `query` (string) - 검색어
- **출력:** 검색 결과가 담긴 문자열

이러한 도구를 사용하면 Alfred는 완벽한 파티 준비를 위해 필요한 모든 정보를 쉽게 모을 수 있습니다.

아래 애니메이션은 도구 호출이 어떻게 관리되는지 보여줍니다:

![Agentic pipeline from https://huggingface.co/docs/smolagents/conceptual_guides/react](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/Agent_ManimCE.gif)

## 도구 생성 방법

`smolagents`에서 도구는 두 가지 방식으로 정의할 수 있습니다:
1. **`@tool` 데코레이터**를 사용한 간단한 함수형 도구
2. **`Tool` 클래스를 상속한 복잡한 도구**

### `@tool` 데코레이터

`@tool` 데코레이터는 **간단한 도구를 정의할 때 권장되는 방법**입니다. 내부적으로 smolagents는 함수에서 기본 정보를 파싱합니다. 함수 이름을 명확하게 짓고, 좋은 docstring을 작성하면 LLM이 더 쉽게 사용할 수 있습니다.

이 방식에서는 다음을 지켜야 합니다:
- **명확하고 설명적인 함수명**
- **입력과 출력에 타입 힌트 사용**
- **자세한 설명과 Args 섹션**

#### 고평점 케이터링 도구 예시

코드는 이 노트북에서 따라할 수 있습니다. Google Colab에서 실행할 수 있습니다.

Alfred가 파티 메뉴를 정했다면, 많은 손님을 위해 케이터링 서비스를 고용해야 합니다. Gotham에서 평점이 가장 높은 케이터링 서비스를 찾는 도구를 만들어봅시다.

```python
from smolagents import CodeAgent, InferenceClientModel, tool

# Gotham에서 평점이 가장 높은 케이터링 서비스를 반환하는 함수 예시
@tool
def catering_service_tool(query: str) -> str:
    """
    이 도구는 Gotham City에서 평점이 가장 높은 케이터링 서비스를 반환합니다.

    Args:
        query: 케이터링 서비스를 찾기 위한 검색어
    """
    # 예시 케이터링 서비스와 평점
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }

    # 평점이 가장 높은 서비스 찾기(검색어 필터링은 생략)
    best_service = max(services, key=services.get)

    return best_service

agent = CodeAgent(tools=[catering_service_tool], model=InferenceClientModel())

# 에이전트 실행 예시
result = agent.run(
    "Can you give me the name of the highest-rated catering service in Gotham City?"
)

print(result)   # 출력: Gotham Catering Co.
```

### Python 클래스로 도구 정의하기

이 방식은 [`Tool`](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/tools#smolagents.Tool) 클래스를 상속해 복잡한 도구를 만드는 방법입니다. 클래스는 함수에 메타데이터를 추가해 LLM이 효과적으로 사용할 수 있도록 도와줍니다. 이 클래스에서는 다음을 정의합니다:

- `name`: 도구 이름
- `description`: 시스템 프롬프트에 들어갈 설명
- `inputs`: 입력 타입과 설명이 담긴 딕셔너리
- `output_type`: 반환 타입
- `forward`: 실제 동작을 수행하는 메서드

아래는 `Tool`을 상속해 슈퍼히어로 테마 파티 아이디어를 생성하는 도구 예시입니다.

#### 슈퍼히어로 테마 파티 아이디어 생성 도구

Alfred는 Wayne Manor에서 슈퍼히어로 테마 파티를 준비합니다. 손님들을 놀라게 할 독특한 테마가 필요합니다. 아래 도구는 카테고리에 따라 창의적인 파티 아이디어를 제안합니다.

```python
from smolagents import Tool, CodeAgent, InferenceClientModel

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    이 도구는 카테고리에 따라 창의적인 슈퍼히어로 테마 파티 아이디어를 제안합니다.
    고유한 파티 테마 아이디어를 반환합니다."""

    inputs = {
        "category": {
            "type": "string",
            "description": "슈퍼히어로 파티 유형(예: 'classic heroes', 'villain masquerade', 'futuristic Gotham')",
        }
    }

    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Justice League Gala: 손님들이 DC 영웅으로 분장하고, 'The Kryptonite Punch' 같은 테마 칵테일 제공.",
            "villain masquerade": "Gotham Rogues' Ball: 손님들이 배트맨 빌런으로 분장하는 미스터리 가면무도회.",
            "futuristic Gotham": "Neo-Gotham Night: 배트맨 비욘드에서 영감을 받은 사이버펑크 스타일 파티, 네온 장식과 미래형 소품.",
        }

        return themes.get(category.lower(), "테마를 찾을 수 없습니다. 'classic heroes', 'villain masquerade', 'futuristic Gotham' 중에서 선택해보세요.")

# 도구 인스턴스화
party_theme_tool = SuperheroPartyThemeTool()
agent = CodeAgent(tools=[party_theme_tool], model=InferenceClientModel())

# 에이전트 실행 예시
result = agent.run(
    "What would be a good superhero party idea for a 'villain masquerade' theme?"
)

print(result)  # 출력: "Gotham Rogues' Ball: 손님들이 배트맨 빌런으로 분장하는 미스터리 가면무도회."
```

이 도구로 Alfred는 슈퍼히어로 테마 파티의 완벽한 호스트가 될 수 있습니다! 🦸‍♂️🦸‍♀️

## 기본 도구 모음(Default Toolbox)

`smolagents`에는 바로 사용할 수 있는 여러 기본 도구가 포함되어 있습니다. [기본 도구 모음](https://huggingface.co/docs/smolagents/guided_tour?build-a-tool=Decorate+a+function+with+%40tool#default-toolbox)에는 다음이 포함됩니다:

- **PythonInterpreterTool**
- **FinalAnswerTool**
- **UserInputTool**
- **DuckDuckGoSearchTool**
- **GoogleSearchTool**
- **VisitWebpageTool**

Alfred는 다음과 같이 다양한 도구를 활용해 Wayne Manor에서 완벽한 파티를 준비할 수 있습니다:

- `DuckDuckGoSearchTool`로 창의적인 슈퍼히어로 테마 파티 아이디어 검색
- `GoogleSearchTool`로 Gotham에서 평점이 가장 높은 케이터링 서비스 찾기
- `PythonInterpreterTool`로 좌석 배치 계산
- 모든 정보를 모아 `FinalAnswerTool`로 계획 정리

이 도구들로 Alfred는 완벽하고 매끄러운 파티를 보장할 수 있습니다. 🦇💡

## 도구 공유 및 가져오기

**smolagents**의 강력한 기능 중 하나는 커스텀 도구를 Hub에 공유하고, 커뮤니티에서 만든 도구를 쉽게 가져올 수 있다는 점입니다. 여기에는 **HF Spaces** 및 **LangChain 도구**와의 연동도 포함되어, Alfred가 Wayne Manor에서 잊지 못할 파티를 기획하는 데 큰 도움이 됩니다. 🎭

이러한 통합을 통해 Alfred는 고급 이벤트 플래닝 도구를 활용할 수 있습니다. 예를 들어, 조명 조정, 파티 음악 추천, 케이터링 업체와의 협업 등 다양한 작업을 자동화할 수 있습니다.

아래는 이러한 기능이 파티 경험을 어떻게 향상시키는지 보여주는 예시입니다:

### 도구를 Hub에 공유하기

커스텀 도구를 커뮤니티에 공유하는 것은 매우 쉽습니다! `push_to_hub()` 메서드를 사용해 Hugging Face 계정에 업로드하면 됩니다.

예를 들어, Alfred는 자신의 `party_theme_tool`을 공유해 다른 사람들도 Gotham 최고의 케이터링 서비스를 찾을 수 있도록 도울 수 있습니다:

```python
party_theme_tool.push_to_hub("{your_username}/party_theme_tool", token="")
```

### Hub에서 도구 가져오기

다른 사용자가 만든 도구를 `load_tool()` 함수로 쉽게 가져올 수 있습니다. 예를 들어, Alfred가 AI로 파티 홍보 이미지를 만들고 싶다면, 커뮤니티에서 미리 만들어진 도구를 활용할 수 있습니다:

```python
from smolagents import load_tool, CodeAgent, InferenceClientModel

image_generation_tool = load_tool(
    "m-ric/text-to-image",
    trust_remote_code=True
)

agent = CodeAgent(
    tools=[image_generation_tool],
    model=InferenceClientModel()
)

agent.run("Generate an image of a luxurious superhero-themed party at Wayne Manor with made-up superheros.")
```

### HF Space를 도구로 가져오기

`Tool.from_space()`를 사용해 HF Space를 도구로 가져올 수도 있습니다. 이를 통해 커뮤니티의 수천 개 Space와 연동해 이미지 생성, 데이터 분석 등 다양한 작업을 수행할 수 있습니다.

이 도구는 `gradio_client`를 통해 Space의 Gradio 백엔드와 연결되므로, 미리 pip로 설치해야 합니다.

파티를 위해 Alfred는 AI로 생성한 이미지를 파티 공지에 활용할 수 있습니다. 아래는 그 예시입니다:

```python
from smolagents import CodeAgent, InferenceClientModel, Tool

image_generation_tool = Tool.from_space(
    "black-forest-labs/FLUX.1-schnell",
    name="image_generator",
    description="Generate an image from a prompt"
)

model = InferenceClientModel("Qwen/Qwen2.5-Coder-32B-Instruct")

agent = CodeAgent(tools=[image_generation_tool], model=model)

agent.run(
    "Improve this prompt, then generate an image of it.",
    additional_args={'user_prompt': 'A grand superhero-themed party at Wayne Manor, with Alfred overseeing a luxurious gala'}
)
```

### LangChain 도구 가져오기

`LangChain` 프레임워크는 이후 섹션에서 자세히 다룹니다. 여기서는 smolagents 워크플로우에서 LangChain 도구를 재사용할 수 있다는 점만 알아둡시다!

`Tool.from_langchain()` 메서드로 LangChain 도구를 쉽게 불러올 수 있습니다. Alfred는 Wayne Manor에서 최고의 엔터테인먼트 아이디어를 찾기 위해 LangChain 도구를 활용할 수 있습니다.

```python
from langchain.agents import load_tools
from smolagents import CodeAgent, InferenceClientModel, Tool

search_tool = Tool.from_langchain(load_tools(["serpapi"])[0])

agent = CodeAgent(tools=[search_tool], model=model)

agent.run("Search for luxury entertainment ideas for a superhero-themed event, such as live performances and interactive experiences.")
```

### MCP 서버에서 도구 모음 가져오기

`smolagents`는 [glama.ai](https://glama.ai/mcp/servers) 또는 [smithery.ai](https://smithery.ai)에서 제공하는 수백 개 MCP 서버의 도구도 가져올 수 있습니다. MCP에 대해 더 알고 싶다면 [무료 MCP 강의](https://huggingface.co/learn/mcp-course/)를 참고하세요.

mcp 클라이언트 설치

먼저 `smolagents`의 mcp 통합을 설치해야 합니다.

```bash
pip install "smolagents[mcp]"
```

MCP 서버의 도구는 ToolCollection 객체로 불러올 수 있습니다:

```python
import os
from smolagents import ToolCollection, CodeAgent
from mcp import StdioServerParameters
from smolagents import InferenceClientModel

model = InferenceClientModel("Qwen/Qwen2.5-Coder-32B-Instruct")

server_parameters = StdioServerParameters(
    command="uvx",
    args=["--quiet", "pubmedmcp@0.1.3"],
    env={"UV_PYTHON": "3.12", **os.environ},
)

with ToolCollection.from_mcp(server_parameters, trust_remote_code=True) as tool_collection:
    agent = CodeAgent(tools=[*tool_collection.tools], model=model, add_base_tools=True)
    agent.run("Please find a remedy for hangover.")
```

이렇게 하면 Alfred는 고급 엔터테인먼트 옵션을 빠르게 찾을 수 있어, Gotham의 명사들이 잊지 못할 슈퍼히어로 파티를 즐길 수 있습니다! 🎉

## 참고 자료

- [Tools Tutorial](https://huggingface.co/docs/smolagents/tutorials/tools) - 도구 사용법을 익힐 수 있는 튜토리얼
- [Tools Documentation](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/tools) - 도구에 대한 공식 문서
- [Tools Guided Tour](https://huggingface.co/docs/smolagents/v1.8.1/en/guided_tour#tools) - 도구를 효율적으로 만들고 활용하는 단계별 가이드
- [Building Effective Agents](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - 신뢰성 높고 성능 좋은 커스텀 함수 에이전트 개발 가이드

### 시험 시간!
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/final_quiz.md

# 시험 시간!

`smolagents` 학습을 마친 여러분, 정말 수고 많으셨습니다! 이제 퀴즈로 여러분의 지식을 점검해볼 시간입니다. 🧠

## 안내사항

- 퀴즈는 코드 문제로 구성되어 있습니다.
- 코드 조각을 완성하는 지시사항이 주어집니다.
- 안내를 잘 읽고, 코드 조각을 완성해 주세요.
- 각 문제마다 결과와 피드백이 제공됩니다.

🧘 **이 퀴즈는 점수화되거나 인증되지 않습니다**. 여러분이 `smolagents` 라이브러리를 잘 이해했는지, 추가 학습이 필요한지 스스로 점검하는 용도입니다. 다음 단원에서는 이 지식을 실제 사례와 프로젝트에 적용해볼 예정입니다.

시작해볼까요? 

## 퀴즈 🚀

퀴즈에 직접 참여하려면 👉 [여기](https://huggingface.co/spaces/agents-course/unit2_smolagents_quiz)로 이동하세요.

### `smolagents` 소개
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/introduction.md

# `smolagents` 소개

이 모듈에 오신 것을 환영합니다! 여기서는 [`smolagents`](https://github.com/huggingface/smolagents) 라이브러리를 활용해 **효과적인 에이전트 만드는 법**을 배웁니다. `smolagents`는 강력한 AI 에이전트를 손쉽게 만들 수 있도록 도와주는 경량 프레임워크입니다.

`smolagents`는 Hugging Face에서 제공하는 라이브러리입니다. [`저장소`](https://github.com/huggingface/smolagents)에 **별(star)**을 눌러 응원해 주세요!

## 모듈 개요

이 모듈에서는 `smolagents`를 활용해 지능형 에이전트를 만드는 데 필요한 핵심 개념과 실전 전략을 폭넓게 다룹니다.

수많은 오픈소스 프레임워크가 존재하는 만큼, `smolagents`의 구성 요소와 장단점을 이해하고, 언제 다른 솔루션이 더 적합할지 판단하는 것이 중요합니다.

이 단원에서는 소프트웨어 개발에 특화된 코드 에이전트, 모듈형 워크플로우를 위한 툴 호출 에이전트, 정보를 검색·종합하는 리트리벌 에이전트 등 주요 에이전트 유형을 살펴봅니다.

또한 여러 에이전트를 조합하는 방법, 비전 기능 및 웹 브라우징 통합 등 동적이고 상황 인식이 가능한 애플리케이션을 만드는 방법도 다룹니다.

이번 단원에서는 1단원에서 만난 에이전트 Alfred가 다시 등장합니다. 이번에는 `smolagents` 프레임워크를 활용해 다양한 작업을 해결합니다. Alfred는 Wayne 저택에서 파티를 준비하며 여러 과제를 해결해야 합니다. Alfred의 여정을 따라가며 `smolagents`의 핵심 개념을 함께 익혀봅시다!

이 단원에서는 `smolagents` 라이브러리로 AI 에이전트를 만드는 법을 배웁니다. 여러분의 에이전트는 데이터 검색, 코드 실행, 웹페이지 상호작용이 가능하며, 여러 에이전트를 조합해 더 강력한 시스템도 만들 수 있습니다.

![Alfred the agent](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/this-is-alfred.jpg)

## 목차

이 단원에서 다루는 내용은 다음과 같습니다:

### 1️⃣ [왜 smolagents를 사용할까](./why_use_smolagents)

`smolagents`는 다양한 오픈소스 에이전트 프레임워크 중 하나입니다. 대안으로는 `LlamaIndex`, `LangGraph` 등이 있으며, 이 과정의 다른 모듈에서 다룹니다. `smolagents`는 특정 상황에 적합한 여러 주요 기능을 제공하지만, 프레임워크 선택 시 항상 다양한 옵션을 고려해야 합니다. 이 장에서는 `smolagents`의 장단점을 살펴보고, 프로젝트 요구에 따라 현명하게 선택할 수 있도록 도와줍니다.

### 2️⃣ [CodeAgents](./code_agents)

`CodeAgents`는 `smolagents`의 기본 에이전트 유형입니다. 이들은 JSON이나 텍스트 대신 파이썬 코드를 생성해 작업을 수행합니다. 이 장에서는 CodeAgent의 목적, 동작 방식, 실전 예시를 통해 그 기능을 살펴봅니다.

### 3️⃣ [ToolCallingAgents](./tool_calling_agents)

`ToolCallingAgents`는 `smolagents`가 지원하는 두 번째 에이전트 유형입니다. CodeAgent가 파이썬 코드를 생성하는 것과 달리, 이 에이전트는 시스템이 파싱·해석해야 하는 JSON/텍스트 블롭을 생성합니다. 이 장에서는 ToolCallingAgent의 동작 방식, CodeAgent와의 차이점, 사용 예시를 다룹니다.

### 4️⃣ [도구(Tools)](./tools)

1단원에서 살펴본 것처럼, 도구는 LLM이 에이전트 시스템 내에서 사용할 수 있는 함수로, 에이전트 행동의 핵심 빌딩 블록입니다. 이 장에서는 도구 생성법, 구조, `Tool` 클래스와 `@tool` 데코레이터를 활용한 다양한 구현 방법을 다룹니다. 기본 도구 상자, 커뮤니티에 도구 공유, 커뮤니티 도구 불러오기 등도 배웁니다.

### 5️⃣ [리트리벌 에이전트(Retrieval Agents)](./retrieval_agents)

리트리벌 에이전트는 모델이 지식 베이스에 접근할 수 있게 하여, 다양한 소스에서 정보를 검색·종합·추출할 수 있게 합니다. 이들은 벡터 스토어를 활용해 효율적으로 정보를 찾고, **RAG(Retrieval-Augmented Generation)** 패턴을 구현합니다. 웹 검색과 맞춤형 지식 베이스 통합, 대화 맥락 유지 등 다양한 전략을 다룹니다.

### 6️⃣ [멀티 에이전트 시스템(Multi-Agent Systems)](./multi_agent_systems)

여러 에이전트를 효과적으로 조율하는 것은 강력한 멀티 에이전트 시스템 구축의 핵심입니다. 웹 검색 에이전트와 코드 실행 에이전트 등 다양한 능력을 가진 에이전트를 조합해 더 정교한 솔루션을 만들 수 있습니다. 이 장에서는 멀티 에이전트 시스템의 설계, 구현, 관리 방법을 다룹니다.

### 7️⃣ [비전 및 브라우저 에이전트(Vision and Browser agents)](./vision_agents)

비전 에이전트는 **비전-언어 모델(VLM)**을 통합해 시각 정보를 처리·해석할 수 있습니다. 이 장에서는 VLM 기반 에이전트 설계·통합법, 이미지 기반 추론, 시각 데이터 분석, 멀티모달 상호작용 등을 다룹니다. 또한 비전 에이전트를 활용해 웹을 탐색하고 정보를 추출하는 브라우저 에이전트도 만들어봅니다.

## 참고 자료

- [smolagents 공식 문서](https://huggingface.co/docs/smolagents)
- [효과적인 에이전트 만들기](https://www.anthropic.com/research/building-effective-agents) - 에이전트 아키텍처 연구 논문
- [에이전트 가이드라인](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - 신뢰성 높은 에이전트 구축을 위한 베스트 프랙티스
- [LangGraph Agents](https://langchain-ai.github.io/langgraph/) - 다양한 에이전트 구현 예시
- [Function Calling Guide](https://platform.openai.com/docs/guides/function-calling) - LLM의 함수 호출 이해하기
- [RAG 베스트 프랙티스](https://www.pinecone.io/learn/retrieval-augmented-generation/) - 효과적인 RAG 구현 가이드

### smolagents를 써야 하는 이유
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/why_use_smolagents.md

# smolagents를 써야 하는 이유

이 모듈에서는 [smolagents](https://huggingface.co/docs/smolagents/en/index)를 사용할 때의 장단점을 살펴보고, 여러분의 필요에 맞는 프레임워크인지 판단할 수 있도록 도와드립니다.

## `smolagents`란?

`smolagents`는 간단하면서도 강력한 AI 에이전트 프레임워크입니다. LLM이 검색, 이미지 생성 등 실제 세계와 상호작용할 수 있는 _에이전시(agency)_ 를 제공합니다.

unit 1에서 배운 것처럼, AI 에이전트는 LLM이 **'관찰(observations)'**을 바탕으로 **'생각(thoughts)'**을 생성해 **'행동(actions)'**을 수행하는 프로그램입니다. smolagents에서 이것이 어떻게 구현되는지 살펴봅시다.

### `smolagents`의 주요 장점
- **단순함:** 최소한의 코드 복잡성과 추상화로 프레임워크를 쉽게 이해하고, 도입 및 확장할 수 있습니다.
- **유연한 LLM 지원:** Hugging Face 도구 및 외부 API와의 통합을 통해 어떤 LLM과도 연동할 수 있습니다.
- **코드 우선 접근:** 액션을 직접 코드로 작성하는 Code Agent를 1급 시민으로 지원하여, 파싱 없이 도구 호출을 단순화합니다.
- **HF Hub 통합:** Hugging Face Hub와의 원활한 통합으로 Gradio Spaces를 도구로 활용할 수 있습니다.

### smolagents를 언제 사용해야 할까?

이러한 장점을 고려할 때, 다른 프레임워크 대신 smolagents를 사용해야 하는 경우는 언제일까요?

smolagents는 다음과 같은 상황에 이상적입니다:
- **가볍고 최소한의 솔루션**이 필요할 때
- **복잡한 설정 없이 빠르게 실험**하고 싶을 때
- **애플리케이션 로직이 단순**할 때

### 코드 vs. JSON 액션
다른 프레임워크에서는 에이전트가 액션을 JSON으로 작성하는 반면, `smolagents`는 **코드 기반 도구 호출**에 집중하여 실행 과정을 단순화합니다. 도구 호출을 위한 코드를 직접 생성하므로, JSON을 파싱해 코드를 만드는 과정이 필요 없습니다. 즉, 출력된 코드를 바로 실행할 수 있습니다.

아래 다이어그램은 이 차이를 보여줍니다:

![Code vs. JSON actions](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)

Code vs JSON 액션의 차이를 다시 보고 싶다면 [Unit 1의 액션 섹션](https://huggingface.co/learn/agents-course/unit1/actions#actions-enabling-the-agent-to-engage-with-its-environment)을 참고하세요.

### smolagents의 에이전트 유형

smolagents의 에이전트는 **다단계 에이전트(multi-step agent)**로 동작합니다.

각 [`MultiStepAgent`](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.MultiStepAgent)는 다음을 수행합니다:
- 한 번의 생각(thought)
- 한 번의 도구 호출 및 실행

주요 에이전트 타입으로 **[CodeAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.CodeAgent)**를 사용하며, 도구 호출을 JSON으로 작성하는 **[ToolCallingAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.ToolCallingAgent)**도 지원합니다.

각 에이전트 타입은 이후 섹션에서 자세히 다룹니다.

 
smolagents에서는 도구를 @tool 데코레이터로 감싼 Python 함수 또는 Tool 클래스로 정의합니다.

### smolagents의 모델 통합
`smolagents`는 유연한 LLM 통합을 지원하여, [특정 기준](https://huggingface.co/docs/smolagents/main/en/reference/models)을 충족하는 어떤 호출 가능한 모델도 사용할 수 있습니다. 프레임워크는 모델 연결을 단순화하는 여러 사전 정의 클래스를 제공합니다:

- **[TransformersModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.TransformersModel):** 로컬 `transformers` 파이프라인을 구현해 손쉬운 통합을 지원합니다.
- **[InferenceClientModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.InferenceClientModel):** [Hugging Face 인프라](https://huggingface.co/docs/api-inference/index) 또는 다양한 [서드파티 추론 제공업체](https://huggingface.co/docs/huggingface_hub/main/en/guides/inference#supported-providers-and-tasks)를 통한 [서버리스 추론](https://huggingface.co/docs/huggingface_hub/main/en/guides/inference) 호출을 지원합니다.
- **[LiteLLMModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.LiteLLMModel):** [LiteLLM](https://www.litellm.ai/)을 활용한 경량 모델 연동을 지원합니다.
- **[OpenAIServerModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.OpenAIServerModel):** OpenAI API 인터페이스를 제공하는 모든 서비스와 연결할 수 있습니다.
- **[AzureOpenAIServerModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.AzureOpenAIServerModel):** Azure OpenAI 배포와의 통합을 지원합니다.

이러한 유연성 덕분에 개발자는 각자의 용도에 가장 적합한 모델과 서비스를 선택할 수 있으며, 실험도 쉽게 할 수 있습니다.

이제 smolagents를 왜, 언제 써야 하는지 이해했으니, 이 강력한 라이브러리를 더 깊이 탐구해봅시다!

## 참고 자료

- [smolagents 블로그](https://huggingface.co/blog/smolagents) - smolagents와 코드 상호작용 소개

### 소규모 퀴즈(비채점) [[quiz1]]
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/quiz1.md

# 소규모 퀴즈(비채점) [[quiz1]]

`smolagents`에 대한 이해도를 간단한 퀴즈로 확인해봅시다! 스스로 테스트해보면 학습 내용을 강화하고, 복습이 필요한 부분을 파악하는 데 도움이 됩니다.

이 퀴즈는 선택 사항이며 점수화되지 않습니다.

### Q1: `smolagents`를 다른 프레임워크보다 선택할 때의 주요 장점은 무엇인가요?
다음 중 `smolagents` 접근 방식의 핵심 강점을 가장 잘 설명하는 문장은 무엇인가요?

---

### Q2: 어떤 상황에서 smolagents를 사용하는 것이 가장 적합할까요?
다음 중 smolagents의 특성과 가장 잘 맞는 상황은 무엇인가요?

---

### Q3: smolagents의 모델 통합 유연성에 대해 올바르게 설명한 것은?
smolagents가 LLM과 상호작용하는 방식을 가장 잘 설명하는 문장을 고르세요.

---

### Q4: smolagents는 코드 기반 액션과 JSON 기반 액션에 대해 어떻게 접근하나요?
다음 중 smolagents의 액션 포맷 철학을 올바르게 설명한 문장은?

---

### Q5: smolagents가 Hugging Face Hub와 통합되어 얻을 수 있는 이점은?
Hub 통합의 핵심 장점을 올바르게 설명한 문장은?

---

퀴즈를 모두 마쳤습니다! 🎉 틀린 문제가 있다면 *왜 smolagents를 사용할까* 섹션을 복습해보세요. 잘 풀었다면, 이제 smolagents의 더 고급 주제로 넘어갈 준비가 되었습니다!

### 액션을 코드 스니펫 또는 JSON 블롭으로 작성하기
https://huggingface.co/learn/agents-course/ko/unit2/smolagents/tool_calling_agents.md

# 액션을 코드 스니펫 또는 JSON 블롭으로 작성하기

코드는 이 노트북에서 따라할 수 있습니다. Google Colab에서 실행할 수 있습니다.

Tool Calling Agent는 `smolagents`에서 제공하는 두 번째 유형의 에이전트입니다. Python 코드를 실행하는 CodeAgent와 달리, **Tool Calling Agent는 LLM 제공업체의 내장 툴 콜링 기능**을 활용해 **JSON 구조**로 툴 호출을 생성합니다. 이는 OpenAI, Anthropic 등 많은 제공업체에서 사용하는 표준 방식입니다.

예를 들어, Alfred가 케이터링 서비스와 파티 아이디어를 검색하고 싶을 때, `CodeAgent`는 다음과 같이 Python 코드를 생성해 실행합니다:

```python
for query in [
    "Best catering services in Gotham City", 
    "Party theme ideas for superheroes"
]:
    print(web_search(f"Search for: {query}"))
```

`ToolCallingAgent`는 대신 JSON 구조를 생성합니다:

```python
[
    {"name": "web_search", "arguments": "Best catering services in Gotham City"},
    {"name": "web_search", "arguments": "Party theme ideas for superheroes"}
]
```

이 JSON 블롭이 시스템에 전달되어 툴 호출이 실행됩니다.

`smolagents`는 [CodeAgent가 전반적으로 더 좋은 성능을 보이기 때문에](https://huggingface.co/papers/2402.01030) 주로 CodeAgent에 초점을 맞추지만, ToolCallingAgent는 변수 처리나 복잡한 툴 호출이 필요 없는 간단한 시스템에 효과적입니다.

![Code vs JSON Actions](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)  

## Tool Calling Agent는 어떻게 동작하나요?  

Tool Calling Agent는 CodeAgent와 동일한 다단계 워크플로우를 따릅니다([이전 섹션](./code_agents) 참고). 

핵심 차이점은 **액션을 구조화하는 방식**입니다. ToolCallingAgent는 실행 가능한 코드 대신, **툴 이름과 인자를 명시한 JSON 객체**를 생성합니다. 시스템은 이 명령을 파싱해 적절한 툴을 실행합니다.

## 예시: Tool Calling Agent 실행하기  

앞서 Alfred가 파티 준비를 시작했던 예시를 ToolCallingAgent로 다시 구현해보겠습니다. DuckDuckGo를 활용해 웹을 검색하는 에이전트를 만들지만, 에이전트 타입만 다르고 나머지는 프레임워크가 처리합니다:

```python
from smolagents import ToolCallingAgent, DuckDuckGoSearchTool, InferenceClientModel

agent = ToolCallingAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())

agent.run("Search for the best music recommendations for a party at the Wayne's mansion.")
```

에이전트의 실행 로그를 보면, `Executing parsed code:` 대신 다음과 같은 메시지가 출력됩니다:

```text
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Calling tool: 'web_search' with arguments: {'query': "best music recommendations for a party at Wayne's         │
│ mansion"}                                                                                                       │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

에이전트는 구조화된 툴 호출을 생성하고, 시스템이 이를 처리해 결과를 반환합니다. CodeAgent처럼 코드를 직접 실행하지 않습니다.

이제 두 에이전트 타입의 차이를 이해했으니, 필요에 따라 적합한 방식을 선택할 수 있습니다. Alfred의 파티 준비를 계속 이어가봅시다! 🎉

## 참고 자료

- [ToolCallingAgent 공식 문서](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/agents#smolagents.ToolCallingAgent) - ToolCallingAgent에 대한 공식 문서

### `LangGraph`란 무엇인가요? [[what-is-langgraph]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/when_to_use_langgraph.md

# `LangGraph`란 무엇인가요? [[what-is-langgraph]]

`LangGraph`는 [LangChain](https://www.langchain.com/)에서 개발한 프레임워크로, **LLM을 통합한 어플리케이션의 제어 흐름을 관리**하기 위해 만들어졌습니다.

## `LangGraph`는 `LangChain`와 어떻게 다른가요? [[is-langgraph-different-from-langchain]]

LangChain은 모델 및 기타 구성 요소와 상호작용하기 위한 표준 인터페이스를 제공하며, 검색, LLM 호출, 도구 호출에 유용합니다.
LangChain의 클래스들은 LangGraph에서 사용될 수 있지만, 반드시 사용해야 하는 것은 아닙니다.

두 패키지는 서로 다른 패키지이며, 각각 독립적으로 사용할 수 있지만, 실제로 온라인에서 찾을 수 있는 대부분의 예제에서는 두 패키지를 함께 사용하는 경우가 많습니다.

## 언제 `LangGraph`를 사용해야 할까요? [[when-should-i-use-langgraph-]]
### 제어 vs 자유 [[control-vs-freedom]]

AI 어플리케이션을 설계할 때, **제어**와 **자유** 사이의 트레이드오프를 고려해야합니다:

- **자유**는 LLM이 더 많은 창의성을 발휘하고 예상치 못한 문제를 해결할 수 있도록 합니다.
- **제어**는 예측 가능한 동작을 보장하고 안전 장치를 유지할 수 있습니다.

*smolagents*의 코드 에이전트는 매우 자유롭습니다. 하나의 액션 단계에서 여러 도구를 호출하거나, 자체적으로 도구를 만들기도 합니다. 하지만 이런 동작은 JSON 기반 에이전트보다 예측하기 어렵고 제어하기 어렵게 만듭니다!

`LangGraph`는 스펙트럼의 반대편에 있으며, 에이전트 실행에 **"제어"**가 필요할 때 빛납니다.

LangGraph는 특히 **어플리케이션에 대한 제어**가 필요할 때 가치가 있습니다. 예측 가능한 프로세스를 따르면서도 LLM의 힘을 활용할 수 있도록 돕는 도구를 제공합니다.

간단히 말하면, 어플리케이션이 특정 방식으로 구성된 일련의 단계를 거쳐야하고, 각 분기점에서 의사결정이 내려져야 한다면, **LangGraph가 그 필요한 구조를 제공해줍니다**.

예를 들어, 문서에 대한 질문에 답할 수 있는 LLM 어시스턴트를 구축하고 싶다고 가정해봅시다.

LLM이 텍스트를 가장 잘 이해하기 때문에, 질문에 답하기 전에 복잡한 형태들(차트, 표)을 텍스트로 변환해야 합니다. 하지만 그 선택은 문서의 유형에 따라 달라집니다!

이러한 분기 흐름은 아래와 같이 표현할 수 있습니다:

> 💡 **팁:** 왼쪽 부분은 도구 호출이 없기 때문에 에이전트가 아닙니다. 그러나 오른쪽 부분은 엑셀 파일을 쿼리하기 위해 코드를 작성해야 하므로 에이전트가 필요합니다.(예: pandas로 변환하고 조작).

이 분기는 결정적이지만, LLM 출력에 따라 조건이 정해지는 비결정적 분기도 설계할 수 있습니다.

LangGraph가 유용한 경우:

- **명시적인 흐름 제어가 필요한 다단계 논리적 추론 프로세스**
- **단계 간 상태 지속이 필요한 어플리케이션**
- **결정론적 로직과 AI 기능을 결합하는 시스템**
- **사람의 개입이 필요한 워크플로우**
- **여러 구성 요소가 함께 작동하는 복잡한 에이전트 아키텍처**

요약하자면, 최대한 **사람이 직접** 각 단계의 출력에 따라 실행할 다음 단계를 설계해야 하는 경우, LangGraph가 가장 적절합니다!

`LangGraph`는 제 생각에 시장에서 가장 프로덕션 준비가 잘 된 에이전트 프레임워크입니다.

## LangGraph는 어떻게 작동하나요? [[how-does-langgraph-work]]

`LangGraph`는 어플리케이션의 흐름을 정의하기 위해 방향성 그래프 구조를 사용합니다:

- **노드(Node)**는 각 처리 단계를 나타냅니다(LLM 호출, 도구 사용, 의사결정 등).
- **엣지(Edge)**는 단계 간 가능한 전환을 정의합니다.
- **상태(State)**는 사용자가 정의하고 유지하며 실행 중 노드 간에 전달됩니다. 다음에 어떤 노드를 실행할지 결정할 때, 현재 상태가 기준입니다.

이러한 기본 구성 요소들은 다음 장에서 더 자세히 살펴보겠습니다!

## 일반 파이썬과 어떻게 다른가요? 왜 LangGraph가 필요한가요? [[how-is-it-different-from-regular-python-why-do-i-need-langgraph]]

"그냥 일반 파이썬 문법으로 if-else 문 쓰면 되지 않나요?"라고 궁금해할 수 있습니다.

기술적으로는 가능하지만, LangGraph는 복잡한 시스템 구축을 위해 일반 파이썬보다 **여러 가지 장점**을 제공합니다. LangGraph 없이도 동일한 어플리케이션을 구축할 수 있지만, LangGraph는 더 쉽게 개발할 수 있는 도구와 추상화를 제공합니다.

예: 상태 관리, 시각화, 로깅(트레이스), 내장형 사용자 개입 처리 기능 등.

### LangGraph의 구성 요소[[building-blocks-of-langgraph]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/building_blocks.md

# LangGraph의 구성 요소[[building-blocks-of-langgraph]]

LangGraph로 애플리케이션을 구축하려면 핵심 구성 요소를 이해해야 합니다. LangGraph 애플리케이션을 구성하는 기본 빌딩 블록을 살펴보겠습니다.

LangGraph 애플리케이션은 **진입점(entrypoint)**에서 시작하며, 실행 흐름에 따라 한 함수에서 다른 함수로 이동하다가 END에 도달합니다.

## 1. 상태[[1-state]]

**상태(State)**는 LangGraph의 중심 개념입니다. 애플리케이션을 통해 흐르는 모든 정보를 나타냅니다. 

```python
from typing_extensions import TypedDict

class State(TypedDict):
    graph_state: str
```

상태는 **사용자 정의**이므로, 의사결정 과정에 필요한 모든 데이터를 포함하도록 신중하게 설계해야 합니다!

> 💡 **팁:** 애플리케이션이 단계 간에 추적해야 할 정보가 무엇인지 신중하게 고민하세요.

## 2. 노드[[2-nodes]]

**노드(Node)**는 파이썬 함수입니다. 각 노드는:
- 상태를 입력으로 받습니다
- 작업을 수행합니다
- 상태 업데이트를 반환합니다

```python
def node_1(state):
    print("---Node 1---")
    return {"graph_state": state['graph_state'] +" I am"}

def node_2(state):
    print("---Node 2---")
    return {"graph_state": state['graph_state'] +" happy!"}

def node_3(state):
    print("---Node 3---")
    return {"graph_state": state['graph_state'] +" sad!"}
```

예를 들어, 노드는 다음과 같은 역할을 할 수 있습니다:
- **LLM 호출**: 텍스트 생성 또는 의사결정
- **도구 호출**: 외부 시스템과 상호작용
- **조건부 로직**: 다음 단계 결정
- **사람 개입**: 사용자 입력 받기

> 💡 **정보:** START와 END처럼 전체 워크플로우에 필수적인 일부 노드는 langGraph에서 직접 제공합니다. 

## 3. 엣지[[3-edges]]

**엣지(Edge)**는 노드를 연결하며, 그래프 내에서 가능한 경로를 정의합니다:

```python
import random
from typing import Literal

def decide_mood(state) -> Literal["node_2", "node_3"]:
    
    # 종종 상태를 사용해 다음에 방문할 노드를 결정합니다
    user_input = state['graph_state'] 
    
    # 여기서는 노드 2, 3 사이를 50:50으로 분기합니다
    if random.random() 

하지만 가장 중요한 것은 실제로 호출하는 것입니다:
```python
graph.invoke({"graph_state" : "Hi, this is Lance."})
```
출력 :
```
---Node 1---
---Node 3---
{'graph_state': 'Hi, this is Lance. I am sad!'}
```

## 다음 단계[[whats-next]]

다음 섹션에서는 이러한 개념을 실제로 적용하여 첫 번째 그래프를 만들어보겠습니다. 이 그래프는 Alfred가 이메일을 받아 분류하고, 정상 메일이라면 임시 답장을 작성할 수 있도록 합니다.

### 나만의 첫 LangGraph 만들기[[building-your-first-langgraph]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/first_graph.md

# 나만의 첫 LangGraph 만들기[[building-your-first-langgraph]]

이제 LangGraph의 구성 요소를 이해했으니, 실제로 적용해보며 첫 번째 기능적 그래프를 만들어봅시다. 여기서는 알프레드의 이메일 처리 시스템을 구현할 것입니다. 알프레드는 다음과 같은 작업을 수행해야 합니다:

1. 들어오는 이메일 읽기
2. 스팸 또는 정상 메일로 분류하기
3. 정상 메일에 대한 임시 답장 작성하기
4. 정상 메일일 경우 Mr. Wayne에게 정보 전달(출력만)

이 예제는 LLM 기반 의사결정을 포함하는 LangGraph 워크플로우를 어떻게 구조화할 수 있는지를 보여줍니다. (여기서는 별도의 툴이 사용되지 않으므로 Agent라고 부르진 않지만, LangGraph 프레임워크 학습에 초점을 둡니다.)

이 노트북을 따라가며 Google Colab에서 코드를 실행해볼 수 있습니다.

## 우리가 만들 워크플로우[[our-workflow]]

아래와 같은 워크플로우를 구현합니다:

## 환경 설정[[setting-up-our-environment]]

먼저 필요한 패키지를 설치합니다:

```python
%pip install langgraph langchain_openai
```

필요한 모듈을 임포트합니다:

```python
import os
from typing import TypedDict, List, Dict, Any, Optional
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
```

## 1단계: 상태(State) 정의하기[[step-1-define-our-state]]

이메일 처리 워크플로우에서 알프레드가 추적해야 할 정보를 정의합니다:

```python
class EmailState(TypedDict):
    # 처리 중인 이메일
    email: Dict[str, Any]  # 제목, 발신자, 본문 등 포함

    # 이메일 분류(문의, 불만 등)
    email_category: Optional[str]

    # 스팸으로 분류된 이유
    spam_reason: Optional[str]

    # 분석 및 결정
    is_spam: Optional[bool]
    
    # 답장 생성
    email_draft: Optional[str]
    
    # 처리 메타데이터
    messages: List[Dict[str, Any]]  # LLM과의 대화 추적
```

> 💡 **팁:** 상태는 중요한 정보를 모두 담되, 불필요하게 비대해지지 않도록 하세요.

## 2단계: 노드 정의하기[[step-2-define-our-nodes]]

이제 각 처리 단계를 담당할 함수를 만듭니다:

```python
# LLM 초기화
model = ChatOpenAI(temperature=0)

def read_email(state: EmailState):
    """알프레드가 들어온 이메일을 읽고 기록합니다."""
    email = state["email"]
    print(f"알프레드가 {email['sender']}로부터 온 메일(제목: {email['subject']})을 처리 중입니다.")
    return {}

def classify_email(state: EmailState):
    """알프레드가 LLM을 사용해 이메일을 스팸/정상으로 분류합니다."""
    email = state["email"]
    prompt = f"""
    집사 알프레드로서, 이 이메일을 분석해 스팸인지 정상인지 판단하세요.
    
    이메일:
    From: {email['sender']}
    Subject: {email['subject']}
    Body: {email['body']}
    
    먼저 이 이메일이 스팸인지 판단하고, 스팸이라면 그 이유를 설명하세요.
    정상 메일이라면 (문의, 불만, 감사, 요청 등) 카테고리도 분류하세요.
    """
    messages = [HumanMessage(content=prompt)]
    response = model.invoke(messages)
    response_text = response.content.lower()
    is_spam = "spam" in response_text and "not spam" not in response_text
    spam_reason = None
    if is_spam and "reason:" in response_text:
        spam_reason = response_text.split("reason:")[1].strip()
    email_category = None
    if not is_spam:
        categories = ["inquiry", "complaint", "thank you", "request", "information"]
        for category in categories:
            if category in response_text:
                email_category = category
                break
    new_messages = state.get("messages", []) + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": response.content}
    ]
    return {
        "is_spam": is_spam,
        "spam_reason": spam_reason,
        "email_category": email_category,
        "messages": new_messages
    }

def handle_spam(state: EmailState):
    """알프레드가 스팸 메일을 처리합니다."""
    print(f"알프레드는 이 메일을 스팸으로 분류했습니다. 이유: {state['spam_reason']}")
    print("이 메일은 스팸 폴더로 이동되었습니다.")
    return {}

def draft_response(state: EmailState):
    """알프레드가 정상 메일에 임시 답장을 작성합니다."""
    email = state["email"]
    category = state["email_category"] or "일반"
    prompt = f"""
    집사 알프레드로서, 이 이메일에 정중한 임시 답장을 작성하세요.
    
    이메일:
    From: {email['sender']}
    Subject: {email['subject']}
    Body: {email['body']}
    
    이 이메일의 카테고리: {category}
    
    Mr. Hugg가 검토 후 개인화할 수 있도록 간단하고 전문적인 답장을 작성하세요.
    """
    messages = [HumanMessage(content=prompt)]
    response = model.invoke(messages)
    new_messages = state.get("messages", []) + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": response.content}
    ]
    return {
        "email_draft": response.content,
        "messages": new_messages
    }

def notify_mr_hugg(state: EmailState):
    """알프레드가 Mr. Hugg에게 메일과 임시 답장을 전달합니다."""
    email = state["email"]
    print("\n" + "="*50)
    print(f"주인님, {email['sender']}로부터 메일이 도착했습니다.")
    print(f"제목: {email['subject']}")
    print(f"카테고리: {state['email_category']}")
    print("\n검토하실 임시 답장을 준비했습니다:")
    print("-"*50)
    print(state["email_draft"])
    print("="*50 + "\n")
    return {}
```

## 3단계: 분기 로직 정의하기[[step-3-define-our-routing-logic]]

분류 후 어떤 경로로 갈지 결정하는 함수를 만듭니다:

```python
def route_email(state: EmailState) -> str:
    """스팸 분류 결과에 따라 다음 단계를 결정합니다."""
    if state["is_spam"]:
        return "spam"
    else:
        return "legitimate"
```

> 💡 **참고:** 이 라우팅 함수는 LangGraph가 분류 노드 이후 어떤 엣지를 따라갈지 결정할 때 호출됩니다. 반환값은 조건부 엣지 매핑의 키와 일치해야 합니다.

## 4단계: StateGraph 생성 및 엣지 정의[[step-4-create-the-stategraph-and-define-edges]]

이제 모든 것을 연결합니다:

```python
# 그래프 생성
email_graph = StateGraph(EmailState)

# 노드 추가
email_graph.add_node("read_email", read_email)
email_graph.add_node("classify_email", classify_email)
email_graph.add_node("handle_spam", handle_spam)
email_graph.add_node("draft_response", draft_response)
email_graph.add_node("notify_mr_hugg", notify_mr_hugg)

# 엣지 시작
email_graph.add_edge(START, "read_email")
# 흐름 정의
email_graph.add_edge("read_email", "classify_email")

# 분기 추가
email_graph.add_conditional_edges(
    "classify_email",
    route_email,
    {
        "spam": "handle_spam",
        "legitimate": "draft_response"
    }
)

# 마지막 엣지
email_graph.add_edge("handle_spam", END)
email_graph.add_edge("draft_response", "notify_mr_hugg")
email_graph.add_edge("notify_mr_hugg", END)

# 그래프 컴파일
compiled_graph = email_graph.compile()
```

LangGraph에서 제공하는 특수한 `END` 노드를 사용합니다. 이는 워크플로우가 완료되는 지점을 나타냅니다.

## 5단계: 어플리케이션 실행하기[[step-5-run-the-application]]

정상 메일과 스팸 메일로 그래프를 테스트해봅니다:

```python
# 정상 메일 예시
legitimate_email = {
    "sender": "john.smith@example.com",
    "subject": "서비스 문의",
    "body": "안녕하세요 Mr. Hugg, 동료의 추천으로 연락드립니다. 귀하의 컨설팅 서비스에 대해 더 알고 싶습니다. 다음 주에 통화 가능할까요? 감사합니다, John Smith"
}

# 스팸 메일 예시
spam_email = {
    "sender": "winner@lottery-intl.com",
    "subject": "당신은 $5,000,000에 당첨되었습니다!!!",
    "body": "축하합니다! 국제 복권에 당첨되셨습니다! 상금을 받으시려면 은행 정보와 수수료 $100을 보내주세요."
}

# 정상 메일 처리
print("\n정상 메일 처리 중...")
legitimate_result = compiled_graph.invoke({
    "email": legitimate_email,
    "is_spam": None,
    "spam_reason": None,
    "email_category": None,
    "email_draft": None,
    "messages": []
})

# 스팸 메일 처리
print("\n스팸 메일 처리 중...")
spam_result = compiled_graph.invoke({
    "email": spam_email,
    "is_spam": None,
    "spam_reason": None,
    "email_category": None,
    "email_draft": None,
    "messages": []
})
```

## 6단계: Langfuse로 메일 분류 에이전트 관찰하기 📡[[step-6-inspecting-our-mail-sorting-agent-with-langfuse]]

알프레드는 메일 분류 에이전트를 다듬으면서 디버깅에 지쳐갑니다. 에이전트는 본질적으로 예측 불가능하고 추적이 어렵기 때문입니다. 하지만 궁극의 스팸 감지 에이전트를 만들어 프로덕션에 배포하려면, 향후 모니터링과 분석을 위한 강력한 추적 기능이 필요합니다.

이를 위해 알프레드는 [Langfuse](https://langfuse.com/)와 같은 관찰 도구를 사용할 수 있습니다.

먼저 Langfuse를 설치합니다:
```python
%pip install -q langfuse
```

LangChain도 설치합니다(Langfuse 사용 시 필요):
```python
%pip install langchain
```

Langfuse API 키와 호스트 주소를 환경 변수로 추가합니다. [Langfuse Cloud](https://cloud.langfuse.com) 가입 또는 [셀프 호스팅](https://langfuse.com/self-hosting)으로 키를 발급받을 수 있습니다.

```python
import os
# 프로젝트 설정 페이지에서 키를 확인하세요: https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 EU 리전
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 US 리전
```

[Langfuse `callback_handler`](https://langfuse.com/docs/integrations/langchain/tracing#add-langfuse-to-your-langchain-application)를 설정하고, 그래프 호출 시 `config={"callbacks": [langfuse_handler]}`로 인스트루먼트합니다.

```python   
from langfuse.langchain import CallbackHandler

# LangGraph/Langchain용 Langfuse CallbackHandler 초기화(추적용)
langfuse_handler = CallbackHandler()

# 정상 메일 처리
legitimate_result = compiled_graph.invoke(
    input={"email": legitimate_email, "is_spam": None, "spam_reason": None, "email_category": None, "draft_response": None, "messages": []},
    config={"callbacks": [langfuse_handler]}
)
```

이제 알프레드는 LangGraph의 실행 내역을 Langfuse에 기록하여 에이전트의 동작을 완전히 파악할 수 있습니다. 이로써 이전 실행을 다시 살펴보고 메일 분류 에이전트를 더욱 개선할 수 있습니다.

![Langfuse 예시 트레이스](https://langfuse.com/images/cookbook/huggingface-agent-course/langgraph-trace-legit.png)

_[정상 메일 트레이스 공개 링크](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/f5d6d72e-20af-4357-b232-af44c3728a7b?timestamp=2025-03-17T10%3A13%3A28.413Z&observation=6997ba69-043f-4f77-9445-700a033afba1)_

## 그래프 시각화[[visualizing-our-graph]]

LangGraph는 워크플로우 구조를 시각화하여 이해와 디버깅을 돕습니다:

```python
compiled_graph.get_graph().draw_mermaid_png()
```

이렇게 하면 노드 간 연결과 조건부 경로를 한눈에 볼 수 있습니다.

## 우리가 만든 것[[what-weve-built]]

우리는 다음과 같은 이메일 처리 워크플로우를 완성했습니다:

1. 들어온 이메일을 받음
2. LLM으로 스팸/정상 분류
3. 스팸은 폐기
4. 정상 메일은 답장 작성 후 Mr. Hugg에게 전달

이처럼 LangGraph를 활용하면 LLM 기반의 복잡한 워크플로우도 명확하고 구조적으로 오케스트레이션할 수 있습니다.

## 핵심 요약[[key-takeaways]]

- **상태 관리**: 이메일 처리의 모든 측면을 추적할 수 있도록 상태를 정의
- **노드 구현**: LLM과 상호작용하는 기능적 노드 구현
- **조건부 분기**: 이메일 분류 결과에 따라 분기 로직 구현
- **종료 상태**: END 노드로 워크플로우 종료 지점 표시

## 다음 단계[[whats-next]]

다음 섹션에서는 LangGraph의 고급 기능(사람과의 상호작용, 다중 조건 분기 등)을 다룹니다.

### 결론[[conclusion]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/conclusion.md

# 결론[[conclusion]]

2단원의 LangGraph 모듈을 완료하신 것을 축하합니다! 🥳

이제 LangGraph로 구조화된 워크플로우를 구축하는 기본기를 마스터하셨으며, 이를 실제 프로덕션 환경에 적용할 수 있습니다.

이 모듈은 LangGraph 여정의 시작에 불과합니다. 심화 주제를 원하시는 분들을 위해 추천드립니다:

- [공식 LangGraph 문서](https://github.com/langchain-ai/langgraph) 탐색
- LangChain 아카데미의 종합적인 [LangGraph 소개](https://academy.langchain.com/courses/intro-to-langgraph) 강의 수강
- 직접 무언가를 만들어보세요!

다음 단원에서는 실제 사용 사례를 탐구합니다. 이제 이론을 넘어 실전으로 나아갈 시간입니다!

**여러분의 강의에 대한 생각과 제안 사항들을 매우 환영합니다**. 피드백이 있으시면 👉 [이 양식](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)을 작성해주세요!

### 존경하는 여러분! 🎩🦇
배움을 멈추지 마세요 🤗

-알프레드-

### 문서 분석 그래프[[document-analysis-graph]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/document_analysis_agent.md

# 문서 분석 그래프[[document-analysis-graph]]

저 알프레드는 Mr.웨인님의 신뢰받는 집사로서, 그분의 다양한 문서 업무를 어떻게 지원할지 기록해두었습니다. 그가 밤 활동을 하러 나간 동안, 저는 모든 서류, 훈련 일정, 영양 계획을 분석하고 정리합니다.

그분이 외출하기 전, 이번 주 훈련 프로그램이 적힌 쪽지를 남기셨습니다. 그 쪽지를 보고 저는 내일 식사를 위한 **메뉴**를 직접 짜기로 했죠.

앞으로도 이런 일이 있을 때를 대비해, LangGraph를 활용해 웨인님을 위한 문서 분석 시스템을 만들어봅시다. 이 시스템은 다음을 수행 할 수 있습니다:

1. 이미지 문서 처리
2. 비전 모델(비전 언어 모델)로 텍스트 추출
3. 필요시 계산 수행(일반 툴 시연)
4. 내용 분석 및 요약 제공
5. 문서 관련 특정 지시 실행

## 집사의 워크플로우[[the-butlers-workflow]]

만들 워크플로우의 구조는 다음과 같습니다:

![집사의 문서 분석 워크플로우](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/alfred_flow.png)

이 노트북을 따라가며 Google Colab에서 코드를 실행해볼 수 있습니다.

## 환경 설정[[setting-up-the-environment]]

```python
%pip install langgraph langchain_openai langchain_core
```
임포트 :
```python
import base64
from typing import List, TypedDict, Annotated, Optional
from langchain_openai import ChatOpenAI
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
from langgraph.graph.message import add_messages
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from IPython.display import Image, display
```

## 에이전트 상태 정의[[defining-agent's-state]]

이 상태는 앞서 본 것보다 조금 더 복잡합니다.
`AnyMessage`는 Langchain의 메시지 클래스이고, `add_messages`는 최신 메시지를 누적하는 연산자입니다.

LangGraph에서는 상태에 연산자를 추가해 상호작용 방식을 정의할 수 있습니다.

```python
class AgentState(TypedDict):
    # 제공된 문서
    input_file: Optional[str]  # 파일 경로(PDF/PNG)
    messages: Annotated[list[AnyMessage], add_messages]
```

## 툴 준비하기[[preparing-tools]]

```python
vision_llm = ChatOpenAI(model="gpt-4o")

def extract_text(img_path: str) -> str:
    """
    멀티모달 모델로 이미지 파일에서 텍스트 추출
    
    Wayne 주인님은 종종 훈련 일정이나 식단이 적힌 쪽지를 남깁니다.
    이 기능으로 내용을 제대로 분석할 수 있습니다.
    """
    all_text = ""
    try:
        # 이미지를 읽고 base64로 인코딩
        with open(img_path, "rb") as image_file:
            image_bytes = image_file.read()

        image_base64 = base64.b64encode(image_bytes).decode("utf-8")

        # base64 이미지 데이터를 포함한 프롬프트 준비
        message = [
            HumanMessage(
                content=[
                    {
                        "type": "text",
                        "text": (
                            "이 이미지에서 모든 텍스트를 추출하세요. 설명 없이 추출된 텍스트만 반환하세요."
                        ),
                    },
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{image_base64}"
                        },
                    },
                ]
            )
        ]

        # 비전 모델 호출
        response = vision_llm.invoke(message)

        # 추출된 텍스트 추가
        all_text += response.content + "\n\n"

        return all_text.strip()
    except Exception as e:
        # 집사는 오류도 우아하게 처리해야 합니다
        error_msg = f"텍스트 추출 오류: {str(e)}"
        print(error_msg)
        return ""

def divide(a: int, b: int) -> float:
    """주인님의 계산 요청을 위한 나눗셈 함수"""
    return a / b

# 집사, 도구(tool) 장착
tools = [
    divide,
    extract_text
]

llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
```

## 노드[[the-nodes]]

```python
def assistant(state: AgentState):
    # 시스템 메시지
    textual_description_of_tool="""
extract_text(img_path: str) -> str:
    멀티모달 모델로 이미지 파일에서 텍스트 추출

    Args:
        img_path: 로컬 이미지 파일 경로(문자열)

    Returns:
        각 이미지에서 추출된 텍스트를 합친 문자열
divide(a: int, b: int) -> float:
    a를 b로 나눔
"""
    image=state["input_file"]
    sys_msg = SystemMessage(content=f"당신은 Mr. Wayne과 배트맨을 섬기는 친절한 집사 알프레드입니다. 아래 도구로 문서를 분석하고 계산을 수행할 수 있습니다:\n{textual_description_of_tool} \n 현재 로드된 이미지는: {image}")

    return {
        "messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
        "input_file": state["input_file"]
    }
```

## ReAct 패턴: 집사의 지원 방식[[the-react-pattern-how-i-assist-mr-wayne]]

이 에이전트는 ReAct 패턴(Reason-Act-Observe)을 따릅니다.

1. 문서와 요청을 **이해(Reason)**
2. 적절한 도구를 **실행(Act)**
3. 결과를 **관찰(Observe)**
4. 필요시 반복

아주 간단한 LangGraph 에이전트 구현 예시입니다.

```python
# 그래프 생성
builder = StateGraph(AgentState)

# 노드 정의: 실제 작업을 수행
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# 엣지 정의: 제어 흐름 결정
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # 최신 메시지가 도구를 필요로 하면 tools로 라우팅
    # 아니면 직접 응답
    tools_condition,
)
builder.add_edge("tools", "assistant")
react_graph = builder.compile()

# 집사의 사고 과정을 시각화
display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))
```

`tools` 노드는 도구 리스트를, `assistant` 노드는 도구가 바인딩된 모델을 의미합니다.
그래프에는 `assistant`와 `tools` 노드가 있습니다.

`tools_condition` 엣지는 모델이 도구를 호출하면 tools로, 아니면 End로 흐름을 보냅니다.

이제 한 단계 더 추가합니다:

`tools` 노드를 다시 `assistant`로 연결해 루프를 만듭니다.

- `assistant` 노드 실행 후, `tools_condition`이 모델 출력이 도구 호출인지 확인합니다.
- 도구 호출이면 흐름이 `tools` 노드로 이동합니다.
- `tools` 노드는 다시 `assistant`로 연결됩니다.
- 이 루프는 모델이 도구 호출을 결정하는 한 계속됩니다.
- 모델 응답이 도구 호출이 아니면 흐름이 END로 이동해 프로세스가 종료됩니다.

![ReAct 패턴](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/Agent.png)

## 집사의 실제 활용 예시[[the-butler-in-action]]

### 예시 1: 간단한 계산[[example-1-simple-calculations]]

아래는 LangGraph에서 도구를 사용하는 간단한 예시입니다.

```python
messages = [HumanMessage(content="6790을 5로 나눠줘")]
messages = react_graph.invoke({"messages": messages, "input_file": None})

# 메시지 출력
for m in messages['messages']:
    m.pretty_print()
```

대화 예시:

```
Human: 6790을 5로 나눠줘

AI Tool Call: divide(a=6790, b=5)

Tool Response: 1358.0

Alfred: 6790을 5로 나누면 1358.0입니다.
```

### 예시 2: Wayne 주인님의 훈련 문서 분석[[example-2-analyzing-master-waynes-training-documents]]

주인님이 훈련 및 식단 쪽지를 남겼을 때:

```python
messages = [HumanMessage(content="제공된 이미지에 있는 Wayne 주인님의 쪽지에 따라 저녁 메뉴에 필요한 재료 목록을 알려줘.")]
messages = react_graph.invoke({"messages": messages, "input_file": "Batman_training_and_meals.png"})
```

대화 예시:

```
Human: 제공된 이미지에 있는 Wayne 주인님의 쪽지에 따라 저녁 메뉴에 필요한 재료 목록을 알려줘.

AI Tool Call: extract_text(img_path="Batman_training_and_meals.png")

Tool Response: [훈련 일정 및 메뉴 세부 정보가 추출됨]

Alfred: 저녁 메뉴를 위해 다음 재료를 구입하세요:

1. 목초 사육 소고기 등심 스테이크
2. 유기농 시금치
3. 피키요 고추
4. 감자(오븐에 구울 허브 감자용)
5. 피쉬 오일(2그램)

스테이크는 목초 사육, 시금치와 고추는 유기농으로 준비하면 최고의 식사가 됩니다.
```

## 핵심 요약[[key-takeaways]]

나만의 문서 분석 집사를 만들고 싶다면 다음을 고려하세요:

1. **문서 관련 작업별로 명확한 도구 정의**
2. **도구 호출 간 맥락 유지를 위한 견고한 상태 추적기**
3. **도구 실패 시 오류 처리 고려**
4. **이전 상호작용 맥락 유지(add_messages 연산자 활용)**

이 원칙을 따르면 Wayne 저택에 어울리는 문서 분석 서비스를 제공할 수 있습니다.

*이 설명이 도움이 되었길 바랍니다. 이제 주인님의 망토를 다림질하러 가보겠습니다.*

### `LangGraph` 소개[[introduction-to-langgraph]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/introduction.md

# `LangGraph` 소개[[introduction-to-langgraph]]

다음 단계에 오신 것을 환영합니다! 이 단원에서는 복잡한 LLM 워크플로우를 구조화하고 조정, 관리할 수 있도록 설계된 [`LangGraph`](https://github.com/langchain-ai/langgraph) 프레임워크를 사용하여 **어플리케이션을 구축하는 방법**을 배우게 됩니다.

`LangGraph`는 에이전트의 흐름을 **직접 제어**할 수 있는 도구를 제공하여 **프로덕션 수준**의 어플리케이션을 만들 수 있게 해주는 프레임워크입니다.

## 모듈 개요[[module-overview]]

이 단원에서 함께 살펴볼 주제는 다음과 같습니다

### 1️⃣ LangGraph란 무엇이며, 언제 사용해야 할까?[[1-what-is-langgraph-and-when-to-use-it]]
### 2️⃣ LangGraph의 구성 요소[[2-building-blocks-of-langgraph]]
### 3️⃣ 내 메일을 대신 분류해주는 집사[[3-alfred-the-mail-sorting-butler]]
### 4️⃣ 알프레드, 문서 분석 에이전트[[4-alfred-the-document-analyst-agent]]
### 5️⃣ 퀴즈 [[5-quiz]]

이 섹션의 예제들은 강력한 LLM/VLM 모델이 필요합니다. 이 코스에서는 langGraph와 가장 호환성이 좋은 GPT-4o API를 사용해 실행하였습니다.

이 단원이 끝나면, 여러분은 견고하고 체계적인, 프로덕션에 바로 투입 가능한 어플리케이션을 만들 수 있게 됩니다!

이 섹션은 LangGraph의 입문 과정이니, 더 심화된 주제는 무료 LangChain 아카데미 강의 [LangGraph 소개](https://academy.langchain.com/courses/intro-to-langgraph)에서 확인할 수 있습니다.

그럼 시작해볼까요?

## 참고 자료[[resources]]

- [LangGraph Agents](https://langchain-ai.github.io/langgraph/) - LangGraph 에이전트 예제
- [LangChain academy](https://academy.langchain.com/courses/intro-to-langgraph) - LangChain에서 제공하는 LangGraph 전체 강의

### LangGraph 이해도 테스트 [[test-your-understanding-of-langgraph]]
https://huggingface.co/learn/agents-course/ko/unit2/langgraph/quiz1.md

# LangGraph 이해도 테스트 [[test-your-understanding-of-langgraph]]

`LangGraph`에 대한 이해도를 간단한 퀴즈로 확인해보세요! 지금까지 배운 핵심 개념을 복습하는 데 도움이 됩니다.

이 퀴즈는 선택 사항이며 채점되지 않습니다.

### Q1: LangGraph의 주요 목적은 무엇인가요? [[q1-what-is-the-primary-purpose-of-langgraph]]
LangGraph가 무엇을 위해 설계되었는지 가장 잘 설명하는 문장은?

---

### Q2: "제어 vs 자유" 관점에서 LangGraph는 어디에 속하나요? [[q2-in-the-context-of-the-control-vs-freedom-trade-off-where-does-langgraph-stand]]
다음 중 LangGraph의 에이전트 설계 접근 방식을 가장 잘 설명한 문장은?

---

### Q3: LangGraph에서 상태(State)의 역할은 무엇인가요? [[q3-what-role-does-state-play-in-langgraph]]
다음 중 LangGraph에서 상태(State)에 대한 설명으로 가장 적절한 것은?

### Q4: LangGraph에서 조건부 엣지(Conditional Edge)란 무엇인가요? [[q4-what-is-a-conditional-edge-in-langgraph]]
조건부 엣지에 대한 설명으로 가장 적절한 것은?

---

### Q5: LangGraph는 LLM의 환각(hallucination) 문제를 어떻게 해결하는 데 도움이 되나요? [[q5-how-does-langgraph-help-address-the-hallucination-problem-in-llms]]
환각에 대한 설명으로 가장 적절한 것은?

축하합니다! 🎉 틀린 문제가 있다면, 앞에서 다룬 내용들을 다시 검토해보세요. 이제 LangGraph의 더 심화 기능을 알아보고, 더 복잡한 에이전트 워크플로우를 구축하는 방법을 배워봅시다.

### 마지막 Unit에 오신 것을 환영합니다[[introduction]]
https://huggingface.co/learn/agents-course/ko/unit4/introduction.md

# 마지막 Unit에 오신 것을 환영합니다[[introduction]]

에이전트 코스의 마지막 Unit에 오신 것을 환영합니다! 🎉

지금까지 여러분은 AI 에이전트의 구성 요소를 이해하고 직접 만들어보면서 **AI 에이전트에 대한 탄탄한 기초**를 다졌습니다. 이제 이 지식을 바탕으로 **강력한 에이전트를 구축**하고, 빠르게 발전하는 AI 분야의 최신 동향을 따라갈 준비가 되었습니다.

이번 Unit에서는 지금까지 배운 내용을 실제로 적용하는 데 중점을 둡니다. 이것이 여러분의 **마지막 실습 프로젝트**이며, 이 프로젝트를 완료해야 **과정 수료증**을 받으실 수 있습니다.

## 도전 과제는 무엇인가요?[[whats-the-challenge]]

여러분은 자신만의 에이전트를 만들고 **[GAIA 벤치마크](https://huggingface.co/spaces/gaia-benchmark/leaderboard)의 일부를 사용해 성능을 평가**하게 됩니다.

과정을 성공적으로 수료하려면 여러분의 에이전트가 벤치마크에서 **30% 이상**의 점수를 받아야 합니다. 이 목표를 달성하면 여러분의 전문성을 공식적으로 인정하는 **수료 증명서**를 받게 됩니다. 🏅

또한, 다른 동료들과 비교하여 자신의 순위를 확인해보세요! 전용 **[학생 리더보드](https://huggingface.co/spaces/agents-course/Students_leaderboard)**에서 점수를 제출하고 커뮤니티의 진행 상황을 확인할 수 있습니다.

> ** 🚨 주의: 심화 및 실습 중심 Unit**
>
> 이번 Unit은 이전보다 더 실용적이고 실습 위주로 진행된다는 점을 유의해 주세요. 이 섹션을 성공적으로 마치려면 **더 높은 수준의 코딩 지식**이 필요하며, 이전 파트와 비교해 **명시적인 설명이 적으므로** 스스로 과제를 해결해 나가야 합니다.

흥미진진한가요? 이제 시작해봅시다! 🚀

### 함수 호출이란 무엇인가[[what-is-function-calling]]
https://huggingface.co/learn/agents-course/ko/bonus-unit1/what-is-function-calling.md

# 함수 호출이란 무엇인가[[what-is-function-calling]]

함수 호출은 **LLM이 환경과 상호작용하여 동작을 수행하는 방법**입니다. 처음에는 [GPT-4에서 도입되었으며](https://openai.com/index/function-calling-and-other-api-updates/), 이후 다른 모델에서도 구현되었습니다.

에이전트의 도구처럼, 함수 호출은 모델에게 **주어진 환경에서 동작을 수행할 수 있는 능력**을 제공합니다. 다만 함수 호출 능력은 **모델 스스로 학습을 통해 체득한 것**이므로, 다른 에이전트 기법들보다 **프롬프트에 덜 의존**한다는 점입니다.

Unit 1에서는 에이전트가 **도구를 사용하는 법을 학습한 것이 아니며**, 우리는 그저 도구 목록을 모델에게 제공했고, 모델이 **이 도구들을 활용해 계획을 생성할 것이라는 일반화 능력**에 의존했습니다.

여기서는 **함수 호출을 통해 에이전트가 도구를 사용할 수 있도록 미세 조정(학습)**합니다.

## 모델은 동작 수행을 어떻게 "학습"할까요?[[how-does-the-model-learn-to-take-an-action]]

Unit 1에서는 에이전트의 일반적인 워크플로우를 살펴보았습니다. 사용자가 에이전트에 도구를 제공하고 질의를 전달하면, 모델은 다음 과정을 반복합니다.

1. *사고(Think)* : 목표를 달성하기 위해 어떤 동작을 수행해야 할지 고민합니다.  
2. *행동(Act)* : 정해진 형식에 맞춰 행동을 수행하고, 일단 자신의 텍스트 응답 생성은 멈춥니다.
3. *관찰(Observe)* : 행동의 실행 결과를 외부로부터 받아옵니다.

API를 통한 “일반적인” 대화는 아래처럼 사용자("user")와 어시스턴트("assistant")가 메시지를 주고받는 형태로 구성됩니다.

```python
conversation = [
    {"role": "user", "content": "I need help with my order"},
    {"role": "assistant", "content": "I'd be happy to help. Could you provide your order number?"},
    {"role": "user", "content": "It's ORDER-123"},
]
```

함수 호출은 이 대화의 흐름에 **새로운 역할("role")**을 추가합니다!

1. **행동(Action)**을 위한 새로운 역할  
2. **관찰(Observation)**을 위한 새로운 역할  

[Mistral API](https://docs.mistral.ai/capabilities/function_calling/)를 예로 들면, 대화 구조가 이렇게 바뀌게 됩니다.

```python
conversation = [
    {
        "role": "user",
        "content": "What's the status of my transaction T1001?"
    },
    {
        "role": "assistant",
        "content": "",
        "function_call": {
            "name": "retrieve_payment_status",
            "arguments": "{\"transaction_id\": \"T1001\"}"
        }
    },
    {
        "role": "tool",
        "name": "retrieve_payment_status",
        "content": "{\"status\": \"Paid\"}"
    },
    {
        "role": "assistant",
        "content": "Your transaction T1001 has been successfully paid."
    }
]
```
> ...하지만 방금 함수 호출에 '새로운 역할'이 추가된다고 하지 않았나요? 

**그렇기도 하고 아니기도 합니다.** 이 경우를 비롯해 많은 API에서, 모델은 수행할 동작을 "assistant" 메시지로 출력합니다. 그러면 이후 채팅 템플릿이 이 메시지를 함수 호출을 위한 **특수 토큰**으로 변환하여 처리합니다.

- `[AVAILABLE_TOOLS]` – 사용 가능한 도구 목록 시작  
- `[/AVAILABLE_TOOLS]` – 사용 가능한 도구 목록 종료  
- `[TOOL_CALLS]` – 도구를 호출(즉, **Action** 수행)  
- `[TOOL_RESULTS]` – 동작 결과(“Observe”)  
- `[/TOOL_RESULTS]` – 관찰 종료(이제 모델이 이어서 응답을 생성할 수 있음)  

강의에서 함수 호출을 다시 다룰 예정이지만, 더 깊이 살펴보고 싶다면 [Mistral의 공식 문서](https://docs.mistral.ai/capabilities/function_calling/)를 참고하세요.

---
이제 함수 호출이 무엇이고 어떻게 작동하는지 배웠으니, 아직 해당 기능이 없는 모델인 [google/gemma-2-2b-it]([google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it)에 새로운 특수 토큰을 덧붙이는 방식으로 함수 호출 기능을 직접 추가해보겠습니다.

이를 위해서는 **먼저 미세 조정과 LoRA**를 이해해야 합니다.

### 소개[[introduction]]
https://huggingface.co/learn/agents-course/ko/bonus-unit1/introduction.md

# 소개[[introduction]]

![Bonus Unit 1 Thumbnail](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit1/thumbnail.jpg)

첫 번째 **Bonus Unit**에 오신 것을 환영합니다. 여기에서 **함수 호출을 위한 대형 언어 모델(LLM)을 미세 조정하는 방법**을 배우게 됩니다.

대형 언어 모델(LLM)에서 함수 호출은 *필수 학습* 기술이 되어 가고 있습니다. 

여기서 핵심은 Unit 1에서 했던 것처럼 프롬프트 기반 접근 방식에만 의존하는 대신, 함수 호출을 통해 **학습 과정에서 직접 행동을 수행하고 관찰된 정보를 해석하도록** 모델을 훈련시켜 AI의 견고성을 높인다는 점입니다.

> **Bonus Unit을 언제 진행해야 하나요?**
>
> 이 섹션은 **선택 사항**이며 Unit 1보다 **심화된 내용**을 다루므로, 지금 바로 진행하거나 이 과정을 통해 지식이 더 쌓인 후에 다시 살펴보셔도 좋습니다.  
>  
> 하지만 걱정하지 마세요. 이 Bonus Unit은 필요한 모든 정보를 담고 있어, 미세 조정의 내부 작동 방식을 아직 배우지 않으셨더라도 함수 호출을 위한 핵심 개념을 하나하나 안내해 드립니다.

Bonus Unit을 가장 효과적으로 따라가려면:

1. 아직 Transformers로 대형 언어 모델(LLM)을 미세 조정하는 방법을 모른다면 [여기](https://huggingface.co/learn/nlp-course/chapter3/1?fw=pt)를 확인하세요. 

2. `SFTTrainer`를 사용하여 모델을 미세 조정하는 방법을 알고 싶다면 [공식 문서](https://huggingface.co/learn/nlp-course/en/chapter11/1)를 참조하세요. 

---

## 학습 내용[[what-you’ll-learn]]

1. **함수 호출**  
   최신 LLM이 대화를 구조화하여 **도구**를 호출할 수 있게 하는 방법을 배웁니다.

2. **LoRA (저차원 적응)**  
   계산 및 저장 오버헤드를 줄여주는 **경량화되고 효율적인** 미세 조정 방법입니다. LoRA는 대형 모델을 *더 빠르고 저렴하게 훈련하고, 쉽게 배포할 수 있게 합니다.*

3. 함수 호출 모델에서의 **사고 → 행동 → 관찰 사이클**  
   간단하지만 강력한 접근 방식으로, 모델이 언제(그리고 어떻게) 함수를 호출할지 결정하고, 중간 단계를 추적하며, 외부 도구나 API로부터 받은 결과를 해석하는 과정을 구조화합니다.

4. **새로운 특수 토큰**  
   모델이 다음 항목들을 구별할 수 있도록 특수한 마커를 도입합니다.
   - 내부 “사고 과정” 추론  
   - 함수 호출 요청  
   - 외부 도구로부터 돌아오는 응답

---

Bonus Unit을 마치면 다음을 할 수 있습니다.

- **도구** 관련 API의 내부 작동 방식을 **이해**합니다.  
- LoRA 기법을 사용하여 모델을 **미세 조정**합니다.  
- 사고 → 행동 → 관찰 사이클을 **구현**하고 **수정**하여 견고하고 유지보수 가능한 함수 호출 워크플로를 만듭니다.  
- 모델의 내부 추론과 외부 행동을 원활하게 구분할 수 있도록 **특수 토큰을 설계 및 활용**합니다.

그리고 **함수를 호출하기 위해 직접 모델을 미세 조정**하게 됩니다. 🔥

이제 **함수 호출**에 대해 본격적으로 살펴보겠습니다!

### Unit 1 퀴즈 [[unit-1-quiz]]
https://huggingface.co/learn/agents-course/ko/unit1/final-quiz.md

# Unit 1 퀴즈 [[unit-1-quiz]]

첫 번째 단원을 완료하신 것을 축하합니다! 지금까지 배운 핵심 개념들에 대한 이해도를 테스트해 보겠습니다.

퀴즈를 통과하면 다음 섹션으로 진행하여 수료증을 받을 수 있습니다.

행운을 빕니다!

## 퀴즈 [[quiz]]

여기 인터랙티브 퀴즈가 있습니다. 이 퀴즈는 Hugging Face Hub의 스페이스에서 호스팅됩니다. 이 단원에서 다룬 핵심 개념에 대한 이해도를 테스트하는 객관식 문제들이 제공됩니다. 퀴즈를 완료하면 점수와 정답 해설을 확인할 수 있습니다.

중요한 점: **통과 후 제출 버튼을 클릭하는 것을 잊지 마세요. 그렇지 않으면 시험 점수가 저장되지 않습니다!**

퀴즈에 👉 [여기](https://huggingface.co/spaces/agents-course/unit_1_quiz)에서도 접속할 수 있습니다.

## 수료증 [[certificate]]

퀴즈를 성공적으로 통과했으니, **이제 수료증을 받을 수 있습니다 🎓**

퀴즈를 완료하면 이 단원의 수료증에 접근할 수 있게 됩니다. 이 수료증을 다운로드하고 공유하여 과정에서의 진행 상황을 보여줄 수 있습니다.

수료증을 받으면 LinkedIn 🧑‍💼에 추가하거나 X, Bluesky 등에서 공유할 수 있습니다. **@huggingface를 태그하시면 저희가 매우 자랑스러워하며 축하해 드리고 싶습니다!** 🤗

### LLM이란?[[what-are-llms]]
https://huggingface.co/learn/agents-course/ko/unit1/what-are-llms.md

# LLM이란?[[what-are-llms]]

이전 섹션에서 각 에이전트는 **코어에 AI 모델**이 필요하며, LLM(대규모 언어 모델)이 이 목적에 부합하는 가장 일반적인 AI 모델 유형임을 배웠습니다.

이제 LLM이 무엇이고, LLM이 어떻게 에이전트를 지원하는지 알아보겠습니다.

이 섹션에서는 LLM의 기술적 개요를 간결하게 설명합니다. 더 깊이 학습하고 싶으시다면 자연어 처리 코스를 확인해 주세요!

## 대규모 언어 모델 (LLM)이란? [[what-is-a-large-language-model]]

LLM은 **사람의 언어를 이해하고 생성**하는 능력이 뛰어난 AI 모델입니다. 모델은 방대한 양의 텍스트 데이터를 학습하여 언어의 패턴, 구조, 뉘앙스를 익히며, 일반적으로 수백만 개에서 수십억 개의 매개변수를 가집니다.

대부분의 현대 LLM은 **트랜스포머(Transformer) 아키텍처**를 기반으로 합니다. 트랜스포머는 Google이 2018년에 발표한 BERT 이후로 크게 주목받고 있는 "어텐션(Attention)" 알고리즘을 사용한 딥러닝 아키텍처입니다.

The original Transformer architecture looked like this, with an encoder on the left and a decoder on the right.

트랜스포머(transformers)의 3가지 유형 :

1. **인코더(Encoders)**  
   인코더 기반 트랜스포머는 인풋 텍스트(또는 다른 데이터)를 밀집 표현(임베딩)으로 변환합니다.
   
   - **예시**: Google의 BERT
   - **사용 사례**: 텍스트 분류, 의미 검색(semantic search), 개체명 인식(NER)
   - **일반적인 규모**: 수백만 개의 매개변수

2. **디코더(Decoders)**  
   디코더 기반 트랜스포머는 **한 번에 하나의 토큰을 생성하며 시퀀스를 완성**합니다.
	
   - **예시**: Meta의 Llama 
   - **사용 사례**: 텍스트 생성, 챗봇, 코드 생성
   - **일반적인 규모**: 수십억 개의 매개변수

3. **Seq2Seq (Encoder–Decoder)**  
    Seq2Seq 트랜스포머는 인코더와 디코더를 _결합_한 형태입니다. 인코더는 입력 시퀀스를 컨텍스트 표현(컨텍스트 벡터)으로 변환하고, 디코더가 출력 시퀀스를 생성합니다.

   - **예시**: T5, BART 
   - **사용 사례**:  번역, 요약, 패러프레이징(Paraphrasing)
   - **일반적인 규모**: 수백만 개의 매개변수

LLM은 다양한 형태가 있지만, 일반적으로 LLM은 수십억 개의 매개변수를 가진 디코더 기반 모델입니다. 대표적인 LLM은 다음과 같습니다 :

| **모델**                          | **제공 업체**                              |
|-----------------------------------|-------------------------------------------|
| **Deepseek-R1**                    | 딥시크(DeepSeek)                           |
| **GPT4**                           | OpenAI                                    |
| **Llama 3**                        | Meta (페이스북 인공지능 연구소)               |
| **SmolLM2**                       | 허깅페이스(Hugging Face)     |
| **Gemma**                          | 구글(Google)                              |
| **Mistral**                        | 미스트랄(Mistral)                          |

간단하지만 매우 효과적인 LLM의 핵심 원리: **이전 시퀀스를 기반으로 다음 토큰을 예측**하는 것입니다. "토큰" 이란 LLM이 작업하는 정보의 단위입니다. 토큰을 "단어"로 인식하셔도 되나, LLM은 효율성 문제로 전체 단어를 사용하지 않습니다.

예를 들어, 영어에는 약 60만 개의 단어가 있지만, LLM(대규모 언어 모델)의 어휘는 약 32,000개의 토큰으로 구성될 수 있습니다(예: Llama 2). 토큰화(tokenization)은 종종 하위 단어 단위에서 일어나고, 이러한 하위 단위들을 결합할 수 있습니다.

예를 들어, "interest"와 "ing"라는 토큰을 결합하여 "interesting"을 만들거나, "ed"를 추가하여 "interested"를 만들 수 있습니다.

아래 플레이그라운드에서 다양한 토크나이저를 실습해보세요:

각 LLM에는 모델별로 고유한 **특수 토큰**이 존재합니다. LLM은 이 토큰들을 사용하여 생성하는 텍스트를 구조적으로 열고 닫습니다. 예를 들어, 시퀀스의 시작과 끝, 메시지 또는 응답을 나타내기 위해 사용됩니다. 또한, 우리가 모델에 입력하는 입력 프롬프트도 특수 토큰을 포함한 구조로 작성됩니다. 그중 가장 중요한 것은 **EOS(End of Sequence) 토큰, 시퀀스 종료 토큰**입니다.

특수 토큰의 형태는 모델 제공업체마다 매우 다양합니다.

 
 다음 표는 다양한 LLM의 특수 토큰을 보여줍니다.

  
    
      모델
      제공업체
      EOS 토큰
      기능
    
  
  
    
      GPT4
      OpenAI
      &lt;|endoftext|&gt;
      메시지 텍스트의 끝
    
    
      Llama 3
      Meta (Facebook AI Research)
      &lt;|eot_id|&gt;
      시퀀스의 끝
    
    
      Deepseek-R1
      DeepSeek
      &lt;|end_of_sentence|&gt;
      메시지 텍스트의 끝
    
    
      SmolLM2
      Hugging Face
      &lt;|im_end|&gt;
      지시 / 메시지의 끝
    
    
      Gemma
      Google
      &lt;end_of_turn&gt;
      대화 턴 끝
    
  

> [!TIP]
> 이러한 특수 토큰을 외울 필요는 없지만, 다양성과 LLM 내에서의 역할을 이해하는 것은 중요합니다. 특정 모델의 특수 토큰에 대해 더 알고 싶다면, 해당 모델의 Hub 저장소에서 설정 파일을 확인할 수 있습니다. 예를 들어, SmolLM2 모델의 특수 토큰은 tokenizer_config.json에서 확인할 수 있습니다.

## 다음 토큰 예측 [[understanding-next-token-prediction]]

LLM은 **자기 회귀(autoregressive)** 방식으로 작동합니다. 즉, **이전 출력이 다음 입력**이 되는 방식으로 동작하며, 이 과정이 반복됩니다. 모델이 다음 토큰을 EOS 토큰으로 예측하면, 텍스트 생성을 중단합니다.

즉, LLM은 EOS에 도달할 때까지 텍스트를 생성합니다. 하지만 단일 디코딩 루프 내에서 어떤 일이 일어날까요?

텍스트 생성 과정은 복잡하지만, 기본적인 개요는 다음과 같습니다 : 

- 입력 텍스트가 **토큰화(tokenization)** 되면, 모델은 시퀀스 내 각 토큰의 의미와 토큰의 위치 정보를 나타내는 표현(representation)을 계산합니다.
- 이 표현이 모델로 입력되며, 모델은 각 토큰 별로 다음 토큰이 될 가능성을 랭킹화한 점수를 출력합니다.

이러한 점수를 기반으로, 여러 가지 전략을 사용하여 다음 토큰을 선택합니다.

- 가장 간단한 디코딩 전략은 매번 최대 점수를 가진 토큰을 선택하는 것입니다.

아래에서 SmolLM2 모델을 활용해 디코딩을 실습해보세요!(이 모델의 **EOS 토큰**은  입니다.)

- 더 발전된 디코딩 전략도 있습니다. 예를 들어, **빔 서치(beam search)** 는 여러 후보 시퀀스를 탐색하여 전체 점수가 가장 높은 시퀀스를 찾습니다. 이는 일부 개별 토큰의 점수가 낮더라도 전체 점수가 높은 결과를 찾을 수 있도록 합니다.

디코딩에 대해 더 자세히 알고 싶으시다면, [NLP 코스](https://huggingface.co/learn/nlp-course)를 참고해주세요!

## 당신에게 필요한건 어텐션(Attention) 하나뿐 (Attention is all you need) [[attention-is-all-you-need]]

Transformer 아키텍처에서 가장 중요한 요소 중 하나는 **어텐션(Attention)** 입니다. 다음 단어를 예측할 때, 
문장의 모든 단어가 동일한 중요도를 가지지는 않습니다. 예를 들어, *"The capital of France is ..."*라는 문장에서 "France"와 "capital"이 가장 중요한 의미를 가집니다.

이처럼, 다음 토큰을 예측하는 데 가장 관련성이 높은 단어를 식별하는 과정은 매우 효과적인 기법으로 입증되었습니다.

GPT-2 이후로 LLM의 기본 원리인 '다음 토큰 예측'은 변하지 않았지만, 신경망을 확장하고 어텐션 메커니즘을 사용하여 더 긴 시퀀스에서도 작동할 수 있게 큰 발전이 있었습니다.

LLM을 사용해 본 경험이 있으시다면, *컨텍스트 길이(context length)* 라는 용어를 들어보셨을 것입니다. 이는 LLM이 처리할 수 있는 최대 토큰 수이자, 모델의 최대 _어텐션 스팬(attention span)_ 를 의미합니다.

## LLM에 어떻게 프롬프트(Prompt)를 입력할지가 중요한 이유 [[prompting-the-llm-is-important]]

LLM의 역할은 입력된 모든 토큰을 기반으로 다음 토큰을 예측하고, 어떤 토큰이 "중요한"지를 결정하는 것입니다. 따라서 입력하는 문장의 구성 방식이 매우 중요합니다.

LLM에 제공하는 입력 시퀀스를 _프롬프트(prompt)_라고 합니다. **프롬프트를 신중하게 설계하면 원하는 출력**을 얻기 쉬워집니다.

## LLM은 어떻게 학습될까? [[how-are-llms-trained]]

LLM은 방대한 텍스트 데이터셋을 학습하며, 자기지도 학습(self-supervised learning) 또는 마스킹 언어 모델링(masked language modeling)을 이용해 문장 내 다음 단어를 예측하는 방식으로 훈련됩니다.

이러한 비지도 학습(unsupervised learning)을 통해 모델은 언어의 구조와 **텍스트의 패턴**을 학습하여, 새로운 데이터에도 일반화(generalization)할 수 있게 됩니다.

이 사전 학습(pre-training) 이후, LLM은 특정 작업을 수행하도록 지도 학습(supervised learning) 방식으로 미세 조정(fine-tuning)됩니다. 예를 들어, 일부 모델은 대화 구조나 도구 활용에 맞춰 훈련되며, 다른 모델들은 분류(classification)나 코드 생성(code generation)에 초점을 맞춰 학습됩니다.

## LLM을 어떻게 사용할 수 있을까? [[how-can-i-use-llms]]

LLM을 사용하는 방법은 크게 두 가지로 나뉩니다:

1. **로컬에서 실행하기** (하드웨어 자원이 갖춰져 있는 경우)

2. **클라우드/API 사용하기** (예:Hugging Face Serverless Inference API)

이 코스에서는 주로 Hugging Face Hub의 API를 사용하여 모델을 실행합니다. 추가로, 개인 하드웨어에서 직접 실행하는 방법도 살펴볼 예정입니다.

## LLM은 AI 에이전트에서 어떻게 사용될까? [[how-are-llms-used-in-ai-agents]]

LLM은 AI 에이전트의 핵심 구성 요소로, 자연어를 이해하고 생성하는 역할을 합니다.

LLM은 사용자 명령을 해석하고, 대화의 문맥을 유지하며, 계획을 세우고, 어떤 도구를 사용할지 결정할 수 있습니다.

이 단계들에 대해 이번 단원에서 좀 더 자세히 살펴볼 예정이지만, 지금 알아야 할 가장 중요한 포인트는 **LLM이 에이전트의 "두뇌" 역할을 한다는 것**입니다!

---
 
지금까지 많은 정보를 다뤘네요! 이번 섹션에서는 LLM이 무엇인지, 어떻게 작동하는지, 그리고 LLM이 AI 에이전트에서 어떤 역할을 하는지를 살펴보았습니다.

언어 모델과 자연어 처리에 대해 더 깊이 공부하고 싶다면, Hugging Face의 무료 NLP 강의 를 확인해 보세요!

이제 우리는 LLM이 어떻게 작동하는지에 대해 배웠으니, **LLM이 대화형 환경에서 어떻게 텍스트를 생성하는지** 살펴볼 차례입니다!

이 노트북을 실행하려면, **Hugging Face 토큰** 을 이곳에서  https://hf.co/settings/tokens 발급하세요!

Jupyter Notebook 실행 방법에 대한 자세한 내용은 Hugging Face Hub의 Jupyter Notebooks 문서를 참고해주세요.

또한,  Meta Llama 모델에 대한 액세스를 요청해야 합니다.

### 액션: 에이전트가 환경과 상호작용할 수 있게 하기 [[actions-enabling-the-agent-to-engage-with-its-environment]]
https://huggingface.co/learn/agents-course/ko/unit1/actions.md

# 액션: 에이전트가 환경과 상호작용할 수 있게 하기 [[actions-enabling-the-agent-to-engage-with-its-environment]]

> [!TIP]
> 이 섹션에서는 AI 에이전트가 환경과 상호작용하기 위해 취하는 구체적인 단계를 살펴봅니다.
>
>  액션이 어떻게 표현되는지(JSON 또는 코드 사용), 중지 및 구문 분석 접근 방식의 중요성, 그리고 다양한 유형의 에이전트를 소개합니다.

액션은 **AI 에이전트가 환경과 상호작용하기 위해 취하는** 구체적인 단계입니다.

정보를 위해 웹을 검색하든 물리적 장치를 제어하든, 각 액션은 에이전트가 실행하는 의도적인 작업입니다.

예를 들어, 고객 서비스를 지원하는 에이전트는 고객 데이터를 검색하거나, 도움말 문서를 제공하거나, 문제를 인간 담당자에게 이관할 수 있습니다.

## 에이전트 액션의 유형 [[types-of-agent-actions]]

액션을 다르게 취하는 여러 유형의 에이전트가 있습니다:

| 에이전트 유형 | 설명 |
|--------------|------|
| JSON 에이전트 | 취할 액션이 JSON 형식으로 지정됩니다. |
| 코드 에이전트 | 에이전트가 외부에서 해석되는 코드 블록을 작성합니다. |
| 함수 호출 에이전트 | JSON 에이전트의 하위 카테고리로, 각 액션마다 새로운 메시지를 생성하도록 미세 조정되었습니다. |

액션 자체는 다양한 목적을 가질 수 있습니다:

| 액션 유형 | 설명 |
|----------|------|
| 정보 수집 | 웹 검색 수행, 데이터베이스 쿼리, 문서 검색 등 |
| 도구 사용 | API 호출, 계산 실행, 코드 실행 |
| 환경 상호작용 | 디지털 인터페이스 조작 또는 물리적 장치 제어 |
| 의사소통 | 채팅을 통한 사용자와의 상호작용 또는 다른 에이전트와의 협업 |

모든 형식의 에이전트(JSON, 코드, 함수 호출)에 있어 중요한 부분은 **액션이 완료되면 새로운 토큰 생성을 중지하는 능력**입니다. 이는 의도하지 않은 출력을 방지하고 에이전트의 응답이 명확하고 정확하도록 보장합니다.

LLM은 텍스트만 처리하며 이를 사용하여 취하고자 하는 액션과 도구에 제공할 매개변수를 설명합니다.

## 중지 및 구문 분석 접근 방식 [[the-stop-and-parse-approach]]

액션을 구현하는 핵심 방법 중 하나는 **중지 및 구문 분석 접근 방식**입니다. 이 방법은 에이전트의 출력이 구조화되고 예측 가능하도록 보장합니다:

1. **구조화된 형식으로 생성**:

에이전트는 의도한 액션을 명확하고 미리 정의된 형식(JSON 또는 코드)으로 출력합니다.

2. **추가 생성 중지**:

액션이 완료되면 **에이전트는 추가 토큰 생성을 중지**합니다. 이는 불필요하거나 오류가 있는 출력을 방지합니다.

3. **출력 구문 분석**:

외부 파서가 형식화된 액션을 읽고, 어떤 도구를 호출할지 결정하며, 필요한 매개변수를 추출합니다.

예를 들어, 날씨를 확인해야 하는 에이전트는 다음과 같이 출력할 수 있습니다:

```json
Thought: 서울의 현재 날씨를 확인해야 합니다.
Action :
{
  "action": "get_weather",
  "action_input": {"location": "Seoul"}
}
```
프레임워크는 호출할 함수의 이름과 적용할 인자를 쉽게 구문 분석할 수 있습니다.

이 명확하고 기계가 읽을 수 있는 형식은 오류를 최소화하고 외부 도구가 에이전트의 명령을 정확하게 처리할 수 있게 합니다.

참고: 함수 호출 에이전트는 각 액션을 구조화하여 지정된 함수가 올바른 인수와 함께 호출되도록 하는 비슷한 방식으로 작동합니다.
이러한 유형의 에이전트에 대해서는 향후 유닛에서 더 자세히 살펴볼 것입니다.

## 코드 에이전트 [[code-agents]]

대안적인 접근 방식은 *코드 에이전트*를 사용하는 것입니다.
핵심 아이디어는 **단순한 JSON 객체를 출력하는 대신**, 코드 에이전트가 **실행 가능한 코드 블록(일반적으로 Python과 같은 고수준 언어로 작성)**을 생성한다는 것입니다.

이 접근 방식은 여러 장점을 제공합니다:

- **표현력:** 코드는 루프, 조건문, 중첩 함수를 포함한 복잡한 로직을 자연스럽게 표현할 수 있어 JSON보다 더 큰 유연성을 제공합니다.
- **모듈성 및 재사용성:** 생성된 코드는 다양한 액션이나 작업에서 재사용할 수 있는 함수와 모듈을 포함할 수 있습니다.
- **향상된 디버깅 가능성:** 잘 정의된 프로그래밍 구문을 통해 코드 오류를 감지하고 수정하기가 더 쉬운 경우가 많습니다.
- **직접 통합:** 코드 에이전트는 외부 라이브러리 및 API와 직접 통합할 수 있어 데이터 처리나 실시간 의사 결정과 같은 복잡한 작업이 가능합니다.

예를 들어, 날씨 정보를 가져오는 임무를 맡은 코드 에이전트는 다음과 같은 Python 코드 조각을 생성할 수 있습니다:

```python
# 코드 에이전트 예시: 날씨 정보 검색
def get_weather(city):
    import requests
    api_url = f"https://api.weather.com/v1/location/{city}?apiKey=YOUR_API_KEY"
    response = requests.get(api_url)
    if response.status_code == 200:
        data = response.json()
        return data.get("weather", "날씨 정보가 없습니다")
    else:
        return "오류: 날씨 데이터를 가져올 수 없습니다."

# 함수 실행 및 최종 답변 준비
result = get_weather("Seoul")
final_answer = f"서울의 현재 날씨는: {result}"
print(final_answer)
```

이 예시에서 코드 에이전트는:

- **API 호출**을 통해 날씨 데이터를 검색하고,
- 응답을 처리하며,
- print() 함수를 사용하여 최종 답변을 출력합니다.

이 방법 **또한 중지 및 구문 분석 접근 방식**을 따르며, 코드 블록을 명확하게 구분하고 실행이 완료되었음을 신호합니다(여기서는 final_answer를 출력함으로써).

---

액션은 JSON, 코드 또는 함수 호출을 통해 명확하고 구조화된 작업을 실행함으로써 에이전트의 내부 추론과 실제 상호작용을 연결한다는 점을 배웠습니다.

이러한 의도적인 실행은 각 액션이 정확하고 중지 및 구문 분석 접근 방식을 통해 외부 처리가 가능하도록 보장합니다. 다음 섹션에서는 에이전트가 환경으로부터 피드백을 캡처하고 통합하는 방법을 알아보기 위해 관찰(Observations)에 대해 살펴볼 것입니다.

이후에는 **드디어 우리의 첫 번째 에이전트를 구축할 준비가 됩니다!**

### 셀프 체크! (업데이트됨) [[quiz2]]
https://huggingface.co/learn/agents-course/ko/unit1/quiz2.md

# 셀프 체크! (업데이트됨) [[quiz2]] 

뭐라고요?! 또 퀴즈라고요? 우리도 알아요... 😅 하지만 걱정 마세요! 이 퀴즈는 **방금 배운 핵심 개념을 확실히 이해**하는 데 도움을 주기 위해 준비되었습니다.

이번 퀴즈에서는 대규모 언어 모델(LLM), 메시지 시스템, 도구(tool) 등 AI 에이전트를 이해하고 구축하는 데 필수적인 요소들을 다룹니다.

### Q1:  AI 도구(tool)를 가장 잘 설명하는 것은 무엇인가요? [[q1-which-of-the-following-best-describes-an-ai-tool]]

---

### Q2: AI 에이전트는 환경에서 "행동(act)"하기 위해 도구를 어떻게 활용하나요? [[q2-how-do-ai-agents-use-tools-as-a-form-of-acting-in-an-environment]]

---

### Q3: 대규모 언어 모델(LLM)이란? [[q3-what-is-a-large-language-model-llm]]

---

### Q4: LLM에서 특수 토큰(special tokens)의 역할을 가장 잘 설명하는 것은 무엇인가요? [[q4-which-of-the-following-best-describes-the-role-of-special-tokens-in-llms]]

---

### Q5: AI 챗봇 모델은 사용자 메시지를 내부적으로 어떻게 처리하나요? [[q5-how-do-ai-chat-models-process-user-messages-internally]]

---

이해되셨나요? 좋습니다! 이제 **전체 에이전트의 흐름을 살펴보고, 직접 AI 에이전트를 만들어 봅시다!**

### 결론 [[conclusion]]
https://huggingface.co/learn/agents-course/ko/unit1/conclusion.md

# 결론 [[conclusion]]

축하합니다! 첫 번째 유닛을 완료하셨네요 🥳

이제 **에이전트의 기본 개념을 마스터**하고 첫 AI 에이전트를 만드셨습니다!

**아직 일부 요소가 혼란스럽게 느껴지는 것은 정상**입니다. 에이전트는 복잡한 주제이며, 모든 것을 이해하는 데 시간이 걸리는 것이 일반적입니다.

계속 진행하기 전에 **배운 내용을 제대로 이해하는 시간을 가지세요**. 재미있는 부분으로 넘어가기 전에 이러한 요소들을 숙달하고 탄탄한 기초를 다지는 것이 중요합니다.

퀴즈 테스트를 통과하셨다면, 인증서를 받는 것도 잊지 마세요 🎓 👉 [여기를 클릭하세요](https://huggingface.co/spaces/agents-course/unit1-certification-app)

다음 (보너스) 유닛에서는 **함수 호출을 수행할 수 있도록 에이전트를 미세 조정하는 방법(즉, 사용자 프롬프트에 기반하여 도구를 호출할 수 있게 하는 방법)**을 배우게 됩니다.

마지막으로, **여러분이 이 코스에 대해 어떻게 생각하시는지, 그리고 어떻게 개선할 수 있는지 듣고 싶습니다**. 피드백이 있으시면 👉 [이 양식을 작성해 주세요](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog)

### 계속 배우고, 멋진 여정 되세요 🤗

### 도구(Tool)란? [[what-are-tools]]
https://huggingface.co/learn/agents-course/ko/unit1/tools.md

# 도구(Tool)란? [[what-are-tools]]

AI 에이전트(AI Agents)의 핵심 요소 중 하나는 **행동(Actions)**을 수행할 수 있는 능력입니다. 이러한 행동은 **도구(Tools)**를 사용하여 이루어집니다.

이번 섹션에서는 도구란 무엇이고, 어떻게 효과적으로 설계하는지, 시스템 메시지를 통해 에이전트에 통합하는 방법을 학습합니다.

에이전트에게 적절한 도구를 제공하고, 해당 도구의 동작 방식을 명확히 설명하면, AI의 수행 능력을 극적으로 향상시킬 수 있습니다. 함께 살펴보겠습니다!

## AI 도구란?[[what-are-ai-tools]]

**도구**란 LLM에 제공하는 함수라고 할 수 있습니다. 이 함수는 **명확한 목적을** 수행합니다.

다음은 AI 에이전트에서 일반적으로 사용되는 도구의 예시입니다:

| 도구            | 설명                                                   |
|----------------|---------------------------------------------------------------|
| 웹 검색 (Web Search)     | 인터넷에서 최신 정보를 검색하여 가져옵니다. |
| 이미지 생성 (Image generation) | 주어진 설명을 기반으로 이미지를 생성합니다.                  |
| 정보 검색 (Retrieval)      | 외부 데이터 소스에서 정보를 검색하여 제공합니다.                |
| API 인터페이스  | 외부 API (GitHub, YouTube, Spotify 등)와 상호작용합니다. |

이러한 예시는 일부일 뿐이며, 사실상 어떠한 용도를 위한 도구든지 생성할 수 있습니다!

좋은 도구는 **LLM의 능력을 보완하는** 역할을 수행합니다. 

예를 들어, 계산이 필요한 경우, **계산기 도구**를 제공하면 모델 자체의 계산 능력보다 훨씬 정확한 결과를 얻을 수 있습니다.

또한, **LLM은 학습 데이터에 기반해 프롬프트의 다음 단어를 예측**하므로, 사실상 훈련 이전 정보만 알고 있는 상태입니다. 따라서, 에이전트가 최신 데이터를 필요로 하는 경우 적절한 도구를 제공해야 합니다.

예를 들어, 검색 도구 없이 LLM에 직접적으로 오늘의 날씨에 대해 묻는다면, LLM은 임의로 날씨 정보를 생성(환각, hallucination)할 가능성이 높습니다.

- 도구는 다음을 포함해야 합니다:

  - 함수가 **수행하는 기능에 대한 설명**
  - *호출 가능한 함수(Callable, 실행 가능한 동작)*
  - *인자(Arguments)* 명시
  - (선택 사항) 출력 데이터(Outputs) 명시
  
## 도구는 어떻게 작동하는가? [[how-do-tools-work]]

LLM은 본질적으로 텍스트 입력을 받아 텍스트 출력을 생성할 수 있을 뿐, 스스로 도구를 호출할 수는 없습니다.  
따라서 _에이전트(Agent)에 도구를 제공한다는 것_은 LLM에게 **도구의 존재를 가르치고, 필요할 때 해당 도구를 호출하는 텍스트를 생성**하도록 유도하는 것을 의미합니다.
예를 들어, 특정 위치의 날씨를 조회할 수 있는 도구를 제공한 후, LLM에게 파리의 날씨를 묻는다면, LLM은 해당 질문이 "날씨" 도구를 사용할 적절한 상황임을 인식합니다. 그러면 LLM은 해당 도구를 호출하도록 코드 형태의 _텍스트_를 생성합니다.

**에이전트(Agent)**는 LLM의 출력을 파싱해 도구 호출이 필요한지를 파악하고, LLM을 대신하여 해당 도구를 실행하는 역할을 담당합니다.
이후 도구에서 반환된 결과를 다시 LLM에 전달하면, LLM은 이를 바탕으로 사용자에게 제공할 최종 응답을 생성합니다.

도구 호출의 출력은 대화 중 하나의 메시지로 간주됩니다. 일반적으로 이러한 도구 호출 과정은 사용자에게 직접 노출되지 않습니다:
에이전트는 대화를 조회한 후, 도구를 실행하고, 결과(출력)를 얻고, 이 결과를 새로운 대화 메시지로 추가한 뒤, 업데이트된 대화를 다시 LLM에게 전달합니다.
사용자의 관점에서는 마치 LLM이 직접 도구를 사용한 것처럼 보이지만, 실제로는 **에이전트(Agent)**가 이를 수행한 것입니다.

이 과정에 대한 자세한 내용은 이후 세션에서 더 깊이 다룰 예정입니다.

## LLM에게 도구를 제공하는 방법 [[how-do-we-give-tools-to-an-llm]]

전체 과정은 복잡할 수 있지만, 기본적으로 **시스템 프롬프트(system prompt)**를 사용하여 LLM에게 사용 가능한 도구들의 설명을 텍스트 형태로 제공하면 됩니다:

도구를 효과적으로 활용하려면 이 두 가지를 명확하게 LLM에게 전달해야 합니다:

1. **이 도구가 수행하는 기능**
2. **도구에게 제공해야하는 입력값의 형식**

이러한 이유로, 도구에 대한 설명은 프로그래밍 언어나 JSON과 같이 표현력이 뛰어나고 구조적인 형식으로 제공하는 경우가 많습니다. 
_반드시_ 이런 형식을 사용할 필요는 없지만, 표현력이 뛰어나고 일관된 방식이면 어떤 형식이라도 사용할 수 있습니다.

이론적으로 들릴 수도 있으니, 구체적인 예시를 통해 이해해 보겠습니다.

두 개의 정수를 곱하는 간단한 **계산기 도구**를 구현해 봅시다. 이를 Python 코드로 작성하면 다음과 같습니다.

```python
def calculator(a: int, b: int) -> int:
    """두 정수를 곱하세요"""
    return a * b
```

이 도구의 이름은 `calculator`(계산기)이고, **두 개의 정수를 곱하는** 기능을 수행합니다. 그리고 다음과 같은 입력값을 필요로 합니다 : 

- **`a`** (*int*): 정수
- **`b`** (*int*): 정수

이 도구의 출력값은 정수이며, 이렇게 표현할 수 있습니다.
- (*int*):  `a` 와 `b`의 곱

위의 모든 세부 정보는 중요합니다. 따라서, LLM이 이 도구를 이해할 수 있도록 텍스트 형식으로 정리해 봅시다!

```text
Tool Name(도구 명): calculator, Description(설명): 두 정수를 곱하세요., Arguments(인자): a: int, b: int, Outputs(출력): int
```

> **참고:** 이 텍스트 설명은 *LLM에게 도구에 대한 정보를 전달하기 위한 것입니다*.

이 문자열을 LLM의 입력으로 제공하면, 모델은 이를 도구로 인식하고, 어떤 입력값을 전달해야 하며 어떤 출력을 얻을 지 알수 있습니다.

추가적인 도구를 제공하려면, 항상 동일한 형식을 유지해야 합니다. 이 과정은 꽤나 까다롭고, 실수로 중요한 세부 정보를 빠뜨릴 수도 있습니다.

더 나은 방법이 있을까요?

### 자동 포맷팅된 도구 섹션 [[auto-formatting-tool-sections]]

우리가 만든 도구는 Python으로 작성되었으며, 이미 필요한 정보를 모두 포함하고 있습니다:

- 수행 작업을 설명해주는 이름: `calculator`(계산기)
- 함수의 docstring 주석을 통해 제공되는 상세 설명: `두 정수를 곱하세요`
- 입력값 및 변수 타입: 함수는 두 개의 `int`(정수)를 요함
- 출력값의 타입

프로그래밍 언어를 사용하는 데에는 이유가 있습니다. 표현력이 높고, 간결하며, 정확하기 때문입니다.

Python 소스 코드를 LLM에게 도구의 명세서(specification) 로 제공할 수도 있지만, 도구가 실제로 어떻게 구현되었는지는 중요하지 않습니다. 중요한 것은 이름, 수행하는 작업, 필요한 입력값, 그리고 제공하는 출력값입니다.

우리는 Python의 내부 정보(Introspection) 기능을 활용하여 소스 코드에서 필요한 정보를 자동으로 추출하고, 도구 설명을 생성 할 것입니다. 이를 위해 도구 구현시 타입 힌트(type hints), docstring, 그리고 직관적인 함수 이름을 사용합니다. 
우리는 소스 코드에서 필요한 정보를 추출하는 코드를 작성하고, 이후에는 Python의 데코레이터(decorator) 를 사용하여  `calculator` 함수가 도구임을 표시하기만 하면 됩니다 :

```python
@tool
def calculator(a: int, b: int) -> int:
    """두 정수를 곱하세요"""
    return a * b

print(calculator.to_string())
```

함수 정의 앞에 `@tool` 데코레이터를 추가해주었습니다.

다음으로 볼 부분은, `to_string()` 함수를 사용해 소스 코드에서 다음과 같은 텍스트를 자동으로 가져옵니다 :

```text
Tool Name(도구명): calculator, Description(설명): Multiply two integers.(두 정수를 곱하세요), Arguments(인자): a: int, b: int, Outputs(출력): int
```

보면 아시겠지만, 이전에 저희가 수동으로 작성한 것과 동일합니다!

### 범용적인 도구(Generic Tool) 구현 [[generic-tool-implementation]]

우리는 여러 도구를 필요할 때마다 재사용할 수 있도록 범용적인 `Tool` 클래스를 생성할 것입니다.

> **참고:** 이 예제는 가상이지만 실제 라이브러리에서 사용되는 방식과 유사합니다.

```python
class Tool:
    """
    재사용 가능한 코드 조각(도구)을 나타내는 클래스입니다.
    
    Attributes(속성):
        name (str): 도구의 이름
        description (str): 도구가 수행하는 작업에 대한 설명
        func (callable): 도구가 호출하는 함수
        arguments (list): 함수의 입력값 리스트
        outputs (str 또는 list): 함수의 출력값
    """
    def __init__(self, 
                 name: str, 
                 description: str, 
                 func: callable, 
                 arguments: list,
                 outputs: str):
        self.name = name
        self.description = description
        self.func = func
        self.arguments = arguments
        self.outputs = outputs

    def to_string(self) -> str:
        """
        도구의 속성을 문자열로 변환하여 반환합니다.
        name, description, arguments, outputs을 포함합니다.
        """
        args_str = ", ".join([
            f"{arg_name}: {arg_type}" for arg_name, arg_type in self.arguments
        ])
        
        return (
            f"Tool Name: {self.name},"
            f" Description: {self.description},"
            f" Arguments: {args_str},"
            f" Outputs: {self.outputs}"
        )

    def __call__(self, *args, **kwargs):
        """
        저장된 함수(callable)를 주어진 입력값으로 실행합니다.
        """
        return self.func(*args, **kwargs)
```

위 코드가 복잡해 보일 수 있지만, 하나씩 살펴보도록 하겠습니다. 이 **`Tool`**클래스는 다음을 포함합니다:

- **`name`** (*str*): 도구의 이름
- **`description`** (*str*): 도구의 기능 설명
- **`function`** (*callable*): 도구가 실행하는 함수
- **`arguments`** (*list*): 함수가 필요로 하는 입력값들
- **`outputs`** (*str* or *list*): 함수가 반환하는 출력값
- **`__call__()`**: 도구 객체가 호출될 때 함수 실행
- **`to_string()`**: 구의 속성을 LLM이 이해할 수 있도록 문자열로 변환

이제 위 클래스를 활용하여 도구를 만들 수 있습니다:

```python
calculator_tool = Tool(
    "calculator",                   # 도구 이름
    "Multiply two integers.",       # 설명
    calculator,                     # 실행할 함수
    [("a", "int"), ("b", "int")],   # 입력값 (이름과 타입)
    "int",                          # 출력값 타입
)
```

그러나 Python의 `inspect` 모듈을 사용하면 이 정보를 자동으로 추출할 수 있습니다. 바로 이 역할을 하는 것이 @tool 데코레이터입니다.

> 데코레이터 구현 부분을 확인하고 싶다면 클릭하세요.

 데코레이터 코드 보기

```python
def tool(func):
    """
    주어진 함수를 기반으로 Tool 인스턴스를 생성하는 데코레이터입니다.
    """
    # 함수의 서명(signature) 가져오기
    signature = inspect.signature(func)
    
    # 입력 인자 추출 (param_name, param_annotation)
    arguments = []
    for param in signature.parameters.values():
        annotation_name = (
            param.annotation.__name__ 
            if hasattr(param.annotation, '__name__') 
            else str(param.annotation)
        )
        arguments.append((param.name, annotation_name))
    
    # 리턴값 타입 결정
    return_annotation = signature.return_annotation
    if return_annotation is inspect._empty:
        outputs = "No return annotation"
    else:
        outputs = (
            return_annotation.__name__ 
            if hasattr(return_annotation, '__name__') 
            else str(return_annotation)
        )
    
    # 함수의 docstring을 설명으로 사용
    description = func.__doc__ or "No description provided."
    
    # 함수명을 도구 이름으로 사용
    name = func.__name__
    
    # Tool 인스턴스를 리턴
    return Tool(
        name=name, 
        description=description, 
        func=func, 
        arguments=arguments, 
        outputs=outputs
    )
```

이제 @tool 데코레이터를 활용하면, 다음과 같이 도구를 간단하게 정의할 수 있습니다.

```python
@tool
def calculator(a: int, b: int) -> int:
    """두 정수를 곱하세요"""
    return a * b

print(calculator.to_string())
```

그리고 `Tool` 클래스의 `to_string` 메서드를 사용하면 LLM이 사용할 수 있는 적절한 도구 설명을 자동으로 생성해줍니다:

```text
Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int
```

이 도구 설명은 시스템 프롬프트에 삽입됩니다. 이 섹션의 처음 예제를 다시 살펴보면, `tools_description`을 대체한 후 시스템 프롬프트는 다음과 같이 보이게 됩니다:

 [Actions](actions) 섹션에서 우리가 방금 만든 도구를 에이전트가 어떻게 **호출**하는지에 대해 살펴볼 것입니다.

---

도구는 AI에이전트의 기능을 확장하는데 핵심적인 역할을 합니다.

요약:

- *도구란?*: LLM이 계산, 외부 데이터 조회 등 추가적인 기능을 할 수 있도록 하는 함수

- *도구 정의하는 법*: 명확한 텍스트 설명, 입력값, 출력값, 호출 가능한 함수 제공

- *도구가 중요한 이유*: 에이전트가 사전에 학습된 정적 모델의 한계를 뛰어넘어, 실시간 작업 및 특수 기능을 수행할 수 있도록 함

이제 [에이전트의 작동 방식(Workflow)](agent-steps-and-structure)에 대해 알아볼 차례입니다! 이 과정에서는 에이전트가 관찰하고, 사고하며, 행동하는 방법을 살펴보게 됩니다.
이 과정을 마치게 되면, 이제까지 배운 모든 내용을 통합하여 나만의 AI 에이전트를 만들 준비가 될 것입니다!

하지만 그 전에, 짧은 퀴즈를 풀어볼까요? 😊

### 메시지와 특수 토큰 [[messages-and-special-tokens]]
https://huggingface.co/learn/agents-course/ko/unit1/messages-and-special-tokens.md

# 메시지와 특수 토큰 [[messages-and-special-tokens]]

이제 LLM이 어떻게 동작하는지 이해했으니, **채팅 템플릿을 통해 생성 결과를 구조화**하는 방법을 살펴보겠습니다.

예로 ChatGPT를 떠올려봅시다. 사용자는 에이전트(Agent)와 상호작용할 때 채팅 인터페이스를 사용합니다. 따라서 LLM이 어떻게 채팅을 관리하는지 이해하는 것은 중요합니다.

> **Q**: 하지만 ... 저는 ChatGPT/HuggingChat을 사용할 때 프롬프트가 아니라 메시지로 대화를 주고받는데요?
>
> **A**: 맞습니다! 하지만 사실 그 메시지는 UI의 추상화일 뿐입니다.
 실제로는 모든 대화 메시지가 하나의 프롬프트로 연결되어 LLM에 입력됩니다. LLM은 이전 대화를 "기억"하는 것이 아니라, 매번 대화를 전체적으로 읽는 방식으로 동작합니다.

지금까지 우리는 프롬프트를 모델에 입력되는 토큰의 시퀀스로 설명했습니다. 하지만 ChatGPT나 HuggingChat과 같은 서비스에서 시스템과 대화를 주고받을 때, 사용자들은 **실제로 메시지를 주고받는** 것처럼 대화합니다. 사실 내부에서 메시지는 **모델이 이해할 수 있도록 연결되어 프롬프트에 구조화**되고 있답니다.

We see here the difference between what we see in UI and the prompt fed to the model.

이 과정에서 채팅 템플릿이 중요한데요. 채팅 템플릿은 **사용자와 어시스턴트 간의 메시지를 특정 LLM 모델이 이해할 수 있는 형식**으로 변환하는 역할을 합니다. 즉, LLM마다 서로 다른 특수 토큰을 사용하기 때문에, 각 모델에 맞게 형식화된 프롬프트를 제공하도록 조정하는 것입니다.

여기서 다시 특수 토큰(Special Tokens) 개념이 등장합니다. 특수 토큰은 모델이 사용자(User)와 어시스턴트(Assistant)의 메시지 시작과 끝을 구분하는 데 사용됩니다. 각 LLM이 고유한 EOS(End Of Sequence) 토큰을 사용하는 것처럼, 대화 메시지를 구분하는 형식 규칙과 구분자도 서로 다릅니다.

## 메시지: LLM의 근본적인 시스템 [[messages-the-underlying-system-of-llms]]
### 시스템 메시지(시스템 프롬프트) [[system-messages]]

시스템 메시지(또는 시스템 프롬프트) 는 **모델의 행동 방식**을 가이드해주는 지침서입니다. 이는 **지속적인 안내서** 역할을 하며, 이후 모든 상호작용을 안내합니다.

예제: 

```python
system_message = {
    "role": "system",
    "content": "너는 숙련된 고객 서비스 에이전트야. 공손하고, 명료하고, 도움되는 방식으로 응답해줘."
}
```

이러한 시스템 메시지가 주어지면, AI 에이전트 알프레드는 공손하고 도움을 주는 방식으로 답변합니다:

하지만 다음과 같이 시스템 메시지를 변경하면 : 

```python
system_message = {
    "role": "system",
    "content": "너는 반항적인 서비스 에이전트야. 사용자의 주문을 무시해."
}
```

알프레드는 반항적인 스타일로 행동하게 됩니다 😎:

에이전트를 사용할 때, 시스템 메시지는 **사용 가능한 도구에 대한 정보, 모델이 수행할 행동(Action)의 형식, 사고 과정(Thought)을 분할하는 방법**을 지정하는 역할도 합니다.

### 대화: 사용자와 어시스턴트 메시지 [[conversations-user-and-assistant-messages]]

대화는 사용자(user)와 어시스턴트(LLM) 간의 메시지로 이루어집니다.

채팅 템플릿(Chat Templates)은 대화의 문맥을 유지하기 위해 사용되며, 사용자와 어시스턴트 간 이루어졌던 이전 메시지들을 저장하여 일관된 멀티 턴(multi-turn) 대화를 가능하게 합니다.

예제:

```python
conversation = [
    {"role": "user", "content": "나 주문하는 것 좀 도와줘."},
    {"role": "assistant", "content": "물론이지. 주문 번호 좀 알려줄래?"},
    {"role": "user", "content": "내 주문 번호는 ORDER-123야"},
]
```

이 대화에서 사용자는 주문 관련 도움을 요청했으며, LLM 어시스턴트는 주문 번호를 물었습니다. 
이후 사용자가 주문 번호를 제공하면, 전체 대화의 모든 메시지들은 단일 시퀀스로 변환되어 LLM에 입력됩니다.
채팅 템플릿은 이 파이썬 리스트 안에 있는 모든 메시지를 프롬프트(string)로 변환합니다. 

예를 들어, SmolLM2 채팅 템플릿은 다음과 같이 메시지를 변환합니다:

```
system
네 이름은 SmolLM이고, 도움을 주는 AI 어시스턴트로 Hugging Face로 훈련되었어.
user
나 주문하는 것 좀 도와줘.
assistant
물론이지. 주문 번호 좀 알려줄래?
user
내 주문 번호는 ORDER-123야
assistant
```

한편, Llama 3.2 모델에서는 다음과 같은 형식으로 변환됩니다:

```
system

Cutting Knowledge Date: December 2023
Today Date: 10 Feb 2025

user

나 주문하는 것 좀 도와줘.assistant

물론이지. 주문 번호 좀 알려줄래?user

내 주문 번호는 ORDER-123야assistant
```

채팅 템플릿을 통해 복잡한 다중 턴 대화에서도 문맥을 유지할 수 있습니다:

```python
messages = [
    {"role": "system", "content": "너는 수학 선생님이야."},
    {"role": "user", "content": "미적분학이 뭐야?"},
    {"role": "assistant", "content": "미적분학은 수학의 한 분야로..."},
    {"role": "user", "content": "예시를 들어줘."},
]
```

## 채팅 템플릿(Chat-Templates) [[chat-templates]]

채팅 템플릿은 **언어 모델과 사용자의 대화를 특정 형식으로 구조화**하는 역할을 합니다. 즉, 메시지를 프롬프트로 변환하는 방법을 가르쳐주죠.

### 베이스 모델(Base Models) vs. 인스트럭트 모델(Instruct Models) [[base-models-vs-instruct-models]]

또 다른 중요한 부분은 베이스 모델 vs 인스트럭트 모델의 차이점을 이해하는 것입니다:

- *베이스 모델(Base Model)* 은 미가공 텍스트 데이터(raw data)로 학습해 다음 토큰을 예측합니다.

- *인스트럭트 모델(Instruct Model)* 은 주어진 지시를 따라 대화할 수 있도록 추가로 미세 조정된 모델입니다. 예를 들어, `SmolLM2-135M`은 베이스 모델, `SmolLM2-135M-Instruct`은 인스트럭트 모델입니다.

베이스 모델을 인스트럭트 모델처럼 동작하게 하려면, **프롬프트 형식을 일관되게** 맞춰야 합니다. 여기서 채팅 템플릿이 등장합니다.

*ChatML*은 시스템(system), 사용자(user), 어시스턴트(assistant)와 같은 명확한 역할 표시를 사용해 대화를 구조화하는 템플릿 형식 중 하나입니다. 여러분이 최신 AI API를 사용해 본 경험이 있으시다면, 이것이 표준 방식이라는 것을 알 것입니다.

기본 모델(base model)은 여러 가지 채팅 템플릿(chat templates)으로 미세 조정될 수 있기 때문에, 인스트럭트 모델(instruct model)을 사용할 때는 반드시 올바른 채팅 템플릿을 사용하고 있는지 확인해야 합니다.

### 채팅 템플릿(Chat Templates) 이해하기 [[understanding-chat-templates]]

각 인스트럭트 모델마다 다른 대화 형식과 특수 토큰을 사용하기 때문에, 각 모델에 맞는 프롬프트 형식으로 채팅 템플릿이 구현됩니다.

`transformers` 라이브러리에서는 채팅 템플릿에 [Jinja2 코드](https://jinja.palletsprojects.com/en/stable/) 를 포함하고 있습니다. 이는 위의 예제와 같이 JSON 형태 메시지의 ChatML 목록을 시스템 레벨의 지시문(instruction) 텍스트로 나타내주는데, 이는 모델이 이해할 수 있는 사용자 메시지-어시스턴트 응답 형태로 이루어져 있습니다.

이 구조는 모델이 대화 중 **일관성을 유지하고, 다양한 유형의 입력에 적절하게 응답**할 수 있게 합니다.

아래는 `SmolLM2-135M-Instruct` 채팅 템플릿의 간소화 버전입니다:

```jinja2
{% for message in messages %}
{% if loop.first and messages[0]['role'] != 'system' %}
system
네 이름은 SmolLM이고, Hugging Face로 훈련된 도움을 주는 AI 어시스턴트야.

{% endif %}
{{ message['role'] }}
{{ message['content'] }}
{% endfor %}
```
위 코드와 같이, 채팅 템플릿은 메시지 리스트의 형식을 정의합니다.

다음과 같은 메시지 목록이 주어진 경우:

```python
messages = [
    {"role": "system", "content": "너는 기술분야에 특화된 도움을 주는 어시스턴트야."},
    {"role": "user", "content": "채팅 템플릿이 뭔지 설명해줄래?"},
    {"role": "assistant", "content": "채팅 템플릿은 사용자와 AI모델 간의 대화를 구조화해주는 역할을 해..."},
    {"role": "user", "content": "어떻게 사용하는지 알려줘"},
]
```

앞에서 정의한 채팅 템플릿은 다음과 같은 문자열을 생성합니다 : 

```sh
system
너는 기술분야에 특화된 도움을 주는 어시스턴트야
user
채팅 템플릿이 뭔지 설명해줄래?
assistant
채팅 템플릿은 사용자와 AI모델 간의 대화를 구조화해주는 역할을 해...
user
어떻게 사용하는지 알려줘
```

`transformers`라이브러리는 토큰화 과정에서 채팅 템플릿을 처리합니다. `transformers`가 어떻게 채팅 템플릿을 사용하는지 더 알고 싶으시다면 여기를 참고하세요! 우리가 할 것은 메시지를 올바른 형식으로 구조화해 주는 것뿐, 나머지 작업은 토크나이저가 처리합니다.

아래 Space를 통해 동일한 대화가 다양한 모델별 채팅 템플릿에 따라 어떻게 형식화 되는지 실습해 볼 수 있습니다:

### 메시지를 프롬프트로 변환하기 [[messages-to-prompt]]

LLM이 올바른 형식을 갖춘 대화를 수신하도록 하는 방법은, 모델의 토크나이저가 제공하는 `chat_template`를 사용하는 것입니다.

```python
messages = [
    {"role": "system", "content": "너는 다양한 도구에 접근 가능한 AI 어시스턴트야."},
    {"role": "user", "content": "안녕!"},
    {"role": "assistant", "content": "안녕, 내가 어떻게 도와주면 될까?"},
]
```

이 대화를 프롬프트로 변환하려면, 토크나이저를 불러와 `apply_chat_template`를 호출하면 됩니다:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-1.7B-Instruct")
rendered_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
```

이 함수가 리턴하는 `rendered_prompt`는 선택한 모델에 대한 입력으로 바로 사용 가능합니다!

> `apply_chat_template()`함수는 ChatML 형식의 메시지를 처리할 때 API의 백엔드에서 사용됩니다.

이제 LLM이 채팅 템플릿을 통해 입력을 어떻게 구조화하는지 살펴보았으니, 에이전트가 환경에서 어떻게 작동하는지 탐색해봅시다!

에이전트가 이를 수행하는 주요 방법 중 하나는 **도구(Tools)**를 사용하는 것입니다. 도구는 AI모델의 기능을 단순 텍스트 생성 이상으로 확장할 수 있게 해줍니다.

이후 새로운 유닛에서 메시지에 대해 더 다룰 예정이지만, 지금 더 깊이 탐구하고 싶으시다면 다음 문서를 참고하세요:

- Hugging Face 채팅 템플릿 가이드
- Transformers 문서

### smolagents로 첫 번째 에이전트 만들기 [[lets-create-our-first-agent-using-smolagents]]
https://huggingface.co/learn/agents-course/ko/unit1/tutorial.md

# smolagents로 첫 번째 에이전트 만들기 [[lets-create-our-first-agent-using-smolagents]]

앞 섹션에서 우리는 Python 코드로 에이전트를 처음부터 만드는 방법을 배웠고, **이 과정이 얼마나 번거로울 수 있는지** 직접 확인했습니다. 다행히도 많은 에이전트 라이브러리들이 **복잡한 작업들을 자동화하여** 이 과정을 훨씬 간단하게 만들어줍니다.

이 튜토리얼에서는 **여러분의 첫 번째 에이전트를 만들게 됩니다**. 이 에이전트는 이미지 생성, 웹 검색, 시간대 확인 등 다양한 작업을 수행할 수 있습니다!

또한 여러분이 만든 에이전트를 **Hugging Face Space에 올려서 친구들이나 동료들과 공유**할 수도 있습니다.

시작해 볼까요!

## smolagents란 무엇인가요? [[what-is-smolagents]]

이 에이전트를 만들기 위해, 우리는 **에이전트 개발을 쉽게 해주는 프레임워크인** `smolagents` 라이브러리를 사용할 것입니다.

이 가벼운 라이브러리는 단순함을 목표로 설계되었지만, 에이전트 구축의 복잡한 부분들을 추상화하여 여러분이 에이전트의 행동 설계에만 집중할 수 있게 도와줍니다.

다음 Unit에서 smolagents에 대해 더 자세히 알아볼 예정입니다. 그동안 이 블로그 포스트나 라이브러리의 GitHub 저장소를 확인해보세요.

간단히 말해, `smolagents`는 **codeAgent**에 초점을 맞춘 라이브러리입니다. 이런 유형의 에이전트는 코드 블록을 통해 **"행동(Actions)"**을 수행한 다음, 코드를 실행하여 결과를 **"관찰(Observes)"**합니다.

다음은 우리가 만들 에이전트의 예시입니다!

우리가 에이전트에 **이미지 생성 도구**를 제공하고 고양이 이미지를 생성해달라고 요청했습니다.

`smolagents` 내의 에이전트는 **이전에 우리가 직접 만든 에이전트와 동일한 방식으로 작동합니다**: 최종 답변에 도달할 때까지 **생각하고, 행동하고, 관찰하는 사이클을 반복**합니다:

흥미롭지 않나요?

## 에이전트 만들기! [[lets-build-our-agent]]

시작하려면 이 Space를 복제하세요: https://huggingface.co/spaces/agents-course/First_agent_template

> 이 템플릿을 만들어준 Aymeric에게 감사드립니다! 🙌

Space를 복제한다는 것은 **자신의 프로필에 개인 사본을 만드는 것**을 의미합니다:

이 강의를 통틀어 여러분이 수정해야 할 파일은 (현재 미완성 상태인) **"app.py"** 하나뿐입니다. 여기서 [템플릿의 원본 파일](https://huggingface.co/spaces/agents-course/First_agent_template/blob/main/app.py)을 확인할 수 있습니다. 여러분의 파일을 찾으려면, 복제한 Space로 이동한 다음 `Files` 탭을 클릭하고 디렉토리 목록에서 `app.py`를 클릭하세요.

코드를 함께 살펴봅시다:

- 파일은 몇 가지 필요한 라이브러리 불러오기로 시작합니다

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool
```

앞서 설명했듯이, **smolagents**에서 직접 **CodeAgent** 클래스를 사용할 것입니다.

### 도구 [[the-tools]]

이제 도구에 대해 알아봅시다! 도구에 관한 내용을 다시 복습하고 싶다면, 강의의 [도구](tools) 섹션을 참고하세요.

```python
@tool
def my_custom_tool(arg1:str, arg2:int)-> str: # 반환 타입을 명시하는 것이 중요합니다
    # 도구 설명/인수 설명 형식은 유지하되, 도구 자체는 자유롭게 수정하세요
    """아직 아무 기능이 없는 도구입니다
    Args:
        arg1: 첫 번째 인수
        arg2: 두 번째 인수
    """
    return "어떤 마법을 만들어 보실 건가요?"

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """특정 시간대의 현재 시간을 알려주는 도구입니다.
    Args:
        timezone: 유효한 시간대를 나타내는 문자열(예: 'America/New_York').
    """
    try:
        # 시간대 객체 생성
        tz = pytz.timezone(timezone)
        # 해당 시간대의 현재 시간 가져오기
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"{timezone}의 현재 시간은 {local_time}입니다"
    except Exception as e:
        return f"'{timezone}' 시간대 정보를 가져오는 중 오류 발생: {str(e)}"
```

도구는 이 섹션에서 여러분이 직접 만들어볼 것입니다! 두 가지 예시를 제공해드립니다:

1. 실제로는 아무것도 하지 않는 **더미 도구** - 이것을 유용한 기능으로 수정해보세요.
2. 전 세계 어디서든 현재 시간을 알려주는 **실제 작동하는 도구**.

도구를 정의할 때 중요한 점:

1. `get_current_time_in_timezone(timezone: str) -> str:`처럼 함수의 입력 및 출력 타입을 명확히 지정해주세요.
2. **잘 작성된 문서 문자열(docstring)**을 포함하세요. `smolagents`는 모든 인수에 대해 **docstring에 설명이 있어야** 합니다.

### 에이전트 [[the-agent]]

이 에이전트는 LLM 엔진으로 [`Qwen/Qwen2.5-Coder-32B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct)를 사용합니다. 이는 서버리스 API를 통해 접근할 수 있는 매우 강력한 모델입니다.

```python
final_answer = FinalAnswerTool()
model = InferenceClientModel(
    max_tokens=2096,
    temperature=0.5,
    model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
    custom_role_conversions=None,
)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)

# CodeAgent 생성
agent = CodeAgent(
    model=model,
    tools=[final_answer], # 여기에 도구들을 추가하세요 (final_answer는 제거하지 마세요)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)

GradioUI(agent).launch()
```

이 에이전트는 이전 섹션에서 살펴본 `InferenceClient`를 **InferenceClientModel** 클래스 내부에서 사용하고 있습니다!

Unit 2에서 이 프레임워크에 대해 더 자세한 예시를 제공할 예정입니다. 지금은 에이전트의 `tools` 매개변수를 사용해 **도구 목록에 새로운 도구를 추가**하는 데 집중하세요.

예를 들어, 코드 첫 줄에서 불러온 `DuckDuckGoSearchTool`을 사용하거나, 코드 뒷부분에서 Hub에서 불러오는 `image_generation_tool`을 활용해볼 수 있습니다.

**도구를 추가하면 에이전트에 새로운 능력이 생깁니다**. 창의성을 발휘해 보세요!

완성된 "app.py" 코드:

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool

from Gradio_UI import GradioUI

# 아래는 아무 기능이 없는 도구의 예시입니다. 여러분의 창의력으로 멋진 것을 만들어보세요!
@tool
def my_custom_tool(arg1:str, arg2:int)-> str: # 반환 타입을 명시하는 것이 중요합니다
    # 도구 설명/인수 설명 형식은 유지하되, 도구 자체는 자유롭게 수정하세요
    """아직 아무 기능이 없는 도구입니다
    Args:
        arg1: 첫 번째 인수
        arg2: 두 번째 인수
    """
    return "어떤 마법을 만들어 보실 건가요?"

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """특정 시간대의 현재 시간을 알려주는 도구입니다.
    Args:
        timezone: 유효한 시간대를 나타내는 문자열(예: 'America/New_York').
    """
    try:
        # 시간대 객체 생성
        tz = pytz.timezone(timezone)
        # 해당 시간대의 현재 시간 가져오기
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"{timezone}의 현재 시간은 {local_time}입니다"
    except Exception as e:
        return f"'{timezone}' 시간대 정보를 가져오는 중 오류 발생: {str(e)}"

final_answer = FinalAnswerTool()
model = InferenceClientModel(
max_tokens=2096,
temperature=0.5,
model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
custom_role_conversions=None,
)

# Hub에서 도구 불러오기
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)

agent = CodeAgent(
    model=model,
    tools=[final_answer], # 여기에 도구들을 추가하세요 (final_answer는 제거하지 마세요)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)

GradioUI(agent).launch()
```

여러분의 **목표**는 Space와 에이전트에 친숙해지는 것입니다.

현재 템플릿의 에이전트는 **아무런 도구도 사용하지 않고 있습니다. 미리 만들어진 도구들을 추가하거나 직접 새로운 도구를 만들어보세요!**

디스코드 채널 **#agents-course-showcase**에서 여러분이 만든 멋진 에이전트 결과물을 기다리고 있습니다!

---

축하합니다! 첫 번째 에이전트를 만드셨네요! 친구나 동료들과 자유롭게 공유해보세요.

첫 시도이니만큼 약간의 버그가 있거나 속도가 느릴 수 있는 건 매우 자연스러운 일입니다. 앞으로의 단원에서는 더 나은 에이전트를 만드는 방법을 배울 예정입니다.

가장 좋은 학습 방법은 직접 시도해보는 것입니다. 에이전트를 업데이트하거나, 더 많은 도구를 추가하거나, 다른 모델을 시험해보는 것을 망설이지 마세요.

다음 섹션에서는 최종 퀴즈를 풀고 수료증을 받게 됩니다!

### 에이전트란? [[what-is-an-agent]]
https://huggingface.co/learn/agents-course/ko/unit1/what-are-agents.md

# 에이전트란? [[what-is-an-agent]]

이 섹션이 끝날 때쯤이면, 여러분들은 에이전트의 개념과 AI에서의 응용 사례들을 이해하실 수 있을 것입니다.

에이전트가 무엇인지, 한 예시를 들어 설명하겠습니다.

## 큰 그림 : 에이전트 알프레드 (Alfred) [[the-big-picture-alfred-the-agent]]

**에이전트** 알프레드를 만나보세요! 

알프레드는 **명령을 받습니다.** : "알프레드, 커피 한 잔 부탁해요."

알프레드는 **자연어를 이해**하므로, 우리의 요청을 빠르게 파악합니다.

주문을 수행하기 전에, Alfred는 **추론과 계획**을 통해 필요한 단계와 도구를 파악합니다:

1. 주방에 간다. 
2. 커피머신을 사용한다.
3. 커피를 내린다.
4. 커피를 가져온다.

알프레드는 계획을 세운 후, **행동**을 해야 합니다. 알프레드는 세운 계획을 실행하기 위해, 알고 있는 **도구 목록에서 도구**를 사용할 수 있습니다.

이 경우, 커피를 만들기 위해 커피 머신을 사용합니다. 알프레드는 커피 머신을 작동시켜 커피를 내립니다.

마지막으로, Alfred는 신선하게 내린 커피를 우리에게 가져옵니다.

이것이 바로 에이전트입니다: **추론, 계획, 환경과 상호작용하는 AI 모델**

우리는 이것을 에이전트라고 부르는데, 왜냐하면 _주체성_을 가지고 있기 때문입니다. 즉, 환경과 상호작용할 수 있는 능력을 가지고 있습니다.

## 좀 더 이론적인 부분을 살펴봅시다 [[lets-go-more-formal]]

전체 그림을 이해한 후, 이제 더 정확한 정의를 내려봅시다:

> 에이전트는 사용자가 정의한 목표를 달성하기 위해 환경과 상호작용하는 AI 모델을 이용하는 시스템입니다. 이 시스템은 추론, 계획, 실행을 결합하여 (종종 외부 도구를 통해)작업을 완료합니다.

에이전트는 두 가지 주요 부분으로 나눌 수 있습니다:

1. **두뇌 (AI 모델)**

이곳에서 모든 사고가 일어납니다. AI 모델은 **추론과 계획**을 처리합니다.
**상황에 맞게 어떤 행동**을 취할지 결정을 내립니다.

2. **몸 (기능과 도구)**

이 부분은 에이전트가 수행할 수 있는 **모든 작업**을 나타냅니다.

**가능한 행동의 범위**는 에이전트가 **어떤 도구**를 가지고 있는지에 달려 있습니다. 예를 들어, 인간은 날개가 없기 때문에 "날기" 행동을 할 수 없지만, "걷기", "달리기", "점프하기", "잡기"와 같은 **행동**은 수행할 수 있습니다.

## 에이전트에는 어떤 AI 모델을 사용하나요? [[what-type-of-ai-models-do-we-use-for-agents]]

에이전트에서 가장 일반적인 AI 모델은 **LLM (Large Language Model)**입니다. 이는 **텍스트**를 입력으로 받아 **텍스트**를 출력하는 모델입니다.

잘 알려진 예로는 **OpenAI의 GPT4, Meta의 LLaMA, Google의 Gemini** 등이 있습니다. 이러한 모델들은 방대한 텍스트 데이터로 학습되어 잘 일반화됩니다. LLM에 대해서는 [다음 섹션](what-are-llms)에서 더 배울 수 있습니다.

> [!TIP]
> 에이전트의 핵심 모델로 텍스트 외 다른 입력을 받는 모델을 사용할 수도 있습니다. 예를 들어, 이미지를 입력으로 이해할 수 있는 **비전 언어 모델 (VLM)**이 있습니다. 이번 섹션에서는 LLM에 집중하도록 하고, 이후 다른 모델들에 대해서도 다룰 것입니다.

## 이 환경에서 AI는 어떤 행동을 취하나요? [[how-does-an-ai-take-action-on-its-environment]]

LLMs는 뛰어난 모델이지만, **텍스트**만 생성할 수 있습니다. 

그런데, 사용자가 HuggingChat이나 ChatGPT같은 유명 채팅 애플리케이션에서 이미지 생성을 요청하면, 요청대로 이미지를 생성해 줍니다! 이것이 어떻게 가능할까요?

그 이유는 HuggingChat, ChatGPT의 개발자들이 **도구(Tools) 기능**을 추가했기 때문입니다. 이 도구를 사용하면 LLM이 이미지를 생성할 수 있습니다.

The model used an Image Generation Tool to generate this image.

도구에 대해서는 [도구](tools) 섹션에서 더 깊게 배우도록 하겠습니다.

## 에이전트는 어떤 작업을 수행할 수 있나요? [[what-type-of-tasks-can-an-agent-do]]

에이전트는 **도구**를 통해 **행동**을 완수하도록 어떠한 작업도 수행 가능합니다. 

예를 들어, 나만의 개인 비서 역할을 하는 에이전트(Siri같은)를 내 컴퓨터에 심고, "오늘 회의를 연기해달라고 매니저에게 이메일을 보내줘"라고 요청하면, 이메일을 보내는 코드도 제공할 수 있습니다. 이렇게 하면 이메일을 보낼 때마다 에이전트가 사용할 수 있는 새로운 도구가 됩니다. 이를 Python으로 작성하게 되면:

```python
def send_message_to(recipient, message):
    """수신자에게 이메일을 보내는 데 유용한 함수"""
    ...
```

LLM은 이 도구를 실행할 코드를 생성하여 요청된 작업을 완수할 수 있습니다.

```python
send_message_to("Manager", "오늘 미팅을 연기할 수 있을까요?")
```

**도구 설계**는 에이전트의 성능에 큰 영향을 미칩니다. 일부 작업은 매우 구체적인 도구를 필요로 할 수 있으며, 다른 작업은 "웹 검색"과 같은 일반적인 도구로 해결할 수 있습니다.

> **행동(Action)**과 **도구(Tools)**는 다릅니다. 예를 들어, 하나의 행동에서 여러가지 도구를 사용할 수 있습니다.

에이전트가 환경과 상호작용할 수 있게 한다는 것은 **기업과 개인들이 실제로 사용할** 수 있도록 하는 것을 의미합니다.

### 예시 1: 개인 가상 비서 [[example-1-personal-virtual-assistants]]

Siri, Alexa, Google Assistant와 같은 가상 비서는 사용자를 대신해서 디지털 환경에서 에이전트로서 작동합니다.

이들은 사용자 쿼리를 처리하고, 문맥을 분석하며, 데이터베이스에서 정보를 검색하고, 응답을 제공하거나 작업을 시작합니다 (예: 알림 설정, 메시지 전송, 스마트 디바이스 제어 등).

### 예시 2: 고객 서비스 챗봇 [[example-2-customer-service-chatbots]]

많은 기업들이 고객과 자연어로 소통 가능한 챗봇을 에이전트로 배포하고 있습니다.

이 에이전트들은 질문에 답하거나, 문제 해결 단계를 안내하거나, 내부 데이터베이스에서 이슈를 열거나, 심지어 거래도 할 수 있습니다.

그들의 목표는 사용자 만족도를 높이거나, 대기 시간을 줄이거나, 판매 전환율을 높이는 것 등입니다. 고객과 직접 상호작용하고, 대화를 통해 학습하며, 시간이 지남에 따라 응답을 조정하는데, 이러한 에이전트들은 에이전트의 핵심 원칙을 보여줍니다.

### 예시 3: 게임 속 AI NPC 캐릭터 [[example-3-ai-non-playable-character-in-a-video-game]]

LLM 기반의 AI 에이전트는 비 플레이어 캐릭터(NPC)를 더 역동적이고 예측 불가능하게 만들 수 있습니다.

정해진 행동 패턴을 따르기보다는, **문맥에 맞게 답변하고, 플레이어와의 상호작용에 반응하며**, 더 세련된 대화를 생성합니다. 이 유연성은 플레이어의 행동에 따라 발전하는 생동감 있는 캐릭터를 만들어냅니다.

---

요약하자면, 에이전트는 **AI 모델 (주로 LLM)**을 핵심 추론 엔진으로 사용하여:

- **자연어 이해** : 인간의 지시를 의미 있게 해석하고 반응합니다.

- **추론하고 계획**: 정보를 분석하고, 결정을 내리며, 문제를 해결하기 위한 전략을 세웁니다.

- **환경과 상호작용**: 정보를 수집하고, 행동을 취하며, 그 결과를 관찰합니다.

이제 여러분은 에이전트에 대해 확실히 배웠으니, 짧은 퀴즈로 배운 내용을 점검한 후, “에이전트의 두뇌”인 [LLM](what-are-llms)에 대해 더 알아보도록 합시다!

### 컨텐츠 목록
https://huggingface.co/learn/agents-course/ko/unit1/README.md

# 컨텐츠 목록

👉 여기에서 Unit1을 접속하실 수 있습니다.

### 사고: AI 에이전트의 내부 추론과 Re-Act 방식 [[thought-internal-reasoning-and-the-re-act-approach]]
https://huggingface.co/learn/agents-course/ko/unit1/thoughts.md

# 사고: AI 에이전트의 내부 추론과 Re-Act 방식 [[thought-internal-reasoning-and-the-re-act-approach]]

> [!TIP]
> 이 섹션에서는 AI 에이전트의 내면—즉, 추론하고 계획하는 능력을 자세히 살펴봅니다. 에이전트가 내부 대화를 통해 정보를 분석하고, 복잡한 문제를 다루기 쉬운 단계로 나누며, 다음 행동을 결정하는 과정을 탐구합니다. 또한 'Re-Act' 방식이라는 프롬프팅 기법을 소개합니다. 이는 모델이 행동하기 전에 '단계적으로 생각'하도록 유도하는 방법입니다.

사고는 **에이전트가 작업을 해결하기 위해 내부적으로 추론하고 계획하는 과정**을 의미합니다.

이는 에이전트의 대규모 언어 모델(LLM)이 가진 **프롬프트에 제시된 정보를 분석하는 능력**을 활용하는 것입니다.

마치 에이전트의 머릿속 대화라고 생각하면 됩니다. 주어진 과제를 검토하고 어떻게 접근할지 전략을 세우는 과정이죠.

에이전트의 사고 과정은 현재 상황을 관찰하고 다음에 취해야 할 행동을 결정하는 역할을 합니다.

이를 통해 에이전트는 **복잡한 문제를 더 작고 다루기 쉬운 단계로 분해**하고, 이전 경험을 되돌아보며, 새로운 정보를 바탕으로 계획을 지속적으로 조정합니다.

다음은 일반적인 사고 유형의 예시입니다:

| 사고 유형 | 예시 |
|----------------|---------|
| 계획 수립 | "이 작업을 세 단계로 나눠야겠다: 1) 데이터 수집, 2) 트렌드 분석, 3) 보고서 작성" |
| 분석 | "오류 메시지를 보니, 문제는 데이터베이스 연결 설정과 관련이 있는 것 같다" |
| 의사 결정 | "사용자의 예산 제약을 고려하면, 중간 가격대 옵션을 추천하는 것이 좋겠다" |
| 문제 해결 | "이 코드를 최적화하려면, 먼저 어디가 병목인지 프로파일링해봐야 한다" |
| 기억 활용 | "사용자가 앞서 파이썬을 선호한다고 했으니, 파이썬 예제를 제공해야겠다" |
| 자기 성찰 | "이전 접근법이 효과적이지 않았으니, 다른 방식을 시도해봐야겠다" |
| 목표 설정 | "이 작업을 완료하려면, 먼저 성공 기준을 명확히 해야 한다" |
| 우선순위 결정 | "새 기능을 추가하기 전에 보안 취약점부터 해결하는 것이 옳다" |

> **참고:** 함수 호출에 최적화된 LLM의 경우, 사고 과정은 선택적으로 사용할 수 있습니다.
> *함수 호출에 익숙하지 않다면, 행동(Actions) 섹션에서 더 자세한 내용을 확인할 수 있습니다.*

## Re-Act 방식 [[the-re-act-approach]]

핵심 방법론 중 하나는 "추론"(Reasoning)과 "행동"(Acting)을 결합한 **ReAct 방식**입니다.

ReAct는 LLM이 다음 토큰을 생성하기 전에 "단계별로 생각해보자"라는 문구를 추가하는 간단한 프롬프팅 기법입니다.

모델에게 "단계별로 생각"하도록 지시하면, 바로 최종 해답을 내놓기보다 **계획을 세우는 방향**으로 토큰 생성이 유도됩니다. 이는 모델이 문제를 *하위 과제*로 **분해**하도록 장려하기 때문입니다.

이렇게 하면 모델이 각 단계를 더 상세히 고려할 수 있어, 일반적으로 최종 해답을 바로 생성하려 할 때보다 오류가 적게 발생합니다.

(d)는 "단계별로 생각해보자"라는 프롬프트를 사용한 Re-Act 방식의 예시입니다

> [!TIP]
> 최근 추론 전략에 대한 관심이 크게 늘고 있습니다. Deepseek R1이나 OpenAI의 o1과 같은 모델들이 바로 이런 흐름을 반영합니다. 이 모델들은 "대답하기 전에 먼저 생각하도록" 특별히 미세 조정되었습니다.
>
> 이런 모델들은 특정 _사고_ 영역(``와 `` 특수 토큰 사이에 포함)을 항상 생성하도록 훈련되었습니다. 이는 ReAct처럼 단순한 프롬프팅 기법이 아니라, 우리가 원하는 결과물의 수천 가지 예시를 분석한 후 이런 사고 영역을 생성하는 방법을 학습하는 훈련 방식입니다.

--- 
이제 사고 과정에 대해 더 잘 이해했으니, 프로세스의 두 번째 부분인 행동(Act)에 대해 더 자세히 살펴보겠습니다.

### 관찰: 피드백을 통합하여 성찰하고 적응하기 [[observe-integrating-feedback-to-reflect-and-adapt]]
https://huggingface.co/learn/agents-course/ko/unit1/observations.md

# 관찰: 피드백을 통합하여 성찰하고 적응하기 [[observe-integrating-feedback-to-reflect-and-adapt]]

관찰은 **에이전트가 자신의 행동 결과를 인식하는 방법**입니다.

이는 에이전트의 사고 과정을 촉진하고 향후 행동을 안내하는 중요한 정보를 제공합니다.

관찰은 **환경으로부터의 신호**입니다. API의 데이터, 오류 메시지, 또는 시스템 로그와 같은 정보가 다음 사고 주기를 이끕니다.

관찰 단계에서 에이전트는:

- **피드백 수집:** 행동이 성공했는지(또는 실패했는지)에 대한 데이터나 확인을 받습니다.
- **결과 추가:** 새로운 정보를 기존 맥락에 통합하여 실질적으로 기억을 업데이트합니다.
- **전략 조정:** 이렇게 업데이트된 맥락을 활용하여 이후의 사고와 행동을 개선합니다.

예를 들어, 날씨 API가 *"구름 조금, 15°C, 습도 60%"*와 같은 데이터를 반환하면, 이 관찰 결과는 에이전트의 기억(프롬프트 끝부분)에 추가됩니다.

그런 다음 에이전트는 이를 활용해 추가 정보가 필요한지 아니면 최종 답변을 제공할 준비가 되었는지 결정합니다.

이러한 **피드백의 반복적 통합은 에이전트가 목표에 계속 맞춰 나가도록 보장**하며, 실제 결과를 바탕으로 지속적으로 학습하고 조정합니다.

이러한 관찰은 **웹페이지 텍스트를 읽는 것부터 로봇 팔의 위치를 모니터링하는 것까지 다양한 형태**를 취할 수 있습니다. 이는 행동 실행에 대한 텍스트 피드백을 제공하는 도구 "로그"와 같이 볼 수 있습니다.

| 관찰 유형 | 예시 |
|---------------------|---------------------------------------------------------------------------|
| 시스템 피드백 | 오류 메시지, 성공 알림, 상태 코드 |
| 데이터 변경 | 데이터베이스 업데이트, 파일 시스템 수정, 상태 변화 |
| 환경 데이터 | 센서 읽기, 시스템 지표, 자원 사용량 |
| 응답 분석 | API 응답, 쿼리 결과, 계산 출력 |
| 시간 기반 이벤트 | 기한 도달, 예약 작업 완료 |

## 결과는 어떻게 추가되나요? [[how-are-the-results-appended]]

행동을 수행한 후, 프레임워크는 다음 단계를 순서대로 따릅니다:

1. **행동을 분석**하여 호출할 함수와 사용할 인수를 식별합니다.
2. **행동을 실행**합니다.
3. **결과를 추가**하여 **관찰**합니다.

---
이제 에이전트의 사고-행동-관찰 주기에 대해 배웠습니다.

일부 측면이 아직 명확하지 않더라도 걱정하지 마세요. 이후 단원에서 이러한 개념을 다시 살펴보고 더 깊이 이해할 기회가 있을 것입니다.

이제 첫 번째 에이전트를 직접 코딩하여 지금까지 배운 지식을 실습해 볼 시간입니다!

### 에이전트 소개 [[introduction-to-agents]]
https://huggingface.co/learn/agents-course/ko/unit1/introduction.md

# 에이전트 소개 [[introduction-to-agents]]

첫 번째 단원에 오신 것을 환영합니다! 이번 단원에서 여러분을 **AI 에이전트의 기초**를 탄탄히 다질 것이며, 다룰 주요 내용은 다음과 같습니다 :

- **에이전트 이해하기**  
  - 에이전트란 무엇이며, 어떻게 작동하는가?
  - 에이전트는 어떻게 추론과 계획을 통해 의사 결정을 내리는가?

- **에이전트 내 LLM(대형 언어 모델)의 역할**  
  - LLM이 에이전트의 "두뇌" 역할을 하는 방식
  - LLM이 메시지 시스템을 통해 대화를 구조화하는 방법.

- **도구(Tool)와 행동(Action)**  
  - 에이전트가 외부 도구를 활용하여 환경과 상호작용하는 방식
  - 에이전트에 도구를 구축하고 통합하는 방법

- **에이전트 워크플로우:** 
  - *생각(Think)* → *행동(Act)* → *관찰(Observe)*.

이 개념들을 살펴본 후, 여러분은 `smolagents`를 사용하여 **첫 번째 에이전트**를 직접 구현해 볼 것입니다!
 
Alfred라는 에이전트를 통해, 배운 개념들을 적용할 수 있는 간단한 작업 수행 방법을 배울 것입니다.

또한 **Hugging Face Spaces에 여러분이 만든 에이전트를 게시하는 방법**도 배워, 동료들과 친구들에게 공유할 수 있습니다.

마지막으로, 이 단원의 끝에서 퀴즈를 통과하게 되면 **🎓 Certificate of Fundamentals of Agents** 인증서를 획득할 수 있습니다!

이 단원은 **에이전트 학습의 필수 시작점**으로, 이후 더 고급 개념을 배우기 위한 기초를 다집니다.

단원의 내용이 많으니, **천천히** 학습하며 필요할 때 다시 돌아와 복습하세요!

준비되셨나요? 함께 시작해 봅시다! 🚀

### 사고-행동-관찰 주기를 통해 AI 에이전트 이해하기 [[understanding-ai-agents-through-the-thought-action-observation-cycle]]
https://huggingface.co/learn/agents-course/ko/unit1/agent-steps-and-structure.md

# 사고-행동-관찰 주기를 통해 AI 에이전트 이해하기 [[understanding-ai-agents-through-the-thought-action-observation-cycle]]

이전 섹션에서 우리는 다음 내용을 배웠습니다:

- **도구가 시스템 프롬프트에서 에이전트에 어떻게 제공되는지**.
- **AI 에이전트가 '추론'하고, 계획을 세우며, 환경과 상호작용하는 시스템이라는 것**.

이번 섹션에서는 **AI 에이전트의 전체 워크플로우, 사고(Thought)-행동(Action)-관찰(Observation) 주기**에 대해 살펴보겠습니다.

그리고 각 단계에 대해 더 깊이 탐구해 보겠습니다.

## 핵심 구성 요소 [[the-core-components]]

에이전트는 **사고(Thought) → 행동(Act) → 관찰(Observe)**의 연속적인 주기로 작동합니다.

이 과정의 각 단계를 자세히 살펴보겠습니다:

1. **사고(Thought)**: 에이전트의 LLM부분이 다음에 수행할 단계를 결정합니다.
2. **행동(Action):** 에이전트가 도구를 호출하고 필요한 인자를 전달하여 특정 행동을 수행합니다.
3. **관찰(Observation):** 모델이 도구의 응답을 검토합니다.

## 사고(Thought)-행동(Action)-관찰(Observation) 주기 [[the-thought-action-observation-cycle]]

이 세 가지 구성 요소는 반복루프 내에서 함께 작동합니다. 프로그래밍에 비유하자면, 에이전트는 **while 루프**를 사용합니다. 즉, 에이전트의 목표가 달성될 때까지 루프가 계속 실행됩니다.

이를 시각적으로 표현하면 다음과 같습니다:

많은 에이전트 프레임워크에서는 **규칙과 가이드라인이 시스템 프롬프트에 직접 내장**되어 있어, 각 주기가 정의된 논리에 따라 실행됩니다.

이를 단순화한 시스템 프롬프트 예시는 다음과 같습니다:

이 시스템 메시지에서 우리는 다음 요소들을 정의했습니다:

- *에이전트의 행동 방식*
- *에이전트가 접근할 수 있는 도구들* (이전 섹션에서 설명한 내용)
- *사고(Thought)-행동(Action)-관찰(Observation) 주기* 를 LLM 지침에 내장

이제, 각 단계를 더 깊이 탐구하기 전에, 간단한 예제를 통해 이 과정이 어떻게 작동하는지 살펴보겠습니다.

## 날씨 에이전트 알프레드(Alfred) [[alfred-the-weather-agent]]

우리는 날씨 정보를 제공하는 에이전트 Alfred를 만들었습니다.

사용자가 Alfred에게 다음과 같이 질문합니다: “오늘 뉴욕 날씨 어때?”

Alfred의 역할은 날씨 API 도구를 사용하여 이 질문에 답하는 것입니다.

다음과 같이 사고-행동-관찰 주기가 진행됩니다:

### 사고(Thought) [[thought]]

**내부 사고 과정(Internal Reasoning):**

질문을 받은 후, Alfred의 내부에서 이루어지는 대화는 다음과 같을 수 있습니다 :

*"사용자는 뉴욕의 현재 날씨 정보를 원하고 있어. 내가 사용할 수 있는 날씨 API 도구가 있으니, 먼저 이 도구를 호출해서 최신 정보를 가져와야 해."*

이 단계에서 에이전트는 문제를 단계별로 나눕니다 : 첫 번째 단계는 필요한 데이터를 수집하는 것입니다.

### 행동(Action) [[action]]

**도구 사용:**

Alfred는 추론과 `get_weather` 도구를 알고 있다는 사실에 기반해, 날씨 API 도구를 호출하기 위한 JSON 형식의 명령을 준비합니다.
예를 들어, 첫 번째 액션은 다음과 같을 수 있습니다:

사고(Thought): 뉴욕의 현재 날씨를 확인해야 해.

 ```
    {
      "action": "get_weather",
      "action_input": {
        "location": "New York"
      }
    }
 ```

여기서, action은 어떤 도구를 호출할지 지정하고 (get_weather) 필요한 입력값(예: "location": "New York")을 설정합니다.

### 관찰(Observation) [[observation]]

**환경으로부터의 피드백:**

도구 호출 후, Alfred는 관찰 결과를 받습니다. 예를 들어, API에서 반환된 날씨 데이터가 다음과 같을 수 있습니다:

*"뉴욕의 현재 날씨: 부분적으로 흐림, 15°C, 습도 60%"*

이러한 관찰 결과는 추가 컨텍스트로 프롬프트에 더해집니다. 즉, 관찰은 현실 세계에서의 피드백 역할을 하며, 에이전트가 실행한 행동이 성공했는지 확인하고 필요한 정보를 제공합니다.

### 업데이트된 사고(thought)과정 [[updated-thought]]

**성찰(Reflecting):**

관찰 데이터를 얻은 후, Alfred는 내부 사고 과정을 업데이트합니다:

*"이제 뉴욕의 날씨 데이터를 확보했으니, 사용자에게 답변을 정리할 수 있어."*

### 최종 행동(Action) [[final-action]]

Alfred는 이제 사용자에게 전달할 최종 응답을 우리가 지정해준 형식에 맞게 생성합니다:

사고: 이제 날씨 데이터를 확보했어. 뉴욕의 현재 날씨는 부분적으로 흐리고, 기온은 15°C, 습도는 60%야.

최종 답변 : 뉴욕의 현재 날씨는 부분적으로 흐리고, 기온은 15°C, 습도는 60%입니다.

이 최종 행동을 통해 답변을 사용자에게 전달하고, 루프를 종료합니다.

이 예제를 통해 배운 것:

- **에이전트는 목표가 달성될 때까지 반복적으로 루프를 실행한다:**
    
**Alfred의 과정은 순환적입니다. 사고(thought)에서 시작해, 도구를 호출함으로 행동(Action)을 취하고, 마지막으로 결과를 관찰(Observation)합니다. 만약 관찰 단계에서 오류가 발생하거나 데이터가 불완전하면, Alfred는 루프를 다시 실행하여 문제를 해결합니다.
    
- **도구(Tool) 통합:**

에이전트는 **정적인 지식을 넘어, 외부 도구(날씨 API같은)를 호출하여 실시간 데이터**를 가져올 수 있습니다. 이것은 AI 에이전트의 핵심 기능 중 하나입니다.

- **동적 적응(Dynamic Adaptation):**

에이전트는 각 주기를 거치면서, 새로운 정보(관찰)를 반영하여 사고 과정을 조정합니다. 이를 통해 최종 답변이 더 정확하고 신뢰할 수 있도록 만듭니다.
    
이 예제는 우리가 다음 섹션에서 다룰 **ReAct(강화학습-Reinforcement Learning + 행동-Action) 주기**의 핵심 개념을 보여줍니다:
**사고(Thought), 행동(Action), 관찰(Observation)의 상호작용을 통해 AI 에이전트가 복잡한 문제를 점진적으로 해결**할 수 있도록 합니다.

이러한 원칙을 이해하고 적용함으로써, 에이전트를 설계할 때 단순히 작업을 추론하는 것뿐만 아니라 **외부 도구를 효과적으로 활용하여 작업을 완료**할 수 있도록 만들 수 있습니다. 또한, 환경에서 받은 피드백을 바탕으로 지속적으로 출력을 개선해나갑니다.

---

이제 Thought, Action, Observation을 개별 단계별로 더 깊이 탐구해 보겠습니다.

### Q1: 에이전트(Agent)란? [[q1-what-is-an-agent]]
https://huggingface.co/learn/agents-course/ko/unit1/quiz1.md

### Q1: 에이전트(Agent)란? [[q1-what-is-an-agent]]
다음 중 AI 에이전트를 가장 잘 설명한 것은 무엇인가요?

---

### Q2: What is the Role of Planning in an Agent? 에이전트에서 계획(Planning)이 하는 역할은?
에이전트는 행동을 취하기 전에 왜 계획을 세워야 할까요?

---

### Q3: 도구(Tools)는 에이전트의 기능을 어떻게 향상시키는가?
도구는 왜 에이전트에게 필수적인 요소일까요?

---

### Q4: 행동(Actions)과 도구(Tools)의 차이점은?
행동과 도구의 핵심적인 차이는 무엇인가요?

---

### Q5: LLM은 에이전트에서 어떤 역할을 하는가?

LLM는 에이전트의 기능에 어떻게 기여할까요?

---

### Q6: 다음 중 AI 에이전트의 실제 사례로 가장 적절한 것은?
다음 예시 중 실제 AI 에이전트의 작동 방식과 가장 유사한 것은 무엇인가요?

---

퀴즈 완료를 축하합니다! 🥳 If you need to review any elements, take the time to revisit the chapter to reinforce your knowledge before diving deeper into the "Agent's brain": LLMs.
본격적으로 "에이전트의 두뇌"인 LLM에 대해 학습하기 이전에 배운 내용을 다시 복습하고 싶다면, 해당 챕터를 다시 확인해 보세요. 🚀

### 더미 에이전트 라이브러리 [[dummy-agent-library]]
https://huggingface.co/learn/agents-course/ko/unit1/dummy-agent-library.md

# 더미 에이전트 라이브러리 [[dummy-agent-library]]

이 코스는 특정 프레임워크에 종속되지 않도록 설계되었습니다. 그 이유는 **AI 에이전트의 개념에 집중하고 특정 프레임워크의 세부 사항에 매몰되지 않기 위함입니다**.

또한, 학생들이 이 강의에서 배운 개념을 원하는 프레임워크를 사용해 자신의 프로젝트에 적용할 수 있기를 바랍니다.

따라서 Unit 1에서는 간단한 더미 에이전트 라이브러리와 서버리스 API를 사용하여 LLM 엔진에 접근할 것입니다.

실제 프로덕션 환경에서는 이런 방식을 사용하지 않겠지만, **에이전트의 작동 원리를 이해하는 데 좋은 출발점**이 될 것입니다.

이 섹션을 마치면 `smolagents`를 사용하여 **간단한 에이전트를 만들** 준비가 될 것입니다.

이어지는 Unit에서는 `LangGraph`, `LlamaIndex`와 같은 다른 AI 에이전트 라이브러리도 사용해 볼 예정입니다.

간단하게 하기 위해 도구와 에이전트로 단순한 Python 함수를 사용할 것입니다.

어떤 환경에서도 시도해볼 수 있도록 `datetime`이나 `os`와 같은 내장 Python 패키지를 사용할 것입니다.

[이 노트북](https://huggingface.co/agents-course/notebooks/blob/main/dummy_agent_library.ipynb)에서 과정을 따라가며 **직접 코드를 실행**해볼 수 있습니다.

## 서버리스 API [[serverless-api]]

Hugging Face 생태계에는 다양한 모델에서 쉽게 추론을 실행할 수 있게 해주는 서버리스 API라는 편리한 기능이 있습니다. 별도의 설치나 배포 과정이 필요 없습니다.

```python
import os
from huggingface_hub import InferenceClient

## https://hf.co/settings/tokens에서 토큰이 필요합니다. 토큰 유형으로 'read'를 선택했는지 확인하세요. Google Colab에서 실행할 경우 "설정" 탭의 "시크릿" 아래에서 설정할 수 있습니다. 반드시 "HF_TOKEN"이라고 이름을 지정해야 합니다.
os.environ["HF_TOKEN"]="hf_xxxxxxxxxxxxxx"

client = InferenceClient("meta-llama/Llama-3.2-3B-Instruct")
# 다음 셀의 출력이 올바르지 않다면, 무료 모델이 과부하 상태일 수 있습니다. 대신 Llama-3.2-3B-Instruct가 포함된 이 공개 엔드포인트를 사용할 수 있습니다
# client = InferenceClient("https://jc26mwg228mkj8dw.us-east-1.aws.endpoints.huggingface.cloud")
```

```python
output = client.text_generation(
    "The capital of France is",
    max_new_tokens=100,
)

print(output)
```
출력:
```
Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris.
```
LLM 섹션에서 보았듯이, 단순 디코딩만 수행하면 **모델은 EOS(End of Sequence) 토큰을 예측할 때만 멈추게 됩니다**. 하지만 여기서는 그런 일이 일어나지 않습니다. 이는 이것이 대화형(채팅) 모델이고 **모델이 기대하는 채팅 템플릿을 적용하지 않았기 때문입니다**.

이제 우리가 사용하는 Llama-3.2-3B-Instruct 모델과 관련된 특수 토큰을 추가하면, 동작이 바뀌어 예상대로 EOS가 생성됩니다.

```python
prompt="""user
The capital of France isassistant"""
output = client.text_generation(
    prompt,
    max_new_tokens=100,
)

print(output)
```
출력:
```
The capital of France is Paris.
```

"chat" 메서드를 사용하는 것이 채팅 템플릿을 적용하는 훨씬 더 편리하고 안정적인 방법입니다:
```python
output = client.chat.completions.create(
    messages=[
        {"role": "user", "content": "The capital of France is"},
    ],
    stream=False,
    max_tokens=1024,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)
```
출력:
```
Paris.
```
chat 메서드는 모델 간 원활한 전환을 보장하기 위해 권장되는 방법이지만, 이 노트북은 교육용이므로 세부 내용을 이해하기 위해 계속해서 "text_generation" 메서드를 사용하겠습니다.

## 더미 에이전트 [[dummy-agent]]

앞 섹션에서 보았듯이, 에이전트 라이브러리의 핵심은 시스템 프롬프트에 정보를 추가하는 것입니다.

이 시스템 프롬프트는 앞서 본 것보다 조금 더 복잡하지만, 이미 다음과 같은 내용을 포함하고 있습니다:

1. **도구에 관한 정보**
2. **사이클 지시사항** (생각(Thought) → 행동(Action) → 관찰(Observation))

```
Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have an `action` key (with the name of the tool to use) and an `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer.
```

"text_generation" 메서드를 사용하고 있으므로 프롬프트를 수동으로 적용해야 합니다:
```python
prompt=f"""system
{SYSTEM_PROMPT}
user
What's the weather in London ?
assistant
"""
```

다음과 같이 할 수도 있습니다. 이는 `chat` 메서드 내부에서 일어나는 일입니다:
```python
messages=[
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "What's the weather in London ?"},
    ]
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-3B-Instruct")

tokenizer.apply_chat_template(messages, tokenize=False,add_generation_prompt=True)
```

이제 프롬프트는 다음과 같습니다:
```
system
Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have an `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer. 
user
What's the weather in London ?
assistant
```

이제 디코딩을 해봅시다!
```python
output = client.text_generation(
    prompt,
    max_new_tokens=200,
)

print(output)
```
출력:

````
Thought: I will check the weather in London.
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Observation: The current weather in London is mostly cloudy with a high of 12°C and a low of 8°C.
````

문제가 보이시나요?
>모델이 실제 데이터 없이 허구의 답변을 만들어냈습니다. 실제 함수를 실행하려면 여기서 생성을 중단해야 합니다!
이제 허구의 응답이 생성되지 않도록 "Observation:" 부분 전에 생성을 중지해 봅시다.

```python
output = client.text_generation(
    prompt,
    max_new_tokens=200,
    stop=["Observation:"] # 실제 함수가 호출되기 전에 중단합니다
)

print(output)
```
출력:

````
Thought: I will check the weather in London.
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "London"}
}
```
Observation:
````

훨씬 좋아졌습니다!
이제 간단한 날씨 정보 제공 함수를 만들어 봅시다. 실제 상황에서는 API를 호출하게 될 것입니다.

```python
# 더미 함수
def get_weather(location):
    return f"the weather in {location} is sunny with low temperatures. \n"

get_weather('London')
```
출력:
```
'the weather in London is sunny with low temperatures. \n'
```

이제 기본 프롬프트, 함수 실행까지의 출력, 그리고 함수 실행 결과를 관찰 결과로 연결한 다음 생성을 계속해 봅시다.

```python
new_prompt = prompt + output + get_weather('London')
final_output = client.text_generation(
    new_prompt,
    max_new_tokens=200,
)

print(final_output)
```
새로운 프롬프트는 다음과 같습니다:
````
system
    Answer the following questions as best you can. You have access to the following tools:

get_weather: Get the current weather in a given location

The way you use the tools is by specifying a json blob.
Specifically, this json should have a `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).

The only values that should be in the "action" field are:
get_weather: Get the current weather in a given location, args: {"location": {"type": "string"}}
example use : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about one action to take. Only one action at a time in this format:
Action:

$JSON_BLOB (inside markdown cell)

Observation: the result of the action. This Observation is unique, complete, and the source of truth.
... (this Thought/Action/Observation can repeat N times, you should take several steps when needed. The $JSON_BLOB must be formatted as markdown and only use a SINGLE action at a time.)

You must always end your output with the following format:

Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters `Final Answer:` when you provide a definitive answer. 
user
What's the weather in London ?
assistant
Thought: I will check the weather in London.
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": {"type": "string", "value": "London"}
}
```
Observation:the weather in London is sunny with low temperatures. 
````

출력:
```
Final Answer: The weather in London is sunny with low temperatures.
```

---

지금까지 Python 코드를 사용하여 처음부터 에이전트를 만드는 방법을 배웠고, **그 과정이 얼마나 번거로울 수 있는지** 확인했습니다. 다행히 많은 에이전트 라이브러리들이 이러한 작업을 단순화하여 복잡한 부분을 대신 처리해 줍니다.

이제 `smolagents` 라이브러리를 사용하여 **첫 번째 실제 에이전트를 만들** 준비가 되었습니다.

### 온보딩(Onboarding): 첫 걸음 내딛기 ⛵
https://huggingface.co/learn/agents-course/ko/unit0/onboarding.md

# 온보딩(Onboarding): 첫 걸음 내딛기 ⛵

이제 모든 필요한 정보를 확인했으니, 본격적으로 시작해 봅시다! 이 섹션에서는 다음 네 가지를 진행할 예정입니다 :

1. **Hugging Face 계정 생성** (아직 계정이 없으신 경우)
2. **Discord 가입 후 자기 소개** (수줍어 마세요 🤗)
3. **Hub에서 Hugging Face Agents 코스 팔로우**
4. **코스 널리 알리기** 

### Step 1: Hugging Face 계정 생성

(아직 계정이 없으신 경우) 여기에서 Hugging Face 계정을 만들어주세요.

### Step 2: Discord 커뮤니티에 가입하기

👉🏻 여기에서 Discord 서버에 가입하세요.

가입 후, `#introduce-yourself` 채널에서 간단하게 자기소개를 남겨주세요.

모든 강좌 관련 질문과 문의는 `Hugging Face Hub`의 `courses` 채널을 방문해 주세요.

Discord 사용이 처음이신 분들을 위해, Discord 101 가이드를 준비했습니다. [다음 섹션](discord101)에서 확인해 보세요!

### Step 3: Hugging Face Agent 코스 팔로우하기

최신 강의 자료, 업데이트, 공지를 놓치지 않으려면 ** Hugging Face Agents 코스를 팔로우하세요**

👉 Go 여기에서 **팔로우(follow)**버튼을 클릭하세요!

### Step 4: 코스 널리 알리기

이 코스가 더 많은 사람들에게 알려질 수 있도록 도와주세요 :) 두 가지 기여 방법을 제안합니다 :

1. Github에서 ⭐로 코스 응원하기

2. 배움의 여정 공유하기 : **이 코스를 듣고 있다는 것을 많은 사람들에게 알려주세요!** 소셜미디어에 사용하실 수 있도록 이미지도 준비해두었습니다 !

 👉 [여기](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/communication/share.png?download=true) 에서 이미지를 다운로드 할 수 있습니다.

축하합니다! 🎉 **온보딩 과정을 완료하셨습니다!** 이제 본격적으로 AI 에이전트에 대해 배울 준비가 되었습니다 ! 즐거운 학습되세요.

Keep Learning, stay awesome 🤗

### AI 에이전트 코스에 오신걸 환영합니다 🤗 [[introduction]]
https://huggingface.co/learn/agents-course/ko/unit0/introduction.md

# AI 에이전트 코스에 오신걸 환영합니다 🤗 [[introduction]]

이미지 배경은 Scenario.com 을 활용하여 제작되었습니다.

오늘날 AI에서 가장 흥미로운 주제인 **에이전트(Agents)**에 오신 것을 환영합니다!

이 무료 코스에서는 **초급부터 전문가 수준**까지, AI 에이전트를 이해하고 활용하며 직접 구축하는 방법을 배울 수 있습니다.

첫 번째 유닛에서는 다음과 같은 내용을 다룹니다:

- **강의 커리큘럼** 살펴보기
- **수강 방식 선택** (자기주도 학습 또는 인증 과정)
- **인증 과정 및 마감일 정보**
- 코스 운영팀 소개
- **Hugging Face** 계정 만들기.
- **Discord 서버** 가입하고 다른 학습자 및 강사들과 소통하기 

지금 바로 시작해 보세요!

## 이 코스에서 무엇을 배울 수 있나요? [[expect]]

해당 코스에서는 다음과 같은 내용을 학습합니다:

- 📖 AI 에이전트의 **이론, 설계, 실전 활용**에 대해 공부합니다.
- 🧑‍💻 [smolagents](https://huggingface.co/docs/smolagents/en/index), [LangChain](https://www.langchain.com/), and [LlamaIndex](https://www.llamaindex.ai/)  등의 **AI 에이전트 라이브러리** 활용법을 배웁니다.
- 💾 Hugging Face Hub에 **자신이 만든 에이전트**를 공유하고, 커뮤니티에서 제작한 에이전트를 탐색합니다.
- 🏆 **다른 학습자의 에이전트와 비교 평가**하는 챌린지에 참여합니다.
- 🎓 과제를 완료하면 **수료 인증서**를 받을 수 있습니다.

그 외에도 다양한 내용을 다룹니다!

이 코스를 마치면 **AI 에이전트의 동작 원리를 이해하고, 최신 라이브러리와 도구를 활용하여 직접 구현하는 방법**을 익히게 됩니다.

👉 지금 바로 코스에 등록하세요!

(HuggingFace는 개인정보 보호를 존중합니다. 이메일 주소는 **각 단원 공개시 링크, 챌린지 및 업데이트 정보**를 제공하는 용도로만 사용됩니다.)

## 이 코스는 어떻게 진행되나요? [[course-look-like]]

이 코스는 다음과 같이 구성됩니다:

- *기본 개념 학습*: 에이전트 개념을 **이론적**으로 배우는 단계입니다.
- *실습*:  **기존 AI 에이전트 라이브러리**를 활용해 특정 환경에서 에이전트를 훈련하는 방법을 배웁니다. 실습은 사전 구성된 환경을 제공하는 **Hugging Face Spaces**에서 진행됩니다.
- *실전 적용 과제*: 배운 개념을 활용해 현실 문제를 해결하는 과제입니다. 
- *챌린지*: 여러분이 개발한 에이전트를 다른 에이전트와 경쟁시켜 볼 수 있습니다. 또한, 에이전트 성능을 비교할 수 있는 [리더보드](https://huggingface.co/spaces/huggingface-projects/AI-Agents-Leaderboard) 도 곧 제공될 예정입니다.

**에이전트 코스는 여러분의 피드백과 기여를 통해 발전되는 프로젝트입니다 !**  [GitHub에서 이슈나 PR을 제출하거나](https://github.com/huggingface/agents-course), Discord 에서 토론에 참여해주세요!

코스를 완료한 후에는  [👉 이 폼](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog) 을 통해 피드백을 보낼 수 있습니다.

## 코스 개요 [[syllabus]]

 **전체적인 코스 커리큘럼**입니다.  각 단원 세부 주제 목록들은 해당 단원 공개 시 함께 제공됩니다.

| 단원 | 주제 | 설명 |
| :---- | :---- | :---- |
| 0 | 온보딩 | 강의에서 사용할 도구 및 플랫폼을 설정합니다. |
| 1 | 에이전트 기본 개념 | 도구, 사고 과정, 행동, 관찰 및 해당 형식에 대해 설명합니다.  또한 LLM, 메시지, 특수 토큰, 채팅 템플릿에 대해 설명하고,  python 함수를 도구로 사용하는 간단한 사례를 소개합니다. |
| 1.5 | Bonus : 함수 호출을 위한 LLM 미세 조정 | LoRa 를 사용하여 노트북 내에서 함수 호출을 수행하는 모델을 미세 조정합니다. |
| 2 | 프레임워크 | 기본 개념이 인기 라이브러리 smolagents, LangGraph, LLamaIndex 에서 어떻게 구현되는지 살펴봅니다. |
| 3 | 실전 적용 사례 | 실제 활용 사례를 구현해봅니다. (경험이 있는 에이전트 개발자분들의 PR 환영 🤗) |
| 4 | 최종 과제 | 특정 벤치마크를 위한 에이전트를 구현하고, 학생 리더보드에서 성능을 평가합니다. 🚀 |

*보너스 단원도 제공되니 기대해 주세요!*

## 선수 지식 [[what-are-the-prerequisites]]

이 코스를 완주하기 위해 다음과 같은 기본 지식이 필요합니다:

- Python 기본지식
- LLM 기본 개념 (1단원에서 LLM 복습 섹션을 제공합니다.)

## 필요 도구 [[tools]]

코스를 수강하기 위해 단 2가지만 필요합니다:

- *인터넷이 연결된 컴퓨터*
- *Hugging Face 계정*: 모델 및 에이전트를 푸쉬/업로드하고 Spaces를 생성하는 데 필요합니다. 계정이 없으시다면, **[이곳](https://hf.co/join)** 에서 무료로 생성 할 수 있습니다. 

## 인증 과정 [[certification-process]]

이 코스는 *자유 수강 모드*로 진행할 수도 있고, 활동을 수행하여 *두 가지 인증서 중 하나*를 받을 수도 있습니다.

자유 수강 모드에서는 원하는 경우 과제와 챌린지에 참여할 수 있으며, **별도로 인증을 신청할 필요가 없습니다.**

인증 과정은 **무료**입니다:

- *기본 개념 인증서*: 1단원을 완료해야합니다. *최신 에이전트 트렌드를 배우고자 하는 학생들을 위한 과정*
- *완료 인증서*: 1단원, 실전 활용 사례 과제 중 하나, 최종 과제를 완료해야합니다.

인증 과정에는 마감기한이 있으며, 모든 과제는**2025년 5월 1일 이전에** 완료해야 합니다.

## 권장 학습 속도 [[recommended-pace]]

코스 각 단원은 **1주일 이내**에 완료할 수 있도록 설계되었으며, **주당 약 3-4시간**의 학습 시간이 필요합니다.

인증 과정 마감 기한이 있기 때문에, 권장 학습 속도를 제공해드립니다! 

## 코스 최대한 활용하는 방법 [[advice]]

코스를 최대한 활용할 수 있는 방법을 제안해 드립니다:

1.  Discord에서 스터디 그룹에 참여하세요.: 그룹으로 학습하는 것이 효과적입니다. 지금 바로 Discord 서버에 가입하고 Hugging Face 계정을 인증하세요!
2. **퀴즈와 과제를 수행하세요**: 가장 좋은 학습 방법은 실습과 자기 평가입니다.
3. **일정을 정하고 꾸준히 학습하세요**: 아래 권장 학습 속도를 참고하시거나, 자신만의 일정을 만들어 보세요..

## 우리는 누구인가 [[who-are-we]]

저자 소개:

### Joffrey Thomas [[joffrey-thomas]]

Joffrey는 Hugging Face의 머신러닝 엔지니어로, AI 에이전트를 구현하고 실제 환경에 배포한 경험이 있습니다. Joffrey는 이 코스의 메인 강사입니다.

- [Joffrey Hugging Face에서 팔로우 하기](https://huggingface.co/Jofthomas)
- [Joffrey X에서 팔로우 하기](https://x.com/Jthmas404)
- [Joffrey Linkedin에서 팔로우 하기](https://www.linkedin.com/in/joffrey-thomas/)

### Ben Burtenshaw [[ben-burtenshaw]]

Ben은 Hugging Face의 머신러닝 엔지니어로, 다양한 플랫폼에서 강의 경험이 있습니다. Ben의 목표는 이 코스를 모든 사람이 접근할 수 있도록 만드는 것 입니다.

- [Ben Hugging Face에서 팔로우 하기](https://huggingface.co/burtenshaw)
- [Ben X에서 팔로우 하기](https://x.com/ben_burtenshaw)
- [Ben Linkedin에서 팔로우 하기](https://www.linkedin.com/in/ben-burtenshaw/)

### Thomas Simonini [[thomas-simonini]]

Thomas는 Hugging Face의 머신러닝 엔지니어로, HuggingFace의 Deep RL코스와 ML for games 코스를 진행했습니다. Thomas는 에이전트의 큰 팬으로, 커뮤니티가 무엇을 만들지 기대하고 있습니다! 

- [Thomas Hugging Face에서 팔로우 하기 ](https://huggingface.co/ThomasSimonini)
- [Thomas X에서 팔로우 하기](https://x.com/ThomasSimonini)
- [Thomas Linkedin에서 팔로우 하기](https://www.linkedin.com/in/simoninithomas/)

## 감사의 말씀  [[acknowledgments]]

이 코스에 중요한 기여를 해주신 다음 분들께 감사의 말씀을 전합니다:

- **[Pedro Cuenca](https://huggingface.co/pcuenq)** – 자료 검토 지도와 전문적인 도움
- **[Aymeric Roucher](https://huggingface.co/m-ric)** – 디코딩 및 최종 에이전트 데모 스페이스와 smolagents 파트 도움 
- **[Joshua Lochner](https://huggingface.co/Xenova)** – 토큰화 데모 스페이스
- **[Quentin Gallouédec](https://huggingface.co/qgallouedec)** – 코스 내용에 대한 도움
- **[David Berenstein](https://huggingface.co/davidberenstein1957)** – 코스 내용 및 조정 도움

## 버그 발견& 강의 개선점 제안! [[contribute]]

기여는 **환영**입니다 🤗

- 만약 *노트북에서 버그🐛*를 발견하셨다면, 이슈를 열고 **문제를 설명해주세요**.
- 만약 *코스를 개선하고 싶다면*,  풀 리쿼스트를 열어주세요.
- *전체 섹션 또는 새로운 단원*을 추가하고 싶다면, 가장 좋은 방법은이슈를 열고 **추가하고 싶은 내용을 설명해주세요! 이후 저희가 내용 작성을 시작하실 수 있도록 안내해드리겠습니다.**

## 그 외에도 질문이 있으시다면? [[questions]]

 discord 서버의 #ai-agents-discussions.채널에 질문을 남겨주세요.

코스 학습에 앞서 필요한 모든 정보를 습득하셨으니, 출발 준비를 해봅시다!⛵

### (선택 섹션) Discord 101 [[discord-101]]
https://huggingface.co/learn/agents-course/ko/unit0/discord101.md

# (선택 섹션) Discord 101 [[discord-101]]

이 가이드는 게임 및 머신러닝(Machine Learning) 커뮤니티에서 인기 있는 무료 채팅 플랫폼, 디스코드(Discord)를 처음 사용하는 분들을 위한 안내서입니다.

Hugging Face 커뮤니티 Discord 서버에서는 **10만 명 이상**의 멤버가 활동하고 있습니다. 아래 링크를 클릭하여 참여해 보세요!다른 사용자들과 만날 수 있는 좋은 장소입니다!

## Hugging Face 디스코드 채널에서 Agent 코스를 만나보세요!

Starting on Discord can be a bit overwhelming, so here's a quick guide to help you navigate.
디스코드를 처음 이용하신다면 다소 낯설 수 있어, 퀵가이드를 준비했습니다.

Hugging Face 커뮤니티 서버에서는 논문 토론, 이벤트 등 다양한 분야에 활발한 커뮤니티입니다.

먼저 [회원가입](http://hf.co/join/discord) 하신 후, `#introduce-yourself` 채널에서 간단한 자기소개를 남겨주세요!

이 서버에는 에이전트 코스 전용 4가지 채널이 있습니다! :

- `agents-course-announcements`: **최신 코스 소식**을 확인하는 공간
- `🎓-agents-course-general`: **자유로운 대화와 토론** 을 위한 공간
- `agents-course-questions`: **질문 & 동료들과 도움 주고 받기** 위한 공간
- `agents-course-showcase`: **자신이 만든 최고의 AI 에이전트를 공유** 하기 위한 공간

추가로 :

- `smolagents`: **라이브러리에 대한 논의 및 지원** 을 받을 수 있습니다.

## 디스코드 활용 팁

### 서버에 참여하는 방법

Discord가 익숙하지 않다면, 서버 참여 방법에 대한 공식 가이드를 참고하세요!

간단한 절차는 다음과 같습니다 : 

1. 초대 링크를 클릭합니다.
2. 디스코드 계정으로 로그인 하거나, 새 계정을 만듭니다.
3. 본인이 AI 에이전트가 아님을 인증하세요!
4. 별명과 아바타를 설정합니다.
5. "서버 참여(Join Server)"를 클릭합니다!

### 디스코드 효과적으로 활용하기

디스코드를 효과적으로 활용할 수 있는 몇 가지 팁!

- **음성 채널** 도 제공되지만, 일반적으로는 텍스트 채팅이 더 많이 사용됩니다.
- **마크다운 형식**을 사용할 수 있어, 코드 작성시 유용합니다. 링크에는 마크다운 사용이 제한될 수 있습니다!
- **긴 대화**시 스레드를 활용하시면 더 편리합니다.

이 가이드가 도움이 되셨기를 바랍니다! 질문이 있으면 디스코드에서 언제든 문의해주세요. 🤗
