AI Agents · Lezione

Versionamento e compatibilità degli strumenti

Versionamento semantico degli strumenti, compatibilità con le versioni precedenti e pattern di deprecazione.

Lezione 3 di 413 passaggi

Versionamento e compatibilità degli strumenti è una lezione AI Agents gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Agents, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Agents include 4 lezioni in totale.

Perché il versionamento degli strumenti è importante

Quando l'interfaccia di uno strumento cambia, ad esempio viene rinominato un parametro, viene aggiunto un campo obbligatorio oppure cambia la struttura di un valore restituito, gli agenti che dipendono dalla vecchia interfaccia si interrompono senza segnalarlo. Il versionamento secondo regole semantiche e gli avvisi di deprecazione impediscono questo problema.

Versionamento semantico degli strumenti

Segua il versionamento semantico: MAJOR.MINOR.PATCH. Incrementi MAJOR in caso di modifiche incompatibili (parametri rimossi, tipi di parametro modificati, struttura dei valori restituiti modificata), MINOR per le aggiunte compatibili con le versioni precedenti e PATCH per le correzioni di bug che non modificano l'interfaccia.

VERSIONING_RULES = {
    'major_bump': [
        'Removed a required or optional parameter',
        'Renamed an existing parameter',
        'Changed parameter type (e.g., string -> object)',
        'Changed response field names or types',
        'Removed a response field',
        'Changed error code values'
    ],
    'minor_bump': [
        'Added an optional parameter',
        'Added a new response field',
        'Added a new tool to the plugin'
    ],
    'patch_bump': [
        'Fixed a bug without interface change',
        'Improved error messages',
        'Performance improvement',
        'Updated documentation'
    ]
}

for bump_type, examples in VERSIONING_RULES.items():
    print(f'{bump_type}:')
    for ex in examples[:2]:
        print(f'  - {ex}')

Analisi e confronto delle versioni

Implementi utilità per confrontare le versioni e verificarne la compatibilità. Un agente può dichiarare nella propria configurazione la versione minima richiesta di uno strumento e il registro rifiuta gli strumenti che non soddisfano tale requisito.

from functools import total_ordering

@total_ordering
class Version:
    def __init__(self, version_str: str):
        parts = version_str.strip().split('.')
        if len(parts) != 3 or not all(p.isdigit() for p in parts):
            raise ValueError(f'Invalid version: {version_str}')
        self.major, self.minor, self.patch = map(int, parts)

    def __str__(self):
        return f'{self.major}.{self.minor}.{self.patch}'

    def __eq__(self, other):
        return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)

    def __lt__(self, other):
        return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)

    def is_compatible_with(self, required: 'Version') -> bool:
        """Compatible if same major version and >= required minor.patch"""
        return self.major == required.major and self >= required

v = Version('2.3.1')
required = Version('2.1.0')
print(f'{v} compatible with {required}: {v.is_compatible_with(required)}')
print(f'Is newer: {v > required}')

Blocco delle versioni nella configurazione dell'agente

Le configurazioni degli agenti dovrebbero specificare la versione minima richiesta per ogni dipendenza da uno strumento. In questo modo si impedisce che un agente utilizzi accidentalmente una versione più recente e incompatibile dello strumento quando il plugin viene aggiornato.

AGENT_TOOL_REQUIREMENTS = {
    'get_weather': '>=1.2.0',
    'search_knowledge_base': '>=3.0.0',
    'send_email': '>=2.1.0,<3.0.0'  # exclude major-version bump
}

def parse_version_constraint(constraint: str) -> list:
    """
    Parses constraints like '>=1.2.0,<3.0.0'
    Returns list of (operator, Version) tuples
    """
    ops = {'>=': lambda a, b: a >= b, '>': lambda a, b: a > b,
           '<=': lambda a, b: a <= b, '<': lambda a, b: a < b,
           '==': lambda a, b: a == b}
    parts = [p.strip() for p in constraint.split(',')]
    parsed = []
    for part in parts:
        for op_str, op_fn in ops.items():
            if part.startswith(op_str):
                parsed.append((op_fn, Version(part[len(op_str):])))
                break
    return parsed

def satisfies_constraint(tool_version: str, constraint: str) -> bool:
    v = Version(tool_version)
    rules = parse_version_constraint(constraint)
    return all(op(v, required) for op, required in rules)

print(satisfies_constraint('2.3.0', '>=2.1.0,<3.0.0'))  # True
print(satisfies_constraint('3.0.0', '>=2.1.0,<3.0.0'))  # False

Avvisi di deprecazione

Quando rimuove o rinomina un parametro, lo renda obsoleto innanzitutto in una versione MINOR: continui a supportarlo, ma emetta un avviso di deprecazione nella risposta. Lo rimuova solo nella versione MAJOR successiva. In questo modo gli sviluppatori degli agenti hanno il tempo di aggiornare il codice.

import warnings

def execute_get_weather(params: dict) -> dict:
    # Handle deprecated parameter 'temp_unit' -> replaced by 'units'
    if 'temp_unit' in params:
        warnings.warn(
            'Parameter temp_unit is deprecated since v1.3.0. '
            'Use units instead. Will be removed in v2.0.0.',
            DeprecationWarning,
            stacklevel=2
        )
        params = dict(params)
        params['units'] = params.pop('temp_unit')

    # Include deprecation notice in response
    result = _fetch_weather(params['city'], params.get('units', 'celsius'))
    if 'temp_unit' in params:
        result['_deprecation_warnings'] = [
            'temp_unit deprecated; use units'
        ]
    return result

# When calling deprecated param:
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter('always')
    # result = execute_get_weather({'city': 'London', 'temp_unit': 'celsius'})
    print('Deprecation warnings would be captured here')

Guide alla migrazione

Per ogni incremento della versione MAJOR, pubblichi una guida alla migrazione che mostri esattamente che cosa è cambiato e fornisca esempi di codice prima e dopo. Senza una guida alla migrazione, gli sviluppatori non possono eseguire l'aggiornamento in sicurezza.

MIGRATION_GUIDES = {
    '1.x_to_2.0': {
        'summary': 'Response structure changed: temperature is now nested under data{}',
        'breaking_changes': [
            {
                'description': 'temperature field moved',
                'before': 'result["temperature"]',
                'after': 'result["data"]["temperature"]'
            },
            {
                'description': 'temp_unit parameter removed',
                'before': 'execute({"city": "London", "temp_unit": "celsius"})',
                'after': 'execute({"city": "London", "units": "celsius"})'
            }
        ],
        'migration_steps': [
            '1. Update parameter name: temp_unit -> units',
            '2. Update response access: result["temperature"] -> result["data"]["temperature"]',
            '3. Run your test suite against v2.0.0'
        ]
    }
}

for guide_key, guide in MIGRATION_GUIDES.items():
    print(f'Migration guide {guide_key}:')
    print(f'  {guide["summary"]}')
    print(f'  Steps: {len(guide["migration_steps"])}')

Matrice di compatibilità

Una matrice di compatibilità documenta quali versioni dello strumento sono compatibili con quali versioni del framework dell'agente. La pubblichi e la mantenga come parte della documentazione del plugin.

COMPATIBILITY_MATRIX = {
    'weather-tools': {
        '1.x': {'framework_min': '0.8.0', 'framework_max': '0.x.x', 'status': 'EOL'},
        '2.x': {'framework_min': '1.0.0', 'framework_max': '1.x.x', 'status': 'supported'},
        '3.x': {'framework_min': '2.0.0', 'framework_max': None, 'status': 'latest'}
    }
}

def check_compatibility(
    plugin_name: str,
    tool_version: str,
    framework_version: str
) -> dict:
    matrix = COMPATIBILITY_MATRIX.get(plugin_name, {})
    major = tool_version.split('.')[0] + '.x'
    row = matrix.get(major)
    if not row:
        return {'compatible': False, 'reason': 'Version not in matrix'}
    fw = Version(framework_version)
    min_fw = Version(row['framework_min'])
    compatible = fw >= min_fw
    return {'compatible': compatible, 'status': row['status'],
            'min_framework': row['framework_min']}

result = check_compatibility('weather-tools', '2.3.0', '1.2.0')
print(result)

Registro delle versioni

Il registro degli strumenti dovrebbe memorizzare le informazioni sulla versione di ogni strumento caricato e avvisare quando due plugin forniscono lo stesso nome di strumento con versioni diverse. Preferisca le versioni più recenti, a meno che un vincolo non specifichi diversamente.

class VersionedToolRegistry(ToolRegistry):
    def register_tool(self, name, schema, execute_fn, plugin_name):
        if name in self._tools:
            existing_v = Version(self._tools[name]['schema'].get('version', '0.0.0'))
            new_v = Version(schema.get('version', '0.0.0'))
            if new_v > existing_v:
                print(f'Upgrading tool {name}: {existing_v} -> {new_v}')
            else:
                print(f'Keeping tool {name} v{existing_v} '
                      f'(skipping older v{new_v} from {plugin_name})')
                return
        super().register_tool(name, schema, execute_fn, plugin_name)

    def get_version(self, tool_name: str) -> str:
        tool = self._tools.get(tool_name)
        if not tool:
            return None
        return tool['schema'].get('version', 'unknown')

    def check_requirement(self, tool_name: str, constraint: str) -> bool:
        v = self.get_version(tool_name)
        if not v:
            return False
        return satisfies_constraint(v, constraint)

Gestione corretta delle incompatibilità di versione

Quando un agente viene caricato e uno strumento richiesto non soddisfa il vincolo di versione, non lo ignori senza segnalarlo. Le opzioni sono: interrompere subito l'esecuzione (la scelta più sicura), operare in modalità degradata (senza lo strumento) oppure mostrare un avviso e continuare.

class VersionCheckResult:
    def __init__(self):
        self.satisfied = []
        self.unsatisfied = []
        self.missing = []

def check_all_requirements(
    requirements: dict,
    registry
) -> VersionCheckResult:
    result = VersionCheckResult()
    for tool_name, constraint in requirements.items():
        installed_v = registry.get_version(tool_name)
        if installed_v is None:
            result.missing.append(tool_name)
        elif not satisfies_constraint(installed_v, constraint):
            result.unsatisfied.append({
                'tool': tool_name,
                'required': constraint,
                'installed': installed_v
            })
        else:
            result.satisfied.append(tool_name)
    return result

def start_agent_with_version_check(requirements, registry):
    check = check_all_requirements(requirements, registry)
    if check.missing:
        raise RuntimeError(f'Missing tools: {check.missing}')
    if check.unsatisfied:
        for item in check.unsatisfied:
            print(f'VERSION MISMATCH: {item["tool"]} '
                  f'requires {item["required"]}, got {item["installed"]}')
        raise RuntimeError('Tool version requirements not satisfied')
    print('All tool requirements satisfied')

Automazione del changelog

Generi automaticamente un changelog dai messaggi dei commit git utilizzando il formato conventional commit. In questo modo, le guide alla migrazione e le note di rilascio sono sempre aggiornate.

# Conventional commit format:
# feat!: (major) remove temp_unit parameter
# feat: (minor) add 'humidity_pct' to response
# fix: (patch) handle API timeout correctly
# docs: update README

import subprocess

def generate_changelog_from_git(
    from_tag: str = 'v1.2.0',
    to_tag: str = 'HEAD'
) -> dict:
    try:
        log = subprocess.check_output(
            ['git', 'log', f'{from_tag}..{to_tag}',
             '--oneline', '--pretty=format:%s'],
            text=True
        ).strip().split('\n')
    except subprocess.CalledProcessError:
        return {'error': 'git log failed'}

    changelog = {'breaking': [], 'features': [], 'fixes': [], 'docs': []}
    for msg in log:
        if msg.startswith('feat!'):
            changelog['breaking'].append(msg[5:].strip())
        elif msg.startswith('feat:'):
            changelog['features'].append(msg[5:].strip())
        elif msg.startswith('fix:'):
            changelog['fixes'].append(msg[4:].strip())
        elif msg.startswith('docs:'):
            changelog['docs'].append(msg[5:].strip())
    return changelog

if __name__ == '__main__':
    subprocess.check_output = lambda *a, **k: (
        "feat!: remove temp_unit parameter\n"
        "feat: add humidity_pct to response\n"
        "fix: handle API timeout correctly\n"
        "docs: update README"
    )
    changelog = generate_changelog_from_git()
    print('Changelog:')
    for section, items in changelog.items():
        print(f'  {section}: {items}')

File di lock per la riproducibilità

Come package-lock.json in npm, mantenga un file di lock dello strumento che registri la versione esatta di ogni plugin installato. Quando l'agente si avvia, verifichi che le versioni installate corrispondano a quelle nel file di lock, garantendo deployment riproducibili e prevedibili in ambienti diversi.

import json
import os

LOCK_FILE = '.tool-lock.json'

def generate_lock_file(registry) -> dict:
    lock = {
        'generated_at': __import__('datetime').datetime.utcnow().isoformat(),
        'tools': {}
    }
    for tool_name, tool_info in registry._tools.items():
        lock['tools'][tool_name] = {
            'version': tool_info['schema'].get('version', 'unknown'),
            'plugin': tool_info['plugin']
        }
    with open(LOCK_FILE, 'w') as f:
        json.dump(lock, f, indent=2)
    print(f'Lock file written: {len(lock["tools"])} tools')
    return lock

def verify_lock_file(registry) -> bool:
    if not os.path.exists(LOCK_FILE):
        print('No lock file found — run generate_lock_file() first')
        return False
    with open(LOCK_FILE) as f:
        lock = json.load(f)
    for tool_name, locked_info in lock['tools'].items():
        installed_v = registry.get_version(tool_name)
        if installed_v != locked_info['version']:
            print(f'VERSION MISMATCH: {tool_name} locked={locked_info["version"]} installed={installed_v}')
            return False
    print('Lock file verified: all versions match')
    return True

if __name__ == '__main__':
    import tempfile
    os.chdir(tempfile.gettempdir())

    class MockRegistry:
        def __init__(self):
            self._tools = {'get_weather': {'schema': {'version': '1.2.0'}, 'plugin': 'weather-tools'}}
        def get_version(self, name):
            return self._tools[name]['schema']['version']

    registry = MockRegistry()
    generate_lock_file(registry)
    verify_lock_file(registry)

Verifica delle conoscenze

Aggiunge un parametro format opzionale a uno strumento esistente. I chiamanti esistenti non lo utilizzano. Quale componente della versione dovrebbe incrementare?

Riepilogo: versionamento e compatibilità degli strumenti

Concetti chiave di questa lezione:

  • Versionamento semantico: MAJOR=breaking, MINOR=additive, PATCH=fix
  • Deprecazione: avviso in MINOR, rimozione in MAJOR; includere la deprecazione nella risposta
  • Guide alla migrazione: esempi di codice prima/dopo per ogni incremento MAJOR
  • Matrice di compatibilità: quali versioni dello strumento funzionano con quali versioni del framework
  • Vincolo delle versioni: la configurazione dell'agente specifica vincoli come >=1.2.0,<2.0.0
  • Fallimento immediato: rifiutare il caricamento degli strumenti che non rispettano i vincoli

Prossimo argomento: creare un marketplace di plugin per agenti con funzionalità di pubblicazione, ricerca e installazione.

Gratis per iniziare

Impara AI Agents con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
60
Lezioni
239

Domande Frequenti

La lezione «Versionamento e compatibilità degli strumenti» è gratuita?

Sì — il testo completo di «Versionamento e compatibilità degli strumenti» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Agents, passa a CoddyKit PRO. Il corso AI Agents include 4 lezioni in totale.

Cosa imparerò in «Versionamento e compatibilità degli strumenti»?

Versionamento semantico degli strumenti, compatibilità con le versioni precedenti e pattern di deprecazione. Eserciti AI Agents con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Agents?

Non è richiesta alcuna esperienza precedente. AI Agents su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Versionamento e compatibilità degli strumenti»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Agents?

Sì. Ogni lezione AI Agents include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Progettazione di strumenti condivisibili per agenti
  2. Individuazione e registrazione dei plugin
  3. Versionamento e compatibilità degli strumenti
  4. Creazione di un marketplace di plugin per agenti
← Torna a AI Agents