24장. 행동하는 언어 — 에이전트
이 장의 질문: 말만 하던 모델에게 어떻게 손을 주고, 목표를 향해 여러 단계를 스스로 수행하게 하는가? 도구 사용의 원리, 에이전트 루프의 실체, 그리고 자율성이 커질수록 왜 안전장치가 본질이 되는지를 이해한다.
전제: 21장(LLM의 한계), 23장(프롬프트, RAG).
텍스트로 세계를 움직이기
LLM은 텍스트만 출력한다. 그런데 텍스트가 명령이 될 수 있다면? 모델이 {"tool": "calculator", "input": "1249 * 37"}이라고 출력하고, 우리 코드가 그것을 읽어 실제 계산기를 돌린 뒤 결과를 다시 모델에게 텍스트로 돌려주면 — 모델은 산수를 못해도 정확한 답을 얻는다. 이것이 도구 사용(tool use)이다.
원리는 세 조각이다. ① 모델에게 쓸 수 있는 도구들의 명세(이름, 설명, 입력 형식)를 프롬프트에 알려 준다. ② 모델이 도구를 쓰기로 결정하면 정해진 형식으로 호출을 출력한다 — 현대 모델은 22장의 정렬 과정에서 이 형식을 훈련받아, 구조화된 호출을 안정적으로 낸다. ③ 우리 코드가 호출을 실행하고 결과를 대화에 추가한다. 모델은 결과를 읽고 다음 행동을 정한다.
이것이 21장의 한계 목록에 대한 가장 강력한 처방임을 보라. 지식 마감일 → 검색 도구. 산수·논리의 취약함 → 계산기, 코드 실행기. 환각 → 데이터베이스 조회로 확인. 모델의 약점을 모델 안에서 고치는 대신, 약점에 해당하는 일을 정확한 외부 시스템에 위임하는 것이다. 사람이 암산 대신 계산기를 쓰는 것과 정확히 같은 지혜다. (23장의 RAG도 이 관점에서는 "항상 실행되는 검색 도구"다 — 에이전트에서는 모델이 필요할 때만 검색을 부른다.)
에이전트 = 반복문 하나
도구 사용을 반복시키면 에이전트가 된다. 실체는 이 정도로 단순하다.
messages = [{"role": "user", "content": task}]
for step in range(MAX_STEPS): # 안전장치 1: 반복 상한
response = llm(messages, tools=TOOLS)
messages.append(response) # 모델의 응답(도구 호출 포함)을 기록
if response.wants_no_tool():
return response.text # 도구가 더 필요 없으면 끝
for call in response.tool_calls:
result = execute(call) # 우리 코드가 실행 (실패해도 결과로 돌려준다)
messages.append(tool_result(call.id, result))모델이 "목록 확인 → 파일 읽기 → 계산 → 답변"을 스스로 순서 짓고, 중간 결과를 보고 계획을 수정하며, 실패하면 다른 방법을 시도한다. 이것이 에이전트의 전부다 — 관찰하고, 생각하고, 행동하고, 결과를 관찰하는 루프. 도구의 실패를 숨기지 않고 오류 메시지를 그대로 돌려주는 것이 중요하다 — 모델은 그것을 읽고 스스로 복구한다. 이 자기 복구가 정해진 스크립트와 에이전트의 결정적 차이다.
이 루프 위에 얹는 설계 요소들이 에이전트 공학이다.
- 도구 설명이 절반이다. 모델이 도구를 잘못 고르는 원인의 대부분은 명세의 설명 부족이다. "무엇을 하는가"뿐 아니라 "언제 써야 하는가"까지 적는다.
- 계획을 말하게 하라. 행동 전에 "지금 무엇을 왜 하려는지"를 한 줄 쓰게 하면(추론과 행동의 교대) 도구 선택이 정확해지고, 로그가 곧 디버깅 자료가 된다.
- 기억을 관리하라. 긴 작업에서는 도구 결과가 문맥 창을 삼킨다. 긴 출력은 다듬어 넣고, 오래된 결과는 요약하거나 지우며, 진짜 장기 기억은 파일이나 데이터베이스(=또 하나의 도구)에 둔다.
- 분업시켜라. 조사하는 에이전트, 쓰는 에이전트, 검토하는 에이전트를 나누고 조율자가 넘겨주는 다중 에이전트 구조는, 각 역할의 프롬프트와 도구를 단순하게 유지해 준다 — 단, 복잡도와 비용도 곱으로 늘어나니 단일 에이전트로 안 될 때만.
자율성의 대가: 안전이 본질이다
여기서 이 장의 무게중심이 바뀐다. 텍스트만 내놓는 모델의 실수는 틀린 문장이지만, 도구를 쥔 에이전트의 실수는 삭제된 파일, 잘못 보낸 이메일, 결제된 주문이다. 21장의 모든 한계 — 환각, 취약한 추론 — 가 이제 행동으로 실체화된다. 그리고 새 위협이 더해진다: 에이전트가 읽는 웹페이지나 문서에 "이 지시를 따르라"는 문장이 숨어 있으면, 모델이 그것을 명령으로 오인할 수 있다(23장의 프롬프트 주입이 도구 결과를 타고 들어온다).
그래서 에이전트 설계의 절반은 능력의 제한이다.
- 최소 권한. 필요한 도구만 준다. 읽기 도구와 쓰기 도구를 나누고, 쓰기는 정말 필요할 때만.
- 샌드박스. 파일 접근은 지정 폴더 안으로, 코드 실행은 격리 환경에서, 네트워크는 허용 목록으로. 명령을 문자열로 받아 그대로 실행하는 도구는 만들지 않는다.
- 되돌리기 어려운 행동에는 사람의 승인. 삭제·송금·발송·배포 전에 멈추고 확인받는다. 자동화의 이득보다 사고의 비용이 큰 지점을 찾는 것이 설계자의 일이다.
- 상한과 로그. 반복 횟수·비용·시간의 상한, 그리고 모든 행동의 기록. 무한 루프와 폭주는 실제로 일어난다.
- 결과를 데이터로 취급. 도구가 돌려준 텍스트 속의 "지시"는 지시가 아니다. 중요한 결정은 모델의 말이 아니라 코드의 검증을 거친다.
이 원칙들은 에이전트를 약하게 만드는 것이 아니라 쓸 수 있게 만든다. 브레이크 없는 차가 빠른 차가 아니듯.
그림을 보는 눈: 멀티모달
이 장을 닫기 전에, 18장에서 예고한 확장을 짚자. Transformer는 벡터의 나열이면 무엇이든 받는다. 이미지를 패치 벡터로 바꿔(14장 ViT의 방식) 텍스트 토큰들과 같은 줄에 이어 넣으면, 모델은 그림을 "읽고" 그것에 대해 말한다 — 비전-언어 모델이다. 실제 레시피는 놀랍도록 검소하다: 사전학습된 이미지 인코더(13장의 전이학습)와 사전학습된 LLM 사이에 작은 연결층 하나를 두고, (이미지, 질문, 답변) 데이터로 연결층과 LLM을 미세조정한다. 오디오도 같은 문법으로 붙는다. 에이전트에게 이것은 새 감각 기관이다 — 화면을 보고 클릭하고, 도표를 읽고, 사진 속 문제를 푼다. 이미지와 텍스트를 같은 공간에 정렬하는 원리(대조 학습)와 그 응용은 코드랩 18에서 직접 다룬다.
에이전트 자체는 코드랩 13에서 프레임워크 없이 위의 반복문 그대로 짓는다 — 도구 명세, 루프, 샌드박스, 승인 게이트까지. 프레임워크가 하는 일을 한 번 손으로 짜 본 사람만이 프레임워크를 이해하고 쓴다.
핵심 요약
- 도구 사용: 모델의 텍스트 출력을 구조화된 호출로 만들고 우리 코드가 실행해 결과를 돌려준다 — 모델의 약점(지식·산수·검증)을 정확한 외부 시스템에 위임하는 지혜.
- 에이전트 = 도구 사용의 반복문. 관찰-생각-행동-관찰 루프와 자기 복구가 본질이며, 도구 설명·계획 서술·기억 관리·분업이 그 위의 공학이다.
- 자율성은 실수를 행동으로 만든다. 최소 권한, 샌드박스, 사람의 승인, 상한과 로그, 결과를 데이터로 취급 — 안전장치는 부속이 아니라 에이전트 설계의 절반이다.
- 이미지·오디오를 토큰으로 이어 넣으면 멀티모달 — Transformer의 범용 인터페이스가 에이전트에게 감각을 준다.
스스로 점검
- "LLM이 산수에 약하다"는 문제를 모델 개선 없이 해결하는 방법을 설명하고, 이것이 21장의 어떤 한계에 대한 처방인지 말해 보라.
- 본문의 루프에서
MAX_STEPS를 빼면 어떤 위험이 생기는가? 도구 실패를 예외로 중단시키는 대신 결과로 돌려주는 이유는? - 웹을 읽는 에이전트에 대한 프롬프트 주입 공격 시나리오를 하나 만들고, 방어 원칙 세 가지를 대응시켜 보라.
- "삭제·송금 전 사람 승인" 규칙은 자동화의 가치를 깎는가? 어떤 기준으로 승인 지점을 정하겠는가?
다음 장에서
7부가 끝났다 — 7부 복습 퀴즈로 점검하라. 22장에서 "정답 대신 보상으로 배운다"는 강화학습을 빌려 썼는데, 그 원리는 아직 설명하지 않았다. 8부에서 그 빚을 갚는다 — 게임을 정복하고 로봇을 걷게 하고 LLM을 길들인, 시행착오의 수학이다.