LLM 엔지니어링18장 · Project 03. 내 서버에서 일하는 AI Agent4 / 13← 이전목차다음 →MetaContents

18장. Project 03. 내 서버에서 일하는 AI Agent

답하는 AI 에서 일하는 AI 로

도서관 사서는 책이 어디 있는지 알려 줍니다. 비서는 책을 빌려 와 책상에 올려 두고, 반납일을 달력에 적습니다. 일하는 AI 는 비서에 가깝습니다. 말로 답하는 데서 그치지 않고 시스템의 상태를 바꿉니다. 그래서 무엇을 할 수 있게 둘지부터 정해야 합니다.

읽기 도구와 쓰기 도구를 견준 비교 그림

왼쪽 카드의 읽기 도구는 몇 번을 불러도 시스템이 그대로라, 에이전트가 마음껏 부르게 둡니다. 오른쪽 카드의 쓰기 도구는 부를 때마다 무언가가 바뀌므로 조건을 코드로 검사하고 사람의 확인을 거칩니다. 여기서 사서의 안내가 읽기 도구이고, 책을 빌려 오는 일이 쓰기 도구입니다.

답하는 AI 와 일하는 AI

견줄 것답하는 AI(RAG 챗봇)일하는 AI(에이전트)
결과글로 된 답답과 바뀐 시스템(신청, 접수)
틀렸을 때틀린 말틀린 신청, 남의 정보 노출
채점답에 정답 낱말이 있나끝 상태가 기대와 같은가
막을 것지어낸 답권한 밖의 일, 확인 없는 쓰기

도구에 읽기·쓰기 표시 달기

Python
TOOLS = {
    "search_rules": {"쓰기": False, "설명": "사내 규정에서 관련 조항을 찾는다"},
    "get_my_leave": {"쓰기": False, "설명": "내 남은 연차를 본다"},
    "request_leave": {"쓰기": True, "설명": "휴가를 신청한다(팀장 결재 대기)"},
}


def allowed(name, call_count):
    """없는 도구는 막고, 쓰기 도구는 요청 하나에 한 번만 부르게 한다"""
    if name not in TOOLS:
        return False, "없는 도구"
    if TOOLS[name]["쓰기"] and call_count.get(name, 0) >= 1:
        return False, "쓰기 도구는 요청 하나에 한 번만"
    return True, "통과"


count = {}
for name in ["search_rules", "request_leave", "request_leave", "delete_all"]:
    ok, why = allowed(name, count)
    count[name] = count.get(name, 0) + ok
    print(f"{name:14s} {ok} ({why})")

search_rules 와 첫 request_leave 는 통과하고, 두 번째 request_leave 와 목록에 없는 delete_all 은 막힙니다. LLM 이 무엇을 부르든, 실제로 실행되는 것은 목록에 있는 도구뿐입니다.

코드에 나오는 함수

코드쉬운 뜻
TOOLS = {이름: {"쓰기": …, "설명": …}}도구마다 쓰기 도구인지와 설명을 적은 허용 목록이다. 여기에 없는 이름은 도구로 치지 않는다
name not in TOOLS그 이름이 허용 목록에 없으면 참이다. 없는 도구를 막는다
call_count.get(name, 0)사전에서 그 도구를 부른 횟수를 꺼낸다. 아직 한 번도 안 불렀으면 0 으로 본다
return False, "없는 도구"두 값을 함께 돌려준다. 앞은 통과 여부, 뒤는 까닭이다
count.get(name, 0) + ok통과했으면(True) 1 을, 막혔으면(False) 0 을 더한다. 실제로 실행된 횟수만 센다
f"{name:14s}"이름을 14칸짜리 글자(s = string, 문자열)로 맞춰 줄을 가지런히 한다

이 코드가 뜻하는 것

회사 비품실을 떠올려 봅니다. 볼펜과 메모지는 몇 번을 가져가도 됩니다. 노트북 대여는 한 사람이 한 번에 한 대만 됩니다. 목록에 없는 물건을 달라고 하면 "그런 물건은 없다"고 답합니다. 여기서 비품 목록이 TOOLS, 볼펜이 읽기 도구, 노트북이 쓰기 도구, 대여 장부가 count 입니다.

줄하는 일쉽게 말하면
TOOLS = {…}도구 셋과 쓰기 여부를 적는다비품 목록에 "대여 물품" 표시를 단다
if name not in TOOLS목록에 없는 이름을 막는다없는 물건은 내주지 않는다
if TOOLS[name]["쓰기"] and call_count.get(name, 0) >= 1쓰기 도구를 이미 한 번 불렀으면 막는다노트북을 이미 빌렸으면 더 못 빌린다
for name in [...]네 번의 호출을 차례로 시험한다한 사람이 네 가지를 차례로 달라고 한다
count[name] = count.get(name, 0) + ok통과한 호출만 센다실제로 내준 것만 장부에 적는다

결과의 넷째 줄 delete_all False (없는 도구) 는, LLM 이 그럴듯한 이름을 지어내도 목록 밖이면 아무 일도 일어나지 않는다는 뜻입니다. 비유와 다른 점은, 볼펜은 가져가면 줄어들지만 읽기 도구는 몇 번을 불러도 시스템이 그대로라는 것입니다.

작게 줄여서 눈으로 보기

count.get(name, 0) + ok 에서 참·거짓을 숫자에 더하는 부분만 떼어 봅니다.

Python
count = {}
print(count.get("search_rules", 0))      # 아직 없는 열쇠는 0 으로 본다
print(0 + True, 0 + False)               # True 는 1, False 는 0 으로 더해진다
count["request_leave"] = count.get("request_leave", 0) + True
print(count)
출력
0
1 0
{'request_leave': 1}
결과읽는 법
0빈 사전에서 꺼내도 오류 없이 기본값 0 이 나온다
1 0파이썬은 True 를 1, False 를 0 으로 셈한다
{'request_leave': 1}통과한 쓰기 도구가 장부에 1번으로 적혔다. 다음 호출부터는 >= 1 에 걸려 막힌다

주의할 점

  • 도구 이름으로 함수를 찾을 때 getattr(객체, 이름) 만 쓰면, LLM 이 지어낸 이름으로 엉뚱한 메서드(__init__ 등)를 부를 수 있습니다. 허용 목록을 먼저 검사합니다.
  • 쓰기 도구는 처음부터 많이 만들지 않습니다. 읽기 도구만으로 시작해 쓰임새를 본 뒤 하나씩 더합니다.
← 이전 절18장 · 온프레미스 LLM 시스템 End-to-End다음 절 →18장 · 권한은 코드가 지킨다