작동하는 AI 에이전트 만들기: 실용적인 용어집
AI 에이전트가 실제로 무엇인지, 챗봇과 어떻게 다른지, 그리고 작동하는 에이전트를 만들기 위해 필요한 컴포넌트들.
AI 에이전트는 목표를 받고, 일련의 행동을 결정한 후, 목표가 달성되거나 포기할 때까지 도구를 사용해 그 행동들을 실행하는 프로그램입니다. 이는 단지 메시지에 응답하는 챗봇과는 다릅니다. 에이전트는 계획하고, 함수를 호출하고, 결과를 확인하고, 조정합니다. 브라우저에서만 ChatGPT를 사용해본 적이 있다면, 첫 번째 에이전트를 만드는 것이 모델이 텍스트 생성기에서 더 큰 시스템의 컴포넌트로 변하는 지점입니다.
핵심 루프
모든 에이전트는 어떻게 마케팅되든 동일한 루프의 어떤 버전을 실행합니다: 관찰, 사고, 행동, 반복. 구체적으로:
- 에이전트가 작업을 받습니다("다음 달 리스본으로 가는 가장 저렴한 항공편을 찾고 이메일 요약을 작성해").
- 이용 가능한 도구의 설명과 함께 작업을 LLM에 전달합니다.
- 모델은 최종 답변이나
search_flights(destination="Lisbon", month="2025-06")같은 도구 호출을 반환합니다. - 코드가 그 함수를 실행하고, 실제 결과를 얻은 후, 모델의 컨텍스트에 다시 입력합니다.
- 모델은 다음 할 일을 결정합니다: 다른 도구를 호출하거나, 명확함을 요청하거나, 종료합니다.
이 패턴은 보통 ReAct 루프(이유 제시 및 행동)라 불리며, LangChain의 에이전트 실행자, LlamaIndex 에이전트, OpenAI의 함수 호출 API 같은 프레임워크의 백본입니다. while 루프와 LLM API만으로 Python의 100줄 미만으로 전체를 직접 구현할 수 있습니다 — 프레임워크는 편의성을 더할 뿐, 마법을 더하지는 않습니다.
도구가 에이전트를 만드는 것
도구가 없는 모델은 말만 할 수 있습니다. 함수를 주면 행동할 수 있습니다. 도구는 보통 이름, 매개변수, 목적을 설명하는 스키마로 래핑된 평범한 Python 함수이므로 모델이 언제 어떻게 호출할지 알 수 있습니다. 예시:
def get_weather(city: str) -> str:
"""Return current weather for a given city."""
resp = requests.get(f"https://api.weather.example/v1/{city}")
return resp.json()["summary"]
OpenAI의 함수 호출을 사용하면, 이 함수와 함께 JSON 스키마를 전달하고, 모델은 자유 형식 텍스트 대신 {"name": "get_weather", "arguments": {"city": "Lisbon"}} 같은 구조화된 호출을 출력합니다. 코드가 그것을 파싱하고, 실제 함수를 실행하고, 결과를 대화의 새 메시지로 반환합니다. 모델이 답변할 충분한 정보를 가질 때까지 반복합니다.
메모리와 상태
단일 API 호출은 컨텍스트 윈도우 너머의 메모리가 없습니다. 에이전트는 명시적인 상태가 필요합니다: 실행 중인 메시지 목록(대화 기록)과 흔히 장기적인 사실을 위한 별도의 저장소입니다. 단기 작업의 경우, 대화를 나타내는 Python 딕셔너리 리스트로 충분합니다. 여러 세션에 걸쳐 무언가를 기억해야 하는 에이전트의 경우, 벡터 데이터베이스(Chroma, Pinecone, Qdrant)를 사용해 임베딩을 통해 관련된 과거 정보를 저장하고 검색합니다.
처음에 이것을 과도하게 설계하지 마십시오. 놀랍게도 많은 "에이전트" 프로젝트가 실패하는 이유는 LLM이 약해서가 아니라 상태 관리가 부실하기 때문입니다: 중복된 메시지, 제한 없는 컨텍스트 증가, 또는 어떤 도구 호출이 어떤 결과에 속하는지 추적 손실입니다.
최소 작동 예제
프레임워크 없이 OpenAI의 API를 사용한 기본 에이전트의 형태입니다:
messages = [{"role": "user", "content": "What's the weather in Lisbon?"}]
while True:
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=[weather_tool_schema],
)
msg = response.choices[0].message
if msg.tool_calls:
for call in msg.tool_calls:
result = get_weather(**json.loads(call.function.arguments))
messages.append(msg)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result,
})
else:
print(msg.content)
break
이것이 실제로 작동하는 에이전트입니다. 나머지 — 계획 전략, 다중 에이전트 조정, 검색 파이프라인 — 모두 이 루프 위에 구축됩니다.
실패하는 곳
에이전트는 예측 가능한 방식으로 실패합니다: 모델이 성공했는지 알 수 없을 때 무한 도구 호출 루프, 환각된 함수 인수, 비싼 도구를 다시 호출하는 것에서 비롯된 엄청난 비용입니다. while 루프에 하드 반복 제한(예: 10 단계)을 두고 개발 중에 모든 도구 호출을 기록해 루프를 막습니다. 실제 시스템에 영향을 주는 것을 실행하기 전에 인수를 검증합니다 — 이메일을 보내거나 셸 명령을 실행할 수 있는 에이전트는 사용자 대면 코드에 적용하는 것과 동일한 입력 살균이 필요합니다. LLM은 실질적으로 그 입력을 생성하는 신뢰할 수 없는 사용자이기 때문입니다.
다음으로 갈 곳
루프가 작동하면, 흥미로운 문제들이 시작됩니다: 단일 에이전트와 다중 에이전트 설계 중에서 선택하기, 얼마나 많은 자율성을 부여할지 결정하기, 출력이 비결정적일 때 에이전트를 신뢰성 있게 테스트하기입니다. 여기서부터 계속 구축하려면 Korra Studio의 AI 및 Python 트랙이 검색 증강 생성, 프롬프트 설계, 도구 호출 패턴을 더 깊이 있게 다룹니다.
AI 도움을 받아 작성했으며, Michal Pilch(CISSP), Korra Studio에서 검토 및 게시했어요.
이것은 Korra Studio 나레지베이스의 한 노트예요. 플랫폼은 모든 주제를 1-to-1 멘토링과 함께 제공해요.
무료로 시작하기arrow_forward