fix: splitting context into references & verifying content is consistent.

This commit is contained in:
ajspig 2026-07-15 16:19:02 -04:00
parent bcb588f980
commit a26fafc905
6 changed files with 450 additions and 568 deletions

View File

@ -12,7 +12,17 @@ Honcho is an open source memory library for building stateful agents. It works w
The key mental model: **Peers** are any participant — human or AI. Both are represented the same way. Observation settings (`observe_me`, `observe_others`) control which peers Honcho reasons about. Typically you want Honcho to model your users (`observe_me=True`) but not your AI assistant (`observe_me=False`). **Sessions** scope conversations between peers. **Messages** are the raw data you feed in — Honcho reasons about them asynchronously and stores the results as the peer's **representation**. No messages means no reasoning means no memory.
Your agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer), `session.context()` (get formatted conversation history + representations), or both.
Your agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer — a few seconds of live reasoning) or `session.context()` (near-instant read of formatted history + representation). Prefer `context()` for per-turn grounding; use `chat()` when you need a reasoned answer.
## Reference map
Follow the workflow below. Read a reference file only when you reach the step that needs it:
| When you're… | Read |
| --- | --- |
| Writing the client/peer/session setup (init, peers, sessions, add messages) | `references/core-patterns.md` |
| Wiring how the AI reads context (tool call, pre-fetch, `context()`, streaming) | `references/agent-patterns.md` |
| Integrating into a bot framework (nanobot, openclaw, picoclaw, …) | `references/bot-frameworks.md` + `references/bot-frameworks/<framework>/` |
## Integration Workflow
@ -34,7 +44,7 @@ Use Glob and Grep to find:
- User/session models or types
- API routes handling chat or conversation endpoints
> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `{baseDir}/references/bot-frameworks.md` for framework-specific integration guidance and check `{baseDir}/references/bot-frameworks/<framework>/` for concrete reference implementations.
> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `references/bot-frameworks.md` for framework-specific integration guidance and check `references/bot-frameworks/<framework>/` for concrete reference implementations.
### Phase 2: Interview (REQUIRED)
@ -51,7 +61,7 @@ Ask about which entities should be Honcho peers:
#### Question Set 2 - Integration Pattern
Ask how they want to use Honcho context:
Ask how they want to use Honcho context (see `references/agent-patterns.md` for the implementation of each):
- header: "Pattern"
- question: "How should your AI access Honcho's user context?"
@ -82,11 +92,11 @@ If they chose pre-fetch, ask what context matters:
Based on interview responses, implement the integration:
1. Install the SDK
2. Create Honcho client initialization
3. Set up peer creation for identified entities
4. Implement the chosen integration pattern(s)
5. Add message storage after exchanges
1. Install the SDK (see [Installation](#installation))
2. Create Honcho client initialization`references/core-patterns.md` §1
3. Set up peer creation for identified entities`references/core-patterns.md` §23
4. Implement the chosen integration pattern(s)`references/agent-patterns.md`
5. Add message storage after exchanges`references/core-patterns.md` §4
6. Update any existing conversation handlers
### Phase 4: Verification
@ -132,394 +142,7 @@ uv add honcho-ai
bun add @honcho-ai/sdk
```
## Sync vs Async
**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.
**Python** — The SDK provides both sync and async interfaces:
- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)
- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)
```python
# Sync usage (Flask, Django, scripts)
from honcho import Honcho
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
peer = honcho.peer("user-123")
response = peer.chat("What does this user prefer?")
# Async usage (FastAPI, Starlette)
from honcho import Honcho
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
peer = await honcho.aio.peer("user-123")
response = await peer.aio.chat("What does this user prefer?")
```
Match the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The rest of this skill shows sync Python examples; swap to `.aio` equivalents for async codebases.
## Core Integration Patterns
### 1. Initialize with a Single Workspace
Use ONE workspace for your entire application. The workspace name should reflect your app/product.
**Python:**
```python
from honcho import Honcho
import os
# Sync client (Flask, Django, scripts)
honcho = Honcho(
workspace_id="your-app-name",
api_key=os.environ["HONCHO_API_KEY"],
environment="production"
)
# Async client (FastAPI, Starlette) — use honcho.aio for all operations
# honcho.aio.peer(), honcho.aio.session(), etc.
```
**TypeScript:**
```typescript
import { Honcho } from '@honcho-ai/sdk';
// All methods are async by default
const honcho = new Honcho({
workspaceId: "your-app-name",
apiKey: process.env.HONCHO_API_KEY,
environment: "production"
});
```
### 2. Create Peers for ALL Entities
Create peers for **every entity** in your business logic - users AND AI assistants.
**Python:**
```python
from honcho.api_types import PeerConfig
# Human users
user = honcho.peer("user-123")
# AI assistants - set observe_me=False so Honcho doesn't model the AI
assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False))
support_bot = honcho.peer("support-bot", configuration=PeerConfig(observe_me=False))
```
**TypeScript:**
```typescript
// Human users
const user = await honcho.peer("user-123");
// AI assistants - set observeMe=false so Honcho doesn't model the AI
const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } });
const supportBot = await honcho.peer("support-bot", { configuration: { observeMe: false } });
```
### 3. Multi-Peer Sessions
Sessions can have multiple participants. Configure observation settings per-peer.
**Python:**
```python
from honcho.api_types import SessionPeerConfig
session = honcho.session("conversation-123")
# User is observed (Honcho builds a model of them)
user_config = SessionPeerConfig(observe_me=True, observe_others=True)
# AI is NOT observed (no model built of the AI)
ai_config = SessionPeerConfig(observe_me=False, observe_others=True)
session.add_peers([
(user, user_config),
(assistant, ai_config)
])
```
**TypeScript:**
```typescript
const session = await honcho.session("conversation-123");
await session.addPeers([
[user, { observeMe: true, observeOthers: true }],
[assistant, { observeMe: false, observeOthers: true }]
]);
```
### 4. Add Messages to Sessions
**Python:**
```python
session.add_messages([
user.message("I'm having trouble with my account"),
assistant.message("I'd be happy to help. What seems to be the issue?"),
user.message("I can't reset my password")
])
```
**TypeScript:**
```typescript
await session.addMessages([
user.message("I'm having trouble with my account"),
assistant.message("I'd be happy to help. What seems to be the issue?"),
user.message("I can't reset my password")
]);
```
## Using Honcho for AI Agents
### Pattern A: Dialectic Chat as a Tool Call (Recommended for Agents)
Make Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.
**Python (OpenAI function calling):**
```python
import openai
from honcho import Honcho
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
# Define the tool for your agent
honcho_tool = {
"type": "function",
"function": {
"name": "query_user_context",
"description": "Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'"
}
},
"required": ["query"]
}
}
}
def handle_honcho_tool_call(user_id: str, query: str) -> str:
"""Execute the Honcho chat tool call."""
peer = honcho.peer(user_id)
return peer.chat(query)
# Use in your agent loop
def run_agent(user_id: str, user_message: str):
messages = [{"role": "user", "content": user_message}]
response = openai.chat.completions.create(
model="gpt-4",
messages=messages,
tools=[honcho_tool]
)
# Handle tool calls
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
if tool_call.function.name == "query_user_context":
import json
args = json.loads(tool_call.function.arguments)
result = handle_honcho_tool_call(user_id, args["query"])
# Continue conversation with tool result...
```
**TypeScript (OpenAI function calling):**
```typescript
import OpenAI from 'openai';
import { Honcho } from '@honcho-ai/sdk';
const honcho = new Honcho({
workspaceId: "my-app",
apiKey: process.env.HONCHO_API_KEY
});
const honchoTool: OpenAI.ChatCompletionTool = {
type: "function",
function: {
name: "query_user_context",
description: "Query Honcho to retrieve relevant context about the user based on their history and preferences.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "A natural language question about the user"
}
},
required: ["query"]
}
}
};
async function handleHonchoToolCall(userId: string, query: string): Promise<string> {
const peer = await honcho.peer(userId);
return await peer.chat(query);
}
```
### Pattern B: Pre-fetch Context with Targeted Queries
For simpler integrations, fetch user context before the LLM call using pre-defined queries.
**Python:**
```python
def get_user_context_for_prompt(user_id: str) -> dict:
"""Fetch key user attributes via targeted Honcho queries."""
peer = honcho.peer(user_id)
return {
"communication_style": peer.chat("What communication style does this user prefer? Be concise."),
"expertise_level": peer.chat("What is this user's technical expertise level? Be concise."),
"current_goals": peer.chat("What are this user's current goals or priorities? Be concise."),
"preferences": peer.chat("What key preferences should I know about this user? Be concise.")
}
def build_system_prompt(user_context: dict) -> str:
return f"""You are a helpful assistant. Here's what you know about this user:
Communication style: {user_context['communication_style']}
Expertise level: {user_context['expertise_level']}
Current goals: {user_context['current_goals']}
Key preferences: {user_context['preferences']}
Tailor your responses accordingly."""
```
**TypeScript:**
```typescript
async function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {
const peer = await honcho.peer(userId);
const [style, expertise, goals, preferences] = await Promise.all([
peer.chat("What communication style does this user prefer? Be concise."),
peer.chat("What is this user's technical expertise level? Be concise."),
peer.chat("What are this user's current goals or priorities? Be concise."),
peer.chat("What key preferences should I know about this user? Be concise.")
]);
return {
communicationStyle: style,
expertiseLevel: expertise,
currentGoals: goals,
preferences: preferences
};
}
```
### Pattern C: Get Context for LLM Integration
Use `context()` for conversation history with built-in LLM formatting.
**Python:**
```python
import openai
session = honcho.session("conversation-123")
user = honcho.peer("user-123")
assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False))
# Get context formatted for your LLM
context = session.context(
tokens=2000,
peer_target=user.id, # Include representation of this user
summary=True # Include conversation summaries
)
# Convert to OpenAI format
messages = context.to_openai(assistant=assistant)
# Or Anthropic format
# messages = context.to_anthropic(assistant=assistant)
# Add the new user message
messages.append({"role": "user", "content": "What should I focus on today?"})
response = openai.chat.completions.create(
model="gpt-4",
messages=messages
)
# Store the exchange
session.add_messages([
user.message("What should I focus on today?"),
assistant.message(response.choices[0].message.content)
])
```
**TypeScript:**
```typescript
import OpenAI from 'openai';
const session = await honcho.session("conversation-123");
const user = await honcho.peer("user-123");
const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } });
// Get context formatted for your LLM
const context = await session.context({
tokens: 2000,
peerTarget: user.id, // Include representation of this user
summary: true // Include conversation summaries
});
// Convert to OpenAI format
const messages = context.toOpenAI(assistant);
// Or Anthropic format
// const messages = context.toAnthropic(assistant);
// Add the new user message
messages.push({ role: "user", content: "What should I focus on today?" });
const openai = new OpenAI();
const response = await openai.chat.completions.create({
model: "gpt-4",
messages
});
// Store the exchange
await session.addMessages([
user.message("What should I focus on today?"),
assistant.message(response.choices[0].message.content!)
]);
```
## Streaming Responses
**Python:**
```python
stream = peer.chat_stream("What do we know about this user?")
for chunk in stream:
print(chunk, end="", flush=True)
```
**TypeScript:**
```typescript
const stream = await peer.chatStream("What do we know about this user?");
for await (const chunk of stream) {
process.stdout.write(chunk);
}
```
The SDK is sync-by-default in Python (with an `.aio` async namespace) and async-only in TypeScript — match the client to your framework. Full sync/async guidance and the base client/peer/session/message code are in `references/core-patterns.md`.
## Integration Checklist

View File

@ -0,0 +1,257 @@
# Agent Access Patterns
How your AI accesses Honcho's user context. Pick based on the interview answer to Question Set 2. These build on the client/peer/session setup in `core-patterns.md`.
- **Pattern A — Dialectic chat as a tool call** (recommended for agents): the agent decides when to query context on-demand.
- **Pattern B — Pre-fetch with targeted queries**: fetch a fixed set of attributes before each LLM call.
- **Pattern C — `context()` for LLM integration**: inject conversation history + representation into the prompt.
> **Speed note.** `chat()` runs live dialectic reasoning (a few seconds) — Patterns A and B call it. `context()` (Pattern C) is a near-instant read. Prefer `context()` for per-turn grounding; reach for `chat()` when you genuinely need a reasoned answer.
## Pattern A: Dialectic Chat as a Tool Call (Recommended for Agents)
Make Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.
**Python (OpenAI function calling):**
```python
import openai
from honcho import Honcho
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
# Define the tool for your agent
honcho_tool = {
"type": "function",
"function": {
"name": "query_user_context",
"description": "Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'"
}
},
"required": ["query"]
}
}
}
def handle_honcho_tool_call(user_id: str, query: str) -> str:
"""Execute the Honcho chat tool call."""
peer = honcho.peer(user_id)
return peer.chat(query)
# Use in your agent loop
def run_agent(user_id: str, user_message: str):
messages = [{"role": "user", "content": user_message}]
response = openai.chat.completions.create(
model="gpt-4",
messages=messages,
tools=[honcho_tool]
)
# Handle tool calls
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
if tool_call.function.name == "query_user_context":
import json
args = json.loads(tool_call.function.arguments)
result = handle_honcho_tool_call(user_id, args["query"])
# Continue conversation with tool result...
```
**TypeScript (OpenAI function calling):**
```typescript
import OpenAI from 'openai';
import { Honcho } from '@honcho-ai/sdk';
const honcho = new Honcho({
workspaceId: "my-app",
apiKey: process.env.HONCHO_API_KEY
});
const honchoTool: OpenAI.ChatCompletionTool = {
type: "function",
function: {
name: "query_user_context",
description: "Query Honcho to retrieve relevant context about the user based on their history and preferences.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "A natural language question about the user"
}
},
required: ["query"]
}
}
};
async function handleHonchoToolCall(userId: string, query: string): Promise<string> {
const peer = await honcho.peer(userId);
return await peer.chat(query);
}
```
## Pattern B: Pre-fetch Context with Targeted Queries
For simpler integrations, fetch user context before the LLM call using pre-defined queries.
**Python:**
```python
def get_user_context_for_prompt(user_id: str) -> dict:
"""Fetch key user attributes via targeted Honcho queries."""
peer = honcho.peer(user_id)
return {
"communication_style": peer.chat("What communication style does this user prefer? Be concise."),
"expertise_level": peer.chat("What is this user's technical expertise level? Be concise."),
"current_goals": peer.chat("What are this user's current goals or priorities? Be concise."),
"preferences": peer.chat("What key preferences should I know about this user? Be concise.")
}
def build_system_prompt(user_context: dict) -> str:
return f"""You are a helpful assistant. Here's what you know about this user:
Communication style: {user_context['communication_style']}
Expertise level: {user_context['expertise_level']}
Current goals: {user_context['current_goals']}
Key preferences: {user_context['preferences']}
Tailor your responses accordingly."""
```
**TypeScript:**
```typescript
async function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {
const peer = await honcho.peer(userId);
const [style, expertise, goals, preferences] = await Promise.all([
peer.chat("What communication style does this user prefer? Be concise."),
peer.chat("What is this user's technical expertise level? Be concise."),
peer.chat("What are this user's current goals or priorities? Be concise."),
peer.chat("What key preferences should I know about this user? Be concise.")
]);
return {
communicationStyle: style,
expertiseLevel: expertise,
currentGoals: goals,
preferences: preferences
};
}
```
## Pattern C: Get Context for LLM Integration
Use `context()` for conversation history with built-in LLM formatting. This is a near-instant read — no dialectic reasoning — so it's the cheapest way to ground each turn.
**Python:**
```python
import openai
session = honcho.session("conversation-123")
user = honcho.peer("user-123")
assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False))
# Get context formatted for your LLM
context = session.context(
tokens=2000,
peer_target=user.id, # Include representation of this user
summary=True # Include conversation summaries
)
# Convert to OpenAI format
messages = context.to_openai(assistant=assistant)
# Or Anthropic format
# messages = context.to_anthropic(assistant=assistant)
# Add the new user message
messages.append({"role": "user", "content": "What should I focus on today?"})
response = openai.chat.completions.create(
model="gpt-4",
messages=messages
)
# Store the exchange
session.add_messages([
user.message("What should I focus on today?"),
assistant.message(response.choices[0].message.content)
])
```
**TypeScript:**
```typescript
import OpenAI from 'openai';
const session = await honcho.session("conversation-123");
const user = await honcho.peer("user-123");
const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } });
// Get context formatted for your LLM
const context = await session.context({
tokens: 2000,
peerTarget: user.id, // Include representation of this user
summary: true // Include conversation summaries
});
// Convert to OpenAI format
const messages = context.toOpenAI(assistant);
// Or Anthropic format
// const messages = context.toAnthropic(assistant);
// Add the new user message
messages.push({ role: "user", content: "What should I focus on today?" });
const openai = new OpenAI();
const response = await openai.chat.completions.create({
model: "gpt-4",
messages
});
// Store the exchange
await session.addMessages([
user.message("What should I focus on today?"),
assistant.message(response.choices[0].message.content!)
]);
```
### What `context()` returns
`session.context()` bundles the session-local view you can drop straight into an LLM call:
- **Recent messages** from the session, trimmed to the `tokens` budget.
- **Conversation summaries** when `summary=True` — the two-tier short/long summaries so older turns still count without spending the full token budget.
- **The target peer's representation** when you pass `peer_target` — Honcho's synthesized understanding of that user, folded in. Omit it and you get session-local context only (no cross-session memory).
The `to_openai()` / `to_anthropic()` helpers format all of that as a `messages` array for the respective provider. Pass your `assistant` peer so its turns are tagged as the assistant role.
## Streaming Responses
```python
stream = peer.chat_stream("What do we know about this user?")
for chunk in stream:
print(chunk, end="", flush=True)
```
```typescript
const stream = await peer.chatStream("What do we know about this user?");
for await (const chunk of stream) {
process.stdout.write(chunk);
}
```

View File

@ -0,0 +1,146 @@
# Core Integration Patterns
The base SDK boilerplate for any Honcho integration: choosing sync vs async, initializing the client, creating peers, configuring sessions, and adding messages. Read this once you've chosen your entities and session structure (Phases 12). For the agent-facing recall patterns (tool call, pre-fetch, `context()`), see `agent-patterns.md`.
## Sync vs Async
**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.
**Python** — The SDK provides both sync and async interfaces:
- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)
- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)
```python
# Sync usage (Flask, Django, scripts)
from honcho import Honcho
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
peer = honcho.peer("user-123")
response = peer.chat("What does this user prefer?")
# Async usage (FastAPI, Starlette)
from honcho import Honcho
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
peer = await honcho.aio.peer("user-123")
response = await peer.aio.chat("What does this user prefer?")
```
Match the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The examples below show sync Python; swap to `.aio` equivalents for async codebases.
## 1. Initialize with a Single Workspace
Use ONE workspace for your entire application. The workspace name should reflect your app/product.
**Python:**
```python
from honcho import Honcho
import os
# Sync client (Flask, Django, scripts)
honcho = Honcho(
workspace_id="your-app-name",
api_key=os.environ["HONCHO_API_KEY"],
environment="production"
)
# Async client (FastAPI, Starlette) — use honcho.aio for all operations
# honcho.aio.peer(), honcho.aio.session(), etc.
```
**TypeScript:**
```typescript
import { Honcho } from '@honcho-ai/sdk';
// All methods are async by default
const honcho = new Honcho({
workspaceId: "your-app-name",
apiKey: process.env.HONCHO_API_KEY,
environment: "production"
});
```
## 2. Create Peers for ALL Entities
Create peers for **every entity** in your business logic - users AND AI assistants.
**Python:**
```python
from honcho.api_types import PeerConfig
# Human users
user = honcho.peer("user-123")
# AI assistants - set observe_me=False so Honcho doesn't model the AI
assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False))
support_bot = honcho.peer("support-bot", configuration=PeerConfig(observe_me=False))
```
**TypeScript:**
```typescript
// Human users
const user = await honcho.peer("user-123");
// AI assistants - set observeMe=false so Honcho doesn't model the AI
const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } });
const supportBot = await honcho.peer("support-bot", { configuration: { observeMe: false } });
```
## 3. Multi-Peer Sessions
Sessions can have multiple participants. Configure observation settings per-peer.
**Python:**
```python
from honcho.api_types import SessionPeerConfig
session = honcho.session("conversation-123")
# User is observed (Honcho builds a model of them)
user_config = SessionPeerConfig(observe_me=True, observe_others=True)
# AI is NOT observed (no model built of the AI)
ai_config = SessionPeerConfig(observe_me=False, observe_others=True)
session.add_peers([
(user, user_config),
(assistant, ai_config)
])
```
**TypeScript:**
```typescript
const session = await honcho.session("conversation-123");
await session.addPeers([
[user, { observeMe: true, observeOthers: true }],
[assistant, { observeMe: false, observeOthers: true }]
]);
```
## 4. Add Messages to Sessions
**Python:**
```python
session.add_messages([
user.message("I'm having trouble with my account"),
assistant.message("I'd be happy to help. What seems to be the issue?"),
user.message("I can't reset my password")
])
```
**TypeScript:**
```typescript
await session.addMessages([
user.message("I'm having trouble with my account"),
assistant.message("I'd be happy to help. What seems to be the issue?"),
user.message("I can't reset my password")
]);
```

View File

@ -51,7 +51,7 @@ add_messages_to_session session_id: "<session-id>"
## Speed: reads vs. reasoning
- **`chat` is the slow one** — it runs the dialectic (live reasoning over the user's memory), so it takes a few seconds. Use it when you need a reasoned answer, not for every turn.
- **`get_context` / `get_peer_context` / `get_representation` / `search` are reads** — near-instantaneous. Reach for these first when you just need the current representation or history; only call `chat` when you actually need reasoning.
- **`get_session_context` / `get_peer_context` / `get_representation` / `search` are reads** — near-instantaneous. Reach for these first when you just need the current representation or history; only call `chat` when you actually need reasoning.
## Reasoning levels

View File

@ -26,11 +26,21 @@ Reasoning happens **asynchronously**. After you record a turn, don't poll or wai
Do this every conversation. It's the whole skill.
1. **Once per conversation** — make sure there's a session with you and the user as peers (observe the user, don't observe yourself).
2. **Before responding, when personalization helps** — pull the user's current context (`get_context` / `get_representation`) or search past messages (`search`) — these are fast reads. For a reasoned answer to a specific question, ask the dialectic (`chat`) — that one takes a few seconds, so use it when it earns its keep.
2. **Before responding, when personalization helps** — pull the user's current context (`get_session_context` / `get_representation`) or search past messages (`search`) — these are fast reads. For a reasoned answer to a specific question, ask the dialectic (`chat`) — that one takes a few seconds, so use it when it earns its keep.
3. **After every exchange** — record both the user's message and your reply. This is what makes Honcho learn. Don't skip it.
Optionally, when you learn a durable fact you don't want to wait for background reasoning to surface, **store a conclusion** directly.
## What you get back when you recall
Three ways to pull memory, cheapest first:
- **Representation** (`get_representation`) — Honcho's synthesized understanding of the user as text, ready to drop straight into a system prompt. Near-instant read.
- **Context** (`get_session_context`) — the fuller session view: a session summary + recent messages covering the conversation, and — *only if you target a peer* — that peer's representation folded in. Without a peer target it's session-local (recent turns + summary) and carries no cross-conversation memory. Near-instant read.
- **Dialectic** (`chat`) — a *reasoned* natural-language answer to a specific question ("How does this user like to receive feedback?"). Runs live reasoning, so it takes a few seconds. Use it when a plain read won't answer the question.
The dialectic (`chat`) also takes a **reasoning level** that trades speed for depth — from `minimal` (fast factual lookup) through `low` (the default balance) to `max` (deep synthesis for the hardest questions). Pick the lowest level that answers the question; higher levels are slower and cost more. The full level-by-level table and model routing live in the path skills (`honcho-mcp`, `honcho-cli`) and the [chat docs](https://honcho.dev/docs/v3/documentation/features/chat.md).
---
## Pick your access path
@ -59,7 +69,7 @@ You need a Honcho API key — get one free at <https://app.honcho.dev> (starts w
- **One stable peer ID per entity.** Reuse the same `peer_id` for a person across every session and channel; splitting them (`user`, `user-web`, `user-discord`) builds separate representations and fragments memory.
- **Scope sessions so reasoning actually fires.** Reasoning is token-batched per peer (~1,000 tokens within a session). Scope a session to one active interaction (per-conversation, per-channel, per-task, per-project); create a new one when context genuinely resets (new topic, new day), reuse it while context should keep accumulating. Many tiny sessions each stall below the threshold and never get reasoned over.
- **Don't block on reasoning.** It's asynchronous. Respond now; the representation will be richer next time.
- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_context`, `get_representation`, `search`) is a near-instant read — use it freely. The dialectic (`chat`) runs live reasoning and takes a few seconds, so save it for when you genuinely need a reasoned answer, not every turn.
- **Reads are cheap; reasoning isn't.** Fetching the representation/context (`get_session_context`, `get_representation`, `search`) is a near-instant read — use it freely. The dialectic (`chat`) runs live reasoning and takes a few seconds, so save it for when you genuinely need a reasoned answer, not every turn.
- **Check before you store.** Background reasoning derives most conclusions automatically. Store a conclusion manually only for a durable fact you want available immediately; `list`/`query` first to avoid duplicates.
- **One workspace per app/user-context.** Don't scatter the same user's memory across multiple workspaces.
- **Unify memory across tools with a shared workspace + peer ID.** To give one user continuous memory across several apps or agents (e.g. Claude Code, Cursor, your own app), point them at the same workspace and reuse the same peer ID — that shared ID is what links the representation. See [Unified Memory Setup](https://honcho.dev/docs/v3/guides/recipes/unified-memory-setup.md).

View File

@ -21,6 +21,8 @@ This skill migrates code from `honcho` Python SDK v1.6.0 to v2.1.1 (required for
## Quick Migration
The four changes below are the structural breaks you hit first — apply them inline. The remaining renames and smaller changes are one-line lookups in the [Quick Reference Table](#quick-reference-table); the full before/after for every change lives in [DETAILED-CHANGES.md](DETAILED-CHANGES.md).
### 1. Update async architecture
```python
@ -98,177 +100,21 @@ session.get_configuration()
client.get_configuration()
```
### 5. Update method names
### Changes 515 (at a glance)
```python
# Before
peer.working_rep()
peer.get_context()
peer.get_sessions()
session.get_context()
session.get_summaries()
session.get_messages()
session.get_peers()
session.get_peer_config()
client.get_peers()
client.get_sessions()
client.get_workspaces()
The remaining breaks are one-line mappings in the [Quick Reference Table](#quick-reference-table) below, with full before/after in [DETAILED-CHANGES.md](DETAILED-CHANGES.md):
# After
peer.representation()
peer.context()
peer.sessions()
session.context()
session.summaries()
session.messages()
session.peers()
session.get_peer_configuration()
client.peers()
client.sessions()
client.workspaces()
```
### 6. Update streaming
```python
# Before
response = peer.chat("query", stream=True)
for chunk in response:
print(chunk, end="")
# After
stream = peer.chat_stream("query")
for chunk in stream:
print(chunk, end="")
```
### 7. Update queue status (formerly deriver)
```python
# Before
from honcho_core.types import DeriverStatus
status = client.get_deriver_status()
status = client.poll_deriver_status(timeout=300.0) # Removed!
# After
from honcho.api_types import QueueStatusResponse
status = client.queue_status()
# poll_deriver_status removed - implement polling manually if needed
```
### 8. Update representation parameters
```python
# Before
rep = peer.working_rep(
include_most_derived=True,
max_observations=50
)
# After
rep = peer.representation(
include_most_frequent=True,
max_conclusions=50
)
```
### 9. Move update_message to session
```python
# Before
updated = client.update_message(message=msg, metadata={"key": "value"}, session="sess-id")
# After
updated = session.update_message(message=msg, metadata={"key": "value"})
```
### 10. Update card() return type and method name
```python
# Before
card: str = peer.card() # Returns str
# After (v2.0.0+)
card: list[str] | None = peer.get_card() # Returns list[str] | None
if card:
print("\n".join(card))
# peer.card() still works but is deprecated — use get_card()
# New in v2.0.1: set_card()
peer.set_card(["Prefers dark mode", "Located in US"])
```
### 11. Strict input validation (v2.0.2+)
All input models now reject unknown fields via `extra="forbid"` Pydantic validation. Previously, misspelled or extraneous fields were silently ignored.
```python
# Before (v2.0.1 and earlier) — silently ignored
peer = client.peer("user-1", configuration=PeerConfig(observe_mee=True)) # typo silently ignored
# After (v2.0.2+) — raises ValidationError
peer = client.peer("user-1", configuration=PeerConfig(observe_mee=True)) # ValidationError!
```
### 12. peer() and session() always make API calls (v2.1.0+)
**Breaking**: `peer()` and `session()` now always make a get-or-create API call. Previously, calling without metadata/configuration returned a lazy object with no API call.
```python
# Before (v2.0.x) — no API call without options
peer = client.peer("user-123") # Lazy, no network request
# After (v2.1.0+) — always hits the API
peer = client.peer("user-123") # Makes POST to /peers (get-or-create)
# Async
peer = await client.aio.peer("user-123") # Also always hits API
```
### 13. New properties and methods (v2.1.0+)
```python
# created_at on Peer and Session
peer = client.peer("user-123")
print(peer.created_at) # datetime | None
session = client.session("sess-1")
print(session.created_at) # datetime | None
# is_active on Session
print(session.is_active) # bool | None
# get_message() on Session
msg = session.get_message("msg-id")
# Async: msg = await session.aio.get_message("msg-id")
```
### 14. Pagination parameters on list methods (v2.1.0+)
All list methods now accept `page`, `size`, and `reverse` parameters:
```python
# Before (v2.0.x) — only filters
peers_page = client.peers(filters={"metadata": {"role": "admin"}})
# After (v2.1.0+) — pagination controls
peers_page = client.peers(
filters={"metadata": {"role": "admin"}},
page=2,
size=25,
reverse=True
)
# Works on: client.peers(), client.sessions(), peer.sessions(),
# session.messages(), scope.list()
```
### 15. Broader HTTP retry logic (v2.1.1+)
The SDK now retries on `httpx.TimeoutException`, `httpx.NetworkError`, and `httpx.RemoteProtocolError` (previously only `httpx.TimeoutException` and `httpx.ConnectError`). These are mapped to the SDK's `TimeoutError` and `ConnectionError` respectively. No code changes needed — this is transparent.
- **5. Method renames**`get_context()`→`context()`, `get_sessions()`→`sessions()`, `get_messages()`→`messages()`, etc. (drop the `get_` prefix)
- **6. Streaming**`chat("q", stream=True)``chat_stream("q")`
- **7. Queue status**`get_deriver_status()`→`queue_status()`; `poll_deriver_status()` removed (poll manually)
- **8. Representation params**`include_most_derived=`→`include_most_frequent=`, `max_observations=`→`max_conclusions=`
- **9. `update_message`** — moved from `client.update_message(..., session=)` to `session.update_message(...)`
- **10. Card**`card(): str``get_card(): list[str] | None`; new `set_card(list[str])` (v2.0.1); `card()` deprecated
- **11. Strict validation** (v2.0.2) — unknown config fields now raise `ValidationError` instead of being ignored
- **12. `peer()`/`session()`** (v2.1.0) — now always make a get-or-create API call (previously lazy)
- **13. New properties** (v2.1.0) — `created_at`, `session.is_active`, `session.get_message(id)`
- **14. Pagination** (v2.1.0) — `page=`, `size=`, `reverse=` on all list methods
- **15. Retries** (v2.1.1) — broader HTTP retry coverage; transparent, no code changes
## Quick Reference Table