Agent 工具開發指南:從設計到最佳化

muzinan110發表於2024-11-19

1. 引言

想象你在組裝一個超級智慧管家機器人(Agent)。這個機器人需要各種工具才能幫你完成任務 - 就像哆啦A夢的百寶袋一樣。本文將教你如何打造這些強大的工具,讓你的 AI 管家更加得心應手。

2. 兩種核心工具設計模式

2.1 同步工具:即問即答模式

想象你在使用一臺自助咖啡機:

  1. 投幣按下"美式咖啡"按鈕
  2. 等待幾秒鐘
  3. 咖啡直接流出來,拿走就能喝

這就是典型的同步工具模式。Agent 呼叫工具後會等待直接得到結果,整個過程快速且簡單。

class WeatherTool(BaseTool):
    """天氣查詢工具 - 同步模式"""
    async def execute(self, city: str) -> dict:
        # 就像按下咖啡機按鈕一樣簡單直接
        weather_data = await self.weather_api.get_current(city)
        return {
            "status": "success",
            "data": {
                "temperature": weather_data.temp,
                "humidity": weather_data.humidity,
                "description": weather_data.desc
            }
        }

適用場景:

  • 快速查詢:天氣、匯率、簡單計算
  • 簡單操作:傳送訊息、開關控制
  • 實時反饋:驗證碼校驗、餘額查詢

2.2 非同步工具:任務追蹤模式

設想你在使用外賣 APP 點餐:

  1. 下單後,APP 給你一個訂單號
  2. 你可以隨時開啟 APP 檢視訂單狀態
  3. 送達時,APP 推送通知告訴你

這就是非同步工具的工作方式。適合那些需要較長時間處理的任務。

class DocumentAnalysisTool(BaseTool):
    """文件分析工具 - 非同步模式"""
    
    async def start_task(self, file_path: str) -> str:
        # 類似下外賣訂單,先返回一個任務ID
        task_id = str(uuid.uuid4())
        await self.task_queue.put({
            "task_id": task_id,
            "file_path": file_path,
            "status": "processing"
        })
        return task_id
    
    async def get_status(self, task_id: str) -> dict:
        # 像檢視外賣訂單狀態一樣
        task = await self.task_store.get(task_id)
        return {
            "task_id": task_id,
            "status": task["status"],
            "progress": task.get("progress", 0),
            "result": task.get("result", None)
        }

適用場景:

  • 耗時操作:大檔案處理、資料分析
  • 多步驟任務:影片渲染、報表生成
  • 需要進度追蹤:模型訓練、批次處理

3. 工具介面標準化:制定通用規範

就像所有電器都遵循統一的插座標準一樣,我們的工具介面也需要標準化。這樣可以確保所有工具都能完美配合 Agent 使用。

3.1 工具描述規範

想象你在寫產品說明書,需要清晰地告訴使用者:

  • 這個工具是做什麼用的
  • 需要提供什麼引數
  • 會返回什麼結果
from pydantic import BaseModel, Field

class ToolSchema(BaseModel):
    """工具說明書模板"""
    name: str = Field(..., description="工具名稱")
    description: str = Field(..., description="工具用途說明")
    parameters: dict = Field(..., description="需要的引數")
    required: List[str] = Field(default_factory=list, description="必填引數")
    
    class Config:
        schema_extra = {
            "example": {
                "name": "天氣查詢",
                "description": "查詢指定城市的天氣資訊",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "城市名稱"
                        }
                    }
                },
                "required": ["city"]
            }
        }

3.2 統一的工具基類

就像所有電器都需要有開關和電源介面一樣,所有工具都需要遵循基本規範:

class BaseTool(ABC):
    """所有工具的基礎模板"""
    
    @abstractmethod
    def get_schema(self) -> ToolSchema:
        """工具說明書"""
        pass
    
    def validate_input(self, params: Dict) -> Dict:
        """引數檢查,就像電器的保險絲"""
        return ToolSchema(**params).dict()
    
    @abstractmethod
    async def execute(self, **kwargs) -> Dict:
        """實際執行功能"""
        pass

4. 錯誤處理:讓工具更可靠

就像家用電器需要防水、防震、防過載一樣,工具也需要完善的保護機制。

4.1 錯誤分類和處理

想象你在處理快遞:

  • 地址寫錯了 → 引數錯誤
  • 系統維護中 → 服務暫時不可用
  • 快遞員太忙了 → 需要限流重試
class ToolError(Exception):
    """工具錯誤基類"""
    def __init__(self, message: str, error_code: str, retry_after: Optional[int] = None):
        self.message = message
        self.error_code = error_code
        self.retry_after = retry_after

@error_handler
async def execute(self, **kwargs):
    try:
        # 執行具體操作
        result = await self._do_work(**kwargs)
        return {"status": "success", "data": result}
    except ValidationError:
        # 引數錯誤,就像地址寫錯了
        return {"status": "error", "code": "INVALID_PARAMS"}
    except RateLimitError as e:
        # 需要限流,就像快遞員太忙
        return {
            "status": "error", 
            "code": "RATE_LIMIT",
            "retry_after": e.retry_after
        }

4.2 重試機制

就像遇到快遞派送失敗會自動安排第二次派送:

class RetryableTool(BaseTool):
    @retry(
        stop=stop_after_attempt(3),  # 最多重試3次
        wait=wait_exponential(multiplier=1, min=4, max=10)  # 等待時間遞增
    )
    async def execute_with_retry(self, **kwargs):
        return await self.execute(**kwargs)

5. 效能最佳化:讓工具更高效

5.1 快取機制

就像便利店會把熱門商品放在顯眼的位置:

class CachedSearchTool(BaseTool):
    def __init__(self):
        self.cache = {}  # 簡單的記憶體快取
        self.cache_ttl = 3600  # 快取1小時
    
    async def execute(self, query: str):
        # 先檢查"貨架"上有沒有
        cache_key = f"search:{query}"
        if cache_key in self.cache:
            return self.cache[cache_key]
        
        # 沒有才去"倉庫"拿
        result = await self._do_search(query)
        self.cache[cache_key] = result
        return result

5.2 併發控制

就像醫院的掛號系統,控制同時服務的人數:

class RateLimiter:
    def __init__(self, max_concurrent: int = 5):
        self._semaphore = Semaphore(max_concurrent)  # 最多同時處理5個請求
    
    @asynccontextmanager
    async def acquire(self):
        async with self._semaphore:
            yield

class ApiTool(BaseTool):
    def __init__(self):
        self.rate_limiter = RateLimiter(max_concurrent=5)
    
    async def execute(self, **kwargs):
        async with self.rate_limiter.acquire():
            return await self._call_api(**kwargs)

6. 測試和文件:保證工具可靠性

6.1 單元測試

就像新產品上市前需要質檢:

class TestWeatherTool:
    @pytest.mark.asyncio
    async def test_normal_weather(self):
        """測試正常天氣查詢"""
        tool = WeatherTool()
        result = await tool.execute(city="北京")
        assert result["status"] == "success"
        assert "temperature" in result["data"]
    
    @pytest.mark.asyncio
    async def test_invalid_city(self):
        """測試無效城市名"""
        tool = WeatherTool()
        result = await tool.execute(city="不存在的城市")
        assert result["status"] == "error"

6.2 文件規範

就像產品說明書要詳細清晰:

class WeatherTool(BaseTool):
    """
    天氣查詢工具
    
    功能:查詢指定城市的實時天氣資訊
    
    使用示例:
    ```python
    tool = WeatherTool()
    result = await tool.execute(city="北京")
    print(f"溫度: {result['data']['temperature']}°C")
    ```
    
    注意事項:
    1. 城市名必須是有效的中國城市名稱
    2. 每分鐘最多查詢 10 次
    """

7. 總結

開發好的 Agent 工具就像打造稱手的工具箱:

  1. 工具分類要合理 - 同步/非同步各有用途
  2. 介面要標準 - 便於統一管理
  3. 要有保護機制 - 處理各種異常情況
  4. 追求高效 - 該快取快取,該限流限流
  5. 重視質量 - 測試充分,文件清晰

記住:好的工具能讓 Agent 事半功倍,糟糕的工具會讓 Agent 處處受限。

相關文章