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 :
- Reconnaître quand un outil est nécessaire
- Générer les paramètres corrects pour appeler une fonction
- 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 connaissancesBonnes 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 anomaliesDesign 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...)