--- title: "Chapter 3: Memory and Session (Persistent Conversations)" --- The goal of this chapter is to implement persistent storage for conversation history, supporting session recovery across processes. > **Warning: Business Layer Concepts vs Framework Concepts** > > The **Memory, Session, and Store concepts introduced in this chapter are business layer concepts**, **not core components of the Eino framework**. > > - **Eino framework level**: Only provides basic abstractions like `adk.Runner` and `schema.Message`; the framework itself does not concern itself with how conversation history is stored > - **Business layer level**: Memory/Session/Store are business logic designed in this example project to implement persistent conversations, interacting with the Eino framework by assembling input for `adk.Runner` > > In other words, the Eino framework is only responsible for "how to process messages", while "how to store messages" is entirely up to the business layer. The implementation provided in this chapter is just a simple reference example — you can choose a completely different storage solution (database, Redis, cloud storage, etc.) based on your business needs. ## Code Location - Entry code: [cmd/ch03/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/cmd/ch03/main.go) - Memory implementation: [mem/store.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/mem/store.go) ## Prerequisites Same as Chapter 1: you need to configure an available ChatModel (OpenAI or Ark). ## Running In the `examples/quickstart/chatwitheino` directory, run: ```bash # Create a new session go run ./cmd/ch03 # Resume an existing session go run ./cmd/ch03 --session ``` Output example: ```text Created new session: 083d16da-6b13-4fe6-afb0-c45d8f490ce1 Session title: New Session Enter your message (empty line to exit): you> Hello, my name is Zhang San [assistant] Hello Zhang San! Nice to meet you... you> What's my name? [assistant] Your name is Zhang San... Session saved: 083d16da-6b13-4fe6-afb0-c45d8f490ce1 Resume with: go run ./cmd/ch03 --session 083d16da-6b13-4fe6-afb0-c45d8f490ce1 ``` ## From In-Memory to Persistent: Why We Need Memory In Chapter 2, we implemented multi-turn conversation, but there was a problem: **conversation history only existed in memory**. **Limitations of in-memory storage:** - Conversation history is lost when the process exits - Cannot resume sessions across devices or processes - Cannot implement session management (listing, deleting, searching, etc.) **The role of Memory:** - **Memory is persistent storage for conversation history**: Saves conversations to disk or a database - **Memory supports Session management**: Each Session represents a complete conversation - **Memory is decoupled from Agent**: The Agent doesn't care about storage details, only about the message list **Simple analogy:** - **In-memory storage** = "scratch paper" (gone when the process exits) - **Memory** = "notebook" (permanently saved, can be reviewed at any time) ## Key Concepts > **Reminder**: The following Session, Store, and other concepts are all **business layer implementations** used to manage conversation history storage. The Eino framework itself does not provide these components — the business layer is responsible for managing the message list, then passing messages to `adk.Runner` for processing. ### Session (Business Layer Concept) `Session` represents a complete conversation session: ```go type Session struct { ID string CreatedAt time.Time messages []*schema.Message // Conversation history // ... } ``` **Core methods:** - `Append(msg)`: Append a message to the session and persist it - `GetMessages()`: Get all messages - `Title()`: Generate a session title from the first user message ### Store (Business Layer Concept) `Store` manages persistent storage for multiple Sessions: ```go type Store struct { dir string // Storage directory cache map[string]*Session // In-memory cache } ``` **Core methods:** - `GetOrCreate(id)`: Get or create a Session - `List()`: List all Sessions - `Delete(id)`: Delete a Session ### JSONL File Format Each Session is stored as a `.jsonl` file: ```jsonl {"type":"session","id":"083d16da-...","created_at":"2026-03-11T10:00:00Z"} {"role":"user","content":"Hello, who am I?"} {"role":"assistant","content":"Hello! I don't know who you are yet..."} {"role":"user","content":"My name is Zhang San"} {"role":"assistant","content":"Got it, Zhang San, nice to meet you!"} ``` **Why JSONL?** - **Simple**: One JSON object per line, easy to read and write - **Extensible**: New messages can be appended without rewriting the entire file - **Readable**: Can be viewed directly with a text editor - **Fault-tolerant**: A corrupted line doesn't affect other lines ## Memory Implementation (Business Layer Example) Below is a simple business layer implementation example that uses JSONL files to store conversation history. This is just one of many possible implementations — you can choose databases, Redis, or other storage solutions based on your actual needs. ### 1. Create a Store ```go sessionDir := "./data/sessions" store, err := mem.NewStore(sessionDir) if err != nil { log.Fatal(err) } ``` ### 2. Get or Create a Session ```go sessionID := "083d16da-6b13-4fe6-afb0-c45d8f490ce1" session, err := store.GetOrCreate(sessionID) if err != nil { log.Fatal(err) } ``` ### 3. Append a User Message ```go userMsg := schema.UserMessage("Hello") if err := session.Append(userMsg); err != nil { log.Fatal(err) } ``` ### 4. Get History and Call the Agent ```go history := session.GetMessages() events := runner.Run(ctx, history) content := collectAssistantFromEvents(events) ``` ### 5. Append the Assistant Message ```go assistantMsg := schema.AssistantMessage(content, nil) if err := session.Append(assistantMsg); err != nil { log.Fatal(err) } ``` **Key code snippet (Note: this is a simplified code snippet that cannot be run directly. For the complete code, please refer to** [cmd/ch03/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/cmd/ch03/main.go)**)**: ```go // Create or resume a Session session, err := store.GetOrCreate(sessionID) if err != nil { log.Fatal(err) } // User input userMsg := schema.UserMessage(line) if err := session.Append(userMsg); err != nil { log.Fatal(err) } // Call the Agent history := session.GetMessages() events := runner.Run(ctx, history) content := collectAssistantFromEvents(events) // Save assistant reply assistantMsg := schema.AssistantMessage(content, nil) if err := session.Append(assistantMsg); err != nil { log.Fatal(err) } ``` ## The Relationship Between Session and Agent: Business Layer and Framework Layer Collaboration **Key understanding:** - **Session is a business layer concept**: Implemented and managed by business code, responsible for storing and loading conversation history - **Agent (Runner) is a framework layer concept**: Provided by the Eino framework, responsible for processing messages and generating replies - **Their interaction point**: The business layer uses `session.GetMessages()` to get the message list, which is passed to `runner.Run(ctx, history)` for processing **Architecture layers:** ``` +-------------------------------------------------------------+ | Business Layer (Your Code) | | +-------------+ +--------------+ +---------------+ | | | Session |--->| GetMessages() |--->| runner.Run() | | | | (Storage) | | (Message List)| | (Framework) | | | +-------------+ +--------------+ +---------------+ | | ^ | | | | v | | +-------------+ +---------------+ | | | Append() |<--------------------| Assistant Reply| | | | (Save Msg) | +---------------+ | | +-------------+ | +-------------------------------------------------------------+ | v +-------------------------------------------------------------+ | Framework Layer (Eino Framework) | | +-------------------------------------------------------+ | | | adk.Runner: Receives message list, calls ChatModel, | | | | returns reply | | | +-------------------------------------------------------+ | +-------------------------------------------------------------+ ``` **Flow diagram:** ``` +------------------------------------------+ | User Input | +------------------------------------------+ | +------------------------+ | session.Append() | | Save user message | +------------------------+ | +------------------------+ | session.GetMessages() | | Get complete history | +------------------------+ | +------------------------+ | runner.Run(history) | | Agent processes msgs | +------------------------+ | +------------------------+ | Collect assistant | | reply | +------------------------+ | +------------------------+ | session.Append() | | Save assistant message| +------------------------+ ``` ## Chapter Summary **Framework Layer vs Business Layer:** - **Eino framework layer**: Provides basic abstractions like `adk.Runner` and `schema.Message`; does not concern itself with how messages are stored - **Business layer (this chapter's implementation)**: Memory/Session/Store are business layer concepts used to manage conversation history storage **Business layer concepts:** - **Memory**: Persistent storage for conversation history, supporting cross-process recovery - **Session**: A complete conversation session, containing ID, creation time, and message list - **Store**: Manages storage for multiple Sessions, supporting create, get, list, and delete operations - **JSONL format**: A simple file format, easy to read, write, and extend **Business layer and framework layer interaction:** - The business layer is responsible for storing messages and uses `session.GetMessages()` to get the message list - The message list is passed to the framework layer's `runner.Run(ctx, history)` for processing - The reply returned by the framework layer is collected and then saved to storage by the business layer > **Tip**: The implementation in this chapter is just one simple example among many storage solutions. In real projects, you can choose databases, Redis, cloud storage, or other solutions based on business needs, and even implement more complex features like session expiration cleanup, search, sharing, etc. ## Further Thinking: Choosing a Business Layer Storage Solution The JSONL file storage solution provided in this chapter is suitable for simple single-machine applications. In real business scenarios, you may need to consider other storage solutions: **Other storage implementations:** - Database storage (MySQL, PostgreSQL, MongoDB) - Redis storage (supports distributed setups) - Cloud storage (S3, OSS) **Advanced features:** - Session expiration cleanup - Session search - Session export/import - Session sharing