Agentic Context Engine (ACE)

repository·main·Indexed 25 days ago

https://github.com/kayba-ai/agentic-context-engine

An open-source engine and framework (ace-framework v0.12.0) designed to add a persistent learning loop to AI agents. ACE allows agents to learn from mistakes and feedback by extracting strategies into a 'Skillbook' to prevent repetitive errors across sessions. It includes an Offline ACE adapter for processing past traces, a benchmark suite for evaluating performance across domains like finance and mathematics, and integration capabilities for OpenClaw via Docker.

Tokens
121.7K
Snippets
298
Records
554
Agent score
81%

What's inside Agentic Context Engine

  1. Overview of ACE Architecture

    main

    ACE (Agentic Context Engine) is a pipeline-based framework designed for building self-improving AI agents. It enables agents to learn from their own executions through a continuous loop of execution, reflection, and skill updating.

    Core Components

    • Pipeline Engine: Handles composition, concurrency, and typed step contracts using requires/provides logic.
    • Roles: Business logic implementations (e.g., Agent, Reflector, SkillManager) backed by PydanticAI agents for structured LLM interactions.
    • Skillbook: An evolving knowledge base of strategies that agents read from and learning loops write to.
    • Steps: Context plumbing that extracts data, calls a role, and returns results to the pipeline.
    • Runners: Orchestration components that compose steps into pipelines and manage iterations (e.g., ACERunner).
    • Observability: Integrated via Logfire auto-instrumentation of PydanticAI agent calls.
  2. Overview of ACE benchmark scripts

    main

    The scripts/ directory contains the following tools for evaluating and analyzing the Agentic Context Engine:

    • run_benchmark.py: A CLI tool to execute ACE benchmarks using train/test splits.
    • analyze_ace_results.py: Used to analyze the output of benchmark runs.
    • explain_ace_performance.py: Generates explanations for observed ACE performance patterns.
  3. Available ACE Integrations and Runners

    main

    ACE provides various runners and step-based integrations for different agentic frameworks. Integrations are categorized by their input type and the level of insight they provide (Micro, Meso, or Cloud).

    RunnerFrameworkInputInsight Level
    ACELiteLLMLiteLLM (100+ providers)QuestionsMicro
    LangChainLangChain RunnablesChain inputsMeso
    BrowserUsebrowser-useTask stringsMeso
    ClaudeCodeClaude Code CLITask stringsMeso
    Claude SDKAnthropic Python SDKTask strings or ACESampleMeso
    OpenClawOpenClaw transcriptsJSONL trace filesMeso
    MCP ServerMCP (stdio)Tool callsMicro
    MCP Client SetupClaude Code, Cursor, WindsurfSetup Guide
    OpikOpik observabilityMonitoring
    TracingKayba tracing SDK@trace / start_spanCloud
    Hosted APIKayba hosted APITrace filesCloud
  4. Core components kept from the legacy implementation

    main

    While much of the LLM plumbing was migrated to PydanticAI, the following core ACE components remain unchanged and are central to the engine's functionality:

    • Pipeline engine (pipeline/): Manages requires/provides contracts, async_boundary, per-step max_workers, and SampleResult error isolation.
    • Skillbook & learning loop: The core logic of Reflect → Update → Apply → Deduplicate.
    • Step composition: Includes learning_tail(), pipeline-as-step nesting, and the SkillbookView read/write split.
    • Domain-specific prompts: Specialized prompts tightly coupled to the skillbook format and ACE's reflection strategy.
    • Pipeline protocols: All pipeline steps depend on protocols rather than specific implementations.
  5. What is the Skillbook in ACE?

    main

    The Skillbook is the ACE knowledge store, acting as a structured collection of learned issues and insights. It stores skills, which are individual entries representing a problem (issue) and a recommended action (insight).

    Skills are categorized into two distinct sections:

    • context: Learnings the agent should apply while solving a task.
    • harness: Environment or runtime learnings that affect the pipeline itself.

    Each skill includes metadata such as a unique id, keywords (for topic labeling), an active status, effectiveness counters (helpful_count, harmful_count, neutral_count), and occurrences for provenance tracing.

    {
      "id": "context-00001",
      "section": "context",
      "keywords": ["math", "decomposition"],
      "issue": "Complex arithmetic questions are easier to solve when the work is decomposed into smaller verified steps.",
      "insight": "Break the problem into smaller steps before computing.",
      "active": true,
      "helpful_count": 5,
      "harmful_count": 0,
      "neutral_count": 1
    }
  6. What is the Pipeline Engine?

    main

    The Pipeline Engine is a lightweight, domain-agnostic framework for composing processing steps into pipelines. It uses three core primitives to handle all composition patterns:

    1. Sequential: Steps run one after another.
    2. Branch: Forks the data to run multiple pipelines in parallel, then joins them.
    3. Nesting: A Pipeline can be used as a single step within another pipeline.

    Key features include:

    • Contract Validation: Steps declare requires and provides fields. The pipeline validates that data dependencies are met at construction time, preventing runtime wiring errors.
    • Immutable Context: Steps receive a frozen StepContext and must return a new one via .replace(), ensuring thread safety.
    • Per-sample Error Isolation: A failure in one sample does not block others; every sample produces a SampleResult.
    • Observation Hooks: Use PipelineHook to observe step transitions (e.g., for logging or metrics) without altering data flow.
  7. What is a Branch and how does it work?

    main

    A Branch is a specialized step that implements StepProtocol, allowing you to run multiple pipelines in parallel on the same input. It forks the context to all child pipelines, executes them concurrently, and then implicitly joins (merges) their outputs before the next step in the main pipeline begins.

    Key characteristics:

    • Parallelism: Branches run in parallel using a ThreadPoolExecutor (sync) or asyncio.gather (async).
    • Immutability: All branches receive the same frozen context. Since StepContext is immutable, branches cannot corrupt each other's input.
    • Contract Inference: A Branch automatically computes its requires and provides by taking the union of its children's requirements and provisions. This allows for seamless nesting of branches within pipelines.
    graph LR
        T[Tokenize] --> U[Uppercase]
        T --> R[Reverse]
        U --> S[Summarize]
        R --> S
  8. How ACE MCP sessions and safety work

    main

    Session Isolation

    Every tool call is scoped to a session_id. The server lazily initializes session runners (defaulting to ACELiteLLM) and manages their lifecycle using a configurable TTL (session_ttl_seconds).

    Safety Modes

    The server provides two layers of protection for non-production or read-only environments:

    1. Safe Mode (ACE_MCP_SAFE_MODE=true): Blocks all mutating operations, including learning and skillbook persistence. Attempting these will return ACE_MCP_FORBIDDEN_IN_SAFE_MODE.
    2. Save/Load Restriction (ACE_MCP_ALLOW_SAVE_LOAD=false): Specifically disables ace.skillbook.save and ace.skillbook.load. Attempting these will return ACE_MCP_SAVE_LOAD_DISABLED.

    Path Security

    When using ace.skillbook.save or ace.skillbook.load, the server resolves the provided path to an absolute canonical path. If ACE_MCP_SKILLBOOK_ROOT is set, the server will reject any paths that resolve to locations outside of that root directory.

  9. Perform incremental or full reprocessing of sessions

    main

    The ACE learning process supports incremental processing to save LLM API credits and computation time.

    • Incremental Mode (Default): ACE tracks which sessions have already been handled in a processed session log. On subsequent runs, it only processes new sessions.
    • Full Reprocess: If you need to re-evaluate all historical data (e.g., after resetting your skillbook), you can trigger a full reprocess to ignore the session log and process all available sessions.
  10. Define a Pipeline Step

    main

    A step in the ACE pipeline is a class that must implement three specific attributes and a __call__ method:

    1. requires: A frozenset of keys that must exist in the StepContext.metadata before this step runs.
    2. provides: A frozenset of keys that this step will add to the StepContext.metadata.
    3. __call__(self, ctx: StepContext) -> StepContext: The execution logic. It should return a new StepContext (typically using ctx.replace(metadata=...)) containing the updated metadata.

    Note: Use types.MappingProxyType when updating metadata to ensure immutability/correctness where required.

    from types import MappingProxyType
    from pipeline import StepContext
    
    class Tokenize:
        """Split text into tokens and count words."""
        requires = frozenset()
        provides = frozenset({"tokens", "word_count"})
    
        def __call__(self, ctx: StepContext) -> StepContext:
            tokens = str(ctx.sample).split()
            return ctx.replace(
                metadata=MappingProxyType({
                    **ctx.metadata,
                    "tokens": tokens,
                    "word_count": len(tokens),
                })
            )
  11. Understand context compaction tiers

    main

    To prevent context window exhaustion, the system employs a two-tier compaction strategy when PydanticAI: UsageLimitExceeded is triggered.

    Tier 1: microcompact

    • Action: Clears old execute_code tool results but keeps the last 3 tool results intact.
    • Retention: Keeps all model messages (the reasoning chain).
    • Flow: If the context size is still too large after microcompaction, the system falls through to Tier 2.

    Tier 2: summarize_and_compact

    • Action: Uses the LLM to summarize the progress (consumes 1 request from the budget).
    • Context Preservation: Saves the pre-compaction context to the sandbox history variable.
    • Replacement: Replaces the history with a [summary + continuation prompt].
    • Limit: Compaction is capped at max_compactions=3.

    Compaction Callback

    RecursiveAgent.on_compaction() is used to save compaction metadata to the sandbox's history variable, allowing the agent to reference prior context even after it has been summarized.

  12. How StepContext and the .replace() pattern work

    main

    StepContext is an immutable, frozen dataclass that carries data (sample and metadata) between steps.

    Immutability & The .replace() Pattern Steps must never mutate the incoming context. Instead, they must use the .replace() method to return a new instance of the context with updated values. This ensures thread safety, branch safety, and a clear trace of data transformations.

    Metadata vs. Named Fields

    • metadata: Use a MappingProxyType (automatically coerced if you pass a plain dict) for transient, step-specific, or integration-specific data (e.g., metadata["debug_log"]).
    • Named Fields: Subclass StepContext to add named, type-checkable fields for data shared across multiple steps in a pipeline (e.g., predictions, scores).
    @dataclass(frozen=True)
    class MLContext(StepContext):
        # Shared configuration
        model_config: dict | None = None
    
        # Produced by steps (None until the providing step runs)
        predictions: list | None = None
        scores: dict | None = None
        report: str | None = None