Skip to content

Commit f5a6c30

Browse files
authored
feat(agent-framework): Hindsight memory for Microsoft Agent Framework (no MCP) (#1989)
* feat(agent-framework): add Hindsight memory integration via context provider Persistent memory for Microsoft Agent Framework (the successor to Semantic Kernel) without MCP. HindsightProvider is a ContextProvider whose before_run recalls relevant memories and injects them into the agent's instructions, and whose after_run retains the conversation. Reuses the LlamaIndex integration's client/config pattern and the hindsight-client Python SDK. Targets the agent-framework-core 1.x before_run/after_run + SessionContext contract (verified against the installed package since the API has churned). 15 unit tests subclass the real ContextProvider so drift fails loudly, plus a gated e2e. Includes CI job, release + changelog + docs wiring, and an icon. * chore(agent-framework): refresh lock to agent-framework-core 1.8.1 (verified no API drift) * fix(agent-framework): drop unused per-op timeout constants TIMEOUT_RETAIN/TIMEOUT_RECALL/TIMEOUT_BANK were defined but never used: the hindsight-client SDK sets one timeout on the constructor and has no per-call timeout argument, so per-op values can't be wired in. Keep the single constructor-level TIMEOUT_DEFAULT and document why. Addresses review feedback.
1 parent 443cce8 commit f5a6c30

20 files changed

Lines changed: 2123 additions & 1 deletion

File tree

.github/workflows/test.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
integration-tests: ${{ steps.filter.outputs.integration-tests }}
3333
integrations-openclaw: ${{ steps.filter.outputs.integrations-openclaw }}
3434
integrations-ai-sdk: ${{ steps.filter.outputs.integrations-ai-sdk }}
35+
integrations-agent-framework: ${{ steps.filter.outputs.integrations-agent-framework }}
3536
integrations-chat: ${{ steps.filter.outputs.integrations-chat }}
3637
integrations-claude-code: ${{ steps.filter.outputs.integrations-claude-code }}
3738
integrations-cline: ${{ steps.filter.outputs.integrations-cline }}
@@ -124,6 +125,8 @@ jobs:
124125
- 'hindsight-integrations/openclaw/**'
125126
integrations-ai-sdk:
126127
- 'hindsight-integrations/ai-sdk/**'
128+
integrations-agent-framework:
129+
- 'hindsight-integrations/agent-framework/**'
127130
integrations-chat:
128131
- 'hindsight-integrations/chat/**'
129132
integrations-claude-code:
@@ -3155,6 +3158,49 @@ jobs:
31553158
working-directory: ./hindsight-integrations/obsidian
31563159
run: npm test
31573160

3161+
test-agent-framework-integration:
3162+
needs: [detect-changes]
3163+
if: >-
3164+
(github.event_name == 'workflow_dispatch' ||
3165+
needs.detect-changes.outputs.integrations-agent-framework == 'true' ||
3166+
needs.detect-changes.outputs.ci == 'true')
3167+
runs-on: ubuntu-latest
3168+
timeout-minutes: 30
3169+
3170+
steps:
3171+
- uses: actions/checkout@v6
3172+
with:
3173+
ref: ${{ github.event.pull_request.head.sha || '' }}
3174+
3175+
- name: Install uv
3176+
uses: astral-sh/setup-uv@v7
3177+
with:
3178+
enable-cache: true
3179+
prune-cache: false
3180+
3181+
- name: Set up Python
3182+
uses: actions/setup-python@v6
3183+
with:
3184+
python-version-file: ".python-version"
3185+
3186+
- name: Build agent-framework integration
3187+
working-directory: ./hindsight-integrations/agent-framework
3188+
run: uv build
3189+
3190+
- name: Install dependencies
3191+
working-directory: ./hindsight-integrations/agent-framework
3192+
run: uv sync --frozen
3193+
3194+
- name: Lint
3195+
working-directory: ./hindsight-integrations/agent-framework
3196+
run: uv run ruff check .
3197+
3198+
- name: Run tests
3199+
working-directory: ./hindsight-integrations/agent-framework
3200+
# PR CI runs only the deterministic bucket; the real-LLM E2E bucket
3201+
# (requires_real_llm) needs a live Hindsight server and runs separately.
3202+
run: uv run pytest tests -v -m "not requires_real_llm"
3203+
31583204
test-crewai-integration:
31593205
needs: [detect-changes]
31603206
if: >-
@@ -4486,6 +4532,7 @@ jobs:
44864532
- test-dify-integration
44874533
- test-flowise-integration
44884534
- test-obsidian-integration
4535+
- test-agent-framework-integration
44894536
- test-crewai-integration
44904537
- test-langgraph-integration
44914538
- test-superagent-integration

hindsight-dev/hindsight_dev/generate_changelog.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class IntegrationMeta:
4141
"litellm": IntegrationMeta("hindsight-litellm", "LiteLLM"),
4242
"pydantic-ai": IntegrationMeta("hindsight-pydantic-ai", "Pydantic AI"),
4343
"crewai": IntegrationMeta("hindsight-crewai", "CrewAI"),
44+
"agent-framework": IntegrationMeta("hindsight-agent-framework", "Microsoft Agent Framework"),
4445
"ag2": IntegrationMeta("hindsight-ag2"),
4546
"ai-sdk": IntegrationMeta("@vectorize-io/hindsight-ai-sdk", "AI SDK"),
4647
"chat": IntegrationMeta("@vectorize-io/hindsight-chat", "Chat SDK"),
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
sidebar_position: 8
3+
title: "Microsoft Agent Framework Persistent Memory with Hindsight | Integration Guide"
4+
description: "Add persistent memory to Microsoft Agent Framework agents with Hindsight via a context provider. Recalls relevant context before each run and retains the conversation after — no MCP, no tools to call."
5+
---
6+
7+
# Microsoft Agent Framework
8+
9+
[View Changelog →](/changelog/integrations/agent-framework)
10+
11+
Persistent memory for [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) — the successor to Semantic Kernel — using [Hindsight](https://vectorize.io/hindsight). The integration plugs in as a **context provider**, so every agent run automatically recalls relevant memories into the agent's context and retains the conversation afterward. No MCP, and no tools the model has to remember to call.
12+
13+
## Quick Start
14+
15+
:::tip Recommended: Hindsight Cloud
16+
[Sign up free](https://ui.hindsight.vectorize.io/signup) for a Hindsight Cloud API key — no self-hosting required.
17+
:::
18+
19+
```bash
20+
pip install hindsight-agent-framework
21+
export HINDSIGHT_API_KEY=your-hindsight-key
22+
```
23+
24+
```python
25+
from agent_framework.openai import OpenAIChatClient
26+
from hindsight_agent_framework import HindsightProvider
27+
28+
agent = OpenAIChatClient().as_agent(
29+
name="assistant",
30+
instructions="You are a helpful assistant.",
31+
context_providers=[HindsightProvider(bank_id="user-123")],
32+
)
33+
34+
session = agent.create_session()
35+
await agent.run("Remember that I prefer vegetarian food.", session=session)
36+
await agent.run("Suggest a recipe.", session=session) # recalls the preference
37+
```
38+
39+
## How It Works
40+
41+
| Hook | Behavior |
42+
| --- | --- |
43+
| `before_run` | Recall memories relevant to the user's message and inject them as a `## Memories` block in the agent's instructions. |
44+
| `after_run` | Retain the user input + agent response so future runs build on them. |
45+
46+
Memories live in a Hindsight **bank** — one per user, agent, or session (you choose via `bank_id`). Recall and retain are best-effort: a memory hiccup never blocks the agent.
47+
48+
## Configuration
49+
50+
`HindsightProvider(bank_id, ...)` accepts `hindsight_api_url`, `api_key`, `budget` (low/mid/high), `max_tokens`, `tags`, `recall_tags`, `mission`, `auto_recall`, `auto_retain`, and more. Process-wide defaults can be set with `configure(...)`.
51+
52+
## Self-Hosting
53+
54+
```bash
55+
pip install hindsight-all
56+
export HINDSIGHT_API_LLM_API_KEY=your-openai-key
57+
hindsight-api # http://localhost:8888
58+
```
59+
60+
```python
61+
HindsightProvider(bank_id="user-123", hindsight_api_url="http://localhost:8888")
62+
```
63+
64+
See the [integration source](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/agent-framework) for full details.

hindsight-docs/src/data/integrations.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010
"link": "/sdks/integrations/litellm",
1111
"icon": "/img/icons/litellm.png"
1212
},
13+
{
14+
"id": "agent-framework",
15+
"name": "Microsoft Agent Framework",
16+
"description": "Persistent memory for Microsoft Agent Framework (the successor to Semantic Kernel) via a context provider that auto-recalls and auto-retains each turn.",
17+
"type": "official",
18+
"by": "hindsight",
19+
"category": "framework",
20+
"link": "/sdks/integrations/agent-framework",
21+
"icon": "/img/icons/agent-framework.svg"
22+
},
1323
{
1424
"id": "crewai",
1525
"name": "CrewAI",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
hide_table_of_contents: true
3+
---
4+
5+
import PageHero from '@site/src/components/PageHero';
6+
7+
<PageHero title="Microsoft Agent Framework Changelog" subtitle="Hindsight memory integration for Microsoft Agent Framework." />
8+
9+
[← Microsoft Agent Framework integration](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/agent-framework)
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# hindsight-agent-framework
2+
3+
Persistent long-term memory for [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) via [Hindsight](https://github.com/vectorize-io/hindsight).
4+
5+
Microsoft Agent Framework is the successor to Semantic Kernel. This integration plugs Hindsight in as a **context provider**, so every agent run automatically **recalls** relevant memories into the agent's context and **retains** the conversation afterward — no MCP, and no tools the model has to remember to call.
6+
7+
## Installation
8+
9+
```bash
10+
pip install hindsight-agent-framework
11+
```
12+
13+
> **Recommended:** [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) — sign up free, get an API key, and skip self-hosting.
14+
15+
## Usage
16+
17+
```python
18+
from agent_framework.openai import OpenAIChatClient
19+
from hindsight_agent_framework import HindsightProvider
20+
21+
agent = OpenAIChatClient().as_agent(
22+
name="assistant",
23+
instructions="You are a helpful assistant.",
24+
context_providers=[HindsightProvider(bank_id="user-123")],
25+
)
26+
27+
session = agent.create_session()
28+
await agent.run("Remember that I prefer vegetarian food.", session=session)
29+
# ...later, even in a new process:
30+
await agent.run("Suggest a recipe.", session=session) # recalls the preference
31+
```
32+
33+
Set your API key once via the `HINDSIGHT_API_KEY` environment variable, or pass `api_key=`/`hindsight_api_url=` to `HindsightProvider`. For self-hosting:
34+
35+
```bash
36+
pip install hindsight-all
37+
export HINDSIGHT_API_LLM_API_KEY=your-openai-key
38+
hindsight-api # http://localhost:8888
39+
```
40+
41+
```python
42+
HindsightProvider(bank_id="user-123", hindsight_api_url="http://localhost:8888")
43+
```
44+
45+
## How It Works
46+
47+
| Hook | Behavior |
48+
| --- | --- |
49+
| `before_run` | Recall memories relevant to the user's message and inject them as a `## Memories` block in the agent's instructions. |
50+
| `after_run` | Retain the user input + agent response so future runs build on them. |
51+
52+
Memories live in a Hindsight **bank** (one per user/agent/session — you choose). Recall and retain are best-effort: a memory hiccup never blocks the agent.
53+
54+
## Configuration
55+
56+
`HindsightProvider(bank_id, ...)` accepts: `client`, `hindsight_api_url`, `api_key`, `budget` (low/mid/high), `max_tokens`, `context`, `tags`, `recall_tags`, `recall_tags_match`, `mission` (creates the bank with a fact-extraction persona), `auto_recall`, `auto_retain`, `source_id`. You can also set process-wide defaults via `configure(...)`.
57+
58+
## Development
59+
60+
```bash
61+
uv sync
62+
uv run ruff check .
63+
uv run pytest tests -v # unit tests
64+
uv run pytest tests -v -m requires_real_llm # e2e (needs a live Hindsight server)
65+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Hindsight memory integration for Microsoft Agent Framework.
2+
3+
Provides ``HindsightProvider``, an Agent Framework ``ContextProvider`` that
4+
automatically recalls relevant memories into an agent's context before each run
5+
and retains the conversation afterward — persistent long-term memory with no MCP
6+
and no tools the model must remember to call.
7+
8+
Usage::
9+
10+
from agent_framework.openai import OpenAIChatClient
11+
from hindsight_agent_framework import HindsightProvider
12+
13+
agent = OpenAIChatClient().as_agent(
14+
name="assistant",
15+
context_providers=[HindsightProvider(bank_id="user-123")],
16+
)
17+
"""
18+
19+
from .config import (
20+
HindsightAgentFrameworkConfig,
21+
configure,
22+
get_config,
23+
reset_config,
24+
)
25+
from .errors import HindsightError
26+
from .provider import HindsightProvider
27+
28+
__version__ = "0.1.0"
29+
30+
__all__ = [
31+
"configure",
32+
"get_config",
33+
"reset_config",
34+
"HindsightAgentFrameworkConfig",
35+
"HindsightError",
36+
"HindsightProvider",
37+
]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Shared Hindsight client resolution logic."""
2+
3+
import os
4+
from importlib import metadata
5+
from typing import Any, Optional
6+
7+
from hindsight_client import Hindsight
8+
9+
from .config import DEFAULT_HINDSIGHT_API_URL, HINDSIGHT_API_KEY_ENV, get_config
10+
11+
try:
12+
_VERSION = metadata.version("hindsight-agent-framework")
13+
except metadata.PackageNotFoundError:
14+
_VERSION = "0.0.0"
15+
_USER_AGENT = f"hindsight-agent-framework/{_VERSION}"
16+
17+
# Request timeout (seconds). The hindsight-client SDK takes a single timeout at
18+
# construction and exposes no per-call timeout, so this one bound applies to
19+
# every recall/retain/bank operation. The provider guards all calls and degrades
20+
# gracefully, so this is a backstop, not a tight per-op budget.
21+
TIMEOUT_DEFAULT = 30.0
22+
23+
24+
def resolve_client(
25+
client: Optional[Hindsight],
26+
hindsight_api_url: Optional[str],
27+
api_key: Optional[str],
28+
) -> Hindsight:
29+
"""Resolve a Hindsight client from explicit args or global config.
30+
31+
Falls back to the default API URL and the ``HINDSIGHT_API_KEY`` env var
32+
when neither an explicit argument nor a prior ``configure()`` call supplied
33+
them, so the provider works with nothing but the env var set. Self-hosted
34+
users override the URL. The API key is optional at construction time — a
35+
missing key only fails when a call is actually made.
36+
"""
37+
if client is not None:
38+
return client
39+
40+
config = get_config()
41+
url = hindsight_api_url or (config.hindsight_api_url if config else DEFAULT_HINDSIGHT_API_URL)
42+
# Read HINDSIGHT_API_KEY directly so the no-configure() path still honours
43+
# the env var — the base Hindsight client doesn't fall back to it on its own.
44+
key = api_key or (config.api_key if config else None) or os.environ.get(HINDSIGHT_API_KEY_ENV)
45+
46+
kwargs: dict[str, Any] = {"base_url": url, "timeout": TIMEOUT_DEFAULT, "user_agent": _USER_AGENT}
47+
if key:
48+
kwargs["api_key"] = key
49+
return Hindsight(**kwargs)

0 commit comments

Comments
 (0)