← Hub ← 04 06 →
05
Chapter 05 · Tools

LLM에게
손과 발
달아준다.

DB 조회, 파일 저장, 메일 발송. LLM 혼자선 못 한다.
툴은 그저 평범한 파이썬 함수.
다섯 개를 하나씩 직접 호출해본다.

·
왜 툴이 필요한가

LLM이 할 수 있는 것 vs
없는 것.

✓ 스스로 가능
— 툴이 필요
이력서 요약
DB에서 이력서 가져오기
평가·채점
RAG로 이력서 검색
지원자 비교·선정
엑셀 파일에 저장
보고서 작성
메일 발송

왼쪽은 '글을 생성하고 추론하는 일'.
오른쪽은 '외부 세계와 상호작용하는 일'. 후자는 함수가 필요하다.

·
강의에서 쓰는 다섯 툴

손 다섯, 각자 역할.

Tool 1

DB

execute_sql

SQL → list[dict]

Tool 2

RAG

retrieve_from_rag

자연어 → 유사 문서

Tool 3

위키

get_confluence_page

page_id → HTML

Tool 4

Excel

save_excel

dict → row 추가

Tool 5

Email

send_email

HTML → 메일

전부 tools.py 한 파일에 정의. 각 함수는 그냥 파이썬 함수일 뿐 — LLM이 안 부른다고 해서 특별할 것이 없다.

·
Tool 1/5 · DB

SQL 한 줄로
조건이 명확한
데이터를 꺼낸다.

id, 부서명처럼 조건이 분명할 때.
반환은 항상 list[dict] — LLM에게 넘기기 가장 쉬운 모양.

import sqlite3, pandas as pd

def execute_sql(sql_query, db_path=DB_PATH):
    with sqlite3.connect(db_path) as conn:
        data = pd.read_sql_query(sql_query, conn)
        return data.to_dict(orient="records")

# 호출 예
execute_sql("SELECT * FROM job_posting;")
·
Tool 2/5 · RAG

조건이 모호할 땐
의미로 찾는다.

"머신러닝 경험자" natural query Embedding API text → vector Chroma DB vector search top-5 청크 resume_id
def retrieve_from_rag(query, n_results=5, collection_name="resume"):
    query_embedding = embed(query)              # 텍스트 → 벡터
    results = collection.query(
        query_embeddings=[query_embedding],
        n_results=n_results,
        include=["documents", "metadatas"],
    )
    return [{"resume_id": m["doc_id"], "title": m["title"], "content": d}
            for d, m in zip(results["documents"][0], results["metadatas"][0])]
·
Tool 3/5 · 위키

사내 위키 페이지를
page_id로 꺼낸다.

def get_confluence_page_content(page_id):
    url    = f"{WIKI_BASE}/rest/api/content/{page_id}"
    params = {"expand": "body.view"}
    auth   = (os.getenv("user_id"), os.getenv("user_pw"))

    response = requests.get(url, params=params, auth=auth, verify=False)
    body     = response.json()["body"]["view"]["value"]
    return {"content": body}

반환은 HTML 그대로. LLM이 마크업을 직접 파싱하므로 텍스트 변환 불필요.
평가 기준표처럼 표 구조가 있는 페이지에 특히 유용.

·
Tool 4·5/5 · Excel & Email

마지막은
저장발송.

Tool 4 · 누적 저장

save_excel(data)

def save_excel(data, path=EXCEL_PATH):
    df_new = pd.DataFrame([data])
    df_new["저장일시"] = now()
    if os.path.exists(path):
        df = pd.concat([read(), df_new])
    df.to_excel(path)

Tool 5 · SSO 메일

send_email(content)

def send_email(content):
    res = requests.post(
        f"{MAIL_API}/auth/mail/prepare",
        json={"content": content},
    )
    request_id = res.json()["request_id"]
    webbrowser.open(
        f"{LOGIN_URL}?request_id={request_id}"
    )
·
LLM + Tool

LLM → 툴 → LLM.
샌드위치 패턴.

사용자 "이력서 몇 건?" LLM #1 SQL 생성 execute_sql 실제 조회 LLM #2 결과 해석 "12건"

LLM은 SQL을 생성할 줄 알지만 직접 DB에 못 간다.
우리는 그 SQL을 실행해서 결과를 다시 LLM에게 넘긴다.
— Chapter 7부터는 이 연결을 LLM이 스스로 하게 된다.

·
Code · 샌드위치를 코드로

시스템 프롬프트 2개,
호출 2번.

# 1단계: SQL 생성
sql = generate_response(
    system_prompt=sql_generator_system_prompt,   # "SELECT만 생성"
    user_prompt=f"{user_question}\n[테이블 정보]{table_info}",
)

# 2단계: 툴 실행
result = execute_sql(sql)

# 3단계: 결과 해석
answer = generate_response(
    system_prompt=sqlite_result_system_prompt,   # "한국어로 요약"
    user_prompt=f"{user_question}\n[조회 결과]{result}",
)

세 줄짜리 함수 호출. 하지만 이 패턴이 곧 tool augmentation 의 본질.

Chapter 05 · 마무리

툴이 LLM과
잘 붙는 게
Agent의 시작.

개별 툴이 깔끔히 붙으면
그 다음은 '몇 개를 어떤 순서로 엮느냐'.

Next · Chapter 06
고정 워크플로우