Function Calling με Python: Πώς τα LLMs Εκτελούν Ενέργειες στον Πραγματικό Κόσμο

Μάθε πώς δουλεύει το function calling σε AI agents, με πρακτικά παραδείγματα σε Python χρησιμοποιώντας τα APIs του OpenAI και της Anthropic. Περιλαμβάνει MCP, Pydantic validation και production tips.

Γιατί το Function Calling Άλλαξε Ριζικά τα AI Agents

Αν δουλεύεις αρκετό καιρό με LLMs, κάποια στιγμή θα χτυπήσεις τον ίδιο τοίχο που χτυπάμε όλοι: τα μοντέλα γλωσσικής νοημοσύνης, από μόνα τους, δεν μπορούν να κάνουν απολύτως τίποτα. Δεν στέλνουν email. Δεν ελέγχουν τον καιρό. Δεν κάνουν query σε βάση δεδομένων. Το μόνο που κάνουν είναι να παράγουν κείμενο — και ας μην πιάσουμε το θέμα με τις "αυτοπεποίθηση" απαντήσεις που είναι εντελώς λάθος.

Αυτό ακριβώς λύνει το function calling (ή tool use, αν προτιμάς τον πιο σύγχρονο όρο). Πρόκειται για τον μηχανισμό που μετατρέπει ένα LLM από παθητική γεννήτρια κειμένου σε μηχανή λήψης αποφάσεων — ένα σύστημα που μπορεί να αποφασίσει τι πρέπει να γίνει, ποια εξωτερική συνάρτηση πρέπει να κληθεί, και με ποιες παραμέτρους.

Και οι αριθμοί δεν ψεύδονται: πάνω από το 78% των production AI agents στο 2026 βασίζονται σε function calling ως τον πυρήνα της αλληλεπίδρασής τους με τον εξωτερικό κόσμο. Δεν είναι πια ένα "nice to have" — είναι η βάση πάνω στην οποία χτίζονται σχεδόν όλα τα σύγχρονα agentic workflows.

Λοιπόν, σε αυτόν τον οδηγό θα δούμε πώς λειτουργεί πραγματικά (spoiler: το LLM δεν εκτελεί τίποτα μόνο του), θα φτιάξουμε λειτουργικούς agents με Python χρησιμοποιώντας τα APIs του OpenAI και της Anthropic, και θα μάθουμε τα production best practices που οι περισσότεροι οδηγοί βολικά παραλείπουν.

Πώς Λειτουργεί Πραγματικά το Function Calling

Ας ξεκαθαρίσουμε κάτι εξαρχής, γιατί αυτή η παρεξήγηση κυκλοφορεί παντού: τα LLMs δεν εκτελούν ποτέ συναρτήσεις. Αυτό που κάνουν είναι να αποφασίζουν ποια συνάρτηση πρέπει να κληθεί και ποιες παραμέτρους να στείλουν — μετά εσύ (δηλαδή ο κώδικάς σου) αναλαμβάνεις την εκτέλεση.

Σκέψου το κάπως έτσι: το LLM είναι ο έξυπνος υπάλληλος στη reception που ξέρει ποιον να καλέσει, αλλά δεν μπορεί να κάνει τη δουλειά μόνος του. Ο πλήρης κύκλος έχει πέντε στάδια:

  1. Ορισμός εργαλείων (Tool Definition) — Περιγράφεις τις διαθέσιμες συναρτήσεις με JSON Schema: όνομα, περιγραφή, παραμέτρους.
  2. Ερώτηση χρήστη — Ο χρήστης υποβάλλει ένα αίτημα (π.χ. "Τι καιρό κάνει στη Θεσσαλονίκη;").
  3. Απόφαση LLM — Το μοντέλο αξιολογεί αν χρειάζεται κάποιο εργαλείο και, αν ναι, επιστρέφει δομημένο JSON αντί για κείμενο.
  4. Εκτέλεση — Ο κώδικάς σου εκτελεί τη συνάρτηση και παίρνει αποτελέσματα.
  5. Τελική απάντηση — Τα αποτελέσματα επιστρέφουν στο LLM, που τα μορφοποιεί σε φυσική γλώσσα.

Αυτός ο κύκλος μπορεί να επαναληφθεί πολλές φορές — κι αυτό ακριβώς κάνει έναν agent agentic. Δεν τρέχει μια φορά και τελειώνει. Επαναλαμβάνει μέχρι να ολοκληρώσει αυτό που του ζήτησες.

Ρύθμιση Περιβάλλοντος

Πριν γράψουμε κώδικα, θα χρειαστούμε τα SDK των δύο μεγαλύτερων providers. Δημιουργούμε ένα virtual environment και εγκαθιστούμε τα βασικά:

python -m venv agent-env
source agent-env/bin/activate

pip install openai anthropic pydantic python-dotenv requests

Και ένα αρχείο .env με τα κλειδιά σου (κράτα τα μακριά από version control, προφανώς):

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

Function Calling με το OpenAI API (Responses API)

Η OpenAI αντικατέστησε το Assistants API με το νέο Responses API τον Αύγουστο 2025. Αν ακόμα χρησιμοποιείς Chat Completions για agents, ήρθε η ώρα να αναβαθμιστείς. Η βασική διαφορά; Το Responses API τρέχει ένα agentic loop ενσωματωμένα, δίνοντάς σου πρόσβαση σε πολλά εργαλεία μέσα σε ένα μόνο API request.

Ας φτιάξουμε έναν agent που ελέγχει τιμές μετοχών:

import json
import requests
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
client = OpenAI()

# Βήμα 1: Ορισμός εργαλείων με JSON Schema
tools = [
    {
        "type": "function",
        "name": "get_stock_price",
        "description": "Get the current stock price for a given ticker symbol",
        "parameters": {
            "type": "object",
            "properties": {
                "ticker": {
                    "type": "string",
                    "description": "Stock ticker symbol, e.g. AAPL, GOOGL"
                }
            },
            "required": ["ticker"],
            "additionalProperties": False
        },
        "strict": True  # Εξασφαλίζει σωστό schema
    },
    {
        "type": "function",
        "name": "compare_stocks",
        "description": "Compare two stocks and return the one with higher price",
        "parameters": {
            "type": "object",
            "properties": {
                "ticker_a": {"type": "string", "description": "First ticker"},
                "ticker_b": {"type": "string", "description": "Second ticker"}
            },
            "required": ["ticker_a", "ticker_b"],
            "additionalProperties": False
        },
        "strict": True
    }
]

# Βήμα 2: Υλοποίηση συναρτήσεων
def get_stock_price(ticker: str) -> dict:
    """Simulated stock price lookup."""
    prices = {"AAPL": 242.50, "GOOGL": 191.30, "MSFT": 458.20, "NVDA": 138.70}
    price = prices.get(ticker.upper())
    if price:
        return {"ticker": ticker.upper(), "price": price, "currency": "USD"}
    return {"error": f"Unknown ticker: {ticker}"}

def compare_stocks(ticker_a: str, ticker_b: str) -> dict:
    a = get_stock_price(ticker_a)
    b = get_stock_price(ticker_b)
    if "error" in a or "error" in b:
        return {"error": "Could not compare — invalid ticker"}
    winner = ticker_a if a["price"] > b["price"] else ticker_b
    return {"higher": winner, "prices": {ticker_a: a["price"], ticker_b: b["price"]}}

# Registry αντιστοίχισης ονομάτων → συναρτήσεων
TOOL_REGISTRY = {
    "get_stock_price": get_stock_price,
    "compare_stocks": compare_stocks,
}

# Βήμα 3: Agentic loop
def run_agent(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]

    while True:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
        )

        choice = response.choices[0]

        # Αν δεν υπάρχουν tool calls, επέστρεψε την απάντηση
        if not choice.message.tool_calls:
            return choice.message.content

        # Πρόσθεσε την απάντηση του assistant στο ιστορικό
        messages.append(choice.message)

        # Εκτέλεσε κάθε tool call
        for tool_call in choice.message.tool_calls:
            fn_name = tool_call.function.name
            fn_args = json.loads(tool_call.function.arguments)

            fn = TOOL_REGISTRY.get(fn_name)
            if fn is None:
                result = {"error": f"Unknown function: {fn_name}"}
            else:
                result = fn(**fn_args)

            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })

    return "Max iterations reached"

# Χρήση
print(run_agent("Ποια μετοχή έχει μεγαλύτερη τιμή, η Apple ή η Google;"))

Πρόσεξε τη χρήση strict: True — είναι ίσως η σημαντικότερη αλλαγή του 2026 στο OpenAI API. Εγγυάται ότι τα arguments που παράγει το μοντέλο ακολουθούν πιστά το JSON Schema σου. Χωρίς αυτό, μπορεί κάλλιστα να πάρεις "ticker": "apple" αντί για "ticker": "AAPL" — κι αυτά τα μικρά bugs είναι που σε τρελαίνουν στο debugging.

Function Calling με το Claude API (Anthropic)

Η Anthropic ακολουθεί μια ελαφρώς διαφορετική φιλοσοφία, αλλά η βασική αρχιτεκτονική είναι σχεδόν ίδια. Η κύρια διαφορά: στο Claude API, τα εργαλεία ορίζονται με input_schema αντί για parameters, και τα αποτελέσματα επιστρέφονται ως content blocks τύπου tool_result.

import json
import anthropic
from dotenv import load_dotenv

load_dotenv()
client = anthropic.Anthropic()

# Ορισμός εργαλείων για Claude
tools = [
    {
        "name": "search_database",
        "description": "Search a product database by name or category. Returns matching products with prices.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search query — product name or category"
                },
                "max_results": {
                    "type": "integer",
                    "description": "Maximum results to return (1-20)",
                    "default": 5
                }
            },
            "required": ["query"]
        }
    },
    {
        "name": "place_order",
        "description": "Place an order for a product. Requires product_id and quantity.",
        "input_schema": {
            "type": "object",
            "properties": {
                "product_id": {"type": "string", "description": "The product ID"},
                "quantity": {"type": "integer", "description": "Number of items"}
            },
            "required": ["product_id", "quantity"]
        }
    }
]

# Υλοποίηση εργαλείων
def search_database(query: str, max_results: int = 5) -> str:
    products = [
        {"id": "P001", "name": "Mechanical Keyboard", "price": 89.99},
        {"id": "P002", "name": "USB-C Hub", "price": 45.00},
        {"id": "P003", "name": "Monitor Arm", "price": 120.00},
    ]
    results = [p for p in products if query.lower() in p["name"].lower()]
    return json.dumps(results[:max_results])

def place_order(product_id: str, quantity: int) -> str:
    return json.dumps({
        "order_id": "ORD-2026-1234",
        "product_id": product_id,
        "quantity": quantity,
        "status": "confirmed"
    })

TOOL_REGISTRY = {
    "search_database": search_database,
    "place_order": place_order,
}

# Agentic loop για Claude
def run_claude_agent(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            tools=tools,
            messages=messages,
        )

        # Έλεγξε αν χρειάζεται tool use
        if response.stop_reason == "end_turn":
            # Εξαγωγή κειμένου
            for block in response.content:
                if hasattr(block, "text"):
                    return block.text

        # Πρόσθεσε assistant response στο ιστορικό
        messages.append({"role": "assistant", "content": response.content})

        # Εκτέλεσε tool calls
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                fn = TOOL_REGISTRY.get(block.name)
                if fn:
                    result = fn(**block.input)
                else:
                    result = json.dumps({"error": f"Unknown tool: {block.name}"})

                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result
                })

        messages.append({"role": "user", "content": tool_results})

    return "Max iterations reached"

# Χρήση
print(run_claude_agent("Ψάξε για keyboard και παράγγειλε 2 τεμάχια"))

Βασικές Διαφορές OpenAI vs Claude

Αν δουλεύεις και με τα δύο APIs (κάτι που γίνεται ολοένα πιο συνηθισμένο σε production), να οι κρίσιμες διαφορές που πρέπει να ξέρεις:

  • Ορισμός εργαλείων: Στο OpenAI χρησιμοποιείς parameters με strict: True. Στο Claude χρησιμοποιείς input_schema.
  • Stop reason: Στο OpenAI ελέγχεις finish_reason == "tool_calls". Στο Claude ελέγχεις stop_reason == "tool_use".
  • Tool results: Στο OpenAI στέλνεις μήνυμα με role: "tool". Στο Claude στέλνεις tool_result content blocks μέσα σε user message.
  • Parallel calls: Και τα δύο υποστηρίζουν παράλληλες κλήσεις εργαλείων. Στο OpenAI μπορείς να τις απενεργοποιήσεις με parallel_tool_calls=false.

Validation με Pydantic: Μη Δίνεις Ελεύθερο Πεδίο στο LLM

Εδώ μπαίνουμε σε ένα θέμα που οι περισσότεροι tutorials αγνοούν εντελώς: η επικύρωση δεδομένων εισόδου. Ειλικρινά, αυτό είναι που ξεχωρίζει ένα demo project από ένα production-ready σύστημα.

Το LLM μπορεί να παράξει οποιοδήποτε JSON θέλει. Ακόμα και με strict: True, τα δεδομένα μπορεί να είναι συντακτικά σωστά αλλά λογικά λάθος — σκέψου αρνητική ποσότητα σε παραγγελία ή product ID που δεν υπάρχει.

from pydantic import BaseModel, Field, field_validator

class OrderRequest(BaseModel):
    product_id: str = Field(..., pattern=r"^P\d{3}$")
    quantity: int = Field(..., ge=1, le=100)

    @field_validator("product_id")
    @classmethod
    def validate_product_exists(cls, v):
        valid_ids = {"P001", "P002", "P003"}
        if v not in valid_ids:
            raise ValueError(f"Product {v} not found in catalog")
        return v

def safe_place_order(product_id: str, quantity: int) -> str:
    try:
        validated = OrderRequest(product_id=product_id, quantity=quantity)
        return json.dumps({
            "order_id": "ORD-2026-5678",
            "product_id": validated.product_id,
            "quantity": validated.quantity,
            "status": "confirmed"
        })
    except Exception as e:
        return json.dumps({"error": str(e)})

Η συμβουλή μου: χρησιμοποίησε πάντα Pydantic models αντί για raw JSON Schema validation. Παίρνεις automatic schema generation, runtime validation, και IDE support — και αποφεύγεις τα κενά ασφαλείας που δεν ήξερες ότι υπήρχαν.

Το Model Context Protocol (MCP): Ο Ενιαίος Τρόπος Σύνδεσης Εργαλείων

Αν κάθε API provider έχει τον δικό του τρόπο ορισμού εργαλείων (και τον έχει — μόλις το είδαμε παραπάνω), τότε αναγκαστικά γράφεις κώδικα ενοποίησης ξεχωριστά για κάθε provider. Αυτό ακριβώς λύνει το Model Context Protocol (MCP) — ένα ανοικτό πρωτόκολλο που ξεκίνησε η Anthropic τον Νοέμβριο 2024 και πλέον φιλοξενείται στο Linux Foundation.

Η πιο εύστοχη αναλογία; Σκέψου το σαν "USB-C για AI εφαρμογές". Ορίζεις τα εργαλεία σου μία φορά ως MCP server, και κάθε MCP-compatible client (Claude, ChatGPT, Gemini, τοπικά μοντέλα) μπορεί να τα χρησιμοποιήσει χωρίς αλλαγές.

Μερικές σημαντικές εξελίξεις του 2026 που αξίζει να γνωρίζεις:

  • Tool Output Schemas — Ο client γνωρίζει πλέον εκ των προτέρων τη μορφή των αποτελεσμάτων κάθε εργαλείου, βελτιώνοντας δραματικά τη χρήση του context window.
  • OAuth Resource Servers — Οι MCP servers ταξινομούνται τώρα ως OAuth Resource Servers, λύνοντας τα ζητήματα authentication που ταλαιπωρούσαν τις πρώτες υλοποιήσεις.
  • Streamable HTTP transport — Αντικατέστησε τα SSE-based transports, προσφέροντας stateless λειτουργία πίσω από load balancers.
# Παράδειγμα MCP server με Python (mcp SDK)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("product-catalog")

@mcp.tool()
def search_products(query: str, max_results: int = 5) -> list[dict]:
    """Search the product catalog by name or category."""
    products = [
        {"id": "P001", "name": "Mechanical Keyboard", "price": 89.99},
        {"id": "P002", "name": "USB-C Hub", "price": 45.00},
    ]
    return [p for p in products if query.lower() in p["name"].lower()][:max_results]

@mcp.tool()
def get_product_details(product_id: str) -> dict:
    """Get detailed information about a specific product."""
    catalog = {
        "P001": {"id": "P001", "name": "Mechanical Keyboard", "price": 89.99,
                 "stock": 150, "category": "peripherals"},
    }
    return catalog.get(product_id, {"error": "Product not found"})

if __name__ == "__main__":
    mcp.run(transport="streamable-http", port=8080)

Ο παραπάνω server μπορεί τώρα να συνδεθεί σε οποιοδήποτε MCP client — Claude Desktop, VS Code, ή ο δικός σου custom agent — χωρίς να αλλάξεις ούτε μία γραμμή κώδικα στο backend. Αρκετά κομψό, αν μου επιτρέπεις.

Best Practices για Production Function Calling

Αφού φτιάξεις τον πρώτο σου agent και αρχίσεις να χαίρεσαι, ήρθε η ώρα να τον κάνεις πραγματικά αξιόπιστο. Τα παρακάτω μαθήματα βγαίνουν μόνο από πραγματική εμπειρία σε production — δεν τα βρίσκεις εύκολα σε tutorials.

1. Κράτα τα εργαλεία λίγα και εστιασμένα

Η γενική σύσταση για το 2026: λιγότερα από 20 εργαλεία ανά κύκλο. Αν χρειάζεσαι περισσότερα, χρησιμοποίησε tool search (δυναμική φόρτωση εργαλείων βάσει context) αντί να τα δίνεις όλα μαζί στο μοντέλο. Πολλά εργαλεία αυξάνουν σημαντικά τον κίνδυνο λανθασμένης επιλογής.

2. Γράψε περιγραφές σαν documentation

Η περιγραφή του εργαλείου είναι ολόκληρο το context που έχει το LLM για να αποφασίσει αν θα το χρησιμοποιήσει. Μην τσιγκουνεύεσαι τις λέξεις εδώ. Εξήγησε πότε πρέπει να χρησιμοποιηθεί, τι κάνει, και ποιους περιορισμούς έχει.

3. Πρόσθεσε timeout σε κάθε εξωτερική κλήση

Αυτό μπορεί να φαίνεται αυτονόητο, αλλά θα εκπλαγείς πόσοι agents σε production τρέχουν χωρίς timeouts.

import asyncio

async def safe_tool_call(fn, args, timeout_seconds=10):
    try:
        result = await asyncio.wait_for(fn(**args), timeout=timeout_seconds)
        return result
    except asyncio.TimeoutError:
        return {"error": f"Tool call timed out after {timeout_seconds}s"}
    except Exception as e:
        return {"error": f"Tool execution failed: {str(e)}"}

4. Εφάρμοσε Human-in-the-Loop για κρίσιμες ενέργειες

Αν ένα εργαλείο κάνει κάτι μη αναστρέψιμο (διαγραφή δεδομένων, πληρωμή, αποστολή email), μην το αφήνεις να τρέχει αυτόματα. Σοβαρά, μην το κάνεις. Πρόσθεσε ένα επίπεδο επιβεβαίωσης:

HIGH_RISK_TOOLS = {"delete_record", "send_email", "process_payment"}

def execute_tool(name: str, args: dict) -> str:
    if name in HIGH_RISK_TOOLS:
        print(f"Tool '{name}' requires approval.")
        print(f"  Args: {json.dumps(args, indent=2)}")
        confirm = input("Approve? (yes/no): ")
        if confirm.lower() != "yes":
            return json.dumps({"error": "Action rejected by user"})

    fn = TOOL_REGISTRY.get(name)
    return fn(**args) if fn else json.dumps({"error": "Unknown tool"})

5. Κάνε logging σε κάθε tool call

Σε production, κάθε tool call πρέπει να καταγράφεται. Χωρίς logs, δεν μπορείς να κάνεις debug έναν agent — βλέπεις μια λάθος τελική απάντηση και δεν έχεις ιδέα πού πήγε στραβά στην αλυσίδα.

import logging
from datetime import datetime, timezone

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("agent")

def logged_tool_call(name: str, args: dict) -> str:
    start = datetime.now(timezone.utc)
    logger.info(f"Tool call: {name} | Args: {args}")

    try:
        result = TOOL_REGISTRY[name](**args)
        duration = (datetime.now(timezone.utc) - start).total_seconds()
        logger.info(f"Tool result: {name} | Duration: {duration:.2f}s | OK")
        return result
    except Exception as e:
        duration = (datetime.now(timezone.utc) - start).total_seconds()
        logger.error(f"Tool error: {name} | Duration: {duration:.2f}s | {e}")
        return json.dumps({"error": str(e)})

6. Μην βάζεις ποτέ sensitive data στα tool definitions

API keys, tokens και κωδικοί πρέπει να φορτώνονται από environment variables μέσα στη συνάρτηση — ποτέ μέσα στο JSON Schema του εργαλείου. Τα tool definitions περνούν μέσα από το context window του LLM, οπότε αν βάλεις ένα κλειδί εκεί, ουσιαστικά το στέλνεις στον provider. Κάτι που προφανώς δεν θέλεις.

Πότε ΔΕΝ Πρέπει να Χρησιμοποιήσεις Function Calling

Όσο ισχυρό κι αν είναι, δεν είναι πάντα η σωστή λύση. Να μερικές περιπτώσεις:

  • Αν το αποτέλεσμα είναι στατικό — Αν η πληροφορία δεν αλλάζει (π.χ. μαθηματικοί τύποι, σταθερά δεδομένα), βάλτην στο system prompt αντί να φτιάξεις εργαλείο.
  • Αν χρειάζεσαι μόνο δομημένο output — Χρησιμοποίησε Structured Outputs αντί για function calling. Πιο απλό, πιο αξιόπιστο, λιγότερα tokens.
  • Αν η εκτέλεση είναι εξαιρετικά αργή — Εργαλεία που χρειάζονται πάνω από 30 δευτερόλεπτα δημιουργούν τρομερά κακή εμπειρία χρήστη. Σκέψου asynchronous patterns σε αυτή την περίπτωση.
  • Αν το RAG αρκεί — Αν χρειάζεσαι μόνο ανάκτηση πληροφορίας από ένα σύνολο εγγράφων, ένα RAG pipeline μπορεί να είναι πιο αποδοτικό (και φθηνότερο).

Συχνές Ερωτήσεις (FAQ)

Ποια είναι η διαφορά μεταξύ function calling και tool use;

Στην πράξη, χρησιμοποιούνται εναλλακτικά. Ο όρος "tool use" είναι ο πιο σύγχρονος και γενικός, καλύπτοντας custom functions, built-in εργαλεία (code interpreter, web search) και μηχανισμούς ανάκτησης δεδομένων. Ο όρος "function calling" αναφέρεται πιο συγκεκριμένα στην κλήση custom συναρτήσεων μέσω structured JSON.

Μπορεί ένα LLM να εκτελέσει κώδικα μόνο του μέσω function calling;

Κατηγορηματικά, όχι. Αυτό είναι ίσως η πιο συχνή παρανόηση εκεί έξω. Το LLM δεν εκτελεί τίποτα — παράγει μόνο τα arguments σε JSON format. Ο κώδικάς σου (ή ο framework σου) αναλαμβάνει την πραγματική εκτέλεση. Αν ένα SDK φαίνεται να "εκτελεί" αυτόματα, υπάρχει ένα layer λογισμικού γύρω από το LLM που κάνει αυτό ακριβώς το βήμα.

Πόσα εργαλεία μπορώ να δώσω σε ένα LLM ταυτόχρονα;

Τεχνικά, δεν υπάρχει σκληρό όριο — αλλά πρακτικά, κράτα τα κάτω από 20 ανά κύκλο. Πάνω από αυτό, αυξάνεται σημαντικά ο κίνδυνος να επιλέξει λάθος εργαλείο. Αν η εφαρμογή σου χρειάζεται περισσότερα, χρησιμοποίησε tool search ή κατηγοριοποίηση εργαλείων.

Τι είναι το MCP και πρέπει να το χρησιμοποιώ;

Το Model Context Protocol είναι ένα ανοιχτό πρωτόκολλο τυποποίησης για τη σύνδεση AI μοντέλων με εξωτερικά εργαλεία. Αν φτιάχνεις εργαλεία που θέλεις να δουλεύουν με πολλαπλούς providers (OpenAI, Anthropic, Google), τότε ναι, σου εξοικονομεί πολύ χρόνο. Αν όμως δουλεύεις αποκλειστικά με έναν provider, το native API αρκεί μια χαρά.

Πώς μπορώ να κάνω debug έναν agent που δεν επιλέγει τα σωστά εργαλεία;

Τρεις βασικές τεχνικές, με σειρά προτεραιότητας: πρώτον, βελτίωσε τις περιγραφές των εργαλείων — αν το μοντέλο δεν καταλαβαίνει πότε να χρησιμοποιήσει ένα εργαλείο, η περιγραφή χρειάζεται δουλειά. Δεύτερον, πρόσθεσε οδηγίες στο system prompt για πότε και πώς πρέπει να χρησιμοποιεί κάθε εργαλείο. Τρίτον, κάνε log κάθε tool call και ψάξε patterns — αν επιλέγει συστηματικά λάθος εργαλείο, πιθανότατα τα ονόματα ή τα schemas μπερδεύονται μεταξύ τους.

Σχετικά με τον Συγγραφέα Editorial Team

Our team of expert writers and editors.