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

# Follow-Up Agent

> Turn your latest call into next steps with an owner, a team, a deadline and the Linear issue that tracks each one, posted to Slack

A call rarely says which team owns each task, when it is due, or which issue already tracks it. This agent fills those in from what it remembers: the team from an earlier call, the deadline from an email, the issue from Linear.

## What You'll Build

Your Granola calls, your Linear issues and your Gmail inbox go into one cognee memory. After your latest call, an agent lists the next steps everyone agreed to and fills in what the call left out from everything else it remembers. It posts the list to a Slack channel, or prints it when Slack is not set up.

Building the memory takes three `cognee.remember` calls, one per source, and working out the next steps takes three `cognee.recall` calls.

The complete cookbook is
[`examples/cookbooks/company_brain/follow_up_agent/`](https://github.com/topoteretes/cognee/tree/dev/examples/cookbooks/company_brain/follow_up_agent).

## Try It on Sample Data

Two days ago, on a launch-readiness call, Omar agreed to move card payments to 3DS2, Sam to load-test the checkout API, and Lena to write the announcement. The call names no team and no deadline. An earlier call says which team each person is on, Linear already tracks Omar's step as PAY-104, and an email from the bank sets the 3DS2 deadline. You need only an [LLM key](/setup-configuration/llm-providers), no Granola, Linear, Gmail or Slack account. A sample run never posts to Slack.

```bash theme={null}
git clone https://github.com/topoteretes/cognee.git && cd cognee
uv sync
echo 'LLM_API_KEY="your-key"' >> .env
```

`LLM_API_KEY` alone is enough for OpenAI, cognee's default for both the LLM and the embeddings. Other providers, such as Anthropic, Gemini, Azure OpenAI, AWS Bedrock or a local model through Ollama, need a few more lines in the same `.env`: [LLM Providers](/setup-configuration/llm-providers#provider-setup-guides) and [Embedding Providers](/setup-configuration/embedding-providers#provider-setup-guides) list them for each provider. Set both, because embeddings you leave at the default still need an OpenAI key.

<Tabs>
  <Tab title="Terminal">
    ```bash theme={null}
    uv run python examples/cookbooks/company_brain/follow_up_agent/follow_up_agent.py --sample
    ```
  </Tab>

  <Tab title="Coding agent">
    The cognee repo ships a skill for this cookbook, [`.agents/skills/company-brain-follow-up-agent/SKILL.md`](https://github.com/topoteretes/cognee/blob/dev/.agents/skills/company-brain-follow-up-agent/SKILL.md). Claude Code and Codex find it on their own when you open them in the cognee folder. Ask in plain words, or type `/company-brain-follow-up-agent` in Claude Code:

    ```text theme={null}
    What are the next steps from my latest call?
    ```

    With no Granola, Linear or Gmail set up, it runs on the sample and never posts to Slack. Once your accounts are set up, the same request follows up your real latest call; the skill runs `--check` first and tells you what is missing.
  </Tab>
</Tabs>

```text theme={null}
[setup] CLEAR: the cookbook's dataset is forgotten before this run.
[setup] SKIPPED: Slack (not set up, or a sample run): steps are printed.
[setup] Wrote the sample from setup.py.
[clear] Nothing to forget: the dataset follow_up_agent does not exist yet.
[ingest_granola] Remembered 2 sample calls
[ingest_linear] Remembered 2 sample Linear issues
[ingest_email] Remembered 1 sample inbox emails
[follow_up] Call: Checkout v2 launch readiness
...
[follow_up] Steps (a sample run, so not posted):
*Next steps from "Checkout v2 launch readiness"*
### Next steps

- **Migrate card payments to 3DS2** — **Owner:** Omar Haddad; **Team:** Payments; **Due:** 2026-11-05; **Linear:** PAY-104.
- **Load-test the checkout API at three times peak traffic** — **Owner:** Sam Okoro; **Team:** Platform; **Due:** 2026-10-16; **Linear:** PLAT-88.
- **Write the launch announcement after both tasks are complete** — **Owner:** Lena Fischer; **Team:** Payments; **Due:** Not specified; **Linear:** None.
```

None of the teams, dates or issues were said on the call. Payments and Platform come from the earlier call, PAY-104 and PLAT-88 from Linear, Sam's due date from PLAT-88, and Omar's from the bank's email, because PAY-104 has none. The agent also kept Lena's step after the other two, as she said, and gave it no issue because Linear tracks none. The first run has nothing to forget; a later sample run prints `[clear] Forgot the dataset follow_up_agent` instead. The sample is dated relative to today, so your dates and wording will differ.

## Run It on Your Data

Granola is required. Linear and Gmail are optional sources: without one, its step is skipped. Slack is optional too: without it, the steps are printed instead of posted. Set up each one you use:

<Tabs>
  <Tab title="Granola">
    The cookbook reads your calls through Granola's API with a personal API key. Creating one needs a Granola Business plan; on Enterprise, a workspace admin enables it first.

    1. In the Granola desktop app, open **Settings → Connectors → API keys** and click **Create new key**.
    2. Choose the **Personal notes** scope and click **Generate API Key**. Copy the key now; Granola shows it only once.
    3. Add it to `.env` at the repo root: `GRANOLA_API_KEY="grn_..."`.

    The API returns only notes that already have an AI summary and a transcript. [Granola's API docs](https://docs.granola.ai/introduction) list the other scopes.
  </Tab>

  <Tab title="Linear">
    The cookbook reads your issues through Linear's API with a personal API key:

    1. In Linear, open **Settings → Security & access** and create a new personal API key.
    2. Add it to `.env` at the repo root: `LINEAR_API_KEY="lin_api_..."`.

    The key reads what your Linear account can see. [Linear's API docs](https://linear.app/developers/graphql) cover keys and permissions.
  </Tab>

  <Tab title="Gmail">
    The cookbook reads your mail read-only through a Google OAuth client you create once:

    1. In the [Google Cloud Console](https://console.cloud.google.com/), create or pick a project and enable the **Gmail API**.
    2. Configure the **OAuth consent screen**. While the app is in *Testing*, add your own Google account as a test user.
    3. Go to **Credentials → Create credentials → OAuth client ID**, choose **Desktop app**, and download the JSON file.
    4. Save it as `credentials.json` in the cookbook folder, next to `follow_up_agent.py`.
    5. Install the Google client libraries with `uv sync --extra gmail`.

    The first run opens a browser so you can allow read access. It then writes `token.json` next to `credentials.json`, with the `gmail.readonly` scope. [Gmail Ingestion](/guides/gmail-ingestion) covers the connector in more detail.
  </Tab>

  <Tab title="Slack">
    The cookbook posts the next steps as a Slack bot:

    1. On [Slack's app page](https://api.slack.com/apps), click **Create New App → From scratch** and pick your workspace.
    2. Open **OAuth & Permissions**, add the `chat:write` bot token scope, and click **Install App to Workspace**.
    3. Copy the **Bot User OAuth Token** (it starts with `xoxb-`) into `.env`: `SLACK_BOT_TOKEN="xoxb-..."`.
    4. In the channel, type `/invite @<your app>`. Then open the channel's details and copy the **Channel ID** at the bottom into `.env`: `SLACK_CHANNEL="C..."`.

    [Slack's messaging docs](https://docs.slack.dev/messaging/sending-and-scheduling-messages) cover the setup in more detail.
  </Tab>
</Tabs>

* `credentials.json` and `token.json` are git-ignored. Never commit them.

```bash theme={null}
uv run python examples/cookbooks/company_brain/follow_up_agent/follow_up_agent.py --check
uv run python examples/cookbooks/company_brain/follow_up_agent/follow_up_agent.py
uv run python examples/cookbooks/company_brain/follow_up_agent/follow_up_agent.py --days 7 --ui
```

`--check` reports what is missing and which optional sources are skipped, without doing any work. The first Gmail run opens a browser so you can let it read your mail. Running it again skips content cognee already holds; add `--clear` to forget the dataset first and start over from your sources as they are now. Each script also runs alone, for example `uv run python examples/cookbooks/company_brain/follow_up_agent/scripts/follow_up.py`.

## How It Works

### Step 1: Start From an Empty Memory

Source: [`scripts/clear.py`](https://github.com/topoteretes/cognee/blob/dev/examples/cookbooks/company_brain/follow_up_agent/scripts/clear.py)

This step forgets what an earlier run remembered, so old copies never mix with new ones. It runs only with `--clear`, which a sample run turns on by default; `--no-clear` keeps the dataset.

```python theme={null}
await clear()
```

The sample is dated relative to today, so each day's sample differs from yesterday's. Without this step, a second run would add today's calls next to yesterday's, and the agent could pick yesterday's deadline. cognee's part is one call: [`cognee.forget(dataset="follow_up_agent")`](/core-concepts/main-operations/forget), which deletes the dataset and everything extracted from it. On your own data, use `--clear` only to start over.

### Step 2: Remember Your Calls

Source: [`scripts/ingest_granola.py`](https://github.com/topoteretes/cognee/blob/dev/examples/cookbooks/company_brain/follow_up_agent/scripts/ingest_granola.py)

This step saves your recent calls so the agent knows who works on what. It reads the calls from the last 30 days; pass `--days N` to change that.

```python theme={null}
await ingest_granola(args.days, args.sample)
```

The script reads each call from Granola's API, with its attendees and transcript. With `args.sample`, it reads the sample calls instead; every ingest step works this way. cognee's part is one call: `cognee.remember`, into the dataset `follow_up_agent` with `node_set=["calls"]`. [`remember`](/core-concepts/main-operations/remember) turns text into a knowledge graph of people, teams and decisions. A [*dataset*](/core-concepts/further-concepts/datasets) is the named memory everything goes into. A [*node set*](/core-concepts/further-concepts/node-sets) is a tag on what you remember, so you can later recall from that part alone.

### Step 3: Remember Your Linear Issues

Source: [`scripts/ingest_linear.py`](https://github.com/topoteretes/cognee/blob/dev/examples/cookbooks/company_brain/follow_up_agent/scripts/ingest_linear.py)

This step saves the work your team already tracks, so the agent can say a step has an issue. It runs only when Linear is set up and you don't pass `--no-linear`. It reads the issues changed in the last 30 days, not your whole workspace; `--days N` changes that too.

```python theme={null}
await ingest_linear(args.days, args.sample)
```

The script fetches the issues from Linear's API and writes each one as a short text: identifier, title, status, team, assignee and due date. Then it calls `cognee.remember` with `node_set=["linear"]`. Writing the identifier into the text is what lets the agent copy `PAY-104` exactly.

### Step 4: Remember Your Inbox

Source: [`scripts/ingest_email.py`](https://github.com/topoteretes/cognee/blob/dev/examples/cookbooks/company_brain/follow_up_agent/scripts/ingest_email.py)

This step saves your newest emails, because deadlines often arrive by email, not on a call. It runs only when Gmail is set up and you don't pass `--no-email`. It takes the newest 50 inbox emails, not your whole mailbox; pass `--emails N` to change how many.

```python theme={null}
await ingest_email(args.emails, args.sample)
```

The script calls `cognee.remember` with cognee's [Gmail connector](/guides/gmail-ingestion), `gmail_source`, as the input and `node_set=["email"]`. `write_disposition="merge"` with `primary_key="id"` merges emails by message id, so a rerun does not store the same email twice.

### Step 5: Work Out the Next Steps

Source: [`scripts/follow_up.py`](https://github.com/topoteretes/cognee/blob/dev/examples/cookbooks/company_brain/follow_up_agent/scripts/follow_up.py)

This step reads your newest call from the last 30 days and lists its next steps, then posts them to Slack or prints them.

```python theme={null}
await follow_up(args.days, args.sample)
```

The agent is three [`cognee.recall`](/core-concepts/main-operations/recall) calls. The first two use `query_type=SearchType.CHUNKS` with the call as the query: one with `node_name=["linear"]`, one with `node_name=["email"]`. Each returns the three closest issues or emails as raw text, because they rarely rank in a search over the whole graph, and raw text keeps identifiers and dates exact. The third uses `HYBRID_COMPLETION`, which answers from the matching passages and the graph across all sources. It gets the call, those issues and emails, and `NEXT_STEPS_PROMPT`: take teams from earlier calls and issues, deadlines from emails, and never invent a date, a team or an issue.

### Step 6: Browse the Graph

Source: [`scripts/ui.py`](https://github.com/topoteretes/cognee/blob/dev/examples/cookbooks/company_brain/follow_up_agent/scripts/ui.py)

This step opens the cognee UI so you can see the memory behind the answer. It runs only when you pass `--ui`.

```python theme={null}
await open_ui()
```

The script starts cognee's API server inside the same process, next to the databases the earlier steps opened, and then the UI with `cognee.start_ui`. Open `http://localhost:3000` to browse the people, teams, issues and deadlines in the graph. Ctrl+C stops both. See [Run the UI Locally](/cognee-cloud/local-ui) for what the UI can do.

## Make It Yours

* **Ask for more in each step.** Edit `NEXT_STEPS_PROMPT` in `scripts/follow_up.py` to add a priority or the customer a step is for. Keep its last rule, so the agent still never invents a fact.
* **Post where your team works.** The Slack post is one HTTP request at the end of `follow_up` in `scripts/follow_up.py`. Swap it for your chat tool's API, or for a comment on the Linear issue.
* **Add another source.** Write one more ingest script that calls `cognee.remember` with its own node set, such as a folder of specs or postmortems, and add it to `run()`. See [Remember](/core-concepts/main-operations/remember) for the inputs it accepts.
* **Build a weekly team digest.** Keep steps 2 to 4 and replace the agent with one `HYBRID_COMPLETION` recall with `node_name=["calls", "linear"]`: "What did the Payments team commit to this month, and what is still open?" Same memory, a different application. [NodeSet Grouping](/guides/nodeset-grouping) shows how scoping works.

## Clean Up

```bash theme={null}
uv run cognee-cli forget --dataset follow_up_agent
```

To start over completely, also delete `sample/` and `token.json` in the cookbook folder.

<Columns cols={2}>
  <Card title="Company Brain Q&A" icon="building-2" href="/cookbooks/company-qa">
    Build another one: answer questions across your database, tickets and docs.
  </Card>

  <Card title="Personalized Email" icon="mail-check" href="/cookbooks/personalized-email">
    Build another one: email replies that know what you promised.
  </Card>

  <Card title="Self-Hosted AI Companion" icon="notebook-pen" href="/cookbooks/self-hosted-companion">
    Build another one: a chat companion that remembers your notes.
  </Card>

  <Card title="NodeSet Grouping" icon="layers" href="/guides/nodeset-grouping">
    Tag memories by source and recall from one part at a time.
  </Card>
</Columns>


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