Les assistants basés sur les grands modèles linguistiques (LLM) ne se limitent plus à des échanges conversationnels simples. Un véritable agent web intelligent doit pouvoir comprendre l'intention de l'utilisateur, interagir avec des outils externes et exécuter des tâches complexes en plusieurs étapes.
Architecture du système
L'architecture repose sur trois composants principaux :
- FastAPI : Interface HTTP pour l'exposition d'API REST
- LangGraph : Moteur d'orchestration des flux décisionnels de l'agent
- MCP : Protocole standardisé pour l'intégration d'outils externes
Initialisation de l'application FastAPI
# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import structlog
logger = structlog.get_logger()
@asynccontextmanager
async def app_lifecycle(app: FastAPI):
logger.info("Démarrage de l'application")
yield
logger.info("Arrêt de l'application")
app = FastAPI(
title="Agent IA Intelligent",
lifespan=app_lifecycle
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"]
)
@app.get("/health")
async def health():
return {"status": "opérationnel"}
@app.get("/")
async def root():
return {"message": "API Agent IA", "docs": "/docs"}
Gestion des configurations
# app/core/settings.py
from pydantic_settings import BaseSettings
class AppConfig(BaseSettings):
PROJECT_TITLE: str = "Agent IA API"
ENV_MODE: str = "development"
LLM_BACKEND: str = "openai"
OPENAI_KEY: str = ""
ANTHROPIC_KEY: str = ""
DEFAULT_MODEL: str = "gpt-4"
class Config:
env_file = ".env"
config = AppConfig()
Définition de l'état de l'agent
# app/agent/state.py
from typing import Annotated, TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
class ConversationState(TypedDict):
history: Annotated[list[BaseMessage], add_messages]
Construction du graphe décisionnel
# app/agent/workflow.py
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode
from app.agent.state import ConversationState
from app.services.llm import get_model
from app.tools.registry import available_tools
def build_agent_workflow():
model = get_model()
bound_model = model.bind_tools(available_tools)
tool_executor = ToolNode(available_tools)
workflow = StateGraph(ConversationState)
async def reasoning_step(state):
response = await bound_model.ainvoke(state["history"])
return {"history": [response]}
def routing_logic(state):
latest = state["history"][-1]
if hasattr(latest, "tool_calls") and latest.tool_calls:
return "execute"
return END
workflow.add_node("think", reasoning_step)
workflow.add_node("execute", tool_executor)
workflow.add_edge(START, "think")
workflow.add_conditional_edges("think", routing_logic, ["execute", END])
workflow.add_edge("execute", "think")
return workflow.compile()
_agent_instance = None
def get_agent():
global _agent_instance
if not _agent_instance:
_agent_instance = build_agent_workflow()
return _agent_instance
Intégration d'outils personnnalisés
# app/tools/calculator.py
from langchain_core.tools import tool
from datetime import datetime
@tool
def compute(expression: str) -> str:
"""Évalue une expression mathématique"""
try:
result = eval(expression, {"__builtins__": {}})
return str(result)
except:
return "Erreur de calcul"
@tool
def current_datetime() -> str:
"""Retourne la date et heure actuelle"""
return datetime.now().isoformat()
def list_available_tools():
return [compute, current_datetime]
Sélection dnyamique du modèle LLM
# app/services/llm.py
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from app.core.settings import config
def get_model():
if config.LLM_BACKEND == "openai":
return ChatOpenAI(
api_key=config.OPENAI_KEY,
model=config.DEFAULT_MODEL,
temperature=0.7
)
elif config.LLM_BACKEND == "anthropic":
return ChatAnthropic(
api_key=config.ANTHROPIC_KEY,
model=config.DEFAULT_MODEL,
temperature=0.7
)
raise ValueError("Fournisseur LLM non supporté")
Points d'accès API
# app/api/endpoints.py
from fastapi import APIRouter
from pydantic import BaseModel
from app.agent.workflow import get_agent
from langchain_core.messages import HumanMessage
router = APIRouter(prefix="/agent")
class QueryRequest(BaseModel):
text: str
session: str = None
class AgentResponse(BaseModel):
answer: str
steps: int
@router.post("/process", response_model=AgentResponse)
async def process_query(req: QueryRequest):
agent = get_agent()
initial_state = {"history": [HumanMessage(content=req.text)]}
result = await agent.ainvoke(initial_state)
final_msg = result["history"][-1]
return AgentResponse(
answer=final_msg.content,
steps=len(result["history"])
)
Exemples d'utilisation
Pour tester le système :
curl -X POST http://localhost:8000/agent/process \
-H "Content-Type: application/json" \
-d '{"text": "Quelle est la racine carrée de 144 ?"}'
Perspectives d'amélioration
- Ajout de persistance des sesisons via PostgreSQL
- Implémentation de limites de taux d'appel
- Intégration de traces distribuées avec OpenTelemetry
- Déploiement de serveurs MCP autonomes pour des fonctionnalités avancées