ml-agent / README.md
akseljoonas's picture
akseljoonas HF Staff
ui updates (#1)
9dbf093
metadata
title: HF Agent
emoji: πŸ€–
colorFrom: blue
colorTo: purple
sdk: docker
app_port: 7860
hf_oauth: true
hf_oauth_scopes:
  - read-repos
  - write-repos
  - contribute-repos
  - manage-repos
  - inference-api
  - jobs
  - write-discussions

HF Agent

An MLE agent CLI with MCP (Model Context Protocol) integration and built-in tool support.

Quick Start

Installation

# Clone the repository
git clone git@github.com:huggingface/hf_agent.git
cd hf_agent

Install recommended dependencies

uv sync --extra agent # or uv sync --extra all

Interactive CLI

uv run python -m agent.main

This starts an interactive chat session with the agent. Type your messages and the agent will respond, using tools as needed.

The agent will automatically discover and register all tools from configured MCP servers.

Env Setup

ANTHROPIC_API_KEY=<one-key-to-rule-them-all>
HF_TOKEN=<hf-token-to-access-the-hub>
GITHUB_TOKEN=<gh-pat-key-for-not-reinventing-the-wheel>
HF_NAMESPACE=<hf-namespace-to-use>

Architecture

Component Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         User/CLI                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚ User request                                β”‚ Events
             ↓                                             ↑
      submission_queue                                   event_queue
             β”‚                                                 β”‚
             ↓                                                 β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚            submission_loop (agent_loop.py)         β”‚         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚         β”‚
β”‚  β”‚  1. Receive Operation from queue             β”‚  β”‚         β”‚
β”‚  β”‚  2. Route to Handler (run_agent/compact/...) β”‚  β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚         β”‚
β”‚                      ↓                             β”‚         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚         β”‚
β”‚  β”‚         Handlers.run_agent()                 β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”‚                                              β”‚  β”‚ Emit    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚ Events  β”‚
β”‚  β”‚  β”‚  Agentic Loop (max 10 iterations)      β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚                                        β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚ Session                          β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚ ContextManager             β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚ β€’ Message history          β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚   (litellm.Message[])      β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚ β€’ Auto-compaction (180k)   β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚                                  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚ ToolRouter                 β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ explore_hf_docs        β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ fetch_hf_docs          β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ find_hf_api            β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ plan_tool              β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ hf_jobs*               β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ hf_private_repos*      β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€ github_* (3 tools)     β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚  └─ MCP tools (e.g.,       β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β”‚      model_search, etc.)   β”‚  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚                                        β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚  Loop:                                 β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚    1. LLM call (litellm.acompletion)   β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚       ↓                                β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚    2. Parse tool_calls[]               β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚       ↓                                β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚    3. Execute via ToolRouter           β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚       ↓                                β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚    4. Add results to ContextManager    β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚       ↓                                β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β”‚    5. Repeat if tool_calls exist       β”‚  β”‚  β”‚         β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Agentic Loop Flow

User Message
     ↓
[Add to ContextManager]
     ↓
     ╔═══════════════════════════════════════╗
     β•‘      Iteration Loop (max 10)          β•‘
     β•‘                                       β•‘
     β•‘  Get messages + tool specs            β•‘
     β•‘         ↓                             β•‘
     β•‘  litellm.acompletion()                β•‘
     β•‘         ↓                             β•‘
     β•‘  Has tool_calls? ──No──> Done         β•‘
     β•‘         β”‚                             β•‘
     β•‘        Yes                            β•‘
     β•‘         ↓                             β•‘
     β•‘  Add assistant msg (with tool_calls)  β•‘
     β•‘         ↓                             β•‘
     β•‘  For each tool_call:                  β•‘
     β•‘    β€’ ToolRouter.execute_tool()        β•‘
     β•‘    β€’ Add result to ContextManager     β•‘
     β•‘         ↓                             β•‘
     β•‘  Continue loop ─────────────────┐     β•‘
     β•‘         ↑                       β”‚     β•‘
     β•šβ•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•

Project Structure

agent/
β”œβ”€β”€ config.py                 # Configuration models
β”œβ”€β”€ main.py                   # Interactive CLI entry point
β”œβ”€β”€ prompts/
β”‚   └── system_prompt.yaml   # Agent behavior and personality
β”œβ”€β”€ context_manager/
β”‚   └── manager.py           # Message history & auto-compaction
└── core/
    β”œβ”€β”€ agent_loop.py        # Main agent loop and handlers
    β”œβ”€β”€ session.py           # Session management
    β”œβ”€β”€ mcp_client.py        # MCP SDK integration
    └── tools.py             # ToolRouter and built-in tools

configs/
└── main_agent_config.json   # Model and MCP server configuration

tests/                       # Integration and unit tests
eval/                        # Evaluation suite (see eval/README.md)

Events

The agent emits the following events via event_queue:

  • processing - Starting to process user input
  • assistant_message - LLM response text
  • tool_call - Tool being called with arguments
  • tool_output - Tool execution result
  • approval_request - Requesting user approval for sensitive operations
  • turn_complete - Agent finished processing
  • error - Error occurred during processing
  • interrupted - Agent was interrupted
  • compacted - Context was compacted
  • undo_complete - Undo operation completed
  • shutdown - Agent shutting down

Development

Adding Built-in Tools

Edit agent/core/tools.py:

def create_builtin_tools() -> list[ToolSpec]:
    return [
        ToolSpec(
            name="your_tool",
            description="What your tool does",
            parameters={
                "type": "object",
                "properties": {
                    "param": {"type": "string", "description": "Parameter description"}
                },
                "required": ["param"]
            },
            handler=your_async_handler
        ),
        # ... existing tools
    ]

Adding MCP Servers

Edit configs/main_agent_config.json:

{
  "model_name": "anthropic/claude-sonnet-4-5-20250929",
  "mcpServers": {
    "your-server-name": {
      "transport": "http",
      "url": "https://example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${YOUR_TOKEN}"
      }
    }
  }
}

Note: Environment variables like ${YOUR_TOKEN} are auto-substituted from .env.