LLM 앱 개발 실전 #10 AI 에이전트 만들기
6편에서 Claude가 도구를 한 번 부르고 우리가 결과를 돌려주는 흐름을 봤습니다. 에이전트는 이걸 한 단계 끌어올린 것입니다. Claude가 목표를 받고, 스스로 어떤 도구를 어떤 순서로 쓸지 판단하며, 여러 단계를 밟아 일을 끝까지 처리합니다. 이번 글에서는 그 에이전트를 만듭니다.
에이전트란 #
지금까지는 우리가 흐름을 정했습니다. “검색하고, 그 결과로 답해라"처럼 순서가 코드에 박혀 있었습니다. 에이전트는 다릅니다. 우리는 도구들과 목표만 주고, 무엇을 먼저 할지, 다음에 무엇을 할지는 Claude가 정합니다. 도구를 쓰고, 결과를 보고, 다음 행동을 정하고, 다시 도구를 쓰는 일을 목표를 이룰 때까지 반복합니다.
이 반복을 에이전트 루프라고 합니다. 사실 6편의 도구 실행 루프와 구조가 같습니다. 다른 점은 도구가 여러 개이고, 단계가 여러 번이며, 순서를 Claude가 정한다는 것입니다.
여러 도구를 주기 #
에이전트에게는 보통 도구를 여러 개 줍니다. 예를 들어 문서 검색과 계산 도구를 함께 주면, Claude는 질문에 따라 둘을 알아서 조합합니다.
tools = [
{
"name": "search_docs",
"description": "사내 문서에서 질문과 관련된 내용을 검색한다.",
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
{
"name": "calculate",
"description": "수식을 계산한다. 예: '1200 * 0.1'",
"input_schema": {
"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"],
},
},
]
def run_tool(name: str, args: dict) -> str:
if name == "search_docs":
return search_docs(args["query"]) # 8편의 검색을 재사용
if name == "calculate":
return str(eval(args["expression"])) # 예시. 실제로는 안전한 계산기를 쓴다
return "알 수 없는 도구입니다."“환불 수수료가 얼마야?“라고 물으면 Claude는 먼저 search_docs로 수수료 정책을 찾고, 거기에 계산이 필요하면 calculate를 이어서 부릅니다. 이 순서를 우리가 짜 주지 않아도 Claude가 정합니다.
에이전트 루프 #
루프 자체는 6편과 같되, 안전을 위해 최대 반복 횟수를 둡니다. 에이전트가 도구를 끝없이 부르며 멈추지 않는 상황을 막기 위해서입니다.
import anthropic
client = anthropic.Anthropic()
def run_agent(goal: str, max_steps: int = 10) -> str:
messages = [{"role": "user", "content": goal}]
for _ in range(max_steps):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
return next(b.text for b in response.content if b.type == "text")
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = run_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "user", "content": tool_results})
return "최대 단계 수에 도달했습니다."
print(run_agent("환불 수수료 정책을 찾고, 5만 원 주문의 수수료를 계산해줘."))Claude가 도구를 그만 부르고 end_turn으로 답을 마치면 결과를 돌려줍니다. 그렇지 않으면 도구를 실행하고 결과를 다시 넣어 루프를 계속합니다. max_steps는 안전장치입니다. 어떤 이유로든 에이전트가 끝맺지 못하면 정해진 횟수에서 멈춥니다.
에이전트가 잘 맞는 일 #
에이전트가 항상 정답은 아닙니다. 단계를 미리 다 알 수 있는 일이라면, 코드로 순서를 짜는 편이 더 빠르고 예측 가능합니다. 에이전트는 미리 순서를 정하기 어려운 일에 잘 맞습니다. 질문에 따라 필요한 도구와 단계가 달라지고, 중간 결과를 보고 다음을 정해야 하는 경우입니다.
비용과 위험도 함께 봅니다. 에이전트는 여러 번 호출하므로 단일 호출보다 비싸고 느립니다. 또 잘못된 도구를 부르거나 잘못된 판단을 할 수 있으므로, 되돌리기 어려운 작업(결제, 삭제 등)에는 사람의 확인을 끼워 넣는 것이 안전합니다.
흔히 걸려 넘어지는 곳 #
- 종료 조건이 없다 —
max_steps같은 상한이 없으면, 에이전트가 같은 도구를 반복하며 멈추지 않아 비용이 폭주할 수 있습니다. 반드시 상한을 둡니다. - 위험한 도구를 그냥 맡긴다 —
eval처럼 위험한 실행이나 결제,삭제 같은 도구는 그대로 자동 실행하면 안 됩니다. 입력을 검증하고, 필요하면 실행 전 사람의 승인을 받습니다. - 도구 설명이 겹친다 — 도구 여러 개의 설명이 비슷하면 Claude가 어느 걸 쓸지 헷갈립니다. 각 도구의 쓰임을 분명히 구분해 적습니다.
마무리 #
이번 글에서는 Claude가 스스로 도구를 골라 여러 단계를 처리하는 에이전트를 만들었습니다.
- 에이전트는 도구와 목표만 주면, 순서를 스스로 정해 목표를 이룰 때까지 도구 사용을 반복합니다.
- 루프는 6편과 같되, 안전을 위해 최대 반복 횟수를 둡니다.
- 미리 순서를 정하기 어려운 일에 잘 맞고, 비싸고 위험할 수 있으므로 종료 조건과 사람의 확인을 둡니다.
지금은 도구를 우리가 직접 정의해 연결했습니다. 다음 글인 “LLM 앱 개발 실전 #11 MCP로 도구 연결하기"에서는 도구를 연결하는 표준인 MCP를 다루겠습니다. 매번 도구를 손으로 짜는 대신, 이미 만들어진 도구 서버에 Claude를 연결하는 방법입니다.