Avec les réglages adoptés, le ChatEngine est de type C3 : CondensePlusContextChatEngine.
La classe est : <class 'llama_index.core.chat_engine.condense_plus_context.CondensePlusContextChatEngine'>
Il en résulte l’API suivante :
Méthodes par catégorie fonctionnelle
1. Méthodes publiques (API officielle)
chat(prompt)
- Appel synchrone.
- Retourne une réponse complète (pas de streaming).
- Utilise la condensation + récupération de contexte + synthèse.
stream_chat(message)
- Version streaming de
chat(). - Retourne un
ChatResponseavecresponse_gen(générateur de tokens). - C’est celle que tu utilises.
achat(prompt)
- Version async de
chat().
astream_chat(message)
- Version async de
stream_chat().
chat_history
- Liste interne des messages échangés.
- Utilisée pour la condensation (C3).
reset()
- Vide l’historique du chat.
- Réinitialise l’état interne.
- Ne ferme aucun stream (important).
from_defaults(...)
- Constructeur de convenance.
- Permet de créer un CondensePlusContextChatEngine avec des paramètres par défaut.
chat_repl() / streaming_chat_repl()
- Interfaces REPL (console).
- Pas utiles dans ton architecture Streamlit.
2. Méthodes internes liées à la condensation (C3)
C’est le cœur du moteur.
_condense_question()
- Prend la dernière question utilisateur.
- Condense en une question autonome.
- Utilise
_condense_prompt_template.
_acondense_question()
- Version async.
_skip_condense
- Booléen interne.
- Permet de bypasser la condensation (rare).
_condense_prompt_template
- Prompt utilisé pour la condensation.
3. Méthodes internes liées à la récupération de contexte
_get_nodes(query)
- Appelle le retriever.
- Retourne les nœuds pertinents.
_aget_nodes(query)
- Version async.
_retriever
- L’objet retriever (vector store, BM25, hybrid…).
_node_postprocessors
- Liste de post‑processeurs appliqués aux nœuds (rerankers, filtres…).
4. Méthodes internes liées à la synthèse de réponse
_run_c3()
- Pipeline complet :
- condensation
- récupération
- synthèse
_arun_c3()
- Version async.
_get_response_synthesizer()
- Retourne l’objet responsable de la génération finale.
- Souvent un
ResponseSynthesizer.
5. Méthodes internes liées au LLM
_llm
- L’instance du LLM (OpenAI, Ollama, Azure, etc.).
_system_prompt
- Prompt système injecté dans la conversation.
_token_counter
- Compteur interne pour mesurer les tokens consommés.
_verbose
- Active les logs détaillés.
6. Méthodes héritées / utilitaires
callback_manager
- Gestion des callbacks (events, logs, instrumentation).
__abstractmethods__
- Indique que la classe hérite d’une ABC.
__dict__, __repr__, etc.
- Méthodes Python standard.
Résumé
| Méthode | Rôle |
|---|---|
chat() |
Réponse complète |
stream_chat() |
Streaming |
achat() |
Async |
astream_chat() |
Async streaming |
reset() |
Réinitialise l’historique |
_condense_question() |
Condensation |
_get_nodes() |
Récupération |
_run_c3() |
Pipeline complet |
_llm |
LLM utilisé |
chat_history |
Historique interne |
Le pipeline C3
CondensePlusContextChatEngine applique un pipeline en 3 phases :
- Condense → transformer l’entrée utilisateur en une question autonome
- Context → récupérer les nœuds pertinents via le retriever
- Chat → synthétiser une réponse avec le LLM (streaming ou non)
Ce pipeline est implémenté dans _run_c3() et _arun_c3().
Étape 1 — Condensation de la question
Méthode interne : _condense_question()
Objectif :
Transformer la question utilisateur + historique en une question autonome, courte, optimisée pour le retriever.
Processus :
- Le moteur regarde
chat_history - Il prend le dernier message utilisateur
- Il applique
_condense_prompt_template - Il appelle le LLM (
_llm) pour produire une version condensée - Il retourne un texte propre, sans bruit conversationnel
Si _skip_condense=True, cette étape est sautée.
Étape 2 — Récupération du contexte
Méthode interne : _get_nodes()
Objectif :
Récupérer les documents pertinents pour répondre à la question condensée.
Processus :
- Le moteur appelle
_retriever.retrieve(condensed_question) - Le retriever renvoie une liste de
NodeWithScore - Le moteur applique les post‑processeurs (
_node_postprocessors) - Le moteur obtient une liste finale de nœuds pertinents
C’est ici que vector store, BM25, hybrid search, etc. interviennent.
Étape 3 — Synthèse de la réponse
Méthode interne : _get_response_synthesizer()
Objectif :
Générer la réponse finale à partir :
- de la question condensée
- des nœuds récupérés
- du prompt système
- de l’historique
Processus :
- Le moteur construit un prompt complet (system + context + question)
- Il appelle le LLM via le
ResponseSynthesizer - Si on utilise
stream_chat():- le LLM est appelé en mode streaming
- un générateur Python est créé (
response.response_gen) - chaque token est yieldé au fur et à mesure
- Le moteur met à jour
chat_historyavec :- la question condensée
- la réponse finale
Ce qui se passe EXACTEMENT dans stream_chat()
Voici la séquence interne réelle :
1. Normalisation de l’entrée
stream_chat() accepte :
- un dict
- un
ChatMessage - un
QueryBundle
Il convertit tout en un ChatMessage.
2. Ajout du message utilisateur dans chat_history
Le moteur stocke le message brut.
3. Appel à _run_c3()
C’est le pipeline complet :
condensed = _condense_question()
nodes = _get_nodes(condensed)
response = synthesizer.synthesize(condensed, nodes, streaming=True)
4. Construction d’un ChatResponse
Ce n’est pas un simple objet :
c’est un wrapper contenant :
response_gen→ le générateur de tokensresponse→ la réponse complète (remplie à la fin)metadata→ infos sur les nœuds, tokens, etc.
5. Retour du ChatResponse
Et c’est là que tu dois consommer le générateur.
Le point critique : le générateur
response.response_gen est un générateur Python qui encapsule :
- un flux HTTP (OpenAI, Ollama, Azure…)
- un pipe interne
- un buffer de tokens
Et il n’est jamais fermé automatiquement.
Cela peut provoquer l’erreur :
OSError: [Errno 24] Too many open files
Pourquoi reset() ne ferme rien
Parce que :
reset()videchat_history- mais ne touche pas aux générateurs
- ni aux connexions HTTP
- ni aux file descriptors
Donc aucun impact sur les fuites.
Comment terminer le pipeline proprement
gen = response.response_gen
try:
for token in gen:
...
finally:
gen.close()
C’est la seule manière de libérer les ressources.
Résumé
Voici le pipeline interne complet :
stream_chat()
↓
normalisation du message
↓
chat_history.append(user_message)
↓
_run_c3()
↓
_condense_question()
↓
_get_nodes()
↓
_get_response_synthesizer()
↓
LLM(streaming=True)
↓
ChatResponse(response_gen=generator)
↓
return ChatResponse