Getting Started¶
This guide walks you through installing orqest, configuring a provider, building your first agent, and having a multi-turn conversation.
Installation¶
Requires Python 3.12+.
Configuration¶
Create a .env file in your project root:
# Required
LLM_API_KEY=your_api_key_here
LLM_MODEL=openai:gpt-4.1
# Optional (defaults to LLM_API_KEY if not set)
EMBEDDING_API_KEY=your_embedding_key
EMBEDDING_MODEL=all-MiniLM-L6-v2
The LLM_MODEL value uses provider:model_id format:
| Provider | Prefix | Example |
|---|---|---|
| OpenAI | openai: |
openai:gpt-4.1 |
| Anthropic | anthropic: |
anthropic:claude-sonnet-4-20250514 |
google: |
google:gemini-2.0-flash |
|
| OpenRouter | openrouter: |
openrouter:anthropic/claude-3.5-sonnet |
Load config in your code — orqest never reads environment variables at import time:
Define an Agent¶
Every orqest agent needs three things:
- An output type — a Pydantic model defining the structured response
- A subclass of
BaseAgent— generic over a state type and the output type _run_implementation()— the async method where your logic lives
from pydantic import BaseModel, Field
from orqest.agents import BaseAgent, GlobalState
class AnalysisOutput(BaseModel):
sentiment: str = Field(description="positive, negative, or neutral")
confidence: float = Field(description="Confidence score 0-1")
reasoning: str = Field(description="Why this sentiment was chosen")
class SentimentAgent(BaseAgent[GlobalState, AnalysisOutput]):
def __init__(self, model: str, api_key: str):
super().__init__(
agent_name="sentiment_agent",
system_prompt="Analyze the sentiment of the user's message.",
output_type=AnalysisOutput,
model=model,
api_key=api_key,
)
async def _run_implementation(self, state: GlobalState, **kwargs) -> AnalysisOutput:
user_message = state.get_latest_message("user")
result = await self.call_model(user_message, state)
return result.output
Run the Agent¶
Create a GlobalState, add a user message, and call await agent.run(state):
config = load_config()
agent = SentimentAgent(model=config.llm_model, api_key=config.llm_api_key)
state = GlobalState()
state.add_message("user", "I absolutely love this new framework!")
output = await agent.run(state)
print(f"Sentiment: {output.sentiment} ({output.confidence:.0%})")
print(f"Reasoning: {output.reasoning}")
Multi-Turn Conversation¶
Because _run_implementation() uses self.call_model(), conversation history is automatically managed. Each call stores the full message history on state.message_history, so the next call picks up where the previous one left off.
# First turn
state = GlobalState()
state.add_message("user", "Analyze: I'm frustrated with the slow delivery.")
output1 = await agent.run(state)
print(f"Turn 1: {output1.sentiment}")
# Second turn — same state, agent has full context
state.add_message("user", "But the product itself is amazing!")
output2 = await agent.run(state)
print(f"Turn 2: {output2.sentiment}")
# History accumulates
print(f"Messages in history: {len(state.message_history)}")
The LLM sees the full conversation on the second call, so it can factor in both messages when analyzing sentiment.
History truncation
By default, keep_recent_messages() limits history to the 100 most recent messages while preserving the first message and tool-call integrity. Configure this with the truncated_history constructor parameter or provide custom history_processors.
Next Steps¶
- Agents — constructor parameters,
call_model()vsself.agent.run(), tools and toolsets - State & History — how
GlobalStateworks, custom state types, history processing - Agent as Tool — composing agents with the
as_tool()wrapper - Orchestration — pipeline, parallel, router, and refinement loop patterns
- Hooks & Lifecycle — fire-and-forget hook system for tool execution
- Memory — pluggable memory with SQLite backend and full-text search
- Observability — structured tracing and event bus