Recent Posts
Recent Comments
반응형
«   2026/02   »
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
Archives
Today
Total
관리 메뉴

오늘도 공부

3. session_notes로 공부하는 AI 에이전트 세션 관리 본문

AI/Agent

3. session_notes로 공부하는 AI 에이전트 세션 관리

행복한 수지아빠 2026. 2. 23. 11:22
반응형

 

 

GitHub - MiniMax-AI/Mini-Agent: A minimal yet professional single agent demo project that showcases the core execution pipeline

A minimal yet professional single agent demo project that showcases the core execution pipeline and production-grade features of agents. - MiniMax-AI/Mini-Agent

github.com

 

"""Example 3: Session Note Tool Usage

This example demonstrates the Session Note Tool - one of the core features
that allows agents to maintain memory across sessions.

Based on: tests/test_note_tool.py, tests/test_integration.py
"""

import asyncio
import json
import tempfile
from pathlib import Path

from mini_agent import LLMClient
from mini_agent.agent import Agent
from mini_agent.config import Config
from mini_agent.tools import BashTool, ReadTool, WriteTool
from mini_agent.tools.note_tool import RecallNoteTool, SessionNoteTool


async def demo_direct_note_usage():
    """Demo: Direct usage of Session Note tools."""
    print("\n" + "=" * 60)
    print("Demo 1: Direct Session Note Tool Usage")
    print("=" * 60)

    with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f:
        note_file = f.name

    try:
        # Create tools
        record_tool = SessionNoteTool(memory_file=note_file)
        recall_tool = RecallNoteTool(memory_file=note_file)

        # Record some notes
        print("\n📝 Recording notes...")

        result = await record_tool.execute(
            content="User is a Python developer working on agent systems",
            category="user_info",
        )
        print(f"  ✓ {result.content}")

        result = await record_tool.execute(
            content="Project name: mini-agent, Tech: Python 3.12 + async",
            category="project_info",
        )
        print(f"  ✓ {result.content}")

        result = await record_tool.execute(
            content="User prefers concise, well-documented code",
            category="user_preference",
        )
        print(f"  ✓ {result.content}")

        # Recall all notes
        print("\n🔍 Recalling all notes...")
        result = await recall_tool.execute()
        print(result.content)

        # Recall filtered notes
        print("\n🔍 Recalling user preferences only...")
        result = await recall_tool.execute(category="user_preference")
        print(result.content)

        # Show the memory file
        print("\n📄 Memory file content:")
        print("=" * 60)
        notes = json.loads(Path(note_file).read_text())
        print(json.dumps(notes, indent=2, ensure_ascii=False))
        print("=" * 60)

    finally:
        Path(note_file).unlink(missing_ok=True)


async def demo_agent_with_notes():
    """Demo: Agent using Session Notes to remember context."""
    print("\n" + "=" * 60)
    print("Demo 2: Agent with Session Memory")
    print("=" * 60)

    # Load configuration
    config_path = Path("mini_agent/config/config.yaml")
    if not config_path.exists():
        print("❌ config.yaml not found")
        return

    config = Config.from_yaml(config_path)

    if not config.llm.api_key or config.llm.api_key.startswith("YOUR_"):
        print("❌ API key not configured")
        return

    with tempfile.TemporaryDirectory() as workspace_dir:
        print(f"📁 Workspace: {workspace_dir}\n")

        # Load system prompt (Agent will auto-inject workspace info)
        system_prompt_path = Path("mini_agent/config/system_prompt.md")
        if system_prompt_path.exists():
            system_prompt = system_prompt_path.read_text(encoding="utf-8")
        else:
            system_prompt = "You are a helpful AI assistant."

        # Add Session Note instructions
        note_instructions = """

IMPORTANT - Session Note Management:
You have access to record_note and recall_notes tools. Use them to:
- record_note: Save important facts, preferences, decisions that should persist
- recall_notes: Retrieve previously saved notes

Guidelines:
- Proactively record key information during conversations
- Recall notes at the start to restore context
- Categories: user_info, user_preference, project_info, decision, etc.
"""
        system_prompt += note_instructions

        # Initialize LLM
        llm_client = LLMClient(
            api_key=config.llm.api_key,
            api_base=config.llm.api_base,
            model=config.llm.model,
        )

        # Memory file
        memory_file = Path(workspace_dir) / ".agent_memory.json"

        # Tools including Session Note tools
        tools = [
            ReadTool(workspace_dir=workspace_dir),
            WriteTool(workspace_dir=workspace_dir),
            BashTool(),
            SessionNoteTool(memory_file=str(memory_file)),
            RecallNoteTool(memory_file=str(memory_file)),
        ]

        # === First Session ===
        print("=" * 60)
        print("Session 1: Teaching the agent about user preferences")
        print("=" * 60)

        agent1 = Agent(
            llm_client=llm_client,
            system_prompt=system_prompt,
            tools=tools,
            max_steps=15,
            workspace_dir=workspace_dir,
        )

        task1 = """
        Hello! Let me introduce myself:
        - I'm Alex, a senior Python developer
        - I'm building an AI agent framework called "mini-agent"
        - I use Python 3.12 with asyncio
        - I prefer type hints and comprehensive docstrings
        - My coding style: clean, functional, well-tested

        Please remember this information for future conversations.
        Also, create a simple README.md file acknowledging you understood.
        """

        print(f"\n📝 User message:\n{task1}\n")
        print("🤖 Agent is working...\n")

        agent1.add_user_message(task1)

        try:
            result1 = await agent1.run()
            print("\n" + "=" * 60)
            print("Agent response:")
            print("=" * 60)
            print(result1)
            print("=" * 60)

            # Check memory file
            if memory_file.exists():
                notes = json.loads(memory_file.read_text())
                print(f"\n✅ Agent recorded {len(notes)} notes in memory")
                for note in notes:
                    print(f"  - [{note['category']}] {note['content'][:50]}...")
            else:
                print("\n⚠️  No notes found")

        except Exception as e:
            print(f"❌ Error: {e}")
            return

        # === Second Session (New Agent Instance) ===
        print("\n\n" + "=" * 60)
        print("Session 2: New agent instance (simulating new conversation)")
        print("=" * 60)

        agent2 = Agent(
            llm_client=llm_client,
            system_prompt=system_prompt,
            tools=tools,
            max_steps=10,
            workspace_dir=workspace_dir,
        )

        task2 = """
        Hello! I'm back. Do you remember who I am and what project I'm working on?
        What were my code style preferences?
        """

        print(f"\n📝 User message:\n{task2}\n")
        print("🤖 Agent is working (should recall previous notes)...\n")

        agent2.add_user_message(task2)

        try:
            result2 = await agent2.run()
            print("\n" + "=" * 60)
            print("Agent response:")
            print("=" * 60)
            print(result2)
            print("=" * 60)

            print("\n✅ Session Note Demo completed!")
            print("\nKey Points:")
            print("  1. Agent in Session 1 recorded important information")
            print("  2. Agent in Session 2 recalled previous notes")
            print("  3. Memory persists across agent instances via file")

        except Exception as e:
            print(f"❌ Error: {e}")


async def main():
    """Run all demos."""
    print("=" * 60)
    print("Session Note Tool Examples")
    print("=" * 60)
    print("\nSession Notes allow agents to remember context across sessions.")
    print("This is a key feature for building production-ready agents.\n")

    # Run demos
    await demo_direct_note_usage()
    print("\n" * 2)
    await demo_agent_with_notes()

    print("\n" + "=" * 60)
    print("All demos completed! ✅")
    print("=" * 60)


if __name__ == "__main__":
    asyncio.run(main())

03_session_notes.py 해설: “기억”을 도구로 만들면 무엇이 달라지나

이 글은 examples/03_session_notes.py를 기반으로, Mini Agent에서 “기억(memory)”을 구현하는 가장 단순하고 실용적인 방법을 설명합니다.
핵심은 한 문장입니다.

대화 히스토리(컨텍스트)와 별개로, 중요한 정보를 파일에 저장/조회하는 도구를 제공하면 “세션을 넘어가는 기억”을 만들 수 있다.

TL;DR

  • SessionNoteTool(record_note)과 RecallNoteTool(recall_notes)는 JSON 파일을 백엔드로 쓰는 지속 메모리입니다.
    1. 도구를 직접 써보는 데모, 2) Agent에게 메모리를 사용하게 하는 데모, 두 파트로 구성됩니다.
  • “메모리가 있다”는 건 자동이 아니라, Agent가 recall을 호출하도록 프롬프트/정책을 설계해야 합니다.

실행 방법

이 파일은 두 개의 데모를 실행합니다.

uv run python examples/03_session_notes.py

주의:

  • Demo 1(직접 도구 사용)은 API 키가 필요 없습니다.
  • Demo 2(Agent 사용)은 mini_agent/config/config.yaml과 API 키가 필요합니다.

Demo 1: 도구를 직접 호출해 “기억 저장/조회 계약” 확인하기

demo_direct_note_usage()는 LLM도 Agent도 없이, 노트 도구를 직접 호출합니다.

1) 저장(record_note)

  • 입력: content, category
  • 출력: ToolResult(success=True/False, content/error)

여기서 중요한 건 “content는 자유 텍스트지만, category는 구조화의 시작점”이라는 점입니다.
카테고리를 설계해두면, 나중에 recall 시에 정보량을 조절하기가 쉬워집니다.

2) 조회(recall_notes)

  • 입력: (선택) category
  • 출력: 사람이 읽기 좋은 문자열로 포맷팅된 노트 목록

실무 관점 팁:

  • 반환을 JSON으로 주는 것보다 “사람이 읽기 쉬운 텍스트”가 모델에게는 더 잘 먹히는 경우가 많습니다.
  • 다만 운영 환경에서는 감사(audit)나 파이프라인 처리용으로 JSON도 함께 제공하는 설계가 자주 필요합니다.

3) 파일 백엔드(JSON)의 의미

이 예제는 임시 .json 파일을 사용합니다.

  • 장점: 구현이 단순하고 디버깅이 쉽습니다(파일 열면 끝).
  • 단점: 검색/중복/동시성/보안 같은 문제가 커지면 DB/벡터DB로 발전이 필요합니다.

이 프로젝트의 Production Guide에서도 “데모 레벨 구현”임을 명확히 말합니다.

Demo 2: Agent에 메모리를 붙여 “세션을 넘어 기억하기”

demo_agent_with_notes()는 두 개의 Agent 인스턴스를 만들어 “세션 1에서 기록 → 세션 2에서 회상”을 시뮬레이션합니다.

핵심 구성 요소

(1) 메모리 파일 경로를 고정한다

memory_file = Path(workspace_dir) / ".agent_memory.json"

Agent 인스턴스가 바뀌더라도 동일한 파일을 바라보면 “세션 간 기억”이 됩니다.
즉, 기억의 실체는 Agent 객체가 아니라 파일(스토리지) 입니다.

(2) tools에 record/recall을 함께 넣는다

tools = [
  ...,
  SessionNoteTool(memory_file=str(memory_file)),
  RecallNoteTool(memory_file=str(memory_file)),
]

Agent는 tools 목록에 있는 것만 호출할 수 있습니다.
따라서 “기억을 쓰게 하고 싶다”는 요구는 결국 tools에 넣는 것으로 시작합니다.

(3) 시스템 프롬프트로 “언제 기록/회상할지” 정책을 심는다

예제는 system prompt 뒤에 아래와 같은 가이드를 덧붙입니다.

  • “대화 시작 시 recall을 호출해 컨텍스트 복원”
  • “중요한 사실/선호/결정을 기록”
  • “category 사용”

여기서 중요한 현실적인 포인트:

모델은 지시를 따르려고 하지만 100% 보장은 아닙니다.

운영 수준에서는 아래 같은 장치를 추가하는 경우가 많습니다.

  • 매 턴 시작 시 “자동 recall”을 런타임에서 강제 실행(도구 호출을 모델 판단에 맡기지 않음)
  • 또는 “답변하기 전에 반드시 recall하고 그 결과를 요약해라” 같은 강한 규칙을 프롬프트에 넣기

세션 1: “가르치기(teach)”

첫 번째 Agent(agent1)에게:

  • 사용자 소개(이름/프로젝트/기술/선호)
  • “기억해라” 요청
  • README 생성 요청

을 한번에 줍니다. 좋은 실험 포인트는 “이 복합 요구에서 모델이 무엇을 ‘기억해야 할 정보’로 선택하는가”입니다.

세션 2: “회상하기(recall)”

두 번째 Agent(agent2)는 새 인스턴스입니다.
그런데 tools와 memory_file이 동일하므로, recall_notes만 호출하면 세션 1의 정보가 복원됩니다.

여기서 학습할 mental model은:

  • 대화 히스토리는 Agent 객체 내부에 있고(프로세스 메모리)
  • 노트는 파일에 있어(스토리지)
  • 새 Agent는 대화 히스토리는 없지만, 파일을 통해 “기억”은 가져올 수 있다

“기억”과 “요약(summarization)”은 다른 문제다

Mini Agent에는 대화가 길어질 때 히스토리를 요약하는 로직도 있습니다(mini_agent/agent.py).
하지만 요약은 어디까지나 현재 대화 기록을 압축하는 것이고, Session Notes는 의도적으로 남기는 장기 기억입니다.

 

둘의 차이를 명확히 구분하면 설계가 쉬워집니다.

  • 요약: 컨텍스트 윈도우 관리(기술적 제약 대응)
  • 노트: 중요한 사실의 영속화(제품 기능)

실습 과제(공부용)

  1. “회상을 강제”하는 프롬프트 만들기
    사용자 메시지에 “답변하기 전에 recall_notes를 반드시 호출하고, 반환을 먼저 요약해라”를 넣어보세요.
  2. category 설계 실험
    user_info, project_info, decision, todo 같은 카테고리를 정하고, recall을 category별로 분리해보세요.
  3. 메모리 파일 위치 바꾸기
    memory_file을 workspace 바깥으로 빼면(예: ~/.mini-agent/...) “진짜 세션 간 기억”의 느낌을 더 강하게 체감할 수 있습니다.

다음 글 안내

다음 문서인 examples/docs/04_full_agent.md에서는:

  • 기본 도구 + 세션 노트 + MCP 도구(선택)

를 모두 묶어 “작은 데모에서 운영형 구조로” 확장하는 관점을 다룹니다.

반응형