Agentic AI/Memory

16. 장기기억(12)- PostgreSQL의 memories 테이블과 ChromaDB연결 단계(4.3)

아톨 2026. 9. 12. 20:33
반응형

지금 구조를 보면:

LLM이 Memory를 추출 → save_memory() → PostgreSQL 저장 → memory_id 확보 → save_memories()가 실제 저장 결과를 반환

까지 검증됐습니다.

이제 다음 단계는 자연스럽게 PostgreSQL에 저장된 새 Memory를 ChromaDB에도 넣는 것입니다.

 

[요약]

# PostgreSQL의 memories 테이블과 ChromaDB연결 단계(4.3)
=> agent_with_memory.py->PostgreSQL 저장 후 ChromaDB에도 저장하도록 연결
# step 1. agent_with_memory.py를 수정
# 1.1. from vector_store import add_memories 추가하기
# 1.2. run_once()의 마지막 부분을 통째로 교체하기
# step 2. 테스트
# 2.1. python agent_with_memory.py
    ===> 질문: 나는 앞으로 AI Agent 공부를 계속할 생각이야.
               (장기적으로 유지될 가능성이 있는 내용)
# 2.2. 첫 번째 실행이 성공했다면 같은 프로그램을 다시 실행
    ===> 질문: 내가 요즘 공부하고 있는 분야가 뭐였지?
              (첫 번째 질문과 의미적으로 관련된 질문)


다음 단계 — PostgreSQL + ChromaDB 이중 저장

현재 구조는 약간 이런 상태입니다.

사용자 대화
    │
    ▼
extract_memory()
    │
    ▼
save_memories()
    │
    ▼
PostgreSQL
    │
    └── memory_id 생성
 

그런데 Agent가 다음 대화에서 새로 저장된 기억까지 검색하려면:

사용자 대화
    │
    ▼
extract_memory()
    │
    ▼
save_memories()
    │
    ├──────────────► PostgreSQL
    │                 │
    │                 └── memory_id
    │
    └──────────────► ChromaDB
                      │
                      └── Vector Index
 

이렇게 되어야 합니다.

즉, 이번 단계의 핵심은 agent_with_memory.py에서 두 DB에 연속으로 저장하는 것입니다.


1. 먼저 중요한 설계 원칙

여기서 한 가지를 확실히 이해하고 넘어가면 좋습니다.

우리는

PostgreSQL과 ChromaDB를 동등한 DB로 사용하는 것

이 아닙니다.

역할이 다릅니다.

PostgreSQL

원본(Source of Truth)

memory_id
user_id
memory_type
content
importance
created_at
 

을 정확하게 보관합니다.

ChromaDB

검색용 Index

memory_39
memory_40
...
 

같은 ID와 embedding을 가지고

"이 질문과 의미적으로 가까운 기억이 무엇인가?"

를 빠르게 찾는 역할입니다.

따라서 앞으로 문제가 생기더라도

PostgreSQL = 원본
ChromaDB   = 검색 인덱스
 

라는 원칙을 유지하는 것이 좋습니다.


2. 이제 agent_with_memory.py를 수정합니다

현재 agent_with_memory.py에는 이런 import가 있습니다.

 
from memory import (
    extract_memory,
    save_memories,
    search_memory,
    build_memory_context,
)
 

여기에 ChromaDB 저장 함수를 추가합니다.

맨 위에 다음을 추가하세요.

 
from vector_store import add_memories
 

따라서 import 부분은 이렇게 됩니다.

 
import os
from dotenv import load_dotenv
from openai import OpenAI

from database import (
    connect_db,
    get_or_create_user,
)

from memory import (
    extract_memory,
    save_memories,
    search_memory,
    build_memory_context,
)

from vector_store import add_memories
 

3. 가장 중요한 부분을 수정합니다

현재 run_once()의 마지막 부분이 아마 이렇게 되어 있을 겁니다.

 
if new_memories:
    print(f"☑️ 새로운 Memory {len(new_memories)}개 발견")
    save_memories(
        conn,
        user_id,
        new_memories,
    )
else:
    print("☑️ 새롭게 저장할 Memory가 없습니다.")
 

이 부분을 통째로 아래 코드로 교체하세요.

 
if new_memories:

    print(
        f"☑️ 새로운 Memory "
        f"{len(new_memories)}개 발견"
    )

    # --------------------------------------------------------
    # 1. PostgreSQL에 저장
    # --------------------------------------------------------
    saved_memories = save_memories(
        conn,
        user_id,
        new_memories,
    )

    print(
        f"☑️ PostgreSQL에 "
        f"{len(saved_memories)}개 저장 완료"
    )

    # --------------------------------------------------------
    # 2. 실제 PostgreSQL 저장 결과를 ChromaDB에 저장
    # --------------------------------------------------------
    if saved_memories:

        print(
            "\n🔎 새로운 Memory를 "
            "ChromaDB에 동기화하는 중..."
        )

        add_memories(
            saved_memories
        )

        print(
            f"☑️ ChromaDB에 "
            f"{len(saved_memories)}개 동기화 완료"
        )

else:

    print(
        "☑️ 새롭게 저장할 Memory가 없습니다."
    )
 

4. 왜 new_memories가 아니라 saved_memories를 사용하는가?

이 부분이 이번 단계에서 가장 중요한 개념입니다.

LLM이 만들어 준 결과:

 
new_memories
 

는 아직 DB에 저장되지 않은 데이터입니다.

예를 들어 LLM이 이런 결과를 만들었다고 합시다.

 
new_memories = [
    {
        "memory_type": "interest",
        "content": "AI Agent 개발에 관심이 있다.",
        "importance": 8,
    }
]
 

아직 memory_id가 없습니다.


그런데 PostgreSQL에 저장하면:

memory_id = 41
 

이 생성됩니다.

그래서 save_memories()의 결과는:

 
saved_memories = [
    {
        "memory_id": 41,
        "user_id": 8,
        "memory_type": "interest",
        "content": "AI Agent 개발에 관심이 있다.",
        "importance": 8,
    }
]
 

가 됩니다.

이제야 ChromaDB에 넣을 수 있습니다.


5. ChromaDB에는 왜 memory_id가 필요한가?

현재 vector_store.py에서 Chroma ID를 이렇게 만들고 있습니다.

 
chroma_id = f"memory_{memory_id}"
 

예를 들어:

PostgreSQL
memory_id = 41
 

이면

ChromaDB
memory_41
 

가 됩니다.

이 관계가 상당히 중요합니다.

PostgreSQL              ChromaDB
─────────────────       ─────────────
memory_id = 41    ←→    memory_41
memory_id = 42    ←→    memory_42
memory_id = 43    ←→    memory_43
 

그러면 나중에 ChromaDB 검색 결과가:

memory_41
 

이라고 나오더라도

"아, PostgreSQL의 memory_id 41번이구나."

라고 연결할 수 있습니다.


6. 현재 전체 흐름은 이렇게 됩니다

이제 Agent의 run_once()가 다음 순서로 움직입니다.

                    사용자 질문
                        │
                        ▼
              ┌─────────────────┐
              │ search_memory() │
              └────────┬────────┘
                       │
                       ▼
                  ChromaDB
                       │
                       ▼
              관련 Memory 검색
                       │
                       ▼
              build_memory_context()
                       │
                       ▼
                    LLM
                       │
                       ▼
                    답변
                       │
                       ▼
               extract_memory()
                       │
                       ▼
                새로운 Memory
                       │
                       ▼
                save_memories()
                       │
                       ▼
                 PostgreSQL
                       │
                       │ memory_id 생성
                       ▼
               saved_memories
                       │
                       ▼
                 add_memories()
                       │
                       ▼
                  ChromaDB
 

이제 상당히 AI Agent다운 구조가 됩니다.


7. agent_with_memory.py 전체 코드

혼선을 방지하기 위해 현재 단계에서는 전체 파일을 이렇게 맞춰 놓는 것을 추천합니다.

 
# ============================================================
# agent_with_memory.py
# ChromaDB 기반 장기기억 AI Agent
# ============================================================

import os

from dotenv import load_dotenv
from openai import OpenAI

from database import (
    connect_db,
    get_or_create_user,
)

from memory import (
    extract_memory,
    save_memories,
    search_memory,
    build_memory_context,
)

from vector_store import add_memories


# ============================================================
# 1. 환경 설정
# ============================================================

load_dotenv()

MODEL_NAME = os.getenv(
    "MODEL_NAME",
    "gpt-4o-mini",
)

client = OpenAI()


# ============================================================
# 2. System Prompt
# ============================================================

SYSTEM_PROMPT = """
당신은 사용자의 장기기억을 참고하여 답변하는 AI Agent입니다.

사용자의 장기기억이 제공되면 답변에 도움이 되는 경우
자연스럽게 활용하세요.

단, 장기기억에 없는 내용을 기억하고 있다고 주장하지 마세요.

기억이 불확실하면 솔직하게 모른다고 답하세요.

답변은 친절하고 이해하기 쉽게 작성하세요.
"""


# ============================================================
# 3. LLM 호출
# ============================================================

def call_model(
    user_message,
    memory_context,
):

    prompt = f"""
[사용자의 장기기억]
{memory_context}

[현재 사용자 질문]
{user_message}
"""

    response = client.responses.create(
        model=MODEL_NAME,
        instructions=SYSTEM_PROMPT,
        input=prompt,
    )

    return response.output_text


# ============================================================
# 4. Agent 1회 실행
# ============================================================

def run_once(
    conn,
    user_id,
    user_message,
):

    # --------------------------------------------------------
    # STEP 1. 관련 장기기억 검색
    # --------------------------------------------------------

    print(
        "\n🧠 관련 장기기억 검색 중..."
    )

    memories = search_memory(
        query=user_message,
        user_id=user_id,
        limit=5,
    )

    print(
        f"☑️ 관련 Memory "
        f"{len(memories)}개 검색 완료"
    )


    # --------------------------------------------------------
    # STEP 2. Memory → LLM Context
    # --------------------------------------------------------

    memory_context = build_memory_context(
        memories
    )


    # --------------------------------------------------------
    # STEP 3. LLM 답변 생성
    # --------------------------------------------------------

    print(
        "\n🤖 AI가 답변을 생성하는 중..."
    )

    answer = call_model(
        user_message=user_message,
        memory_context=memory_context,
    )


    # --------------------------------------------------------
    # STEP 4. 새로운 Memory 추출
    # --------------------------------------------------------

    print(
        "\n🧠 새로운 Memory 추출 중..."
    )

    conversation = (
        f"사용자: {user_message}\n"
        f"AI: {answer}"
    )

    new_memories = extract_memory(
        conversation
    )


    # --------------------------------------------------------
    # STEP 5. 새로운 Memory 저장
    # --------------------------------------------------------

    if new_memories:

        print(
            f"☑️ 새로운 Memory "
            f"{len(new_memories)}개 발견"
        )


        # ----------------------------------------------------
        # 5-1. PostgreSQL 저장
        # ----------------------------------------------------

        saved_memories = save_memories(
            conn,
            user_id,
            new_memories,
        )

        print(
            f"☑️ PostgreSQL에 "
            f"{len(saved_memories)}개 저장 완료"
        )


        # ----------------------------------------------------
        # 5-2. ChromaDB 동기화
        # ----------------------------------------------------

        if saved_memories:

            print(
                "\n🔎 새로운 Memory를 "
                "ChromaDB에 동기화하는 중..."
            )

            add_memories(
                saved_memories
            )

            print(
                f"☑️ ChromaDB에 "
                f"{len(saved_memories)}개 "
                f"동기화 완료"
            )


    else:

        print(
            "☑️ 새롭게 저장할 Memory가 없습니다."
        )


    # --------------------------------------------------------
    # STEP 6. 답변 반환
    # --------------------------------------------------------

    return answer


# ============================================================
# 5. Main
# ============================================================

def main():

    print("=" * 70)

    print(
        "ChromaDB 기반 장기기억 AI Agent"
    )

    print("=" * 70)


    # --------------------------------------------------------
    # PostgreSQL 연결
    # --------------------------------------------------------

    conn = connect_db()


    try:

        # ----------------------------------------------------
        # 사용자 조회 / 생성
        # ----------------------------------------------------

        user_id = get_or_create_user(
            conn,
            user_name="스티븐김",
            location="대한민국",
            preferences="Python과 AI Agent 학습",
        )


        print(
            f"\n☑️ 사용자 ID: {user_id}"
        )


        # ----------------------------------------------------
        # 사용자 질문 입력
        # ----------------------------------------------------

        user_message = input(
            "\n사용자 질문: "
        )


        # ----------------------------------------------------
        # Agent 실행
        # ----------------------------------------------------

        answer = run_once(
            conn=conn,
            user_id=user_id,
            user_message=user_message,
        )


        # ----------------------------------------------------
        # 최종 답변 출력
        # ----------------------------------------------------

        print(
            "\n" + "=" * 70
        )

        print(
            "🤖 AI 답변"
        )

        print(
            "=" * 70
        )

        print(answer)

        print(
            "=" * 70
        )


    finally:

        conn.close()

        print(
            "\n📴 PostgreSQL 연결 종료"
        )


# ============================================================
# 6. 프로그램 시작점
# ============================================================

if __name__ == "__main__":

    main()
 

8. 이번에는 바로 Agent를 실행하지 맙시다

이번 단계에서는 두 가지를 확인하는 것이 좋습니다.

먼저 1차 확인

agent_with_memory.py를 수정한 뒤:

 
python agent_with_memory.py
 

실행하세요.

질문은 예를 들어:

나는 앞으로 AI Agent 공부를 계속할 생각이야.
 

처럼 장기적으로 유지될 가능성이 있는 내용을 넣어보세요.

그러면 대략:

🧠 관련 장기기억 검색 중...
☑️ 관련 Memory 5개 검색 완료

🤖 AI가 답변을 생성하는 중...

🧠 새로운 Memory 추출 중...
☑️ 새로운 Memory 1개 발견

🧠기억 저장 완료 (memory_id=41)

☑️ PostgreSQL에 1개 저장 완료

🔎 새로운 Memory를 ChromaDB에 동기화하는 중...
☑️ ChromaDB에 1개 동기화 완료
 

같은 흐름이 나오면 됩니다.

단, 실제 memory_id나 Memory 개수는 달라져도 정상입니다.


9. 그리고 두 번째 테스트가 중요합니다

첫 번째 실행이 성공했다면 같은 프로그램을 다시 실행합니다.

이번에는 첫 번째 질문과 의미적으로 관련된 질문을 해보세요.

예:

내가 요즘 공부하고 있는 분야가 뭐였지?
 

그러면 처음 단계에서:

🧠 관련 장기기억 검색 중...
 

하면서 방금 ChromaDB에 추가한 Memory까지 검색되는지 확인합니다.

이것이 성공하면 우리가 처음부터 만들고 있던 구조가 실제로 연결됩니다.

① 기억 추출
   ↓
② PostgreSQL 저장
   ↓
③ memory_id 생성
   ↓
④ ChromaDB 저장
   ↓
⑤ 다음 질문
   ↓
⑥ ChromaDB 의미검색
   ↓
⑦ 방금 저장한 기억 검색
   ↓
⑧ LLM 답변에 활용
 

🎯 지금 단계의 목표

지금은 더 복잡하게 만들 필요가 없습니다.

이번 단계의 목표는 딱 하나입니다.

"새로 만들어진 Memory가 PostgreSQL과 ChromaDB 양쪽에 정상적으로 저장되고, 다음 질문에서 ChromaDB 검색을 통해 다시 사용되는가?"

이것만 확인합시다.

그리고 여기까지 성공하면 다음 단계에서는 상당히 재미있는 부분으로 넘어갈 수 있습니다.

PostgreSQL → ChromaDB를 수동으로 sync하는 구조에서 벗어나, Memory 저장 과정 자체가 자동으로 두 저장소를 관리하도록 구조를 한 단계 정리할 수 있습니다.

 

[ 결과 예시]

### [1]

======================================================================
ChromaDB 기반 장기기억 AI Agent
======================================================================
☑️ PostgreSQL 연결 성공

☑️ 사용자 ID: 8

사용자 질문: 나는 앞으로 AI Agent 공부를 계속할 생각이야.

🧠 관련 장기기억 검색 중...
☑️ 관련 Memory 5개 검색 완료

🤖 AI가 답변을 생성하는 중...

🧠 새로운 Memory 추출 중...


======================================================================
🧠 LLM Memory Extraction 결과
======================================================================
[
    {
        "memory_type": "goal",
        "content": "사용자는 앞으로 AI Agent 공부를 계속할 생각이다.",
        "importance": 9
    },
    {
        "memory_type": "project",
        "content": "사용자는 자동으로 저작권 안내 전화를 걸고 구매 요청을 하는 AI Agent 프로젝트를 진행 중이다.",
        "importance": 8
    }
]
☑️ 새로운 Memory 2개 발견
🧠기억 저장 완료 (memory_id=42)
🧠기억 저장 완료 (memory_id=43)
☑️ PostgreSQL에 2개 저장 완료

🔎 새로운 Memory를 ChromaDB에 동기화하는 중...
☑️ChromaDB에 2개 기억 저장 완료
☑️ ChromaDB에 2개 동기화 완료

======================================================================
🤖 AI 답변
======================================================================
AI Agent 공부를 계속하는 건 정말 좋은 결정이에요! Python으로 AI Agent를 개발하고 있다는 점이 인상적입니다. 자동으로 저작권 안내 전화를 걸고 구매 요청을 하는 프로젝트도 매우 유용해 보이네요. 공부하면서 어떤 부분이 특히 어렵거나 궁금한지, 아니면 어떤 주제에 대해 더 알고 싶으신지 말씀해 주시면 도와드릴 수 있을 거예요!
======================================================================

📴 PostgreSQL 연결 종료

 

### [2]

======================================================================
ChromaDB 기반 장기기억 AI Agent
======================================================================
☑️ PostgreSQL 연결 성공

☑️ 사용자 ID: 8

사용자 질문: 내가 요즘 공부하고 있는 분야가 뭐였지?

🧠 관련 장기기억 검색 중...
☑️ 관련 Memory 5개 검색 완료

🤖 AI가 답변을 생성하는 중...

🧠 새로운 Memory 추출 중...


======================================================================
🧠 LLM Memory Extraction 결과
======================================================================
[
    {
        "memory_type":"interest",
        "content":"사용자는 Python을 이용하여 자동으로 저작권 안내 전화를 걸고 구매 요청을 하는 AI를 만드는 분야에 관심이 있다.",
        "importance":8
    }
]
☑️ 새로운 Memory 1개 발견
🧠기억 저장 완료 (memory_id=44)
☑️ PostgreSQL에 1개 저장 완료

🔎 새로운 Memory를 ChromaDB에 동기화하는 중...
☑️ChromaDB에 1개 기억 저장 완료
☑️ ChromaDB에 1개 동기화 완료

======================================================================
🤖 AI 답변
======================================================================
현재 공부하고 있는 분야는 자동으로 저작권 안내 전화를 걸고 구매 요청을 하는 AI를 만드는 것과 관련된 Python 프로그래밍입니다. 이와 관련하여 특정 기술이나 알고리즘에 대해 더 알고 싶으신 점이 있나요?
======================================================================

📴 PostgreSQL 연결 종료

반응형