0Pricing
AI Agents · 강의

도구 버전 관리 및 호환성

도구의 의미 체계적 버전 관리, 이전 버전 호환성, 사용 중단 패턴을 다룹니다.

도구 버전 관리 및 호환성은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 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)

버전 레지스트리

도구 레지스트리는 로드된 각 도구의 버전 정보를 저장하고, 두 플러그인이 서로 다른 버전으로 동일한 도구 이름을 제공할 때 경고해야 합니다. 별도의 제약 조건이 지정되지 않았다면 더 최신 버전을 우선합니다.

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

변경 로그 자동화

일반적인 커밋 형식을 사용하여 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/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“도구 버전 관리 및 호환성”에서 뭘 배우나요?

도구의 의미 체계적 버전 관리, 이전 버전 호환성, 사용 중단 패턴을 다룹니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“도구 버전 관리 및 호환성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 공유 가능한 에이전트 도구 설계
  2. 플러그인 검색 및 등록
  3. 도구 버전 관리 및 호환성
  4. 에이전트 플러그인 마켓플레이스 구축
← AI Agents(으)로 돌아가기