> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wandb.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Feedback en production

> Collectez et analysez les feedback utilisateur sur les résultats des LLM en production grâce aux fonctionnalités de suivi des feedback de W&B Weave.

<Note>
  Il s'agit d'un notebook interactif. Vous pouvez l'exécuter localement ou utiliser les liens ci-dessous :

  * [Ouvrir dans Google Colab](https://colab.research.google.com/github/wandb/docs/blob/main/weave/cookbooks/source/feedback_prod.ipynb)
  * [Voir la source sur GitHub](https://github.com/wandb/docs/blob/main/weave/cookbooks/source/feedback_prod.ipynb)
</Note>

Évaluer automatiquement une réponse générée par un LLM peut être difficile. Pour mieux évaluer et améliorer les réponses, vous pouvez également recueillir directement les feedback des utilisateurs afin d’identifier les points problématiques.

Ce notebook montre comment collecter les feedback des utilisateurs sur les réponses d’un chatbot personnalisé. Il utilise Streamlit pour créer l’interface et capturer les interactions avec le LLM ainsi que les feedback dans W\&B Weave. À la fin, vous disposerez d’un chatbot opérationnel qui journalise chaque réponse vers Weave, ainsi que les réactions positives ou négatives et les feedback en texte libre de vos utilisateurs, afin de pouvoir examiner les interactions en production et identifier les axes d’amélioration.

<div id="setup">
  ## Configuration
</div>

Commencez par installer les packages requis et définir vos clés API OpenAI et W\&B afin que le chatbot puisse appeler l’API OpenAI et consigner les appels vers Weave.

```python lines theme={null}
!pip install weave openai streamlit wandb
!pip install set-env-colab-kaggle-dotenv -q # pour les variables d'environnement
python
# Ajoutez un fichier .env avec vos clés API OpenAI et WandB
from set_env import set_env

_ = set_env("OPENAI_API_KEY")
_ = set_env("WANDB_API_KEY")
```

Ensuite, créez un fichier nommé `chatbot.py` contenant ce qui suit. Ce fichier définit l’interface de chat Streamlit, encapsule l’appel à OpenAI dans une op Weave afin de suivre chaque réponse, et affiche les contrôles de feedback qui associent les réactions et les notes à l’appel Weave correspondant.

```python lines {13,15,27} theme={null}
# chatbot.py

import openai
import streamlit as st
import wandb
from set_env import set_env

import weave

_ = set_env("OPENAI_API_KEY")
_ = set_env("WANDB_API_KEY")

wandb.login()

weave_client = weave.init("feedback-example")
oai_client = openai.OpenAI()

def init_states():
    """Configurer les clés session_state si elles n'existent pas encore."""
    if "messages" not in st.session_state:
        st.session_state["messages"] = []
    if "calls" not in st.session_state:
        st.session_state["calls"] = []
    if "session_id" not in st.session_state:
        st.session_state["session_id"] = "123abc"

@weave.op
def chat_response(full_history):
    """
    Appelle l'API OpenAI en mode streaming en tenant compte de l'intégralité de l'historique de conversation jusqu'à présent.
    full_history est une liste de dicts : [{"role":"user"|"assistant","content":...}, ...]
    """
    stream = oai_client.chat.completions.create(
        model="gpt-4", messages=full_history, stream=True
    )
    response_text = st.write_stream(stream)
    return {"response": response_text}

def render_feedback_buttons(call_idx):
    """Affiche les boutons pouce haut/bas et le feedback textuel pour l'appel."""
    col1, col2, col3 = st.columns([1, 1, 4])

    # Bouton pouce haut
    with col1:
        if st.button("👍", key=f"thumbs_up_{call_idx}"):
            st.session_state.calls[call_idx].feedback.add_reaction("👍")
            st.success("Thanks for the feedback!")

    # Bouton pouce bas
    with col2:
        if st.button("👎", key=f"thumbs_down_{call_idx}"):
            st.session_state.calls[call_idx].feedback.add_reaction("👎")
            st.success("Thanks for the feedback!")

    # Feedback textuel
    with col3:
        feedback_text = st.text_input("Feedback", key=f"feedback_input_{call_idx}")
        if (
            st.button("Submit Feedback", key=f"submit_feedback_{call_idx}")
            and feedback_text
        ):
            st.session_state.calls[call_idx].feedback.add_note(feedback_text)
            st.success("Feedback submitted!")

def display_old_messages():
    """Affiche la conversation stockée dans st.session_state.messages avec les boutons de feedback"""
    for idx, message in enumerate(st.session_state.messages):
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

            # S'il s'agit d'un message de l'assistant, afficher le formulaire de feedback
            if message["role"] == "assistant":
                # Déterminer l'index de ce message de l'assistant dans st.session_state.calls
                assistant_idx = (
                    len(
                        [
                            m
                            for m in st.session_state.messages[: idx + 1]
                            if m["role"] == "assistant"
                        ]
                    )
                    - 1
                )
                # Afficher les boutons pouce haut/bas et le feedback textuel
                if assistant_idx < len(st.session_state.calls):
                    render_feedback_buttons(assistant_idx)

def display_chat_prompt():
    """Affiche la zone de saisie du prompt de chat."""
    if prompt := st.chat_input("Ask me anything!"):
        # Afficher immédiatement le nouveau message de l'utilisateur
        with st.chat_message("user"):
            st.markdown(prompt)

        # Enregistrer le message de l'utilisateur dans la session
        st.session_state.messages.append({"role": "user", "content": prompt})

        # Préparer l'historique de chat pour l'API
        full_history = [
            {"role": msg["role"], "content": msg["content"]}
            for msg in st.session_state.messages
        ]

        with st.chat_message("assistant"):
            # Associer les attributs Weave pour le suivi des instances de conversation
            with weave.attributes(
                {"session": st.session_state["session_id"], "env": "prod"}
            ):
                # Appeler l'API OpenAI (stream)
                result, call = chat_response.call(full_history)

                # Stocker le message de l'assistant
                st.session_state.messages.append(
                    {"role": "assistant", "content": result["response"]}
                )

                # Stocker l'objet d'appel Weave pour associer le feedback à la réponse concernée
                st.session_state.calls.append(call)

                # Afficher les boutons de feedback pour le nouveau message
                new_assistant_idx = (
                    len(
                        [
                            m
                            for m in st.session_state.messages
                            if m["role"] == "assistant"
                        ]
                    )
                    - 1
                )

                # Afficher les boutons de feedback
                if new_assistant_idx < len(st.session_state.calls):
                    render_feedback_buttons(new_assistant_idx)

def main():
    st.title("Chatbot with immediate feedback forms")
    init_states()
    display_old_messages()
    display_chat_prompt()

if __name__ == "__main__":
    main()
```

Vous pouvez lancer ceci avec `streamlit run chatbot.py`.

Vous pouvez maintenant interagir avec cette application et cliquer sur les boutons de feedback après chaque réponse. Rendez-vous dans la Weave UI pour voir les feedback associés. Chaque échange dans le chat est enregistré en tant qu’appel Weave, et toutes les réactions ou notes que vous soumettez sont liées à l’appel qui a produit la réponse.

<div id="explanation">
  ## Explications
</div>

La section suivante présente les principales API Weave utilisées dans le chatbot, afin que vous puissiez appliquer le même schéma à votre propre application.

Considérez la fonction de prédiction décorée suivante :

```python lines {5} theme={null}
import weave

weave.init("feedback-example")

@weave.op
def predict(input_data):
    # Votre logique de prédiction ici
    some_result = "hello world"
    return some_result
```

Vous pouvez l’utiliser comme d’habitude pour fournir à l’utilisateur une réponse générée par le modèle :

```python lines theme={null}
with weave.attributes(
    {"session": "123abc", "env": "prod"}
):  # associer des attributs arbitraires à l'appel avec les entrées et sorties
    result = predict(input_data="your data here")  # question de l'utilisateur via l'interface de l'application
```

Pour associer le feedback, vous avez besoin de l’objet `call`, que vous obtenez en utilisant la méthode `.call()` au lieu d’appeler la fonction normalement :

```python lines theme={null}
result, call = predict.call(input_data="your data here")
```

Vous avez besoin de cet objet `call` pour associer un feedback à cette réponse précise. Après avoir effectué l'appel, le résultat de l'opération est disponible dans `result`.

Une fois que vous avez l'objet `call`, vous pouvez ensuite enregistrer le feedback de l'utilisateur pour cette réponse précise :

```python lines theme={null}
call.feedback.add_reaction("👍")  # réaction de l'utilisateur via l'interface de l'application
```

<div id="conclusion">
  ## Conclusion
</div>

Dans ce tutoriel, vous avez créé une interface de chat avec Streamlit qui enregistre les entrées et les sorties dans Weave, ainsi que des boutons de pouce levé et de pouce baissé pour recueillir les feedback des utilisateurs.
