1. 학습 주제
Agentic AI의 기본 구조를 학습했다.
이전까지는 RAG, VectorStore, LangGraph처럼 정해진 흐름을 구성하는 데 집중했다면, 이번에는 모델이 상황에 따라 Tool을 선택하고 실행 결과를 보고 다음 행동을 결정하는 구조를 다뤘다.
핵심 내용은 다음과 같다.
- Workflow와 Agent 차이
- Tool Contract와 Schema
- LangChain create_agent
- Tool Calling 메시지 흐름
- ReAct 구조
- Action, Observation, Final Answer
- Checkpointer 기반 Agent Memory
- thread_id를 통한 대화 기억 분리
2. Workflow와 Agent 차이
Workflow는 개발자가 실행 순서를 미리 정해두는 방식이다.
질문 분석
-> 장소 검색이 필요하면 search_destination 실행
-> 예산 계산이 필요하면 estimate_day_budget 실행
-> 답변 작성
즉, 어떤 Tool을 언제 실행할지 개발자가 if문으로 결정한다.
if needs_destination:
search_destination.invoke(...)
if needs_budget:
estimate_day_budget.invoke(...)
반면 Agent는 모델이 질문과 Tool 설명을 보고 실행 중에 어떤 Tool을 사용할지 결정한다.
사용자 질문
-> 모델이 Tool Schema 확인
-> 필요한 Tool 선택
-> Tool 실행
-> 실행 결과를 보고 최종 답변
정리하면 다음과 같다.
구분결정 주체특징
| Workflow | 개발자 | 흐름이 명확하고 예측 가능 |
| Agent | 모델 | 질문에 따라 실행 중 Tool 선택 |
단순하고 고정된 흐름이면 Workflow가 더 안전하고 충분하다.
하지만 질문이 다양하고 어떤 Tool이 필요할지 매번 달라진다면 Agent 구조가 유용하다.
3. Tool Contract
Agent가 Tool을 사용하려면 단순 Python 함수만 있어서는 부족하다.
모델은 함수 내부 코드를 직접 이해하는 것이 아니라, Tool의 이름, 설명, 입력 Schema를 보고 사용 방법을 판단한다.
@tool(args_schema=BudgetInput)
def estimate_day_budget(
travelers: int,
ticket_price: int,
meal_budget: int
) -> int:
return travelers * (ticket_price + meal_budget)
Tool Contract의 핵심은 다음과 같다.
요소의미
| Tool 이름 | 모델이 호출할 기능 이름 |
| Tool 설명 | 언제 이 도구를 써야 하는지 알려주는 설명 |
| 입력 Schema | 어떤 인자를 어떤 타입으로 받아야 하는지 정의 |
예를 들어 예산 계산 Tool은 다음 입력을 요구한다.
class BudgetInput(BaseModel):
travelers: int
ticket_price: int
meal_budget: int
이 Schema가 있으면 모델이 Tool을 호출할 때 필요한 인자 구조를 맞출 수 있다.
4. Schema 검증과 업무 규칙 검증
Tool에는 두 종류의 검증이 필요하다.
첫 번째는 Schema 검증이다.
필수 필드가 빠졌거나 타입이 맞지 않으면 함수 실행 전에 막는다.
travelers 필수
ticket_price 필수
meal_budget 필수
두 번째는 업무 규칙 검증이다.
타입은 맞지만 값이 업무적으로 말이 안 되는 경우 함수 내부에서 확인한다.
if travelers < 1:
raise ValueError("여행 인원은 1명 이상이어야 합니다.")
정리하면 다음과 같다.
구분처리 위치예시
| Schema 검증 | 함수 실행 전 | 필수값 누락, 타입 오류 |
| 업무 규칙 검증 | 함수 내부 | 인원 0명, 음수 금액 |
검색 결과 없음은 입력 오류가 아니라 정상 실행 결과로 볼 수 있다.
검색 결과 없음
이 경우는 예외라기보다 “검색은 했지만 조건에 맞는 데이터가 없음”으로 처리한다.
5. create_agent
create_agent는 모델과 Tool을 연결해 Agent를 만드는 함수이다.
agent = create_agent(
model=model,
tools=[estimate_day_budget, search_destination],
system_prompt="너는 여행 도우미다. 필요한 경우에만 Tool을 사용한다."
)
Agent는 사용자 질문을 보고 다음을 판단한다.
Tool이 필요한가?
어떤 Tool이 필요한가?
Tool 인자는 무엇인가?
Tool 결과를 어떻게 최종 답변에 반영할 것인가?
예를 들어 질문이 다음과 같다면:
3명이 입장료 35000원, 식비 20000원씩 쓰면 얼마야?
Agent는 estimate_day_budget Tool을 선택하고 다음 인자를 만들 수 있다.
{
"travelers": 3,
"ticket_price": 35000,
"meal_budget": 20000
}
6. Tool Calling 메시지 흐름
Agent 실행 결과는 단순 문자열 하나가 아니라 여러 메시지 흐름으로 구성된다.
HumanMessage
-> AIMessage with tool_calls
-> ToolMessage
-> AIMessage final answer
각 메시지의 의미는 다음과 같다.
메시지의미
| HumanMessage | 사용자 질문 |
| AIMessage with tool_calls | 모델이 Tool 실행을 요청한 메시지 |
| ToolMessage | Tool 실행 결과 |
| AIMessage final | Tool 결과를 반영한 최종 답변 |
즉, Agent는 바로 답변하지 않고 중간에 Tool을 호출할 수 있다.
질문
-> Action: Tool 호출
-> Observation: Tool 결과
-> Final Answer: 최종 답변
7. ReAct 구조
ReAct는 Reasoning + Acting의 흐름을 가진 Agent 패턴이다.
실행 흐름은 다음과 같다.
Action 1
-> Observation 1
-> Action 2
-> Observation 2
-> Final Answer
예를 들어 장소 검색에서 처음에는 strict 모드로 정확히 검색한다.
Action 1: strict 검색
Observation 1: NOT_FOUND
검색 결과가 없으면 Tool 결과의 retry_hint를 보고 broad 모드로 다시 검색한다.
Action 2: broad 검색
Observation 2: FOUND
그리고 성공한 결과를 근거로 최종 답변한다.
Final Answer
ReAct의 핵심은 Tool 결과를 보고 다음 행동을 바꿀 수 있다는 점이다.
단순히 한 번 Tool을 호출하고 끝나는 구조보다 더 유연하다.
8. ToolNode와 tools_condition
LangGraph에서는 Agent와 Tool 실행을 Graph로 구성할 수 있다.
builder.add_node("agent", call_model)
builder.add_node("tools", ToolNode(tools))
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")
흐름은 다음과 같다.
START
-> agent
-> tool_calls가 있으면 tools
-> Tool 실행 후 다시 agent
-> tool_calls가 없으면 END
tools_condition은 마지막 AIMessage에 Tool 호출이 있는지 확인한다.
- Tool 호출이 있으면 tools 노드로 이동
- Tool 호출이 없으면 종료
즉, Agent가 더 이상 Tool이 필요 없다고 판단할 때까지 agent -> tools -> agent 흐름을 반복할 수 있다.
9. 정보 부족 시 Tool 호출하지 않기
Agent가 항상 Tool을 호출하면 안 된다.
예산 계산에는 최소한 인원, 입장료, 식비가 필요하다.
3명 여행 예산을 계산해줘
이 질문에는 인원은 있지만 입장료와 식비가 없다.
이때 모델이 값을 추측해서 Tool을 호출하면 잘못된 결과가 나온다.
올바른 흐름은 다음과 같다.
정보 부족 확인
-> Tool 호출하지 않음
-> 사용자에게 필요한 값 질문
Agent를 만들 때 system prompt에 이런 규칙을 명확히 넣어야 한다.
인원, 입장료, 식비 중 하나라도 없으면 값을 추측하지 말고 사용자에게 질문한다.
10. Agent Memory
Agent Memory는 이전 대화를 기억해 다음 Tool 호출에 활용하는 구조이다.
이번 실습에서는 InMemorySaver를 checkpointer로 사용했다.
create_agent(
model=ChatOpenAI(...),
tools=[recommend_destination],
checkpointer=InMemorySaver(),
)
그리고 thread_id로 대화 세션을 구분했다.
config = {
"configurable": {
"thread_id": "student-1"
}
}
같은 thread_id로 대화를 이어가면 이전 메시지가 checkpoint에 저장되어 다음 요청에서 다시 사용된다.
1. 내 이름은 수진이고 아이와 여행해. 전체 예산은 20만원이야.
2. 내 조건에 맞는 장소를 추천해줘.
두 번째 질문에는 이름, 동행인, 예산이 직접 들어있지 않다.
하지만 같은 thread에 이전 대화가 저장되어 있으면 Agent가 그 정보를 찾아 Tool 인자로 만들 수 있다.
11. thread_id로 기억 분리
Memory에서 중요한 점은 사용자별 또는 세션별 기억이 섞이면 안 된다는 것이다.
student-1 thread
-> 수진, 아이, 예산 20만원
student-2 thread
-> 이전 정보 없음
student-1에서 장소 추천을 요청하면 이전 정보를 사용할 수 있다.
하지만 student-2에서 같은 질문을 하면 이전 정보가 없으므로 Tool을 호출하지 않고 추가 질문을 해야 한다.
즉, thread_id는 대화 기억을 분리하는 기준이다.
12. 수정된 정보는 최신 값 사용
사용자가 이전 정보를 바꿀 수도 있다.
전체 예산을 15만원으로 변경해줘
이후 추천을 요청하면 Agent는 예전 예산 20만원이 아니라 최신 예산 15만원을 사용해야 한다.
이전 값: 20만원
수정 값: 15만원
Tool 인자: max_budget = 150000
Agent Memory는 단순히 과거를 저장하는 것뿐만 아니라, 대화 안에서 가장 최근 정보를 기준으로 판단해야 한다.
13. 핵심 정리
- Workflow는 개발자가 실행 순서를 정하는 구조이다.
- Agent는 모델이 질문과 Tool Schema를 보고 필요한 Tool을 선택하는 구조이다.
- Tool Contract는 Tool 이름, 설명, 입력 Schema로 구성된다.
- 일반 함수와 달리 LangChain Tool은 모델에게 사용 방법을 설명할 수 있다.
- Schema 검증은 함수 실행 전 입력 구조를 확인한다.
- 업무 규칙 검증은 함수 내부에서 값의 의미를 확인한다.
- create_agent는 모델과 Tool을 연결해 Agent를 만든다.
- Tool Calling은 AIMessage -> ToolMessage -> Final Answer 흐름으로 진행된다.
- ReAct는 Action과 Observation을 반복하며 다음 행동을 조정하는 구조이다.
- Tool 결과가 NOT_FOUND이면 Agent는 다른 방식으로 재검색할 수 있다.
- 정보가 부족하면 Tool을 호출하지 않고 사용자에게 추가 질문해야 한다.
- LangGraph의 ToolNode는 실제 Tool 실행을 담당한다.
- tools_condition은 Tool 호출 여부에 따라 다음 노드를 결정한다.
- Checkpointer는 같은 thread_id의 대화 메시지를 이어준다.
- Agent Memory는 이전 대화를 바탕으로 Tool 인자를 생성할 수 있다.
- 다른 thread_id끼리는 기억이 섞이면 안 된다.
- 사용자가 정보를 수정하면 Agent는 가장 최근 값을 기준으로 판단해야 한다.
'TIL > [TIL]' 카테고리의 다른 글
| [TIL]LangSmith로 Workflow 추적과 평가하기 (0) | 2026.07.31 |
|---|---|
| [TIL]Agent Safety와 Plan-Execute-Replan Workflow (0) | 2026.07.30 |
| [TIL]FastAPI 파일 브릿지와 LangGraph 기초 (0) | 2026.07.28 |
| [TIL]FastAPI REST API, Router 분리, File Upload, Spring 연동 (0) | 2026.07.27 |
| [TIL]Chroma VectorStore 기반 RAG와 FastAPI 기초 (0) | 2026.07.24 |