Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions web/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ export default defineConfig({
trailingSlash: "always",
integrations: [
mermaid({
theme: 'forest',
autoTheme: true
theme: "neutral",
autoTheme: true,
enableLog: false,
}),
starlight({
title: "Prompty",
Expand Down
12 changes: 8 additions & 4 deletions web/src/content/docs/agentic-concepts/agent-loop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,11 @@ The loop does not invent tool implementations. A `.prompty` file can declare
tool schemas, but your application still supplies the functions or provider
implementations that execute them.

The loop also does not automatically persist memory. Conversation history is
passed by the application, usually through a `kind: thread` input. See
[Conversation History](/core-concepts/conversation-history/) for the thread
pattern.
The loop also does not automatically choose a memory backend. Prompty defines
the portable memory store and deterministic recall/formatting behavior, but
your application still supplies the persistence hook and lifecycle policy. See
[Memory](/agentic-concepts/memory/) for the host/runtime boundary.

Conversation history is passed by the application, usually through a
`kind: thread` input. See [Conversation History](/core-concepts/conversation-history/)
for the thread pattern.
15 changes: 14 additions & 1 deletion web/src/content/docs/agentic-concepts/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
title: Agentic Concepts
description: >
Understand Prompty's agentic runtime model: turns, tool-calling loops,
guardrails, context compaction, steering, cancellation, events, and retries.
guardrails, memory, context compaction, steering, cancellation, events, and
retries.
sidebar:
order: 1
---
Expand All @@ -16,6 +17,7 @@ tool-using agents. After reading it, you should be able to answer:

- where the boundary is between a `.prompty` file and host runtime code;
- how a single `turn()` differs from a full chat session;
- how memory is represented by Prompty and persisted by your host;
- when messages are prepared, appended, trimmed, compacted, or steered;
- where to enforce policy with guardrails instead of prompt instructions;
- how to observe and stop a long-running tool loop.
Expand All @@ -38,6 +40,7 @@ flowchart TD
C -. "optional controls" .-> K["context budget + compaction"]
C -. "optional controls" .-> L["guardrails"]
C -. "optional controls" .-> M["steering"]
C -. "host-provided context" .-> N["memory store"]
```

The key distinction is:
Expand All @@ -46,6 +49,9 @@ The key distinction is:
- **`turn()`** runs one user turn and, if needed, loops internally for tool calls.
- **Tool-loop iterations do not re-render the template.** They mutate the prepared
message array by appending assistant/tool messages.
- **Memory is a host boundary.** Prompty defines the portable memory model and
deterministic behavior; your application decides where the memory store lives
and when it is loaded or saved.
- **External user turns call `turn()` again.** If you pass conversation history as
a thread input, that thread is expanded during the next `prepare()` call.

Expand All @@ -54,6 +60,7 @@ The key distinction is:
| Concept | What it controls | Start here |
|---|---|---|
| Agent loop | How tool calls are executed and fed back to the model | [Agent Loop](/agentic-concepts/agent-loop/) |
| Memory | Portable memory semantics, recall, formatting, and host persistence boundaries | [Memory](/agentic-concepts/memory/) |
| Runtime controls | The per-iteration order for cancellation, steering, context, guardrails, model calls, and tools | [Runtime Controls](/agentic-concepts/runtime-controls/) |
| Guardrails | Validate or rewrite inputs, outputs, and tool arguments | [Guardrails](/agentic-concepts/guardrails/) |
| Context budget | Trim messages before model calls | [Context & Compaction](/agentic-concepts/context-compaction/) |
Expand All @@ -76,10 +83,16 @@ available in the v2 runtimes:
| C# | Yes | Yes | Yes | Yes |
| Rust | Yes | Yes | Yes | Yes |

Memory helpers and conformance vectors are available across the generated model
and runtime surface. The host still owns persistence by implementing the runtime's
memory port or equivalent load/save adapter.

## Related docs

- [Agent tool calling guide](/how-to/agent-tool-calling/) for a hands-on walkthrough
- [Tools](/core-concepts/tools/) for `.prompty` tool declarations
- [Conversation history](/core-concepts/conversation-history/) for thread inputs
- [Memory reference](/reference/memorystore/) for the generated `MemoryStore`
contract
- [Tracing](/core-concepts/tracing/) for observing runtime execution
- [Agent loop specification](/specification/agent-loop/) for the lower-level contract
230 changes: 230 additions & 0 deletions web/src/content/docs/agentic-concepts/memory.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
---
title: Memory
description: >
How Prompty represents memory, what behavior the runtimes provide, and where
your host application plugs in persistence.
sidebar:
order: 6
---

import { Aside, Tabs, TabItem } from '@astrojs/starlight/components';

Prompty memory is a **portable runtime contract**, not a hosted database. The
runtimes know how to work with memory once a store is available; your
application decides where that store lives and when to load or save it.

That split keeps memory behavior consistent across runtimes without forcing
every app into the same storage, privacy, or lifecycle model.

## Mental model

```mermaid
flowchart LR
A["Host application"] --> B["load MemoryStore"]
B --> C["Prompty turn()"]
C --> D["core memories injected into system prompt"]
C --> E["recall / remember / update / clear"]
E --> F["updated MemoryStore"]
F --> G["host saves store"]

H["Storage choice\nfile, database, service"] -. owned by host .-> A
```

Prompty owns the portable behavior:

- `MemoryStore`, `MemoryEntry`, and `MemoryCategory`;
- deterministic `recall` scoring and formatting;
- `remember`, `update`, `remove`, `clear`, and cap-based eviction;
- core-memory formatting for model-visible system context;
- conformance vectors that keep those semantics aligned across runtimes.

Your host owns the integration:

- persistence location: file, database, profile service, project store, or
tenant-scoped service;
- scoping: user, project, agent, session, organization, or some combination;
- lifecycle policy: when to remember, summarize, update, expire, or ask for
permission;
- privacy and security boundaries: consent, audit, tenant isolation, encryption,
and deletion;
- wiring the store into the turn loop through the runtime's memory port or
equivalent load/save adapter.

<Aside type="note">
The promise is: Prompty can run against a memory store consistently once your
host plugs one in. Prompty does not ship a universal managed memory backend.
</Aside>

## Categories

Memory entries are tiered so runtimes agree on what each entry means.

| Category | Meaning | Runtime behavior |
|---|---|---|
| `core` | Persistent facts or preferences that should be model-visible by default | Injected into the system prompt, boosted during recall, and deduplicated by tag set on write |
| `archival` | Summaries or older context that should be available through recall | Not injected by default; preferred for eviction when the store exceeds its cap |
| `insight` | Saved reflections or derived observations | Available through recall; not injected by default |

Only `core` memories are formatted directly into the model-visible system prompt:

```text
## Memory
- User prefers concise answers
- Deploys on Friday afternoons
```

Archival and insight memories stay out of the prompt unless your app recalls
them and chooses to include the recall results.

## Runtime shape

<Tabs>
<TabItem label="Python">

```python
from prompty import MemoryEntry, MemoryStore, format_for_system_prompt, recall, remember

store = MemoryStore.load({"entries": []})
remember(
store,
MemoryEntry.load(
{
"content": "User prefers concise answers",
"category": "core",
"tags": ["preference", "tone"],
}
),
)

system_context = format_for_system_prompt(store)
results = recall(store, "tone", limit=3)
```

</TabItem>
<TabItem label="TypeScript">

```ts
import {
MemoryEntry,
MemoryStore,
formatForSystemPrompt,
recall,
remember,
} from "@prompty/core";

const store = MemoryStore.load({ entries: [] });
remember(
store,
MemoryEntry.load({
content: "User prefers concise answers",
category: "core",
tags: ["preference", "tone"],
}),
);

const systemContext = formatForSystemPrompt(store);
const results = recall(store, "tone", 3);
```

</TabItem>
<TabItem label="C#">

```csharp
using Prompty.Core;

var store = MemoryStore.Load(new Dictionary<string, object?> { ["entries"] = new List<object>() });

Memory.Remember(
store,
MemoryEntry.Load(new Dictionary<string, object?>
{
["content"] = "User prefers concise answers",
["category"] = "core",
["tags"] = new[] { "preference", "tone" },
})
);

var systemContext = Memory.FormatForSystemPrompt(store);
var results = Memory.Recall(store, "tone", limit: 3);
```

</TabItem>
<TabItem label="Rust">

```rust
use prompty::{MemoryCategory, MemoryEntry, MemoryStore};

let mut store = MemoryStore { entries: vec![] };
store.remember(
MemoryEntry {
content: "User prefers concise answers".to_string(),
category: MemoryCategory::Core,
created_at: None,
tags: Some(vec!["preference".to_string(), "tone".to_string()]),
},
0,
);

let system_context = store.format_for_system_prompt();
let results = store.recall("tone", 3);
```

</TabItem>
</Tabs>

## Persistence boundary

Each flagship runtime exposes a memory port shape: load a whole `MemoryStore`
snapshot, let Prompty apply deterministic behavior, then save the updated
snapshot. A host can back that port with any storage system.

```mermaid
sequenceDiagram
participant App
participant MemoryPort
participant Prompty
participant Model

App->>MemoryPort: load scoped store
MemoryPort-->>App: MemoryStore
App->>Prompty: turn(agent, inputs, memory)
Prompty->>Prompty: format core memories
Prompty->>Model: messages + memory context
Model-->>Prompty: response / tool calls
Prompty-->>App: result + updated store
App->>MemoryPort: save updated store
```

Prompty deliberately does not decide when a model response should become a
memory. Many apps should ask for user permission, apply policy, or summarize
conversation history before writing. Those choices depend on the host's product
requirements, not the `.prompty` file.

## Conformance

Memory is covered by shared vectors, so flagship runtimes benefit equally from
the same semantics. The vectors assert:

- recall ranking by weighted lexical score;
- tag matches and core-memory boosts;
- empty-query behavior and result limits;
- stable tie ordering;
- core deduplication by tags;
- archival-first eviction;
- clear, update, and out-of-range behavior;
- system-prompt formatting;
- snapshot round-trip behavior;
- model-visible memory context in a turn.

If a runtime changes any of those behaviors, its generated conformance suite
goes red.

## Related docs

- [Agent Loop](/agentic-concepts/agent-loop/) for where memory context appears
in a turn
- [Context & Compaction](/agentic-concepts/context-compaction/) for trimming and
summarizing conversation history
- [MemoryStore reference](/reference/memorystore/) for the generated store shape
- [MemoryEntry reference](/reference/memoryentry/) for entry fields and category
metadata
57 changes: 57 additions & 0 deletions web/src/styles/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,60 @@
starlight-tabs .tablist-wrapper {
overflow-y: hidden;
}

/* Improve Mermaid contrast in Starlight dark mode. */
:root[data-theme="dark"] .mermaid {
background: var(--sl-color-bg-nav);
border: 1px solid var(--sl-color-gray-5);
border-radius: 0.5rem;
padding: 1rem;
}

:root[data-theme="dark"] .mermaid svg {
color: var(--sl-color-white);
}

:root[data-theme="dark"] .mermaid .node rect,
:root[data-theme="dark"] .mermaid .node circle,
:root[data-theme="dark"] .mermaid .node ellipse,
:root[data-theme="dark"] .mermaid .node polygon,
:root[data-theme="dark"] .mermaid .node path,
:root[data-theme="dark"] .mermaid rect.actor,
:root[data-theme="dark"] .mermaid .actor.actor-top,
:root[data-theme="dark"] .mermaid .actor.actor-bottom {
fill: var(--sl-color-gray-6) !important;
stroke: var(--prompty-blue) !important;
}

:root[data-theme="dark"] .mermaid .edgeLabel,
:root[data-theme="dark"] .mermaid .labelBkg,
:root[data-theme="dark"] .mermaid .labelBox {
background-color: var(--sl-color-bg-nav) !important;
fill: var(--sl-color-bg-nav) !important;
}

:root[data-theme="dark"] .mermaid text,
:root[data-theme="dark"] .mermaid tspan,
:root[data-theme="dark"] .mermaid .label,
:root[data-theme="dark"] .mermaid .nodeLabel,
:root[data-theme="dark"] .mermaid .edgeLabel,
:root[data-theme="dark"] .mermaid .messageText,
:root[data-theme="dark"] .mermaid .actor,
:root[data-theme="dark"] .mermaid .actor tspan,
:root[data-theme="dark"] .mermaid .label tspan,
:root[data-theme="dark"] .mermaid .nodeLabel tspan,
:root[data-theme="dark"] .mermaid .edgeLabel tspan {
color: var(--sl-color-white) !important;
fill: var(--sl-color-white) !important;
}

:root[data-theme="dark"] .mermaid .flowchart-link,
:root[data-theme="dark"] .mermaid .messageLine0,
:root[data-theme="dark"] .mermaid .messageLine1 {
stroke: var(--prompty-blue) !important;
}

:root[data-theme="dark"] .mermaid marker path {
fill: var(--prompty-blue) !important;
stroke: var(--prompty-blue) !important;
}
Loading