Skip to content

Repository files navigation

LearnKit

Procedural memory for tool-using AI agents. Capture the tool procedure that solved a task, replay it on exact repeats with zero planning calls, and guide similar tasks with a learned playbook.

PyPI License: Apache 2.0 Python 3.11+ Ask DeepWiki

v1.0 Β· pip install learnkit-ai Β· wrap one function Β· watch it stop re-planning.


Stop Re-Planning the Same Task

LearnKit is an agent-agnostic SDK that makes tool-using AI agents learn from experience. It captures the tool-call procedure an agent uses to solve a task, then:

  • replays it on exact repeats with zero planning/LLM calls, and
  • guides sibling tasks with a distilled playbook so the model follows the proven shape instead of re-exploring.

The expensive, compressible part of a tool-using agent isn't the answer text β€” it's the planning loop (the back-and-forth LLM calls to decide which tools to call in what order). That plan is normally thrown away after every task. LearnKit keeps it, quality-gates it on the real tool outcome (not a fragile LLM judge), and reuses it.

Reproducible result (agentic matrix, 3/3 models PASS, equal task success):

+2.25 best quality lift Β· βˆ’38.4% pooled LLM planning calls on Qwen2.5-14B/32B and Llama-3.3-70B.

Memory is typed, quality-gated, attributed (help/harm/reuse per record), and lifecycle-managed (confidence decay, quarantine β†’ promote, TTL) β€” auditable, deletable JSON, no model retraining.


Where this fits β€” procedural memory, not caching or hand-written skills

LearnKit adds procedural memory (how to do a task) β€” distinct from the semantic/episodic memory that Mem0, Zep, and vector stores provide (facts and past conversations).

  • Not plan caching. Plan caching keys a whole plan and trades accuracy for cost. LearnKit does workflow induction: hard-replay only on an exact match (zero LLM), and guide sibling tasks (the model still plans) β€” never a fuzzy replay that could silently produce the wrong result. Success holds; it doesn't degrade.
  • Not hand-written Skills. LangChain / Deep Agents Skills are procedural memory too, but you author and maintain each one by hand. LearnKit auto-induces procedures from real successful runs, quality-gates them on the tool outcome, tracks help/harm, and decays the stale ones β€” and it's framework-agnostic. It even exports a Deep Agents-compatible library: learnkit skills export --format deepagents.

Architecture at a glance

flowchart LR
    T[Task] --> W["@memory.agent_learn"]
    W --> R["classify β†’ retrieve β†’ compose"]
    R --> M{"procedure match?"}
    M -->|exact| RP["replay_plan<br/>0 planning calls"]
    M -->|sibling| G["guided ReAct<br/>follows playbook"]
    M -->|none| C["cold ReAct<br/>explore + capture"]
    RP --> O["tool-success gate"]
    G --> O
    C --> O
    O -->|pass| D["distill procedure β†’ SkillRecord"]
    O -->|fail| X["demote"]
    D --> S[("SQLite: records + runs")]
    X --> S
    S -->|next encounter| R
    S --> DASH["FastAPI β†’ React dashboard<br/>calls-reduced Β· replays Β· traces"]
Loading

The wrapped agent function never changes β€” the @memory.agent_learn decorator orchestrates everything around it. Full, detailed diagrams live in architecture/; see How it works for the step-by-step.

🎨 A colored, presentation-ready version is in architecture/high_level_architecture.mmd β€” render it at mermaid.live and export a PNG.


To install from PyPI (recommended):

pip install learnkit-ai
# Or with integration extras:
pip install "learnkit-ai[langchain]"

To install from local repo root:

pip install -e .                    # core SDK
pip install -e ".[langchain]"       # adds LangChain + langchain-anthropic
pip install -e ".[dev]"             # pytest + pytest-asyncio

Other optional extras: mem0, zep, qdrant.

Set your Anthropic key (optional β€” only the LLM classifier/distiller use it; the agent path and benchmarks run keyless). PowerShell, persists across sessions:

[Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY", "sk-ant-...", "User")

On bash/zsh: export ANTHROPIC_API_KEY=sk-ant-... in your shell rc.


60-second Quick Start

python examples/agent_learn_demo.py

A runnable, offline (no API key) demo of the cold→warm capture-and-replay loop: on first exposure the agent explores and LearnKit captures the productive tool procedure; on repeats it replays that procedure with zero planning calls.


Wrap your agent β€” the agent path (@memory.agent_learn)

import learnkit as lk

memory = lk.LearnKit(memory_backend="sqlite", scope="team")

@memory.agent_learn(domain="pipeline")
def my_tool_agent(task: str, _learnkit_context: str = "", _learnkit_tools=None) -> str:
    # Record every tool call so LearnKit can learn / replay the productive procedure.
    rows = _learnkit_tools.record("query", {"table": "users"}, run_query("users"))
    _learnkit_tools.record("format", {"fmt": "csv"}, to_csv(rows))
    return "report ready"

# Same task, called twice β€” run 2 replays run 1's captured procedure (zero planning calls).
my_tool_agent("Build an active-user CSV report")
my_tool_agent("Build an active-user CSV report")

Valid scope values: "user", "team", "public" (see learnkit/schemas/base.py).

Auto-replay β€” zero-wiring reuse (run_react_agent)

Don't want to hand-check has_plan/plan_steps? Use the built-in ReAct runner. It prepares the run, retrieves any matching procedure, auto-replays exact matches with zero planning calls, and otherwise drives your planner while capturing the trajectory:

from learnkit import run_react_agent, LLMStep, ToolCall

def planner(task, context, history):
    # One planning turn. Return tool calls to make, and/or a final answer.
    # `context` already contains playbook guidance for sibling tasks.
    ...
    return LLMStep(tool_calls=[ToolCall("query", {"table": "users"})], final=None)

result = run_react_agent(memory, task, tools, planner, exploration_tools={"list_tables"})
print(result.replayed, result.llm_calls, result.tool_calls)  # True 0 3  on an exact repeat

This path supports exact replay (zero-LLM for exact re-encounters) and guided sibling reuse. A runnable, offline demo (no API key) lives at examples/agent_learn_demo.py. See benchmarks/injection_ablation.py for a quality-focused ablation that isolates the effect of playbook injection on novel sibling tasks.


Integrate with LangChain (and others)

LangChain, LangGraph, AutoGen, CrewAI, LlamaIndex, and the OpenAI Agents SDK are all supported through the universal adapter contract described in Framework integrations below. Each adapter captures tool calls onto the run so the agent path learns and replays procedures with no change to your agent logic.


Framework integrations

LearnKit is framework-agnostic. Every integration subclasses one universal contract (learnkit.adapters.BaseAdapter), so they all expose the same agent-path API β€” start_run β†’ inject memory + arm the ToolTracker, complete_run β†’ capture and distill the procedure β€” and an exception-safe session() / asession() lifecycle.

Framework Adapter Install Native hook
LangChain LangChainAdapter learnkit-ai[langchain] LearnKitCallbackHandler (BaseCallbackHandler)
LangGraph LangGraphAdapter learnkit-ai[langgraph] as_node() graph node
AutoGen / AG2 AutoGenAdapter learnkit-ai[autogen] inject() system-message + ChatResult capture
CrewAI CrewAIAdapter learnkit-ai[crewai] step_callback() (AgentAction)
LlamaIndex LlamaIndexAdapter learnkit-ai[llamaindex] LearnKitLlamaHandler (FUNCTION_CALL events)
OpenAI Agents SDK OpenAIAgentsAdapter learnkit-ai[openai-agents] LearnKitRunHooks (RunHooks)
Raw OpenAI / Anthropic OpenAIRawAdapter core wraps the chat call
from learnkit import LearnKit
from learnkit.adapters import get_adapter

lk = LearnKit(memory_backend="sqlite")
adapter = get_adapter("crewai")(lk)        # resolve any adapter by name

with adapter.session(task) as run:          # exception-safe lifecycle
    tools = adapter.wrap_tools(run, tools)  # agent path: capture tool calls
    run.response = my_agent(run.context, tools)

Any third-party package can register its own adapter without a PR β€” declare a learnkit.adapters entry point and LearnKit discovers it lazily via get_adapter(name) / available_adapters().

Offline mode (no API key)

When no LLM provider key is set, LearnKit runs keyless: task classification falls back to a deterministic heuristic instead of a network call, so the agent path and the deterministic benchmarks run with zero credentials and zero latency. Force it explicitly with LEARNKIT_OFFLINE=1; it is auto-detected otherwise (no provider key and no LEARNKIT_CLASSIFIER_MODEL override).


How it works β€” the 8-step loop

The agent function never changes. The decorator orchestrates everything around it.

  1. User calls your wrapped agent with a task.
  2. Classify β€” TaskClassifier returns a domain vector, e.g. {"Python": 0.9, "Concurrency": 0.7}.
  3. Retrieve β€” SemanticRetriever pulls relevant records (FTS5 lexical + optional dense rerank), filtered by domain and scope.
  4. Compose β€” compose_context formats records into a bounded prompt block (≀ 8 records, ≀ 1,200 tokens, inference mode = PRESCRIPTIVE / GUIDED / EXPLORATORY based on top-record confidence).
  5. Run β€” your function executes with _learnkit_context injected as a kwarg.
  6. Evaluate β€” on the agent path the outcome is gated on the real tool success (ToolTracker.outcome_score()), not a fragile LLM judge; an optional judge scores 0–5 when no tool signal is available.
  7. Distill β€” a passing run captures the cleaned productive tool sequence into a procedural SkillRecord (procedure / tool_sequence / trigger) for replay, plus optional facts/failures. Below threshold, a FailureRecord is stored directly so future runs avoid the same path.
  8. Persist β€” records are written via the active backend; the trajectory is registered against a per-run ID for inspection.

Memory model

LearnKit stores seven typed record kinds (learnkit/schemas/):

Record Activates as Notes
SkillRecord quarantine Promoted to active after the configured probation window
FactRecord quarantine Same probation as skills
FailureRecord active immediately Per ReaComp β€” agents must avoid known dead ends as fast as possible
StrategyRecord quarantine Higher-level approaches
PreferenceRecord quarantine User / team preferences
TraceRecord active Raw execution trace for replay
HeuristicRecord quarantine Domain heuristics

Bounded memory is enforced at retrieval: the router caps results at 8 records / ~1,200 tokens before the composer formats them.


Maintenance

Call memory.maintain_memory() periodically (cron, background job, etc.):

memory.maintain_memory(weeks=1, decay_rate=0.02, quarantine_hours=24)
# β†’ {"decayed": N, "stale": M, "promoted": K}
  • Decay: every active/stale record loses decay_rate confidence per weeks elapsed.
  • Stale: records past expires_at get marked stale and excluded from retrieval.
  • Promote: quarantined records older than quarantine_hours are promoted to active.

Project structure

learnkit/
β”œβ”€ core.py            # orchestrator: @memory.agent_learn, prepare_run β†’ finalize_run β†’ post-process
β”œβ”€ tool_tracker.py    # ToolTracker β€” captures tool calls, tool-success gate, plan attach
β”œβ”€ procedural.py      # extract_procedure, task-signature / coverage matching
β”œβ”€ replay.py          # replay_plan β€” execute a captured procedure with 0 LLM calls
β”œβ”€ adapters/react.py  # run_react_agent β€” auto-replay ReAct runner
β”œβ”€ drift.py           # check_drift + golden-suite export (procedure regression)
β”œβ”€ retriever.py       # hybrid FTS5 + dense retrieval
β”œβ”€ router.py          # bounded retrieval plan (≀ 8 records / ~1,200 tokens)
β”œβ”€ composer.py        # compose_context β€” the injected prompt block
β”œβ”€ distiller.py       # MemoryDistiller (DSPy) β€” distills records
β”œβ”€ memory_quality.py  # storage gates, confidence, help/harm utility
β”œβ”€ schemas/           # 7 typed record kinds (SkillRecord, FailureRecord, …)
β”œβ”€ backends/          # SQLite (default) + Qdrant / Mem0 / Zep + registry
β”œβ”€ adapters/          # LangChain, LangGraph, CrewAI, AutoGen, LlamaIndex, OpenAI
└─ cli.py             # `learnkit maintain`, `learnkit skills export`

architecture/          # Mermaid diagrams (product map Β· agent runtime Β· storage Β· benchmark)
benchmarks/            # agentic suite + reproducible RESULTS.json / MATRIX.md
Docs/                  # FastAPI server + React observability dashboard
examples/              # runnable demos (agent_learn_demo.py β€” offline, no key)
tests/                 # pytest suite

Contributing

Contributions are welcome β€” see CONTRIBUTING.md for the full guide (setup, structure, PR checklist, design principles).

git clone https://github.com/siddhu1716/LearnKit && cd LearnKit
python -m venv .venv && . .venv/Scripts/Activate.ps1   # bash: source .venv/bin/activate
pip install -e ".[dev]"
pytest -q                     # run the test suite (offline, no key)
ruff check learnkit tests     # lint
pre-commit install            # enable commit hooks

The observability dashboard (Docs/dashboard) builds with npm install && npm run build.


Architecture docs

All diagrams live in architecture/:

Diagram Shows
high_level_architecture.mmd colored, at-a-glance overview (screenshot-ready)
product_architecture.mmd the whole system, subsystem by subsystem
agent_runtime_flow.mmd one task: capture β†’ match β†’ replay/guide β†’ gate β†’ persist
storage_lifecycle.mmd a record's lifecycle (quarantine β†’ active β†’ decay)
benchmark_flow.mmd how the reproducible benchmark numbers are produced

Benchmarks & status

v1.0. The agent path (@memory.agent_learn) is the product: procedure capture, exact-match replay (zero planning calls), and guided sibling reuse, over a SQLite + FTS5 store with a React observability dashboard.

Reproduce the published numbers in one command (offline, from committed runs):

python -m benchmarks.make_results     # regenerates benchmarks/RESULTS.json + MATRIX.md

Agentic matrix (trials=1, k=1, seed=7, temperature=0) β€” 3/3 models PASS the playbook_effect β‰₯ 0.5 gate at equal task success:

Model Gate Quality lift LLM calls (pooled)
Qwen2.5-14B-Instruct βœ“ PASS +1.875 53 β†’ 30 (βˆ’43%)
Qwen2.5-32B-Instruct βœ“ PASS +1.75 44 β†’ 28 (βˆ’36%)
Llama-3.3-70B-Instruct βœ“ PASS +2.25 88 β†’ 56 (βˆ’36%)

Headline: +2.25 best quality lift Β· βˆ’38.4% pooled LLM planning calls Β· equal success. See benchmarks/README.md to run against your own endpoints.


License

Apache-2.0 β€” see LICENSE and NOTICE. Β© 2026 LIA Labs.

Apache-2.0 is a permissive open-source license: commercial use is allowed. It adds an explicit patent grant and attribution/NOTICE requirements over MIT.

Releases

Packages

Contributors

Languages