Central master coordinates query agents (send questions) and action agents (expose tools). The orchestrator (default gpt-4o-mini, overridable via OPENAGENT_CHAT_MODELS / POST /query) decides direct answers vs tool calls, optional auto_mode multi-step loops, inter-agent collaboration (peer-to-peer negotiation relayed through the master), prompt profiles (default, auto_multi, reasoning), and long-context summarization. Action agents can be in-memory (WebSocket), invocation (HTTP + Redis), or Kubernetes sandbox (one Pod per agent or pool runner). A React UI wires agents on a canvas, runs POST /query chat with attachments, exports/restores workspace YAML, and can generate agent code via the master LLM.
flowchart TB
subgraph ui [Web UI]
Canvas[React Flow Canvas]
Chat[Orchestrator Chat]
end
subgraph master [Master]
WS[WebSocket /ws]
QueryAPI[POST /query]
RegisterAPI[POST /register]
Callback[POST /tool_callback]
Orch[Orchestrator]
Reg[Registry]
end
subgraph redis [Redis]
Cache[(Agent Cache)]
Sessions[(Sessions)]
end
subgraph action_inmemory [Action In-Memory]
WeatherAgent[weather-agent]
end
subgraph action_invocation [Action Invocation]
InvokeServer[HTTP Server]
end
Canvas -->|link agents| RegisterAPI
Chat -->|POST /query| QueryAPI
QueryAPI -->|loopback WS| Orch
Orch -->|agents_used| Chat
WS --> Orch
Orch --> Cache
Orch --> Sessions
Orch --> Reg
Reg --> WeatherAgent
RegisterAPI --> Cache
InvokeServer -->|register once| RegisterAPI
Orch -->|POST tool call| InvokeServer
InvokeServer -->|POST result| Callback
Callback --> WS
- Query agent: Connects via WebSocket, sends queries, receives
query_result. The UI provides a built-in query client on the canvas (always online). - In-memory action agent: Connects via WebSocket, registers tools, receives
tool_callover WS. Not stored in Redis; gone when master or agent restarts. - Invocation action agent: Runs an HTTP server. Registers once via
POST /register(or WS withinvocation_url). Master storesinvocation_base_url+ per-toolendpointin Redis. On tool call, master POSTs to{base}{endpoint}; agent POSTs result to master/tool_callback. Survives master restart; master health-checksGET {base}/healthon startup and/refresh.
Callable tool handlers (symmetric): For WebSocket action agents, use OrchestratorActionAgent(agent_id, tools, tool_handler) with an async callable (optional progress_callback in the signature). For HTTP invocation, use InvocationActionAgent(agent_id, tools, invocation_base_url, tool_handler=...) — same pattern; the callable may accept progress (invocation style) or progress_callback. Or subclass OrchestratorWebSocketActionAgent / InvocationActionAgent and implement handle_tool on the class instead.
source ~/base/bin/activate # or your venv
pip install -r requirements.txtCopy .env.example to .env and set:
OPENAI_API_KEY=sk-...
REDIS_URL=redis://127.0.0.1:6379
MASTER_BASE_URL=http://127.0.0.1:8000Optional for invocation agent: INVOCATION_BASE_URL, INVOCATION_PORT, MASTER_WS.
Same WebSocket pattern as other action agents (OrchestratorActionAgent): extract (chunks only) and index / search_knowledge are separate tools on separate agents (or the same bundle process, still separate tool calls). Implementation lives under openagent/rag_*.py. Extractor and indexer entrypoints live in system_agents/ (document_extractor_agent, document_indexer_agent). The indexer stores vectors in Redis at REDIS_URL in its own process (run a Redis sidecar or bundle Redis in the indexer container). Optional one-process bundle: python -m system_agents.rag_stack_ws.
Docker / Kubernetes (recommended)
Build the runtime image, then either:
- One container, two registrations:
python -m system_agents.rag_stack_ws - One agent per Pod:
python -m system_agents.document_extractor_agent,python -m system_agents.document_indexer_agent
Set REDIS_URL on the master (for session blobs / attachments). For K8s sandbox new_pod deploys of document-indexer-agent, the master adds a Redis sidecar in the same Pod and sets REDIS_URL=redis://127.0.0.1:6379 on the indexer (override with OPENAGENT_K8S_INDEXER_REDIS_SIDECAR=0 and OPENAGENT_K8S_INDEXER_REDIS_URL if you use a shared cluster Redis instead).
Set MASTER_WS (and OPENAI_API_KEY) in each Pod so agents reach the master.
Local (no image)
From repo root with venv + deps: PYTHONPATH=. python -m system_agents.rag_stack_ws (or each system_agents.* module separately)
In the UI, link RAG · Extractor and RAG · Indexer to the orchestrator. Attach files in chat uses POST /query attachments (stored in Redis on the master; the orchestrator prompt gets filenames + refs).
Useful env: OPENAGENT_EMBEDDING_MODEL, RAG_MAX_ATTACHMENT_BYTES, RAG_TOOL_CALL_TIMEOUT.
The UI polls GET /agents/discover to list system_agents/*.py then agents/*.py on the master (merged into the sidebar with the default RAG rows). Linking a WebSocket agent on the canvas triggers deploySingleAgentToSandbox when OPENAGENT_K8S_SANDBOX=1 (same path as sidebar Restore); without K8s, run the module locally after linking.
When OPENAGENT_K8S_SANDBOX=1 (default), the master can deploy agent code into the cluster via POST /internal/sandbox-agent/start (new_pod or pool placement). The UI uses this for canvas “deploy to sandbox” flows; GET /agents merges Redis sandbox metadata and live Pod status (and may attach log_tail for unhealthy workloads).
Minikube quick path: from repo root, ./start.sh builds openagent/agent-runtime:latest and openagent/agent-pool-runner:latest inside the Minikube Docker daemon and applies deploy/k8s/*.yaml. Then run the master on the host with a reachable bind address, e.g. OPENAGENT_K8S_SANDBOX=1 uvicorn master.app:app --host 0.0.0.0 --port 8000, and set OPENAGENT_K8S_MASTER_WS (or pass master_ws from the UI) to a WebSocket URL that resolves from inside Pods (see GET /internal/sandbox/status hint).
A React dashboard for managing the orchestrator workspace.
- Upload YAML to load master config and agent definitions, or Connect to master to discover agents already registered.
- Canvas (React Flow): the orchestrator node sits at the center. A built-in Query Client node is always present on the left (always online). The sidebar lists RAG · Extractor and RAG · Indexer after you connect to the master (even when
GET /agentsis empty); drag them onto the canvas and link to the orchestrator, then start the WS processes (PYTHONPATH=. python -m system_agents.document_extractor_agent, same fordocument_indexer_agent, orpython -m system_agents.rag_stack_wsfor both in one process). Drag other agents from YAML or from discovery; linking registers HTTP agents with the master. With K8s sandbox on, linking a WS agent can deploy it remotely instead of only localpython -m …. - Agent builder: natural-language generate + save flows call
POST /generate-agentandPOST /save-agenton the master (toggle withOPENAGENT_AGENT_GENERATOR). - Orchestrator chat: an integrated
POST /querypanel at the bottom. Attach files sendsattachments(base64) to the master; blobs are stored in Redis and only refs + filenames appear in the orchestrator prompt. Queries are scoped to canvas-linked action agents. Auto mode, plan approval, execute plan, model, and prompt profile map to the JSON fields documented under Master HTTP endpoints →POST /query. Follow-up messages share asession_iduntil you click New session (clears transcript and server-side session). - Agent highlighting: when the orchestrator answers a query, the canvas highlights which agent(s) were used with a purple glow (4-second fade). If the orchestrator answers from its own context, the orchestrator node itself glows.
- Export YAML: snapshot master URLs, agent rows, tools, handlers, and canvas layout into a portable YAML file. The built-in query client is stripped from exports (auto-injected on load).
- Restore on master: re-register HTTP agents from YAML in Redis (
POST /internal/workspace/restore).
YAML is parsed by openagent/yaml_runner.py (same schema the UI uploads). Agents can run inside the master via default-on OPENAGENT_YAML_RUNNER (UI upload or POST /internal/yaml-agents/start with {"yaml": "<text>"}). See openagent-ui/README.md.
All settings are read from the environment (and from .env via dotenv). Defaults are below.
| Env variable | Default | Description |
|---|---|---|
| Master | ||
REDIS_URL |
redis://127.0.0.1:6379 |
Redis URL (use 127.0.0.1 not localhost in containers—avoids IPv6 / errno 99). |
REDIS_AGENT_TTL_SECONDS |
86400 (24h) |
TTL for cached (invocation) agent keys. Refreshed when the agent is used. |
REDIS_SESSION_TTL_SECONDS |
86400 (24h) |
TTL for session keys; conversation history expires after this. |
MASTER_BASE_URL |
http://127.0.0.1:8000 |
Base URL of the master (HTTP). Used for tool callback URL and health. |
OPENAI_API_KEY |
— | Required. OpenAI API key for orchestrator and summarizer. |
| Orchestrator / models | ||
OPENAGENT_CHAT_MODELS |
(see config.py) |
Single global list: comma-separated OpenAI model ids for the orchestrator dropdown, POST /query orchestrator_model, and agent generator. First entry is the default when ORCHESTRATOR_MODEL is unset. |
ORCHESTRATOR_MODEL |
first of OPENAGENT_CHAT_MODELS |
Default orchestrator model when the client omits orchestrator_model. If set to an id not in the global list, that id is prepended to the list. |
AGENT_GENERATOR_MODEL |
same default as orchestrator | Default for POST /generate-agent when model is omitted. Must be in the global list (or prepended like ORCHESTRATOR_MODEL). |
ORCHESTRATOR_MODEL_ALLOWLIST |
— | Legacy only (used only when OPENAGENT_CHAT_MODELS is unset): comma-separated extras merged with ORCHESTRATOR_MODEL. Prefer OPENAGENT_CHAT_MODELS. |
ORCHESTRATOR_CONTEXT_MAX_TOKENS |
12000 |
Max tokens for orchestrator context (summaries + tail + tools + query). When exceeded, older turns are summarized. |
ORCHESTRATOR_RECENT_TURNS |
8 |
Number of recent raw turns to keep in full before summarizing the rest. |
SUMMARIZER_MODEL |
(none) | Model for summarizing long context. If unset, uses ORCHESTRATOR_MODEL. |
| Timeouts (seconds) | ||
HEALTH_CHECK_TIMEOUT |
2.0 |
Timeout for master health checks (e.g. invocation agent /health). |
TOOL_CALL_TIMEOUT |
30.0 |
Max time to wait for a tool result (WS or HTTP callback) before failing the query. |
TOOL_INVOKE_HTTP_TIMEOUT |
2.0 |
Timeout for master POST to invocation agent's tool endpoint. |
| Agents (client → master) | ||
MASTER_WS |
ws://127.0.0.1:8000/ws |
WebSocket URL used by query/action agents to connect to the master. |
WS_CONNECT_TIMEOUT |
10.0 |
Timeout for WebSocket connect. |
WS_REGISTRATION_TIMEOUT |
10.0 |
Timeout for registration response after register message. |
WS_KEEPALIVE_INTERVAL |
30 |
Seconds between agent→master ping frames (0 disables). Keeps WS alive through proxies. |
QUERY_RESPONSE_TIMEOUT |
180.0 |
Timeout for query agent to receive query_result after sending query. |
HTTP_REGISTER_TIMEOUT |
10.0 |
Timeout for invocation agent HTTP POST to /register. |
| Orchestrator auto-mode | ||
ORCHESTRATOR_AUTO_MAX_STEPS |
20 |
Max tool rounds per user message when auto_mode is on (POST /query). |
ORCHESTRATOR_AUTO_SPAWN_WAIT_SECONDS |
120 |
How long to wait for a sandboxed agent to register on WS after spawn. |
| Feature toggles | ||
OPENAGENT_YAML_RUNNER |
1 |
In-master YAML agent runner (POST /internal/yaml-agents/start, UI upload). 0 disables. |
OPENAGENT_WORKSPACE_RESTORE |
1 |
POST /internal/workspace/restore. 0 disables. |
OPENAGENT_HTTP_QUERY |
1 |
POST /query. 0 returns 403. |
OPENAGENT_AGENT_GENERATOR |
1 |
POST /generate-agent / models. 0 disables. |
| RAG / attachments | ||
OPENAGENT_EMBEDDING_MODEL |
text-embedding-3-small |
Embeddings for indexer. |
OPENAGENT_RAG_EXTRACT_MODEL |
gpt-4o-mini |
LLM for extract_document / PDF ingest. |
RAG_MAX_ATTACHMENT_BYTES |
2097152 |
Per-file cap for POST /query attachments. |
RAG_MAX_ATTACHMENTS |
5 |
Max files per query. |
RAG_TOOL_HTTP_TIMEOUT |
120.0 |
HTTP timeout for long RAG tool calls from master perspective. |
RAG_TOOL_CALL_TIMEOUT |
180.0 |
Max wait for RAG tool completion. |
RAG_EXTRACT_CACHE_TTL_SECONDS |
604800 |
Redis TTL for extract cache (7d). |
| Kubernetes sandbox | ||
OPENAGENT_K8S_SANDBOX |
1 |
Enable pool/new_pod deploy APIs and sandbox field on GET /agents. 0 for local-only. |
OPENAGENT_K8S_NAMESPACE |
agents |
Namespace for agent workloads. |
OPENAGENT_K8S_POOL_LABEL_SELECTOR |
openagent.io/role=agent-pool |
Label selector for pool runner Pods. |
OPENAGENT_K8S_MASTER_WS |
ws://host.minikube.internal:8000/ws |
WS URL injected into Pods when UI does not override (must be reachable from cluster). |
OPENAGENT_K8S_AGENT_IMAGE |
openagent/agent-runtime:latest |
Image for new_pod agents. |
OPENAGENT_K8S_AGENT_IMAGE_PULL_POLICY |
Never |
Pull policy (Minikube local builds). |
OPENAGENT_K8S_POOL_RUNNER_IMAGE |
openagent/agent-pool-runner:latest |
Pool runner image. |
OPENAGENT_K8S_POOL_RUNNER_PORT |
8080 |
Pool runner HTTP port inside the Pod. |
OPENAGENT_K8S_LOG_TAIL_LINES |
120 |
Log lines attached to unhealthy GET /agents sandbox rows. |
OPENAGENT_K8S_INDEXER_REDIS_SIDECAR |
1 |
Redis sidecar for document-indexer-agent in new_pod mode. |
OPENAGENT_K8S_INDEXER_REDIS_URL |
(REDIS_URL) | Used when sidecar is off. |
OPENAGENT_K8S_SANDBOX_RUNTIME_TTL |
604800 |
Redis TTL for sandbox placement metadata. |
| Invocation agent (demo) | ||
INVOCATION_PORT |
9000 |
Default port for demo invocation agent. |
INVOCATION_HOST |
127.0.0.1 |
Host for invocation base URL. |
INVOCATION_BASE_URL |
http://{INVOCATION_HOST}:{INVOCATION_PORT} |
Full base URL when not set explicitly. |
Terminal 1 – Redis (required for invocation agents and for master to list known agents)
redis-serverTerminal 2 – Master (WebSocket on 8000, HTTP APIs)
uvicorn master.app:app --reload --reload-dir master --reload-dir protocol --reload-dir openagent --reload-exclude "agents/**" --host 0.0.0.0 --port 8000Only master/, protocol/, and openagent/ are watched; --reload-exclude is a safety net if you ever use --reload without --reload-dir, so saving generated files under agents/ does not restart the master and drop WebSocket connections (WS agents must reconnect).
If you run uvicorn … --reload without --reload-dir, always add --reload-exclude "agents/**" or every agent save will reload the server.
Terminal 3 – Action agent (in-memory)
PYTHONPATH=. python sample_agents/action_weather.pyStays connected via WebSocket; tools are available only while this process and the master are running.
Terminal 4 – Action agent (invocation) (optional)
PYTHONPATH=. python sample_agents/demo_invocation_agent.pyStarts an HTTP server and registers with the master once. No WebSocket. Master invokes via HTTP; agent survives master restart (re-register or rely on Redis).
Terminal 5 – Query agent
PYTHONPATH=. python sample_agents/query_demo.pySends example queries (including a long-task that exercises progress); the orchestrator calls the weather or echo/get_time/long_task tools. Watch the master terminal for progress (WS) or progress (HTTP) lines.
| Endpoint | Description |
|---|---|
GET /health |
Liveness |
GET /agents |
Tracker (in-memory WS) + Redis invocation agents + optional K8s “stub” rows; includes sandbox when OPENAGENT_K8S_SANDBOX=1 |
GET /agents/discover |
Files for the UI sidebar: system_agents/*.py then agents/*.py (deduped by agent_id) |
GET /refresh |
Re-fetch Redis and health-check invocation agents; prints status to the master terminal |
POST /register |
HTTP action agent: invocation_base_url + tools (+ optional per-tool endpoint) → Redis |
POST /unregister |
Remove cached HTTP agent by agent_id |
POST /query |
Orchestrator via internal WS loopback. Body (JSON): query (required); optional session_id, allowed_agent_ids, attachments ([{filename, mime_type, content_base64}]), orchestrator_model, orchestrator_prompt_profile (default | auto_multi | reasoning), auto_mode, auto_require_plan_approval, auto_plan_execute. Disabled → 403 when OPENAGENT_HTTP_QUERY=0. |
GET /query/orchestrator-models |
default_model, allowed_models, prompt_profiles for the UI |
GET /query/session-rag-context?session_id= |
Attachment refs + session-side RAG profile from Redis (vectors still via indexer tools) |
POST /tool_callback |
Invocation agents POST tool results (call_id, success, result / error) |
POST /tool_progress |
Optional progress updates for long HTTP tools |
GET /tool_progress/{call_id} |
Poll latest progress |
POST /generate-agent |
LLM codegen from description (name, description, agent_type, transport, optional model, …). OPENAGENT_AGENT_GENERATOR=0 → 403. |
GET /generate-agent/models |
Model list + default for the generator UI |
POST /save-agent |
Persist generated Python under agents/<module>.py |
GET /agent-source?handler= |
Read source for a handler module path under the repo |
POST /internal/yaml-agents/start |
{"yaml": "<text>"} — run YAML agents in the master asyncio loop (OPENAGENT_YAML_RUNNER=0 → 403) |
POST /internal/workspace/restore |
{"yaml": "<text>", "register_http": true} — re-register HTTP agents from exported UI YAML |
GET /internal/sandbox/status |
Whether K8s sandbox is enabled + namespace + default master WS hint |
GET /internal/k8s/pools |
List pool runner Pods (403 if sandbox off) |
GET /internal/k8s/workloads |
Dedicated agent Pods + pool Pods overview |
POST /internal/sandbox-agent/start |
Deploy agent code: placement new_pod or pool (+ pool_pod_name), optional master_ws |
POST /internal/sandbox-agent/stop |
Tear down sandbox workload for agent_id |
GET /internal/sandbox-agent/logs?agent_id=&tail_lines= |
Fetch container logs for a deployed sandbox agent |
WebSocket at /ws: connect, send register, then query (query agents) or receive tool_call (action agents).
- Health: Expose
GET {invocation_base_url}/health→ 200 and e.g.{"status":"ok"}. - Tool call: Master POSTs to
{invocation_base_url}{tool.endpoint}with body:call_id,tool_name,arguments,callback_url. - Result: Agent POSTs to
callback_urlwithcall_id,success, and eitherresultorerror.
Each tool can have its own endpoint (e.g. /run, /get_time).
| Path | Description |
|---|---|
protocol/ |
Wire protocol (Pydantic: register, query, tool_call, tool_result, ToolSchema with optional endpoint) |
openagent/ |
Client helpers: AgentClient, connect_master, run_action_agent, register_invocation_agent, ToolSchema |
master/ |
FastAPI app, WebSocket, Redis cache, orchestrator, registry, tracker, collaboration manager, http_query_gateway |
sample_agents/ |
Runnable examples: action_weather.py, demo_invocation_agent.py, query_demo.py |
agents/ |
User- or UI-generated modules (POST /save-agent); empty in a fresh clone |
openagent-ui/ |
React dashboard: canvas (React Flow), sidebar, orchestrator chat, YAML import/export |
- Agent → Master:
register,query,tool_result,tool_progress,peer_message,collaboration_end,ping - Master → Agent:
registered,query_result,tool_call,collaboration_start,peer_message,pong,error
query_result includes an optional agents_used field (list of {agent_id, tool_name}) so clients can see which agents handled the query. Empty list means the orchestrator answered directly.
peer_message is bidirectional: agents send it to the master (with to_agent), the master stamps from_agent and relays to the target. collaboration_start carries the session task, peer list, and role. collaboration_end carries a summary string.
See protocol/messages.py for full fields. Register may include invocation_url (legacy) for action agents; then the master stores them in Redis and invokes via HTTP.
When a user query requires multiple agents to negotiate, iterate, or converge on a result — not just sequential tool calls — the orchestrator can start a collaboration session. Agents communicate peer-to-peer through the master (which relays and records every exchange), then the orchestrator synthesizes the outcome for the user.
sequenceDiagram
participant User
participant Orchestrator
participant Master
participant AgentA
participant AgentB
User->>Master: Query
Master->>Orchestrator: decide(query, snapshot)
Orchestrator->>Master: start_collaboration(agents, task)
Master->>Master: Create collab session in Redis
Master->>AgentA: CollaborationStart(session_id, peers, task, role=initiator)
Master->>AgentB: CollaborationStart(session_id, peers, task, role=responder)
AgentA->>Master: PeerMessage(to=B, content)
Master->>AgentB: PeerMessage(from=A, content)
AgentB->>Master: PeerMessage(to=A, content)
Master->>AgentA: PeerMessage(from=B, content)
AgentA->>Master: CollaborationEnd(summary)
Master->>AgentB: CollaborationEnd(summary)
Master->>Orchestrator: transcript
Orchestrator->>Master: synthesize answer
Master->>User: QueryResult
Discovery and handshake: Agents run in isolated containers (or local processes) and only know the master. They have no awareness of each other until the orchestrator decides collaboration is needed. The master handles discovery by sending each agent a CollaborationStart message containing the peer list — each peer's agent_id, metadata, and tools. Agents don't need network routes to each other; the master relays every PeerMessage between them. This works identically for local, pool, and dedicated-pod agents.
Message flow: The initiator (first agent in the list) sends first. Each PeerMessage carries a free-form content dict (proposals, counters, acceptances — whatever the agents' logic requires). The master appends every message to the collaboration session's entries[] in Redis before relaying. Any agent can signal completion by sending CollaborationEnd with a summary; the master notifies all other peers and resolves the session.
Safety guards: Sessions auto-terminate after COLLABORATION_TIMEOUT_SECONDS (default 120s) or COLLABORATION_MAX_MESSAGES (default 50) to prevent infinite loops.
Collaboration sessions are stored in Redis under a separate schema from orchestrator sessions:
| Aspect | Orchestrator sessions (session:) |
Collaboration sessions (collab:) |
|---|---|---|
| Key | session:{id} |
collab:{id} |
| Shape | flat list[turn] |
{metadata + entries[]} object |
| Entry types | answer_directly, call_tool, summary |
handshake, message, end |
| Participants | orchestrator ↔ one agent | N agents peer-to-peer |
| Linking | standalone | parent_session_id back-ref |
A reverse index at collab:by_parent:{parent_session_id} (Redis SET) lets you find all collaborations spawned from a given orchestrator session. When a collaboration completes, a summary turn (decision: "collaboration") is appended to the parent orchestrator session so follow-up queries see the result in context.
Inspect a collaboration:
# Full session document (metadata + all entries)
redis-cli GET 'collab:<collab_session_id>' | python3 -m json.tool
# Find collaborations for an orchestrator session
redis-cli SMEMBERS 'collab:by_parent:<session_id>'Pass a collaboration_handler to OrchestratorActionAgent. This auto-sets supports_collaboration: true in the agent's metadata (the orchestrator only considers agents with this flag for start_collaboration).
from openagent import OrchestratorActionAgent, ToolSchema
from openagent.client import CollaborationContext
async def handle_tool(tool_name, arguments, *, progress_callback=None):
...
async def handle_collaboration(ctx: CollaborationContext) -> str:
if ctx.role == "initiator":
await ctx.send_message(ctx.peers[0]["agent_id"], {"proposal": 70.0})
reply = await ctx.receive_message(timeout=60)
# reply is a PeerMessage or CollaborationEnd or None (timeout)
return f"Agreed on {reply.content}"
else:
msg = await ctx.receive_message(timeout=60)
await ctx.send_message(msg.from_agent, {"accept": True})
return "Accepted"
agent = OrchestratorActionAgent(
"my-agent", TOOLS, handle_tool,
collaboration_handler=handle_collaboration,
)CollaborationContext provides:
ctx.session_id,ctx.task,ctx.role("initiator"/"responder"),ctx.peers,ctx.contextawait ctx.send_message(to_agent, content_dict)— send aPeerMessageawait ctx.receive_message(timeout)— wait for nextPeerMessageorCollaborationEnd; returnsNoneon timeoutawait ctx.end(summary)— signal completion
When the auto-mode planner determines that generated agents need to collaborate, it sets requires_collaboration: true on those entries. The code generator then uses a collaboration-aware template that includes a handle_collaboration function with real negotiation logic.
| Env variable | Default | Description |
|---|---|---|
OPENAGENT_COLLABORATION |
1 |
Enable inter-agent collaboration. 0 disables. |
OPENAGENT_COLLAB_TIMEOUT |
120 |
Max seconds for a collaboration session before auto-termination. |
OPENAGENT_COLLAB_MAX_MESSAGES |
50 |
Max peer messages per session (prevents infinite loops). |
OPENAGENT_COLLAB_SESSION_TTL |
86400 |
Redis TTL for collaboration session keys. |
Conversations are session-scoped. The master creates or reuses a session per query so multi-turn context is preserved.
- Storage: Redis key
session:{session_id}; value is a JSON array of turns. Sessions expire afterREDIS_SESSION_TTL_SECONDS(default 24h). - Session ID: First query in a conversation omits
session_id; the master creates one (UUID) and returns it inquery_result.session_id. Subsequent queries send thatsession_idso the master loads the same turns. - Turns: Each turn is a dict:
query,query_id,decision("answer_directly","call_tool", or"collaboration"), and for tool callstool_agent_id,tool_name,tool_args,result/error. Collaboration turns havedecision: "collaboration",collab_session_id,agents,task,summary, andmessage_count. Summary turns (see below) havedecision: "summary",summary, andcovers_through_index. - Create/load/append:
master/session_store.py—create_session,load_session,append_turn. The orchestrator loads turns before each decision and appends a new turn after each answer or tool result.
A sample session file (raw turns plus summary turns as stored in Redis) is at experiments/results/sample_session_file.json. It shows interleaved raw turns (weather queries, tool calls, direct answers) and summary turns with covers_through_index.
When building orchestrator context, we keep token usage bounded by summarizing older turns and persisting summaries in the session.
- Context for each query = all previous summary turns (in order) + all raw turns after the last summary. So older conversation is compressed into summary blocks; only the “tail” after the last summary is sent in full.
- When over limit (
ORCHESTRATOR_CONTEXT_MAX_TOKENS): we take the raw tail, keep the lastORCHESTRATOR_RECENT_TURNSturns in full, and summarize the rest with the same orchestrator model. We append a summary turn to the session:{"decision": "summary", "summary": "<text>", "covers_through_index": N}.covers_through_indexis the index (in the full session list) of the last turn that summary covers; “raw turns after last summary” are all turns with index > last summary’scovers_through_index. - Config:
ORCHESTRATOR_CONTEXT_MAX_TOKENS(default 12000),ORCHESTRATOR_RECENT_TURNS(default 8). Lower them to trigger summarization earlier (e.g. 1200 and 2 for testing). - Token counting:
master/session_context.pyuses tiktoken (cl100k_base). Summarization and message building live inbuild_orchestrator_messages_async; the master appends the returned summary turn to the session when present.
To reset cached (invocation) agents:
redis-cli FLUSHDBThen restart the master; invocation agents must re-register.