Server: Agents HTTP
Deploy agents as HTTP API servers using praisonai –serve or Agent.launch()
Deploy single or multi-agent systems as HTTP REST API servers.
Quick Start
- Install Dependencies
bash pip install "praisonaiagents[os]" - Set API Key
bash export OPENAI_API_KEY="your-key" - Initialize Agents
bash praisonai --init "helpful assistant" - Start Server (localhost)
bash praisonai serve agents --port 8000Expected Output:
📄 Loading workflow from: agents.yaml 🚀 Starting PraisonAI API server... Host: 127.0.0.1 Port: 8000 🚀 Multi-Agent HTTP API available at http://127.0.0.1:8000/agents ✅ FastAPI server started at http://127.0.0.1:8000 📚 API documentation available at http://127.0.0.1:8000/docs - Start Server (public bind)
A non-localhost host requires an API key:
bash export PRAISONAI_SERVE_API_KEY="$(openssl rand -hex 32)" praisonai serve agents --port 8000 --host 0.0.0.0 - Verify
bash curl http://localhost:8000/health
Security
praisonai serve agents drives YAML-defined tools (including execute_command), so binding it to a non-localhost interface without authentication is refused.
# Localhost — no key required
praisonai serve agents --port 8000
# Remote — key required (via env var)
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0
# Or via flag
praisonai serve agents --port 8000 --host 0.0.0.0 --api-key "your-secret"Clients then send the key in the Authorization: Bearer … header (mirrors jobs/server.py).
curl -X POST http://SERVER_IP:8000/agents \
-H "Authorization: Bearer your-secret" \
-H "Content-Type: application/json" \
-d '{"query": "What is AI?"}'Python – Single Agent
from praisonaiagents import Agent
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
llm="gpt-4o-mini"
)
agent.launch(path="/ask", port=8000, host="127.0.0.1") # loopback default; set "0.0.0.0" to expose (requires a token)Expected Output:
🚀 Agent 'Assistant' available at http://127.0.0.1:8000
✅ FastAPI server started at http://127.0.0.1:8000
📚 API documentation available at http://127.0.0.1:8000/docs
🔌 Available endpoints: /askCleaning up .launch() endpoints
Call Agent.close() when you’re done serving an agent — the launched HTTP endpoint is torn down, its route is removed from the FastAPI app, and the cached OpenAPI schema is invalidated so /docs and /openapi.json no longer advertise it. Only the agent’s own POST route on that path is dropped; other verbs bound to the same path (e.g. the built-in GET /health) are preserved.
from praisonaiagents import Agent
agent = Agent(name="Assistant", instructions="You are a helpful assistant.", llm="gpt-4o-mini")
agent.launch(path="/ask", port=8000)
# ... serve for a while ...
agent.close() # /ask stops accepting requests; /health, /docs still workPython – Multi-Agent
from praisonaiagents import Agent, AgentTeam
researcher = Agent(name="Researcher", instructions="Research topics", llm="gpt-4o-mini")
writer = Agent(name="Writer", instructions="Write content", llm="gpt-4o-mini")
agents = AgentTeam(agents=[researcher, writer])
agents.launch(path="/content", port=8000, host="127.0.0.1") # loopback default; set "0.0.0.0" to expose (requires a token)Expected Output:
🚀 Multi-Agent HTTP API available at http://127.0.0.1:8000/content
📊 Available agents for this endpoint (2): Researcher, Writer
🔗 Per-agent endpoints: /content/researcher, /content/writer
✅ FastAPI server started at http://127.0.0.1:8000
📚 API documentation available at http://127.0.0.1:8000/docsagents.yaml
framework: praisonai
topic: research and write content
roles:
researcher:
role: Researcher
goal: Research topics thoroughly
backstory: Expert researcher
tasks:
research_task:
description: Research the topic
expected_output: Research findings
writer:
role: Writer
goal: Write engaging content
backstory: Expert writer
tasks:
write_task:
description: Write based on research
expected_output: Written contentexport PRAISONAI_SERVE_API_KEY="$(openssl rand -hex 32)"
praisonai serve agents --port 8000 --host 0.0.0.0CLI Commands
# Start agents server (localhost default — no key needed)
praisonai serve agents --port 8000
# With custom host for remote access (key required — see Security)
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0
# With agents file
praisonai serve agents --file agents.yaml --port 8000
# Give the server more time on slow machines
export PRAISONAI_SERVER_READY_TIMEOUT=15
praisonai serve agents --port 8000| Option | Default | Description |
|---|---|---|
--port | 8000 | Server port |
--host | 127.0.0.1 | Server host (use 0.0.0.0 for remote) |
--file | agents.yaml | Agents YAML file |
--reload | false | Enable hot reload |
--api-key | – | API key. Required when --host is not 127.0.0.1 / localhost / ::1. Can also be set via PRAISONAI_SERVE_API_KEY. |
launch() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
path | str | / | API endpoint path |
port | int | 8000 | Server port |
host | str | 127.0.0.1 | Server host. Loopback by default; set "0.0.0.0" to expose, which requires PRAISONAI_LAUNCH_AUTH_TOKEN. |
debug | bool | False | Debug mode |
protocol | str | http | http or mcp |
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/agents | POST | Send query to all agents — runs through the cached generator (ToolResolver, tool_timeout, approval, guardrails, retry) |
/agents/{agent_name} | POST | Call a specific agent through the same pipeline as /agents. Returns 404 for an unknown name |
/{path} | POST | Send query to agent(s) |
/{path}/list | GET | List available agents |
/{path}/{agent_id} | POST | Call specific agent |
/health | GET | Health check |
/docs | GET | Swagger UI |
Example Request/Response
Request:
curl -X POST http://localhost:8000/ask \
-H "Content-Type: application/json" \
-d '{"query": "What is AI?"}'Response:
{
"response": "Artificial intelligence (AI) refers to..."
}Remote Access
Use host="0.0.0.0" to allow remote connections. For praisonai serve agents, a non-localhost bind requires an API key (see Security):
# CLI — key required for non-localhost
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0
# Python launch() — set a token before exposing publicly
export PRAISONAI_LAUNCH_AUTH_TOKEN="$(openssl rand -base64 32)"
agent.launch(path="/ask", port=8000, host="0.0.0.0") # binding non-loopback without a token auto-generates oneConnect from remote (include the Bearer key when serving agents):
curl -X POST http://SERVER_IP:8000/ask \
-H "Authorization: Bearer your-secret" \
-H "Content-Type: application/json" \
-d '{"query": "Hello"}'How It Works
The agents server builds one shared generator per app at startup, then builds a lightweight per-request generator for each call that borrows the cached generator’s warm pieces.
sequenceDiagram
participant Client
participant App as FastAPI (lifespan)
participant Cached as Cached Generator
participant PerReq as Per-request Generator
App->>Cached: build once at startup<br/>(warm adapter, config_list,<br/>tool_resolver, tool-timeout pool)
Client->>App: POST /agents or /agents/{name}
App->>PerReq: build per-request<br/>(own cli_config, borrows cached pieces)
PerReq->>PerReq: run through ToolResolver, tool_timeout,<br/>approval, guardrails, retry
PerReq-->>Client: response
App->>Cached: close() at shutdown<br/>(owns tool-timeout pool)
classDef app fill:#189AB4,stroke:#7C90A0,color:#fff
classDef cached fill:#10B981,stroke:#7C90A0,color:#fff
classDef perreq fill:#F59E0B,stroke:#7C90A0,color:#fff
class App app
class Cached cached
class PerReq perreq- Generator cached per app. A FastAPI
lifespanbuilds a singleAgentsGeneratorat startup andclose()s it at shutdown — no per-request YAML re-parse, framework re-resolution, or fresh 32-worker tool-timeout pool on every call. - Truly concurrent. Each request builds a lightweight per-request
AgentsGeneratorthat carries its owncli_configand borrows the cached generator’s warm, immutable pieces (adapter,config_list,tool_resolver, tool-timeout thread pool). Concurrent requests never share mutable state and are not serialised — throughput now scales with the event loop. - Cached pool ownership. The cached generator still owns and shuts down the tool-timeout thread pool at app shutdown; per-request generators treat it as borrowed and delegate leak accounting / recycling back to the owner (
_get_tool_timeout_executor,_note_leaked_worker,_timeout_owner_key). - Graceful fallback. If the cached generator can’t be built, the server falls back to the per-request
praisonai.arunpath. - Route convergence. Both
/agentsand/agents/{agent_name}run through the same YAML lowering (ToolResolver,tool_timeout,approval,guardrails, retry policy).POST /agents/{agent_name}returns404for an unknown name.
Per-tenant validation strictness and timeout pool
Scope YAML validation strictness and the sync-tool timeout pool per request instead of per process. Tenant A migrating strict configs no longer forces strictness on tenant B’s legacy YAMLs.
Pass strict_validation / tool_timeout_workers through the per-request generator’s cli_config — the same DI seam the fast-path already uses to borrow the cached generator’s warm pieces. No os.environ mutation, no cross-tenant bleed.
# Per-request generator borrows warm pieces from the cached generator
# (adapter, config_list, tool_resolver, tool-timeout pool ownership)
# and carries its own cli_config — no os.environ mutation, no cross-tenant bleed.
per_request = AgentsGenerator(
agent_file=tenant_agent_file,
framework=framework,
config_list=cached.config_list,
cli_config={
**base_cli_config,
"strict_validation": tenant.strict, # True/False per tenant
"tool_timeout_workers": tenant.pool_size, # e.g. 128 for a heavy tenant
},
tool_timeout_executor=cached._get_tool_timeout_executor(), # borrowed
tool_resolver=cached.tool_resolver,
adapter=cached.framework_adapter,
)Each toggle resolves the same way: explicit constructor arg first, then the matching cli_config key, then the env var, then the built-in default.
strict_validation:
explicit arg > cli_config["strict_validation"] > PRAISONAI_VALIDATE_STRICT env > False (default)
tool_timeout_workers:
explicit arg > cli_config["tool_timeout_workers"] > PRAISONAI_TOOL_TIMEOUT_WORKERS env > 32 (default)Environment Variables
| Variable | Default | Description |
|---|---|---|
PRAISONAI_SERVE_API_KEY | – | API key required to bind praisonai serve agents on a non-localhost host. The server exits with SystemExit if this is unset and no --api-key is passed when --host is not localhost. Clients authenticate with Authorization: Bearer <key>. |
PRAISONAI_SERVER_READY_TIMEOUT | 5.0 | Seconds to wait for the FastAPI server to become ready after .launch() / praisonai serve agents. A warning is logged if exceeded; startup continues. |
Troubleshooting
| Issue | Fix |
|---|---|
--api-key ... is required when binding to a non-localhost host at startup | You bound to a non-localhost host without a key. Set PRAISONAI_SERVE_API_KEY=... (or --api-key ...), or bind to 127.0.0.1 / localhost for a private-only server. |
| Port in use | lsof -i :8000 then kill process |
| No agents.yaml | praisonai --init "topic" |
| Missing API key | export OPENAI_API_KEY="your-key" |
Server exits immediately on --host 0.0.0.0 | Non-localhost bind needs auth: export PRAISONAI_SERVE_API_KEY="your-secret" (or pass --api-key) |
| Missing deps | pip install "praisonaiagents[os]" |
| Connection refused | Use host="0.0.0.0" for remote |
| Firewall blocking | Open port in firewall |
| Agent server slow to start / “did not become ready” warning | Set PRAISONAI_SERVER_READY_TIMEOUT=10 (seconds) before launching, or check server logs for the underlying startup error. |
Related
- Agents API Reference – Full API documentation
- Agents MCP – Deploy agents as MCP server
- Tools MCP – Deploy tools as MCP server
- Deploy CLI – Deploy using praisonai deploy