메인 콘텐츠로 건너뛰기
이것은 인터랙티브 노트북입니다. 로컬에서 실행하거나 아래 링크를 사용할 수 있습니다:

생성된 LLM 응답을 자동으로 평가하는 것은 종종 어렵습니다. 따라서 위험 허용 범위에 따라 사용자로부터 직접 피드백을 수집하여 개선이 필요한 영역을 찾을 수 있습니다. 이 튜토리얼에서는 사용자 피드백을 수집하기 위한 예시 애플리케이션으로 커스텀 챗봇을 사용하겠습니다. 인터페이스 구축을 위해 Streamlit을 사용하고, LLM 상호작용과 피드백을 Weave 에 캡처합니다.

설정

!pip install weave openai streamlit wandb
!pip install set-env-colab-kaggle-dotenv -q # 환경 변수 설정을 위해 사용
python
# OpenAI 및 WandB API 키가 포함된 .env 파일을 추가하세요
from set_env import set_env

_ = set_env("OPENAI_API_KEY")
_ = set_env("WANDB_API_KEY")
다음으로, 아래 내용을 포함하는 chatbot.py 파일을 생성합니다:
# 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():
    """session_state 키가 아직 존재하지 않는 경우 설정합니다."""
    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):
    """
    지금까지의 전체 대화 기록을 바탕으로 스트리밍 모드에서 OpenAI API를 호출합니다.
    full_history는 딕셔너리 리스트입니다: [{"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):
    """해당 호출에 대해 좋아요/싫어요 및 텍스트 피드백 버튼을 렌더링합니다."""
    col1, col2, col3 = st.columns([1, 1, 4])

    # 좋아요 버튼
    with col1:
        if st.button("👍", key=f"thumbs_up_{call_idx}"):
            st.session_state.calls[call_idx].feedback.add_reaction("👍")
            st.success("피드백 감사합니다!")

    # 싫어요 버튼
    with col2:
        if st.button("👎", key=f"thumbs_down_{call_idx}"):
            st.session_state.calls[call_idx].feedback.add_reaction("👎")
            st.success("피드백 감사합니다!")

    # 텍스트 피드백
    with col3:
        feedback_text = st.text_input("피드백", key=f"feedback_input_{call_idx}")
        if (
            st.button("피드백 제출", key=f"submit_feedback_{call_idx}")
            and feedback_text
        ):
            st.session_state.calls[call_idx].feedback.add_note(feedback_text)
            st.success("피드백이 제출되었습니다!")

def display_old_messages():
    """st.session_state.messages에 저장된 대화와 피드백 버튼을 표시합니다."""
    for idx, message in enumerate(st.session_state.messages):
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

            # 어시스턴트 메시지인 경우 피드백 양식을 표시
            if message["role"] == "assistant":
                # st.session_state.calls에서 이 어시스턴트 메시지의 인덱스를 찾습니다.
                assistant_idx = (
                    len(
                        [
                            m
                            for m in st.session_state.messages[: idx + 1]
                            if m["role"] == "assistant"
                        ]
                    )
                    - 1
                )
                # 좋아요/싫어요 및 텍스트 피드백 렌더링
                if assistant_idx < len(st.session_state.calls):
                    render_feedback_buttons(assistant_idx)

def display_chat_prompt():
    """채팅 프롬프트 입력 상자를 표시합니다."""
    if prompt := st.chat_input("무엇이든 물어보세요!"):
        # 새 사용자 메시지를 즉시 렌더링
        with st.chat_message("user"):
            st.markdown(prompt)

        # 세션에 사용자 메시지 저장
        st.session_state.messages.append({"role": "user", "content": prompt})

        # API를 위한 채팅 기록 준비
        full_history = [
            {"role": msg["role"], "content": msg["content"]}
            for msg in st.session_state.messages
        ]

        with st.chat_message("assistant"):
            # 대화 인스턴스 트래킹을 위해 Weave 속성 첨부
            with weave.attributes(
                {"session": st.session_state["session_id"], "env": "prod"}
            ):
                # OpenAI API 호출 (스트림)
                result, call = chat_response.call(full_history)

                # 어시스턴트 메시지 저장
                st.session_state.messages.append(
                    {"role": "assistant", "content": result["response"]}
                )

                # 특정 응답에 피드백을 연결하기 위해 weave 호출 오브젝트 저장
                st.session_state.calls.append(call)

                # 새 메시지에 대한 피드백 버튼 렌더링
                new_assistant_idx = (
                    len(
                        [
                            m
                            for m in st.session_state.messages
                            if m["role"] == "assistant"
                        ]
                    )
                    - 1
                )

                # 피드백 버튼 렌더링
                if new_assistant_idx < len(st.session_state.calls):
                    render_feedback_buttons(new_assistant_idx)

def main():
    st.title("즉각적인 피드백 양식이 포함된 챗봇")
    init_states()
    display_old_messages()
    display_chat_prompt()

if __name__ == "__main__":
    main()
streamlit run chatbot.py 명령어로 실행할 수 있습니다. 이제 이 애플리케이션을 사용하여 대화하고 각 응답 후에 피드백 버튼을 클릭할 수 있습니다. Weave UI를 방문하여 첨부된 피드백을 확인하세요.

설명

데코레이터가 적용된 예측 함수를 다음과 같이 가정해 보겠습니다:
import weave

weave.init("feedback-example")

@weave.op
def predict(input_data):
    # 여기에 예측 로직 작성
    some_result = "hello world"
    return some_result
일반적으로 다음과 같이 사용자에게 모델 응답을 전달하는 데 사용할 수 있습니다:
with weave.attributes(
    {"session": "123abc", "env": "prod"}
):  # 입력 및 출력과 함께 호출에 임의의 속성을 첨부합니다.
    result = predict(input_data="your data here")  # 앱 UI를 통한 사용자 질문
피드백을 첨부하려면 call 오브젝트가 필요합니다. 이는 함수를 평소처럼 호출하는 대신 .call() 메소드를 사용하여 얻을 수 있습니다:
result, call = predict.call(input_data="your data here")
이 call 오브젝트는 특정 응답에 피드백을 연결하는 데 필요합니다. 호출을 수행한 후, 작업의 결과값은 위의 result를 통해 사용할 수 있습니다.
call.feedback.add_reaction("👍")  # 앱 UI를 통한 사용자 반응

결론

이 튜토리얼에서는 Streamlit을 사용하여 입력과 출력을 Weave 에 캡처하고, 사용자 피드백을 받기 위한 👍👎 버튼을 포함한 채팅 UI를 구축했습니다.