Skip to main content
이 튜토리얼에서는 사용자 피드백을 기반으로 분류기를 최적화하는 방법을 안내합니다. 분류기는 원하는 출력을 수집하기가 일반적으로 매우 간단하기 때문에 최적화하기에 좋으며, 이를 통해 사용자 피드백을 기반으로 few-shot 예제를 쉽게 만들 수 있습니다. 이 예제에서 바로 그 작업을 수행할 것입니다.

목표

이 예제에서는 GitHub 이슈의 제목을 기반으로 분류하는 봇을 구축합니다. 이 봇은 제목을 입력받아 여러 클래스 중 하나로 분류합니다. 그런 다음 사용자 피드백을 수집하기 시작하고 이를 사용하여 분류기의 성능을 개선합니다.

시작하기

시작하려면 먼저 모든 트레이스를 특정 프로젝트로 전송하도록 설정합니다. 환경 변수를 설정하여 이 작업을 수행할 수 있습니다:
import os
os.environ["LANGSMITH_PROJECT"] = "classifier"
이제 초기 애플리케이션을 생성할 수 있습니다. 이것은 GitHub 이슈 제목을 입력받아 레이블을 지정하려고 시도하는 매우 간단한 함수입니다.
import openai
from langsmith import traceable, Client
import uuid

client = openai.Client()

available_topics = [
    "bug",
    "improvement",
    "new_feature",
    "documentation",
    "integration",
]

prompt_template = """Classify the type of the issue as one of {topics}.
Issue: {text}"""

@traceable(
    run_type="chain",
    name="Classifier",
)
def topic_classifier(
    topic: str):
    return client.chat.completions.create(
        model="gpt-4o-mini",
        temperature=0,
        messages=[
            {
                "role": "user",
                "content": prompt_template.format(
                    topics=','.join(available_topics),
                    text=topic,
                )
            }
        ],
    ).choices[0].message.content
이제 이 함수와 상호작용을 시작할 수 있습니다. 상호작용할 때 LangSmith run ID를 미리 생성하여 이 함수에 전달합니다. 나중에 피드백을 첨부할 수 있도록 이 작업을 수행합니다. 애플리케이션을 호출하는 방법은 다음과 같습니다:
run_id = uuid.uuid4()
topic_classifier(
    "fix bug in LCEL",
    langsmith_extra={"run_id": run_id})
다음은 나중에 피드백을 첨부하는 방법입니다. 두 가지 형태로 피드백을 수집할 수 있습니다. 먼저 “긍정적인” 피드백을 수집할 수 있습니다. 이것은 모델이 올바르게 예측한 예제에 대한 것입니다.
ls_client = Client()
run_id = uuid.uuid4()
topic_classifier(
    "fix bug in LCEL",
    langsmith_extra={"run_id": run_id})
ls_client.create_feedback(
    run_id,
    key="user-score",
    score=1.0,
)
다음으로, 생성 결과에 대한 “수정”에 해당하는 피드백을 수집하는 데 중점을 둘 수 있습니다. 이 예제에서는 모델이 이를 버그로 분류하지만, 실제로는 문서화로 분류되기를 원합니다.
ls_client = Client()
run_id = uuid.uuid4()
topic_classifier(
    "fix bug in documentation",
    langsmith_extra={"run_id": run_id})
ls_client.create_feedback(
    run_id,
    key="correction",
    correction="documentation")

자동화 설정하기

이제 어떤 형태로든 피드백이 있는 예제를 데이터셋으로 이동하는 자동화를 설정할 수 있습니다. 긍정적인 피드백에 대한 자동화와 부정적인 피드백에 대한 자동화, 두 가지 자동화를 설정합니다. 첫 번째 자동화는 긍정적인 피드백이 있는 모든 실행을 가져와서 자동으로 데이터셋에 추가합니다. 이것의 논리는 긍정적인 피드백이 있는 모든 실행을 향후 반복에서 좋은 예제로 사용할 수 있다는 것입니다. 이 데이터를 추가할 classifier-github-issues라는 데이터셋을 생성하겠습니다. 최적화 부정 두 번째 자동화는 수정이 포함된 모든 실행을 가져와서 웹훅을 사용하여 데이터셋에 추가합니다. 이 웹훅을 생성할 때 “Use Corrections” 옵션을 선택합니다. 이 옵션을 사용하면 실행에서 데이터셋을 생성할 때 실행의 출력을 데이터 포인트의 정답 출력으로 사용하는 대신 수정 내용을 사용하게 됩니다. 최적화 긍정

애플리케이션 업데이트하기

이제 코드를 업데이트하여 실행을 보내고 있는 데이터셋을 가져올 수 있습니다. 데이터셋을 가져오면 예제가 포함된 문자열을 만들 수 있습니다. 그런 다음 이 문자열을 프롬프트의 일부로 넣을 수 있습니다!
### NEW CODE ###
# 데이터셋을 가져오는 데 사용할 수 있도록 LangSmith Client를 초기화합니다
ls_client = Client()

# 예제 목록을 받아 문자열로 포맷하는 함수를 생성합니다
def create_example_string(examples):
    final_strings = []
    for e in examples:
        final_strings.append(f"Input: {e.inputs['topic']}\n> {e.outputs['output']}")
    return "\n\n".join(final_strings)
### NEW CODE ###

client = openai.Client()

available_topics = [
    "bug",
    "improvement",
    "new_feature",
    "documentation",
    "integration",
]

prompt_template = """Classify the type of the issue as one of {topics}.

Here are some examples:
{examples}

Begin!
Issue: {text}
>"""

@traceable(
    run_type="chain",
    name="Classifier",
)
def topic_classifier(
    topic: str):
    # 이제 데이터셋에서 예제를 가져올 수 있습니다
    # 항상 최신 예제를 가져오기 위해 함수 내부에서 이 작업을 수행하지만,
    # 원하는 경우 속도를 위해 외부에서 수행하고 캐시할 수 있습니다
    examples = list(ls_client.list_examples(dataset_name="classifier-github-issues"))  # <- 새 코드
    example_string = create_example_string(examples)
    return client.chat.completions.create(
        model="gpt-4o-mini",
        temperature=0,
        messages=[
            {
                "role": "user",
                "content": prompt_template.format(
                    topics=','.join(available_topics),
                    text=topic,
                    examples=example_string,
                )
            }
        ],
    ).choices[0].message.content
이전과 유사한 입력으로 애플리케이션을 실행하면 문서와 관련된 모든 것(버그라 하더라도)이 documentation으로 분류되어야 한다는 것을 올바르게 학습한 것을 확인할 수 있습니다.
ls_client = Client()
run_id = uuid.uuid4()
topic_classifier(
    "address bug in documentation",
    langsmith_extra={"run_id": run_id})

예제에 대한 시맨틱 검색

추가로 할 수 있는 한 가지는 의미적으로 가장 유사한 예제만 사용하는 것입니다. 이것은 많은 예제가 쌓이기 시작할 때 유용합니다. 이를 위해 먼저 k개의 가장 유사한 예제를 찾는 함수를 정의할 수 있습니다:
import numpy as np

def find_similar(examples, topic, k=5):
    inputs = [e.inputs['topic'] for e in examples] + [topic]
    vectors = client.embeddings.create(input=inputs, model="text-embedding-3-small")
    vectors = [e.embedding for e in vectors.data]
    vectors = np.array(vectors)
    args = np.argsort(-vectors.dot(vectors[-1])[:-1])[:5]
    examples = [examples[i] for i in args]
    return examples
그런 다음 애플리케이션에서 이 함수를 사용할 수 있습니다.
ls_client = Client()

def create_example_string(examples):
    final_strings = []
    for e in examples:
        final_strings.append(f"Input: {e.inputs['topic']}\n> {e.outputs['output']}")
    return "\n\n".join(final_strings)

client = openai.Client()

available_topics = [
    "bug",
    "improvement",
    "new_feature",
    "documentation",
    "integration",
]

prompt_template = """Classify the type of the issue as one of {topics}.

Here are some examples:
{examples}

Begin!
Issue: {text}
>"""

@traceable(
    run_type="chain",
    name="Classifier",
)
def topic_classifier(
    topic: str):
    examples = list(ls_client.list_examples(dataset_name="classifier-github-issues"))
    examples = find_similar(examples, topic)
    example_string = create_example_string(examples)
    return client.chat.completions.create(
        model="gpt-4o-mini",
        temperature=0,
        messages=[
            {
                "role": "user",
                "content": prompt_template.format(
                    topics=','.join(available_topics),
                    text=topic,
                    examples=example_string,
                )
            }
        ],
    ).choices[0].message.content

Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I