> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAI Agents SDK

> Search and store function tools that let OpenAI Agents recall and persist memories on demand.

Give an OpenAI Agent persistent, per-user memory by exposing two function tools: one that searches Synap and one that stores new memories. The agent decides when to call each, with no hidden injection and no chain modifications.

<Note>
  Requires Python 3.11+.
</Note>

## Overview

This guide shows how to add Synap to an OpenAI Agents SDK application to build agents that:

* Recall user-specific facts, preferences, and past conversations
* Persist new information surfaced during a conversation for future runs
* Stay fully in control of *when* memory is queried or written

The Synap OpenAI Agents integration ships two factory functions, each of which returns an async callable you wrap with the `function_tool(...)` helper from `agents` (the `name_override` argument lives on `function_tool`, not on the `FunctionTool` class).

| Export               | Returns                                    | Purpose                                     |
| -------------------- | ------------------------------------------ | ------------------------------------------- |
| `create_search_tool` | `async (query, max_results) -> list[dict]` | Function tool that searches Synap memory    |
| `create_store_tool`  | `async (content, memory_type) -> dict`     | Function tool that stores a memory in Synap |

## Setup

Install the package alongside the OpenAI Agents SDK:

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-openai-agents openai-agents
  ```

  ```bash uv theme={null}
  uv add maximem-synap-openai-agents openai-agents
  # pip-compatible (existing venv): uv pip install maximem-synap-openai-agents openai-agents
  ```
</CodeGroup>

<Note>
  The pip package is `maximem-synap-openai-agents`, but the import drops the `maximem-` prefix and uses underscores: `from synap_openai_agents import ...`.
</Note>

Configure your API key. Generate one from the [Synap Dashboard](https://synap.maximem.ai).

```bash .env theme={null}
SYNAP_API_KEY=synap_your_key_here
OPENAI_API_KEY=your-openai-api-key
```

Initialize the SDK once at application startup:

```python theme={null}
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK()
await sdk.initialize()
```

See [SDK Initialization](/sdk/initialization) for the full lifecycle and configuration options.

## Basic integration

The smallest useful integration registers both tools on an agent and runs a query. The agent reads its tool descriptions, decides whether to search or store, and calls the matching function:

```python theme={null}
# pip install maximem-synap-openai-agents openai-agents
from maximem_synap import MaximemSynapSDK
from agents import Agent, Runner, function_tool
from synap_openai_agents import create_search_tool, create_store_tool

sdk = MaximemSynapSDK()
await sdk.initialize()

search_fn = create_search_tool(sdk=sdk, user_id="alice", customer_id="acme")
store_fn = create_store_tool(sdk=sdk, user_id="alice", customer_id="acme")

agent = Agent(
    name="Memory Agent",
    instructions=(
        "Use synap_search to recall facts about the user. "
        "Use synap_store to remember new information they share."
    ),
    tools=[
        function_tool(search_fn, name_override="synap_search"),
        function_tool(store_fn, name_override="synap_store"),
    ],
)

result = await Runner.run(agent, "What do you know about my project deadlines?")
print(result.final_output)
```

The scoping triple (`user_id`, optional `customer_id`) is bound when you construct the tool: the agent only ever sees the `query` and `content` parameters, never the user identity. This keeps the model from leaking or spoofing user IDs.

***

## Core concepts

### Search tool

`create_search_tool` returns an async callable that takes a natural-language query and returns a list of memory objects.

```python theme={null}
from synap_openai_agents import create_search_tool

search_fn = create_search_tool(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",   # optional, required for B2B instances
)
```

Tool signature exposed to the model:

```text theme={null}
synap_search(query: str, max_results: int = 5) -> list[dict]
```

Each result has the shape `{"content": "...", "type": "fact", "confidence": 0.91}`. The agent sees the JSON list and can quote or reason over the entries directly.

**Search failures degrade gracefully**: the tool returns an empty list and logs an error, so the agent continues without recall rather than aborting.

### Store tool

`create_store_tool` returns a companion callable that ingests a new memory.

```python theme={null}
from synap_openai_agents import create_store_tool

store_fn = create_store_tool(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",
)
```

Tool signature exposed to the model:

```text theme={null}
synap_store(content: str, memory_type: str = "fact") -> dict
```

Returns `{"status": "stored", "id": "..."}` on success. **Store failures surface explicitly**: the tool raises `SynapIntegrationError` so the agent (and you) know if persistence failed.

***

## Complete example: assistant with explicit memory control

The following agent is told to consult its memory before answering and store anything the user shares. The Synap calls only happen when the model elects to invoke the tools:

```python theme={null}
from agents import Agent, Runner, function_tool
from synap_openai_agents import create_search_tool, create_store_tool


def build_memory_agent(sdk, user_id: str, customer_id: str | None = None) -> Agent:
    search_fn = create_search_tool(sdk=sdk, user_id=user_id, customer_id=customer_id)
    store_fn = create_store_tool(sdk=sdk, user_id=user_id, customer_id=customer_id)

    return Agent(
        name="Personal Assistant",
        instructions=(
            "You are a personal assistant with long-term memory.\n"
            "1. Always call synap_search FIRST for any question about the user, "
            "their preferences, or past conversations.\n"
            "2. When the user shares a new fact, preference, or decision, "
            "call synap_store to remember it.\n"
            "3. Never fabricate facts. If synap_search returns nothing, "
            "say you don't know yet."
        ),
        tools=[
            function_tool(search_fn, name_override="synap_search"),
            function_tool(store_fn, name_override="synap_store"),
        ],
    )


# Usage
agent = build_memory_agent(sdk, user_id="alice", customer_id="acme")

# First conversation: agent stores a fact
await Runner.run(agent, "I just upgraded to the Pro plan.")

# Later conversation: agent recalls it
result = await Runner.run(agent, "What plan am I on?")
print(result.final_output)  # → "You're on the Pro plan."
```

Three things to notice in this pattern:

1. **The agent owns the memory loop.** Search and store calls are model-driven, not framework-driven; the prompt steers the behavior.
2. **Scope is bound at construction.** The model never sees `user_id` or `customer_id`, so even a prompt-injection attempt cannot make it query memories for someone else.
3. **Failure modes split.** Search is best-effort (empty list on failure); store is strict (raises on failure) so silent data loss is impossible.

***

## Advanced patterns

### Multi-tenant scoping

Both factories accept the same scoping triple: `user_id` (required), optional `customer_id`. `customer_id` is required on B2B Synap instances and ignored on single-tenant ones. See [Memory Scopes](/concepts/memory-scopes).

```python theme={null}
# User-scoped only
search_fn = create_search_tool(sdk=sdk, user_id="alice")

# Organization-scoped (user sees org-shared memories too)
search_fn = create_search_tool(sdk=sdk, user_id="alice", customer_id="acme-corp")
```

### Per-request scoping

If your service handles many users in one process, build a fresh pair of tools per request rather than caching them; each agent run should have its scope baked in to prevent cross-user leakage:

```python theme={null}
def per_request_agent(sdk, user_id: str, customer_id: str | None = None):
    return build_memory_agent(sdk, user_id, customer_id)
```

### Failure semantics

The integration follows the Synap-wide contract:

* **Search failures degrade gracefully**: `synap_search` returns `[]` and logs an error so the agent can continue.
* **Store failures surface explicitly**: `synap_store` raises `SynapIntegrationError` so the agent (and caller) know persistence failed.

This is by design: a transient outage shouldn't break a user-facing answer, but a silent write failure would let memory drift away from reality.

***

## Going further

* [Patterns overview](/patterns/overview): reusable memory patterns across frameworks.
* [Cookbook overview](/cookbook/overview): end-to-end worked examples.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Pydantic AI" icon="shield-check" href="/integrations/pydantic-ai">
    Type-safe deps and tools for Pydantic AI agents.
  </Card>

  <Card title="AutoGen" icon="arrows-rotate" href="/integrations/autogen">
    `BaseTool` implementations for AutoGen agents.
  </Card>

  <Card title="Context Fetch" icon="search" href="/sdk/context-fetch">
    The retrieval API that powers `synap_search`: modes, scopes, and response shapes.
  </Card>

  <Card title="Memory Scopes" icon="layer-group" href="/concepts/memory-scopes">
    How `user_id` and `customer_id` interact across reads and writes.
  </Card>
</CardGroup>
