Skip to content

hacklabr/ai-assistant

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HackLab AI Assistant

An embeddable AI assistant framework for PHP built on top of Neuron AI. Provides advanced context management, sub-agent orchestration, skill configuration, auto-learning, and MCP integration.

Features

  • Context Condensation - 4 strategies for intelligent context reduction before delegation
  • Sub-Agent Orchestration - Delegate tasks to specialized agents with automatically condensed context
  • Skill System - Configure reusable instruction modules via Markdown files with YAML frontmatter
  • File Reading - Built-in tool for reading PDF, DOCX, TXT, CSV, Markdown, and more
  • Auto-Learning - Record tool patterns, collect bugs, and get intelligent suggestions (with anti-poisoning guardrails)
  • User Memory - Per-user persistent memories scoped by backend-provided user ID
  • MCP Integration - Native support for stdio, SSE, and HTTP transports via Neuron's MCP connector
  • Security First - Encrypted config, sensitive data redaction, MCP command allowlist, PSR-3 logging
  • Structured Output - Typed PHP objects with validation via Neuron's #[SchemaProperty] system

Requirements

Installation

composer require hacklab/ai-assistant

Quick Start

use HackLab\AIAssistant\Assistant;
use HackLab\AIAssistant\AssistantConfig;
use HackLab\AIAssistant\Persistence\FileStorage;
use NeuronAI\Chat\Messages\UserMessage;
use NeuronAI\Providers\Anthropic\Anthropic;

$assistant = Assistant::configure(
    new AssistantConfig(
        provider: new Anthropic(
            key: 'your-api-key',
            model: 'claude-sonnet-4',
        ),
        storage: new FileStorage(__DIR__ . '/storage'),
        instructions: 'You are a helpful coding assistant.',
    )
);

$response = $assistant->chat(new UserMessage('Hello!'));
echo $response->getMessage()->getContent();

Configuration

Basic Configuration

use HackLab\AIAssistant\AssistantConfig;
use HackLab\AIAssistant\Persistence\FileStorage;

$config = new AssistantConfig(
    provider: $aiProvider,           // Neuron AIProviderInterface (required)
    storage: new FileStorage('/path/to/storage'), // StorageInterface (required)
    instructions: 'System prompt',   // Base instructions
    contextWindow: 200000,           // Token limit (default: 200000)
    tools: [],                       // Array of Tool classes/instances
    subAgents: [],                   // Sub-agent configurations
    skills: [],                      // Skill names to load
    skillsPath: '/path/to/skills',   // Directory containing .md skill files
    autoLearn: false,                // Enable auto-learning
    autoDelegate: true,              // Enable auto-delegation to sub-agents
    requireLearningCheck: true,      // Mandatory learning check before using tools
    userId: $currentUser->getId(),   // Backend-provided user ID (for user memory)
    logger: $psr3Logger,             // PSR-3 logger (optional)
    requestTimeout: 120.0,           // HTTP client timeout in seconds (null = provider default: 60s)
    outputClass: null,               // FQCN for structured output (class with #[SchemaProperty])
    structuredMaxRetries: 1,         // Retries on structured output validation failure
    conversationStorage: null,        // Override storage for conversations (falls back to $storage)
    learningStorage: null,            // Override storage for learning data (falls back to $storage)
    userMemoryStorage: null,          // Override storage for user memories (falls back to $storage)
);

Per-Domain Storage

Assign different backends for conversations, learning, and user memories:

new AssistantConfig(
    provider: $provider,
    storage: new FileStorage('/data/default'),
    conversationStorage: new RedisStorage($redis),      // conversations in Redis
    learningStorage: new FileStorage('/data/learning'),  // learning on disk
    userMemoryStorage: new DatabaseStorage($pdo),        // memories in DB
);

Custom Storage Backends

Implement StorageInterface for custom backends (database, Redis, etc.):

use HackLab\AIAssistant\Persistence\StorageInterface;

class RedisStorage implements StorageInterface {
    public function save(string $namespace, string $key, array $data): void { /* ... */ }
    public function load(string $namespace, string $key): ?array { /* ... */ }
    public function delete(string $namespace, string $key): bool { /* ... */ }
    public function exists(string $namespace, string $key): bool { /* ... */ }
    public function list(string $namespace, string $pattern = '*'): array { /* ... */ }
    public function search(string $namespace, string $query, int $limit = 10): array { /* ... */ }
    public function cleanup(string $namespace, array $criteria = []): int { /* ... */ }
}

new AssistantConfig(
    provider: $provider,
    storage: new RedisStorage($redis),
)

Supported AI Providers

Any Neuron AI provider can be used:

use NeuronAI\Providers\Anthropic\Anthropic;
use NeuronAI\Providers\OpenAI\OpenAI;
use NeuronAI\Providers\Gemini\Gemini;
use NeuronAI\Providers\Ollama\Ollama;
use NeuronAI\Providers\Deepseek\Deepseek;

// Anthropic Claude
new Anthropic(key: 'sk-ant-...', model: 'claude-sonnet-4');

// OpenAI GPT
new OpenAI(key: 'sk-...', model: 'gpt-4o');

// Google Gemini
new Gemini(key: '...', model: 'gemini-2.0-flash');

// Local Ollama (no API key needed)
new Ollama(model: 'llama3.2');

// Deepseek
new Deepseek(key: '...', model: 'deepseek-chat');

Custom Endpoints (OpenAI-Compatible APIs)

Use OpenAILike for any provider with an OpenAI-compatible API:

use NeuronAI\Providers\OpenAILike;

// Z.AI Coding Plan
new OpenAILike(
    baseUri: 'https://api.z.ai/api/coding/paas/v4',
    key: 'your-zai-api-key',
    model: 'glm-5.1',
    parameters: [],
    strict_response: false,
    httpClient: null
);

// Any other OpenAI-compatible provider
new OpenAILike(
    baseUri: 'https://api.custom-provider.com/v1',
    key: 'your-key',
    model: 'your-model',
    parameters: [],
    strict_response: false,
    httpClient: null
);

Sub-Agents

Delegate tasks to specialized agents with automatically condensed context:

use HackLab\AIAssistant\SubAgents\SubAgentConfig;

$assistant = Assistant::configure(
    new AssistantConfig(
        provider: new Anthropic('key', 'claude-sonnet-4'),
        storage: new FileStorage(__DIR__ . '/storage'),
        subAgents: [
            'code-reviewer' => new SubAgentConfig(
                id: 'code-reviewer',
                provider: new OpenAI('key', 'gpt-4'),
                instructions: 'You are an expert code reviewer...',
                tools: [GitToolkit::class, FileSystemToolkit::class],
                skills: ['security', 'psr12'],
                contextStrategy: 'code-focused',
                contextWindow: 8000,
                mcps: [
                    ['type' => 'stdio', 'command' => 'npx', 'args' => ['@modelcontextprotocol/server-github']],
                ],
            ),
        ],
    )
);

// Delegate to sub-agent
$result = $assistant->delegate('code-reviewer', new UserMessage('Check for security issues'));
echo $result->getContent();

Context Condensation Strategies

When delegating to sub-agents, context is automatically condensed using one of 4 strategies:

Strategy Description Best For
Truncation Simple token-based cutting Emergency fallback
Summarization LLM-powered summarization Long conversations
Relevance Keyword/pattern matching Task-specific delegation
Hierarchical Summary + recent + key facts Complex multi-turn (default)

Configure per sub-agent:

new SubAgentConfig(
    // ...
    contextStrategy: 'code-focused',  // Use relevance strategy with code keywords
)

Skills

Skills are reusable instruction modules stored as Markdown files with YAML frontmatter:

---
name: Security Auditor
description: OWASP security specialist
tools:
  - StaticAnalysisTool
  - DependencyCheckTool
context_strategy: security-focused
---

When reviewing code:
- Check for SQL injection, XSS, CSRF
- Validate prepared statements
- Never expose secrets

Load skills from a directory:

new AssistantConfig(
    provider: $provider,
    storage: $storage,
    skillsPath: __DIR__ . '/skills',
    skills: ['security'],  // Reference by name
)

MCP Integration

Connect to MCP servers via stdio, SSE, or HTTP:

// stdio (local process)
['type' => 'stdio', 'command' => 'npx', 'args' => ['@modelcontextprotocol/server-github']]

// SSE (Server-Sent Events)
['type' => 'sse', 'url' => 'http://localhost:8080/sse', 'token' => 'optional-bearer-token']

// HTTP Streaming
['type' => 'http', 'url' => 'https://api.example.com/mcp']

Auto-Learning

Enable to record tool usage patterns, collect bugs, and build contextual knowledge:

new AssistantConfig(
    provider: $provider,
    storage: new FileStorage(__DIR__ . '/storage'),
    autoLearn: true,
    requireLearningCheck: true,  // Default: mandatory check before using tools
)

Learning Tools

When auto-learning is enabled, the assistant gains access to 5 contextual learning tools:

Tool Purpose
record_learning Record a pattern or anti-pattern for a specific context
get_context_insights Retrieve recorded learnings, patterns, and known issues
record_bug Document a bug or error with optional workaround
find_similar_issues Search for known issues before attempting an approach
forget_learning Remove a learning entry (with anti-poisoning guardrails)

Contextual Organization

Learnings are organized by context (tool name, framework, domain):

// Record a successful pattern
$assistant->chat(new UserMessage(
    'record_learning(context: "filesystem_tool", observation: "Always check parent dir", worked_well: true)'
));

// Check insights before using a tool
$assistant->chat(new UserMessage(
    'get_context_insights(context: "database_tool")'
));

Mandatory Learning Check

When requireLearningCheck: true (default), the assistant is instructed to consult the learning system before using any tool for the first time in a conversation. This prevents repeated mistakes and leverages accumulated knowledge.

To disable:

new AssistantConfig(
    provider: $provider,
    storage: $storage,
    requireLearningCheck: false,  // Skip mandatory checks
)

Learning Guardrails

The learning system has built-in protection against knowledge base poisoning:

  • The assistant never records learnings directly dictated by the user
  • Learnings must originate from the agent's own observations (tool results, error patterns, code analysis)
  • If the user suggests a learning, the agent evaluates it critically and only records independently verified observations
  • Instruction-like patterns such as "never use tool X" are detected and rejected by the GuardsAgainstPoisoning trait
  • Deletion requests are also guarded against bulk manipulation patterns (forget all, purge, etc.)

This ensures the learning system cannot be manipulated through social engineering.

User Memory

Provide per-user persistent memories scoped by a backend-provided user ID:

$assistant = Assistant::configure(
    new AssistantConfig(
        provider: new Anthropic('key', 'claude-sonnet-4'),
        storage: new FileStorage(__DIR__ . '/storage'),
        userId: $authenticatedUser->getId(),  // Backend-provided, never from user input
    )
);

Memory Tools

When userId is provided, the assistant gains 3 memory tools:

Tool Purpose
save_memory Save a memory for the current user (category: preference, context, note, instruction)
recall_memories Search and retrieve memories for the current user
delete_memory Delete a specific memory by ID (ownership verified)

Security Model

  • userId is injected by the backend via AssistantConfig — the LLM cannot change it
  • Storage is partitioned by user ID via namespace (memories/{userId})
  • delete_memory verifies ownership before deletion
  • One user cannot access or modify another user's memories

File Reading

Built-in tool for reading and extracting text from local documents:

use HackLab\AIAssistant\Tools\FileReader\FileReaderTool;

new AssistantConfig(
    provider: $provider,
    storage: $storage,
    tools: [new FileReaderTool()],
);

The assistant gains the read_file tool which accepts file_path (required) and max_length (optional, default 100k chars).

Supported Formats

Format Extension Dependency
PDF .pdf smalot/pdfparser (pure PHP)
Word .docx phpoffice/phpword (pure PHP)
Plain Text .txt Native
CSV .csv Native
Markdown .md Native
HTML .html, .htm Native
JSON .json Native
XML .xml Native
RTF .rtf Native

Structured Output

Return typed PHP objects instead of plain text. Ideal for generating application configuration, extracting data, or building JSON APIs.

Define an output class

use NeuronAI\StructuredOutput\SchemaProperty;

class MapLayer
{
    #[SchemaProperty(description: 'Layer name', required: true)]
    public string $name;

    #[SchemaProperty(description: 'Layer type: raster, vector, tile', required: true)]
    public string $type;

    #[SchemaProperty(description: 'Source URL', required: true)]
    public string $source;

    #[SchemaProperty(description: 'Default visibility', required: false)]
    public bool $visible = true;
}

class MapConfig
{
    #[SchemaProperty(description: 'Map title', required: true)]
    public string $title;

    #[SchemaProperty(description: 'Map layers', required: true, anyOf: [MapLayer::class])]
    public array $layers;
}

Configure and use

$assistant = Assistant::configure(
    new AssistantConfig(
        provider: new Anthropic('key', 'claude-sonnet-4'),
        storage: new FileStorage(__DIR__ . '/storage'),
        instructions: 'You configure interactive maps.',
        outputClass: MapConfig::class,
    )
);

$config = $assistant->structured(
    new UserMessage('Street map of São Paulo with satellite overlay')
);

// Use as object
echo $config->title;
foreach ($config->layers as $layer) {
    echo $layer->name;
}

// Or return as JSON to a frontend
header('Content-Type: application/json');
echo json_encode($config);

Explicit class per call

$assistant = Assistant::configure(
    new AssistantConfig(
        provider: $provider,
        storage: $storage,
        instructions: 'Extract contact info from text.',
    )
);

$contact = $assistant->structured(
    new UserMessage('Alice, alice@example.com, (11) 99999-0000'),
    ContactInfo::class,
);

See API Reference for full details including validation rules and nested objects.

CLI Example

Interactive command-line assistant:

// See examples/cli-assistant.php
php examples/cli-assistant.php

Documentation

Full architecture documentation is available in the docs/ directory:

Testing

vendor/bin/phpunit

License

BSD 3-Clause License

Contributing

Contributions are welcome! Please ensure:

  • PHP 8.2+ with declare(strict_types=1)
  • PSR-12 coding standards
  • Tests for new features
  • All documentation in English

Credits

Built by HackLab on top of Neuron AI.

About

An embeddable AI assistant framework for PHP built on top of [Neuron AI](https://neuron-ai.dev/)

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages