Skip to main content

Ethan — Lightweight personal AI agent with memory, skills, and multi-model support

Project description

Ethan Agent

中文文档

A lightweight, extensible personal AI agent built in Python. Designed to run persistently on your own hardware with memory that grows over time, scheduled tasks, and a pluggable tool/skill system.

Ethan combines ideas from OpenClaw (structured agent loop, layered memory), Hermes Agent (self-improving skills, memory consolidation), and nanobot (minimal core, readable codebase).


Features

Memory system (five layers)

  • Hot/warm/cold three-tier sliding window for long-conversation context; older content auto-compressed by a cheap model
  • Structured Facts: confidence-scored entries with conflict detection and deduplication (~/.ethan/memory/facts.json)
  • Behavioral Procedures: learned from user corrections, loaded every conversation (procedures.json)
  • Session Episodes: auto-summarized on exit, supports keyword search (episodes.json)
  • User Profile: narrative document storing personal phrases, goals, and agent agreements (user_profile.md)
  • Proactive memory write: Agent calls tools mid-conversation to instantly persist anything worth remembering — no waiting for batch processing

Skill system

  • Keyword trigger matching, auto-injected into system prompt
  • fast_path: true routes matched input to the millisecond fast track
  • channels: [lark, web] filters skills by channel so each surface gets only relevant skills
  • Hit tracking and correction collection; Heartbeat auto-updates skill content with a cheap model when corrections accumulate
  • Agent can create new skills mid-conversation via the skill_create tool

Three-track routing

  • fast: short commands + keyword match → minimal prompt + fast_path tools only + 2 iterations
  • medium: mid-length messages → full prompt + all tools + 4 iterations
  • full: complex tasks → full prompt + all tools + 10 iterations

Scheduler

  • Create cron or interval jobs in conversation; SQLite-persisted, survives restarts
  • heartbeat.md: write natural-language tasks; the system runs them periodically

Tool system

  • Shell execution, web search (DuckDuckGo), web fetch, file I/O, knowledge base
  • Tool results over 4 000 chars are auto-summarized by a cheap model before going back to the main model
  • Identical calls within the same turn hit an in-memory cache — no duplicate execution

Prompt Caching

  • System prompt split into stable layer / dynamic layer; stable layer cached 5 min, token cost drops to 0.1×

Multi-channel

  • CLI REPL, Web UI (Next.js), Lark/Feishu (WebSocket, no public IP required)

Install

pip install ethan-agent

Set an API key and start:

ethan provider set anthropic --api-key sk-ant-xxx
ethan

That's it. On first run, default skills and system files are written to ~/.ethan/.


Quick Start (Docker, recommended for server deployment)

Docker runs backend and Web UI as separate containers, data persisted to a local volume.

Prerequisites

  • Docker 20.10+
  • Docker Compose v2

1. Clone

git clone https://github.com/llm011/ethan-agent.git
cd ethan-agent

2. Configure

cp deploy/.env.example deploy/.env
# Edit deploy/.env with your API keys

At least one provider is required:

# Anthropic (recommended — supports Prompt Caching)
ANTHROPIC_API_KEY=sk-ant-xxx

# Or any OpenAI-compatible API
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.example.com/v1

AGENT_DEFAULT_MODEL=claude-sonnet-4-6

3. Build and start

cd deploy
docker compose up -d --build

First build takes ~3–5 minutes (installing deps + building Next.js).

4. Access

5. Common commands

docker compose logs -f ethan      # tail logs
docker compose restart ethan      # restart backend
docker compose down               # stop

Local Development / Install from Source

Prerequisites

  • Python 3.12+
  • uv package manager
  • Node.js 20+ (Web UI only)

Install

# From PyPI
pip install ethan-agent

# Or from source
git clone https://github.com/llm011/ethan-agent.git
cd ethan-agent
uv sync

Configure

ethan provider set anthropic --api-key sk-ant-xxx
# or
ethan provider set openai_compat --api-key sk-xxx --base-url https://api.example.com/v1

Run

# Interactive REPL
ethan

# Single-turn query
ethan -p "What's the weather in Tokyo?"

# Specify model
ethan -m claude-sonnet-4-6

# Resume last session
ethan -r last

# Start HTTP API server (needed for Web UI)
ethan serve

Web UI (dev mode)

cd web
npm install
npm run dev   # http://localhost:3000

macOS auto-start (launchd)

./deploy/install.sh

Architecture

ethan/
├── core/
│   ├── agent.py               # ReAct loop, three-track router (fast/medium/full)
│   ├── config.py              # YAML config (~/.ethan/config.yaml)
│   └── heartbeat.py           # Heartbeat system, periodic maintenance
├── providers/
│   ├── base.py                # Unified interface (Message, ToolCall, BaseProvider)
│   ├── anthropic.py           # Claude native protocol + Prompt Caching
│   ├── openai_compat.py       # OpenAI-compatible protocol
│   └── manager.py             # Route model ID → provider
├── memory/
│   ├── session.py             # Session persistence (SQLite)
│   ├── working.py             # Three-tier sliding window memory
│   ├── facts.py               # Structured Facts (conflict detection + confidence)
│   ├── procedures.py          # Behavioral rules (learned from corrections)
│   ├── episodic.py            # Session episode archive
│   └── consolidator.py        # Compress with cheap model
├── skills/
│   ├── loader.py              # Load skills (directory format + legacy .md)
│   ├── registry.py            # Match (with channel filter) + hit stats
│   ├── stats.py               # Hit count + correction collection
│   ├── updater.py             # Auto-update skill content via cheap model
│   └── generator.py           # Auto-generate skills from sessions
├── tools/
│   ├── base.py                # BaseTool abstract class
│   ├── registry.py            # Registry + concurrent executor + turn cache
│   ├── result_compressor.py   # Auto-summarize long tool output
│   └── builtin/
│       ├── shell.py           # Execute shell commands
│       ├── web_search.py      # DuckDuckGo search
│       ├── web.py             # Fetch & extract web page text
│       ├── file.py            # File read/write/list
│       ├── memory_write.py    # Proactive fact write
│       ├── procedure_write.py # Proactive procedure write
│       ├── profile_update.py  # Update user profile
│       └── skill_create.py    # Create skill mid-conversation
├── scheduler/
│   └── cron.py                # APScheduler with SQLite persistence
└── interface/
    ├── cli.py                 # Typer CLI entry point
    ├── repl.py                # Interactive REPL with prompt_toolkit
    ├── api.py                 # FastAPI HTTP + SSE streaming
    ├── lark_events.py         # Lark WebSocket
    └── commands/              # Subcommands (model, provider, session, skill, schedule)

Memory System

Ethan uses a five-layer memory architecture:

Layer Content Storage
Hot Last N turns (full messages) In-memory
Warm Rolling summary of older turns In-memory
Cold (Facts) Key facts extracted across sessions ~/.ethan/memory/facts.json
Procedures Behavioral rules learned from corrections ~/.ethan/memory/procedures.json
User Profile Narrative personal context (goals, phrases, agreements) ~/.ethan/memory/user_profile.md

Compression is batched (not per-turn) and uses an automatically inferred cheap model (e.g. Haiku for Claude users, Flash Lite for Gemini users).

Agent proactively writes to all layers mid-conversation via memory_write, procedure_write, and profile_update tools — no waiting for the next compression cycle.


Skills

Skills are Markdown files loaded from ~/.ethan/skills/. On first run, default skills (channels, deepwiki, lark-im, lark-shared, skills-manager) are automatically copied there from the package.

Both directory format (<name>/SKILL.md + references/) and legacy single-file .md format are supported.

---
name: deploy-checklist
trigger: deploy|ship|release
description: Pre-deployment checklist
fast_path: true       # route to fast track when triggered
channels:             # empty = all channels; list = restrict
  - web
version: "1.0"
---

Steps before deploying:
1. Run tests
2. Check for uncommitted changes
3. ...

When a user message matches a skill's trigger, the skill content is injected into the system prompt. Built-in skills include channels, lark-im, and home-assistant.

Skills accumulate hit stats and user corrections. When corrections reach a threshold (default: 2), the Heartbeat job merges them into the skill file using a cheap model.


Tools

Tools are pluggable — add a new one without touching the agent loop:

from ethan.tools.base import BaseTool

class MyTool(BaseTool):
    name = "my_tool"
    description = "Does something useful"
    fast_path = False   # set True to make available in fast-track mode
    cacheable = False   # set True to cache identical calls within a turn
    parameters = {"type": "object", "properties": {...}, "required": [...]}

    async def run(self, **kwargs) -> str:
        return "result"

Register it in cli.py and the LLM will automatically use it when relevant.


CLI Commands

ethan                              Start interactive REPL
ethan -p "..."                     Single-turn query
ethan -m MODEL                     Use specific model
ethan -r last                      Resume last session
ethan serve                        Start HTTP API server

ethan model list|add|remove|default
ethan provider list|set
ethan session list|show|delete
ethan skill list|show|create
ethan schedule list|remove|pause|resume

HTTP API

GET  /health                    # Health check
GET  /models                    # Available models
POST /chat                      # Chat (stream: true for SSE)
GET  /sessions                  # Session list
GET  /sessions/{id}             # Session detail + messages
GET  /memory/facts              # Facts list
GET  /memory/episodes           # Episode summaries
GET  /skills                    # Skill list
POST /skills                    # Create skill
POST /skills/evolve             # Trigger skill auto-update
GET  /schedule                  # Scheduled jobs
GET  /system-prompt-preview     # Current system prompt preview
GET  /channels                  # Channel list
GET  /knowledge/search          # Semantic search

Configuration

All config lives in ~/.ethan/config.yaml:

providers:
  anthropic:
    api_key: sk-ant-xxx
    base_url: https://api.anthropic.com   # optional
    proxy: null                           # per-provider proxy
  openai_compat:
    api_key: sk-xxx
    base_url: https://api.openai.com/v1

models:
  - id: claude-sonnet-4-6
    provider: anthropic
    description: Claude Sonnet 4.6
    alias: [sonnet]
  - id: gpt-4o
    provider: openai_compat
    alias: [gpt]

network:
  proxy: http://127.0.0.1:7890           # global proxy

defaults:
  model: claude-sonnet-4-6
  agent_name: Ethan
  max_tokens: 4096
  max_tool_iterations: 10
  routing:
    fast_max_length: 12
    medium_max_length: 80
    medium_max_iters: 4
    fast_keywords:
      - "turn off*light"
      - "play music"
    fast_skill_triggers:
      - "home assistant"

Environment variables in .env override config values (useful for secrets).

Config directory layout

~/.ethan/
├── config.yaml          # Main config (providers, models, routing)
├── system/
│   ├── identity.md      # Agent identity (name, role)
│   ├── soul.md          # Behavioral principles
│   └── heartbeat.md     # Heartbeat tasks (natural language)
├── memory/
│   ├── facts.json       # Structured facts
│   ├── procedures.json  # Behavioral rules
│   ├── episodes.json    # Session episode archive
│   └── user_profile.md  # User profile (narrative)
├── skills/              # User-defined skills
│   └── <name>/
│       └── SKILL.md
└── sessions.db          # Session history (SQLite)

Roadmap

✅ Completed

Core Agent

  • Multi-model provider (Anthropic + OpenAI-compatible: Gemini, GPT, Ollama, etc.)
  • ReAct agent loop with streaming output
  • Three-track router: fast / medium / full, tool result compression, per-turn dedup cache
  • Prompt Caching (Anthropic stable-prefix cache_control, ~0.1× input cost)

Five-Layer Memory

  • Hot/warm/cold sliding window + cheap-model batch compression
  • Structured Facts (confidence scoring + conflict detection)
  • Behavioral Procedures (learned from user corrections)
  • Session Episode archive (auto-summary, keyword search)
  • User Profile — narrative document with five named sections
  • Proactive memory write: memory_write, procedure_write, profile_update, skill_create
  • Memory context isolation (anti-pollution XML tags)

Skill System

  • Dual-source loading (built-in + user-defined) + channel filter (channels field)
  • fast_path opt-in, hit stats, correction collection, auto-update (Updater)
  • Session-end background Skill generation (Hermes-style)
  • Built-in skills: home-assistant, lark-im, channels, deepwiki

Tools

  • shell, web_search, web_fetch, file_read/write/list, rg, fd
  • Knowledge base (sqlite-vec semantic search), scheduler tools, ACP → Claude Code

Scheduler

  • Cron + interval, SQLite persistence, auto-restore on restart
  • heartbeat.md: natural-language periodic tasks executed automatically

Channels & API

  • Web UI (Next.js): chat timeline, memory, skills, schedule, knowledge, settings
  • Tool call timeline (collapsible, with icons + duration)
  • Feishu/Lark WebSocket (no public IP required)
  • OpenAI-compatible Completions API (/v1/chat/completions) + API key management
  • Docker deployment + macOS launchd auto-start

🚀 Planned

UX Improvements

  • Message quoting: quote a previous message in the input box
  • User profile settings: avatar upload, display name shown in chat bubbles
  • Scheduler suggestions: Agent detects scheduling opportunities in conversation and prompts user with 1-2-3 options
  • Scheduler templates: ready-to-use tasks (daily briefing, HA device check, knowledge digest)

Channel Expansion

  • WeCom (Enterprise WeChat): alongside Feishu as a second messaging channel
  • Mobile UI: bottom tab nav, touch gestures, keyboard inset handling

Coding Agent Integration

  • ACP multi-turn optimization: smoother Claude Code / OpenCode / Codex sessions with collapsible tool traces
  • MCP client: connect external MCP servers, auto-register tools

Long-term

  • Space isolation: separate memory/skills per context (life / work / project)
  • Async interrupt: detect new messages during long tasks, respond between tool calls
  • Obsidian integration: read/write Obsidian vault as knowledge base

Documentation

Detailed design docs for each module are in docs/:

License

MIT

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

ethan_agent-0.1.2.tar.gz (150.4 kB view details)

Uploaded Source

Built Distribution

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

ethan_agent-0.1.2-py3-none-any.whl (206.8 kB view details)

Uploaded Python 3

File details

Details for the file ethan_agent-0.1.2.tar.gz.

File metadata

  • Download URL: ethan_agent-0.1.2.tar.gz
  • Upload date:
  • Size: 150.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ethan_agent-0.1.2.tar.gz
Algorithm Hash digest
SHA256 12c5a17f6c4c638ab03ee64e0bb38c68975b45cb0e7180a551a91031026eef5b
MD5 64522df8ccfd8038f4e50793dc5fb14b
BLAKE2b-256 3ee6f440b0155aa4ebfd4c46d2eec60cfee33a0fb64e92b425d86177b6027797

See more details on using hashes here.

File details

Details for the file ethan_agent-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: ethan_agent-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 206.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ethan_agent-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 58a825aac7f1f2c0b5bd7a8ea9d550da1bf007acfb9b4959476c612946db2bfa
MD5 d9870bd1b7f0255e0b4c91485916f0ba
BLAKE2b-256 6b80504c323876d8d112b82d1a135237f2bebfb092bd0917f4317bf8f74f9d6c

See more details on using hashes here.

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