Skip to main content

Branch-based decision memory for AI agents — stores choices, alternatives, and regret.

Project description

ForkLedger

CI PyPI Python License Comparison

The memory layer your AI agents actually need.
Not what was said — but what was chosen, what it cost, and what to do differently next time.


The problem with AI memory today

Every major memory system stores what happened: messages, chunks, embeddings, facts.

None of them store why a decision was made — or what it cost.

That means every time your agent faces the same situation, it starts from zero. It can't learn from its own mistakes. It has no way to say: "Last time I chose this, it cost me 3 points. Don't do it again."

ForkLedger fixes this.


What ForkLedger stores

situation before the choice
        ↓
[ branch A ] [ branch B ✓ chosen ] [ branch C ]
                     ↓
               observed outcome
                     ↓
     regret vector — what each path cost relative to the best

Over time, ForkLedger learns which branches produce the lowest regret under similar conditions — and surfaces that knowledge when it matters.


Install

pip install forkledger              # core — zero dependencies
pip install forkledger[mcp]         # + MCP server (Claude Desktop, Cursor, VS Code)
pip install forkledger[api]         # + REST API (FastAPI)
pip install forkledger[embeddings]  # + semantic similarity
pip install forkledger[all]         # everything

60-second demo

from forkledger import ForkLedgerEngine, ForkRecord, Branch, OutcomeEstimate

engine = ForkLedgerEngine("decisions.db", backend="sqlite")

# Record a decision and its outcome
engine.add_record(ForkRecord(
    fork_id   = "trade-001",
    pre_state = {"market": "BTC", "signal": "breakout", "volume": "high"},
    trigger   = "RSI crossed 70 with volume confirmation",
    possible_branches = [Branch("enter"), Branch("wait"), Branch("short")],
    chosen_branch     = "enter",
    realized_value    = 2.4,
    estimated_outcomes = [
        OutcomeEstimate("wait",  estimated_value=0.0),
        OutcomeEstimate("short", estimated_value=-1.5),
    ],
    confidence = 0.8,
    tags = ["crypto", "momentum"],
))

# Update with actual result after the fact
engine.update_outcome("trade-001", realized_value=1.9)

# Ask: given this new situation, what should I do?
recs = engine.recommend(
    current_state = {"market": "ETH", "signal": "breakout", "volume": "high"},
)
# → [{"branch": "enter", "score": 0.82}, ...]

# What patterns have emerged from repeated decisions?
policies = engine.policies()
# → [{"recommended_branch": "enter", "win_rate": {"enter": 0.72}, ...}]

# Win rates across all historical decisions
win_rates = engine.win_rates()

# CFR-style accumulated regret (time-decay weighted)
regret = engine.accumulated_regret()

# Full audit trail with effective weights
trail = engine.audit_trail()

MCP Server — works with Claude Desktop, Cursor, VS Code

pip install forkledger[mcp]

Add to your Claude Desktop config (~/.config/claude/claude_desktop_config.json):

{
  "mcpServers": {
    "forkledger": {
      "command": "forkledger",
      "args": ["mcp", "--store", "/path/to/decisions.db", "--backend", "sqlite"]
    }
  }
}

Your Claude agent can now:

  • record_decision(...) — store a decision with alternatives and outcome
  • get_recommendation(...) — ask what worked in similar past situations
  • update_outcome(...) — update realized value after observing results
  • get_policies(...) — see distilled patterns from repeated decisions
  • list_decisions(...) — browse decision history
  • memory_stats() — store health and statistics

No code required. Works with any MCP-compatible client.


REST API

pip install forkledger[api]
forkledger serve --backend sqlite --store decisions.db
# → http://localhost:8000/docs  (Swagger UI)
Method Path Description
GET /health Health check
GET /stats Store statistics
GET /forks List (filterable by tags, confidence)
POST /forks Add one fork
POST /forks/bulk Bulk add
PATCH /forks/{id}/outcome Update realized value
DELETE /forks/{id} Delete
DELETE /forks/expired Purge expired
POST /recommend Get branch recommendations
POST /rank Rank forks by similarity
GET /policies Distilled policies
GET /win-rates Win rates per branch
GET /audit Decision audit trail
GET /export Export all as JSON

Docker

docker run -p 8000:8000 -v $(pwd)/data:/data \
  ghcr.io/sliper82/forkledger:latest

# or with compose:
docker compose up

Dashboard

pip install forkledger[dashboard]
forkledger dashboard --store decisions.db --backend sqlite
# → http://localhost:8501

Visualize decision history, win rates, accumulated regret, and policies.


CLI

forkledger add decisions.json                           # load from file
forkledger recommend --state '{"signal":"breakout"}'   # get recommendation
forkledger update-outcome trade-001 1.9                 # update outcome
forkledger policies --min-support 3                     # distilled patterns
forkledger win-rates                                    # branch win rates
forkledger accumulated-regret                           # CFR-weighted regret
forkledger audit --limit 20                             # decision trail
forkledger stats                                        # store health
forkledger serve --backend sqlite --port 8000           # REST API
forkledger mcp --backend sqlite                         # MCP server

What's unique about ForkLedger

Concept Description
Regret vector Computed automatically. regret(branch) = best_value − branch_value. Zero = best choice.
Fuzzy policy clustering Groups similar (not just identical) states. Real-world agent states are never perfectly equal.
Confidence decay Exponential time-weighting. Old evidence fades. Configurable half-life.
CFR-style accumulation Weighted regret accumulates across history per branch, similar to Counterfactual Regret Minimization.
Branch win rates How often each branch was the best available choice.
Outcome update Update realized values after observing results. Regret is recomputed automatically.
Zero dependencies Core library requires nothing. SQLite is stdlib. All extras are opt-in.

How it compares

→ See COMPARISON.md for a full side-by-side with Mem0, Hindsight, LangMem, and mcp-memory.

Short version: ForkLedger does not compete with those systems — it complements them. They store facts. ForkLedger stores decisions.


Architecture

ForkLedgerEngine
├── storage/
│   ├── JsonForkStore       — flat file, zero deps
│   ├── SqliteForkStore     — indexed, atomic, production
│   └── PostgresForkStore   — cloud-native, pgvector
├── counterfactual.py       — regret + decay + CFR accumulation
├── retrieval.py            — scoring + optional embeddings
├── policy.py               — fuzzy + exact policy distillation
├── mcp_server.py           — MCP server (8 tools)
├── api.py                  — FastAPI REST (14 endpoints)
├── cli.py                  — CLI (18 commands)
└── dashboard/app.py        — Streamlit UI

Retrieval scoring:

Signal Weight
State similarity 45%
Constraint match 20%
Recency 15%
Confidence 10%
Regret salience 10%

Benchmark results

ForkLedger Benchmark — 300 records, 80 queries
Load: 2.19s (137 rec/s)
Latency: p50=5.6ms  p95=25.9ms
Precision@1: 100.0%
Policy accuracy: 4/4 = 100.0%

Run yourself:

python benchmarks/locomo_benchmark.py --records 500 --queries 100

Integrations

LangChain

pip install forkledger[langchain]
from forkledger.integrations.langchain import ForkLedgerMemory

memory = ForkLedgerMemory(
    store_path="decisions.db",
    backend="sqlite",
    agent_id="my-agent",
    domain="research",   # pre-tuned scoring weights
)

# Record decisions manually
memory.record_decision(
    fork_id="dec-001",
    situation={"task": "research", "signal": "mixed"},
    trigger="Conflicting sources",
    chosen="verify",
    alternatives=["fast-publish", "wait"],
    outcome=1.8,
)

# Or use as drop-in with ConversationChain
from langchain.chains import ConversationChain
chain = ConversationChain(llm=llm, memory=memory)

AutoGen

pip install forkledger[autogen]
from autogen import AssistantAgent
from forkledger.integrations.autogen import ForkLedgerHook

hook = ForkLedgerHook(
    store_path="decisions.db",
    backend="sqlite",
    agent_id="assistant",
    domain="code",
)

# Attach to any AutoGen agent
assistant = AssistantAgent("assistant", llm_config=llm_config)
hook.attach(assistant)   # injects decision context into every message

# Or use standalone
hook.record(
    situation={"task": "web_research", "sources": "multiple"},
    trigger="Conflicting data",
    chosen="verify_cross_reference",
    alternatives=["use_first", "skip"],
    outcome=2.1,
)
recs = hook.recommend({"task": "web_research", "sources": "multiple"})

Roadmap

  • SQLite + JSON backends
  • REST API (FastAPI)
  • MCP server (Claude Desktop, Cursor, VS Code)
  • Fuzzy policy clustering
  • Confidence decay + CFR-style accumulation
  • Branch win rates + audit trail
  • PostgreSQL + pgvector backend
  • Streamlit dashboard
  • Benchmark suite
  • LangChain official memory adapter (ForkLedgerMemory)
  • AutoGen integration (ForkLedgerHook, ForkLedgerGroupChatManager)
  • PyPI release — pip install forkledger

Contributing

git clone https://github.com/sliper82/forkledger
cd forkledger
pip install -e ".[dev]"
pytest
python benchmarks/locomo_benchmark.py

Issues, PRs, and ideas welcome. See CONTRIBUTING.md.


License

Apache 2.0

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

forkledger-0.4.1.tar.gz (46.7 kB view details)

Uploaded Source

Built Distribution

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

forkledger-0.4.1-py3-none-any.whl (46.7 kB view details)

Uploaded Python 3

File details

Details for the file forkledger-0.4.1.tar.gz.

File metadata

  • Download URL: forkledger-0.4.1.tar.gz
  • Upload date:
  • Size: 46.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for forkledger-0.4.1.tar.gz
Algorithm Hash digest
SHA256 fd0adc81693b93b4d6fb344218e283d719fbf7355ce7225b30479ac2c8ca7ce4
MD5 0febd0ede69037bc2329e635ed3d1873
BLAKE2b-256 0b4e77e145f763d391ef8768480c7c8736e5c40813095717988e0ef47fa0ca6b

See more details on using hashes here.

File details

Details for the file forkledger-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: forkledger-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 46.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for forkledger-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6b11d1f2ff5184692914e58ee6a1e00f666d4d3af1a94024404bf614c2a6baeb
MD5 a8d66785bd4b03395c44d97c506023f5
BLAKE2b-256 c0e591f8572b866e690a0873011b5c3edc094272ef59df368ca2a1d23e660782

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