Skip to main content

Modular Agentic Framework — LLM + Agent + Tools with MCP support

Project description

Fluxibly

Modular Agentic Framework — LLM + Agent + Tools

Python 3.11+ License: MIT

Fluxibly is a framework for building any Agent pipeline simply. Provide general, easy-to-plug, mix-and-match components — to build a new Agent, you only tweak a few core pieces.

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                        USER / APPLICATION                           │
│                                                                     │
│   from fluxibly import Runner                                       │
│   result = await Runner.run("config/agent.yaml", messages, context) │
│                                                                     │
└──────────────────────────────┬──────────────────────────────────────┘
                               │
┌──────────────────────────────▼──────────────────────────────────────┐
│                       RUNNER (Execution Loop)                       │
│  Manages: handoff chain, max_turns, agent swapping, session sandbox │
│  run() → agent.forward() → if handoff → swap agent → repeat         │
├─────────────────────────────────────────────────────────────────────┤
│                         AGENT LAYER                                 │
│                                                                     │
│  BaseAgent (abstract) → SimpleAgent, CustomAgent, ...               │
│  Manages: LLMs, tools, prompts, delegation (handoffs + agents)      │
│  forward() = prepare → LLM → tool loop → return                     │
│                                                                     │
│  Delegation:                                                        │
│   Handoffs ────── transfer_to_xxx → one-way control transfer        │
│   Agents-as-tools  agent_xxx → delegate & return                    │
│   Skills ────── load_skill → on-demand instruction loading          │
├─────────────────────────────────────────────────────────────────────┤
│                          LLM LAYER                                  │
│                                                                     │
│  BaseLLM (abstract) → OpenAILLM, AnthropicLLM, GeminiLLM            │
│                      → LangChainLLM, LiteLLM                        │
│  prepare() → format for provider                                    │
│  forward() → single inference, standardized output                  │
├─────────────────────────────────────────────────────────────────────┤
│                        TOOLS LAYER                                  │
│                                                                     │
│  Functions │ MCP │ Web Search │ File Search │ Shell │ Computer Use  │
└─────────────────────────────────────────────────────────────────────┘

Installation

pip install fluxibly

With provider extras:

pip install fluxibly[openai]          # OpenAI only
pip install fluxibly[anthropic]       # Anthropic only
pip install fluxibly[gemini]          # Google Gemini only
pip install fluxibly[all]             # All providers

Usage

Run an Agent

The simplest way — pass a config path to Runner.run(). It loads the config, resolves the agent class, and runs.

import asyncio
from fluxibly import Runner

async def main():
    result = await Runner.run(
        "agents/my_agent.yaml",
        [{"role": "user", "content": "Hello!"}],
    )
    print(result.response.content.output.output_text)

asyncio.run(main())

Runner.run() accepts:

Input What happens
str (path/name) Loads config YAML → resolves agent_class → instantiates → runs
AgentConfig Resolves agent_class → instantiates → runs
BaseAgent instance Uses directly → runs

Agent Config (YAML)

Every agent is a YAML file:

# agents/my_agent.yaml
name: "my_agent"
description: "A helpful general assistant"
# agent_class: "SimpleAgent"          # Default — or "path.to.module::CustomAgent"

llm:
  model: "gpt-4o"
  temperature: 0.7

system_prompt: "You are a helpful assistant."

tools:
  - "lookup_faq"                      # Tool name → resolved from tools/ directory
  - "./tools/web_search.yaml"         # Tool path → loaded directly

handoffs:
  - "billing_specialist"              # Agent name → one-way transfer
  - agent: "refund_specialist"        # With custom options
    input_filter: "remove_tools"

agents:
  - "researcher"                      # Agent name → delegate & return

skills:
  - "csv-insights"                    # Skill name → on-demand loading

sandbox:                              # Optional: sandboxed shell execution config
  use_uv: true                        # Create a uv venv in the sandbox
  packages: ["pandas"]                # Pre-install packages in the venv
  timeout_seconds: 300                # Command timeout (default: 300)

max_tool_iterations: 10

Create a New Tool

Each tool has two files — a YAML schema and an optional Python handler:

tools/get_weather/
├── get_weather.yaml     # Required: schema (what the LLM sees)
├── get_weather.py       # Optional: handler (what runs when called)
└── requirements.txt     # Optional: Python dependencies (auto-installed)
# tools/get_weather/get_weather.yaml — schema
type: "function"
function:
  name: "get_weather"
  description: "Get current weather for a city"
  parameters:
    type: "object"
    properties:
      city: { type: "string", description: "City name" }
    required: ["city"]
# tools/get_weather/get_weather.py — handler
async def handler(city: str) -> str:
    return f"Weather for {city}: sunny, 72°F"

If a requirements.txt sits alongside the tool YAML, dependencies are auto-installed into the sandbox venv on first use. The file follows the standard uv/pip requirements format:

# tools/get_weather/requirements.txt
requests>=2.31

The handler .py file is auto-discovered by name (same stem as the .yaml). If a tool needs runtime context (e.g., a sandbox session), use the factory pattern:

# tools/file_stats.py — factory handler (receives sandbox session)
async def create_handler(*, session=None, **kwargs):
    async def file_stats(file_path: str) -> str:
        r = await session.run(f"wc {file_path}")
        return r.stdout or r.stderr
    return file_stats

Reference tools from an agent config:

# agents/weather_agent.yaml
name: "weather_agent"
llm:
  model: "gpt-4o-mini"
system_prompt: "You help with weather queries."
tools:
  - "get_weather"

The agent auto-discovers the YAML schema, the .py handler, and requirements.txt — no manual wiring needed.

Create a New Agent (for delegation)

Each agent is its own YAML config. Reference it as a handoff or agent-as-tool from another agent:

# agents/researcher.yaml
name: "researcher"
description: "Do deep web research on a topic. Returns a summary."
llm:
  model: "gpt-4o-mini"
  temperature: 0.3
system_prompt: "You are a research assistant."
tools:
  - "web_search"
# agents/manager.yaml
name: "manager"
description: "Senior analyst who delegates research and writing"
llm:
  model: "gpt-4o"
system_prompt: "Break down questions. Use your research and writing agents."

# These become agent_researcher and agent_writer tools
agents:
  - "researcher"
  - "writer"
result = await Runner.run("agents/manager.yaml", messages)

Create a New Skill

Skills are on-demand instructions that load only when needed. Each skill is a directory with a SKILL.md:

skills/csv-insights/
├── SKILL.md            # Required: metadata + instructions
├── requirements.txt    # Optional: Python dependencies (auto-installed)
├── scripts/            # Optional: executable code
│   └── analyze.py
└── assets/             # Optional: reference files
---
name: csv-insights
description: Summarize a CSV, compute basic stats, and produce a markdown report.
---

# CSV Insights Skill

## When to use this
- User provides a CSV and wants a summary, stats, or visualization

## How to run
python scripts/analyze.py --input <csv_path> --outdir output

Only the frontmatter (name + description) is loaded at startup. The full body loads when the LLM calls load_skill.

Skill staging: When a sandbox is configured, the skill directory (including scripts/) is automatically copied into the sandbox so the LLM can reference and execute scripts via shell commands.

Auto-install: If the skill directory contains a requirements.txt, dependencies are automatically installed into the sandbox venv before the first LLM call. The file uses standard uv/pip requirements format.

Reference from an agent config:

skills:
  - "csv-insights"

Runtime Context

Pass additional resources at runtime via context:

result = await Runner.run(
    "agents/my_agent.yaml",
    messages,
    context={
        "prompt_params": {
            "domain": "finance",
            "system": {"personality": "formal"},
        },
        "tools": ["extra_tool"],
        "agents": ["extra_agent"],
        "skills": ["extra_skill"],
    },
)

Dynamic Resource Bundles

Inject full resource definitions at runtime (e.g. from an external service):

agent_bundle = {
    "name": "specialist_v2",
    "type": "agent",
    "resources": {
        "config.yaml": 'name: "specialist_v2"\nllm:\n  model: "gpt-4o"\n...',
        "agent.py": 'class SpecialistV2(SimpleAgent):\n    ...',
    },
    "structure": {"config.yaml": "file", "agent.py": "file"},
}

result = await Runner.run(
    "agents/triage.yaml",
    messages,
    context={"agents": [agent_bundle]},
)

Bundles are materialized to the session sandbox as real files, then resolved identically to any other path.

Sandboxed Execution

When tools or skills need to run shell commands, Fluxibly provides OS-level isolation using Anthropic's sandbox-runtime (SRT) and fast Python environment creation via uv.

Sandboxing is lazy — nothing is created until the first shell command runs. The sandbox is then reused for the entire session and cleaned up automatically by the Runner.

Install SRT for OS-level isolation (optional):

npm install -g @anthropic-ai/sandbox-runtime
brew install ripgrep   # Required by SRT (macOS)

If SRT is not installed, commands fall back to direct subprocess execution with a warning.

Sandbox YAML config

sandbox:
  use_uv: true                          # Create a uv venv in the sandbox (default: true)
  python_version: "3.11"                 # Python version for uv venv (default: system)
  packages: ["pandas", "matplotlib"]     # Pre-install packages on first use (prefer requirements.txt)
  timeout_seconds: 300                   # Max seconds per command (default: 300)
  allowed_domains:                       # Network allowlist (default: pypi.org, github.com)
    - "pypi.org"
    - "files.pythonhosted.org"
  deny_read:                             # Filesystem read denylist (default: ~/.ssh, ~/.aws, .env)
    - "~/.ssh"
    - "~/.aws"
  allow_write: ["/data/output"]          # Extra writable paths beyond sandbox_dir

Prefer requirements.txt: Instead of listing packages in the sandbox config, place a requirements.txt in each tool or skill directory. Dependencies are auto-installed on first use, and each tool/skill owns its own deps.

Programmatic usage

from fluxibly import SandboxSession
from fluxibly.tools import ToolService

session = SandboxSession("/tmp/my-sandbox")
ts = ToolService()
ts.register_sandboxed_shell(session)  # Registers a "shell" function tool

See fluxibly/tools/sandbox.py and examples/03_multi_agent.py for details.

Environment Setup

Copy .env.example to .env and fill in your values:

cp .env.example .env

The .env file is auto-loaded when you import fluxibly. At minimum, set your LLM provider API key:

# .env
OPENAI_API_KEY=sk-...
# or
ANTHROPIC_API_KEY=sk-ant-...
# or
GOOGLE_API_KEY=...

See .env.example for the full list of available settings (database, monitoring, etc.).

Monitoring

Fluxibly includes built-in monitoring that records traces, spans, and tool calls to a PostgreSQL database. Monitoring is configured exclusively via environment variables in your .env file — never through agent YAML configs.

1. Set up the database

Create a PostgreSQL database for monitoring data:

createdb fluxibly_monitoring

2. Enable monitoring in .env

FLUXIBLY_MONITORING_ENABLED=true
FLUXIBLY_MONITORING_DASHBOARD_DB_HOST=localhost
FLUXIBLY_MONITORING_DASHBOARD_DB_PORT=5432
FLUXIBLY_MONITORING_DASHBOARD_DB_NAME=fluxibly_monitoring
FLUXIBLY_MONITORING_DASHBOARD_DB_USER=postgres
FLUXIBLY_MONITORING_DASHBOARD_DB_PASSWORD=your_password

When FLUXIBLY_MONITORING_ENABLED=true, all agents automatically pick up monitoring — no code changes needed. Tables are created automatically on first run.

3. Launch the monitoring dashboard

python -m fluxibly.monitoring.dashboard

The dashboard runs at http://localhost:8555 by default. You can customize the host/port:

python -m fluxibly.monitoring.dashboard --host 0.0.0.0 --port 9000

Or via environment variables:

FLUXIBLY_MONITORING_DASHBOARD_HOST=0.0.0.0
FLUXIBLY_MONITORING_DASHBOARD_PORT=8555

Custom Agents

Extend AgentTemplate to build custom agents with pre/post hooks:

from fluxibly.agent import AgentTemplate, AgentConfig, AgentResponse

class MyAgent(AgentTemplate):
    async def pre_forward(self, messages, context):
        # Inject custom params before the pipeline runs
        context.setdefault("prompt_params", {})
        context["prompt_params"]["custom_var"] = "value"
        return messages, context

    async def post_forward(self, response, messages, context):
        # Inspect or modify the response
        return response

See examples/04_custom_agent.py for a full working example.

Requirements

  • Python 3.11+
  • API keys for your chosen LLM provider
  • For sandboxed execution (optional):
    • SRT: npm install -g @anthropic-ai/sandbox-runtime
    • ripgrep: brew install ripgrep (macOS) / apt install ripgrep (Linux) — required by SRT
    • uv: pip install uv or brew install uv — for fast venv creation

License

MIT License — see LICENSE for details.

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

fluxibly-1.0.0.tar.gz (361.9 kB view details)

Uploaded Source

Built Distribution

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

fluxibly-1.0.0-py3-none-any.whl (133.6 kB view details)

Uploaded Python 3

File details

Details for the file fluxibly-1.0.0.tar.gz.

File metadata

  • Download URL: fluxibly-1.0.0.tar.gz
  • Upload date:
  • Size: 361.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for fluxibly-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6cbf8d22ca501702d3162114031d0bf688e9ae886df5972337d4db7ebf522f21
MD5 9ab6a9a1b0b159fbe0cfecc760f77b62
BLAKE2b-256 bbc9332a979ca7d10dd6b5f5f24bba676825548c6e929a9bf04697fa7d85a3ea

See more details on using hashes here.

File details

Details for the file fluxibly-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: fluxibly-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 133.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for fluxibly-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e2c5bad7a783aa578b38d4628e02bc29aa0dd361c25c2c31c3d1cbe422620c6
MD5 595e8d63d60cecb9f5838b2b47a5b9f8
BLAKE2b-256 ea283ac0df5b379a2dab8499310d4b54ee8652fff1e50be6ff51349e45654b3a

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