Skip to main content

Learnable conditional edges for agentic workflows using contextual bandits

Project description

AdaptiveGraph

CI License: MIT Python 3.11+ Code style: black

from adaptivegraph import LearnableEdge

AdaptiveGraph provides LearnableEdge – a conditional edge that improves routing decisions over time through reinforcement learning.

What is LearnableEdge?

LearnableEdge replaces static routing logic with a learning-based approach. Instead of hard-coded if/else statements, it learns from feedback which routes work best for different inputs.

Key Features:

  • 🧠 Learning: Adapts routing decisions based on feedback (currently LinUCB, more policies coming)
  • Lightweight: No training phase, no model files – learns online
  • 🎯 Balanced: Balances exploration (trying new routes) vs exploitation (using proven routes)
  • 🔧 Flexible: Works with strings, dicts, numpy arrays, or custom embeddings

Why LearnableEdge?

Traditional routing logic is static and brittle. LearnableEdge adds plasticity – it learns which routes work best through experience, automatically adapting as patterns change.

Installation

From PyPI:

pip install adaptivegraph

From source:

git clone https://github.com/BharathBillawa/adaptivegraph.git
cd adaptivegraph
pip install -e .

With optional dependencies:

# For semantic embeddings
pip install adaptivegraph[embed]

# For persistent storage
pip install adaptivegraph[faiss]

# For development
pip install adaptivegraph[dev]

# Everything
pip install adaptivegraph[all]

Quick Start

from adaptivegraph import LearnableEdge

# Create edge with routing options
edge = LearnableEdge(options=["expert_model", "fast_model", "cheap_model"])

# Make decisions
route = edge({"user": "premium", "query": "complex question"})

# Provide feedback so it learns
edge.record_feedback(result={}, reward=1.0)

See more examples:

How LearnableEdge Works

Think of LearnableEdge as a smart traffic router that learns which highway to recommend:

  1. Input State → You provide any state (string, dict, array)
  2. Encoding → Converts state into a fixed-size vector (32-dim by default)
  3. UCB Scoring → For each option, calculates:
    • Expected reward (what worked before)
    • Uncertainty bonus (exploration value)
    • UCB Score = expected_reward + α × uncertainty
  4. Selection → Picks option with highest UCB score
  5. Feedback → You call record_feedback(reward)
  6. Learning → Updates internal belief about which options work best

The algorithm balances:

  • Exploitation → Using routes with good historical performance
  • Exploration → Trying routes with high uncertainty

Features

- **Semantic Routing**: Use sentence transformers for similarity-based decisions (requires `adaptivegraph[embed]`)
- **Async Feedback**: Provide feedback hours later using event IDs
- **Trajectory Rewards**: Reward entire multi-step sessions with decay
- **Policy Persistence**: Save and restore learned routing policies

## Usage Examples

### Async Feedback
Pass `event_id` in the state to track decisions, then use it to record feedback later.
```python
# 1. Make decision with event_id
state = {"user": "premium", "query": "async test", "event_id": "req_123"}
route = edge(state)

# 2. Record feedback later using the same ID (no state needed)
edge.record_feedback(result={}, reward=1.0, event_id="req_123")
```

### Trajectory (Multi-step) Rewards
Pass `trace_id` in the state to group decisions into a session.
```python
# Step 1
edge({"step": 1, "intent": "greeting", "trace_id": "session_abc"})
# Step 2
edge({"step": 2, "intent": "params", "trace_id": "session_abc"})

# Reward the whole trace
edge.complete_trace(trace_id="session_abc", final_reward=1.0)
```

### Statistics
Inspect memory to see what the model has learned.
```python
stats = edge.memory.get_statistics()
print(f"Total decisions: {stats['total_decisions']}")
print(f"Average reward: {stats['average_reward']:.2f}")

# Access raw history
print(f"History size: {len(edge.memory.state_history)}")
```

## Notes & Gotchas
*   **Numpy Arrays**: Must match `feature_dim` (default 32). E.g., `np.random.randn(32)`.
*   **Optional Dependencies**:
    *   `embedding="sentence-transformers"` requires `pip install adaptivegraph[embed]`
    *   `memory="faiss"` requires `pip install adaptivegraph[faiss]`

See [`examples/`](examples/) and [`notebooks/`](notebooks/) for detailed usage.

Comparison

LearnableEdge vs alternatives:

  • vs If/Else Rules: Learns from feedback instead of requiring manual updates
  • vs ML Classifiers: No training data needed, learns online from real usage
  • vs LLM Routers: Faster, cheaper, and adapts based on actual outcomes
  • vs Random/Epsilon-Greedy: Uses context to make smarter decisions, not just random exploration

Performance

On a typical customer support routing task (3 options, 1000 decisions):

  • Convergence: Achieves >95% accuracy within 50-100 iterations
  • Throughput: ~10,000 decisions/second on MacBook Pro M1
  • Memory: <50MB for 10,000 experiences

Roadmap

Completed ✅

  • Core LinUCB policy
  • Persistent storage (FAISS/Pickle)
  • Semantic state encoding (Sentence Transformers)
  • Async / ID-based feedback
  • Trajectory / trace rewards
  • Policy state persistence
  • Input validation and logging

Planned 🚧

  • Thompson Sampling policy
  • Epsilon-Greedy policy
  • Shared feature LinUCB (not disjoint)
  • Distributed training support
  • Visualization dashboard
  • Automated hyperparameter tuning
  • Integration examples (FastAPI, Streamlit)

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Quick Start for Contributors

# Clone and setup
git clone https://github.com/BharathBillawa/adaptivegraph.git
cd adaptivegraph
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Format code
black src/ tests/
isort src/ tests/

Citation

If you use AdaptiveGraph in your research, please cite:

@software{adaptivegraph2024,
  title = {AdaptiveGraph: Learnable Conditional Edges for Agentic Workflows},
  author = {BharathPoojary},
  year = {2024},
  url = {https://github.com/BharathBillawa/adaptivegraph}
}

License

MIT License - see LICENSE for details.

Acknowledgments

  • Built for LangGraph but framework-agnostic
  • LinUCB algorithm from Li et al. (2010)
  • Inspired by contextual bandit research in recommendation systems

Support


Made with ❤️ for the AI agent community

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

adaptivegraph-0.1.1.tar.gz (63.0 kB view details)

Uploaded Source

Built Distribution

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

adaptivegraph-0.1.1-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file adaptivegraph-0.1.1.tar.gz.

File metadata

  • Download URL: adaptivegraph-0.1.1.tar.gz
  • Upload date:
  • Size: 63.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for adaptivegraph-0.1.1.tar.gz
Algorithm Hash digest
SHA256 54917264c193d20103795f8ce180eeee87ac95206abb1abae3f3ef5daaae47a9
MD5 0f2306145be4d5a5f8a19c3d5609c7cf
BLAKE2b-256 230c6b684b3ef180b128911ba719883faf9238db44388ef50dd968734eecd40e

See more details on using hashes here.

File details

Details for the file adaptivegraph-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: adaptivegraph-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for adaptivegraph-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ee097e2407620e98a635174b02c8909de7d53e09271ed562a3b694794fadbfc4
MD5 611f1479cb8bbfbe5e8ccca278dd0d26
BLAKE2b-256 380c0a2d99d0b4e7d94f04fd0112469e034027503ebbd0990fbd446f63f780ab

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