Content
# PonyAgent
> **One-sentence definition**: PonyAgent is the "first stepping stone for small and medium-sized enterprises' AI transformation" - allowing traditional business systems to access AI capabilities within 3 days with zero risk, no code refactoring required, and no AI team needed, capable of running on a single machine.
> **Core value**: Single intelligent body + Tools + MCP minimalist architecture · Bypass access with zero code intrusion · Maintainable by ordinary backend engineers · Gradual iterative zero-risk trial and error
---
## Table of Contents
- [Why PonyAgent?](#why-ponyagent)
- [System Introduction](#system-introduction)
- [Applicable Scenarios](#applicable-scenarios)
- [Core Features](#core-features)
- [Competitive Comparison](#competitive-comparison)
- [Quick Start](#quick-start)
- [Architecture Design](#architecture-design)
- [Project Structure](#project-structure)
- [Technical Stack](#technical-stack)
- [Configuration Instructions](#configuration-instructions)
- [Multi-Intelligent Body Expansion](#multi-intelligent-body-expansion)
- [Pluggable Tool Development Guide](#pluggable-tool-development-guide)
- [A2UI Front-end Rendering Component System](#a2ui-frontend-rendering-component-system)
- [User Authentication](#user-authentication)
- [Logs and Monitoring](#logs-and-monitoring)
- [Testing](#testing)
- [Environment Requirements](#environment-requirements)
- [License](#license)
---
## Why PonyAgent?
Small and medium-sized enterprises face three major pain points in AI transformation:
| Pain Point | Traditional Solution (LangGraph/Dify) | PonyAgent Solution |
|------|--------------------------|---------------|
| **High trial and error cost** | Requires 2-4 weeks of development, waste if failed | **3 days to go live** with the first AI feature, can roll back at any time |
| **High technical threshold** | Requires AI engineers + complex framework learning | **Ordinary backend engineers** can develop and maintain |
| **High transformation cost** | Requires reconstructing existing systems, invasive business code | **Bypass access**, no changes to original business code |
| **High operation and maintenance cost** | Multiple containers/services/complex monitoring | **Single machine deployment**, one-click start with Docker |
**PonyAgent is not the strongest AI framework, but it is the fastest to land, lowest cost, and lowest risk solution for small and medium-sized enterprises' AI transformation.**
### Design Philosophy
- **Simplicity is justice**: A single instance can carry the complete dialogue, memory, task, and execution chain, rejecting over-design
- **Isolation is security**: `session_id` runs through the entire chain, Redis/SQLite strictly isolates by session, absolutely avoiding data interference
- **Configuration is code**: 100% configuration interface `.env`, model parameters, storage thresholds, and human templates are all external, zero code changes to switch environments
- **Task standardization**: `TaskResult` fixed structure + `run_user_task` fixed signature, business logic is plug-and-play
---
## System Introduction
PonyAgent adopts a minimalist architecture of "**single main intelligent body + business Tools + external MCP**":
```
User query → LLM understands intent → Calls Tools/MCP → Gets result → LLM organizes reply → Returns to user
```
**No need** for multi-intelligent body orchestration, **no need** for state machines, **no need** for complex workflows.
**Only need**: Business logic encapsulated into Python functions, LLM automatically determines when to call.
### Why a single intelligent body is sufficient?
| Business scenario | Solution | Is multi-intelligent body needed? |
|---------|---------|---------------|
| Check inventory, orders, customers | **Tools Handler** directly checks database | ❌ Not needed |
| Generate reports, data analysis | **Tools Handler** calls local calculation | ❌ Not needed |
| Connect external API, file system | **MCP protocol** standardized access | ❌ Not needed |
| Complex reasoning, multi-step tasks | **Main intelligent body LLM** automatically disassembles | ❌ Not needed |
> One LLM understands intent + Tools execute business, covering 90% of small and medium-sized enterprise scenarios. Multi-intelligent body orchestration is over-design.
---
## Applicable Scenarios
| Industry | Landing scenario | Implementation method | Estimated time to go live |
|------|---------|---------|------------|
| **Medical** | Health manager (check department, ask for scheduling, appointment) | Tools check HIS database | 2-3 days |
| **Retail** | Intelligent customer service + inventory query + order assistant | Tools connect ERP API | 3-5 days |
| **Manufacturing** | Production data query + equipment repair + report generation | Tools connect MES database | 1 week |
| **Education** | Course consultation + homework marking + learning report | Tools + MCP access document system | 1 week |
| **General** | Enterprise knowledge base Q&A + internal tool assistant | MCP access document/knowledge base | 3 days |
### Gradual landing path
```
Week 1: Knowledge base Q&A (simplest scenario, validate value)
↓
Week 2: Add data query (check inventory, orders)
↓
Week 3: Add business processing (reservation, ordering, approval)
↓
Week 4: Add intelligent analysis (report generation, data insight)
```
**Each stage can go live independently, can roll back at any time, without affecting the original business system.**
---
## Core Features
### 1. Zero code intrusion into old systems
- **Bypass access**: Access existing ERP/CRM/business systems through database read-only permission or API
- **No business code changes**: Original system continues to run, AI capabilities deployed independently
- **Failed can roll back**: Closing PonyAgent service can restore the original process, zero-risk trial and error
### 2. Minimal deployment, single machine combat
- Only need Python + Redis + SQLite, single container/single machine operation
- Docker Compose one-click start, complete deployment in 5 minutes
- Redis connection failure **automatically downgrades to memory storage**, service uninterrupted
### 3. Strong session isolation, zero data interference
- `session_id` is the unique isolation key throughout the chain
- Redis Key format: `session:{sid}:short` / `session:{sid}:summary`
- SQLite isolates user profile storage by `session_id`
- Any cross-session access automatically intercepts and records ERROR logs
### 4. Standardized task engine
- `TaskStatus` fixed enumeration: `pending` / `running` / `progress` / `completed` / `failed`
- `TaskResult` standardized structure, required fields fully covered
- Task lifecycle automatically managed: timeout recovery, deadlock inspection, failure retry
- Concurrent current limit protection (default 5 concurrent), prevent system overload
### 5. 100% configuration external
- All parameters interface `.env`, prohibit direct reading `os.environ`
- Support `dev` / `prod` environment one-click switching
- Development mode automatically enable MD template hot reload + Uvicorn hot restart
### 6. Three-layer architecture of memory system
- **Short-term memory**: Redis List sliding window, quick response
- **Dialogue summary**: Trigger asynchronous LLM summary every N rounds, persist to SQLite
- **User profile**: Update profile every N summaries, implement personalized service
### 7. Enterprise-level authentication system
- Session Cookie + Redis + SQLite triple authentication
- Role system: `user` / `operator` / `admin`, permissions increase gradually
- **bcrypt 12-round password hash**, audit log fully recorded
- **Password policy**: Minimum 8 characters, mandatory complexity (uppercase + numbers + special characters)
- **Login failure lock**: Continuous 5 failures lock for 15 minutes, prevent brute force cracking
- **Session rotation**: Regularly refresh Session ID, prevent fixed session attacks
- Automatically create administrator at first startup, out of the box
### 8. Multi-intelligent body zero-change expansion
- **No need to change a line of code**, no need to introduce complex scheduling framework
- Copy instance + independent `.env` = multi-intelligent body cluster
- Each instance completely isolated, independent iteration, independent start and stop
### 9. Pluggable task type (Harness architecture + automatic scanning)
- Dynamically register task processor through `TaskRegistry`, add task type **zero change framework code**
- **Automatic scanning**: New `tools/handlers/*.py` file, system automatically load, **no need to modify main.py**
- **Metadata driven**: Declare `params` (parameter description) and `examples` (usage examples) during registration, LLM capability list real-time synchronization
- Support group registration, prefix naming, automatic capability list generation
### 10. LLM model backend pluggable
- Abstract `LLMProvider` interface, interface programming instead of hard coding
- Built-in OpenAI / Ollama dual backend, configuration drive one-click switching
- Circuit breaker three-state protection: close → open → half-open
- **Cost controllable**: Simple tasks use lightweight model (GPT-3.5), complex tasks use heavy model (GPT-4)
### 11. MCP protocol access external service
- Access external tools through MCP (Model Context Protocol) standardization
- Support STDIO / SSE / HTTP three transmission methods
- Built-in circuit breaker protection, prevent external service failure affecting local service
- Tool list automatically cache, reduce repeated requests
### 12. Production-level observability
- `/metrics` endpoint exposes Prometheus format indicators (no external dependencies)
- Built-in current limiting middleware (token bucket algorithm, isolated by session_id)
- Circuit breaker protect downstream service, prevent avalanche
### 13. A2UI front-end rendering component system
- LLM output structured JSON contract (`{"intent":"chat","content":"...","ui":{...}}`), parsing no longer fragile
- 8 built-in A2UI components: progress/card/form/confirm/list/chart/file/link
- `ui` field optional, does not affect `content` text, front-end render rich components as needed
- Task progress real-time push to WebSocket (`type="progress"` + `ui.type="progress"`)
- A2UI components pluggable: 1 line of code register custom front-end component type
---
## Competitive Comparison
### Small and medium-sized enterprises' AI transformation perspective
| Comparison dimension | **PonyAgent** | **LangGraph** | **Dify/Coze** | **OpenClaw** |
|---------|--------------|--------------|--------------|-------------|
| **Core positioning** | Small and medium-sized enterprises' AI transformation base | Universal intelligent body orchestration framework | Low-code AI application platform | Multi-channel self-tested gateway |
| **Time to go live** | **3 days** | 2-4 weeks | 1-2 weeks | 1 week |
| **Invasiveness** | **Zero invasion (bypass)** | Need to develop according to framework specifications | Need to develop according to platform specifications | Medium |
| **Operation and maintenance personnel requirements** | **Ordinary backend** | AI engineers | Platform operation and maintenance | Backend |
| **Annual cost** | **< 10,000** | 100,000-200,000 | 20,000-50,000 | 30,000-80,000 |
| **Private deployment** | **Complete privatization** | Can be privatized, complex | SaaS-based | Can be privatized |
| **Adaptation to old systems** | **Perfect adaptation** | Need to transform | Need to transform | Need to transform |
| **Gradual iteration** | **Phase-by-phase addition of functions** | Need to design as a whole | Need to design as a whole | Can be gradual |
| **Framework dependency** | **Zero dependency** | Strong dependency on LangChain | Strong dependency on platform | Medium |
| **Learning curve** | **Extremely low** | Steep | Medium | Medium |
### PonyAgent six major advantages
#### Advantage 1: Minimal deployment, single machine combat
**PonyAgent**: Only need Docker Compose one-click start, single machine run complete service.
**Competitors**:
- LangGraph: Need to understand Graph, Node, Edge concepts, need LangSmith monitoring
- Dify: Need to develop according to platform specifications, high learning cost
- OpenClaw: Multi-channel adaptation configuration complex
**Value**: Personal developers/small teams do not need professional operation and maintenance, 5 minutes have production-level intelligent body service.
#### Advantage 2: Strong session isolation, zero data interference
**PonyAgent**: `session_id` throughout the chain, Redis/SQLite isolate storage by session, absolutely avoid cross-user data leakage.
**Competitors**:
- OpenClaw: Multi-channel access, session isolation weak, easy to interfere
- LangGraph: No built-in isolation mechanism, need to implement yourself
- Dify: Platform-level isolation, but multi-tenant scenarios still need to be cautious
**Value**: Suitable for scenarios with high data privacy requirements (such as medical, financial, enterprise internal intelligent body).
#### Advantage 3: Standardized task, business plug-and-play
**PonyAgent**: `TaskResult` fixed structure + `TaskRegistry` dynamic registration, business logic zero change framework:
```python
# Add task type: New tools/handlers/*.py, automatically scan and load, no need to modify main.py
from tools.executor import registry
async def my_handler(task, engine) -> str:
await engine.update_progress(task.task_id, 50)
return "Processing completed"
# Register declare parameters and examples, LLM capability list automatically synchronize
registry.register(
"my_task",
my_handler,
description="Custom task",
params={"key": "parameter description"},
examples=["usage examples"]
)
```
**Competitors**:
- OpenClaw: Rely on Skills ecosystem, task logic coupled heavily
- LangGraph: Need to understand Chain/Agent/LCEL and other concepts
- Dify: Need to configure workflow according to platform specifications
**Value**: Developers focus on business logic, no need to understand framework bottom layer, reduce 80% access cost.
#### Advantage 4: Configuration 100% interface .env
**PonyAgent**: Model parameters, storage thresholds, concurrent control, human templates all external to `.env`.
**Competitors**:
- OpenClaw: Configuration scattered in multiple files
- LangGraph: Configuration scattered, need code-level adjustment
- Dify: Platform configuration, cannot version control
**Value**: One-click switch environment (development/test/production), dynamic parameter tuning, zero code change switch model (OpenAI ↔ Ollama).
#### Advantage 5: Multi-intelligent body zero change expansion
**PonyAgent**: Copy instance + independent `.env` = multi-intelligent body cluster, each instance completely isolated.
```bash
# Customer service intelligent body
cp -r PonyAgent agent_customer
cd agent_customer && docker-compose up -d
# Technical intelligent body
cp -r PonyAgent agent_tech
cd agent_tech && docker-compose up -d
```
**Competitors**:
- LangGraph: Need to learn graph orchestration concepts
- Dify: Single application mode, multi-application need additional configuration
- OpenClaw: Single instance multi-channel, not really multi-intelligent body
**Value**: No need to change a line of code, no need to introduce scheduling framework, no need to handle Agent communication protocol. Keep kernel extremely simple, through horizontal expansion to achieve multi-intelligent body.
#### Advantage 6: Private deployment friendly
**PonyAgent**: Data do not leave the country, local SQLite persistence + Redis hot data, built-in user authentication, role system, audit log.
**Competitors**:
- LangGraph: Cloud-first, privatization need additional configuration
- Dify: SaaS-based, privatization version function limited
- OpenClaw: Personal assistant positioning, enterprise-level function insufficient
**Value**: Meet government, finance, medical and other industry data security compliance requirements, support completely off-grid operation.
### Market positioning summary
| Scenario | Recommended framework |
|------|---------|
| Small and medium-sized enterprises' AI transformation (zero-risk trial and error) | **PonyAgent** ✅ |
| Personal developers quickly verify ideas | **PonyAgent** ✅ |
| Small team commercial intelligent body base | **PonyAgent** ✅ |
| Enterprise privatization deployment | **PonyAgent** ✅ |
| Large enterprise complex multi-intelligent body orchestration | LangGraph |
| Multi-platform IM access gateway | OpenClaw |
| Low-code AI application construction | Dify/Coze |
**PonyAgent is not doing the "**most powerful**" intelligent body framework, but doing the "**simplest**, **cleanest**, and **controllable**" intelligent body base.** In the era of complex framework over-design, use minimalist architecture to solve 80% of real business scenarios.
---
## Quick Start
### Method 1: Docker deployment (recommended, 5 minutes)
```bash
# 1. Clone project
git clone https://github.com/yourname/ponyagent.git
cd ponyagent
# 2. Copy configuration template
cp .env.example .env
# Edit .env, configure LLM API Key and other parameters
# 3. Start service
docker-compose up -d
# 4. Health check
curl http://localhost:9001/health
```
### Method 2: Local development
```bash
# 1. Install dependencies
pip install -r requirements.txt
# 2. Configure environment
cp .env.example .env
# Edit .env
# 3. Development mode start (hot reload)
ENV=dev python main.py
```
### Method 3: Access existing business system (3 days landing)
```bash
# Day 1: Deploy PonyAgent, configure database connection (read-only)
cp .env.example .env
# Edit .env:
# - Configure LLM API Key
# - Configure database connection (point to existing business library, read-only permission)
docker-compose up -d
# Day 2: Write business Tools (check inventory, orders, customers)
# New tools/handlers/my_biz.py:
# - Connect existing database
# - Implement query logic
# - Register to TaskRegistry
# System automatically scan and load, no need to modify main.py
# Day 3: Front-end access WebSocket, test conversation
# User: "Check inventory" → LLM call query_inventory → return result
```
**Key principles**: No modification to existing system code, through database read-only or API bypass access.
### First login
```bash
# Default administrator account (automatically created at first startup)
# Username: .env configuration ADMIN_USERNAME
# Password: .env configuration ADMIN_PASSWORD
# Login get Session
curl -X POST http://localhost:9001/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"changeme"}' \
-c cookies.txt"'
```
### Why is it simple enough?
```
┌─────────────────────────────────────────────┐
│ User (WebSocket/HTTP) │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ PonyAgent Main Intelligent Body │
│ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │
│ │ LLM │ │ Memory │ │ Task Engine │ │
│ │ Understand Intent │ │ Management │ │ Progress/Timeout │ │
│ └────┬────┘ └─────────┘ └──────────────┘ │
│ │ │
│ ┌────▼──────────────────────────────────┐ │
│ │ Tools Registry │ │
│ │ ┌────────┐ ┌────────┐ ┌──────────┐ │ │
│ │ │ Inventory │ │ Write Record │ │ Generate Report │ │ │
│ │ └────────┘ └────────┘ └──────────┘ │ │
│ │ (Business logic encapsulated as Python functions) │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ┌────▼──────────────────────────────────┐ │
│ │ MCP Protocol Layer (External Services) │ │
│ │ Database │ File System │ Third-party API │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
```
**Unnecessary Components** (not required by other frameworks):
- ❌ Multi-agent Orchestrator
- ❌ State Machine / Workflow Engine
- ❌ Complex Scheduling Framework
- ❌ Visual Process Designer
**Because**: One LLM understands intent + Tools execute business, covering 90% of scenarios.
### Core Process
```
User Input
↓
LLM Understand Intent (intent: chat / task)
↓
├─ intent=chat → Directly organize language response
└─ intent=task → Call Tools / MCP
↓
Execute business logic
↓
Return result
↓
LLM organize response
↓
Return to user
```
---
## Project Structure
```
.
├── main.py # Entry: Uvicorn + Lifecycle Management
├── config.py # Unique configuration entry: pydantic + .env
├── requirements.txt
├── docker-compose.yml
├── Dockerfile
├── .env.example # Configuration template (all parameters collected)
│
├── api/ # Access layer
│ ├── routes.py # WebSocket chat /health /tasks
│ ├── auth.py # Authentication routes: login/logout/user management
│ ├── middleware.py # Authentication middleware: Session verification/permission verification
│ ├── rate_limit.py # Rate limiting middleware (token bucket, isolated by session_id)
│ ├── a2ui.py # A2UI component registry (8 built-in + pluggable)
│ ├── progress.py # Task progress real-time notification system (bridge engine → WebSocket)
│ ├── metrics.py # /metrics endpoint (Prometheus format, no external dependencies)
│ ├── dependencies.py # FastAPI dependencies: get_current_user
│ └── schemas.py # Pydantic models (including A2UIComponent)
│
├── core/ # Main intelligent body — Harness architecture
│ ├── agent.py # LLM call + intent judgment + task trigger
│ ├── container.py # DI container: unified management of dependent instances
│ ├── interfaces.py # Abstract interface layer (IRedisClient/ISQLitePool, etc.)
│ ├── loader.py # MD template loading + development mode hot reload
│ ├── llm_provider.py # LLM Provider abstract base class + factory
│ ├── providers/ # Model backend implementation
│ │ ├── __init__.py
│ │ ├── openai.py # OpenAI compatible interface
│ │ └── ollama.py # Ollama local model
│ ├── flow.md # Process rules (user-customizable)
│ ├── soul.md # Character template (user-customizable)
│ ├── agents.md # Capability list (user-customizable)
│ ├── summary_prompt.md # Conversation summary template
│ └── profile_prompt.md # User profile template
│
├── session/ # Session isolation + memory system
│ ├── manager.py # Session strong isolation + disconnection snapshot + idle inspection
│ ├── memory.py # Short-term memory + asynchronous summary + profile update
│ └── context.py # Session context sharing module (decoupling cyclic import)
│
├── task/ # Task engine
│ ├── models.py # TaskStatus/TaskResult (fixed structure)
│ ├── engine.py # Task CRUD + progress + timeout recovery
│ └── scheduler.py # Zombie task inspection coroutine
│
├── tools/ # Local tool layer — pluggable
│ ├── __init__.py
│ ├── registry.py # Dynamic task registry (supporting metadata: params + examples)
│ ├── executor.py # Registry distribution + automatic scanning of handlers/*.py
│ └── handlers/ # Tool processor directory (automatically loaded, no need to modify main.py)
│ ├── __init__.py
│ ├── echo.py # Echo test processor
│ ├── data_analysis.py # Data analysis processor
│ ├── file_process.py # File processing processor
│ ├── report_generate.py # Report generation processor
│ └── crm.py # CRM business processor
│
├── mcp/ # MCP protocol layer — external tool access
│ ├── __init__.py
│ ├── protocol/ # MCP protocol implementation
│ │ ├── __init__.py
│ │ ├── client.py # MCP client (JSON-RPC + tool cache)
│ │ ├── types.py # MCP data model (JSON-RPC / Tool / Capabilities)
│ │ └── transport.py # Transport layer abstract interface
│ ├── adapters/ # Transport adapters
│ │ ├── __init__.py
│ │ ├── stdio.py # STDIO transport adapter
│ │ ├── sse.py # SSE transport adapter
│ │ └── http.py # HTTP transport adapter
│ ├── circuit.py # MCP dedicated circuit breaker
│ └── config_loader.py # MCP server configuration loader
│
├── store/ # Storage layer
│ ├── redis.py # Asynchronous Redis singleton + TTL + retry
│ └── sqlite.py # SQLite WAL + asynchronous connection pool
│
└── utils/ # Tool layer
├── async_util.py # Asynchronous retry + timeout decorator
├── circuit_breaker.py # Three-state circuit breaker (CLOSED/OPEN/HALF_OPEN)
├── prompt_render.py # Jinja2 template rendering
├── logger.py # Graded logging + link tracking
└── security.py # Password hashing + Session generation + role verification
```
---
## Technology Stack
| Category | Technology | Version |
|------|------|------|
| Web Framework | FastAPI | 0.110 |
| ASGI Server | Uvicorn | 0.29 |
| Real-time Communication | WebSockets | 12.0 |
| Configuration Management | Pydantic Settings | 2.2 |
| Environment Variables | python-dotenv | - |
| Hot Data Storage | Redis | 5 (supporting downgrade memory mode) |
| Persistent Storage | aiosqlite | 0.20 |
| HTTP Client | httpx | 0.27 |
| Template Rendering | Jinja2 | 3.1.5 |
| Development Tool | watchdog | 4.0.0 |
| Testing Framework | pytest + pytest-asyncio | 9.0+ |
| Coverage | pytest-cov | 7.1+ |
---
## Configuration Description
All configurations are managed through the `.env` file, and **prohibited** to directly read `os.environ`.
### Core Configuration Items
```bash
# Service basics
ENV=prod # prod/dev, dev enables hot reload
HOST=0.0.0.0
PORT=9001
WORKERS=1 # Fixed 1 for a single machine to avoid multi-process isolation issues
# LLM Model (all parameters collected)
LLM_PROVIDER=openai # openai/ollama/oneapi
LLM_BASE_URL=https://api.openai.com/v1
LLM_API_KEY=sk-xxx
LLM_MODEL=gpt-3.5-turbo
LLM_TEMPERATURE=0.7
LLM_MAX_TOKENS=2048
# Session and Memory
SESSION_IDLE_SECONDS=600 # Idle timeout 10 minutes
SESSION_REDIS_TTL=3600 # Redis session data expiration time after idle
MEMORY_SHORT_WINDOW=15 # Short-term memory window
SUMMARY_TRIGGER_ROUND=5 # Trigger summary every 5 rounds
PROFILE_TRIGGER_COUNT=3 # Update profile every 3 summaries
MEMORY_SYNC_RETRY_TIMES=2 # Memory storage failure retry times
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_MAX_CONNECTIONS=20
REDIS_KEY_TTL_TASK=604800 # Completed/failed task Redis retention for 7 days
REDIS_KEY_TTL_SESSION_IDLE=3600 # Idle session TTL
# SQLite
SQLITE_DB_PATH=./store/user_data.db
SQLITE_WAL_MODE=ON
SQLITE_SYNC_MODE=NORMAL
SQLITE_CONNECTION_POOL_SIZE=3
# Task Engine
TASK_DEFAULT_TIMEOUT=300 # Task timeout 5 minutes
TASK_MAX_CONCURRENT=5 # Maximum concurrency
TASK_CLEANUP_INTERVAL=60 # Zombie inspection cycle
TASK_FAILED_RETAIN_DAYS=7 # Failed task history retention days
# Circuit Breaker
CIRCUIT_BREAKER_THRESHOLD=5 # Open circuit breaker after continuous N failures
CIRCUIT_BREAKER_RECOVERY=30 # Circuit breaker recovery waiting seconds
# Rate Limiting (strongly recommended to enable in production environment)
RATE_LIMIT_ENABLED=true # true=enable rate limiting (default enabled)
RATE_LIMIT_TOKENS=60 # Token bucket capacity (per minute)
RATE_LIMIT_REFILL_RATE=1.0 # Token supplement rate (pieces/second)
# Authentication
ADMIN_USERNAME=admin # Required for first startup
ADMIN_PASSWORD=changeme # Change immediately after first startup!
# Password Policy (security reinforcement)
PASSWORD_MIN_LENGTH=8 # Minimum password length
PASSWORD_REQUIRE_COMPLEXITY=true # Mandatory complexity (case + number + special character)
PASSWORD_HASH_ROUNDS=12 # bcrypt hash rounds
# Login failure locking (anti-brute-force cracking)
LOGIN_MAX_ATTEMPTS=5 # Continuous failure times trigger locking
LOGIN_LOCKOUT_MINUTES=15 # Locking duration (minutes)
# Session Security
SESSION_ROTATION_ENABLED=true # Enable Session ID periodic rotation
SESSION_ROTATION_INTERVAL=1800 # Rotation interval (seconds, default 30 minutes)
# HTTPS enforcement (mandatory in production environment)
FORCE_HTTPS=false # true=reject HTTP requests (need to cooperate with reverse proxy)
# CORS cross-domain (default closed, production environment restricted domain)
CORS_ENABLED=false # Same domain deployment无需开启
CORS_ALLOW_ORIGINS= # Empty string=prohibit all cross-domain (security default)
```
### MCP Configuration Description
PonyAgent supports accessing external tool servers through MCP (Model Context Protocol):
```bash
# MCP function switch
MCP_ENABLED=false # true=enable MCP function
# MCP server configuration path
MCP_CONFIG_PATH=./mcp_servers.json # MCP server configuration file
```
**MCP Server Configuration Example** (`mcp_servers.json`):
```json
{
"servers": [
{
"name": "filesystem",
"transport": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"]
}
},
{
"name": "fetch",
"transport": {
"type": "sse",
"url": "http://localhost:3000/sse"
}
}
]
}
```
**Transport Types**:
| Type | Description | Applicable Scenarios |
|------|------|---------|
| `stdio` | Standard input and output | Local command-line tools |
| `sse` | Server-Sent Events | Remote HTTP services |
| `http` | HTTP request | REST API services |
**Features**:
- MCP tool call **do not occupy** local task engine concurrent slots
- Built-in circuit breaker protection to prevent external service faults from affecting local services
- Tool list automatically cached to reduce repeated requests
- Support for multiple server concurrent connections
---
## Multi-agent Extension
PonyAgent supports two multi-agent modes, **Mode A** recommended:
### Mode A: Physical Multi-instance (Recommended)
Applicable scenarios: customer service Agent + technical Agent + sales Agent, each Agent business is completely different, no collaboration required.
```bash
# 1. Copy project directory
for agent in customer tech sales; do
cp -r PonyAgent agent_$agent
cd agent_$agent
# 2. Modify .env: port, character, capabilities
sed -i "s/PORT=9001/PORT=9002/" .env
sed -i "s/ADMIN_USERNAME=.*/ADMIN_USERNAME=admin_$agent/" .env
# 3. Customize character and capabilities
cp soul.md.example soul.md
cp agents.md.example agents.md
# 4. Start instance
docker-compose up -d
cd ..
done
```
**Advantages**:
- ✅ Zero architecture changes
- ✅ Completely isolated, no interference
- ✅ Independent iteration, independent upgrade, independent start and stop
- ✅ Consistent with 'single main intelligent body' core architecture
### Mode B: Logical Multi-agent (Single Instance Switching)
Applicable scenarios: automatically switch customer service/technical/sales roles within the same conversation.
Implementation:
1. Add multiple sub-agent configuration directories under `core/`
2. Main Agent intent judgment → route to corresponding sub-Agent
3. Sub-Agents share session memory and task engine
**Note**: This mode increases architectural complexity and is recommended only when necessary for collaboration within the same conversation.
---
## Pluggable Tool Development Guide
### Task Type Pluggable (TaskRegistry)
Add custom task types **without modifying framework code**, just create a new `tools/handlers/*.py` file:
```python
# tools/handlers/crm.py (new file, no need to modify main.py, system automatically scans and loads)
from tools.executor import registry
```
# Tool List
## 1. Writing Processor Functions (Fixed Signature)
async def crm_add_customer(task, task_engine) -> str:
"""Add customer to CRM"""
await task_engine.update_progress(task.task_id, 50)
phone = task.params.get("phone")
# Call CRM API...
return f"Customer added successfully, phone: {phone}"
## 2. Registering to the Registry (Supporting Metadata: Parameter Description + Usage Examples)
registry.register(
"crm_add_customer",
crm_add_customer,
description="Add customer to CRM system",
params={
"name": "Customer name, optional, default 'Unnamed Customer'",
"phone": "Mobile phone number, required"
},
examples=[
"Add customer Zhang San, phone 13800138000",
"Add new customer, phone 1730165"
]
)
## Automatic Scanning Mechanism
- The system automatically scans all `*.py` files in the `tools/handlers/` directory at startup
- Dynamically imports and executes registration, **no need to manually import to main.py**
- LLM capability list is synchronized in real-time and always consistent with the code
## Batch Registration (Optional)
```python
registry.register_group("biz_", {
"order_create": my_order_handler,
"order_query": my_query_handler,
"order_cancel": my_cancel_handler,
})
```
### LLM Model Backend Pluggable (Provider Abstraction)
Implementing the `LLMProvider` abstract class allows access to any model backend:
```python
# core/providers/my_provider.py
from core.llm_provider import LLMProvider
class MyCustomProvider(LLMProvider):
def __init__(self):
super().__init__("my_custom")
async def _do_call(self, context: str, system_prompt: str = "") -> str:
# Call custom model API
return "Model response"
def to_dict(self) -> dict:
return {"provider": "my_custom"}
# Register to Provider Factory
from core.llm_provider import create_provider
# Add a branch in create_provider() in core/llm_provider.py
```
### Custom PromptLoader (Prompt Hot Reload)
Modifying `core/*.md` files adjusts Agent behavior, and development mode automatically hot reloads:
- `soul.md` — Character template, defines AI assistant's personality and behavior
- `flow.md` — Process rules, defines intent judgment logic and task trigger rules
- `agents.md` — Capability list rules (task list automatically generated by the system, no need for manual maintenance)
### Complete Example: Adding "Report Generation" Task
```python
# tools/handlers/report_generate.py (new file, automatically scanned and loaded)
import time
from tools.executor import registry
async def report_generate(task, engine) -> str:
await engine.update_progress(task.task_id, 10)
title = task.params.get("title", "Report")
content = task.params.get("content", "")
await engine.update_progress(task.task_id, 40)
report = f"# {title}\n\n{content}\n\n---\nGeneration time: {time.ctime()}"
await engine.update_progress(task.task_id, 80)
return report
# Register (supporting metadata, LLM automatically generates capability list)
registry.register(
"report_generate",
report_generate,
description="Generate structured report",
params={
"title": "Report title",
"content": "Report content"
},
examples=["Generate a sales analysis report"]
)
```
### Architecture Diagram
```
User → Agent(LLM Provider) → TaskRegistry → Custom Processor
↓ ↑
OpenAI/Ollama registry.register()
```
---
## A2UI Frontend Rendering Component System
A2UI (Agent-to-UI) allows AI responses to carry frontend rendering instructions, **not affecting text messages**, `content` always displayed, `ui` field optional additional rendering.
### 8 Built-in Components
| Component Type | Purpose | data key fields |
|---------|------|----------------|
| `progress` | Task progress bar | `title, progress(0-100), status(pending/running/completed/failed)` |
| `card` | Information card | `title, content, highlight(optional), footer(optional)` |
| `form` | Dynamic form | `title, fields[{name,label,type,required}], submit` |
| `confirm` | Confirmation dialog | `title, message, confirm/cancel, level(info/warning/danger)` |
| `list` | Data list | `title, columns[], items[{}], empty_text` |
| `chart` | Simple chart | `chart_type(bar/line/pie), title, labels[], values[]` |
| `file` | File download | `name, url, size, type, preview` |
| `link` | External link | `text, url, description, target` |
### LLM Output JSON Contract
```json
{
"intent": "chat",
"content": "Text for user to see",
"ui": {"type": "card", "data": {"title": "Analysis result", "content": "..."}}
}
```
- `ui` optional, if not filled = pure text message
- Keep old format line parsing compatibility (automatic fallback)
- `ui` data independent of `content`, frontend finds renderer according to `ui.type`
### Frontend Processing Flow
```
Receive WS message → Render content text → Check response.ui
├─ ui == null → Display text only
└─ ui != null → Find corresponding renderer according to ui.type → Pass ui.data for rendering
```
### Custom A2UI Component (1-line registration)
```python
from api.a2ui import a2ui_registry
a2ui_registry.register("weather_card", {
"title": "Weather card",
"description": "Weather information with temperature icon",
"schema": {"type": "weather_card", "data": {"city": "string", "temp": "number"}},
"renderer": "a2ui-weather-card" # Frontend Web Component / component name
})
```
### Real-time Task Progress Push
Task engine automatically pushes WebSocket messages at the following nodes:
| Timing | type value | ui.type |
|------|--------|---------|
| Task startup | `task` | `progress` (0%) |
| Progress update | `progress` | `progress` (0-100%) |
| Task completion | `progress` | `progress` (100%) |
| Task failure | `progress` | `progress` (same progress, status=failed) |
Frontend can also actively query: send `{"task_id": "task_xxx"}` to get current progress.
---
## User Authentication
### Role System
| Role | Permissions |
|------|------|
| `user` | Ordinary user, can use conversation and task functions |
| `operator` | Operator, can view task status and user data |
| `admin` | Administrator, can create/manage users, view audit logs |
### Authentication Process
1. Call `/auth/login` to get Session Cookie
2. Cookie name `pony_session`, valid for 1 hour
3. All APIs (except `/health`, `/auth/login`) require cookie
4. `AuthMiddleware` uniformly checks permissions
### Password Strategy
System enforces password security strategy:
- **Minimum length**: 8 characters (configurable `PASSWORD_MIN_LENGTH`)
- **Complexity requirements**: Must contain uppercase and lowercase letters, numbers, and special characters (configurable `PASSWORD_REQUIRE_COMPLEXITY`)
- **Hash storage**: bcrypt 12-round hash, never store plaintext
### Login Failure Lockout
Anti-brute-force cracking mechanism:
- **Trigger condition**: 5 consecutive login failures (configurable `LOGIN_MAX_ATTEMPTS`)
- **Lockout time**: 15 minutes (configurable `LOGIN_LOCKOUT_MINUTES`)
- **Anti-enumeration**: User does not exist / password error / account disabled uniformly returns "Username or password error"
### Session Rotation
Regularly refresh Session ID to prevent fixed session attacks:
- **Enabled status**: Default enabled (configurable `SESSION_ROTATION_ENABLED`)
- **Rotation interval**: 30 minutes (configurable `SESSION_ROTATION_INTERVAL`)
- **Secure transmission**: Production environment automatically enables `Secure` flag (only HTTPS)
### Create User
```bash
# Administrator creates new user (password must meet policy)
curl -X POST http://localhost:9001/admin/users \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{
"username": "newuser",
"password": "SecureP@ssw0rd",
"role": "user"
}'
```
### Security Hardening Suggestions
Production environment deployment suggestions:
1. **Immediately modify default password**: Change `ADMIN_PASSWORD` after first startup
2. **Enable rate limiting**: `RATE_LIMIT_ENABLED=true`
3. **Limit CORS**: `CORS_ALLOW_ORIGINS=https://yourdomain.com` (prohibit use of `*`)
4. **Force HTTPS**: `FORCE_HTTPS=true` (need to cooperate with Nginx/Traefik reverse proxy)
5. **Redis security**: Bind internal network IP + strong password + disable dangerous commands
6. **File permissions**: Set SQLite data file permissions to `600`
7. **Regular audit**: View `login_audit` table to detect abnormal login
---
## Log and Monitoring
### Log Configuration
```bash
# Log directory: logs/, split by day
LOG_FORMAT=text # text/json
LOG_ROTATE_DAYS=7 # retention days
```
### Link Tracking
```python
from utils.logger import set_log_context, clear_log_context
# Set context
set_log_context(session_id="xxx", task_id="yyy")
# Clear context (prevent leakage)
clear_log_context()
```
### Performance Indicators
- Slow request alert: > 100ms
- Slow operation alert: > 50ms
- Error rate limiting: same type of error 1 second at most 10
### Metrics Monitoring Endpoint
> Built-in Prometheus text format indicators, no external dependencies.
```
GET /metrics
```
Return indicators:
| Indicator name | Type | Description |
|-------|------|------|
| `ponyagent_task_created_total` | counter | Total tasks created |
| `ponyagent_task_completed_total` | counter | Total tasks completed |
| `ponyagent_task_failed_total` | counter | Total tasks failed |
| `ponyagent_task_running` | gauge | Current running tasks |
| `ponyagent_llm_calls_total` | counter | Total LLM calls |
| `ponyagent_llm_failures_total` | counter | Total LLM call failures |
| `ponyagent_llm_circuit_breaker_open` | gauge | Is circuit breaker open (1=open) |
| `ponyagent_llm_latency_seconds` | histogram | LLM call time distribution |
| `ponyagent_ws_connections_total` | counter | Cumulative WebSocket connections |
| `ponyagent_rate_limit_hits_total` | counter | Total rate limit hits |
### Circuit Breaker
LLM call built-in three-state circuit breaker protection:
| State | Behavior |
|------|------|
| CLOSED (closed) | Normal call, cumulative failure times |
| OPEN (open) | Quick refusal, `CIRCUIT_BREAKER_RECOVERY` seconds later try to recover |
| HALF_OPEN (half-open) | Release a request test recovery, successful then close, failed then continue open |
Configuration items:
```bash
CIRCUIT_BREAKER_THRESHOLD=5 # 5 consecutive failures open circuit breaker
CIRCUIT_BREAKER_RECOVERY=30 # 30 seconds later try to recover
```
---
## Test
The project has **260+ test cases**, overall coverage rate **86%** (core module 90%+).
```bash
# Run all tests
python -m pytest .test/
# Detailed output
python -m pytest .test/ -v
# With coverage report
python -m pytest .test/ --cov=. --cov-report=html
# Concurrency stress test
python -m pytest .test/test_concurrency_stress.py -v
```
### Test Coverage Report (v2.0)
| Module | Coverage | Description |
|------|--------|------|
| `api/auth.py` | 80% | Login/logout/user management full-link test |
| `api/metrics.py` | 100% | Prometheus indicator endpoint complete coverage |
| `api/middleware.py` | 89% | Authentication middleware Session verification |
| `api/rate_limit.py` | 100% | Rate limiting token bucket algorithm |
| `task/engine.py` | 92% | Task life cycle + concurrency control |
| `task/models.py` | 92% | TaskResult contract test |
| `utils/security.py` | 100% | Password hash + role verification |
| `core/llm_provider.py` | 100% | LLM Provider abstract interface |
| `store/sqlite.py` | 95% | WAL mode + asynchronous connection pool |
### Test Specifications
- **Mock external dependencies**: Redis, SQLite, LLM all Mock, no real service
- **DI container test**: Inject Fake object through `container` fixture
- **Asynchronous test**: Use `@pytest.mark.asyncio` decorator
- **Contract test**: `TaskStatus` / `TaskResult` / interface signature cannot be modified
- **Stress test**: Verify `asyncio.Lock` + `_running_count` concurrency control correctness
### Concurrency Stress Test
| Test scenario | Concurrency | Verification point | Test file |
|---------|--------|--------|---------|
| Concurrent create task | 100 | All created successfully, ID unique | `test_concurrency_stress.py` |
| Concurrent limit execution | 10 | Only 5 enter RUNNING (TASK_MAX_CONCURRENT=5) | `test_concurrency_stress.py` |
| Counter correctness | 5 running | Complete/failed counter accurately decremented | `test_concurrency_stress.py` |
| High concurrency competition condition | 50 | Counter always <=5, no competition | `test_concurrency_stress.py` |
| Concurrent progress update | 10 simultaneous updates | No data competition | `test_concurrency_stress.py` |
| Mixed stress test | 20 create + 10 start | Concurrency slot release can continue to start | `test_concurrency_stress.py` |
---
## Environment Requirements
- Python 3.11+
- Redis 7+ (Docker orchestration included)
- Port: 9001 (API)
---
## License
MIT License
---
## Contribution Guide
Welcome to submit Issue and Pull Request!
### Development Specifications
1. All configurations must be read from `config.py`
2. Prohibit modifying `TaskStatus` enumeration value and `TaskResult` structure
3. Keep `session_id` full-link isolation
4. All user logic outer layer must try-except
5. Run test before submission: `python -m pytest .test/`
Connection Info
You Might Also Like
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
awesome-claude-skills
A curated list of awesome Claude Skills, resources, and tools for...
claude-flow
Claude-Flow v2.7.0 is an enterprise AI orchestration platform.
Appwrite
Build like a team of hundreds
semantic-kernel
Build and deploy intelligent AI agents with Semantic Kernel's orchestration...
Anthropic-Cybersecurity-Skills
734+ structured cybersecurity skills for AI agents · MITRE ATT&CK mapped ·...