Wiki IA
LLM et IA Générative

Agents et Function Calling

Étendre les capacités des LLM avec des outils et des actions

Les agents IA combinent la puissance de raisonnement des LLM avec la capacité d'exécuter des actions dans le monde réel via des outils (tools) et le function calling.

Du LLM à l'Agent

Les limites des LLM seuls

LLM CLASSIQUE :
┌─────────────────────────────────────────────────────────────┐
│  Input (texte) → LLM → Output (texte)                       │
│                                                              │
│  Limites :                                                   │
│  ├── Pas d'accès à des données en temps réel               │
│  ├── Pas de capacité de calcul précis                       │
│  ├── Pas d'interaction avec des systèmes externes          │
│  └── Connaissances figées à la date d'entraînement         │
└─────────────────────────────────────────────────────────────┘

L'approche Agent

AGENT IA :
┌─────────────────────────────────────────────────────────────┐
│                                                              │
│  Utilisateur : "Quel temps fait-il à Paris ?"               │
│            ↓                                                 │
│       ┌─────────┐                                           │
│       │   LLM   │ ← Raisonne et décide d'utiliser un outil  │
│       └────┬────┘                                           │
│            ↓                                                 │
│  ┌─────────────────┐                                        │
│  │ Function Call   │ → get_weather(city="Paris")           │
│  └────────┬────────┘                                        │
│            ↓                                                 │
│  ┌─────────────────┐                                        │
│  │  API Météo      │ → {"temp": 18, "conditions": "sunny"}  │
│  └────────┬────────┘                                        │
│            ↓                                                 │
│       ┌─────────┐                                           │
│       │   LLM   │ ← Formule la réponse avec les données     │
│       └────┬────┘                                           │
│            ↓                                                 │
│  "Il fait 18°C à Paris avec un temps ensoleillé."           │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Function Calling

Concept

Le function calling permet au LLM de :

  1. Reconnaître quand un outil est nécessaire
  2. Générer les paramètres corrects pour appeler une fonction
  3. Utiliser le résultat pour formuler sa réponse

Implémentation avec OpenAI

from openai import OpenAI
import json

client = OpenAI()

# Définir les outils disponibles
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Obtenir la météo actuelle d'une ville",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Le nom de la ville"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "L'unité de température"
                    }
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "Rechercher des informations sur le web",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "La requête de recherche"
                    }
                },
                "required": ["query"]
            }
        }
    }
]

# Implémentation des fonctions
def get_weather(city: str, unit: str = "celsius") -> dict:
    # Appeler une API météo réelle
    return {"city": city, "temperature": 18, "unit": unit, "conditions": "ensoleillé"}

def search_web(query: str) -> str:
    # Appeler une API de recherche
    return f"Résultats pour '{query}': ..."

# Mapping des fonctions
available_functions = {
    "get_weather": get_weather,
    "search_web": search_web
}

def run_conversation(user_message: str):
    messages = [{"role": "user", "content": user_message}]

    # Premier appel au LLM
    response = client.chat.completions.create(
        model="gpt-4",
        messages=messages,
        tools=tools,
        tool_choice="auto"
    )

    response_message = response.choices[0].message

    # Vérifier si le modèle veut appeler une fonction
    if response_message.tool_calls:
        messages.append(response_message)

        for tool_call in response_message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)

            # Appeler la fonction
            function_response = available_functions[function_name](**function_args)

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

        # Deuxième appel pour la réponse finale
        final_response = client.chat.completions.create(
            model="gpt-4",
            messages=messages
        )

        return final_response.choices[0].message.content

    return response_message.content

# Utilisation
response = run_conversation("Quel temps fait-il à Paris ?")
print(response)

Implémentation avec Anthropic Claude

from anthropic import Anthropic
import json

client = Anthropic()

# Définir les outils
tools = [
    {
        "name": "get_weather",
        "description": "Obtenir la météo actuelle d'une ville",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "Le nom de la ville"
                }
            },
            "required": ["city"]
        }
    },
    {
        "name": "calculator",
        "description": "Effectuer des calculs mathématiques",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "L'expression mathématique à calculer"
                }
            },
            "required": ["expression"]
        }
    }
]

def process_tool_call(tool_name: str, tool_input: dict) -> str:
    if tool_name == "get_weather":
        return json.dumps({"temperature": 22, "conditions": "nuageux"})
    elif tool_name == "calculator":
        # Utiliser une bibliothèque de calcul sécurisée
        import numexpr
        result = numexpr.evaluate(tool_input["expression"])
        return json.dumps({"result": float(result)})

def run_agent(user_message: str):
    messages = [{"role": "user", "content": user_message}]

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

        if response.stop_reason == "end_turn":
            for block in response.content:
                if hasattr(block, 'text'):
                    return block.text

        if response.stop_reason == "tool_use":
            messages.append({"role": "assistant", "content": response.content})

            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = process_tool_call(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    })

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

# Utilisation
response = run_agent("Calcule 15% de 250 et dis-moi la météo à Lyon")
print(response)

Architecture d'un Agent

Composants principaux

┌─────────────────────────────────────────────────────────────┐
│                      AGENT IA                               │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐     │
│  │   MÉMOIRE   │    │     LLM     │    │   OUTILS    │     │
│  │             │    │  (Cerveau)  │    │             │     │
│  │ • Court     │←──→│ • Raisonne  │←──→│ • APIs      │     │
│  │   terme     │    │ • Planifie  │    │ • Bases de  │     │
│  │ • Long      │    │ • Décide    │    │   données   │     │
│  │   terme     │    │             │    │ • Code      │     │
│  └─────────────┘    └─────────────┘    └─────────────┘     │
│         ↑                  ↑                  ↑             │
│         └──────────────────┼──────────────────┘             │
│                            │                                 │
│                    ┌───────┴───────┐                        │
│                    │  ORCHESTRATEUR │                        │
│                    │  (Boucle)      │                        │
│                    └───────────────┘                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

La boucle ReAct

ReAct (Reasoning + Acting) est un pattern populaire pour les agents :

BOUCLE ReAct :

1. THOUGHT (Réflexion)
   "Je dois trouver la météo de Paris"

2. ACTION
   get_weather(city="Paris")

3. OBSERVATION
   {"temperature": 18, "conditions": "sunny"}

4. THOUGHT
   "J'ai la météo, je peux répondre"

5. ANSWER
   "Il fait 18°C à Paris..."

(Répéter si nécessaire)
class ReActAgent:
    def __init__(self, llm, tools):
        self.llm = llm
        self.tools = tools
        self.max_iterations = 10

    def run(self, task: str) -> str:
        thoughts = []
        observations = []

        for i in range(self.max_iterations):
            prompt = self._build_prompt(task, thoughts, observations)
            response = self.llm.generate(prompt)

            thought, action = self._parse_response(response)
            thoughts.append(thought)

            if action["type"] == "finish":
                return action["answer"]

            observation = self._execute_action(action)
            observations.append(observation)

        return "Max iterations reached"

    def _execute_action(self, action: dict) -> str:
        tool_name = action["tool"]
        tool_input = action["input"]

        if tool_name in self.tools:
            return self.tools[tool_name](tool_input)
        return f"Unknown tool: {tool_name}"

Frameworks d'agents

LangChain Agents

from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain import hub

@tool
def search(query: str) -> str:
    """Recherche sur le web."""
    return f"Résultats pour: {query}"

@tool
def calculator(expression: str) -> str:
    """Effectue un calcul mathématique."""
    import numexpr
    return str(numexpr.evaluate(expression))

llm = ChatOpenAI(model="gpt-4")
tools = [search, calculator]
prompt = hub.pull("hwchase17/openai-tools-agent")

agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

result = agent_executor.invoke({
    "input": "Quel est le PIB de la France ?"
})

LangGraph

Pour des workflows plus complexes avec état :

from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]
    next_step: str

def reasoning_node(state: AgentState):
    return {"next_step": "action"}

def action_node(state: AgentState):
    return {"messages": ["Action exécutée"]}

def should_continue(state: AgentState):
    if state["next_step"] == "end":
        return END
    return "reasoning"

workflow = StateGraph(AgentState)
workflow.add_node("reasoning", reasoning_node)
workflow.add_node("action", action_node)
workflow.add_edge("action", "reasoning")
workflow.add_conditional_edges("reasoning", should_continue)
workflow.set_entry_point("reasoning")

app = workflow.compile()

CrewAI

Pour des agents multi-rôles qui collaborent :

from crewai import Agent, Task, Crew

researcher = Agent(
    role="Chercheur",
    goal="Trouver des informations pertinentes",
    backstory="Expert en recherche d'information",
    tools=[search_tool]
)

writer = Agent(
    role="Rédacteur",
    goal="Rédiger du contenu de qualité",
    backstory="Rédacteur expérimenté"
)

research_task = Task(
    description="Rechercher les dernières tendances en IA",
    agent=researcher
)

write_task = Task(
    description="Rédiger un article basé sur la recherche",
    agent=writer
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task]
)

result = crew.kickoff()

Types d'outils courants

OUTILS D'INFORMATION
├── Recherche web
├── Lecture de documents
├── Requêtes base de données
└── APIs externes

OUTILS D'ACTION
├── Envoi d'emails
├── Création de fichiers
├── Appels API
└── Exécution de code

OUTILS DE CALCUL
├── Calculatrice (numexpr, sympy)
├── Analyse de données
├── Génération de graphiques
└── Traitement d'images

OUTILS DE MÉMOIRE
├── Sauvegarde de contexte
├── Récupération d'historique
└── Gestion de connaissances

Bonnes pratiques

Sécurité

RÈGLES DE SÉCURITÉ :

1. VALIDATION DES ENTRÉES
   ├── Valider tous les paramètres
   ├── Limiter les types acceptés
   └── Échapper les caractères spéciaux

2. SANDBOXING
   ├── Exécuter le code dans un environnement isolé
   ├── Limiter les ressources (CPU, mémoire)
   └── Timeout sur les opérations

3. PERMISSIONS
   ├── Principe du moindre privilège
   ├── Actions réversibles quand possible
   └── Confirmation humaine pour actions critiques

4. LOGGING
   ├── Tracer toutes les actions
   ├── Auditer les accès
   └── Monitorer les anomalies

Design des outils

# BON : Description claire et paramètres bien définis
{
    "name": "search_products",
    "description": "Rechercher des produits dans le catalogue. Retourne une liste de produits avec nom, prix et disponibilité.",
    "parameters": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "Termes de recherche (ex: 'chaussures running')"
            },
            "max_price": {
                "type": "number",
                "description": "Prix maximum en euros (optionnel)"
            },
            "in_stock_only": {
                "type": "boolean",
                "description": "Filtrer uniquement les produits en stock"
            }
        },
        "required": ["query"]
    }
}

# MAUVAIS : Vague et ambigu
{
    "name": "search",
    "description": "Cherche des trucs",
    "parameters": {"type": "object", "properties": {"q": {"type": "string"}}}
}

Résumé

AGENTS IA = LLM + Outils + Boucle de raisonnement

FUNCTION CALLING :
├── Définir des outils avec schémas JSON
├── Le LLM décide quand les utiliser
├── Exécuter et retourner les résultats
└── Le LLM formule la réponse finale

PATTERN ReAct :
Thought → Action → Observation → (répéter) → Answer

FRAMEWORKS :
├── LangChain : Agents flexibles
├── LangGraph : Workflows complexes
├── CrewAI : Multi-agents collaboratifs
└── Anthropic/OpenAI : APIs natives

SÉCURITÉ :
├── Valider les entrées
├── Sandboxer l'exécution
├── Limiter les permissions
└── Logger les actions

OUTILS COURANTS :
├── Recherche web
├── Bases de données
├── APIs externes
├── Calcul sécurisé (numexpr, sympy)
└── Actions (email, fichiers...)

On this page