Używamy plików cookie. Pliki cookie i pamięć przeglądarki pozwalają utrzymać logowanie i zapamiętać Twoje preferencje. Za Twoją zgodą mierzymy też, jak korzystasz ze strony. W każdej chwili możesz . Zobacz naszą politykę cookie.

Przewodnik: agenci wywołujący narzędzia

Pierwsze kroki

Przewodnik: agenci wywołujący narzędzia

Zbuduj pętlę agenta z równoległymi wywołaniami narzędzi, limitem kroków, ustrukturyzowaną odpowiedzią końcową i kontrolą kosztów.

Agent to pętla: model czyta rozmowę, decyduje o wywołaniu jednego lub kilku Twoich narzędzi, Ty je uruchamiasz i odsyłasz wyniki, a model kontynuuje, dopóki nie może odpowiedzieć. api14 obsługuje to w formacie OpenAI tools na każdym modelu z obsługą narzędzi, więc ten sam kod działa z OpenAI, Anthropic, Google i innymi dostawcami po zmianie identyfikatora modelu.

Ten przewodnik buduje małego agenta z limitem kroków, równoległym wykonywaniem narzędzi, ustrukturyzowaną odpowiedzią końcową i kontrolą kosztów. Kod używa oficjalnego pakietu openai z bazowym adresem URL https://api14.com/api/v1.

Zdefiniuj narzędzia

Każde narzędzie to nazwa, opis czytany przez model i JSON Schema jego argumentów. Pisz konkretne opisy; to jedyna dokumentacja, jaką ma model.

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api14.com/api/v1", api_key=os.environ["API14_API_KEY"])

def search_orders(customer_email: str) -> list:
    return [{"id": "A-1001", "status": "shipped"}]  # your database query

def get_shipment(order_id: str) -> dict:
    return {"order_id": order_id, "carrier": "UPS", "eta": "2026-10-09"}  # your API call

TOOLS = {"search_orders": search_orders, "get_shipment": get_shipment}

tools = [
    {"type": "function", "function": {
        "name": "search_orders",
        "description": "List a customer's orders with their status.",
        "parameters": {"type": "object", "properties": {"customer_email": {"type": "string"}},
                       "required": ["customer_email"]},
    }},
    {"type": "function", "function": {
        "name": "get_shipment",
        "description": "Carrier and estimated delivery date for one order.",
        "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
                       "required": ["order_id"]},
    }},
]

Pętla z limitem kroków

Każda iteracja to osobne, rozliczane żądanie, więc zawsze ograniczaj liczbę kroków. Błędy narzędzi zwracaj modelowi jako treść zamiast przerywać działanie; modele zwykle radzą sobie, poprawiając argumenty.

MAX_STEPS = 8

def run_tool(call):
    try:
        args = json.loads(call.function.arguments)
        result = TOOLS[call.function.name](**args)
    except Exception as e:  # bad JSON, unknown tool, or your function failed
        result = {"error": str(e)}
    return {"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)}

def agent(task, model="openai/gpt-5-mini"):
    messages = [
        {"role": "system", "content": "You are a support agent. Use tools to look things up. Be brief."},
        {"role": "user", "content": task},
    ]
    spent = 0.0
    for step in range(MAX_STEPS):
        r = client.chat.completions.create(model=model, messages=messages, tools=tools, max_tokens=1000)
        spent += r.usage.cost
        msg = r.choices[0].message
        if not msg.tool_calls:
            return msg.content, spent
        messages.append(msg)
        messages.extend(run_tool(call) for call in msg.tool_calls)
    raise RuntimeError(f"No answer after {MAX_STEPS} steps (spent ${spent:.4f})")

print(agent("Where is my order? My email is ada@example.com"))

Wysyłaj tools w każdym żądaniu pętli. Wielu dostawców odrzuca wiadomość tool, gdy żądanie nie zawiera definicji narzędzi.

Równoległe wywołania narzędzi

Przy parallel_tool_calls (domyślnie włączonym tam, gdzie jest obsługiwane) model może zażądać kilku wywołań w jednej turze, na przykład get_shipment dla trzech zamówień. Uruchom je współbieżnie i dołącz po jednej wiadomości tool na wywołanie, w dowolnej kolejności; tool_call_id łączy każdy wynik z jego wywołaniem. W Node:

import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api14.com/api/v1", apiKey: process.env.API14_API_KEY });
const MAX_STEPS = 8;

async function agent(messages, tools, impl, model = "openai/gpt-5-mini") {
  for (let step = 0; step < MAX_STEPS; step++) {
    const res = await client.chat.completions.create({ model, messages, tools, max_tokens: 1000 });
    const msg = res.choices[0].message;
    if (!msg.tool_calls?.length) return msg.content;

    messages.push(msg);
    const results = await Promise.all(msg.tool_calls.map(async (call) => {
      let content;
      try {
        content = JSON.stringify(await impl[call.function.name](JSON.parse(call.function.arguments)));
      } catch (err) {
        content = JSON.stringify({ error: String(err) });
      }
      return { role: "tool", tool_call_id: call.id, content };
    }));
    messages.push(...results);
  }
  throw new Error(`No answer after ${MAX_STEPS} steps`);
}

Jeśli Twoje narzędzia mają skutki uboczne, które muszą zachodzić po kolei (utwórz, potem zaktualizuj), ustaw parallel_tool_calls: false, aby model prosił o jedno wywołanie na turę.

Ustrukturyzowana odpowiedź końcowa

Gdy odpowiedź agenta zasila inny kod, poproś o JSON zgodny ze schematem. Prosty, przenośny wzorzec to jedno dodatkowe wywołanie po tym, jak pętla zbierze fakty (messages to tutaj rozmowa z pętli; zwróć ją z agent() razem z odpowiedzią):

schema = {
    "type": "object",
    "properties": {
        "order_id": {"type": "string"},
        "status": {"type": "string", "enum": ["processing", "shipped", "delivered", "unknown"]},
        "eta": {"type": ["string", "null"]},
        "reply": {"type": "string"},
    },
    "required": ["order_id", "status", "eta", "reply"],
    "additionalProperties": False,
}

final = client.chat.completions.create(
    model="openai/gpt-5-mini",
    messages=messages + [{"role": "user", "content": "Summarize the result for our ticket system."}],
    tools=tools,
    tool_choice="none",
    response_format={"type": "json_schema", "json_schema": {"name": "ticket", "strict": True, "schema": schema}},
)
ticket = json.loads(final.choices[0].message.content)

tool_choice: "none" zostawia tools w żądaniu (jest to potrzebne, bo historia zawiera wiadomości narzędzi), a jednocześnie blokuje kolejne wywołania. Zawsze waliduj sparsowany JSON. Zobacz Ustrukturyzowane wyjście.

Wybór modelu

  • Możliwości: model musi mieć tools w supported_parameters. Sprawdź to przez GET /models, zobacz Modele. Dla kroku końcowego szukaj structured_outputs lub response_format.
  • Routing: dodaj provider: { "require_parameters": true } (w Pythonie przez extra_body), aby żądania trafiały tylko do dostawców obsługujących każdy wysyłany parametr. Zobacz Routing dostawców.
  • Rozmiar: małe modele, takie jak openai/gpt-5-mini, niezawodnie i tanio obsługują kilka dobrze opisanych narzędzi. Przejdź na większy model, gdy agent ma wiele narzędzi, długie wieloetapowe plany lub wciąż wybiera złe narzędzie.
  • Testuj na swoich narzędziach: uruchom te same 20–50 prawdziwych zadań na dwóch lub trzech kandydatach i porównaj skuteczność, liczbę kroków na zadanie i łączny usage.cost.
curl -s "https://api14.com/api/v1/models?category=text" \
  | jq -r '.data[] | select(.supported_parameters | index("tools")) | "\(.id)  \(.pricing.prompt)"'

Kontrola kosztów

Agent może wykonać wiele żądań dla jednego zadania, a cała rozmowa jest wysyłana ponownie w każdym kroku. Ogranicz wydatki na kilku poziomach:

  • Limit kroków (MAX_STEPS) i max_tokens na żądanie, jak powyżej.
  • Budżet na zadanie: sumuj usage.cost ze wszystkich kroków i zatrzymaj się, gdy przekroczy Twój próg.
  • Krótkie wyniki narzędzi: zwracaj tylko pola, których model potrzebuje. Duże wyniki narzędzi są opłacane jako tokeny wejściowe w każdym kolejnym kroku.
  • Dzienny limit USD na klucz: utwórz dedykowany klucz dla agenta w kluczach API i ustaw dzienny limit. Po jego osiągnięciu żądania zwracają 429 daily_limit_reached do następnego dnia UTC; obsłuż ten kod, zatrzymując agenta, a nie ponawiając.
  • Lista dozwolonych modeli: ogranicz klucz do przetestowanych modeli; wszystko inne zwraca 403 model_not_allowed.
  • Cache promptów: utrzymuj prompt systemowy i definicje narzędzi identyczne i na początku każdego żądania, aby zadziałały zniżki za prefiks z cache. Zobacz Cache promptów.

Zobacz Wywoływanie narzędzi, Limity zapytań i Błędy.