LangSmith 평가를 위한 Pytest 및 Vitest 통합 기능 소개
요약
본 기사는 LangSmith의 Pytest 및 Vitest/Jest 통합 기능을 소개하며, LLM 애플리케이션의 신뢰성 있는 평가(evals)를 위한 새로운 방법을 제시합니다. 이 기능은 개발자들이 익숙한 테스트 프레임워크 환경에서 LangSmith의 관찰 가능성과 공유 기능을 결합하여 사용할 수 있게 합니다.
핵심 포인트
- Pytest/Vitest 통합으로 LLM 평가에 친숙한 DX 제공
- LangSmith를 통해 비결정론적 LLM 애플리케이션 디버깅 용이
- 단순 pass/fail을 넘어선 메트릭 로깅 및 회귀 방지 가능
- 실험 결과 공유가 쉬워져 팀 협업 효율성 증대
평가(evals)는 신뢰할 수 있고 고품질의 LLM 애플리케이션을 구축하는 데 필수적인 부분입니다. 평가는 업데이트를 진행하면서도 품질이 일관되게 유지되는지 확인하며 애플리케이션의 성능을 평가하는 데 도움을 줍니다. 만약 소프트웨어 엔지니어링 배경을 가지고 있다면, 이 목적을 위해 테스트를 사용하는 것에 익숙할 것입니다. 이러한 친숙한 인터페이스를 확장하기 위해, LangSmith의 Pytest 및 Vitest/Jest 통합 기능을 사용하여 evals를 실행하는 새로운 방법을 소개하게 되어 기쁩니다.
이러한 새로운 통합 기능은 LangSmith Python 및 Typescript SDK의 v0.3.0 버전에서 현재 베타로 이용 가능합니다.

LLM 평가에 테스트 프레임워크를 사용하는 이유
이미 Pytest 또는 Vitest/Jest를 사용하여 애플리케이션을 테스트하고 있다면, 새로운 LangSmith 통합 기능은 Pytest/Vitest의 유연성, 친숙함 및 런타임 동작과 LangSmith의 관찰 가능성(observability) 및 공유 기능을 결합하여 제공합니다. 이 통합 기능들은 사용자가 익숙한 정확한 개발자 경험(DX)을 사용하며 다음과 같은 이점을 가집니다:
LangSmith에서 테스트 디버깅
LLM을 사용하는 애플리케이션은 비결정론적(non-determistic) 특성 때문에 디버깅 시 추가적인 복잡성을 가집니다. LangSmith는 테스트 케이스의 입력/출력 및 스택 트레이스를 저장하여 문제를 근본 원인으로 찾아내는 데 도움을 줍니다.
LangSmith에 메트릭 로깅 (pass/fail 이상) 및 시간 경과에 따른 진행 상황 추적
일반적으로 테스트 프레임워크는 pass/fail 결과에만 초점을 맞추지만, LLM 애플리케이션을 테스트하는 것은 종종 더 미묘한 접근 방식을 필요로 합니다. 반드시 명확한 pass/fail 기준이 있는 것이 아니라, 결과를 로깅하고 시간이 지남에 따라 애플리케이션이 어떻게 개선되는지 보고 싶을 수 있습니다. LangSmith를 사용하면 피드백을 로깅하고 시간을 두고 결과를 비교하여 회귀(regressions)를 방지하고 항상 애플리케이션의 최고의 버전을 배포하도록 보장할 수 있습니다.

팀과 결과 공유
팀과 결과 공유
LLM을 활용하여 애플리케이션을 구축하는 것은 종종 팀의 노력이 필요합니다. 저희는 프롬프트 생성 과정이나 평가(evals)를 만들 때 해당 분야 전문가(subject matter experts)가 참여하는 경우를 흔히 볼 수 있습니다. LangSmith를 사용하면 실험 결과를 팀 전체와 공유할 수 있어 협업이 더욱 쉬워집니다.
내장 평가 함수 (Built-in evaluation functions)
Python을 사용하고 있다면, LangSmith는 LLM의 출력 결과에 대해 검사하는 데 도움이 되는 몇 가지 내장 평가 함수를 제공합니다. 예를 들어, expect.edit_distance()는 테스트의 출력과 제공된 참조 출력 간의 문자열 거리를 계산하는 데 사용됩니다. 내장 평가 함수에 대한 자세한 내용은 API 레퍼런스를 방문해 주세요.
시작하기
SQL 쿼리를 생성하는 애플리케이션을 평가하는 방법을 보여주는 간단한 테스트 케이스가 있습니다. 이 테스트는 애플리케이션이 주제에서 벗어난 사용자 입력(off-topic user input)을 올바르게 식별하고 그 결과를 LangSmith에 기록하는지 확인합니다. 테스트 스위트를 실행하면 LangSmith에 데이터셋이 생성/업데이트되고 새로운 실험이 만들어집니다.
Pytest로 시작하기
LangSmith에서 테스트를 추적하려면 @pytest.mark.langsmith 데코레이터를 추가하세요.
# tests/test_sql.py
import openai
import pytest
from langsmith import wrappers
from langsmith import testing as t
oai_client = wrappers.wrap_openai(openai.OpenAI())
# 앱 로직은 다른 곳에 정의하세요:
# @traceable
def generate_sql(user_query: str) -> str: ...
@pytest.mark.langsmith
def test_offtopic_input() -> None:
# 테스트 케이스 입력 및 참조 출력을 기록합니다.
user_query = "whats up"
t.log_inputs({"user_query": user_query})
expected = "Sorry that is not a valid question."
t.log_reference_outputs({"response": expected})
actual = generate_sql(user_query)
t.log_outputs({"response": actual})
# 평가 피드백을 생성하는 데 사용되는 모든 단계를 메인 애플리케이션 로직과 분리하여 추적하려면 이 컨텍스트 관리자를 사용하세요.
with t.trace_feedback():
instructions = (
"""
"otherwise return 0. Return only 0 or 1 and nothing else."
)
grade = oai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": instructions},
{"role": "user", "content": f"ACTUAL: {actual}\nEXPECTED: {expected}"},
],
)
score = float(grade.choices[0].message.content)
t.log_feedback(key="correctness", score=score)
assert actual
assert score
테스트를 평소처럼 시작하세요:
`pytest tests`
이렇게 하면 다른 pytest 테스트 실행과 동일하게 작동하며, 모든 테스트 케이스 결과, 애플리케이션 트레이스, 피드백 트레이스를 LangSmith에 기록합니다.




전체 예시는 Pytest how-to 가이드를 방문하세요.
**Vitest로 시작하기**
LangSmith에서 테스트를 추적하려면 테스트 케이스를 `ls.describe()` 블록으로 묶으세요.
`import * as ls from "langsmith/vitest";`
import OpenAI from "openai";
import { traceable } from "langsmith/traceable";
import { wrapOpenAI } from "langsmith/wrappers/openai";
// OPENAI_API_KEY를 환경 변수로 설정하세요
const tracedClient = wrapOpenAI(new OpenAI());
const myEvaluator = async (params: {
outputs: { sql: string };
referenceOutputs: { sql: string };
}) => {
const { outputs, referenceOutputs } = params;
const instructions = [
"Return 1 if the ACTUAL and EXPECTED answers are semantically equivalent, ",
"otherwise return 0. Return only 0 or 1 and nothing else.",
].join("\n");
const grade = await tracedClient.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: instructions,
},
{
role: "user",
content: `ACTUAL: ${outputs.sql}\nEXPECTED: ${referenceOutputs.sql}`,
},
]);
const score = parseInt(grade.choices[0].message.content ?? "");
return { key: "correctness", score };
};
ls.describe("generate sql demo", () => {
ls.test(
"offtopic input",
{
inputs: { userQuery: "whats up" },
referenceOutputs: { sql: "sorry that is not a valid query" },
},
async ({ inputs, referenceOutputs }) => {
const sql = await generateSql(inputs.userQuery); //generateSql은 사용자 입력을 받아 SQL 쿼리를 생성하는 함수입니다.
ls.logOutputs({ sql });
const wrappedEvaluator = ls.wrapEvaluator(myEvaluator);
// "correctness"를 피드백으로 자동으로 기록합니다.
await wrappedEvaluator({
outputs: { sql },
referenceOutputs,
});
}
);
});
Vitest/Jest 사용법 가이드를 방문하여 전체 예제를 확인하세요.
## 테스트 프레임워크 vs. evaluate()
OpenAI Evals, Hugging Face의 Evaluate, LangSmith의 evaluate()와 같은 대부분의 인기 평가 라이브러리는 유사하게 작동합니다. 즉, 먼저 데이터셋을 준비하고, 그 다음 생성 함수와 데이터셋 전체에 실행할 평가자(evaluators) 세트를 정의하는 방식입니다. 이 접근 방식은 에이전트의 입력과 출력을 블랙박스 테스트해야 하는 경우처럼, 동일한 평가 세트를 데이터셋 전반에 걸쳐 실행해야 할 때 잘 작동하는 경향이 있습니다. 하지만 저희는 이것이 일부 경우에는 부족하다는 것을 발견했습니다.
저희가 구축한 여러 애플리케이션에서 새로운 Pytest 및 Vitest/Jest 통합 기능을 적용해 보았고(곧 비디오 공개 예정!), 세 가지 주요 이점을 발견했습니다:
**각 테스트 케이스별 특정 평가 로직.**
애플리케이션의 특정 부분을 평가하고 싶다면, evaluate()를 사용하는 것보다 예제와 평가자를 테스트 케이스로 정의하는 것이 더 유연하고 직관적입니다. 예를 들어, 여러 도구(tools)에 접근할 수 있는 에이전트를 테스트할 때 모델이 각 도구를 호출하는 능력을 테스트하고 싶을 것입니다. 하지만 두 가지 도구를 평가하는 방법은 완전히 다를 수 있어 전역 평가자 함수를 정의하기가 번거롭습니다. 새로운 테스트 통합 기능을 사용하면 각각의 경우에 맞춤형 평가 로직을 가진 별도의 테스트 케이스를 가질 수 있습니다. 실제 작동 방식을 보려면 튜토리얼을 확인해 보세요.
**실시간 로컬 피드백.**
테스팅 프레임워크는 테스트 상태에 대한 실시간 피드백을 제공하여, 문제를 발견하고 수정하는 과정을 훨씬 쉽게 만듭니다. 이러한 빠른 피드백 루프는 애플리케이션의 일부를 모킹(mocking)하면서 로컬에서 반복 작업을 하거나 평가(evals)를 빠르게 테스트해야 할 때 유용합니다.
**CI 파이프라인 통합.**
평가(evals)를 CI 파이프라인의 일부로 실행하면 회귀(regression)를 조기에 발견하는 데 도움이 됩니다. 테스팅 프레임워크는 자연스럽게 통과/실패 기준을 정의하고 CI 워크플로우에서 단언 오류(assertion errors)를 발생시키는 것을 지원합니다.
앞으로 몇 주 안에, 이를 특히 쉽게 구성할 수 있도록 Github Action을 출시할 예정입니다.
## 직접 사용해 보세요!
Pytest 및 Vitest/Jest 통합 기능을 사용하여 평가(evals)를 실행하는 새로운 접근 방식을 공유하게 되어 기쁩니다! 시작하려면 개발자 튜토리얼과 방법 가이드(Python, TypeScript)를 방문하고, 비디오 워크스루(Python, TypeScript)도 확인해 보세요.
피드백이나 기능 요청 사항이 있다면, LangChain Slack 커뮤니티를 통해 저희에게 의견을 주시거나 GitHub에 이슈를 열어 알려주세요. 아직 Slack 커뮤니티의 일원이 아니라면, 여기에서 가입하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 LangChain Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기