Learnable conditional edges for agentic workflows using contextual bandits
Project description
AdaptiveGraph
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:
examples/basic_routing.py- Simple routing with feedbackexamples/customer_support_agent.py- LangGraph integrationnotebooks/interactive_demo.ipynb- Interactive walkthrough
How LearnableEdge Works
Think of LearnableEdge as a smart traffic router that learns which highway to recommend:
- Input State → You provide any state (string, dict, array)
- Encoding → Converts state into a fixed-size vector (32-dim by default)
- UCB Scoring → For each option, calculates:
- Expected reward (what worked before)
- Uncertainty bonus (exploration value)
- UCB Score = expected_reward + α × uncertainty
- Selection → Picks option with highest UCB score
- Feedback → You call
record_feedback(reward) - 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54917264c193d20103795f8ce180eeee87ac95206abb1abae3f3ef5daaae47a9
|
|
| MD5 |
0f2306145be4d5a5f8a19c3d5609c7cf
|
|
| BLAKE2b-256 |
230c6b684b3ef180b128911ba719883faf9238db44388ef50dd968734eecd40e
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee097e2407620e98a635174b02c8909de7d53e09271ed562a3b694794fadbfc4
|
|
| MD5 |
611f1479cb8bbfbe5e8ccca278dd0d26
|
|
| BLAKE2b-256 |
380c0a2d99d0b4e7d94f04fd0112469e034027503ebbd0990fbd446f63f780ab
|