17장. 장기 기억 관리
기억의 세 층과 고객 프로필 — 대화가 끝나도 남는 것
한 줄 요약
우리 상담원의 기억은 세 층입니다. 최근 대화의 원문(단기), 같은 대화 안의 요약(중기), 그리고 대화가 끝난 뒤에도 파일로 남는 고객 프로필(장기)입니다. 층마다 저장하는 방식이 다릅니다.
1. 세 층
| 층 | 우리 코드에서 | 담긴 것 | 수명 | 사람으로 치면 |
|---|---|---|---|---|
| 단기 | recent (16장의 history) |
대화 원문 그대로 | 대화 안 | 방금 나눈 말 |
| 중기 | summary |
오래된 턴의 요약 | 대화 안 | 아까 한 이야기의 줄거리 |
| 장기 | customer_profiles.json |
대화에서 뽑은 사실 | 대화가 끝난 뒤에도 | 그 사람에 대해 아는 것 |
2. 층마다 모양이 다른 이유
단기는 원문이어야 합니다. 바로 앞의 말에 나온 "그거"를 해석하려면 세부가 필요합니다.
중기는 요약이면 됩니다. 열 턴 전의 대화에서 필요한 것은 문장 하나하나가 아니라 줄거리입니다.
장기는 정해진 틀에 담긴 사실이어야 합니다. 다음 방문에서 쓰려면 "선호: 이메일 연락"처럼 항목이 나뉘어 있어야 합니다. 대화 원문 뭉치는 다음 주에 꺼내 쓰기 어렵습니다.
그래서 프로필은 세 칸짜리 JSON으로 정했습니다.
{"선호": [], "진행중_이슈": [], "메모": []}
| 칸 | 담는 것 | 예 |
|---|---|---|
선호 |
이 고객이 바라는 응대 방식 | 문자보다 이메일 연락 선호 |
진행중_이슈 |
아직 끝나지 않은 일 | 운동화 260 사이즈 교환 진행 중 |
메모 |
그 밖에 알아 두면 좋은 것 | 오늘 교환 신청 예정 |
이 세 칸은 개발자가 정한 틀입니다. 칸에 무엇을 채울지는 모델이 대화를 읽고 고릅니다.
3. 저장 — 대화가 끝날 때 사실을 뽑는다
save_profile은 대화 전체(요약 + 최근 원문)를 모델에게 주고 프로필을 뽑게 합니다.
r = client.models.generate_content(
model=MODEL,
contents="다음 상담에서, 이 고객을 다시 응대할 때 유용한 사실만 JSON 으로 추출하라.\n"
'형식: {"선호": [], "진행중_이슈": [], "메모": []}\n'
"카드번호·계좌번호·비밀번호 등 민감정보는 절대 포함하지 마라.\n\n"
+ transcript,
config=types.GenerateContentConfig(
temperature=1.0, response_mime_type="application/json"),
)
response_mime_type="application/json"은 4장에서 쓴 JSON 응답 모드입니다. 프로필은 코드가 읽고 쓰는 데이터이므로 자유로운 문장이 아니라 JSON이어야 합니다.
뽑은 프로필은 고객 ID를 열쇠로 삼아 파일에 넣습니다.
profiles = {}
if PROFILE_PATH.exists():
profiles = json.loads(PROFILE_PATH.read_text(encoding="utf-8"))
profiles[customer_id] = json.loads(r.text)
PROFILE_PATH.parent.mkdir(exist_ok=True)
PROFILE_PATH.write_text(json.dumps(profiles, ensure_ascii=False, indent=2),
encoding="utf-8")
| 줄 | 하는 일 |
|---|---|
PROFILE_PATH |
haru-market/memory_store/customer_profiles.json. 고객 여러 명의 프로필이 이 파일 하나에 들어갑니다 |
profiles[customer_id] = ... |
이 고객의 프로필을 통째로 새것으로 바꿉니다. 다른 고객의 프로필은 그대로 둡니다 |
mkdir(exist_ok=True) |
memory_store 폴더가 없으면 만듭니다 |
두 번째 줄을 기억해 두세요. 같은 고객의 프로필을 다시 저장하면 앞의 내용에 보태는 것이 아니라 덮어씁니다. 「따라하기」에서 다시 실행할 때 이 동작을 확인합니다.
4. 복원 — 다음 방문 때 시스템 프롬프트에 넣는다
재방문한 고객의 프로필을 읽어 시스템 프롬프트 끝에 붙입니다.
profile = load_profile(cid)
profile_txt = json.dumps(profile, ensure_ascii=False)
system2 = (SYSTEM_BASE
+ "\n\n[이 고객에 대한 기억] 지난 상담에서 적어 둔 기록입니다. "
"현재 처리 상태는 아니므로, 지난 상담 내용으로만 말하고 "
"현재 상태는 확인 후 안내드린다고 답합니다.\n"
+ profile_txt)
모델에게 따로 "기억" 기능이 생긴 것이 아닙니다. 3장에서 배운 대로 글을 넣어 준 것입니다. 역할과 규칙 뒤에 "이 고객에 대해 알고 있는 것"이 한 덩어리 더 붙었을 뿐입니다.
프로필 앞에 붙인 한 문장을 봅니다. "지난 상담에서 적어 둔 기록입니다. 현재 처리 상태는 아니므로 …" 프로필은 적어 둔 날의 사실입니다. 일주일 뒤의 상태가 아닙니다. 그 성격을 함께 적어 주면 상담원은 지난 일은 지난 일로 말하고, 지금의 상태는 확인 후 안내하겠다고 답합니다.
| 일 | 누가 |
|---|---|
| 프로필의 틀(세 칸)을 정한다 | 개발자 |
| 대화에서 사실을 골라 칸에 채운다 | 모델 |
| 파일에 쓰고, 다음에 읽어 온다 | 우리 코드 |
| 읽어 온 프로필을 프롬프트의 어디에 넣을지 정한다 | 개발자 |
| 프로필을 읽고 응대에 반영한다 | 모델 |
핵심 정리
- 기억은 단기(원문), 중기(요약), 장기(프로필) 세 층입니다.
- 층마다 모양이 다릅니다. 가까운 것은 자세하게, 먼 것은 줄여서, 오래 둘 것은 틀에 맞춰 담습니다.
- 프로필은 대화가 끝날 때 JSON 모드로 뽑아 고객 ID별로 파일에 저장합니다.
- 같은 고객을 다시 저장하면 덮어씁니다.
- 재방문 때는 프로필을 시스템 프롬프트에 붙여 넣습니다. 기억도 결국 프롬프트입니다.