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

> ## Agent Instructions
> AGENTS.md is HydraDB's maintained agent guide. Read https://docs.hydradb.com/AGENTS.md once, then use its content. /llms.txt and /llms-full.txt redirect to the same guide; they are not separate documentation indexes. If the guide is already loaded, continue with it instead of fetching either alias or reloading the guide.

# Build your own AI Chief of Staff

> An assistant that tracks decisions and commitments across meetings and email, and knows which ones are still current.

A chief of staff remembers what was decided, who committed to what, and what has changed since. This cookbook builds one from two types of context: meetings and email as knowledge, and the decisions drawn from them as memories that HydraDB keeps current.

| Benchmark | Result | What it shows here |
| - | - | - |
| [BEAM 1M](https://benchmarks.hydradb.com/beam-1m) | 82% overall across ten dimensions | Decisions, tasks, and events tracked over months, where newer information replaces older |

No benchmark tests a chief of staff end to end. BEAM 1M tests the memory underneath it.

## Prerequisites

* A HydraDB API key from [dashboard.hydradb.com](https://dashboard.hydradb.com), set as `HYDRADB_API_KEY`.
* An OpenAI API key, set as `OPENAI_API_KEY`. HydraDB returns context, not answers, so any model works; swap the `answer` function below for your own.
* Python 3.10 or later, with the SDKs: `pip install "hydradb-sdk>=2.1.7,<3" openai`.

Save this as `hydra.py`. Every cookbook imports it. It holds what every HydraDB application does around its own logic: wait for the database, check what an ingest accepted, wait for indexing, and pass query results to a model.

```python hydra.py theme={"dark"}
import os
import time

from hydra_db import HydraDB
from hydra_db.errors import ConflictError
from hydra_db.helpers import build_string
from openai import OpenAI

client = HydraDB(token=os.environ["HYDRADB_API_KEY"])
llm = OpenAI()


def create_database(database, schema=None):
    """Create the database if it is new, then wait until it accepts context."""
    try:
        if schema:
            client.databases.create(database=database, database_metadata_schema=schema)
        else:
            client.databases.create(database=database)
    except ConflictError:
        pass  # The database already exists.
    while not client.databases.status(database=database).data.infra.ready_for_ingestion:
        time.sleep(5)


def source_ids(result):
    """Return the IDs an ingest accepted. Raise if it rejected any item."""
    for item in result.data.results:
        if item.error_code:
            raise RuntimeError(f"{item.filename or item.id}: {item.error_code} {item.error}")
    return [item.id for item in result.data.results]


def wait_indexed(database, collection, ids):
    """Block until every source has finished indexing. Indexing runs in the background."""
    while True:
        statuses = client.context.status(
            database=database, collection=collection, ids=ids
        ).data.statuses
        for status in statuses:
            if status.indexing_status == "errored":
                raise RuntimeError(f"{status.error_code}: {status.error_message}")
        if all(s.indexing_status == "completed" for s in statuses):
            return
        time.sleep(2)


def answer(question, result, instructions=""):
    """Send a query result to the model and return its answer."""
    completion = llm.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": "Answer using only the context provided. If the answer "
                "is not in the context, say you don't know. " + instructions,
            },
            {
                "role": "user",
                "content": f"Context:\n{build_string(result)}\n\nQuestion: {question}",
            },
        ],
    )
    return completion.choices[0].message.content
```

## 1. Create a database with two collections

Work records go in one collection. The executive's own decisions and commitments go in another, so they can be queried alone or together with the records.

```python chief_of_staff.py theme={"dark"}
import json

from hydra import answer, client, create_database, source_ids, wait_indexed

DATABASE = "acme_corp"
RECORDS, EXECUTIVE = "work_records", "exec_dana"

create_database(DATABASE)
```

## 2. Ingest meetings and email as knowledge

Meetings and email are [app sources](https://docs.hydradb.com/essentials/v2/context.md#send-app-sources). The `meeting` and `email` kinds keep participants, senders, and dates, which is what lets a query answer "what did Priya agree to on the September call".

```python chief_of_staff.py theme={"dark"}
records = [
    {
        "id": "gong_call_0912",
        "title": "Q4 planning call",
        "kind": "meeting",
        "provider": "gong",
        "external_id": "call_0912",
        "timestamp": "2026-09-12T16:00:00Z",
        "fields": {
            "kind": "meeting",
            "title": "Q4 planning call",
            "body": "Dana: we launch the EU region on November 3. Priya: I will "
            "have the data residency review done by October 15.",
            "host": "Dana Okafor",
            "participants": [{"name": "Priya Shah", "email": "priya@acme.com"}],
            "started_at": "2026-09-12T16:00:00Z",
        },
    },
    {
        "id": "gmail_msg_1002",
        "title": "EU launch date",
        "kind": "email",
        "provider": "gmail",
        "external_id": "msg_1002",
        "timestamp": "2026-10-02T09:30:00Z",
        "fields": {
            "kind": "email",
            "subject": "EU launch date",
            "body": "Legal needs two more weeks. Moving the EU launch to November 17.",
            "from": "dana@acme.com",
            "to": ["leadership@acme.com"],
            "thread_id": "thread_eu_launch",
            "created_at": "2026-10-02T09:30:00Z",
        },
    },
]

result = client.context.ingest(
    type="knowledge",
    database=DATABASE,
    collection=RECORDS,
    app_knowledge=json.dumps(records),
)
wait_indexed(DATABASE, RECORDS, source_ids(result))
```

## 3. Save decisions as memories

After each meeting or thread, save what was decided as a [memory](https://docs.hydradb.com/essentials/v2/context.md#2-save-memories). With `infer: true`, HydraDB extracts the facts and checks them against what the collection already holds, so the October change to the launch date is reconciled with the September decision instead of sitting beside it.

```python chief_of_staff.py theme={"dark"}
def remember(text):
    result = client.context.ingest(
        type="memory",
        database=DATABASE,
        collection=EXECUTIVE,
        memories=json.dumps([{"text": text, "infer": True, "user_name": "Dana"}]),
    )
    wait_indexed(DATABASE, EXECUTIVE, source_ids(result))


remember("2026-09-12: Decided to launch the EU region on November 3. "
         "Priya owns the data residency review, due October 15.")
remember("2026-10-02: Moved the EU launch to November 17 because legal needs two more weeks.")
```

## 4. Ask what is current

Query both collections at once with `type: "all"`. `recency_bias` favors newer context, and `thinking` mode reranks, so the current launch date ranks above the one it replaced.

```python chief_of_staff.py theme={"dark"}
def brief(question):
    result = client.query(
        database=DATABASE,
        collections=[RECORDS, EXECUTIVE],
        query=question,
        type="all",
        mode="thinking",
        recency_bias=0.6,
    )
    return answer(question, result, "When facts conflict, the most recent one is current. "
                                    "Name the owner and due date of each commitment.")


print(brief("When is the EU launch, and what is still open before it?"))
```

## Run it

```bash theme={"dark"}
python chief_of_staff.py
```

The answer should give November 17, note that the date moved from November 3, and list Priya's data residency review as the open commitment.

## Use real data

Connect the calendar, meeting, and email apps with [connectors](https://docs.hydradb.com/essentials/v2/connectors.md) in place of step 2. Keep step 3 in your application: decide what counts as a decision, and save it when the meeting ends.

## Common mistakes

* **Saving decisions as knowledge only:** a transcript records what was said. A memory records what is true now. Save both.
* **Querying one collection:** a query that names only `RECORDS` does not read the memories. Use `collections` with both names.
* **Leaving dates out of memories:** write the date into the memory text. It is what lets a later change supersede an earlier one.

## Next steps

* [Context](https://docs.hydradb.com/essentials/v2/context.md#2-save-memories): when to use `infer`, and how to update or delete a memory.
* [Query](https://docs.hydradb.com/essentials/v2/query.md): `recency_bias` and `mode`.
* [Build your own personal assistant](https://docs.hydradb.com/cookbooks/v2/personal-assistant.md): the same memory, for one person's life outside work.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.