Aller au contenu

Validation IA Locale Et Hors Ligne

Objectif: prouver rapidement qu'un LLM local, un adapter LLM Arclith et un agent LangGraph fonctionnent, même sans LangSmith ni accès internet.

Modèle Mental

Trois composants sont séparés:

Text Only
1
2
3
4
client local, curl, UI ou test
  -> Agent Server LangGraph :2024
  -> adapter LLM Arclith / ChatOpenAI
  -> LM Studio :1234/v1

LM Studio sert le modèle. LangGraph orchestre le graphe. Arclith garde le métier dans les ports et use cases.

Ne pas confondre:

Besoin Surface
tester que le modèle local répond LM Studio :1234/v1
tester que le graphe agent répond LangGraph :2024
inspecter un thread durable API LangGraph /threads/...
tracer dans LangSmith optionnel, nécessite réseau et clé
utiliser LM Studio Chat comme interface possible via MCP, pas via :2024 directement

Préparer Le Mode Hors Ligne

Pour un test strictement local:

Bash
1
2
3
unset LANGSMITH_API_KEY LANGCHAIN_API_KEY
export LANGSMITH_TRACING=false
export LANGGRAPH_CLI_NO_ANALYTICS=1

Si le projet charge .env, conserver les mêmes valeurs dans .env.local ou .env:

Text Only
LANGSMITH_TRACING=false
LANGGRAPH_CLI_NO_ANALYTICS=1

LangGraph Studio charge une interface hébergée depuis smith.langchain.com. Hors ligne, utiliser l'API locale, le SDK Python ou une petite UI locale.

Tester LM Studio

Démarrer le serveur local LM Studio sur http://127.0.0.1:1234/v1, puis vérifier les modèles:

Bash
curl -fsS http://127.0.0.1:1234/v1/models | python -m json.tool

Recopier le id exact du modèle chargé. Un alias inventé comme local-model peut être refusé par LM Studio.

Tester une complétion minimale:

Bash
1
2
3
4
5
6
7
8
9
curl -fsS http://127.0.0.1:1234/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mistralai/ministral-3-3b",
    "messages": [
      {"role": "user", "content": "Réponds uniquement: ok"}
    ],
    "stream": false
  }' | python -m json.tool

Remplacer mistralai/ministral-3-3b par l'identifiant retourné par /v1/models.

Tester L'Adapter LLM Arclith

Installer et configurer l'adapter:

Bash
1
2
3
4
5
6
7
uv add "arclith[langgraph]"

arclith-cli add-adapter \
  --capability llm \
  --adapter lmstudio \
  --param model_name="<model-id-lm-studio>" \
  --yes

Vérifier la configuration:

YAML
1
2
3
4
5
# config/adapters/outbound/lm.yaml
provider: openai
model_name: "<model-id-lm-studio>"
api_key: "lm-studio"
base_url: "http://127.0.0.1:1234/v1"

Tester le client OpenAI-compatible depuis Python:

Bash
uv run --with langchain-openai python - <<'PY'
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="<model-id-lm-studio>",
    base_url="http://127.0.0.1:1234/v1",
    api_key="lm-studio",
    temperature=0,
)

response = llm.invoke("Réponds uniquement: ok")
print(response.content)
PY

Ce test prouve que le modèle local, l'endpoint OpenAI-compatible et la dépendance Python sont cohérents. Les tests unitaires métier doivent rester sur un fake de port LLM.

Tester LangGraph Sans Studio

Lancer l'Agent Server local:

Bash
uv run langgraph dev --no-browser --allow-blocking --port 2024

Le serveur expose une API locale:

Text Only
http://127.0.0.1:2024
http://127.0.0.1:2024/docs

Tester un run stateless:

Bash
curl -N -X POST "http://127.0.0.1:2024/runs/stream" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_id": "todo_agent",
    "input": {
      "messages": [
        {"role": "human", "content": "Quelles sont mes tâches en cours ?"}
      ]
    },
    "stream_mode": "values"
  }'

Le assistant_id doit correspondre au nom déclaré dans langgraph.json.

Inspecter Un Thread Durable

Pour pouvoir relire l'état final, créer un thread explicite:

Sous Agent Server, la persistance est gérée par le serveur. Pour reproduire le même test avec un graphe embedded, activer d'abord agent-persistence et invoquer le graphe avec {"configurable": {"thread_id": "<id-stable>"}}. Voir la capability dédiée.

Bash
1
2
3
4
5
THREAD_ID=$(curl -fsS -X POST "http://127.0.0.1:2024/threads" \
  -H "Content-Type: application/json" \
  -d '{}' | python -c 'import json,sys; print(json.load(sys.stdin)["thread_id"])')

echo "$THREAD_ID"

Lancer le run dans ce thread:

Bash
curl -N -X POST "http://127.0.0.1:2024/threads/$THREAD_ID/runs/stream" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_id": "todo_agent",
    "input": {
      "messages": [
        {"role": "human", "content": "Quelles sont mes tâches en cours ?"}
      ]
    },
    "stream_mode": "values"
  }'

Relire l'état et les runs:

Bash
curl -fsS "http://127.0.0.1:2024/threads/$THREAD_ID/state" | python -m json.tool
curl -fsS "http://127.0.0.1:2024/threads/$THREAD_ID/runs" | python -m json.tool

Utiliser stream_mode: "values" ou "updates" pour apprendre le graphe. messages est utile pour le streaming fin des messages, mais il est moins lisible au début.

Tester Avec Le SDK Python

Bash
uv run --with langgraph-sdk python - <<'PY'
from langgraph_sdk import get_sync_client

client = get_sync_client(url="http://127.0.0.1:2024")

state = client.runs.wait(
    None,
    "todo_agent",
    input={
        "messages": [
            {"role": "human", "content": "Quelles sont mes tâches en cours ?"}
        ]
    },
)

print(state)
PY

Ce chemin est le plus pratique pour écrire un smoke test local automatisé.

LM Studio Chat Et LangGraph

LM Studio Chat ne se branche pas directement sur http://127.0.0.1:2024, car l'Agent Server LangGraph n'expose pas une API Chat Completions. Deux chemins sont possibles:

Text Only
1
2
3
4
5
recommande:
client -> LangGraph :2024 -> adapter LLM -> LM Studio :1234/v1

possible:
LM Studio Chat -> serveur MCP local -> LangGraph :2024

Le pont MCP est utile pour tester une ergonomie chat locale, mais LangGraph devient alors un tool appelé par LM Studio. Ce n'est pas le même modèle que LangGraph orchestrateur principal.

Architecture Microservice

En développement, un seul langgraph dev peut exposer plusieurs graphes depuis langgraph.json.

En production, découper selon le bounded context:

  • agent proche du service si l'agent manipule un domaine précis;
  • agent central si l'assistant orchestre plusieurs domaines;
  • jamais d'accès direct depuis l'agent central aux repositories ou bases des autres services.

Un agent central appelle les APIs, events ou tools MCP des microservices. Les use cases restent propriétaires de leur métier.

Checklist

  • /v1/models retourne le model id local.
  • /v1/chat/completions répond avec ce model id.
  • config/adapters/outbound/lm.yaml utilise le même model_name.
  • LANGSMITH_TRACING=false est actif pour un test hors ligne.
  • langgraph dev --no-browser --allow-blocking --port 2024 démarre.
  • un run values répond via /runs/stream.
  • un thread explicite peut être relu via /threads/{thread_id}/state.
  • une mémoire store écrite pour un utilisateur est relue depuis un autre thread.
  • les tests unitaires importants utilisent un fake LLM.

Sources