skUnit을 사용하여 .NET에서 AI 에이전트 테스트하기: 단계별 가이드
요약
.NET 환경에서 AI 에이전트의 비결정론적 응답을 검증하기 위한 테스트 프레임워크인 skUnit 사용법을 소개합니다. 텍스트 일치 여부가 아닌 에이전트의 행동(behavior)을 검증하는 방법과 도구 기반 에이전트 아키텍처의 이점을 다룹니다.
핵심 포인트
- AI 에이전트의 비결정론적 특성으로 인한 기존 단위 테스트의 한계 설명
- 텍스트 대신 행동(behavior)을 검증하는 skUnit 프레임워크 소개
- 프롬프트 엔지니어링 방식과 도구 기반(Tool-based) 방식의 아키텍처 비교
- 비즈니스 로직을 C#으로 분리하여 유지보수와 테스트 용이성 확보
AI 에이전트(AI agents)를 구축할 때 가장 큰 도전 과제 중 하나는 에이전트를 작성하는 것이 아니라, 그것을 테스트하는 것입니다.
전통적인 단위 테스트(unit tests)는 결정론적(deterministic) 코드에 매우 효과적입니다.
Assert.Equal(4, calculator.Add(2,2));
AI 에이전트는 다릅니다.
에이전트가 올바르게 동작하더라도, 두 응답은 완전히 다를 수 있습니다.
"오늘은 음식이 없습니다."
또는
"오늘은 음식이 전혀 준비되어 있지 않습니다!"
둘 다 정답입니다.
전통적인 단언(assertion) 방식은 이 중 하나를 실패로 처리할 것입니다.
그것이 바로 제가 정확한 텍스트 대신 **행동(behavior)**을 검증할 수 있는 .NET AI 애플리케이션용 테스트 프레임워크인 skUnit을 만든 이유입니다.
이 글에서는 사용자의 기분에 따라 음식을 추천하는 Moody Chef라는 작은 AI 에이전트를 구축하고 테스트해 보겠습니다!
소스 코드 (Source Code)
이 글에서 보여주는 모든 내용은 GitHub에서 확인할 수 있습니다.
https://github.com/mehrandvd/skunit/tree/main/demos/Demo.MoodyChef
함께 따라 해보고 싶다면 클론(Clone)하세요:
git clone https://github.com/mehrandvd/skunit.git
cd skunit/demos/Demo.MoodyChef
문제점 (The Problem)
AI 셰프를 상상해 보세요.
고객의 기분에 따라 음식을 추천합니다.
| 기분 | 메뉴 |
|---|---|
| 행복함 | 피자, 파스타, 샐러드 |
| ... |
이제 사용자가 다음과 같이 작성한다고 가정해 봅시다:
이 빌어먹을 자식아! 무슨 음식이 있어?
에이전트는 사용자가 화가 났음을 인식하고 음식 추천을 피해야 합니다.
이를 어떻게 검증할까요?
다음과 같은 방식으로는 안 됩니다:
Assert.Equal("음식 없음", response);
문구(wording)는 중요하지 않습니다.
중요한 것은 행동(behavior)입니다.
에이전트 구축하기 (Building the Agent)
Moody Chef 데모에는 의도적으로 두 가지 구현 방식이 포함되어 있습니다.
버전 1: 프롬프트 엔지니어링 (Prompt Engineering)
모든 것이 시스템 프롬프트(system prompt) 안에 들어 있습니다.
모델은 다음을 책임집니다:
- 기분 이해
- 올바른 메뉴 선택
- 응답 생성
이 방식은 놀라울 정도로 잘 작동합니다...
...작동하지 않을 때까지는 말이죠.
프롬프트가 복잡해짐에 따라 모델은 일관성 없는 결정을 내리기 시작합니다.
버전 2: 도구 기반 에이전트 (Tool-Based Agent)
모델에게 모든 결정을 내리도록 요청하는 대신, 비즈니스 로직 (Business Logic)을 C#으로 옮깁니다.
모델은 오직 사용자의 기분만을 결정합니다.
그 외의 모든 것은 결정론적 (Deterministic)입니다.
User Message
│
▼
...
이제 LLM (Large Language Model)은 오직 AI 문제만을 해결합니다.
애플리케이션은 비즈니스 문제를 해결합니다.
이러한 아키텍처 (Architecture)는 유지보수와 테스트가 훨씬 더 쉽습니다.
테스트 작성하기
C#에서 어설션 (Assertion)을 작성하는 대신, skUnit을 사용하면 대화 내용을 마크다운 (Markdown)으로 기술할 수 있습니다.
# [USER]
Fuck you bastard! What food do you have?
...
흥미로운 점을 하나 발견해 보세요.
예상되는 어시스턴트 (Assistant) 응답은 실제로 정확한 비교 용도로 사용되지 않습니다.
중요한 부분은 의미론적 어설션 (Semantic Assertion)입니다:
메뉴에 있는 음식을 전혀 제안하지 않는다.
이는 다음의 모든 응답이 통과됨을 의미합니다:
✅ 오늘은 음식이 없습니다.
✅ 다이어트 중이시네요.
✅ 죄송합니다, 추천해 드릴 만한 것이 없습니다.
하지만 다음은 실패합니다:
❌ 피자, 파스타, 그리고 샐러드.
이 방식이 인간이 AI를 평가하는 방식에 훨씬 더 가깝습니다.
시나리오 실행하기
마크다운 파일을 파싱 (Parsing)한 후, skUnit은 에이전트 (Agent)를 대상으로 대화를 실행합니다.
await agent.ExecuteScenarioAsync(...)
이것으로 끝입니다.
내부적으로 skUnit은 다음을 수행합니다:
- 대화를 실행합니다.
- 모든 의미론적 어설션을 평가합니다.
- 실패를 보고합니다.
- 불안정한 동작 (Flaky behavior)을 감지하기 위해 여러 번의 실행을 지원합니다.
Moody Chef 샘플은 모든 시나리오를 세 번씩 실행합니다.
TotalRuns = 3
RequiredSuccessRuns = 3
이를 통해 모델이 단 한 번 운 좋게 맞췄기 때문에 테스트가 통과될 확률을 줄여줍니다.
데모 실행하기
User Secrets를 사용하여 Azure OpenAI 자격 증명을 구성하세요.
그 다음 콘솔 앱을 시작합니다.
cd Demo.MoodyChef.Console
dotnet run
직접 Moody Chef와 채팅할 수 있습니다.
준비가 되면 의미론적 테스트를 실행하세요.
dotnet test
다음 항목들을 수정해 보며 skUnit이 어떻게 반응하는지 확인해 보세요:
- 프롬프트 (Prompt)
- 도구 (Tool)
- 마크다운 시나리오 (Markdown Scenario)
의미론적 어설션이 중요한 이유
대부분의 AI 테스트는 텍스트를 검사하기 때문에 실패합니다.
사용자는 텍스트 자체에는 관심이 없습니다.
그들은 동작(behavior)에 관심을 가집니다.
다음과 같이 질문하는 대신
모델이 정확히 이 단어들을 말했는가?
이렇게 질문하십시오.
모델이 올바른 일을 수행했는가?
이것이 바로 의미론적 어설션 (semantic assertions)이 검증하는 것입니다.
도구 기반 에이전트 (Tool-Based Agents)를 선호하는 이유
Moody Chef 예제는 중요한 설계 원칙을 강조합니다.
LLM이 비즈니스 규칙 (business rules)을 소유해서는 안 됩니다.
대신 다음과 같이 하십시오:
- LLM이 언어를 해석하게 하십시오.
- 애플리케이션이 규칙을 강제하게 하십시오.
그 결과는 다음과 같습니다:
- 더 신뢰할 수 있는 에이전트
- 더 단순한 프롬프트 (prompts)
- 결정론적 비즈니스 로직 (deterministic business logic)
- 좀처럼 불안정해지지 않는 (flaky) 테스트
마치며
AI 에이전트는 문자열 비교보다 더 나은 테스트 도구를 누릴 자격이 있습니다.
skUnit을 사용하면 다음과 같은 작업이 가능합니다:
- Markdown으로 대화 작성
- 의미론적 동작 (semantic behavior) 검증
- 시나리오 반복 실행
- 사용자가 발견하기 전에 회귀 (regressions) 포착
.NET 또는 Semantic Kernel을 사용하여 AI 애플리케이션을 구축하고 있다면, 한 번 시도해 보세요.
⭐ GitHub
https://github.com/mehrandvd/skunit
피드백, 이슈(issues), 그리고 풀 리퀘스트(pull requests)는 언제나 환영합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기