아톨러브

IX. AI Agent에 “기억과 기록” 기능을 붙이는 단계 본문

AI, 클라우드, 문서, 자동화/AI_AGENT

IX. AI Agent에 “기억과 기록” 기능을 붙이는 단계

아톨 2026. 8. 23. 21:59
반응형

 

    #-------- 1. 파일에 쓰고 읽기

    with open("notes.txt", "w", encoding="utf-8") as f:

        f.write("에이전트 작동이 시작되었습니다. \n")

        f.write("tool: search \n")

 

    with open("notes.txt", "r", encoding="utf-8") as f:

        contents = f.read()

 

    print(contents)

    print("characters: ", len(contents))

 

    #-------- 2. 로그 파일에 추가하기

    with open("runlog.txt", "a", encoding="utf-8") as f:

        f.write("1단계 | search | ok\n")

        f.write("2단계 | calculator | ok\n")

        f.write("3단계 | read_file | failed\n")

 

    failures = 0

    with open("runlog.txt", "r", encoding="utf-8") as f:

        for number, line in enumerate(f, start=1):

            line = line.strip()

            if line.endswith("failed"):

                failures += 1

                print(f"line {number}: {line}")

 

        print("failed steps: ", failures)

 

    #-------- 3. Agent History JSON으로 저장하기

    import json

 

    history= [

        {"role":"system", "content":"당신은 재고 관리 어시스턴트입니다."},

        {"role":"user", "content":"제품코드 4471 재고가 개입니까"},

        {"role":"assistant", "content":"12개입니다."},

    ]

 

    with open("history.json", "w", encoding="utf-8") as f:

        json.dump(history, f, indent =2, ensure_ascii=False)

 

    with open("history.json", "r", encoding="utf-8") as f:

        restored = json.load(f)

 

    print("messages restored: ", len(restored))

    print("last message: ", restored[-1]["content"])

    print("same data: ", restored == history)

 

    #-------- 4. pathlib.Path

    from pathlib import Path

 

    data_dir = Path("agent_data")

    history_file = data_dir/"sessions"/"history.json"

 

    print(history_file)

    print("name: ", history_file.name)

    print("suffix: ", history_file.suffix)

    print("parent: ", history_file.parent)

    print("exists: ", history_file.exists())

 

    #-------- 5. 폴더 만들기 + JSON 저장

    from pathlib import Path

    import json

 

    data_dir = Path("agent_data")/"sessions"

    data_dir.mkdir(parents=True, exist_ok=True)

 

    target = data_dir/"latest.json"

    target.write_text(json.dumps({"steps":3, "ok":True}), encoding="utf-8")

 

    print("written to: ", target)

    print("size: ", target.stat().st_size, "bytes")

    print("contents: ", json.loads(target.read_text(encoding="utf-8")))

    print("files here: ", [p.name for p in data_dir.iterdir()])

 

    #-------- 6. 안전한 History 불러오기

    from pathlib import Path

    import json

 

    def load_history(path):

        p = Path(path)

        if not p.exists():

            print(f"no history at {p}, starting fresh")

            return []

        try:

            return json.loads(p.read_text(encoding="utf-8"))

        except json.JSONDecodeError:

            print(f"history at {p} is corrupt, starting fresh")

            return []

 

    print(load_history("does_not_exist.json"))

 

    Path("broken.json").write_text("{not json", encoding="utf-8")

    print(load_history("broken.json"))

 

    #-------- 7. 작은 Agent Persistence 시스템

    import json

    from datetime import datetime

    from pathlib import Path

 

    DATA_DIR = Path("agent_data")

    HISTORY = DATA_DIR/"history.json"

    LOG = DATA_DIR/"events.log"

 

    def setup():

        DATA_DIR.mkdir(parents=True, exist_ok=True)

 

    def load():

        if not HISTORY.exists():

            return []

        try:

            return json.loads(HISTORY.read_text(encoding="utf-8"))

        except json.JSONDecodeError:

            return []

 

    def save(history):

        HISTORY.write_text(

            json.dumps(history, indent=2, ensure_ascii=False),encoding="utf-8"

        )

 

    def log_event(text):

        stamp = datetime.now().strftime("%H:%M:%S")

        with open(LOG, "a", encoding="utf-8") as f:

            f.write(f"{stamp} {text} \n")

 

    setup()

    history = load()

    print(f"loaded {len(history)} previous messages.")

 

    run_number = len([m for m in history if m["role"]=="user"]) +1

    history.append({"role":"user", "content":f"request number {run_number}"})

    log_event(f"user turn {run_number}")

 

    history.append({"role":"assistant", "content":"handled it"})

    log_event("assistant replied")

 

    save(history)

    print(f"saved {len(history)} messages to {HISTORY}")

    print("log_tail: ", LOG.read_text(encoding="utf-8").strip().splitlines()[-1])

 

앞에서 우리는:

사용자
 ↓
Agent
 ↓
LLM
 ↓
Tool
 ↓
결과
 

를 공부했습니다.

그런데 프로그램을 종료하면 문제가 생깁니다.

프로그램 종료
     ↓
history 사라짐
로그 사라짐
이전 작업 사라짐
 

그래서 이번 코드에서는 파일을 사용합니다.

AI Agent
   │
   ├── history.json     ← 대화 기억
   │
   ├── events.log       ← 작업 기록
   │
   └── agent_data/      ← Agent 데이터 저장 공간
 

하나씩 보겠습니다.


1. 파일에 쓰고 읽기

 
with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("에이전트 작동이 시작되었습니다. \n")
    f.write("tool: search \n")
 

open()

 
open("notes.txt", "w")
 

의 의미는:

notes.txt 파일을
쓰기 모드(w)로 열어라
 

입니다.


"w" = Write

 
"w"
 

쓰기 모드입니다.

파일이 없다면:

새 파일 생성
 

파일이 이미 있다면:

기존 내용 삭제
새 내용 작성
 

입니다.

예를 들어 기존 파일이:

안녕하세요
기존 데이터
 

였는데:

 
open("notes.txt", "w")
 

를 하면 기존 내용은 사라질 수 있습니다.


with

 
with open(...) as f:
 

는 아주 중요한 문법입니다.

파일을 열고:

파일 열기
   ↓
작업
   ↓
자동으로 파일 닫기
 

를 해줍니다.

예전 방식은:

 
f = open("notes.txt", "w")
f.write("hello")
f.close()
 

였습니다.

하지만 중간에 오류가 발생하면:

 
f.close()
 

가 실행되지 않을 수 있습니다.

그래서:

 
with open(...) as f:
 

를 사용하는 것이 좋습니다.


f.write()

 
f.write("에이전트 작동이 시작되었습니다. \n")
 

파일에 문자열을 씁니다.

\n은 줄바꿈입니다.

최종 파일:

에이전트 작동이 시작되었습니다.
tool: search
 

파일 읽기

 
with open("notes.txt", "r", encoding="utf-8") as f:
    contents = f.read()
 

"r"은:

Read
읽기 모드
 

입니다.

 
f.read()
 

는 파일 전체를 읽습니다.

예:

 
contents
 

에이전트 작동이 시작되었습니다.
tool: search
 

글자 수

 
print("characters: ", len(contents))
 

len()은 문자열의 길이를 계산합니다.

공백과 줄바꿈도 포함될 수 있습니다.


2. 로그 파일에 추가하기

 
with open("runlog.txt", "a", encoding="utf-8") as f:
 

이번에는 "a"입니다.

a = append
 

즉:

기존 내용을 지우지 않고 뒤에 추가

합니다.


예를 들어 기존 파일:

어제 실행
 

여기에:

 
f.write("오늘 실행\n")
 

하면:

어제 실행
오늘 실행
 

이 됩니다.


Agent에서는 아주 중요합니다

events.log

09:00 Agent 시작
09:01 search 실행
09:02 calculator 실행
09:03 완료
 

이런 식으로 기록합니다.


로그 파일 작성

 
f.write("1단계 | search | ok\n")
f.write("2단계 | calculator | ok\n")
f.write("3단계 | read_file | failed\n")
 

파일 내용:

1단계 | search | ok
2단계 | calculator | ok
3단계 | read_file | failed
 

한 줄씩 읽기

 
with open("runlog.txt", "r", encoding="utf-8") as f:
    for number, line in enumerate(f, start=1):
 

파일 객체 f는 반복할 수 있습니다.

즉:

첫 번째 줄
두 번째 줄
세 번째 줄
 

순서대로 가져옵니다.


enumerate()

 
enumerate(f, start=1)
 

는:

1, 첫 번째 줄
2, 두 번째 줄
3, 세 번째 줄
 

을 만들어줍니다.

그래서:

 
for number, line in enumerate(f, start=1):
 

는 대략:

 
number = 1
line = "1단계 | search | ok\n"

number = 2
line = "2단계 | calculator | ok\n"

number = 3
line = "3단계 | read_file | failed\n"
 

입니다.


strip()

 
line = line.strip()
 

앞뒤 공백과 줄바꿈을 제거합니다.

"3단계 | read_file | failed\n"
 

"3단계 | read_file | failed"
 

endswith()

 
if line.endswith("failed"):
 

문자열이:

failed
 

로 끝나는지 검사합니다.

예:

3단계 | read_file | failed
 

→ True

그러면:

 
failures += 1
 

실패 횟수를 증가시킵니다.

최종:

failed steps: 1
 


3. Agent History를 JSON으로 저장하기

이 부분이 매우 중요합니다.

 
history= [
    {"role":"system", "content":"당신은 재고 관리 어시스턴트입니다."},
    {"role":"user", "content":"제품코드 4471은 재고가 몇 개입니까"},
    {"role":"assistant", "content":"12개입니다."},
]
 

이것은 우리가 계속 공부한 Agent의:

Conversation History
 

입니다.

메모리에 있을 때:

Python List
 

입니다.

그런데 프로그램을 종료하면 사라집니다.

그래서:

 
json.dump()
 

를 사용합니다.


JSON 파일 저장

 
with open("history.json", "w", encoding="utf-8") as f:
    json.dump(history, f, indent=2, ensure_ascii=False)
 

의미:

Python history
      ↓
JSON 형식으로 변환
      ↓
history.json 저장
 

indent=2

JSON을 보기 좋게 만듭니다.

대략:

 
[
  {
    "role": "system",
    "content": "당신은 재고 관리 어시스턴트입니다."
  },
  {
    "role": "user",
    "content": "제품코드 4471은 재고가 몇 개입니까"
  }
]
 

ensure_ascii=False

이것도 중요합니다.

한국어를 그대로 저장합니다.

없으면 경우에 따라:

\uc5d0\uc774\uc804\ud2b8
 

같은 Unicode Escape 형태로 보일 수 있습니다.

따라서 한국어 데이터를 저장할 때:

 
ensure_ascii=False
 

를 자주 사용합니다.


JSON 복원

 
with open("history.json", "r", encoding="utf-8") as f:
    restored = json.load(f)
 

이번에는:

history.json
     ↓
Python List
 

로 복원합니다.


확인

 
print("messages restored: ", len(restored))
 

3개 메시지입니다.


 
print("last message: ", restored[-1]["content"])
 

[-1]은 마지막 요소입니다.

즉:

 
{"role":"assistant", "content":"12개입니다."}
 

그리고:

12개입니다.
 

를 출력합니다.


원본과 같은가?

 
print("same data: ", restored == history)
 

JSON 저장 → 읽기 과정을 거쳤지만 데이터가 같으면:

True
 

입니다.

이것이 Agent의:

Memory Persistence
 

의 기초입니다.


4. pathlib.Path

이번부터 파일 경로를 더 Python답게 다룹니다.

 
from pathlib import Path
 

기존에는:

 
"agent_data/sessions/history.json"
 

처럼 문자열로 경로를 많이 사용했습니다.

Path를 사용하면:

 
data_dir = Path("agent_data")
 

이제:

agent_data
 

라는 경로 객체가 됩니다.


경로 연결

 
history_file = data_dir/"sessions"/"history.json"
 

이 문법이 매우 편리합니다.

agent_data
    /
sessions
    /
history.json
 

agent_data/sessions/history.json
 

.name

 
history_file.name
 

history.json
 

.suffix

 
history_file.suffix
 

.json
 

.parent

 
history_file.parent
 

agent_data/sessions
 

.exists()

 
history_file.exists()
 

파일이 존재하는지 확인합니다.

True
 

또는:

False
 

5. 폴더 만들기 + JSON 저장

 
data_dir = Path("agent_data")/"sessions"
 

결과:

agent_data/sessions
 

폴더 생성

 
data_dir.mkdir(parents=True, exist_ok=True)
 

매우 중요한 코드입니다.

parents=True

중간 폴더까지 자동 생성합니다.

agent_data
    └── sessions
 

exist_ok=True

이미 폴더가 있어도 오류를 내지 않습니다.

즉:

폴더 없음 → 생성
폴더 있음 → 그냥 계속
 

파일 경로

 
target = data_dir/"latest.json"
 

agent_data/sessions/latest.json
 

write_text()

 
target.write_text(
    json.dumps({"steps":3, "ok":True}),
    encoding="utf-8"
)
 

여기서는 open() 없이 바로 파일을 씁니다.

과정:

Python Dict
     ↓
json.dumps()
     ↓
JSON 문자열
     ↓
write_text()
     ↓
파일 저장
 

파일 크기

 
target.stat().st_size
 

파일 정보를 가져옵니다.

파일 정보
    ↓
st_size
    ↓
파일 크기(byte)
 

다시 읽기

 
target.read_text(encoding="utf-8")
 

파일 전체를 문자열로 읽습니다.

그 다음:

 
json.loads(...)
 

JSON 문자열을 Python Dictionary로 바꿉니다.


폴더 안 파일 보기

 
[p.name for p in data_dir.iterdir()]
 

iterdir():

폴더 내부 항목들을 순회
 

합니다.

예:

[
    "history.json",
    "latest.json"
]
 

6. 안전한 History 불러오기

이 함수는 실제 Agent에서 상당히 중요합니다.

 
def load_history(path):
 

Agent 시작 시:

history.json이 있으면
   ↓
불러오기

없으면
   ↓
새로운 History 시작
 

하는 구조입니다.


Path 객체로 변환

 
p = Path(path)
 

사용자가:

 
load_history("history.json")
 

을 호출하면:

문자열
   ↓
Path 객체
 

로 바꿉니다.


파일이 없는 경우

 
if not p.exists():
 

예:

 
load_history("does_not_exist.json")
 

파일 없음.

그러면:

 
print(f"no history at {p}, starting fresh")
return []
 

즉:

새로운 Agent History
 

를 반환합니다.

 
[]
 

JSON 파일이 깨진 경우

 
try:
    return json.loads(
        p.read_text(encoding="utf-8")
    )

except json.JSONDecodeError:
 

예를 들어 파일이:

{not json
 

이면 JSON 형식이 아닙니다.

정상 JSON은:

 
{
  "name": "Steven"
}
 

같아야 합니다.

그래서:

JSONDecodeError
 

가 발생합니다.

이 경우:

 
print("history is corrupt, starting fresh")
return []
 

를 합니다.


이것의 Agent적 의미

프로그램이:

history.json 없음
 

때문에 죽으면 안 됩니다.

또:

history.json 깨짐
 

때문에 Agent 전체가 죽어도 안 됩니다.

그래서:

없음 → 빈 History
깨짐 → 빈 History
정상 → History 복원
 

이라는 안전장치를 만드는 것입니다.


7. 마지막 코드

⭐ 작은 Agent Persistence 시스템

이 부분은 지금까지 배운 것이 모두 합쳐집니다.

구조:

agent_data/
│
├── history.json
│
└── events.log
 

DATA_DIR

 
DATA_DIR = Path("agent_data")
 

Agent 데이터 저장 폴더입니다.


HISTORY

 
HISTORY = DATA_DIR/"history.json"
 

agent_data/history.json
 

LOG

 
LOG = DATA_DIR/"events.log"
 

agent_data/events.log
 

setup()

 
def setup():
    DATA_DIR.mkdir(parents=True, exist_ok=True)
 

Agent 실행 전에:

agent_data 폴더가 있는지 확인
 

없으면 생성합니다.


load()

 
def load():
 

역할:

history.json
     ↓
존재?
     │
     ├── 없음 → []
     │
     └── 있음
           ↓
        JSON 정상?
           │
           ├── 정상 → History 반환
           │
           └── 깨짐 → []
 

입니다.


save(history)

 
def save(history):
 

현재 History를 파일에 저장합니다.

 
HISTORY.write_text(
    json.dumps(
        history,
        indent=2,
        ensure_ascii=False
    ),
    encoding="utf-8"
)
 

log_event(text)

 
def log_event(text):
 

이벤트를 로그 파일에 기록합니다.


현재 시간

 
stamp = datetime.now().strftime("%H:%M:%S")
 

예:

21:35:12
 

로그 기록

 
with open(LOG, "a", encoding="utf-8") as f:
    f.write(f"{stamp} {text} \n")
 

예:

21:35:12 user turn 1
21:35:13 assistant replied
 

Agent 시작

 
setup()
 

폴더 준비.


 
history = load()
 

이전 History 불러오기.

예:

이전 실행에서:

user: 안녕
assistant: 안녕하세요
 

가 저장되어 있다면 다시 복원합니다.


몇 번째 요청인지 계산

 
run_number = len(
    [m for m in history if m["role"]=="user"]
) + 1
 

이 부분도 중요합니다.

예:

 
history = [
    {"role":"user"},
    {"role":"assistant"},
    {"role":"user"},
]
 

사용자 메시지는:

2개
 

입니다.

따라서:

2 + 1
 

3번째 요청
 

새로운 사용자 메시지

 
history.append({
    "role":"user",
    "content":f"request number {run_number}"
})
 

예:

 
{
    "role": "user",
    "content": "request number 3"
}
 

이벤트 기록

 
log_event(f"user turn {run_number}")
 

로그 파일:

21:40:11 user turn 3
 

Assistant 응답 추가

 
history.append({
    "role":"assistant",
    "content":"handled it"
})
 

그리고:

 
log_event("assistant replied")
 

로그:

21:40:12 assistant replied
 

History 저장

 
save(history)
 

현재까지의 대화를:

agent_data/history.json
 

에 저장합니다.


마지막 로그 확인

 
LOG.read_text(
    encoding="utf-8"
).strip().splitlines()[-1]
 

이 코드를 분해하면:

① 파일 읽기

 
LOG.read_text()
 

21:40:11 user turn 3
21:40:12 assistant replied
 

② 앞뒤 공백 제거

 
.strip()
 

③ 줄 단위로 나누기

 
.splitlines()
 

 
[
    "21:40:11 user turn 3",
    "21:40:12 assistant replied"
]
 

④ 마지막 줄

 
[-1]
 

21:40:12 assistant replied
 

🌟 이번 코드 전체를 한 그림으로 정리하면

Agent 실행
    │
    ▼
setup()
    │
    ▼
agent_data 폴더 준비
    │
    ▼
load()
    │
    ├── history.json 있음 → 불러오기
    │
    └── 없음/깨짐 → []
    │
    ▼
사용자 요청 추가
    │
    ▼
history.append()
    │
    ▼
events.log 기록
    │
    ▼
Assistant 응답
    │
    ▼
history.append()
    │
    ▼
events.log 기록
    │
    ▼
save()
    │
    ▼
history.json 저장
 

이번 코드에서 꼭 기억할 핵심

① 파일 모드

"r" → Read → 읽기
"w" → Write → 새로 쓰기
"a" → Append → 뒤에 추가
 

② with open()

 
with open(...) as f:
 

파일 작업 후 자동으로 닫아줍니다.


③ JSON 저장과 복원

 
json.dump()
 

Python 객체 → 파일

 
json.load()
 

파일 → Python 객체

문자열이라면:

 
json.dumps()
json.loads()
 

입니다.


④ Path

 
Path("agent_data") / "history.json"
 

파일 경로를 안전하고 편리하게 다룹니다.


⑤ Agent에서 가장 중요한 개념

이번 코드는 결국 이것을 만드는 것입니다.

RAM Memory
history
   │
   │ 프로그램 종료
   ▼
사라짐 ❌


Persistent Memory
history.json
   │
   │ 프로그램 종료
   ▼
파일에 남음 ⭕
   │
   │ 다음 실행
   ▼
다시 복원
 

즉, 이번 단계부터 여러분이 만들고 있는 Agent는 단순히 실행되고 끝나는 프로그램이 아니라,

이전 실행 기록을 저장하고, 다음 실행 때 다시 불러올 수 있는 Agent

로 발전하는 단계라고 보시면 됩니다.

그리고 지금까지 배운 내용을 연결하면 다음 순서입니다.

Python 기본 문법
    ↓
함수
    ↓
Dictionary / JSON
    ↓
History
    ↓
Tool
    ↓
Dispatcher
    ↓
Agent Loop
    ↓
Exception / Retry
    ↓
Logging
    ↓
파일 저장
    ↓
Persistent Memory
 

 

반응형