Module 4 — Interface de programmation locale
La session interactive du module précédent nous a permis de calibrer un modèle. Pour l'intégrer à l'assistant du cabinet — une petite application interne qui reçoit une question du secrétariat et renvoie une réponse formatée — nous avons besoin de l'appeler depuis du code. Ollama expose deux surfaces d'API, et savoir quand utiliser l'une ou l'autre est un vrai choix d'ingénierie.
Les deux surfaces d'API
La première est l'API native d'Ollama, préfixée par /api/. Elle expose trois points d'entrée principaux : POST /api/generate pour une complétion sur une consigne unique, POST /api/chat pour une conversation multi-tours structurée en messages, et POST /api/embeddings pour transformer un texte en vecteur. C'est la surface la plus complète : elle expose tous les paramètres Ollama, y compris num_ctx, keep_alive et les options de Modelfile.
La seconde est une API compatible OpenAI, préfixée par /v1/. Ollama y implémente POST /v1/chat/completions et POST /v1/embeddings avec exactement le même schéma de requête et de réponse que l'API api.openai.com/v1. Concrètement, cela veut dire que n'importe quel client, bibliothèque ou application qui parle à OpenAI peut être redirigé vers Ollama en changeant deux paramètres : l'URL de base et la clé API (qui devient une valeur factice, Ollama ne l'utilise pas). C'est le levier qui rend triviale la migration d'un prototype cloud vers une inférence locale.
L'appel canonique en Python
La bibliothèque officielle ollama-python (pip install ollama) enveloppe l'API native. Voici un appel typique de l'assistant du cabinet :
from ollama import Client
client = Client(host="http://127.0.0.1:11434")
reponse = client.chat(
model="llama3.1:8b-instruct-q4_K_M",
messages=[
{"role": "system", "content": "Tu es l'assistant d'un cabinet juridique francais."},
{"role": "user", "content": "Delai legal pour contester une facture de loyer ?"},
],
options={"temperature": 0.2, "num_ctx": 4096, "num_predict": 512},
keep_alive="10m",
)
print(reponse["message"]["content"])
Trois points méritent qu'on s'y arrête. Le paramètre keep_alive reprend directement OLLAMA_KEEP_ALIVE du module 1, mais par requête : passer "10m" demande à Ollama de garder le modèle en mémoire dix minutes après l'appel. Sur un serveur qui reçoit un pic de questions le matin puis rien de l'après-midi, cela évite de recharger 4,7 Go à chaque appel. Le champ options accepte tous les paramètres du module 3 sans exception. Et surtout, un appel Python passe exactement par le même service que la commande ollama run : les deux clients sont interchangeables, et un modèle chargé par l'un est disponible à l'autre sans coût.
Le flux (streaming)
Pour l'affichage jeton par jeton dans l'interface web du cabinet, on demande le mode flux :
for morceau in client.chat(
model="llama3.1:8b-instruct-q4_K_M",
messages=[{"role": "user", "content": "Explique en cinq lignes la mise en demeure."}],
stream=True,
):
print(morceau["message"]["content"], end="", flush=True)
Le flux transforme la latence perçue : le premier jeton arrive typiquement en 200 ms, alors que la génération complète prend 8 à 15 secondes sur un poste sans GPU. C'est la différence entre une application ressentie comme réactive et une application ressentie comme figée. Le mode flux n'est pas plus coûteux en calcul — au contraire, il évite au serveur de tamponner l'intégralité de la réponse en mémoire avant l'envoi.
L'API compatible OpenAI, en une ligne près
Le même appel via le client openai officiel :
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:11434/v1", api_key="ollama")
reponse = client.chat.completions.create(
model="llama3.1:8b-instruct-q4_K_M",
messages=[
{"role": "system", "content": "Tu es l'assistant d'un cabinet juridique francais."},
{"role": "user", "content": "Delai legal pour contester une facture de loyer ?"},
],
temperature=0.2,
)
print(reponse.choices[0].message.content)
Ce code ne ressemble plus du tout à Ollama : c'est du code OpenAI standard. La conséquence pratique est immense : le cabinet peut prototyper en local sur llama3.1:8b, puis, si un jour il obtient l'autorisation d'utiliser un modèle cloud pour un usage précis, il lui suffit de changer base_url et api_key. Aucune ligne d'appel métier ne bouge. Symétriquement, un prototype OpenAI existant devient utilisable hors ligne en deux lignes.
Deux limites à connaître honnêtement. Certaines options spécifiques d'Ollama (le keep_alive par requête, num_ctx) ne sont pas exposées par cette surface, ou passent par un champ extra_body. Et les fonctionnalités de pointe d'OpenAI (recherche web intégrée, mémoire persistante côté serveur, logprobs complets sur tous les modèles) n'existent évidemment pas dans Ollama, qui ne fournit que ce que le modèle local sait faire.
Les plongements
Le point d'entrée embeddings est structurellement différent : il ne génère pas de texte, il renvoie un vecteur de nombres flottants qui représente le sens d'un texte. C'est la brique de base du système de questions-réponses local du module 9.
vecteur = client.embeddings(
model="nomic-embed-text",
prompt="Un locataire cesse de payer son loyer depuis trois mois.",
)["embedding"]
print(len(vecteur)) # 768 pour nomic-embed-text
Il faut utiliser un modèle de plongements (nomic-embed-text, mxbai-embed-large, bge-m3), pas un modèle d'instruction. Ces modèles sont beaucoup plus petits (200 à 500 Mo) et n'engagent quasiment pas la RAM.
API native /api/ : quand on veut exploiter tous les paramètres Ollama, ou quand on écrit une application Ollama-spécifique en connaissance de cause. API compatible /v1/ : quand on veut la portabilité vers/depuis OpenAI, ou brancher un client existant (LangChain, LlamaIndex, une interface tierce) sans réécrire quoi que ce soit. Le module 8 s'appuiera systématiquement sur la seconde.
En résumé
- Ollama expose deux surfaces : l'API native
/api/(generate,chat,embeddings) et l'API compatible OpenAI/v1/(chat/completions,embeddings). - La bibliothèque
ollama-pythonexpose tous les paramètres du module 3 viaoptions=, pluskeep_alivepar requête pour maîtriser la mémoire. - Le mode
stream=Trueréduit la latence perçue en envoyant les jetons au fur et à mesure ; premier jeton sous la seconde. - Passer par l'API compatible OpenAI rend le code portable et permet la migration prototype cloud → inférence locale (ou l'inverse) sans réécriture métier.
Module suivant : personnaliser un modèle avec un Modelfile pour figer la consigne système et les paramètres du cabinet.