Costruisci un Agente di Intelligenza Artificiale di Stile Nanobot in Google Colab con Chiamate degli Strumenti, Memoria di Sessione, Competenze e Server MCP
Introduzione: costruzione di un Agente AI di tipo Nanobot
Costruiremo un agente AI leggero, ispirato all'architettura di nanobot, eseguendo ciascun passo in Google Colab. Il tutorial copre da astrazioni al codice sorgente reale, permettendoci di comprendere chiaramente il flusso di informazioni, gli strumenti, la memoria sessione e il ciclo principale del modello.
Partiamo con l'astrazione del fornitore, quindi passiamo alla registrazione degli strumenti, alla memorizzazione delle sessioni, alla creazione delle competenze, e infine all'implementazione di un server MCP. Per rendere l'articolo completo, abbiamo incluso i seguenti aspetti essenziali:
- Sviluppo dell'astrazione del provider e di un modello linguistico fittizio (Mock LLM).
- Implementazione delle chiamate degli strumenti, gestione della sessione.
- Gestione avanzata mediante l'utilizzo di librerie come asyncio e nest_asyncio.
- Codice eseguibile in un ambiente isolato e senza bisogno di API Key.
Preparazione: Installazione di Librerie e Funzioni Utility
Per iniziare, installiamo le librerie necessarie e definiamo alcune funzioni utili, ad esempio per gestire l'ambiente di lavoro, caricare pacchetti come openai, nest_asyncio (per gestire coroutine asincrone), e utilizzare funzioni asincrone.
Il codice seguente illustra l'installazione necessaria per creare un ambiente funzionante e gestire correttamente le dipendenze:
import subprocess, sys
def pipinstall(*pkgs):
try:
subprocess.run([sys.executable, "-m", "pip", "install", "-q", *pkgs], check=True)
except Exception as e:
print(f"(pip install skipped/failed for {pkgs}: {e})")
HAVEOPENAI = False
try:
import openai
HAVEOPENAI = True
except Exception:
pipinstall("openai>=1.0.0")
try:
import openai
HAVEOPENAI = True
except Exception:
HAVEOPENAI = False
try:
import nest_asyncio
nest_asyncio.apply()
except Exception:
try:
pipinstall("nest_asyncio")
import nest_asyncio
nest_asyncio.apply()
except Exception:
pass
import os
import re
import json
import time
import math
import asyncio
import inspect
import textwrap
import contextlib
import io
from dataclasses import dataclass, field
from typing import Any, Callable, Optional, Awaitable, gettypehints
def banner(title: str) -> None:
line = "═" * 78
print(f"\n{line}\n {title}\n{line}")
Clausole e Classi di Base per l'LLM
I progetti di AI richiedono la definizione di classi base per la gestione di chiamate alle API, il modello linguistico e l'elaborazione di risposte. L'astrazione fornitore consente l'integrazione flessibile di diversi modelli e ambienti di esecuzione in una singola architettura.
Le definizioni chiave incluse sono:
- Classe ToolCall: Rappresenta una richiesta normalizzata per l'esecuzione di un singolo strumento.
- Classe Usage: Traccia gli utilizzi del token per gli input e output modello.
- Classe LLMResponse: Restituisce le risposte normalizzate prodotte dal provider.
- Classe Provider: Astrazione base per gestire la conversione di messaggi e strumenti in LLMResponse.
Provider API compatibile OpenAI
Il Provider compatibile con OpenAI consente l'erogazione di API in diversi ambienti, come DeepSeek, Ollama, Together, e molto altro. La logica principale si basa sull'invocazione asincrona per rispondere a query.
Il codice seguente include:
@dataclass
class ToolCall:
"""A normalized request from the model to run one tool."""
id: str
name: str
arguments: dict
@dataclass
class Usage:
prompt_tokens: int = 0
completion_tokens: int = 0
@property
def total(self) -> int:
return self.prompttokens + self.completiontokens
@dataclass
class LLMResponse:
"""The single shape every provider must return."""
content: Optional[str]
toolcalls: list[ToolCall] = field(defaultfactory=list)
finish_reason: str = "stop"
usage: Usage = field(default_factory=Usage)
class Provider:
"""Base class. A provider turns (messages, tools) into an LLMResponse."""
name = "base"
async def complete(self, messages: list[dict], tools: list[dict]) -> LLMResponse:
raise NotImplementedError
Provider compatibile OpenAI
Il provider compatibile OpenAI supporta una vasta gamma di API e implementazioni in Python. Questa classe fornisce funzionalità complete per interagire con il modello asincronamente.
class OpenAICompatibleProvider(Provider):
"""
Works with OpenAI and every OpenAI-compatible gateway (OpenRouter, DeepSeek,
Together, vLLM, LM Studio, Ollama's /v1, ...). This mirrors how nanobot speaks
to most providers under the hood.
"""
name = "openai-compatible"
def _init(self, apikey: str, model: str, base_url: Optional[str] = None):
from openai import AsyncOpenAI
self.model = model
self.client = AsyncOpenAI(apikey=apikey, baseurl=baseurl)
async def complete(self, messages: list[dict], tools: list[dict]) -> LLMResponse:
kwargs: dict[str, Any] = {"model": self.model, "messages": messages}
if tools:
kwargs["tools"] = tools
kwargs["tool_choice"] = "auto"
resp = await self.client.chat.completions.create(**kwargs)
choice = resp.choices[0]
msg = choice.message
calls: list[ToolCall] = []
for tc in (msg.tool_calls or []):
try:
args = json.loads(tc.function.arguments or "{}")
except json.JSONDecodeError:
args = {"_raw": tc.function.arguments}
calls.append(ToolCall(id=tc.id, name=tc.function.name, arguments=args))
usage = Usage(
prompttokens=getattr(resp.usage, "prompttokens", 0) or 0,
completiontokens=getattr(resp.usage, "completiontokens", 0) or 0,
)
return LLMResponse(
content=msg.content,
tool_calls=calls,
finishreason=choice.finishreason or "stop",
usage=usage,
)
Provider fittizio per il controllo locale
La classe MockProvider crea un ambiente simulato per l'implementazione senza necessitare API esterne. Simula una risposta AI in modo deterministico e prevedibile.
Alcune caratteristiche principali del provider sono:
- Non necessita di chiavi API.
- Riceve messaggi, interpreta richieste matematiche basate su espressioni.
- Torna risultati come se fossero prodotti da un modello di AI reale.
Il codice del provider fittizio presenta:
class MockProvider(Provider):
"""
A deterministic, rule-based "LLM" so this entire tutorial runs with