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.1.tar.gz (363.3 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.1-py3-none-any.whl (135.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fluxibly-1.0.1.tar.gz
  • Upload date:
  • Size: 363.3 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.1.tar.gz
Algorithm Hash digest
SHA256 286c829639a2c8b572118de47ce6f66a4745d125a6795985d8caf2d99f8e4fc6
MD5 29a18d2743d1cdd0041d475c05e1cd63
BLAKE2b-256 e5aece77ee994befce8791d71f41940875f51497ff711f84cf89e2d48ae7824a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fluxibly-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 135.4 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8183f50d72a4d84960d4180d0a64cf097cfe84ab413938c040e11bed0e6be66b
MD5 8bc4d29da80f0d3da5436295a410bb74
BLAKE2b-256 275dc0fec1df02a251da7c0928f7f7cf594e5a5626a37a395fc78b3e8f0796b5

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