따라하기 — 설치 점검과 첫 LLM 호출
한 줄 요약
code/ch01_hello.py 를 만들어 실행합니다. 이 파일이 끝까지 돌아가면 환경 준비가 끝난 것이고, 동시에 LLM이 답하지 못하는 세 질문을 직접 눈으로 보게 됩니다.
1. 파일 만들기
VS Code 왼쪽 탐색기에서 code 폴더를 오른쪽 클릭 → New File(새 파일) → 이름을 정확히 입력합니다.
ch01_hello.py
아래 코드를 전부 복사해서 붙여 넣고 저장하세요 (Ctrl+S / Cmd+S).
2. 코드
새 파일을 만드세요 —
agentic_ai/code/ch01_hello.py
VS Code 왼쪽 탐색기에서code폴더를 오른쪽 클릭 → New File → 파일명ch01_hello.py입력 → 아래를 전부 복사해 붙여 넣고 저장(Ctrl+S/Cmd+S).
# -*- coding: utf-8 -*-
"""1장 — 설치 점검과 첫 LLM 호출
실행: python code/ch01_hello.py
이 파일이 끝까지 돌아가면 실습 환경이 완성된 것입니다.
마지막에 던지는 세 질문은 LLM 혼자서는 답하지 못합니다.
그 셋을 3장(사내 데이터) · 4장(최신 정보) · 6장(사내 문서)에서 하나씩 풀어 갑니다.
"""
import sys
import pathlib
# [왜 필요한가] 이 파일은 같은 폴더(code/)의 common.py 를 가져다 씁니다.
# 파이썬은 '어디서 실행했는지'에 따라 import 가 실패할 수 있어,
# 이 파일이 있는 폴더를 검색 경로에 직접 추가합니다.
sys.path.append(str(pathlib.Path(__file__).resolve().parent))
from common import get_genai_client, GEMINI_MODEL, DATA
def ask(client, question: str) -> str:
"""질문 하나를 보내고 답변 텍스트를 받는다.
generate_content 는 '한 번 묻고 한 번 답받는' 가장 기본 호출입니다.
- model : 어떤 모델을 쓸지
- contents : 무엇을 물을지
돌아온 응답에서 .text 로 답변 문자열만 꺼냅니다.
"""
resp = client.models.generate_content(model=GEMINI_MODEL, contents=question)
return (resp.text or "")
if __name__ == "__main__":
# ── 1. 환경 점검 ────────────────────────────────────────────
print("=" * 62)
print("[1] 환경 점검")
print(f" 데이터 폴더 : {DATA}")
print(f" 폴더 존재 : {DATA.exists()}")
client = get_genai_client() # .env 에 넣어 둔 API 키로 클라이언트 생성
print(f" 사용 모델 : {GEMINI_MODEL}")
print(" → 여기까지 오류가 없으면 설치 성공입니다.")
# ── 2. LLM 이 잘 답하는 질문 ────────────────────────────────
print("\n" + "=" * 62)
print("[2] LLM 이 답할 수 있는 질문 (일반 지식)")
q = "스마트워치를 고를 때 무엇을 봐야 하나요? 세 가지만 짧게 알려주세요."
print(f" Q: {q}")
print(f" A: {ask(client, q)}")
# ── 3. LLM 이 답하지 못하는 세 질문 ─────────────────────────
print("\n" + "=" * 62)
print("[3] LLM 이 답하지 못하는 질문 — 이 과정에서 하나씩 풀어 갑니다")
questions = [
("사내 데이터", "3장", "승승장구몰 주문 O001203은 지금 어떤 상태인가요?"),
("최신 정보", "4장", "요즘 스마트워치 시장에서 어떤 기능이 가장 주목받나요?"),
("사내 문서", "6장", "승승장구몰에서 불량 상품 반품은 며칠 이내에 신청해야 하나요?"),
]
for kind, chapter, q in questions:
print(f"\n ── [{kind}] {chapter}에 해결합니다")
print(f" Q: {q}")
answer = ask(client, q).strip().replace("\n", " ")
print(f" A: {answer[:150]}{' ...' if len(answer) > 150 else ''}")
print("\n" + "=" * 62)
print("관찰: 일반 지식은 잘 답하지만, 우리 회사 데이터·최신 정보·사내 문서는")
print(" 모른다고 하거나 그럴듯하게 지어냅니다. 이것이 도구가 필요한 이유입니다.")
3. 실행
터미널에서 (agentic) 표시를 확인한 뒤:
터미널에서 실행 — 프로젝트 폴더
agentic_ai에서,(agentic)표시를 확인한 뒤.
python code/ch01_hello.py
30초~1분 걸립니다. LLM을 네 번 부르는데, 출력이 마지막에 몰려 나오므로 화면이 멈춘 것처럼 보여도 기다리세요.
4. 예상 출력
답변 문장은 실행할 때마다 조금씩 달라집니다. LLM은 매번 똑같이 답하지 않기 때문입니다. 틀이 같으면 성공입니다.
==============================================================
[1] 환경 점검
데이터 폴더 : C:\Users\사용자명\Documents\agentic_ai\data
폴더 존재 : True
사용 모델 : gemini-2.5-flash
→ 여기까지 오류가 없으면 설치 성공입니다.
==============================================================
[2] LLM 이 답할 수 있는 질문 (일반 지식)
Q: 스마트워치를 고를 때 무엇을 봐야 하나요? 세 가지만 짧게 알려주세요.
A: 스마트워치를 고를 때 중요하게 봐야 할 세 가지는 다음과 같습니다:
1. **사용 스마트폰과의 호환성 (운영체제):** 아이폰 사용자라면 애플 워치가, 안드로이드
사용자라면 갤럭시 워치나 다른 제조사 제품이 가장 좋은 호환성을 제공합니다.
2. **주요 용도와 필요한 기능:** 운동 추적(GPS, 심박수), 알림 확인, 모바일 결제,
통화, 수면 분석 등 어떤 기능을 주로 사용할지 정하고 그에 맞는 제품을 선택하세요.
3. **배터리 지속 시간:** 매일 충전하는 것이 번거롭다면 며칠 이상 지속되는
배터리 수명을 가진 모델을 고려해야 합니다.
==============================================================
[3] LLM 이 답하지 못하는 질문 — 이 과정에서 하나씩 풀어 갑니다
── [사내 데이터] 3장에 해결합니다
Q: 승승장구몰 주문 O001203은 지금 어떤 상태인가요?
A: 죄송하지만 저는 승승장구몰의 내부 시스템에 직접 접근하여 특정 주문(O001203)의
실시간 상태를 확인할 수는 없습니다. 주문 상태를 확인하시려면 다음 방법들을 ...
── [최신 정보] 4장에 해결합니다
Q: 요즘 스마트워치 시장에서 어떤 기능이 가장 주목받나요?
A: 요즘 스마트워치 시장에서 가장 주목받는 기능들은 단순한 알림이나 기본적인 운동
추적을 넘어, **사용자의 건강과 안전, 그리고 삶의 질을 높이는 데 초점** ...
── [사내 문서] 6장에 해결합니다
Q: 승승장구몰에서 불량 상품 반품은 며칠 이내에 신청해야 하나요?
A: "승승장구몰"의 정확한 반품 규정은 해당 쇼핑몰의 웹사이트에 명시되어 있습니다.
하지만 **일반적으로 전자상거래법에 따르면, 불량 상품의 경우 다음과 같이 반품
신청이 가능합니다:** * **상품을 받은 날로부터 3개월 이내** * 또는 ...
==============================================================
관찰: 일반 지식은 잘 답하지만, 우리 회사 데이터·최신 정보·사내 문서는
모른다고 하거나 그럴듯하게 지어냅니다. 이것이 도구가 필요한 이유입니다.
5. 무슨 일이 일어났는지 뜯어보기
get_genai_client()
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
client = get_genai_client()
common.py 안에서 이런 일이 벌어집니다.
아래는 제공된 code/common.py 의 일부입니다 — 고치지 않아도 됩니다.
def get_genai_client():
require_key() # 키가 없으면 안내하고 멈춘다
from google import genai
return genai.Client(api_key=_key) # 키를 쥔 '연결 담당자'를 만든다
클라이언트(client) 는 구글 서버와 통신할 창구입니다. 한 번 만들어 두고 계속 재사용합니다.
generate_content()
아래는 code/ch01_hello.py 의 일부입니다 — 위에서 이미 붙여 넣었으니 다시 넣지 마세요.
resp = client.models.generate_content(model=GEMINI_MODEL, contents=question)
return (resp.text or "")
이 두 줄이 이 과정 전체의 출발점입니다. 나머지 7장은 전부 이 호출에 살을 붙이는 이야기입니다.
| 넣는 것 | 뜻 |
|---|---|
model |
어떤 모델을 쓸지 (gemini-2.5-flash) |
contents |
무엇을 물을지 (문자열 하나) |
받는 것은 resp 라는 응답 객체이고, 거기서 .text 로 답변 문자열만 꺼냅니다.
(resp.text or "")의or ""는 무엇인가요? 모델이 아무 글자도 만들지 않으면.text가None(값이 없음) 으로 올 수 있습니다. 안전 필터에 걸리거나 응답이 비었을 때가 그렇죠.
or ""는 파이썬에서 "앞이 비어 있으면 뒤의 값을 대신 쓴다" 는 뜻입니다. 이게 없으면None.strip()이 되어AttributeError: 'NoneType' object has no attribute 'strip'이 납니다. 원인을 짐작하기 어려운 오류라, 이 과정의 모든 실습 파일에 넣어 두었습니다.
resp.text말고 다른 것도 들어 있습니다. 토큰 사용량, 안전성 판정, 그리고 3장에서 중요해질 "모델이 부르려는 함수" 같은 것들입니다. 지금은.text만 씁니다.
if __name__ == "__main__":
붙여 넣은 코드 한가운데에 이 줄이 있습니다. 사전 지식으로 요구하지 않은 문법이라 여기서 한 번 짚습니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
if __name__ == "__main__":
print("이 아래는 이 파일을 직접 실행했을 때만 돌아갑니다")
이 줄 아래에 들여 쓴 부분은, 그 파일을 직접 실행했을 때만 돌아갑니다. 다른 파일이 이 파일을 import 해서 함수만 가져다 쓸 때는 실행되지 않습니다.
왜 알아야 하나요? 8장에서 3장 파일의 함수를 그대로 가져다 씁니다. 그때 3장 파일의
[1] [2] [3]출력이 우르르 쏟아지면 곤란하죠. 이 한 줄 덕분에 함수만 조용히 가져올 수 있습니다. 이 과정의 실습 파일 여덟 개가 전부 이 구조로 되어 있습니다.
코드에 나온 나머지 낯선 표현들
읽다가 이해하기 어려웠을 만한 부분만 짧게 설명합니다. 외울 것은 없습니다.
| 코드 | 읽는 법 |
|---|---|
for kind, chapter, q in questions: |
questions 의 한 칸은 괄호로 묶인 세 값 한 묶음(("사내 데이터", "3장", "..."))입니다. 그 세 값을 변수 셋에 한 번에 나눠 담습니다. 이걸 흔히 언패킹(unpacking) 이라고 부릅니다. |
f" Q: {q}" |
앞에 f 가 붙은 문자열은 중괄호 안의 변수를 그 자리에 끼워 넣습니다. 이걸 f-문자열(f-string) 이라고 합니다. |
answer[:150] |
문자열의 앞에서 150글자까지만 잘라 냅니다. 답이 길면 화면이 넘치니까요. 이걸 슬라이싱(slicing) 이라고 부릅니다. |
{' ...' if len(answer) > 150 else ''} |
150글자를 넘겼을 때만 " ..." 를 붙입니다. if를 한 줄로 쓴 것뿐입니다. |
세 질문의 결과
| 질문 | LLM의 반응 | 왜 |
|---|---|---|
| 일반 지식 | 잘 답한다 | 훈련 때 읽은 내용 안에 있다 |
| 주문 상태 | 모른다고 한다 | 우리 DB를 본 적이 없다 |
| 시장 동향 | 답하지만 옛날 정보다 | 훈련 시점까지만 안다 |
| 반품 기간 | 지어낸다 | 일반 법률 지식으로 메운다 |
세 번째를 다시 보세요. 승승장구몰의 실제 규정은 30일인데 LLM은 "3개월 이내 또는 30일 이내" 라고 답했습니다. 이게 환각입니다.
6. 안 될 때
| 오류 메시지 | 원인 | 해결 |
|---|---|---|
ModuleNotFoundError: No module named 'google' |
다른 방에서 실행 | conda activate agentic 후 재실행 |
ModuleNotFoundError: No module named 'common' |
code/ 안에 common.py 가 없음 (폴더 구조가 어긋남) |
ch01_hello.py 와 common.py 가 같은 code/ 폴더에 있는지 확인 |
can't open file ... ch01_hello.py |
실행 위치가 틀림 | agentic_ai 폴더에서 python code/ch01_hello.py |
[설정 필요] Gemini API 키를 찾지 못했습니다 |
.env 문제 |
앞 절 5번 항목 점검 |
폴더 존재 : False |
data/ 가 없거나 위치가 틀림 |
code/ 와 data/ 가 나란히 있는지 확인 |
400 API key not valid |
키가 잘못됨 | 키를 다시 복사해 붙여 넣기 (앞뒤 공백 주의) |
429 RESOURCE_EXHAUSTED |
요청이 몰림 | 1~2분 기다렸다 재실행 |
No module named 'common'은 실행 위치 탓이 아닙니다. 붙여 넣은 코드 맨 위의sys.path.append(...)한 줄이 "이 파일이 있는 폴더를 검색 경로에 넣어라" 라고 해 두었기 때문에, 어느 폴더에서 실행하든common.py를 찾아냅니다. 이 오류가 났다면common.py가code/안에 없는 것입니다. 실행 위치 문제는 그 아래can't open file쪽입니다.
스스로 해 보기
[2]의 질문q를 다른 것으로 바꿔 실행해 보세요. 예:"파이썬으로 CSV 파일을 읽는 방법을 3줄로 알려주세요."- 같은 질문을 두 번 연속 실행해 보세요. 답이 완전히 똑같지 않을 것입니다. 왜 그럴까요? (2장에서 답을 다룹니다)
[3]의 반품 기간 질문을 다시 실행해 보세요. 답이 매번 다른가요? 다르다면, 그게 왜 위험한지 한 문장으로 적어 보세요.
핵심 정리
client.models.generate_content(model=..., contents=...)이 한 줄이 LLM 호출의 전부입니다.- 응답에서
.text로 답변 문자열을 꺼냅니다. - LLM은 일반 지식은 잘 답하고, 사내 데이터·최신 정보·사내 문서는 모르거나 지어냅니다.
- 반품 기간 질문의 답(3개월)은 우리 회사 규정(30일)과 다릅니다. 이것이 환각의 실제 사례입니다.
- 이 세 빈틈을 3·4·6장에서 하나씩 메웁니다.