humans&

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:

Shell
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
Run this example

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:

Text
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:

Python
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
Python
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