Getting started
Praval has two entry paths. Start with direct Agent calls when one agent is
enough. Add Reef and Spores when independent specialists need to collaborate.
Install
python -m pip install praval
Praval 0.8.4 supports Python 3.10 through 3.14. Optional capabilities are installed separately:
python -m pip install "praval[memory]"
python -m pip install "praval[storage]"
python -m pip install "praval[mcp]"
python -m pip install "praval[observability]"
python -m pip install "praval[eval-ragas]"
For a hosted provider, set the corresponding environment variable before running an example:
export OPENAI_API_KEY="..."
# or ANTHROPIC_API_KEY, COHERE_API_KEY, GEMINI_API_KEY, or OPENROUTER_API_KEY
Passing provider and model explicitly makes an example reproducible. If
they are omitted, Praval resolves its configured defaults and available
credentials.
First path: call a model through Agent
from praval import Agent
with Agent(
"assistant",
provider="openai",
model="gpt-5.4-mini",
system_message="Be concise.",
) as assistant:
response = assistant.generate("Explain what a Praval Spore carries.")
print(response.content)
print(response.provider, response.model, response.usage)
Agent.generate() returns ModelResponse. Its content, provider, model,
usage, tool calls, and raw provider data are available without changing the
application contract for each provider.
The compatibility method returns only a string:
from praval import Agent
with Agent("assistant", provider="openai", model="gpt-5.4-mini") as assistant:
text = assistant.chat("Explain Reef in one sentence.")
Prefer generate() for new code. Use agenerate(), stream(), and
astream() for asynchronous work and normalized streaming events.
Structured output
import json
from praval import Agent
with Agent("assistant", provider="openai", model="gpt-5.4-mini") as assistant:
response = assistant.generate(
"Return one fact about Reef as JSON.",
response_schema={
"schema": {
"type": "object",
"properties": {"fact": {"type": "string"}},
"required": ["fact"],
},
"validate_locally": True,
},
)
fact = json.loads(response.content)["fact"]
The runtime checks that the selected provider profile supports structured
output and then sends the schema as a provider constraint. Local validation is
off by default; the example enables it. Invalid final JSON or a schema mismatch
raises ProviderInvalidResponseError, including on streaming final responses.
See Structured Outputs for the full contract.
Second path: collaborate through Reef
A Spore contains routing fields and a structured knowledge dictionary. The
conventional knowledge["type"] value lets decorated agents decide which
messages to handle.
from praval import agent, broadcast, get_reef, start_agents
@agent("researcher", provider="ollama", responds_to=["research_request"])
def researcher(spore):
topic = spore.knowledge["topic"]
broadcast(
{
"type": "research_complete",
"topic": topic,
"finding": f"Evidence collected for {topic}",
}
)
@agent("editor", provider="ollama", responds_to=["research_complete"])
def editor(spore):
print(spore.knowledge["finding"])
start_agents(
researcher,
editor,
initial_data={"type": "research_request", "topic": "agent systems"},
)
reef = get_reef()
reef.wait_for_completion(timeout=30)
reef.shutdown()
This example uses the Ollama preset only to construct credential-free message
handlers; it does not call the local model server. A decorated handler may call
chat() or achat() when it actually needs model output.
wait_for_completion() waits for submitted Reef work. Always shut down Reef
and other resources explicitly in scripts and tests.
When to use each layer
Need |
Start with |
|---|---|
One conversational or tool-using agent |
|
Provider-neutral metadata and usage |
|
Streaming deltas and final events |
|
Multiple reactive specialists |
|
Deterministic application cleanup |
|
External MCP tools |
|
PravalApp owns agents and a Reef for cleanup. In this release it does not
isolate provider registries or redirect agent Reef helpers; see
Application lifecycle.
Local models
Praval connects to an already-running OpenAI-compatible server:
from praval import Agent
local = Agent("local", provider="ollama", model="llama3")
try:
print(local.chat("Say hello."))
finally:
local.close()
The presets are ollama, vllm, lmstudio, and llama-cpp. Text and
streaming are enabled by default. Tools, structured output, reasoning, and
multimodal input require verified capabilities for the server and model you
are using. Ollama can discover these with
config={"provider_options": {"discover_model": True}}; see Local LLMs.
Learn by inspecting execution
The Jupyter course explains Agent, Reef, Spore, tools, memory, HITL, MCP, and
voice flows with visible runtime state. Start with
examples/notebooks/course/00_architecture.ipynb.
For a credential-free runtime check:
python examples/model_runtime_fake_provider.py
Next, read Core concepts and API layers, Model Runtime, and the generated API Reference.
Building with a coding agent
Give a coding agent the installed version (praval --version), the matching
versioned manual at https://pravalagents.com/docs/v0.8.4/, and the task’s
provider/model requirements. praval doctor --json reports installation and
credential presence without printing keys. The published manual includes its
plain-text sources under _sources/, such as
_sources/guide/model-runtime.md.txt, for tools that cannot render HTML.
In 0.8.4, doctor checks the listed provider credentials but does not include
OpenRouter. Check that OPENROUTER_API_KEY is set without printing its value
when using that adapter.
For generated application code:
Use the documented public imports and generated API signatures. Prefer
generate()when the caller needs usage, metadata or tool results.Distinguish constructor
configfrom per-call options; see Model Runtime. Unknown call keywords warn and are ignored in 0.8.4.Verify model capabilities through Providers and Local LLMs. A registry profile describes Praval’s assumptions, not account availability.
Register callable tools through
Agent.tool()oradd_tool_spec(), and use async agent APIs for MCP tools. Keep the client open through approval/resume.Bound retries, output and tool rounds, handle typed failures, and close owned agents, clients and Reef resources. Check tool results before claiming success.
Exercise behavior with fake provider/HTTP clients before paid model checks. Compilation alone does not establish that a tool ran or an artifact is correct.
Design the first agent so it can be evaluated
Use a stable agent name and versioned model/prompt configuration, return a structured terminal outcome, bound retries and tool rounds, and keep providers and tools replaceable in tests. One completed agent call should produce one immutable observation with aggregated model/tool facts. Do not create one evaluation subject per log line, model attempt, or tool call.
When quality becomes a release decision, begin with deterministic assertions and metrics. Add a separate evaluator model or least-privilege evaluator agent only for semantic judgment. The complete pattern and anti-pattern guide is Recommended agent and evaluator-agent patterns; the credential-free first suite is Install and five-minute quickstart.