0Pricing
AI Agents · レッスン

ツールのバージョン管理と互換性

ツールのセマンティックバージョニング、後方互換性、非推奨化のパターンを学びます。

「ツールのバージョン管理と互換性」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。

ツールのバージョン管理が重要な理由

ツールのインターフェースが変更されると、たとえばパラメーター名の変更、必須フィールドの追加、戻り値の構造変更などにより、古いインターフェースに依存するエージェントが気付かないまま動作しなくなることがあります。セマンティックなルールによるバージョン管理と非推奨通知によって、これを防げます。

ツールのセマンティックバージョニング

セマンティックバージョニング(MAJOR.MINOR.PATCH)に従ってください。後方互換性のない変更(パラメーターの削除、パラメーター型の変更、戻り値の構造変更)ではMAJORを、後方互換性のある追加ではMINORを、インターフェースに影響しないバグ修正ではPATCHをインクリメントします。

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}')

バージョンの解析と比較

互換性を確認するために、バージョン比較ユーティリティを実装してください。エージェントは設定でツールに必要な最小バージョンを宣言でき、レジストリは要件を満たさないツールを拒否できます。

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}')

エージェント設定でのバージョン固定

エージェントの設定では、各ツール依存関係に必要な最小バージョンを固定してください。これにより、プラグインの更新時に、エージェントが誤って互換性のない新しいバージョンのツールを使用するのを防げます。

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

非推奨警告

パラメーターを削除または名前変更する場合は、まずMINORバージョンで非推奨にしてください。引き続き動作させたまま、応答に非推奨警告を含めます。削除するのは次のMAJORバージョンにしてください。これにより、エージェントの開発者が更新する時間を確保できます。

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')

移行ガイド

MAJORバージョンを上げるたびに、変更点を正確に示し、変更前と変更後のコード例を提供する移行ガイドを公開してください。移行ガイドがなければ、開発者は安全にアップグレードできません。

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"])}')

互換性マトリックス

互換性マトリックスには、ツールのどのバージョンがエージェントフレームワークのどのバージョンと互換性があるかを記載します。プラグインのドキュメントの一部として公開し、最新の状態に維持してください。

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)

バージョンレジストリ

ツールレジストリには、読み込まれた各ツールのバージョン情報を保存し、異なるバージョンの同じツール名を2つのプラグインが提供している場合は警告する必要があります。制約で指定されていない限り、新しいバージョンを優先してください。

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)

バージョン不一致への適切な対処

エージェントの読み込み時に、必要なツールがバージョン制約を満たしていない場合、黙って無視しないでください。選択肢には、早期に失敗させる(最も安全)、ツールなしの縮退モードで実行する、警告して続行する、があります。

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')

変更履歴の自動生成

Conventional Commits形式のgitコミットメッセージから、変更履歴を自動生成します。これにより、移行ガイドやリリースノートを常に最新の状態に保てます。

# 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}')

ロックファイルによる再現性

npmのpackage-lock.jsonと同様に、インストールした各プラグインの正確なバージョンを記録するツール用ロックファイルを管理します。エージェントの起動時に、インストール済みのバージョンがロックファイルと一致することを検証します。これにより、環境が変わっても再現性があり予測可能なデプロイを実現できます。

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)

理解度チェック

既存のツールにオプションのformatパラメーターを追加します。既存の呼び出し元では、このパラメーターを使用していません。どのバージョン要素をインクリメントすべきでしょうか。

まとめ:ツールのバージョニングと互換性

このレッスンの主なポイント:

  • セマンティックバージョニング:MAJOR=破壊的変更、MINOR=追加、PATCH=修正
  • 非推奨化:MINORで警告し、MAJORで削除します。レスポンスにも非推奨化を含めます
  • 移行ガイド:すべてのMAJOR更新について、変更前と変更後のコード例を用意します
  • 互換性マトリックス:どのツールバージョンがどのフレームワークバージョンで動作するかを示します
  • バージョンの固定:エージェント設定で>=1.2.0,<2.0.0のような制約を指定します
  • 早期失敗:制約を満たさないツールの読み込みを拒否します

次は、公開、検索、インストールに対応したエージェントプラグインマーケットプレイスを構築します。

よくある質問

「ツールのバージョン管理と互換性」レッスンは無料ですか?

はい。「ツールのバージョン管理と互換性」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「ツールのバージョン管理と互換性」で何を学びますか?

ツールのセマンティックバージョニング、後方互換性、非推奨化のパターンを学びます。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「ツールのバージョン管理と互換性」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 共有可能なエージェントツールの設計
  2. プラグインの検出と登録
  3. ツールのバージョン管理と互換性
  4. エージェントプラグインマーケットプレイスを構築する
← AI Agentsに戻る