First commit
This commit is contained in:
43
src/mcp_tool_server.py
Normal file
43
src/mcp_tool_server.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import asyncio
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
# --- 1. La base de données locale du serveur ---
|
||||
# C'est la seule source de vérité pour les données des films.
|
||||
FILM_DATABASE = {
|
||||
1: {
|
||||
"title": "Matrix",
|
||||
"synopsis": """
|
||||
Programmeur anonyme dans un service administratif le jour, Thomas Anderson devient Neo la nuit venue.
|
||||
Sous ce pseudonyme, il est l'un des pirates les plus recherchés du cyber-espace. A cheval entre deux mondes,
|
||||
Neo est assailli par d'étranges songes et des messages cryptés provenant d'un certain Morpheus.
|
||||
Celui-ci l'exhorte à aller au-delà des apparences et à trouver la réponse à la question qui hante
|
||||
constamment ses pensées : qu'est-ce que la Matrice ?
|
||||
"""
|
||||
},
|
||||
2: {
|
||||
"title": "Inception",
|
||||
"synopsis": """
|
||||
Dom Cobb est un voleur expérimenté – le meilleur qui soit dans l'art périlleux de l'extraction :
|
||||
sa spécialité consiste à s'approprier les secrets les plus précieux d'un individu, enfouis au plus
|
||||
profond de son subconscient, pendant qu'il rêve et que son esprit est particulièrement vulnérable.
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
# --- 2. Création du serveur MCP ---
|
||||
async def main():
|
||||
mcp = FastMCP("movies-mcp-server", json_response=True, port=12345)
|
||||
|
||||
# --- 3. Définition de l'unique outil ---
|
||||
# Cet outil ne fait aucune analyse, il retourne juste le synopsis d'un film, donné son ID.
|
||||
# TODO : implémentez l'outil mcp_get_film_synopsis
|
||||
# ... votre code ici ...
|
||||
|
||||
# --- 4. Démarrage du serveur ---
|
||||
print("Serveur de données MCP démarré sur le port 12345...")
|
||||
print("Ce terminal est maintenant dédié au serveur. Laissez-le tourner.")
|
||||
# --- TODO : démarrez le serveur ---
|
||||
# ... votre code ici ...
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
112
src/run_agent.py
Normal file
112
src/run_agent.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import asyncio
|
||||
from typing import List, Dict, Any, Type
|
||||
|
||||
from langchain.globals import set_debug
|
||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||
from langchain_core.tools import StructuredTool
|
||||
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
||||
from langchain_openai import ChatOpenAI
|
||||
from mcp_use import MCPClient
|
||||
from pydantic import BaseModel, Field, create_model
|
||||
|
||||
set_debug(True)
|
||||
|
||||
# --- Configuration pour LM Studio Server ---
|
||||
LLM_CHAT_SERVER_BASE_URL = "http://127.0.0.1:1234/v1"
|
||||
LLM_CHAT_MODEL = "meta-llama-3.1-8b-instruct"
|
||||
LLM_CHAT_TEMPERATURE = 0.3
|
||||
LLM_CHAT_API_KEY = "not-needed"
|
||||
|
||||
agent_executor: AgentExecutor | None = None
|
||||
mcp_client: MCPClient | None = None
|
||||
|
||||
async def build_agent() -> AgentExecutor:
|
||||
print("--- 1. Prêt à analyser un film via son ID ---")
|
||||
|
||||
print("\n--- 2. Initialisation du LLM et du client MCP ---")
|
||||
llm = ChatOpenAI(
|
||||
model=LLM_CHAT_MODEL,
|
||||
base_url=LLM_CHAT_SERVER_BASE_URL,
|
||||
temperature=LLM_CHAT_TEMPERATURE,
|
||||
api_key=LLM_CHAT_API_KEY
|
||||
)
|
||||
|
||||
mcp_client = MCPClient.from_config_file("../resources/servers.json")
|
||||
session = await mcp_client.create_session("movies-mcp-server")
|
||||
print("Connexion au serveur d'outils (MCP) établie.")
|
||||
|
||||
print("\n--- 3. Découverte et création dynamique des outils LangChain ---")
|
||||
remote_tools_definitions = await session.list_tools()
|
||||
langchain_tools: List[StructuredTool] = []
|
||||
type_mapping = {'string': str, 'integer': int, 'number': float, 'boolean': bool, 'object': dict}
|
||||
|
||||
async def run_mcp_tool(tool_name: str, **kwargs: Dict[str, Any]) -> str:
|
||||
print(f"--- AGENT -> OUTIL : Appel de '{tool_name}' avec {kwargs} ---")
|
||||
result = await session.call_tool(name=tool_name, arguments=kwargs)
|
||||
# On formate le dictionnaire de retour en une chaîne de caractères
|
||||
# pour que le LLM puisse le lire facilement dans l'étape d'observation.
|
||||
text_result = result.content[0].text if result.content else "Action effectuée."
|
||||
print(f"--- OUTIL -> AGENT : Résultat : {text_result} ---")
|
||||
return text_result
|
||||
|
||||
|
||||
for tool_def in remote_tools_definitions:
|
||||
fields: Dict[str, Any] = {}
|
||||
params_schema = tool_def.inputSchema
|
||||
|
||||
if 'properties' in params_schema:
|
||||
for param_name, param_details in params_schema['properties'].items():
|
||||
if not param_name.startswith('_'):
|
||||
param_type = type_mapping.get(param_details.get('type'), Any)
|
||||
description = param_details.get('description', '')
|
||||
fields[param_name] = (param_type, Field(..., description=description))
|
||||
|
||||
DynamicToolArgs: Type[BaseModel] = create_model(f'{tool_def.name}Args', **fields)
|
||||
tool_func = (lambda name: lambda **kwargs: run_mcp_tool(name, **kwargs))(tool_def.name)
|
||||
langchain_tool = StructuredTool(
|
||||
name=tool_def.name, description=tool_def.description, func=tool_func,
|
||||
coroutine=tool_func, args_schema=DynamicToolArgs
|
||||
)
|
||||
langchain_tools.append(langchain_tool)
|
||||
|
||||
print(f"Outil LangChain créé dynamiquement : {[tool.name for tool in langchain_tools]}")
|
||||
|
||||
print("\n--- 4. Construction de l'agent avec un prompt système adapté ---")
|
||||
system_prompt = """
|
||||
Tu es un assistant expert en cinéma. Tu dois répondre aux questions de l'utilisateur en français.
|
||||
Analyse la question de l'utilisateur. Si tu as besoin d'informations que tu n'as pas, appelle l'outil approprié que tu as à ta disposition.
|
||||
Une fois que tu as obtenu une réponse de l'outil, utilise cette information pour formuler une réponse finale et claire pour l'utilisateur.
|
||||
"""
|
||||
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", system_prompt),
|
||||
("human", "{input}"),
|
||||
MessagesPlaceholder("agent_scratchpad"),
|
||||
])
|
||||
|
||||
agent = create_tool_calling_agent(llm, langchain_tools, prompt)
|
||||
print("Agent Executor prêt.")
|
||||
|
||||
return AgentExecutor(agent=agent, tools=langchain_tools, verbose=True)
|
||||
|
||||
async def run_agent():
|
||||
# variable globale utile uniquement si par la suite vous souhaitez initialiser l'agent
|
||||
# depuis FastAPI via @asynccontextmanager / async def lifespan(app: FastAPI):
|
||||
global agent_executor
|
||||
print("Démarrage de l'application : initialisation de l'agent...")
|
||||
agent_executor = await build_agent()
|
||||
print("Agent prêt à transmettre une requête.")
|
||||
if agent_executor:
|
||||
user_request = "Pour le film avec l'ID 1, peux-tu m'afficher le synopsis pertinent ?"
|
||||
response = await agent_executor.ainvoke({"input": user_request})
|
||||
print(response.get("output", "Pas de sortie de l'agent."))
|
||||
print("Arrêt de l'application : fermeture des sessions MCP...")
|
||||
if mcp_client:
|
||||
await mcp_client.close_all_sessions()
|
||||
print("Sessions fermées.")
|
||||
|
||||
async def main():
|
||||
await run_agent()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user