📚 Agentic AI - 기업용 자율 에이전트 개발 1장 · 실습 환경과 첫 호출 Agentic AI란

따라하기 — 설치 점검과 첫 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 "" 는 무엇인가요? 모델이 아무 글자도 만들지 않으면 .textNone(값이 없음) 으로 올 수 있습니다. 안전 필터에 걸리거나 응답이 비었을 때가 그렇죠.

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.pycommon.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.pycode/ 안에 없는 것입니다. 실행 위치 문제는 그 아래 can't open file 쪽입니다.


스스로 해 보기

  1. [2] 의 질문 q 를 다른 것으로 바꿔 실행해 보세요. 예: "파이썬으로 CSV 파일을 읽는 방법을 3줄로 알려주세요."
  2. 같은 질문을 두 번 연속 실행해 보세요. 답이 완전히 똑같지 않을 것입니다. 왜 그럴까요? (2장에서 답을 다룹니다)
  3. [3] 의 반품 기간 질문을 다시 실행해 보세요. 답이 매번 다른가요? 다르다면, 그게 왜 위험한지 한 문장으로 적어 보세요.

핵심 정리

  • client.models.generate_content(model=..., contents=...) 이 한 줄이 LLM 호출의 전부입니다.
  • 응답에서 .text 로 답변 문자열을 꺼냅니다.
  • LLM은 일반 지식은 잘 답하고, 사내 데이터·최신 정보·사내 문서는 모르거나 지어냅니다.
  • 반품 기간 질문의 답(3개월)은 우리 회사 규정(30일)과 다릅니다. 이것이 환각의 실제 사례입니다.
  • 이 세 빈틈을 3·4·6장에서 하나씩 메웁니다.
← 이전 절08. API키를 env 파일에 넣기다음 절 →10. 정리 자주 나는 오류 5가지