Skip to main content
Connic
Back to BlogTutorial

Database vs Retrieval vs Sessions: Choose and Debug Agent Memory

Compare Connic's database, Retrieval, and persistent sessions. Configure identity and TTL, then inspect sanitized events and runs matched to that identity.

March 4, 2026(last updated: August 25, 2026)12 min readAuthor: Connic Engineering

Connic uses a document database for structured records, Retrieval for semantic search over unstructured content, and sessions for multi-turn conversation history. Each mode is scoped to one environment.

The session drawer lets operators inspect sanitized recorded events and open the runs matched to the exact session identity. This guide explains where each storage mode fits, how to configure session identity and inactivity TTL, and how to debug retained conversations.

Formerly Knowledge Base

Three Persistence Options

All three are scoped per environment. Every environment in your project gets its own isolated storage, with no cross-environment data leakage.

Database
A managed schemaless document store. Organize data in collections, query with expressive filters, sort, paginate, and update individual fields.
Retrieval
Semantic search powered by RAG. Upload text, PDFs, or images. Content is chunked and embedded so agents find relevant information by meaning, not keywords.
Sessions
Persistent conversation history across requests. The agent remembers previous messages without storing anything manually. Ideal for chatbots and multi-turn flows.

A quick rule of thumb: if you'd look it up in a spreadsheet, use the Database. If you'd Google it, use Retrieval. If you need the agent to remember the conversation, use Sessions.

Give your agents persistent memory

Built-in sessions, a document database, and vector retrieval, so you skip standing up and scaling separate storage services.

Try Connic free

Database: Structured Data and CRUD

Every environment includes a managed schemaless database. You organize data into collections, and each document is a free-form JSON object. No fixed schema, no migrations, no external hosting required. The system adds an auto-generated UUID and _created_at / _updated_at timestamps for you.

Connic Database page browsing the orders collection: schemaless JSON documents with id, amount, status, and customer fields
Browsing the orders collection — schemaless JSON documents with auto-added IDs and timestamps.

How It Works

  • Collections are created automatically when an agent inserts its first document, or manually from the dashboard. Names must be lowercase, start with a letter, and contain only letters, digits, and underscores.
  • Documents are free-form. You define the fields. The system adds _id, _created_at, and _updated_at automatically. Your fields go alongside these.
  • Queries use expressive JSON filters. You can match exact values, use comparison operators ( $gt, $lt, $in), combine conditions with $and and $or, and query nested fields with dot notation. Queries are indexed and optimized for fast lookups across large collections.

Predefined Tools

The database comes with six predefined tools that you can use in custom tool wrappers:

ToolPurpose
db_findQuery documents with filters, sorting, pagination, field projection, and distinct values
db_insertInsert one or more documents. Auto-creates the collection if it doesn't exist
db_updatePartially update documents matching a filter. Existing fields are preserved
db_deleteDelete documents matching a filter. Requires a non-empty filter for safety
db_countCount documents, optionally matching a filter
db_list_collectionsList all collections with document counts and sizes

Example: Order Processing Agent

The recommended approach is to wrap the database primitives in domain-specific functions so the agent calls save_order or get_customer_orders instead of constructing filters manually. Collection names and field mappings are encoded once in the wrapper, not repeated in the system prompt.

tools/order_tools.py
from connic.tools import db_find, db_insert, db_update

async def save_order(customer_id: str, items: list[dict], total: float) -> dict:
    """Store a new order in the database.

    Args:
        customer_id: The customer identifier.
        items: List of order items with sku, qty, and price.
        total: Order total amount.
    """
    return await db_insert("orders", {
        "customer_id": customer_id,
        "items": items,
        "total": total,
        "status": "pending"
    })

async def get_customer_orders(customer_id: str) -> list[dict]:
    """Get all orders for a specific customer, newest first.

    Args:
        customer_id: The customer identifier.
    """
    result = await db_find(
        "orders",
        filter={"customer_id": customer_id},
        sort={"_created_at": -1}
    )
    return result.get("documents", [])

async def update_order_status(order_id: str, status: str) -> dict:
    """Update the status of an order.

    Args:
        order_id: The _id of the order to update.
        status: New status (e.g. "shipped", "delivered", "cancelled").
    """
    result = await db_update(
        "orders",
        filter={"_id": order_id},
        update={"status": status}
    )
    return result

Then reference your custom tools in the agent YAML:

agents/order-processor.yaml
version: "1.0"

name: order-processor
type: llm
model: connic/gpt-5.6-luna

system_prompt: |
  You process e-commerce orders. When you receive an order:
  1. Validate the required fields (customer, items, total)
  2. Store it using save_order
  3. When asked about an order, look it up with get_customer_orders

  Always confirm what you stored, including the _id.

tools:
  - order_tools.save_order
  - order_tools.get_customer_orders
  - order_tools.update_order_status

The agent works in domain language. It calls save_order instead of figuring out which collection to use and how to structure the document. The collection is created automatically on the first insert.

Retrieval: Semantic Search and RAG

The retrieval lets you upload documents, build a semantic search index, and give your agents access to your organization's knowledge. See how retrieval-augmented generation (RAG) works. Content is automatically chunked and turned into vector embeddings, so a query for "cancellation rules" can surface a document titled "return and refund policy".

Connic Retrieval showing entries organized by namespace, with chunk counts and content types for semantic search
The Retrieval: entries organized by namespace, with chunk counts and content types for semantic search.

How It Works

  • Entries are logical documents identified by an entry_id. Long content is automatically split into searchable chunks.
  • Namespaces categorize entries. Use them like folders: policies, products, faq. Entry IDs are unique within a namespace.
  • Semantic search finds the most relevant chunks based on meaning. A min_score threshold (default 0.3) filters out low-similarity results. Results are ranked by relevance so the best matches come first.
  • Multiple content types are supported. Upload plain text, PDF files (pages are extracted, chunked, and embedded), and images (analyzed using vision models and the extracted content is embedded).

Predefined Tools

ToolPurpose
retrieval_querySemantic search across all entries or within a namespace. Returns the most relevant chunks with similarity scores
retrieval_storeStore text content. Automatically chunked and embedded in the background. Supports custom entry IDs and namespaces
retrieval_deleteRemove a retrieval entry by its entry ID

Example: Support Agent with Retrieval

Like with the database, the recommended pattern is to wrap the retrieval tools in domain-specific functions so the agent calls search_solutions instead of working with namespaces and entry IDs directly. The routing logic is encoded once in the wrapper, keeping the agent focused on its task.

tools/support_tools.py
from connic.tools import retrieval_query, retrieval_store

async def search_solutions(query: str) -> list[dict]:
    """Search the support retrieval for solutions.

    Args:
        query: Describe the customer's issue.
    """
    result = await retrieval_query(query, namespace="solutions", max_results=3)
    return result.get("results", [])

async def save_solution(content: str, entry_id: str | None = None) -> dict:
    """Store a reusable solution for future tickets.

    Args:
        content: The solution text including the problem description.
        entry_id: Optional stable ID for future updates (e.g. "password-reset-steps").
    """
    return await retrieval_store(
        content, namespace="solutions", entry_id=entry_id
    )

async def search_policies(query: str) -> list[dict]:
    """Look up company policies relevant to a support question.

    Args:
        query: Describe what policy information you need.
    """
    result = await retrieval_query(query, namespace="policies", max_results=3)
    return result.get("results", [])
agents/support-agent.yaml
version: "1.0"

name: support-agent
type: llm
model: connic/gpt-5.6-terra

system_prompt: |
  You are a customer support agent. For every question:
  1. Search for existing solutions using search_solutions
  2. If needed, check company policies with search_policies
  3. If you solve a new problem, store the solution with save_solution
     so future tickets benefit from it

tools:
  - support_tools.search_solutions
  - support_tools.save_solution
  - support_tools.search_policies

The agent no longer needs to know about namespaces or entry IDs. It calls search_solutions("password reset not working") and gets back relevant results. Semantic search handles synonyms and paraphrasing automatically.

Sessions: Persistent Conversation History

Sessions solve a different problem than the database or retrieval. They give an agent memory of the ongoing conversation across multiple requests. Without sessions, every message is independent: the agent starts fresh each time with no awareness of what was said before.

With sessions enabled, the agent maintains conversation history for you. Messages, responses, and tool activity are recorded and used on the next request with the same session identity. You configure this behavior in YAML instead of calling storage tools.

Configuration

Add a session block to your agent YAML with two fields:

  • key - A source expression that resolves a stable session identifier. Use context.<field> to read from middleware context, or input.<field> to read from the request payload. Reuse the same resolved value for every request that belongs to one conversation.
  • ttl - Optional time-to-live in seconds. This is an inactivity timeout: each update refreshes the expiry. The minimum is 60 seconds; if omitted, sessions never expire.

Connic builds the stored session ID from the agent name and resolved key. Storage identifies the row by that ID, the agent application, and the runtime user identity. Choose a key that is unique to each conversation. The dashboard uses all three stored fields when it fetches the session and finds related runs.

Example: Telegram Chatbot

A Telegram bot can use the chat ID from its connector payload to maintain a separate conversation per chat:

agents/telegram-bot.yaml
version: "1.0"

name: telegram-bot
type: llm
model: connic/gpt-5.6-terra

system_prompt: |
  You are a helpful personal assistant on Telegram.
  You can reference earlier messages in the conversation.

session:
  key: input.chat_id
  ttl: 86400    # 24 hours

Each unique chat_id gets its own session. When the same chat sends another message, the agent receives the retained session context. After 24 hours of inactivity the session expires and the next message starts a new conversation.

Inspect and Debug Persistent Sessions

Open a row under Storage > Sessions to inspect its exact session ID, agent, displayed message count, creation time, last update, and expiry. The conversation view renders the displayable parts of recorded user, assistant, system, and tool messages.

  • Recorded text and JSON appear directly in the conversation. Attachments show the file name, MIME type, and size without exposing inline file data.
  • Tool names and arguments sit beside recorded results or errors. Reasoning appears only when it was retained unredacted; otherwise the drawer marks it as redacted.
  • The related-runs table matches session ID, agent application, and user identity together. Select a row to open the execution behind that conversation.
A Sanitized, Bounded Debugging View
The drawer is not unrestricted raw history. The dashboard API removes reserved runtime state, provider state, internal event record IDs, and inline file bytes. History compaction can also replace superseded messages. The drawer requests the 200 most recent events (the API accepts 1–500) and shows the 20 most recent runs matched to that exact identity. Messages and tool payloads can still contain application data, so do not use sessions to store secrets.
Sessions vs. Retrieval vs. Database
Connic manages session conversation history without storage tools. The database and retrieval store domain data that persists independently of conversations and is accessed through tools. You can combine all three: a chatbot with sessions for conversation memory, retrieval for documentation lookup, and the database for saving structured results.

When to Use Which

The decision comes down to how you need to access the data and what kind of data it is.

ScenarioUseWhy
Track order status and historyDatabaseStructured records with filters on status, date, customer
Answer questions about company policiesRetrievalUsers ask in natural language, need semantic matching
Multi-turn chatbot that remembers contextSessionsConversation history, not stored data
Store user preferences or settingsDatabaseKey-value lookups by user ID, not semantic search
Search product documentationRetrievalLong-form text, users search by describing their problem
Count active users or aggregate metricsDatabaseRequires counting, filtering, and sorting structured data
Look up answers from uploaded PDFsRetrievalPDFs are chunked, embedded, and searchable by meaning
Log events or audit trailDatabaseTimestamped records queried by date range and type
Use Them Together
Many production agents combine all three. A support chatbot uses sessions for conversation memory, the retrieval to look up documentation, and the database to track ticket records. Each system handles what it's built for.

Access Control in Agent YAML

Both the database and retrieval support fine-grained access control directly in your agent YAML. You can restrict which collections or namespaces an agent can access, and prevent write or delete operations. Access control applies regardless of whether the agent uses predefined tools directly or through custom wrappers.

Database Access Control

agent.yaml
tools:
  - order_tools.save_order
  - order_tools.get_customer_orders

# Restrict database access
database:
  collections:
    orders:
      prevent_delete: true    # can read and write, but not delete
    customers: {}             # full access, no restrictions
  prevent_delete: false       # global default
  prevent_write: false        # global default

When collections is set, the agent can only access those collections. Any attempt to query or write to a collection not in the list is blocked. If collections is omitted, all collections are accessible.

For a simpler configuration, you can also pass collections as a list:

agent.yaml
database:
  collections: [orders, customers]
  prevent_delete: true

Retrieval Access Control

Retrieval access control works the same way, but scoped to namespaces instead of collections:

agent.yaml
tools:
  - support_tools.search_solutions
  - support_tools.save_solution
  - support_tools.search_policies

# Restrict retrieval access
retrieval:
  namespaces:
    solutions:
      prevent_delete: true    # can query and store, but not delete
    policies:
      prevent_write: true     # read-only access to policies
  prevent_delete: false
  prevent_write: false

This is especially useful in multi-agent setups. A support agent might have read-only access to the policies namespace but full access to the solutions namespace, while an admin agent can manage both.

Managing Storage in the Dashboard

All three storage systems have dedicated pages in the Connic dashboard under the Storage menu. You can inspect, manage, and debug your data without writing code.

Database
  • Create and delete collections
  • Browse documents with filters and pagination
  • Edit documents inline
  • Insert new documents via JSON
  • View inferred schema (field types, fill rates)
Retrieval
  • Upload text, PDFs, and images
  • Run semantic queries to test retrieval
  • Browse entries by namespace and content type
  • View individual chunks and token counts
  • Monitor upload processing jobs
Sessions
  • Filter by agent or search by session ID
  • Open sanitized recent conversation events
  • Inspect messages, tool activity, file metadata, and reasoning state
  • Open runs matched to the exact session identity
  • Review lifetime timestamps and delete sessions

The dashboard also shows storage usage on the Billing page, with progress bars for each limit. This is where you see how close you are to your tier's capacity.

Populating the Retrieval

There are two ways to get content into the retrieval:

  • 1.Dashboard upload - Use the Retrieval page to upload files (PDF, TXT, MD, PNG, JPG, GIF, WEBP) or paste text directly. Files are processed asynchronously. You can track progress in the upload jobs list.
  • 2.Agent tool calls - Agents with access to retrieval_store (directly or through a wrapper) can write to the retrieval during runs. This is how agents learn from interactions: they discover a solution and store it for next time.
Tip: Use Namespaces From the Start
Even if you only have one type of content today, assign a namespace. It costs nothing and makes it much easier to filter results and manage content as your retrieval grows. A support agent searching only the "solutions" namespace gets faster, more relevant results than searching everything.

Best Practices

Always Wrap Tools in Custom Functions
The recommended approach is to wrap predefined tools in domain-specific functions. The agent calls save_order(customer_id, items, total) instead of db_insert("orders", ...). Collection names, namespaces, and field mappings are encoded once in the wrapper. The agent is less likely to make mistakes and the tool descriptions are more informative.
Use Access Control in Production
Always restrict your agents to the collections and namespaces they actually need. An agent that processes orders shouldn't have access to delete customer records. Set prevent_delete: true on any collection where accidental deletion would be harmful.
Tune Retrieval Search Parameters
The default min_score of 0.3 is a good starting point, but adjust based on your content. If results are too sparse, lower it to 0.2. If results include too much noise, raise it to 0.4. Test with the dashboard's query feature to calibrate.
Use Descriptive Entry IDs
When storing retrieval content, always provide a meaningful entry_id like password-reset-steps or refund-policy-2026. This makes entries easy to identify in the dashboard and allows updating the same entry by re-storing with the same ID.
Keep Database Documents Flat
Nested objects are supported and can be queried with dot notation (address.city), but flatter documents are easier for agents to work with. Keep nesting to one or two levels. If you find yourself nesting deeply, consider separate collections.
Don't Store Structured Data in Retrieval
Storing JSON records or CSV rows in the retrieval is wasteful. The embedding pipeline is designed for natural language text, not structured data. Use the database for anything you'd put in a spreadsheet.
Set Session TTLs Thoughtfully
A TTL that's too short frustrates users who expect the agent to remember earlier context. A TTL that's too long wastes resources and can confuse agents with stale conversation history. 24 hours works well for customer-facing chatbots. A week is reasonable for personal assistants.

Plan Limits

Both the database and retrieval have per-tier limits on collections, documents, and entries. These are enforced automatically and checked before every write operation. If a limit is reached, the agent receives a clear error message.

To compare current plan limits, open the Pricing page or visit the Billing page in your project settings to see your current usage with progress bars for each limit.

Full Example: Support Agent Using All Three

A complete agent that uses all three. It maintains conversation history with sessions, tracks support tickets in the database, and searches the retrieval for solutions.

agents/support-agent.yaml
version: "1.0"

name: support-agent
type: llm
model: connic/gpt-5.6-terra

system_prompt: |
  You are a customer support agent. For every incoming ticket:

  1. Search for existing solutions using search_solutions
  2. Check company policies if relevant with search_policies
  3. Create a ticket record with save_ticket
  4. If you discover a new solution, save it with save_solution

temperature: 0.5
max_iterations: 15

# Persistent conversation history per customer
session:
  key: context.customer_id
  ttl: 86400

tools:
  - support_tools.search_solutions
  - support_tools.save_solution
  - support_tools.search_policies
  - ticket_tools.save_ticket
  - ticket_tools.get_ticket
  - ticket_tools.update_ticket

# Only allow access to what this agent needs
database:
  collections:
    tickets: {}
  prevent_delete: true

retrieval:
  namespaces:
    solutions: {}
    policies:
      prevent_write: true
      prevent_delete: true

This agent remembers the conversation (sessions), can create and update tickets (database), can search documentation (retrieval), and can save new solutions (retrieval). It can't delete tickets, can't write to the policies namespace, and can't access any collection or namespace outside the ones listed.

Getting Started

  • 1.Pick the right storage for your data. Structured records go in the database. Unstructured text searched by meaning goes in the retrieval. Conversation memory goes in sessions.
  • 2.Write custom tool wrappers. Import from connic.tools and wrap the predefined tools in domain-specific functions. This gives your agent a cleaner interface and encodes your business logic once.
  • 3.Pre-populate the retrieval. Upload your documentation, FAQs, and policy documents via the dashboard before you deploy. Agents perform better when the retrieval already has content.
  • 4.Set access controls. Restrict collections and namespaces per agent. Use prevent_delete and prevent_write to enforce the principle of least privilege.
  • 5.Deploy and verify. Use the dashboard to confirm your agent is reading and writing the right data. Check the Database page for collection contents and the Retrieval page to test semantic queries. Open a persistent session to inspect its conversation and follow one of its related runs into the execution details.

For the full API reference and query filter syntax, read the Database Tools documentation, read the Retrieval Tools documentation, and configure persistent sessions. New to Connic? Follow the quickstart guide to deploy your first agent.

More from the Blog

Tutorial

How to Deploy a Python AI Agent Without Kubernetes

Deploy a Python AI agent without Kubernetes using YAML, plain Python, deployment-gated tests, Git, and a managed EU runtime. Includes working code.

August 12, 202612 min read
Tutorial

How to Trigger AI Agents from Kafka Topics

Point a Connic Kafka inbound connector at a topic and every message starts an agent run. Configure the connector, link an agent, deploy, and watch runs.

July 12, 20268 min read
Tutorial

How to Add an AI Agent to Your SaaS Without a Large Engineering Team

A practical, step-by-step path to shipping your first production AI agent with a small team: scope one job, define it in config, connect it to your existing systems, and let a runtime handle the rest.

June 12, 20269 min read
Tutorial

Automated Agent Scoring: AI Agent Evaluation with LLM Judges

Automated agent scoring uses an LLM judge to grade sampled or every matching agent run against criteria you define. Track score trends and alert on regressions.

March 29, 202610 min read
Tutorial

Migrate from LangChain to Production AI Agents

Your LangChain prototype works. Now you need it to handle real traffic. Learn how to migrate existing agent code to a production-grade platform without rewriting from scratch.

March 23, 202611 min read
Tutorial

AI Agents: From Prototype to Production

Your demo works great until you have 1,000 concurrent users. A practical guide to the production requirements most teams find out about too late.

January 10, 202610 min read
Tutorial

Hidden Costs of Self-Hosting AI Agents

We'll just deploy it on Kubernetes. Famous last words. The true cost of self-hosting AI agents versus a managed platform.

December 18, 20257 min read
Tutorial

Add AI Agents to SaaS Without an ML Team

Your customers expect AI features, but you don't have ML engineers. Learn how teams ship AI agents using skills they already have.

December 5, 20258 min read
Tutorial

AI Agent RAG Tutorial: Retrieval With Citations

Build a production RAG agent with scoped retrieval namespaces, read-only permissions, source citations, custom tool wrappers, and regression tests.

November 15, 20259 min read