0Pricing
AI Agents · درس

تصميم أدوات وكلاء قابلة للمشاركة

معايير مخططات الأدوات، ومتطلبات التوثيق، وحزم الأدوات لإعادة استخدامها.

تصميم أدوات وكلاء قابلة للمشاركة درس مجاني في AI Agents على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Agents، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Agents 4 دروس في المجموع.

ما الذي يجعل الأداة قابلة للمشاركة؟

الأداة القابلة للمشاركة للوكيل هي أداة يستطيع المطورون الآخرون دمجها في أنظمة وكلائهم من دون قراءة الشيفرة المصدرية. وتمتلك مخططًا واضحًا قابلًا للقراءة آليًا، وملف README سهل القراءة للبشر، ورموز أخطاء محددة جيدًا، وسلوكًا متوقعًا. فكّر فيها كمكتبة، لا كنص برمجي.

معايير مخططات الأدوات

يجب أن تمتلك كل أداة قابلة للمشاركة مخططًا يصف مدخلاتها ومخرجاتها وبياناتها الوصفية. ويمثل المخطط العقد بين مؤلف الأداة والوكيل الذي يستخدمها. استند إلى JSON Schema لتحقيق أقصى توافق مع جميع أطر عمل الوكلاء الرئيسية.

TOOL_SCHEMA_TEMPLATE = {
    'name': 'get_weather',
    'description': 'Returns current weather conditions for a city. '
                   'Use when the user asks about weather or temperature.',
    'version': '1.2.0',
    'parameters': {
        'type': 'object',
        'properties': {
            'city': {
                'type': 'string',
                'description': 'City name (e.g., London, Tokyo)',
                'minLength': 1,
                'maxLength': 100
            },
            'units': {
                'type': 'string',
                'enum': ['celsius', 'fahrenheit'],
                'default': 'celsius',
                'description': 'Temperature unit'
            }
        },
        'required': ['city']
    },
    'returns': {
        'type': 'object',
        'properties': {
            'temperature': {'type': 'number'},
            'condition': {'type': 'string'},
            'humidity_pct': {'type': 'number'}
        }
    },
    'rate_limit': {'calls_per_minute': 60}
}

if __name__ == '__main__':
    print(f"Tool: {TOOL_SCHEMA_TEMPLATE['name']} (v{TOOL_SCHEMA_TEMPLATE['version']})")
    print('Description:', TOOL_SCHEMA_TEMPLATE['description'])
    print('Required params:', TOOL_SCHEMA_TEMPLATE['parameters']['required'])

رموز الأخطاء

حدّد تنسيقًا موحدًا لاستجابات الأخطاء. يجب أن تعيد كل أداة بنية الأخطاء نفسها: الرمز والرسالة والتفاصيل الاختيارية. يتيح ذلك للوكيل معالجة الأخطاء برمجيًا من دون معرفة التفاصيل الداخلية للأداة.

from enum import Enum

class ToolErrorCode(Enum):
    INVALID_PARAMS = 'INVALID_PARAMS'
    NOT_FOUND = 'NOT_FOUND'
    RATE_LIMITED = 'RATE_LIMITED'
    UPSTREAM_ERROR = 'UPSTREAM_ERROR'
    TIMEOUT = 'TIMEOUT'
    UNAUTHORISED = 'UNAUTHORISED'
    INTERNAL_ERROR = 'INTERNAL_ERROR'

class ToolError(Exception):
    def __init__(self, code: ToolErrorCode, message: str, details: dict = None):
        self.code = code
        self.message = message
        self.details = details or {}

    def to_dict(self) -> dict:
        return {
            'error': True,
            'code': self.code.value,
            'message': self.message,
            'details': self.details
        }

try:
    raise ToolError(
        ToolErrorCode.NOT_FOUND,
        'City not found in database',
        {'city': 'Atlantis', 'suggestion': 'Did you mean Athens?'}
    )
except ToolError as e:
    print(e.to_dict())

التحقق من المدخلات

تحقّق من المدخلات مقابل المخطط قبل التنفيذ. استخدم jsonschema لإجراء التحقق تلقائيًا. أوقف التنفيذ مبكرًا مع عرض خطأ وصفي بدلًا من السماح للمدخلات غير الصالحة بالتسبب في أعطال غامضة في عمق منطق الأداة.

import jsonschema  # pip install jsonschema

def validate_tool_input(params: dict, schema: dict) -> dict:
    """
    Validate params against schema.
    Returns validated (and default-filled) params.
    Raises ToolError on validation failure.
    """
    try:
        # Fill in defaults
        filled = dict(params)
        for prop, definition in schema['properties'].items():
            if prop not in filled and 'default' in definition:
                filled[prop] = definition['default']

        # Validate against schema
        jsonschema.validate(instance=filled, schema=schema)
        return filled

    except jsonschema.ValidationError as e:
        raise ToolError(
            ToolErrorCode.INVALID_PARAMS,
            f'Validation failed: {e.message}',
            {'path': list(e.path), 'schema_path': list(e.schema_path)}
        )

# Example usage:
try:
    params = validate_tool_input({'city': 'London'}, TOOL_SCHEMA_TEMPLATE['parameters'])
    print('Valid params:', params)
except ToolError as e:
    print('Error:', e.to_dict())

أمثلة الاستخدام في المخطط

أضف أمثلة ملموسة إلى مخطط أداتك. تؤدي الأمثلة غرضين: فهي تساعد البشر على فهم الأداة بسرعة، ويمكن إدراجها في سياق الوكيل باعتبارها أمثلة توضيحية قليلة اللقطات لتحسين دقة استدعاء الأداة.

TOOL_WITH_EXAMPLES = {
    'name': 'search_knowledge_base',
    'description': 'Search the company knowledge base for documentation.',
    'parameters': {
        'type': 'object',
        'properties': {
            'query': {'type': 'string'},
            'top_k': {'type': 'integer', 'default': 5, 'minimum': 1, 'maximum': 20}
        },
        'required': ['query']
    },
    'examples': [
        {
            'description': 'Search for onboarding docs',
            'input': {'query': 'how to onboard new users', 'top_k': 3},
            'output': {'results': [{'title': 'User Onboarding Guide', 'score': 0.95}]}
        },
        {
            'description': 'Search with default top_k',
            'input': {'query': 'API authentication'},
            'output': {'results': [{'title': 'API Auth Docs', 'score': 0.88}]}
        }
    ]
}

if __name__ == '__main__':
    print('Tool:', TOOL_WITH_EXAMPLES['name'])
    for ex in TOOL_WITH_EXAMPLES['examples']:
        print(f" - {ex['description']}: input={ex['input']} -> output={ex['output']}")

تحديد معدل الطلبات في الأدوات

يجب أن تلتزم الأدوات التي تستدعي واجهات API خارجية بحدود معدل الطلبات. نفّذ محددًا لمعدل الطلبات لكل أداة باستخدام دلو الرموز أو نافذة منزلقة. أعد خطأ RATE_LIMITED قياسيًا يتضمن عدد ثواني إعادة المحاولة عند تجاوز الحد.

import time
from collections import deque

class SlidingWindowRateLimiter:
    def __init__(self, max_calls: int, window_seconds: int):
        self.max_calls = max_calls
        self.window = window_seconds
        self._calls = deque()  # timestamps of recent calls

    def check(self) -> tuple:
        """
        Returns (allowed: bool, retry_after_seconds: float)
        """
        now = time.time()
        # Remove calls outside the window
        while self._calls and self._calls[0] < now - self.window:
            self._calls.popleft()

        if len(self._calls) >= self.max_calls:
            oldest = self._calls[0]
            retry_after = (oldest + self.window) - now
            return False, round(retry_after, 1)

        self._calls.append(now)
        return True, 0.0

weather_limiter = SlidingWindowRateLimiter(max_calls=10, window_seconds=60)
allowed, retry = weather_limiter.check()
if not allowed:
    raise ToolError(ToolErrorCode.RATE_LIMITED,
                    f'Rate limit exceeded. Retry in {retry}s',
                    {'retry_after': retry})

التغليف كحزمة Python

نظّم أداتك في صورة حزمة Python قابلة للتثبيت، حتى يتمكن المطورون الآخرون من إضافتها إلى وكلائهم باستخدام أمر pip install واحد. تكشف الحزمة الدالتين get_tool_definition() وexecute(params) باعتبارهما واجهة API العامة.

# Directory structure:
# agent_tool_weather/
#   __init__.py
#   tool.py
#   schema.py
#   pyproject.toml
#   README.md

# agent_tool_weather/tool.py
from .schema import SCHEMA
from .errors import ToolError, ToolErrorCode

def get_tool_definition() -> dict:
    return SCHEMA

def execute(params: dict) -> dict:
    validated = validate_tool_input(params, SCHEMA['parameters'])
    city = validated['city']
    units = validated['units']
    return _fetch_weather(city, units)

def _fetch_weather(city: str, units: str) -> dict:
    import requests
    url = f'https://api.weather.example.com/current?city={city}&units={units}'
    response = requests.get(url, headers={'X-API-Key': 'YOUR_KEY'}, timeout=5)
    if response.status_code == 404:
        raise ToolError(ToolErrorCode.NOT_FOUND, f'City not found: {city}')
    response.raise_for_status()
    return response.json()

كتابة ملف README للأداة

يُعد ملف README الواضح ضروريًا لاعتماد الأداة. أدرج فيه: وظيفة الأداة، وأمر التثبيت، ومفاتيح API أو بيانات الاعتماد المطلوبة، وجميع المعلمات مع أنواعها وقيمها الافتراضية، وجميع رموز الأخطاء، ومثال استخدام كامل واحدًا على الأقل.

README_TEMPLATE = '''
# agent-tool-weather

Get real-time weather conditions for any city.

## Installation

pip install agent-tool-weather

## Setup

import os
os.environ["WEATHER_API_KEY"] = "your-key-here"

## Usage

from agent_tool_weather import get_tool_definition, execute

# In your agent
tool_def = get_tool_definition()

# Execute
result = execute({"city": "Tokyo", "units": "celsius"})
print(result)
# {"temperature": 22.5, "condition": "Partly cloudy", "humidity_pct": 65}

## Parameters

| Name  | Type   | Required | Default  | Description     |
|-------|--------|----------|----------|-----------------|
| city  | string | Yes      | -        | City name       |
| units | string | No       | celsius  | celsius or fahrenheit |

## Error Codes

- INVALID_PARAMS: Parameter validation failed
- NOT_FOUND: City not found
- RATE_LIMITED: 60 calls/minute limit exceeded
- UPSTREAM_ERROR: Weather API unavailable
'''

print(README_TEMPLATE[:300])

اختبار أداة قابلة للمشاركة

يجب أن تحتوي الأداة القابلة للمشاركة على اختبارات آلية تغطي: المسار الناجح، وكل رمز من رموز الأخطاء، والحالات الحدية (السلاسل الفارغة والقيم القصوى)، وسلوك تحديد معدل الطلبات. فالاختبارات توثيق؛ إذ توضح بدقة كيفية تصرف الأداة.

import pytest

def test_valid_city_returns_weather(mock_weather_api):
    result = execute({'city': 'London'})
    assert 'temperature' in result
    assert 'condition' in result
    assert isinstance(result['temperature'], (int, float))

def test_missing_required_param_raises_error():
    with pytest.raises(ToolError) as exc_info:
        execute({})  # city is required
    assert exc_info.value.code == ToolErrorCode.INVALID_PARAMS

def test_unknown_city_raises_not_found(mock_weather_api_404):
    with pytest.raises(ToolError) as exc_info:
        execute({'city': 'Atlantis'})
    assert exc_info.value.code == ToolErrorCode.NOT_FOUND

def test_default_units_is_celsius():
    params = validate_tool_input({'city': 'Paris'}, TOOL_SCHEMA_TEMPLATE['parameters'])
    assert params['units'] == 'celsius'

def test_rate_limit_enforced():
    limiter = SlidingWindowRateLimiter(max_calls=2, window_seconds=60)
    limiter.check()  # call 1
    limiter.check()  # call 2
    allowed, _ = limiter.check()  # call 3 — should fail
    assert not allowed

تنسيق متوافق مع استدعاء الدوال في OpenAI

لضمان التوافق مع واجهات API الأصلية لاستخدام الأدوات في OpenAI وClaude، تأكد من أن مخطط أداتك مكتوب بالتنسيق الدقيق الذي تتوقعه واجهات API تلك. تحوّل الطريقة get_openai_schema() مخططك الداخلي إلى التنسيق المتوافق مع OpenAI.

def get_openai_schema(tool_schema: dict) -> dict:
    """Convert internal tool schema to OpenAI function-calling format."""
    return {
        'type': 'function',
        'function': {
            'name': tool_schema['name'],
            'description': tool_schema['description'],
            'parameters': tool_schema['parameters']
        }
    }

def get_anthropic_schema(tool_schema: dict) -> dict:
    """Convert to Anthropic tool format."""
    return {
        'name': tool_schema['name'],
        'description': tool_schema['description'],
        'input_schema': tool_schema['parameters']
    }

# Usage in an agent:
openai_tools = [get_openai_schema(t) for t in [TOOL_SCHEMA_TEMPLATE]]
anthropic_tools = [get_anthropic_schema(t) for t in [TOOL_SCHEMA_TEMPLATE]]
print('OpenAI format:', openai_tools[0]['function']['name'])
print('Anthropic format:', anthropic_tools[0]['name'])

إدارة إصدارات أداتك

استخدم الإصدار الدلالي: MAJOR.MINOR.PATCH. زِد MAJOR عند إجراء تغييرات مكسرة في المخطط، وMINOR عند إضافة معلمات اختيارية، وPATCH لإصلاح الأخطاء. خزّن الإصدار في المخطط وأظهره من خلال get_tool_definition().

VERSION = '1.2.0'

def bump_version(current: str, change_type: str) -> str:
    parts = list(map(int, current.split('.')))
    if change_type == 'major':
        return f'{parts[0]+1}.0.0'
    if change_type == 'minor':
        return f'{parts[0]}.{parts[1]+1}.0'
    if change_type == 'patch':
        return f'{parts[0]}.{parts[1]}.{parts[2]+1}'
    raise ValueError(f'Unknown change_type: {change_type}')

# Breaking change (removed required param) -> major
print(bump_version('1.2.0', 'major'))  # 2.0.0
# Added optional param -> minor
print(bump_version('1.2.0', 'minor'))  # 1.3.0
# Bug fix -> patch
print(bump_version('1.2.0', 'patch'))  # 1.2.1

اختبار المعرفة

أي جزء من مخطط الأداة القابلة للمشاركة يتيح لإطار عمل الوكيل التحقق تلقائيًا من المدخلات قبل استدعاء دالة تنفيذ الأداة؟

مراجعة: تصميم أدوات قابلة للمشاركة للوكلاء

أهم ما تعلمته في هذا الدرس:

  • المخطط: الاسم والوصف والإصدار والمعلمات (JSON Schema) والقيم المعادة وrate_limit
  • رموز الأخطاء: ToolError قياسي يتضمن الرمز والرسالة والتفاصيل
  • التحقق من المدخلات: تتحقق jsonschema من المدخلات مقابل مخطط المعلمات قبل التنفيذ
  • الأمثلة: تُدرج في المخطط باعتبارها سياقًا توضيحيًا قليل اللقطات
  • تحديد معدل الطلبات: نافذة منزلقة مع خطأ RATE_LIMITED وretry_after
  • التغليف: حزمة قابلة للتثبيت باستخدام pip، مع README واختبارات وإدارة للإصدارات

التالي: اكتشاف الإضافات وتحميل الأدوات ديناميكيًا.

الأسئلة الشائعة

هل درس «تصميم أدوات وكلاء قابلة للمشاركة» مجاني؟

نعم — نص درس «تصميم أدوات وكلاء قابلة للمشاركة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Agents، انتقل إلى CoddyKit PRO. تتضمن دورة AI Agents 4 دروس في المجموع.

ماذا ستتعلم في «تصميم أدوات وكلاء قابلة للمشاركة»؟

معايير مخططات الأدوات، ومتطلبات التوثيق، وحزم الأدوات لإعادة استخدامها. تتمرن على AI Agents مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Agents؟

لا تُشترط خبرة سابقة. AI Agents على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «تصميم أدوات وكلاء قابلة للمشاركة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Agents هذا؟

نعم. كل درس في AI Agents يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تصميم أدوات وكلاء قابلة للمشاركة
  2. اكتشاف المكونات الإضافية وتسجيلها
  3. إصدار الأدوات وتوافقها
  4. بناء سوق للمكونات الإضافية للوكلاء
← العودة إلى AI Agents