Function calling — ключ к AI-агентам. Три провайдера, боевые паттерны.
import openai import json from typing import Any client = openai.OpenAI() tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "City name"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["city"] } } }] response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Какая погода в Москве?"}], tools=tools, tool_choice="auto" ) tool_call = response.choices[0].message.tool_calls[0] args = json.loads(tool_call.function.arguments) print(f"Called: {tool_call.function.name} with {args}")
import anthropic from pydantic import BaseModel, Field ac = anthropic.Anthropic() class SearchParams(BaseModel): query: str = Field(..., min_length=1, max_length=500) limit: int = Field(default=5, ge=1, le=20) claude_tools = [{ "name": "web_search", "description": "Search the web for current information", "input_schema": SearchParams.model_json_schema(), }] resp = ac.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, tools=claude_tools, messages=[{"role": "user", "content": "Найди последние новости про AI-агентов"}], ) for block in resp.content: if block.type == "tool_use": validated = SearchParams(**block.input) print(f"Validated search: {validated.query}, limit={validated.limit}")
import asyncio async def call_with_retry(client, messages, tools, max_retries=3): for attempt in range(max_retries): try: resp = await client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="required", ) tc = resp.choices[0].message.tool_calls[0] args = json.loads(tc.function.arguments) assert "city" in args, "Missing required param" return args except Exception as e: print(f"Retry {attempt+1}/{max_retries}: {e}") raise RuntimeError("Failed after all retries")
multi_tools = [ {"type": "function", "function": { "name": "get_weather", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] }}}, {"type": "function", "function": { "name": "get_time", "parameters": { "type": "object", "properties": {"timezone": {"type": "string"}}, "required": ["timezone"] }}}, ] def execute_tools(tool_calls): results = {} for tc in tool_calls: name = tc.function.name args = json.loads(tc.function.arguments) if name == "get_weather": results[name] = {"temp": 22, "condition": "sunny", "city": args["city"]} elif name == "get_time": results[name] = {"time": "14:30", "tz": args["timezone"]} return results resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Погода в Москве и время в Токио?"}], tools=multi_tools, ) results = execute_tools(resp.choices[0].message.tool_calls) print("Parallel results:", results)
class ToolAgent: def __init__(self, client, tools, handlers): self.client = client self.tools = tools self.handlers = handlers def run(self, user_msg, max_turns=5): messages = [{"role": "user", "content": user_msg}] for turn in range(max_turns): resp = self.client.chat.completions.create( model="gpt-4o", messages=messages, tools=self.tools, tool_choice="auto", ) msg = resp.choices[0].message if not msg.tool_calls: return msg.content # Финальный ответ messages.append(msg) for tc in msg.tool_calls: result = self.handlers[tc.function.name](**json.loads(tc.function.arguments)) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": json.dumps(result), }) return "Max turns exceeded" agent = ToolAgent(client, tools, { "get_weather": lambda city, unit="celsius": {"temp": 18, "city": city, "unit": unit}, }) print(agent.run("Какая погода в Берлине? Переведи в фаренгейты."))