Agents
How OrchidAgent works: YAML configuration, skills, MCP tools, and the run lifecycle.
AnthropicOpenAIOllamaGoogleEvery agent in Orchid is a Python class that satisfies the OrchidAgent abstract base class. The framework discovers and wires agents at startup from config files (agents.yaml or agents/*.md) — no manual graph editing required.
The OrchidAgent ABC
OrchidAgent lives in orchid_ai/core/agent.py and defines the minimal contract the supervisor needs to route requests:
| Member | Purpose |
|---|---|
name (abstract property) | Unique identifier, e.g. "basketball" — used in LangGraph node names |
description (abstract property) | Human-readable intent description; the supervisor LLM reads this to decide routing |
rag_namespace (property) | Vector store collection name for RAG. Override if the agent uses RAG; default is "" (no RAG) |
run(state) (abstract) | Main agent logic — receives the full OrchidAgentState, returns a partial update |
The base class also provides shared helpers every subclass can use without reimplementing:
extract_user_query(state)— walks messages to find the last human messagefetch_rag_context(query, scope)— retrieves relevant chunks viaOrchidVectorReadersummarise(query, mcp_data, rag_data, *, system_prompt, ...)— calls the injectedBaseChatModelto synthesise a responseextract_conversation_history(state, *, max_turns, max_chars, ...)— extracts clean[{role, content}]pairs, filtering out supervisor routing noise
GenericAgent: no Python required
For most use cases, GenericAgent is the right choice. It is a concrete subclass whose behaviour is entirely driven by an OrchidAgentConfig parsed from config files. Its run() executes a fixed six-step pipeline:
- RAG retrieval (tenant-scoped via
OrchidRAGScope) - Skill check — if the query matches a configured skill, run it and skip to step 6
- Agentic tool-calling loop — MCP + built-in tools via native
tool_calls - Dynamic RAG injection — write tool results back to the vector store for future retrieval
- LLM summarisation — synthesise the final response
Defining an agent
agents:
basketball:
description: >
NBA basketball expert. Knows player stats, team rosters,
and can compare players head-to-head.
prompt: |
You are a Basketball Expert AI assistant.
Use your tools to provide data-driven analysis.
llm:
model: "ollama/llama3.2"
temperature: 0.2
tools:
- get_player_stats
- compare_players
- get_team_roster
skills:
scouting_report:
description: "Get a player's stats then compare with a rival"
steps:
- tool: get_player_stats
source: builtin
- tool: compare_players
source: builtinCollaborators (Single Responsibility Principle)
GenericAgent delegates each concern to a dedicated class rather than handling everything itself:
| Collaborator | Responsibility |
|---|---|
SkillDetector | Matches user queries to configured agent skills via LLM |
MCPDispatcher | Discovers MCP server capabilities and routes tool calls |
SkillExecutor | Runs multi-step skill pipelines and cross-agent delegation |
Custom agents via dotted imports
When config files aren't enough, subclass OrchidAgent in your own project and reference it by dotted path:
agents:
support:
class: myproject.agents.support.SupportAgent
description: "Handles support tickets and knowledge-base lookups."
prompt: "You are a support specialist..."The framework resolves the class at startup via importlib. Custom agents receive the same injected dependencies (reader, mcp_clients, chat_model) as GenericAgent — override only what you need, inherit the rest.
Mini-agents (opt-in fan-out)
Any agent — including custom OrchidAgent subclasses — can opt into mini-agent fan-out with a single config flag. When enabled, a decomposer runs before run() each turn; if the query contains independent sub-tasks, it fans out into N parallel mini-agents and synthesises their outcomes into one final response. See Mini-Agents for the full decision tree and config reference.