Files
OpenJarvis/docs/user-guide/agents.md
T
Jon Saad-FalconandClaude Opus 4.6 f75afefcfb Add MkDocs Material documentation site with 40 pages and auto-generated API reference
Sets up a complete documentation website with 7 navigable sections (Home, Getting
Started, User Guide, Architecture, API Reference, Deployment, Development), light/dark
mode, search, code copy, and Mermaid diagram support. API reference pages use
mkdocstrings to auto-generate docs from source docstrings. GitHub Actions workflow
deploys to GitHub Pages on push to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 06:09:36 +00:00

270 lines
10 KiB
Markdown

# Agents
Agents are the agentic logic layer of OpenJarvis. They determine how a query is processed -- whether it goes directly to a model, through a tool-calling loop, or via an external agent runtime. All agents implement the `BaseAgent` ABC and are registered via the `AgentRegistry`.
## Overview
| Agent | Registry Key | Tools | Multi-turn | Description |
|------------------|-----------------|-------|------------|----------------------------------------------|
| `SimpleAgent` | `simple` | No | No | Single-turn query-to-response |
| `OrchestratorAgent` | `orchestrator` | Yes | Yes | Multi-turn tool-calling loop |
| `OpenClawAgent` | `openclaw` | Yes | Yes | External agent via HTTP or subprocess |
| `CustomAgent` | `custom` | -- | -- | Template for user-defined agents |
---
## BaseAgent ABC
All agents extend the abstract `BaseAgent` class.
```python
from abc import ABC, abstractmethod
from openjarvis.agents._stubs import AgentContext, AgentResult
class BaseAgent(ABC):
agent_id: str
@abstractmethod
def run(
self,
input: str,
context: AgentContext | None = None,
**kwargs,
) -> AgentResult:
"""Execute the agent on the given input."""
```
### AgentContext
The runtime context handed to an agent on each invocation.
| Field | Type | Description |
|------------------|--------------------|------------------------------------------------|
| `conversation` | `Conversation` | Message history (pre-filled with context if memory injection is active) |
| `tools` | `list[str]` | Tool names available to the agent |
| `memory_results` | `list[Any]` | Pre-fetched memory retrieval results |
| `metadata` | `dict[str, Any]` | Arbitrary metadata for the run |
### AgentResult
The result returned after an agent completes a run.
| Field | Type | Description |
|----------------|--------------------|------------------------------------------------|
| `content` | `str` | The final response text |
| `tool_results` | `list[ToolResult]` | Results from tool executions during the run |
| `turns` | `int` | Number of turns (inference calls) taken |
| `metadata` | `dict[str, Any]` | Arbitrary metadata about the run |
---
## SimpleAgent
The `SimpleAgent` is a single-turn agent that sends the query directly to the inference engine and returns the response. It does not support tool calling.
**How it works:**
1. Builds a message list from the conversation context (if provided) plus the user query.
2. Calls the inference engine via `instrumented_generate()` for telemetry tracking.
3. Returns the response as an `AgentResult` with `turns=1`.
**Constructor parameters:**
| Parameter | Type | Default | Description |
|---------------|-------------------|---------|------------------------------------|
| `engine` | `InferenceEngine` | -- | The inference engine to use |
| `model` | `str` | -- | Model identifier |
| `bus` | `EventBus` | `None` | Event bus for telemetry |
| `temperature` | `float` | `0.7` | Sampling temperature |
| `max_tokens` | `int` | `1024` | Maximum tokens to generate |
**When to use:** For straightforward question-answering without tool calling or multi-turn reasoning.
---
## OrchestratorAgent
The `OrchestratorAgent` is a multi-turn agent that implements a tool-calling loop. It is the primary agent for queries that require computation, knowledge retrieval, or structured reasoning.
**How it works:**
1. Builds the initial message list from context and the user query.
2. Sends messages with tool definitions (OpenAI function-calling format) to the engine.
3. If the engine responds with `tool_calls`, the `ToolExecutor` dispatches each call.
4. Tool results are appended as `TOOL` messages and the loop continues.
5. If no `tool_calls` are returned, the response is treated as the final answer.
6. The loop stops after `max_turns` iterations (default: 10), returning whatever content is available along with a `max_turns_exceeded` metadata flag.
**Constructor parameters:**
| Parameter | Type | Default | Description |
|---------------|-------------------|---------|------------------------------------|
| `engine` | `InferenceEngine` | -- | The inference engine to use |
| `model` | `str` | -- | Model identifier |
| `tools` | `list[BaseTool]` | `[]` | Tool instances to make available |
| `bus` | `EventBus` | `None` | Event bus for telemetry |
| `max_turns` | `int` | `10` | Maximum number of tool-calling turns |
| `temperature` | `float` | `0.7` | Sampling temperature |
| `max_tokens` | `int` | `1024` | Maximum tokens to generate |
**When to use:** For queries that need calculation, memory search, sub-model calls, file reading, or multi-step reasoning.
!!! info "Tool-Calling Loop"
The orchestrator follows the OpenAI function-calling convention. The engine must support returning `tool_calls` in its response for the loop to engage. If tools are provided but the engine does not return any tool calls, the agent behaves like a single-turn agent.
---
## OpenClawAgent
The `OpenClawAgent` wraps the OpenClaw Pi agent runtime, communicating via either HTTP or subprocess transport. It supports tool calling through the OpenClaw protocol.
**How it works:**
1. Checks transport health.
2. Sends a `QUERY` protocol message through the transport.
3. If the response is a `TOOL_CALL`, dispatches the tool locally via `ToolExecutor`.
4. Sends the tool result back as a `TOOL_RESULT` message.
5. Continues the tool-call loop until the response is a final answer or error (up to 10 turns).
**Constructor parameters:**
| Parameter | Type | Default | Description |
|--------------|----------------------|-----------|-------------------------------------------|
| `engine` | `Any` | `None` | Inference engine (fallback/provider) |
| `model` | `str` | `""` | Model identifier |
| `transport` | `OpenClawTransport` | `None` | Pre-configured transport (overrides mode) |
| `mode` | `str` | `"http"` | Transport mode: `"http"` or `"subprocess"` |
| `bus` | `EventBus` | `None` | Event bus for telemetry |
**Transport modes:**
- **HTTP** (`HttpTransport`): Sends HTTP POST requests to an OpenClaw server.
- **Subprocess** (`SubprocessTransport`): Spawns a Node.js process and communicates via stdin/stdout using JSON-line protocol.
!!! warning "Node.js Requirement"
The subprocess transport mode requires Node.js 22+ to be installed on the system.
---
## CustomAgent
The `CustomAgent` is a template for building user-defined agents. It raises `NotImplementedError` by default -- subclass it and override `run()` to implement your logic.
```python
from openjarvis.agents._stubs import AgentContext, AgentResult, BaseAgent
from openjarvis.core.registry import AgentRegistry
@AgentRegistry.register("my-agent")
class MyAgent(BaseAgent):
agent_id = "my-agent"
def __init__(self, engine, model, **kwargs):
self._engine = engine
self._model = model
def run(self, input: str, context: AgentContext | None = None, **kwargs) -> AgentResult:
# Your custom logic here
result = self._engine.generate(
[{"role": "user", "content": input}],
model=self._model,
)
return AgentResult(
content=result.get("content", ""),
turns=1,
)
```
After registration, you can use your custom agent via the CLI or SDK:
```bash
jarvis ask --agent my-agent "Hello"
```
```python
response = j.ask("Hello", agent="my-agent")
```
---
## Using Agents
### Via CLI
```bash
# Simple agent
jarvis ask --agent simple "What is the capital of France?"
# Orchestrator with tools
jarvis ask --agent orchestrator --tools calculator,think "What is sqrt(256)?"
# OpenClaw agent
jarvis ask --agent openclaw "Tell me a story"
```
### Via Python SDK
```python
from openjarvis import Jarvis
j = Jarvis()
# Simple agent
response = j.ask("Hello", agent="simple")
# Orchestrator with tools
response = j.ask(
"Calculate 15% of 340",
agent="orchestrator",
tools=["calculator"],
)
# Full result with tool details
result = j.ask_full(
"What is the square root of 144?",
agent="orchestrator",
tools=["calculator", "think"],
)
print(result["content"])
print(result["turns"])
print(result["tool_results"])
j.close()
```
---
## Agent Registration
Agents are registered via the `@AgentRegistry.register()` decorator. This makes them discoverable by name at runtime:
```python
from openjarvis.core.registry import AgentRegistry
# Check if an agent is registered
AgentRegistry.contains("orchestrator") # True
# Get the agent class
agent_cls = AgentRegistry.get("orchestrator")
# List all registered agent keys
AgentRegistry.keys() # ["simple", "orchestrator", "openclaw", "custom"]
```
---
## Event Bus Integration
All agents publish events on the `EventBus` when a bus is provided:
| Event | When |
|-------------------------|---------------------------------------------|
| `AGENT_TURN_START` | At the beginning of a run |
| `AGENT_TURN_END` | At the end of a run (includes turn count) |
| `INFERENCE_START` | Before each engine call (orchestrator) |
| `INFERENCE_END` | After each engine call (orchestrator) |
| `TOOL_CALL_START` | Before each tool execution (openclaw) |
| `TOOL_CALL_END` | After each tool execution (openclaw) |
These events enable the telemetry and trace systems to record detailed interaction data automatically.