English | 简体中文
EasyAgent is a lightweight agent SDK organised as a small set of composable layers. The goal is to let you learn agent design step by step: start with a single model call, then add memory and context, build up to a ReAct loop with tools and skills, drop into a sandbox, and finally orchestrate multiple agents through the Entity-World-Schedule architecture.
What you get:
- A small single-agent stack: model, memory, context, tools, skills, sandbox.
- Runtime primitives for multi-agent systems: Entity, World, Schedule, Runtime.
- Agent Skills-compatible loading from
.easyagent/skills,.claude/skills,.codex/skills, or any directory you choose. - Optional tracing, stores, and a local dashboard when you need observability.
pip install easy-agent-sdkFrom source:
git clone https://github.com/SNHuan/EasyAgent.git
cd EasyAgent
pip install -e ".[dev]"The default install includes model adapters, sandbox helpers, web helpers, and MCP integration.
import asyncio
from easyagent import LiteLLMModel, ReactAgent
async def main():
agent = ReactAgent(
model=LiteLLMModel("gpt-4o-mini"),
system_prompt="You are a concise assistant.",
max_iterations=5,
)
result = await agent.run("What is 2 + 2?")
print(result.final_output)
asyncio.run(main())Create easyagent/config/config.yaml or configure LiteLLM through environment
variables:
For local development, copy .env.example to .env and fill in only the
variables you need. This single example file covers EasyAgent core settings,
Serper, Claude Code SDK, and Codex/OpenAI SDK authentication.
debug: false
models:
gpt-4o-mini:
api_type: openai
base_url: https://api.openai.com/v1
api_key: sk-xxxEasyAgent is organised around three layers:
Single-agent: Model + Memory + Context + Tool → Agent / ReactAgent / SkillAgent / SandboxAgent
Multi-agent: Entity + World + Schedule → Runtime
Presets: sequential / fanout / debate / chatroom / groupchat
- Model — provider adapter and message schema.
- Memory + Context — store conversation history and decide what reaches the model each turn.
- Agent — composes a model, memory, context, and any tools/skills/sandbox.
Four built-in classes:
Agent(single-turn) →ReactAgent(ReAct loop) →SkillAgent/SandboxAgent. - Entity — wraps an Agent (or any async actor) for multi-agent participation.
Protocol:
idproperty +async act(Perception) -> Action | None. - World — the environment entities perceive and act upon.
Built-ins:
ConversationWorld,PipelineWorld,SpatialWorld,StatefulWorld. - Schedule — determines who acts next.
Built-ins:
TakeTurns,RoundRobin,AllParallel,Reactive,MaxTicks,UntilIdle. - Runtime — the perceive-act-apply loop wiring Entity + World + Schedule.
When connected to an
EventBus, it emits runtime/tick/entity events and links child agent sessions back to the same runtime run.
See docs/architecture.md for the full design guide.
The examples are ordered by layer. Each one introduces one new idea:
# Single agent (00–06)
python examples/00_model_call.py # Just call the model
python examples/01_single_turn_agent.py # Compose a minimal Agent
python examples/02_memory_and_context.py # Memory + Context
python examples/03_react_with_tools.py # ReactAgent + tool calls
python examples/04_skills_lazy_loading.py # SkillAgent (SKILL.md packages)
python examples/05_sandbox_agent.py # SandboxAgent (bash, write/read file)
python examples/06_custom_tool.py # Define your own tool
# Multi-agent: Entity-World-Schedule (07–14)
python examples/07_two_agents_talk.py # LLMEntity + ConversationWorld + RoundRobin
python examples/08_sequential.py # sequential() preset
python examples/09_chatroom.py # Manual turn-taking + if/else
python examples/10_groupchat.py # Reactive schedule, LLM picks next
python examples/11_debate_and_judge.py # Third-party judge after debate
python examples/12_nested.py # TeamEntity: Runtime-as-Entity nesting
python examples/13_shared_state.py # SharedState + StatefulWorld blackboard
python examples/14_advanced_runtime.py # SpatialWorld: 2D grid + range-limited perception
# MCP examples (external tool sources)
python examples/mcp/fastmcp_in_memory.py # Wrap a FastMCP server as EasyAgent tools
python examples/mcp/config_load.py # Load tools from mcp_config.example.jsonfrom easyagent import LiteLLMModel, ReactAgent, register_tool
@register_tool
class GetWeather:
name = "get_weather"
type = "function"
description = "Get weather for a city."
parameters = {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
}
def init(self) -> None: ...
def execute(self, city: str) -> str:
return f"Sunny in {city}."
agent = ReactAgent(
model=LiteLLMModel("gpt-4o-mini"),
tools=[GetWeather],
)Pass tool classes or instances directly via tools=[...]. The ReAct loop
continues while the model returns tool calls; a plain assistant message with no
tool calls is treated as the final answer.
EasyAgent can consume MCP servers as external tool sources. MCP support is included in the default install.
Use a standard FastMCP/MCP config. The mcpServers keys act as natural tool
categories:
{
"mcpServers": {
"literature": {
"command": "python",
"args": ["./examples/mcp/servers/literature_server.py"]
}
}
}Register discovered MCP tools into a ToolManager, then decide per session
which tools are visible to the model:
from easyagent import LiteLLMModel, ReactAgent, ToolManager, register_mcp_tools
tool_manager = ToolManager(discover_builtin=False)
literature_tools = await register_mcp_tools(
tool_manager,
mcp_config,
servers=["literature"],
)
agent = ReactAgent(model=LiteLLMModel("gpt-4o-mini"), tool_manager=tool_manager)
session = agent.create_session()
session.enabled_tools.extend(literature_tools)You can also filter FastMCP tools by tags:
await register_mcp_tools(tool_manager, mcp_config, tags=["demo"])See examples/mcp/ for runnable examples.
Skills are Agent Skills compatible directory
packages loaded on demand. SKILL.md is the required entry file and must
include YAML frontmatter with at least name and description. The name
must match the parent directory name.
.easyagent/skills/my-skill/
├── SKILL.md
├── references/
├── templates/
├── assets/
└── scripts/
from easyagent import LiteLLMModel, SkillAgent
agent = SkillAgent(
model=LiteLLMModel("gpt-4o-mini"),
skills=["my-skill"],
)By default EasyAgent discovers skills from .easyagent/skills. Set
EA_SKILLS_DIR to load skills from another Agent Skills-compatible directory
such as .claude/skills or .codex/skills. Multiple directories can be
separated with the platform path separator (: on macOS/Linux, ; on
Windows).
Wrap any Agent as an LLMEntity, then compose with presets:
from easyagent import LiteLLMModel, ReactAgent, LLMEntity, sequential
model = LiteLLMModel("gpt-4o-mini")
researcher = LLMEntity("researcher", ReactAgent(model=model, name="researcher", system_prompt="..."))
writer = LLMEntity("writer", ReactAgent(model=model, name="writer", system_prompt="..."))
reviewer = LLMEntity("reviewer", ReactAgent(model=model, name="reviewer", system_prompt="..."))
result = await sequential([researcher, writer, reviewer], "Write a product blurb.")
print(result.last_speech)Available presets: sequential / fanout / chatroom / groupchat /
debate. For recursive nesting, wrap an inner Runtime as a TeamEntity
and drop it into any outer pipeline. See examples/07_* through
examples/14_* for walkthroughs.
The architecture is extensible beyond conversation. Swap the World to get entirely different behaviour with the same Entity and Schedule:
from easyagent import SpatialWorld, Grid2D, Runtime, RoundRobin, MaxTicks
grid = Grid2D()
grid.place("alice", (0, 0))
grid.place("bob", (5, 5))
world = SpatialWorld(grid=grid, listen_radius=3.0)
schedule = MaxTicks(inner=RoundRobin(ids=["alice", "bob"]), n=10)
rt = Runtime(world=world, entities={"alice": alice, "bob": bob}, schedule=schedule)
result = await rt.run("Start exploring")EasyAgent can persist agent and runtime traces to SQLite and open a local dashboard for logs, events, message history, and token usage:
easyagent dashboardBy default the CLI reads .easyagent/traces.db. You can point it at another
trace store and open the browser automatically:
easyagent dashboard --db path/to/traces.db --openThe dashboard understands both standalone agent sessions and runtime traces, so runtime/world/entity/session trees appear automatically when your application writes runtime events into the selected trace store.
Custom events can opt into dashboard surfaces by attaching a DisplayHint.
For example, this event is persisted as PlannerStepEvent and rendered in the
Messages tab as an assistant bubble:
from easyagent import CustomTraceEvent, DisplayHint, EventBus, MemoryStore, TraceRecorder
store = MemoryStore()
bus = EventBus()
TraceRecorder(store).attach(bus)
await bus.publish(
CustomTraceEvent(
event_type="PlannerStepEvent",
session_id="sess_planner",
agent_id="planner",
summary="Planner selected search_docs",
payload={"step": "search_docs"},
display=DisplayHint.messages(
"Need to inspect README and pyproject first.",
role="assistant",
title="Planner step",
source="planner",
),
)
)The root package exposes the common SDK surface:
from easyagent import (
# single-agent
Agent, ReactAgent, SkillAgent, SandboxAgent,
AgentSession, AgentRunResult,
LiteLLMModel, Message,
EventBus, MessageEvent,
ToolManager, SkillManager, register_tool,
MCPToolset, load_mcp_tools, register_mcp_tools,
# multi-agent protocols
Entity, World, Schedule, Runtime, RuntimeResult,
# perception & action types
Perception, Speak, Silent, ChatMessage,
# entities
LLMEntity, TeamEntity, HumanEntity,
# worlds
ConversationWorld, PipelineWorld, SpatialWorld, StatefulWorld, SharedState,
# schedules
TakeTurns, RoundRobin, AllParallel, MaxTicks, UntilIdle, Reactive,
# presets
sequential, fanout, debate, chatroom, groupchat,
)easyagent/
├── agent/ # Agent, ReactAgent, SkillAgent, SandboxAgent, AgentSession
├── core/ # Entity, World, Schedule protocols + Runtime loop
├── entities/ # LLMEntity, TeamEntity, HumanEntity
├── worlds/ # ConversationWorld, PipelineWorld, SpatialWorld, StatefulWorld
├── presets.py # sequential, fanout, debate, chatroom, groupchat
├── context/ # SlidingWindowContext, SummaryContext, MultiAgentFormatter
├── events/ # MessageEvent, EventBus, telemetry events
├── memory/ # InMemoryMemory
├── model/ # LiteLLMModel + Message schema
├── prompt/ # System-prompt builders
├── sandbox/ # Local / Docker sandboxes
├── skill/ # SKILL.md loading
├── tool/ # Tool registry + built-ins (bash, file, web, skill helpers)
├── config/ # Config loading
└── debug/ # Logging
MIT License © 2025 Yiran Peng

