Skip to main content

A lightweight AI Agent framework built on LiteLLM with ReAct reasoning, tool calling, and smart memory.

Project description

EasyAgent

License: MIT Python 3.12+

English | 简体中文

A lightweight AI Agent framework built on LiteLLM, featuring multi-model support, tool calling, and intelligent memory management.

~809 lines of code, production-ready Agent capabilities — Multi-model adapters, tool calling, smart memory, ReAct reasoning, DAG pipelines, debug tracing. Core code refined to the extreme.

Features

  • Multi-Model Support - Unified interface via LiteLLM for OpenAI, Anthropic, Gemini, and more
  • Tool Calling - Protocol-based tool definition with @register_tool decorator
  • Memory - Sliding window + auto-summarization strategies for context management
  • ReAct Loop - Standard think → act → observe reasoning cycle
  • DAG Pipeline - Directed Acyclic Graph workflow orchestration with parallel execution
  • Debug Friendly - Colored logging, token usage and cost tracking
  • Minimal Footprint - Only ~809 lines of core code, no bloat, easy to read/modify/extend

Installation

From PyPI:

pip install easy-agent-sdk

From source (development mode):

git clone https://github.com/SNHuan/EasyAgent.git
cd EasyAgent
pip install -e ".[dev]"

Core dependencies:

  • litellm>=1.80.0
  • pydantic>=2.12.5

Architecture

┌──────────────────────────────────────────────────────────────┐
│                         User Layer                           │
│                      (Input / Output)                        │
└─────────────────────────────┬────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────┐
│                        Agent Layer                           │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  ReactAgent  (ReAct Loop: think -> act -> observe)     │  │
│  │      ↓ extends                                         │  │
│  │  ToolAgent   (Tool Registration & Execution)           │  │
│  │      ↓ extends                                         │  │
│  │  BaseAgent   (Model + Memory + History Management)     │  │
│  └────────────────────────────────────────────────────────┘  │
└───────────┬──────────────────┬──────────────────┬────────────┘
            │                  │                  │
            ▼                  ▼                  ▼
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│      Model       │  │      Memory      │  │       Tool       │
│                  │  │                  │  │                  │
│  BaseLLM         │  │  BaseMemory      │  │  Tool Protocol   │
│      ↓           │  │      ↓           │  │      ↓           │
│  LiteLLMModel    │  │  SlidingWindow   │  │  ToolManager     │
│  (OpenAI/Claude) │  │  SummaryMemory   │  │  @register_tool  │
└────────┬─────────┘  └──────────────────┘  └──────────────────┘
         │
         ▼
┌──────────────────────────────────────────────────────────────┐
│                       Schema Layer                           │
│          Message  |  ToolCall  |  LLMResponse                │
└──────────────────────────────────────────────────────────────┘

Layer Overview:

Layer Responsibility Module
User Layer User interaction entry point -
Agent Layer Core control, ReAct loop agent/
Infrastructure Independent modules model/ memory/ tool/
Schema Layer Pydantic data structures model/schema.py

Project Structure

EasyAgent/
├── agent/                  # Agent layer
│   ├── base.py             # BaseAgent abstract class
│   ├── tool_agent.py       # ToolAgent (tool calling support)
│   └── react_agent.py      # ReactAgent (ReAct loop)
├── model/                  # Model layer
│   ├── base.py             # BaseLLM abstract class
│   ├── litellm_model.py    # LiteLLM implementation
│   └── schema.py           # Message, ToolCall, LLMResponse
├── memory/                 # Memory layer
│   ├── base.py             # BaseMemory abstract class
│   ├── sliding_window.py   # Sliding window strategy
│   └── summary.py          # Auto-summarization strategy
├── tool/                   # Tool layer
│   ├── base.py             # Tool Protocol
│   └── manager.py          # ToolManager + @register_tool
├── pipeline/               # DAG Pipeline
│   └── base.py             # BaseNode, BasePipeline, NodeContext
├── prompt/                 # Prompt templates
├── config/                 # Configuration management
├── debug/                  # Debug utilities (colored logs)
└── test/                   # Tests

Quick Start

1. Configuration

Option 1: Environment Variable (Recommended)

Copy .example_env to .env and set your custom config path:

cp .example_env .env
# .env
EA_DEFAULT_CONFIG=/path/to/your/config.yaml

Option 2: Edit Package Config

cp config/config_example.yaml config/config.yaml

Config File Format

debug: true
summary_model: gpt-4o-mini

models:
  gpt-4o-mini:
    api_type: openai
    base_url: https://api.openai.com/v1
    api_key: sk-xxx

  # Custom models with cost configuration
  gemini-2.5-flash:
    api_type: openai
    base_url: https://your-proxy.com/v1
    api_key: your-key
    cost:
      input_cost_per_token: 0.0000003
      output_cost_per_token: 0.00000252
      max_tokens: 8192
      max_input_tokens: 1048576

Config Loading Priority:

  1. Path specified by EA_DEFAULT_CONFIG environment variable
  2. Default config/config.yaml in package

3. Create Agent

import asyncio
from agent.react_agent import ReactAgent
from config.base import ModelConfig
from model.litellm_model import LiteLLMModel

config = ModelConfig.load()
model = LiteLLMModel(**config.get_model("gpt-4o-mini"))

agent = ReactAgent(
    model=model,
    tools=["get_weather"],
    system_prompt="You are a helpful assistant.",
    max_iterations=10,
)

result = asyncio.run(agent.run("What's the weather in Beijing?"))
print(result)

Core Components

Agent Layer

Class Description
BaseAgent Abstract base class with model, memory, and history management
ToolAgent Extends BaseAgent with tool registration and execution
ReactAgent ReAct loop implementation: think → act → observe

Model Layer

Class Description
BaseLLM Abstract interface defining call() and call_with_history()
LiteLLMModel LiteLLM implementation supporting all LiteLLM-compatible models
Message Pydantic message model (system/user/assistant/tool)
ToolCall Tool call structure
LLMResponse Unified response format with content, tool_calls, usage

Memory Layer

Strategy Use Case Features
SlidingWindowMemory Short conversations Truncate by message/token count, keep recent messages
SummaryMemory Long conversations Auto-summarize and persist, respects max_tokens
from memory import SlidingWindowMemory, SummaryMemory

# Sliding window: limit by message and token count
memory = SlidingWindowMemory(max_messages=20, max_tokens=4000)

# Auto-summary: for long tasks, max_tokens fetched from litellm
memory = SummaryMemory(
    task_id="task_001",
    reserve_ratio=0.3,
    workspace="workspace",
)

Tool Layer

Tools must implement the Tool Protocol:

from typing import Protocol

class Tool(Protocol):
    name: str
    type: str
    description: str

    def init(self) -> None: ...
    def execute(self, **kwargs) -> str: ...

Pipeline

DAG-based workflow orchestration with parallel node execution:

import asyncio
from pipeline.base import BaseNode, BasePipeline, NodeContext

# Define nodes
class FetchData(BaseNode):
    async def execute(self, ctx: NodeContext) -> None:
        ctx.data = "raw_data"

class ProcessA(BaseNode):
    async def execute(self, ctx: NodeContext) -> None:
        ctx.result_a = f"{ctx.data}_processed_A"

class ProcessB(BaseNode):
    async def execute(self, ctx: NodeContext) -> None:
        ctx.result_b = f"{ctx.data}_processed_B"

class Merge(BaseNode):
    async def execute(self, ctx: NodeContext) -> None:
        ctx.final = f"{ctx.result_a} + {ctx.result_b}"

# Build DAG using >> syntax
fetch = FetchData()
process_a = ProcessA()
process_b = ProcessB()
merge = Merge()

fetch >> [process_a, process_b]  # Parallel branches
process_a >> merge
process_b >> merge

# Execute
pipeline = BasePipeline(root=fetch)
ctx = asyncio.run(pipeline.run())
print(ctx.final)  # "raw_data_processed_A + raw_data_processed_B"

# Visualize (Mermaid format)
print(pipeline.visualize())

Core Components:

Component Description
BaseNode Abstract node class, implement execute(ctx)
BasePipeline Pipeline executor with level-based parallel execution
NodeContext Shared context for inter-node data passing
>> operator Syntactic sugar for node.add(successor)

Debugging

Enable debug mode for colored logs:

# config/config.yaml
debug: true

Log output example:

14:30:15 DEBUG [ReactAgent] User: What's the weather?
14:30:15 DEBUG [ReactAgent] Iteration 1/10
14:30:16 INFO  [LiteLLM] Response: in=150, out=45, cost=$0.000195
14:30:16 INFO  [ReactAgent] Tool call: get_weather({"city": "Beijing"})
14:30:16 INFO  [ReactAgent] Tool result: The weather in Beijing is sunny, 25°C.
14:30:17 INFO  [ReactAgent] Final: The weather in Beijing is sunny with 25°C.

Use LogCollector to capture logs:

from debug.log import LogCollector, Logger

log = Logger("MyApp")

with LogCollector() as collector:
    log.info("Step 1")
    log.info("Step 2")

print(collector.to_text())  # "Step 1\nStep 2"

Running Tests

python -m test.test_agent
python -m test.test_model

Acknowledgements

Thanks to litellm and OpenManus for inspiration and guidance.

License

MIT License © 2025 Yiran Peng

2. Define Tools

Use the @register_tool decorator:

from tool import register_tool

@register_tool
class GetWeather:
    name = "get_weather"
    type = "function"
    description = "Get the weather for a city."
    parameters = {
        "type": "object",
        "properties": {"city": {"type": "string", "description": "City name"}},
        "required": ["city"],
    }

    def init(self) -> None:
        """Called when tool is initialized"""
        pass

    def execute(self, city: str) -> str:
        """Execute tool logic"""
        return f"The weather in {city} is sunny, 25°C."

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

easy_agent_sdk-0.1.0.tar.gz (27.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

easy_agent_sdk-0.1.0-py3-none-any.whl (28.6 kB view details)

Uploaded Python 3

File details

Details for the file easy_agent_sdk-0.1.0.tar.gz.

File metadata

  • Download URL: easy_agent_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 27.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for easy_agent_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eb5351b47333d239fe5ca863bb5ceed20ccd819988241af6338ba083d9fca30f
MD5 8a15cb12984bba8a894421ccfe156db8
BLAKE2b-256 7bc58db756ad89894f72ec0b1c4b634d6b7b6756f69a6b183089c6da80b40f49

See more details on using hashes here.

Provenance

The following attestation bundles were made for easy_agent_sdk-0.1.0.tar.gz:

Publisher: publish.yml on SNHuan/EasyAgent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file easy_agent_sdk-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: easy_agent_sdk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 28.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for easy_agent_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0ba7a0d38ebe299a22c7ea4e1ce4418b1262b0bf1a1c974231860261a5da52eb
MD5 53b4d8ff93e850f14a899c5f42a76971
BLAKE2b-256 4f44cafe35c4d66b3e7a2df9a8e8709b0bd3865d02e5d76755076cc780d0a5d1

See more details on using hashes here.

Provenance

The following attestation bundles were made for easy_agent_sdk-0.1.0-py3-none-any.whl:

Publisher: publish.yml on SNHuan/EasyAgent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page