| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- dictionary vs class
- JSON
- LLM
- AI
- xls
- AI Agent
- list comprehension
- # 암호(비밀번호) 분실 # 암호(비밀번호) 찾기 #오피스(doc
- #다산 정약용 #유배지에서 보낸 편지 #도덕 #용기 #염 #주역 #호연지기 #효제 #근검
- 티스토리챌린지
- try / except / else / finally
- pathlib.Path
- Agent class
- 인공지능
- 에이전트
- AGENT.PY
- 파이썬
- 로그 파일(LOG FILE)
- agent history
- TOOLS.PY
- 오블완
- Agent Persistence
- LLM.PY
- #선진국 대한민국 #선진국 #대한민국 #아이들 #청소년 #고민 #해결 #심리
- 파워포인트) #집(zip)파일 #아래한글(HWP) #brute-force(무차별 대입)
- AI MODEL
- ppt) 파일 #오피스(워드
- 엑셀
- MAIN.PY
- __repr()
- Today
- Total
아톨러브
XI. Agent의 핵심 부품을 여러 파일로 나누어 실제 프로젝트 구조 만들기 본문
#-------- 1. tools.py 파일와 main.py 파일: main.py에서 tools 사용하기
# 파일1: tools.py
def calculator(expr):
return eval(expr)
def word_count(text):
return len(text.split())
REGISTRY = {"calculator":calculator, "word_count":word_count}
# 파일2: main.py
import tools
from tools import REGISTRY, word_count
print(tools.calculator("8*5"))
print(word_count("에이전트는 루프입니다"))
print("available: ", list(REGISTRY))
print(REGISTRY["calculator"]("200/25"))
#-------- 2. __name__과 __main__
def build_agent():
return {"name":"Scout", "tools":["search"]}
print("__name__is: ", __name__)
if __name__ == "__main__":
agent = build_agent()
print("직접 실행 중, 시작: ", agent["name"])
else:
print("가져오기 완료, 아무것도 시작하지 않음")
#-------- 3. 실제 프로젝트 폴더 구조
# my-agent/
# venv/ 가상 환경 (커밋되지 않음)- the virtual environment, never committed
# .env 비밀 키 (커밋되지 않음) - your secrets, never committed
# .gitignore Git이 건너뛸 항목을 지정 - tells git what to skip
# requirements.txt 고정된 종속성 - pinned dependencies
# main.py 진입점, 모든 것을 연결 - entry point, wires everthing up
# agent.py 에이전트 클래스 및 반복문 - the Agent class and the loop
# tools.py 도구 함수 및 레지스트리 - tool functions and the registry
# llm.py 모델 호출 및 응답 파싱 - the model call and response parsing
# agent_data/ 런타임에 기록되는 기록 및 로그 - history and logs written at runtime
#-------- 4. 환경변수에서 API Key 읽기
import os
api_key = os.environ.get("MODEL_API_KEY")
if not api_key:
print("MODEL_API_KEY is not set, refusing to start")
else:
print("key loaded, length: ", len(api_key))
print("safe preview: ", api_key[:4] + " ... " + api_key[-2:])
#-------- 5. .env와 python-dotenv 그리고 config Dictionary 만들기
# pip install python-dotenv
# .env (a plain text file, no quotes, no spaces around=)
# MODEL_API_KEY=sk-abcde12345
# MODEL_NAME=some-model
# MAX_STEPS=5
import os
from dotenv import load_dotenv
load_dotenv()
config ={
"api_key":os.environ.get("MODEL_API_KEY"),
"model":os.environ.get("MODEL_NAME", "default-model"),
"max_steps":int(os.environ.get("MAX_STEPS", "5")),
}
print("model: ", config["model"])
print("max_steps: ", config["max_steps"], type(config["max_steps"]))
print("key present: ", bool(config["api_key"]))
#-------- 6. .gitignore
# gitignore 파일
# .env
# venv/
# __pycache__/
# *.pyc
# agent_data/
# .DS_Store
#-------- 7. 이제 진짜 프로젝트 구조
# 파일1. tool.py 파일
def calculator(expr):
allowed = set("0123456789+-*/().")
if not set(expr) <= allowed:
return "unsupported characters"
return str(eval(expr))
def greet(name):
return f"안녕, {name}!"
REGISTRY = {"calculator":calculator, "greet":greet}
# 파일2. agent.py
class Agent:
def __init__(self, tools, max_steps=5):
self.tools=tools
self.max_steps=max_steps
self.steps=0
self.history=[]
def run_tool(self, name, args):
self.steps +=1
if name not in self.tools:
return f"unknown tool '{name}'"
try:
return str(self.tools[name](**args))
except Exception as e:
return f"tool failed: {e}"
# 파일3: main.py
import os
from agent import Agent
from tools import REGISTRY
def load_config():
return {
"api_key":os.environ.get("MODEL_API_KEY"),
"model":os.environ.get("MODEL_NAME", "default-model"),
"max_steps":int(os.environ.get("MAX_STEPS", "5")),
}
def main():
config = load_config()
print(f"model={config['model']} max_steps={config['max_steps']}")
print("key configured: ", bool(config["api_key"]))
bot = Agent(REGISTRY, max_steps=config["max_steps"])
print(bot.run_tool("calculator", {"expr":"(25*4)+8"}))
print(bot.run_tool("greet", {"name":"스티븐"}))
print(f"steps used: {bot.steps}/{bot.max_steps}")
if __name__ == "__main__":
main()
지금까지는 하나의 .py 파일 안에서:
Agent
Tools
Config
환경변수
실행 코드
를 모두 작성했습니다.
이번 코드부터는 이렇게 나눕니다.
my-agent/
│
├── main.py ← 프로그램 시작점
├── agent.py ← Agent 클래스
├── tools.py ← Tool 함수들
├── llm.py ← AI 모델 호출
├── .env ← API Key
└── requirements.txt ← 필요한 라이브러리
이것은 실제 AI Agent 프로젝트의 기본적인 구조입니다.
전체 구조를 먼저 그림으로 보면
┌─────────────┐
│ main.py │
│ 프로그램 시작 │
└──────┬──────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
agent.py tools.py llm.py
Agent Tools AI Model
│ │ │
└────────────────┼────────────────┘
│
▼
.env
API KEY / 설정
이제 하나씩 보겠습니다.
1. tools.py 파일
def calculator(expr):
return eval(expr)
def word_count(text):
return len(text.split())
REGISTRY = {
"calculator": calculator,
"word_count": word_count
}
이 파일의 역할은:
Agent가 사용할 도구들을 정의하고 관리하는 것
입니다.
calculator
def calculator(expr):
return eval(expr)
예:
calculator("8*5")
↓
40
word_count
def word_count(text):
return len(text.split())
예:
word_count("에이전트는 루프입니다")
split() 결과:
["에이전트는", "루프입니다"]
길이:
2
REGISTRY
REGISTRY = {
"calculator": calculator,
"word_count": word_count
}
이것은 우리가 이전에 공부했던 Tool Registry입니다.
구조는:
Tool 이름 실제 함수
───────── ─────────
calculator → calculator()
word_count → word_count()
즉:
REGISTRY["calculator"]
↓
calculator
그리고:
REGISTRY["calculator"]("8*5")
↓
40
입니다.
2. main.py에서 tools 사용하기
import tools
의 의미:
tools.py 파일 전체를 가져온다.
이제:
tools.calculator("8*5")
처럼 사용할 수 있습니다.
구조:
tools
│
├── calculator
├── word_count
└── REGISTRY
따라서:
tools.calculator("8*5")
↓
40

3. from tools import ...
from tools import REGISTRY, word_count
이번에는 필요한 것만 가져옵니다.
그래서:
word_count("에이전트는 루프입니다")
처럼 사용할 수 있습니다.
tools.word_count()라고 안 써도 됩니다.
두 import 방식 비교
방법 1
import tools
사용:
tools.calculator("8*5")
장점:
어느 파일의 함수인지 명확함
방법 2
from tools import calculator
사용:
calculator("8*5")
장점:
짧고 편함
실제 프로젝트에서는 개인적으로:
import tools
또는:
from tools import REGISTRY
처럼 사용하는 것이 구조 파악에 편합니다.
4. list(REGISTRY)
print("available: ", list(REGISTRY))
Dictionary에서:
list(REGISTRY)
는 기본적으로 Key 목록을 가져옵니다.
즉:
{
"calculator": calculator,
"word_count": word_count
}
↓
["calculator", "word_count"]
출력:
available: ['calculator', 'word_count']
5. Registry에서 Tool 실행
REGISTRY["calculator"]("200/25")
이 코드는 처음 보면 약간 신기합니다.
단계별로 보면:
REGISTRY["calculator"]
↓
calculator
즉 함수 자체를 가져옵니다.
그리고:
("200/25")
를 붙이면:
calculator("200/25")
가 됩니다.
↓
8.0
6. __name__과 __main__
다음 코드입니다.
def build_agent():
return {
"name": "Scout",
"tools": ["search"]
}
print("__name__ is: ", __name__)
Python 파일에는 자동으로:
__name__
이라는 특별한 변수가 있습니다.
직접 실행하면
예:
python main.py
이 경우:
__name__
은:
__main__
입니다.
다른 파일에서 import하면
예:
import agent
이면 agent.py 안에서는:
__name__
이:
agent
가 됩니다.
그래서:
if __name__ == "__main__":
의 의미는:
이 파일을 직접 실행했을 때만 아래 코드를 실행해라.
입니다.
직접 실행
if __name__ == "__main__":
agent = build_agent()
print("직접 실행 중")
↓
실행됩니다.
import
import some_file
↓
if __name__ == "__main__":
조건이 False입니다.
따라서:
Agent를 자동으로 시작하지 않습니다.
이것이 매우 중요합니다.
쉽게 비유하면
tools.py는:
공구함
입니다.
안에는:
망치
드라이버
렌치
가 있습니다.
하지만 tools.py를 import했다고 해서 망치질을 자동으로 시작하면 이상합니다.
그래서:
도구 정의
와:
실제 프로그램 실행
을 분리하는 것입니다.
7. 실제 프로젝트 폴더 구조
주석에 있는 구조:
my-agent/
│
├── venv/
├── .env
├── .gitignore
├── requirements.txt
├── main.py
├── agent.py
├── tools.py
├── llm.py
└── agent_data/
하나씩 보겠습니다.
venv/
가상환경
프로젝트별 Python 환경입니다.
예:
Project A
└── requests 2.31
Project B
└── requests 2.32
처럼 서로 독립적으로 관리할 수 있습니다.
Git에는 올리지 않습니다.
.env
비밀 설정
예:
MODEL_API_KEY=sk-xxxx
MODEL_NAME=gpt-model
MAX_STEPS=5
API Key를 코드에 직접 쓰지 않는 이유는:
main.py
↓
GitHub
↓
API Key 노출 ❌
위험하기 때문입니다.
.gitignore
Git에게:
이 파일들은 추적하지 마세요.
라고 알려줍니다.
예:
.env
venv/
agent_data/
requirements.txt
프로젝트에 필요한 라이브러리 목록입니다.
예:
openai==...
python-dotenv==...
requests==...
다른 사람이:
pip install -r requirements.txt
하면 필요한 라이브러리를 설치할 수 있습니다.
8. 환경변수에서 API Key 읽기
import os
api_key = os.environ.get("MODEL_API_KEY")
운영체제 환경변수에서:
MODEL_API_KEY
를 찾습니다.
없으면
if not api_key:
print("MODEL_API_KEY is not set, refusing to start")
Agent를 실행하지 않습니다.
이것은 좋은 습관입니다.
왜냐하면 API Key 없이 실행하면:
API 호출 실패
가 발생하기 때문입니다.
있으면
else:
print("key loaded")
정상적으로 Key를 읽었다는 의미입니다.
9. API Key Preview
api_key[:4]
앞 4글자:
sk-a
api_key[-2:]
뒤 2글자입니다.
예:
sk-abcde12345
↓
sk-a ... 45
전체 Key를 출력하지 않고:
정상적으로 Key가 로드되었는지
확인하는 방법입니다.
실제 운영 환경에서는 Key 길이나 일부 문자열조차 로그에 남기지 않는 것이 더 안전한 경우도 많습니다.
10. .env와 python-dotenv
from dotenv import load_dotenv
load_dotenv()
이 함수의 역할:
.env 파일을 읽어서 환경변수로 등록한다.
예:
.env
MODEL_API_KEY=sk-abcde12345
MODEL_NAME=some-model
MAX_STEPS=5
실행:
load_dotenv()
이후:
os.environ.get("MODEL_API_KEY")
로 읽을 수 있습니다.
⚠️ 코드에 작은 오타가 있습니다
주석에서는:
MODLE_NAME = some-model
라고 되어 있습니다.
하지만 코드에서는:
os.environ.get("MODEL_NAME", "default-model")
를 찾습니다.
즉:
MODLE_NAME ❌
MODEL_NAME ⭕
이어야 합니다.
올바른 .env:
MODEL_API_KEY=sk-abcde12345
MODEL_NAME=some-model
MAX_STEPS=5
그리고 = 앞뒤에 공백을 두지 않는 편이 좋습니다.
11. config Dictionary 만들기
config = {
"api_key": os.environ.get("MODEL_API_KEY"),
"model": os.environ.get("MODEL_NAME", "default-model"),
"max_steps": int(os.environ.get("MAX_STEPS", "5")),
}
여기서 중요한 것은:
환경변수 → 문자열
이라는 것입니다.
예:
MAX_STEPS=5
이더라도:
os.environ.get("MAX_STEPS")
결과는:
"5"
문자열입니다.
그래서:
int(...)
로 변환합니다.
↓
5
정수입니다.
기본값
os.environ.get(
"MODEL_NAME",
"default-model"
)
의 의미:
MODEL_NAME이 있으면 그것을 사용하고, 없으면 default-model을 사용하라.
입니다.
12. .gitignore
.env
venv/
__pycache__/
*.pyc
agent_data/
.DS_Store
하나씩 보면:
| .env | API Key 등 비밀 |
| venv/ | 가상환경 |
| __pycache__/ | Python 캐시 |
| *.pyc | Python 컴파일 캐시 |
| agent_data/ | 실행 중 생성된 데이터 |
| .DS_Store | macOS 시스템 파일 |
특히:
.env
는 정말 중요합니다.
API Key가 GitHub에 올라가면 매우 위험합니다.
13. 이제 진짜 프로젝트 구조
마지막 부분은 앞의 모든 내용을 합칩니다.
tools.py
def calculator(expr):
계산기입니다.
allowed = set("0123456789+-*/().")
허용되는 문자만 지정합니다.
예:
0123456789
+
-
*
/
(
)
.
if not set(expr) <= allowed:
의미:
expr 안의 모든 문자가 allowed 안에 들어있는가?
예:
expr = "25*4"
↓
모든 문자가 허용됨.
True
하지만:
expr = "__import__('os')"
허용되지 않은 문자가 포함됩니다.
↓
unsupported characters
그래서 이전의 단순한:
eval(expr)
보다 조금 안전합니다.
다만 실제 서비스용 계산기는 eval() 자체를 피하는 것이 더 좋습니다. 나중에 Agent를 실제 OpenAI 모델과 연결할 때는 안전한 계산기 함수를 별도로 만드는 것을 추천합니다.
greet Tool
def greet(name):
return f"안녕, {name}!"
간단한 Tool입니다.
REGISTRY
REGISTRY = {
"calculator": calculator,
"greet": greet
}
Agent에게 줄 Tool 목록입니다.
Agent
│
└── REGISTRY
│
├── calculator()
└── greet()
agent.py
class Agent:
이 파일은 Agent 자체를 정의합니다.
def __init__(self, tools, max_steps=5):
Agent 생성 시:
사용 가능한 Tool
최대 단계
를 받습니다.
run_tool
def run_tool(self, name, args):
Agent가 Tool을 실행합니다.
Step 증가
self.steps += 1
Tool 호출 횟수를 기록합니다.
Tool 존재 확인
if name not in self.tools:
예:
name = "emailer"
그런데 Registry에는:
calculator
greet
만 있다면:
unknown tool 'emailer'
을 반환합니다.
여기에는 작은 오타가 있습니다.
return f"unknown too '{name}'"
는:
return f"unknown tool '{name}'"
가 자연스럽습니다.
Tool 실행
self.tools[name](**args)
예:
name = "greet"
args = {
"name": "스티븐"
}
↓
greet(name="스티븐")
↓
안녕, 스티븐!
main.py
이 파일은 전체 프로그램을 조립하는 곳입니다.
main.py
│
├── 설정 읽기
│
├── Agent 만들기
│
├── Tools 연결
│
└── Agent 실행
load_config()
def load_config():
설정을 읽는 일을 하나의 함수로 분리했습니다.
이것이 좋은 습관입니다.
이전에는:
api_key = ...
model = ...
max_steps = ...
가 프로그램 여기저기에 흩어질 수 있었습니다.
하지만 이제:
load_config()
만 보면:
설정은 여기서 관리하는구나.
하고 알 수 있습니다.
main()
def main():
실제 프로그램의 중심입니다.
설정 로드
config = load_config()
↓
{
"api_key": "...",
"model": "...",
"max_steps": 5
}
Agent 생성
bot = Agent(
REGISTRY,
max_steps=config["max_steps"]
)
이 부분이 중요합니다.
REGISTRY
│
├── calculator
└── greet
│
▼
Agent
Agent는 Tool을 직접 모르고 있습니다.
단지:
tools
라는 Dictionary를 받습니다.
이것이 좋은 설계입니다.
첫 번째 Tool 실행
bot.run_tool(
"calculator",
{"expr": "(25*4)+8"}
)
흐름:
main.py
│
▼
Agent.run_tool()
│
▼
REGISTRY["calculator"]
│
▼
calculator(expr="(25*4)+8")
│
▼
108
두 번째 Tool
bot.run_tool(
"greet",
{"name": "스티븐"}
)
↓
안녕, 스티븐!
Step 출력
print(f"steps used: {bot.steps}/{bot.max_steps}")
결과:
steps used: 2/5
입니다.
🔥 가장 중요한 전체 흐름
이 프로젝트 구조를 이해하면:
main.py
│
│ 시작
▼
load_config()
│
▼
.env / 환경변수
│
▼
config
│
▼
Agent 생성
│
┌──────────┴──────────┐
│ │
▼ ▼
REGISTRY max_steps
│
┌────┴─────┐
│ │
▼ ▼
calculator greet
실행할 때:
사용자 요청
│
▼
Agent
│
▼
어떤 Tool?
│
▼
REGISTRY[name]
│
▼
실제 함수 실행
│
▼
결과 반환
⭐ 그런데 현재 코드에는 한 가지 중요한 부분이 빠져 있습니다
마지막 main.py 코드에는 현재:
import os
만 있습니다.
하지만 실제로는 Agent와 REGISTRY를 가져와야 합니다.
따라서 실제 main.py는 보통 이렇게 됩니다.
import os
from agent import Agent
from tools import REGISTRY
def load_config():
return {
"api_key": os.environ.get("MODEL_API_KEY"),
"model": os.environ.get("MODEL_NAME", "default-model"),
"max_steps": int(os.environ.get("MAX_STEPS", "5")),
}
def main():
config = load_config()
print(
f"model={config['model']} "
f"max_steps={config['max_steps']}"
)
print("key configured:", bool(config["api_key"]))
bot = Agent(
REGISTRY,
max_steps=config["max_steps"]
)
print(
bot.run_tool(
"calculator",
{"expr": "(25*4)+8"}
)
)
print(
bot.run_tool(
"greet",
{"name": "스티븐"}
)
)
print(
f"steps used: {bot.steps}/{bot.max_steps}"
)
if __name__ == "__main__":
main()
🎯 이번 코드의 핵심은 이것입니다
지금까지 공부한 Agent를 실제 프로젝트로 바꾸는 과정입니다.
[이전]
하나의 파일
main.py
├── Agent
├── Tools
├── Config
└── 실행
↓
[지금]
프로젝트
│
├── main.py
│ └── 조립 및 시작
│
├── agent.py
│ └── Agent의 행동
│
├── tools.py
│ └── Tool 정의
│
├── llm.py
│ └── OpenAI 모델 호출
│
├── .env
│ └── API Key
│
└── agent_data/
└── History / Log
이제 다음 단계에서 여기에:
OpenAI API
가 들어오면 진짜 Agent Loop가 됩니다.
사용자
↓
main.py
↓
Agent
↓
OpenAI Model
↓
"calculator를 사용하세요"
↓
Agent
↓
Tool 실행
↓
결과
↓
OpenAI Model
↓
최종 답변
지금 배우는 순서가 아주 좋습니다. 앞에서 배운 함수 → Dictionary → JSON → Tool Registry → History → Loop → Error 처리 → Class → 파일 분리가 바로 실제 AI Agent를 만들기 위한 부품들이었고, 이번 코드에서 그 부품들을 처음으로 프로젝트 구조로 조립하기 시작한 것입니다.
'AI, 클라우드, 문서, 자동화 > AI_AGENT' 카테고리의 다른 글
| X. Agent의 여러 요소를 하나의 class Agent로 묶어가는 과정. (0) | 2026.08.24 |
|---|---|
| IX. AI Agent에 “기억과 기록” 기능을 붙이는 단계 (0) | 2026.08.23 |
| VIII. Agent Loop + Tool Dispatcher 다음 단계인 예외 처리(Exception), 재시도(Retry), Logging 스터디 (0) | 2026.08.23 |
| VII. Python 함수 → Tool 함수 → Agent의 Tool Dispatcher 구조 (0) | 2026.08.23 |
| VI. Python의 반복문과 자료구조를 이용해 AI Agent의 실제 실행 흐름을 만드는 과정 (0) | 2026.08.23 |
