Learn Claude Code: Harness Engineering for AI Agents

repository·main·Indexed 32 days ago

https://github.com/shareai-lab/learn-claude-code

A comprehensive tutorial repository teaching 'Harness Engineering'—the art of building the infrastructure (tools, context, permissions, and environments) that allows LLMs to function as autonomous agents. Includes a 20-lesson curriculum covering agent loops, tool use, subagents, memory systems, task management, and MCP plugins.

Tokens
87.8K
Snippets
140
Records
307
Agent score
96%

What's inside shareai-lab-learn-claude-code

  1. Understand the Memory System architecture

    main

    The Memory system provides a persistent, cross-session storage layer for LLM agents to prevent information loss during context compression (autoCompact). While session memory manages continuity within a single conversation, the Memory system stores long-term knowledge that survives new sessions.

    Memory Types

    TypePurposeExample
    userPersonal preferences"Use tabs instead of spaces"
    feedbackHow to perform tasks"Don't mock the database"
    projectCurrent project state/context"Auth rewrite is compliance-driven"
    referenceWhere to find information"Pipeline bugs are in Linear INGEST"

    Architecture Components

    • Storage: Markdown files in a .memory/ directory, each with YAML frontmatter (name, description, type).
    • Index: A MEMORY.md file acting as a catalog for the SYSTEM prompt.
    • Loading: Uses a two-path approach: a permanent index in the SYSTEM prompt and on-demand injection of relevant file contents via LLM side-queries.
    • Extraction: A 'forked agent' extracts new memories from dialogue at the end of a turn when the model stops without using tools.
    • Consolidation (Dream): A periodic process that deduplicates, merges, and removes outdated memories when a threshold (e.g., 10 files) is reached.
  2. Understand the Comprehensive Agent Loop Architecture

    main

    The Comprehensive Agent (s20) is a complete harness that wraps a standard LLM loop with multiple layers of context, permission, and execution management.

    The Core Loop Structure:

    while True:
        response = LLM(messages, tools)
        if not has_tool_use(response.content):
            return
        results = execute_tools(response.content)
        messages.append(tool_results)

    The Harness Layers (Execution Flow):

    1. Input Phase: UserPromptSubmit hooks record/audit input; cron and background notifications are injected into messages.
    2. Context Phase: The compaction pipeline compresses large outputs and history; memory, skills, and MCP state are assembled into the system prompt.
    3. LLM Phase: The model is called with error recovery (retries for 429/529, max_tokens upgrades, or reactive compaction for long prompts).
    4. Tool Phase:
      • Pre-Execution: PreToolUse hooks handle permissions and auditing.
      • Dispatch: assemble_tool_pool() combines builtin tools with dynamic MCP tools.
      • Execution: Slow operations are dispatched to background threads; the loop returns a placeholder tool_result immediately.
      • Post-Execution: PostToolUse hooks handle logging and alerts.
    5. Termination: Stop hooks handle statistics and cleanup.
  3. Understand the Comprehensive Agent Architecture (s20)

    main

    The s20 Comprehensive Agent integrates all previously taught mechanisms into a single continuous loop. The architecture follows a pattern where various components are injected at specific lifecycle stages of the agent loop.

    Agent Loop Lifecycle

    1. User Input: Processed via UserPromptSubmit hooks.
    2. Pre-LLM Phase:
      • Cron/Background: Injects scheduled prompts or <task_notification> messages.
      • Compaction: Trims history and summarizes old tool results to manage context.
      • System Prompt Assembly: Combines identity, workspace, skills catalog, .memory/MEMORY.md, and MCP state.
    3. LLM Call: Wrapped in error recovery (retries for 429/529, handling max_tokens, and reactive compaction).
    4. Tool Execution Phase:
      • Pre-Tool: PreToolUse hooks handle permissions and auditing.
      • Dispatch: assemble_tool_pool() combines built-in tools with dynamic MCP tools.
      • Execution: Slow tasks are dispatched to background threads; MCP tools use mcp__{server}__{tool} naming.
      • Post-Tool: PostToolUse hooks handle logging and post-processing.
    5. Loop Continuation: Tool results are appended to messages, and the loop repeats if a tool_use block is present. If no tool use is detected, Stop hooks run for cleanup and exit.
  4. Understand the User vs. Session Memory architecture

    main

    The memory system distinguishes between two types of memory to balance long-term knowledge with immediate context needs:

    • User Memory: Persistent across sessions. It is stored as multiple .md files under the memory/ directory and is loaded into the system prompt. It is used for accumulating knowledge over time.
    • Session Memory: Limited to a single session. It is stored in session-memory/<id>/memory.md and is loaded into the compact summary. It provides context continuity across different context compression (compacting) events.

    Note: The sessionMemoryCompact mechanism (from s08) uses Session Memory to avoid unnecessary LLM calls. If the session memory file is sufficiently large (e.g., $\ge$ 10K tokens, $\ge$ 5 text messages, and $\le$ 40K tokens), it is used directly for summarization.

  5. Understand the Memory System architecture

    main

    The memory system is designed to prevent information loss during context compression by maintaining multiple layers of persistence. It distinguishes between three types of information: memory (long-term knowledge), plan (current objectives), and tasks (specific actionable items).

    Memory Types

    TypePersistenceStorage LocationInjection MethodPurpose
    User MemoryAcross sessionsMultiple .md files in memory/System promptLong-term knowledge accumulation
    Session MemorySingle sessionsession-memory/<id>/memory.mdCompact summaryContinuity beyond context compression

    Storage and Format

    Memories are stored as Markdown files with YAML frontmatter. The system supports four categories:

    • user
    • feedback
    • project
    • reference

    Files are stored in ~/.claude/projects/<sanitized-git-root>/memory/.

  6. Understand the Agent Pattern and Harness Engineering

    main

    This project teaches the concept of Harness Engineering for building AI agents. The core philosophy is that Agency (perceiving, reasoning, and acting) comes from the model, while the Harness provides the environment for that model to operate.

    An agent product is defined as: Agent = Model + Harness.

    A complete Harness consists of:

    • Tools: File I/O, shell, network, database, browser.
    • Knowledge: Product docs, domain references, API specs, style guides.
    • Observation: Git diffs, error logs, browser state, sensor data.
    • Action Interfaces: CLI commands, API calls, UI interactions.
    • Permissions: Sandbox isolation, approval workflows, trust boundaries.

    Instead of building complex procedural logic (prompt-chaining or node graphs), focus on building the environment that allows the model to use tools and manage context effectively.

  7. Understand the Philosophy of Agent Harness Engineering

    main

    In this project, an Agent is defined as the trained model (the intelligence), while the code you write is the Harness (the environment).

    The Harness Components

    A complete harness consists of five key elements:

    • Tools: Atomic actions the agent can take (e.g., file I/O, shell execution, API calls). Design them to be atomic, composable, and well-described.
    • Knowledge: Domain-specific expertise (e.g., documentation, style guides). Use progressive disclosure by injecting knowledge via tool_result on-demand rather than bloating the system prompt.
    • Context: The agent's memory. Protect context by isolating noisy subtasks, compressing long histories, and persisting goals.
    • Permissions: The agent's boundaries (e.g., sandboxed file access, read-only subagents). Constraints should focus behavior rather than micromanage it.
    • Task-Process Data: The traces of perception-reasoning-action sequences used as training signals for future models.

    The Universal Loop

    All effective agents follow this core architectural pattern:

    1. Model sees: Conversation history + available tools.
    2. Model decides: Act or respond.
    3. If act: Execute tool, add result to context, and continue the loop.
    4. If respond: Return the answer and end the loop.

    Core Principles

    • Trust the Model: Avoid building elaborate decision trees or hardcoded workflows. Give the model tools and knowledge, and let it reason through the execution.
    • Constraints Enable: Use constraints (like "one task in progress at a time") to prevent the model from getting lost or overwhelmed.
    • Progressive Complexity: Start with a simple model and one tool (Level 0) and only add complexity (planning, subagents, persistence, teams) as real-world usage requires.
  8. Understand the Agent Harness Engineering concept

    main

    This project is based on the principle that Agency (the ability to perceive, reason, and act) comes from the model training, while an Agent Product is the combination of a Model + Harness.

    As a developer using this repository, you are learning to build the Harness—the infrastructure that allows an LLM to function as an autonomous agent in a specific domain.

    A complete Harness consists of:

    • Tools: File I/O, Shell, Network, Database, Browser.
    • Knowledge: Product docs, domain data, API specs, style guides.
    • Observation: git diff, error logs, browser state, sensor data.
    • Action: CLI commands, API calls, UI interactions.
    • Permissions: Sandbox isolation, approval workflows, trust boundaries.

    The repository uses Claude Code as a primary architectural specimen to teach these patterns.

  9. Understand the Agent Pattern and Harness Engineering

    main

    The project is based on the concept of Harness Engineering: building the infrastructure (tools, context, permissions, and environments) that allows LLMs to function as autonomous agents.

    The Agent Pattern

    The core of every AI agent is a minimal loop where the model decides when to call tools and when to stop. The code's role is simply to execute the model's requirements.

    The Loop Logic:

    1. User sends messages[] to the LLM.
    2. LLM returns a response.
    3. If stop_reason == "tool_use":
      • Execute the requested tools.
      • Append results to messages[].
      • Loop back to step 1.
    4. If stop_reason != "tool_use":
      • Return the text response to the user.

    Harness vs. Agent

    • Agent: The model and its decision-making loop.
    • Harness: The mechanisms built around the loop (e.g., tool dispatch, memory, task management, error recovery) to make the agent efficient in specific domains.
  10. Understand the Agent Pattern and Harness Engineering

    main

    The project defines an Agent as the combination of a Model (the intelligence, e.g., Claude) and a Harness (the infrastructure, tools, context, and permissions).

    Instead of trying to build the intelligence itself, developers should focus on building the Harness. The core architecture follows the Agent Pattern: a continuous loop where the model decides whether to use a tool or return text. If a tool is requested, the harness executes the tool and feeds the results back into the message history to continue the loop.

    The Agent Pattern Loop:

    1. User sends messages.
    2. LLM processes messages and returns a response.
    3. If stop_reason == "tool_use":
      • Execute the requested tools.
      • Append results to the message history.
      • Loop back to step 2.
    4. If stop_reason != "tool_use":
      • Return the text to the user.
    def agent_loop(messages):
        while True:
            response = client.messages.create(
                model=MODEL, system=SYSTEM,
                messages=messages, tools=TOOLS,
            )
            messages.append({"role": "assistant",
                             "content": response.content})
    
            if response.stop_reason != "tool_use":
                return
    
            results = []
            for block in response.content:
                if block.type == "tool_use":
                    output = TOOL_HANDLERS[block.name](**block.input)
                    results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": output,
                    })
            messages.append({"role": "user", "content": results})
  11. Run the Agent Teams Lesson Demo

    main

    To explore the Agent Teams implementation, navigate to the project directory and execute the lesson script. You can test coordination by prompting the Lead to spawn specific roles and then checking its inbox for their results.

    Execution Command

    cd learn-claude-code
    python s15_agent_teams/code.py
    1. Spawn alice as a backend developer. Ask her to create a file called schema.sql with a users table.
    2. Check your inbox for alice's result.
    3. Spawn bob as a tester. Ask him to check if schema.sql exists and list its contents.
  12. Run the Cron Scheduler lesson demo

    main

    To test the Cron Scheduler implementation, run the following command:

    cd learn-claude-code
    python s14_cron_scheduler/code.py

    Recommended Test Prompts:

    1. Schedule a task to print the current date every 2 minutes
    2. List all cron jobs
    3. Create a one-shot reminder in 1 minute to check the build status
    4. Cancel the recurring job and verify with list_crons

    What to observe:

    • The scheduler thread running independently in the background.
    • Automatic execution (look for [queue processor] logs) without new user input.
    • Persistence of tasks in .scheduled_tasks.json after restarting the script.