DB 조회, 파일 저장, 메일 발송. LLM 혼자선 못 한다.
툴은 그저 평범한 파이썬 함수.
다섯 개를 하나씩 직접 호출해본다.
왼쪽은 '글을 생성하고 추론하는 일'.
오른쪽은 '외부 세계와 상호작용하는 일'. 후자는 함수가 필요하다.
execute_sql
SQL → list[dict]
retrieve_from_rag
자연어 → 유사 문서
get_confluence_page
page_id → HTML
save_excel
dict → row 추가
send_email
HTML → 메일
전부 tools.py 한 파일에 정의. 각 함수는 그냥 파이썬 함수일 뿐 — LLM이 안 부른다고 해서 특별할 것이 없다.
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;")
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])]
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이 마크업을 직접 파싱하므로 텍스트 변환 불필요.
평가 기준표처럼 표 구조가 있는 페이지에 특히 유용.
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)
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은 SQL을 생성할 줄 알지만 직접 DB에 못 간다.
우리는 그 SQL을 실행해서 결과를 다시 LLM에게 넘긴다.
— Chapter 7부터는 이 연결을 LLM이 스스로 하게 된다.
# 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 의 본질.
개별 툴이 깔끔히 붙으면
그 다음은 '몇 개를 어떤 순서로 엮느냐'.