Skip to main content

A State-Aware Rollback and Trajectory-Replanning Framework for Autonomous LLM Agents

Project description

๐Ÿ›ก๏ธ DriftGuard

A State-Aware Rollback and Trajectory-Replanning Framework for Autonomous LLM Agents

CI Status PyPI Version Python Versions License: MIT Code Style: Black

Installation โ€ข Quickstart โ€ข How it Works โ€ข API Reference โ€ข Integrations


DriftGuard wraps any LLM agent executor with a causal state graph, automatic divergence detection, rollback to the last safe state, and failure-context-aware replanning โ€” so your agents recover from mid-chain errors without full restarts.

The Problem

Agents in multi-step tasks make errors mid-chain (tool failures, goal drift, infinite loops) and have no way to recover without a full restart. This wastes tokens, time, and context.

The Solution

DriftGuard models an agent run as a causal graph of state transitions:

S0 โ†’ S1 โ†’ S2 โ†’ S3 (๐Ÿ’ฅ error) 
              โ†˜ rollback โ†’ S2 โ†’ S4 (โœ… success)

When divergence is detected, the system:

  1. Identifies the failure via pluggable detectors
  2. Rolls back to the last causally-safe state
  3. Replans with failure context injected into the next prompt
  4. Resumes โ€” the agent tries a different strategy

Installation

pip install agents-driftguard

With optional backends:

# Local embeddings (sentence-transformers)
pip install agents-driftguard[embeddings]

# OpenAI embeddings + function-calling adapter
pip install agents-driftguard[openai]

# LangChain integration
pip install agents-driftguard[langchain]

# Development
pip install agents-driftguard[dev]

Quickstart

from agents_driftguard import AgentUndoWrapper

# Your agent is just a callable: (prompt: str) -> str
def my_agent(prompt: str) -> str:
    # Call your LLM here
    return llm.generate(prompt)

# Wrap it with DriftGuard
wrapper = AgentUndoWrapper(
    executor=my_agent,
    max_rollbacks=3,
    detectors=["goal_drift", "tool_failure", "loop"],
    embedding_backend="dummy",  # or "sentence-transformers" / "openai"
)

# Run with automatic rollback protection
result = wrapper.run(goal="Research and summarize recent AI papers")

print(result.success)             # True/False
print(result.final_output)        # The agent's final answer
print(result.rollbacks_performed) # Number of rollbacks that occurred
print(result.causal_graph_mermaid) # Mermaid diagram of the state graph

How it Works

Execution Loop

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 AgentUndoWrapper                โ”‚
โ”‚                                                โ”‚
โ”‚  1. Call executor(prompt)                      โ”‚
โ”‚  2. Parse action + observation โ†’ AgentState    โ”‚
โ”‚  3. Embed current context โ†’ state_embedding    โ”‚
โ”‚  4. Add to CausalGraph                         โ”‚
โ”‚  5. Run DivergenceDetector                     โ”‚
โ”‚  6. If divergence detected:                    โ”‚
โ”‚     a. Mark node diverged                      โ”‚
โ”‚     b. Find safe rollback point                โ”‚
โ”‚     c. RollbackController.rollback()           โ”‚
โ”‚     d. Replanner.replan() โ†’ inject context     โ”‚
โ”‚     e. Resume from safe state                  โ”‚
โ”‚  7. Repeat until done or max_rollbacks         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Divergence Detectors

Detector What it catches How
GoalDriftDetector Agent wandering off-task Cosine similarity between goal and state embeddings
ToolFailureDetector Tool errors and crashes Regex patterns in observations (errors, tracebacks, HTTP codes)
LoopDetector Infinite loops State hash deduplication over a sliding window
ConstraintChecker Business rule violations User-defined Python lambdas
LLMJudge Subtle misalignment Optional LLM-based scoring
OutputQualityDetector Suspiciously poor outputs Checking length and word repetition ratio
TokenBudgetDetector Excessive API usage Whitespace-based token estimation across state history
HallucinationDetector Made-up information Flagging confident uncertainty or apology patterns

Detectors are combined with configurable strategies: "any" (default), "all", or "weighted".

Causal Graph

Every agent run produces a causal DAG:

flowchart TD
    classDef safe fill:#2d6a4f,stroke:#1b4332,color:#fff
    classDef diverged fill:#d00000,stroke:#6a040f,color:#fff
    S0["S0: __start__"]:::safe
    S1["S1: search"]:::safe
    S2["S2: compute"]:::safe
    S3["S3: write_file"]:::diverged
    S4["S4: output"]:::safe
    S0 --> S1
    S1 --> S2
    S2 --> S3
    S2 --> S4

Dependency-Aware Rollback Traversal

Unlike naive sequential rollback systems, DriftGuard tracks resource inputs and outputs (e.g. specific files, SQL tables, code modules). When a tool failure signal registers a failed_resource (for instance, a SyntaxError in utils.py), DriftGuard:

  1. Performs backwards graph traversal along causal ancestors.
  2. Identifies the exact ancestor step that modified or created the failed resource.
  3. Selects the step immediately preceding that ancestor as the target recovery state.
  4. Unwinds the side-effects of all intermediate steps in reverse chronological order using their registered @reversible compensating hooks, preserving unrelated execution branches.

Replanning

After rollback, the agent receives failure context:

=== ROLLBACK CONTEXT ===
You previously attempted: write_file(path='/protected/path')
This failed because: A tool call returned an error or unexpected result.
You have been rolled back to step 2.
Original goal: Analyze data and produce a summary report
Actions that led to failure (do NOT repeat this approach):
  - Step 3: write_file โ†’ 'Error: Permission denied'
Try a different strategy.
=== END ROLLBACK CONTEXT ===

API Reference

AgentUndoWrapper

wrapper = AgentUndoWrapper(
    executor=my_fn,                    # callable: (str) -> str
    max_rollbacks=3,                   # max rollback attempts
    detectors=["goal_drift", "tool_failure", "loop"],
    goal_drift_threshold=0.75,         # cosine similarity threshold
    store="memory",                    # "memory" | "sqlite"
    constraints=[lambda s: ...],       # user-defined validators
    on_rollback=callback_fn,           # optional rollback hook
    embedding_backend="dummy",         # "dummy" | "sentence-transformers" | "openai"
    max_steps=50,                      # safety limit
    verbose=False,
)

result = wrapper.run(goal="your task")

AgentRunResult

Field Type Description
success bool Whether the agent completed successfully
final_output str The agent's final answer
total_steps int Total steps executed
rollbacks_performed int Number of rollbacks
causal_graph_mermaid str Mermaid diagram string
rollback_history list[RollbackResult] Detailed rollback logs

@reversible Decorator

Register undo functions for tools with side-effects:

from agents_driftguard import reversible

def undo_write(args, result):
    os.remove(args["path"])

@reversible(undo_fn=undo_write, tool_name="write_file")
def write_file(path: str, content: str) -> str:
    with open(path, "w") as f:
        f.write(content)
    return f"Written to {path}"

During rollback, undo_write is automatically called to reverse the side-effect.

Async Support

DriftGuard natively supports asynchronous agent execution using AsyncAgentUndoWrapper.

import asyncio
from agents_driftguard.async_wrapper import AsyncAgentUndoWrapper

async def my_async_agent(prompt: str) -> str:
    # Await your LLM call here
    return await async_llm.generate(prompt)

async def main():
    wrapper = AsyncAgentUndoWrapper(executor=my_async_agent)
    result = await wrapper.arun(goal="Fetch async data")
    print(result.success)

asyncio.run(main())

Live Web Dashboard

Launch the zero-dependency, local web dashboard to view telemetry in real-time.

from agents_driftguard.dashboard.app import start_dashboard_server

# Start the dashboard in the background
start_dashboard_server(port=8050)

# Then run your agent
wrapper.run("Do work")

Navigate to http://localhost:8050 in your browser to see your agent's timeline, causal graph visualization, and rollback events as they happen!

Integrations

LangChain

from langchain.agents import AgentExecutor
from agents_driftguard.integrations.langchain import LangChainAdapter

executor = AgentExecutor(agent=..., tools=...)
adapter = LangChainAdapter(executor)

wrapper = AgentUndoWrapper(
    executor=adapter,
    step_parser=adapter.parse_step,
)

OpenAI Function Calling

from openai import OpenAI
from agents_driftguard.integrations.openai import OpenAIFunctionCallingAdapter

client = OpenAI()
adapter = OpenAIFunctionCallingAdapter(
    client=client,
    model="gpt-4o",
    tools=[...],
    tool_executor=my_tool_runner,
)

wrapper = AgentUndoWrapper(
    executor=adapter,
    step_parser=adapter.parse_step,
)

State Stores

Store Backend Persistence Use Case
InMemoryStateStore Python dict No Development, testing
SQLiteStateStore SQLite (stdlib) Yes Production, debugging
# SQLite store
wrapper = AgentUndoWrapper(executor=fn, store="sqlite")

# Or bring your own
from agents_driftguard.store.sqlite import SQLiteStateStore
store = SQLiteStateStore(db_path="agent_states.db")
wrapper = AgentUndoWrapper(executor=fn, store=store)

Development

# Install in development mode
pip install -e ".[dev]"

# Run tests
pytest agents_driftguard/tests/ -v

# Format
black agents_driftguard/

# Lint
ruff check agents_driftguard/

Architecture

agents_driftguard/
โ”œโ”€โ”€ __init__.py          # AgentUndoWrapper main API
โ”œโ”€โ”€ core/
โ”‚   โ”œโ”€โ”€ state.py         # AgentState + StateStore protocol
โ”‚   โ”œโ”€โ”€ graph.py         # CausalGraph (NetworkX DAG)
โ”‚   โ”œโ”€โ”€ detector.py      # DivergenceDetector + sub-detectors
โ”‚   โ”œโ”€โ”€ rollback.py      # RollbackController + @reversible
โ”‚   โ””โ”€โ”€ replanner.py     # Failure-context injection
โ”œโ”€โ”€ integrations/
โ”‚   โ”œโ”€โ”€ base.py          # Abstract adapter interface
โ”‚   โ”œโ”€โ”€ langchain.py     # LangChain adapter
โ”‚   โ””โ”€โ”€ openai.py        # OpenAI function-calling adapter
โ”œโ”€โ”€ store/
โ”‚   โ”œโ”€โ”€ memory.py        # In-memory StateStore
โ”‚   โ””โ”€โ”€ sqlite.py        # SQLite StateStore
โ”œโ”€โ”€ utils/
โ”‚   โ”œโ”€โ”€ embeddings.py    # Embedding backends
โ”‚   โ”œโ”€โ”€ visualize.py     # Mermaid graph export
โ”‚   โ””โ”€โ”€ hashing.py       # Deterministic state hashing
โ””โ”€โ”€ tests/
    โ”œโ”€โ”€ test_state.py
    โ”œโ”€โ”€ test_graph.py
    โ”œโ”€โ”€ test_detector.py
    โ”œโ”€โ”€ test_rollback.py
    โ””โ”€โ”€ test_wrapper.py  # End-to-end mock agent test

Research Paper & Citation

If you use this framework in your research, please cite our paper:

@article{driftguard2026,
  title={DriftGuard: A State-Aware Rollback and Trajectory-Replanning Framework for Autonomous LLM Agents},
  author={Sanjoy Kumar},
  journal={Preprint / Working Paper},
  year={2026}
}

License

MIT

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

agents_driftguard-1.1.0.tar.gz (59.0 kB view details)

Uploaded Source

Built Distribution

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

agents_driftguard-1.1.0-py3-none-any.whl (64.9 kB view details)

Uploaded Python 3

File details

Details for the file agents_driftguard-1.1.0.tar.gz.

File metadata

  • Download URL: agents_driftguard-1.1.0.tar.gz
  • Upload date:
  • Size: 59.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for agents_driftguard-1.1.0.tar.gz
Algorithm Hash digest
SHA256 a8cb6d5576b62a7e0e253c9cc2c8dca3d378e065fbe6aad842872759623fa544
MD5 5fb7f500f023e152f827842b315cb507
BLAKE2b-256 6cac2cd5bdbe3dac2010e535e43b01dddf5555dc2c8f5f61041255a7a7c731a7

See more details on using hashes here.

File details

Details for the file agents_driftguard-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agents_driftguard-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 62662b67c4b638d33970fdf13825e4d50a989af837df22ca3416a6876104cb43
MD5 f0ece0897138520da848acaca750d1f3
BLAKE2b-256 a06fa4f9f704acb4836b6f085e2b8d1d0abaa1c04f260701b001d19d24377533

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