Build with Ampersand
Ampersand runs your agent and keeps its conversation history. You send messages, provide the tools it can use, and decide where its replies appear.
How it works
An agent defines the assistant. A session holds its conversation and a copy of the agent settings. You can create several sessions with the same agent settings; each session keeps its own history.
| Concept | What it does |
|---|---|
| Agent | The model, instructions, and tools that define how an assistant behaves. |
| Session | One assistant's saved conversation. Reuse its ID to continue without resending history. |
| Turn | The work triggered by input: generate a reply, request tools, or finish silently. A tool result lets the same turn continue. |
| Event | Input you send to a session, or a progress update you receive from it. |
Start with sessions.create(agent=..., input=...). Send later messages and tool
results through sessions.events.create(session_id, events=[...]). When the
session is idle, new input starts a turn.
Read events through SSE, or retrieve saved session and turn state. A session can continue across many turns.
Send your first message
Set up your client
The Python examples use the official OpenAI Python client. They are tested with
openai==3.24.0.
Get an API key from the developer console.
See Pricing and limits for token prices and account tiers.
In Bash or Zsh, replace YOUR_API_KEY and set:
export AMPERSAND_BASE_URL="https://ampersand.humansand.ai/v1"
export AMPERSAND_API_KEY="YOUR_API_KEY"
export AMPERSAND_MODEL="ampersand-v0.1"
Keep your key on your backend. Do not put it in browser code or commit it to source control.
Create a session and read the answer
Create a session with a model and a first message. The Python example sets up the client, prints each event, and collects the answer. The JavaScript snippet shows the request; its download below includes client setup and the stream loop.
import os
from openai import OpenAI
with OpenAI(
base_url=os.environ["AMPERSAND_BASE_URL"],
api_key=os.environ["AMPERSAND_API_KEY"],
max_retries=0,
) as client:
with client.beta.agents.sessions.create(
agent={
"model": os.environ["AMPERSAND_MODEL"],
"instructions": "Help the team plan a reliable launch.",
},
environment={"type": "none"},
input="Suggest three checks before a product launch.",
stream=True,
).with_result_collection() as stream:
for event in stream:
print(event.to_json(indent=None), flush=True)
if event.type in {
"agent.session.idle",
"agent.session.failed",
"agent.session.requires_action",
}:
break
result = stream.get_final_result()
print(result.output_text)
session_id = result.session_id
const stream = await client.beta.agents.sessions.create({
agent: {
model: process.env.AMPERSAND_MODEL,
instructions: "Answer the user in text.",
},
environment: {type: "none"},
input: "Suggest three checks before a product launch.",
stream: true,
}, {signal: AbortSignal.timeout(120_000)});
curl --no-buffer --fail-with-body "${AMPERSAND_BASE_URL%/}/agents/sessions" \
-H "Authorization: Bearer $AMPERSAND_API_KEY" \
-H 'OpenAI-Beta: agents=v1' \
-H 'Content-Type: application/json' \
-d "$(python3 -c 'import json, os; print(json.dumps({"agent": {"model": os.environ["AMPERSAND_MODEL"], "instructions": "Answer the user in text."}, "environment": {"type": "none"}, "input": "Suggest three checks before a product launch.", "stream": True}))')"
Run this example
- Python: Install Python 3.12 or later and
uv. Download first_response.py, then runuv run --script first_response.py. The script installs the client in an isolated environment. - JavaScript: Use Node.js 22 or later. Run
npm install openai@7.28.0, download first_response.mjs to the same folder, then runnode first_response.mjs. - cURL: You need Python 3 to build the request JSON. Paste the command above into the terminal where you set the variables. It prints raw SSE events; press Ctrl-C after
agent.session.idle.
The Python example prints JSON events, followed by the answer. Each event includes
the session ID. It also saves that ID in session_id for the next request.
The JavaScript download prints the session ID, then the answer:
Session: sess_example
Check billing, tools, and recovery.
Your session ID and answer will differ.
Keep the session ID to continue the conversation. If the connection fails, use that ID to check the saved state. The Python and JavaScript downloads close the stream after the turn and stop with an error if it fails. The JavaScript download allows up to 120 seconds for the response.
Send a follow-up
Send the next message to the same session. Add this code after
session_id = result.session_id, inside the with OpenAI(...) as client: block:
client.beta.agents.sessions.events.create(
session_id,
events=[{
"type": "agent.session.input.message",
"input": [{
"role": "user",
"content": [{"type": "input_text", "text": "Which check should I do first?"}],
}],
}],
idempotency_key="launch-followup-1",
)
This request returns 202 with no body. For live output, use the complete example
below instead. It opens the stream before sending the message.
Send the message and read the answer
with client.beta.agents.sessions.events.stream(session_id) as stream:
client.beta.agents.sessions.events.create(
session_id,
events=[{
"type": "agent.session.input.message",
"input": [{
"role": "user",
"content": [{"type": "input_text", "text": "Which check should I do first?"}],
}],
}],
idempotency_key="launch-followup-1",
)
for event in stream:
if event.type == "agent.session.turn.output_text.done":
print(event.text)
elif event.type == "agent.session.turn.completed":
break
elif event.type in {"agent.session.turn.failed", "agent.session.turn.cancelled"}:
raise RuntimeError(f"Turn {event.turn_id}: {event.turn.status}; {event.turn.error}")
else:
raise RuntimeError("Stream closed before the turn finished. Check the saved session.")
These examples assume the previous turn has finished and one process writes to the session. Use a new idempotency key for each new message; reuse the same key and request body when retrying that message.
How tools work
A function tool has a name, a description, and a JSON Schema for its arguments.
When the agent returns agent.session.requires_action, your application runs
the requested function and returns its result so the same turn can continue.
Declare every action the agent can take as a function tool, including chat
actions such as posting or staying silent. Do not ask for them as markup in its
text; see chat actions.
See the returned event and handler for send_message,
or run the small function example.
The full application adds message
routing and permission checks for a group chat with personal assistants.
Next steps
-
Chat integration example: messaging, group/personal setup, and the recorded rollout.
-
OpenAI compatibility: supported features and differences.
-
Streaming, recovery, and webhooks: receive updates and handle interrupted connections.
-
API reference: endpoints, parameters, events, and errors.