AI Agents Framework with Self Reflection and MCP support
io.github.MervinPraison/praisonai — Model Context Protocol (MCP) Server
PraisonAI is an AI agents framework that includes “Self Reflection” and MCP support. It is organized around agents and multi-agent systems, and is packaged as an SDK for building AI agent applications. The project is associated with GitHub repositories under MervinPraison/PraisonAI.
PraisonAI 🦞 — Hire a 24/7 AI Workforce. Stop writing boilerplate and start shipping autonomous, self-improving agents that research, plan, and execute tasks across your apps. From one agent to an entire organization, deployed in 5 lines of code.
from praisonaiagents import Agent
# Give your agent a goal, and watch it work.
agent = Agent(instructions="You are a senior data analyst.")
agent.start("Analyze the top 3 tech trends of 2026 and format as a markdown table.")
🧬 The Five-Layer Agent Stack
Most frameworks hand you one or two layers and leave the rest as homework. PraisonAI covers all five — plus the outer layer that decides where your agent actually runs.
Each layer wraps the one inside it. When an agent misbehaves, the layer tells you where to look.
code
┌─────────────────────────────────────────────────────────────────┐
│ ⬡ MANAGED AGENTS — Where does it actually run? │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 5 · GRAPH — Who runs when, and who checks whom? │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ 4 · LOOP — When do we stop? │ │ │
│ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ 3 · HARNESS — Can it act, and be checked? │ │ │ │
│ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ 2 · CONTEXT — Is the right thing in the window? │ │ │ │ │
│ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │
│ │ │ │ │ │ 1 · PROMPT — Did I say it clearly? │ │ │ │ │ │
│ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ │
│ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │
│ │ │ └─────────────────────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
tools_run_on="docker" — one shared sandbox for the tools, or run_on="anthropic" for the whole agent
Layer 1 · Prompt — Did I say it clearly?
Role, instructions, examples, output format.
python
from praisonaiagents import Agent
agent = Agent(
role="Senior Data Analyst",
goal="Turn raw numbers into decisions",
output="verbose", # markdown-formatted output
)
agent.start("Summarise Q3 revenue trends")
Layer 2 · Context — Is the right thing in the window?
Write, select, compress, isolate — the four context operations, one parameter each.
python
from praisonaiagents import Agent
agent = Agent(
instructions="You are a support engineer.",
memory={"user_id": "u-42"}, # write — persists across runs (needs a user_id)
knowledge=["docs/"], # select — retrieves only what's relevant
context="summarize", # compress — auto-compacts before the limit
)
Isolate is handoffs=[specialist] — a sub-agent inherits the last few messages and the intersection of your tools, not your whole transcript. 📖 Handoffs
Layer 3 · Harness — Can it act, and be checked?
Agent = Model + Harness. Tool dispatch, plus the guides that steer before acting and the sensors that observe after.
python
from praisonaiagents import Agent, MCP, tool
@tooldefdeploy(env: str) -> str:
"""Deploy the current build to an environment."""returnf"Deployed to {env}"
agent = Agent(
name="ReleaseEngineer",
instructions="You are a release engineer.",
tools=[deploy, MCP("npx -y @modelcontextprotocol/server-filesystem /tmp")],
approval=True, # guide — human gate before risky tools run
)
agent.start("Deploy to staging, then list the files you can read")
Layer 4 · Loop — When do we stop?
Hard iteration caps, budget ceilings, no-progress detection and completion checks — every brake is explicit.
python
from praisonaiagents import Agent, ExecutionConfig
agent = Agent(
instructions="Fix the failing tests.",
execution=ExecutionConfig(max_iter=30, max_budget=0.50, on_budget_exceeded="stop"),
autonomy=True, # required to drive the loop with run_autonomous()
)
result = agent.run_autonomous("Refactor the auth module", max_iterations=5)
print(result.completion_reason)
# goal | no_tool_calls | max_iterations | timeout | doom_loop | needs_help | error# (with on_budget_exceeded="stop", hitting the cap raises BudgetExceededError,# surfaced here as completion_reason="error")
Doom-loop detection is on by default. Repeated identical tool calls and A→B→A→B oscillation get caught — while a poller whose output keeps changing does not. 📖 Doom Loop Detection
Layer 5 · Graph — Who runs when, and who checks whom?
Topology as a versionable artifact: prompt chaining, routing, parallelisation, orchestrator-worker.
The same graph is expressible in YAML with no Python at all. 📖 AgentFlow
⬡ Outside the stack: Managed Agents — Where does it actually run?
The harness is commoditising; where the agent executes is the next multiplier. Rather than burning your laptop's CPU, hand an agent a short-lived cloud sandbox — repo, tools and tests run there.
bash
pip install praisonai
The simplest way in is tools_run_on= — one whole team or workflow shares one sandbox, so a file written by step 1 is there for step 2. Thinking stays on your machine:
python
from praisonaiagents import Agent, AgentFlow
writer = Agent(name="Writer", instructions="You write files.")
reader = Agent(name="Reader", instructions="You read files.")
flow = AgentFlow(tools_run_on="docker", steps=[writer, reader]) # or e2b | modal | daytona | flyio
flow.run("Write 'hello' to /workspace/note.txt, then read it back")
Same thing with no Python at all:
yaml
name:remote-demotools_run_on:docker# every step shares one sandboxagents:writer: {role:Writer, goal:Writefiles}
reader: {role:Reader, goal:Readfiles}
steps:-agent:writeraction:"Write 'hello' to /workspace/note.txt"-agent:readeraction:"Read /workspace/note.txt"
For a single agent, two words cover it — and they answer different questions:
python
from praisonaiagents import Agent
# A. Only the TOOLS move. Thinking stays on your machine.
agent = Agent(name="builder", instructions="You build things.",
tools_run_on="docker") # docker | e2b | modal | daytona | flyio# tenki | sandlock | ssh | novita | subprocess# B. The WHOLE agent moves — model calls, loop and tools
agent = Agent(name="teacher", instructions="You teach.", run_on="anthropic") # hosted
agent = Agent(name="builder", instructions="You build.", run_on="docker") # self-hosted
agent.start("Write a Python script that prints the first 10 primes, then run it")
Ask any object where it runs, and it will tell you:
python
>>> Agent(name="builder", instructions="x", tools_run_on="docker")
Agent(name='builder', thinks_on='this machine', tools_run_on='a Docker container')
>>> agent.where_does_it_run()
Thinking (the AI model calls) happens on this machine.
Tools run on a Docker container.
Your own tools (check_db) still run on this machine -- only shell, file and
code tools move. They read and write this machine's files.
Naming a place that cannot do the job is a typo, not a preference, so it says so:
python
>>> Agent(name="x", instructions="i", run_on="e2b")
TypeError: Agent(run_on='e2b') isnot valid: run_on= places the whole agent
-- model calls, loop and tools -- on a managed runtime, and'e2b' runs
commands but cannot host an agent loop.
To run only the tools there: Agent(tools_run_on='e2b')
To run one block of code somewhere else, name the place on that call:
praisonai managed ps # list running sandboxes
praisonai managed stop --all # reclaim them
Sandboxes shut themselves down when idle (auto_shutdown, idle_timeout_s), and a post-setup snapshot is reused so the next run skips the image pull and dependency install. Commit a .praisonai/environment.yaml and the environment travels with the repo.
📖 20 runnable examples · manage sessions with praisonai managed sessions list <agent-id> or praisonai managed sessions resume <session-id> "<prompt>"
from praisonaiagents import Agent
agent = Agent(instructions="You are a helpful AI assistant")
agent.start("Write a movie script about a robot in Mars")
2. Multi Agents
python
from praisonaiagents import Agent, Agents
research_agent = Agent(instructions="Research about AI")
summarise_agent = Agent(instructions="Summarise research agent's findings")
agents = Agents(agents=[research_agent, summarise_agent])
agents.start()
📖 Full MCP docs — stdio, HTTP, WebSocket, SSE transports
4. Custom Tools
python
from praisonaiagents import Agent, tool
@tooldefsearch(query: str) -> str:
"""Search the web for information."""returnf"Results for: {query}"@tooldefcalculate(expression: str) -> float:
"""Safely evaluate a numeric arithmetic expression."""import ast
import operator
# Define allowed operations
_OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
def_safe_eval(node):
ifisinstance(node, ast.Constant) andisinstance(node.value, (int, float)):
return node.value
elifisinstance(node, ast.BinOp) andtype(node.op) in _OPS:
return _OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
elifisinstance(node, ast.UnaryOp) andtype(node.op) in _OPS:
return _OPS[type(node.op)](_safe_eval(node.operand))
else:
raise ValueError("Unsupported expression")
try:
return _safe_eval(ast.parse(expression, mode="eval").body)
except (ValueError, SyntaxError, TypeError, ZeroDivisionError, OverflowError):
raise ValueError("Invalid arithmetic expression")
agent = Agent(
instructions="You are a helpful assistant",
tools=[search, calculate]
)
agent.start("Search for AI news and calculate 15*4")
⚠️ Security Note: Never use eval(), exec(), or subprocess in tool functions that process LLM-generated or user-supplied input. Always validate and sanitize inputs to prevent code injection attacks.
📖 Full tools docs — BaseTool, tool packages, 100+ built-in tools
Open http://localhost:8082 — the dashboard comes with 13 built-in pages: Chat, Agents, Memory, Knowledge, Channels, Guardrails, Cron, and more. Add messaging channels directly from the UI.
📖 Full Claw docs — platform tokens, CLI options, Docker, and YAML agent mode
7. Langflow Integration 🔗 (Visual Flow Builder)
Build multi-agent workflows visually with drag-and-drop components in Langflow.
bash
pip install "praisonai[flow]"
praisonai flow
Open http://localhost:7861 — use the Agent and Agent Team components to create sequential or parallel workflows. Connect Chat Input → Agent Team → Chat Output for instant multi-agent pipelines.
📖 Full Flow docs — visual agent building, component reference, and deployment
8. PraisonAI UI 🤖 (Clean Chat)
Lightweight chat interface for your AI agents.
bash
pip install "praisonai[ui]"
praisonai ui
📄 Using YAML (No Code)
Example 1: Two Agents Working Together
Create agents.yaml:
yaml
framework:praisonaitopic:"Write a blog post about AI"agents:researcher:role:ResearchAnalystgoal:ResearchAItrendsandgatherinformationinstructions:"Find accurate information about AI trends"writer:role:ContentWritergoal:Writeengagingblogpostsinstructions:"Write clear, engaging content based on research"
Run with:
bash
praisonai agents.yaml
The agents automatically work together sequentially
Example 2: Agent with Custom Tool
Create two files in the same folder:
agents.yaml:
yaml
framework:praisonaitopic:"Calculate the sum of 25 and 15"agents:calculator_agent:role:Calculatorgoal:Performcalculationsinstructions:"Use the add_numbers tool to help with calculations"tools:-add_numbers
tools.py:
python
defadd_numbers(a: float, b: float) -> float:
"""
Add two numbers together.
Args:
a: First number
b: Second number
Returns:
The sum of a and b
"""return a + b
Run with:
bash
praisonai agents.yaml
💡 Tips:
Use the function name (e.g., add_numbers) in the tools list, not the file name
Tools in tools.py are automatically discovered
The function's docstring helps the AI understand how to use it
const { Agent } = require('praisonai');
const agent = newAgent({ instructions: 'You are a helpful AI assistant' });
agent.start('Write a movie script about a robot in Mars');
⚡ Performance
PraisonAI is built for speed, with agent instantiation in around 14μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
# Start Ollama and pull a model
ollama serve
ollama pull llama3.2
python
from praisonaiagents import Agent
agent = Agent(instructions="You are a helpful assistant", llm="ollama/llama3.2")
agent.start("Why is the sky blue?")
The ollama/ prefix is what selects Ollama's handling — tool-call repair,
tool-result formatting and the streaming rules small local models need. Or set
it by environment instead:
bash
export OPENAI_MODEL_NAME=ollama/llama3.2
Setting only OPENAI_BASE_URL is not enough: with no model named, the OpenAI
default (gpt-4o-mini) is sent to Ollama, which answers
404 model 'gpt-4o-mini' not found. Always name the model.
Point at a non-default host with base_url= or OLLAMA_HOST: