Laya로 문의를 분류할 때 평균 점수와 선택 등급을 나누는 법
Laya로 문의 처리 순서를 만들려면 평균 점수, 선택 등급, 높은 등급에 놓인 확률을 따로 읽어야 합니다. Python SDK의 score는 등급 번호의 기댓값이고, 정수 스키마로 받는 값은 가장 확률이 높은 등급입니다. 작은 팀이 둘을 같은 ‘긴급도’ 열에 저장하면 정렬과 분류의 뜻이 달라질 수 있습니다. 하나의 가상 분포로 어떤 값을 어디에 쓸지 살펴보겠습니다.
확인: 2026년 10월 17일 UTC · 대상: Laya Python SDK 0.4.11 · 공식 문서·소스·배포 wheel을 정적으로 비교했습니다. Laya 설치, 모델 추론, 성능 측정은 하지 않았습니다. 아래 확률은 설명을 위해 만든 값입니다. 공개 자료 조사와 AI의 도움으로 작성했습니다.
문장을 생성하는 대신 정해진 선택지를 평가하는 Laya
Laya는 텍스트나 문의 내용에 대해 선택형 choice, 순서형 score, 참·거짓 확률인 noul 질문을 평가하는 프로젝트입니다. 팀이 정한 담당 부서나 문의 유형처럼 답의 범위가 고정된 작업에 쓸 수 있습니다. 답변 문장을 생성한 뒤 부서명을 다시 파싱하는 절차를 줄일 수 있지만, 실제 분류의 정확도는 별도로 평가해야 합니다.
0.4.11 릴리스는 2026년 10월 17일 17:03 UTC에 공개됐고, 같은 시각 PyPI 배포도 확인했습니다. Rust SDK와 ONNX 내보내기, MCP·TypeScript 관련 변경이 포함됐습니다. 이 글은 해당 버전의 Python 동작을 검토하며, 평균과 선택 등급의 구분이 이번 버전에 새로 생겼다는 뜻은 아닙니다.
최근 관심도 확인할 수 있습니다. Archestack의 10월 17일 집계는 GitHub 별이 9월 18일 335개에서 10월 16일 33,196개로 늘었다고 기록합니다. 이는 해당 서비스가 제시한 날짜별 증가 자료이며, 실제 사용 규모나 모델 정확도를 측정한 수치는 아닙니다.

평균 2.15인데 선택 등급은 1인 경우
문의 긴급도를 0부터 4까지 다섯 단계로 표현한다고 가정하겠습니다. 숫자가 클수록 더 급하다는 순서만 정한 예입니다. 단계별 처리 기준과 시간은 팀이 따로 정의해야 합니다.
아래는 Laya가 실제로 반환한 결과가 아닌, 계산을 설명하기 위해 작성한 가상 확률 분포입니다.
| 등급 번호 | 가상 확률 |
|---|---|
| 0 | 0.05 |
| 1 | 0.55 |
| 2 | 0.00 |
| 3 | 0.00 |
| 4 | 0.40 |
이 분포를 세 가지 방식으로 읽으면 다음과 같습니다.
- 평균 점수는 2.15입니다. 각 번호에 확률을 곱해 더하면 0×0.05 + 1×0.55 + 2×0 + 3×0 + 4×0.40 = 2.15입니다.
- 가장 확률이 높은 등급은 1입니다. 이 등급의 확률 0.55가 가장 큽니다.
- 3 이상에 놓인 확률의 합은 0.40입니다. 높은 등급 쪽에 얼마나 무게가 남아 있는지 보여 줍니다.
평균 2.15를 반올림하면 2가 됩니다. 그런데 이 예시에서 등급 2의 확률은 0입니다. 평균은 서로 떨어진 등급에 놓인 무게를 하나의 숫자로 요약하므로, 가장 가능성 높은 범주를 고르는 용도로 그대로 반올림하면 뜻이 바뀝니다.
0.4.11이 두 값을 만드는 방식
공개된 score 디코딩 코드는 0부터 시작하는 등급 번호에 확률을 곱해 더한 값을 반환합니다. 각 등급의 설명인 legend와 전체 probabilities도 함께 돌려줍니다.
반면 minimum: 0, maximum: 4인 정수 스키마의 변환 코드는 확률이 가장 큰 번호를 선택합니다. 최솟값이 0이 아니면 그 값을 더합니다. 따라서 위 가상 분포를 이 변환 규칙으로 읽으면 정수 값은 1입니다. 이 동작은 공식 스키마 안내에도 명시돼 있습니다.
확률 필드가 없는 응답에는 평균 번호를 반올림하는 대체 경로가 있지만, 정상적으로 확률이 포함된 응답에는 그 경로를 쓰지 않습니다. 평균 기반 정책을 구현하려고 확률을 일부러 버리기보다, 원래 응답을 보존하고 애플리케이션에서 사용할 요약값을 명시하는 편이 이해하기 쉽습니다.
비교할 때 입력도 같아야 합니다. 정수 스키마의 질문 생성 코드는 등급을 숫자 문자열로 만듭니다. 사람이 각 단계의 설명을 적은 score 질문과는 모델이 읽는 내용이 달라질 수 있습니다. 이 글의 계산은 하나의 분포를 여러 방식으로 읽은 것이며, 서로 다른 질문으로 호출해도 같은 확률이 나온다는 주장이 아닙니다.
팀의 문의함에는 무엇을 저장할까
먼저 그 열이 해야 할 일을 정하십시오. 같은 숫자 칸으로 모두 처리하기보다 다음처럼 역할을 나누는 편이 좋습니다.
- 분류 표시: ‘가장 가능성 높은 긴급도 등급’을 보여 주려면 선택 등급을 저장합니다. 이 예에서는 1입니다.
- 정렬 보조: 팀이 등급 번호 사이의 간격을 받아들이고 평균 기반 정렬을 선택했다면 2.15를 별도 열에 둘 수 있습니다. 순서형 번호의 평균이 고객 피해나 처리 비용을 뜻하는 것은 아닙니다.
- 사람의 검토 후보: 높은 등급을 놓치는 비용이 크다면 3 이상 확률의 합도 보존할 만합니다. 다만 어느 값에서 검토할지는 실제 문의에 붙인 정답과 업무 비용으로 정해야 합니다. 여기의 0.40을 곧바로 실제 긴급 문의일 확률로 해석해서는 안 됩니다.
스키마 API를 쓴다면 return_details=True로 한 번의 호출에서 선택 값과 원래 응답을 함께 받을 수 있습니다. result.values["urgency"]는 선택 등급, result.answers["urgency"]["score"]는 원래 평균 번호, result.probabilities["urgency"]는 등급별 분포입니다. 이 설명은 신뢰도 필터를 켜지 않고 정상 응답을 받은 경우를 전제로 합니다.
문의함에는 이런 값에 더해 사용한 등급 설명, 체크포인트·SDK 버전, 사람이 최종 판단한 등급을 함께 남기는 것이 유용합니다. 그래야 정렬 방식 변경과 모델 판단 변경을 구별해 비교할 수 있습니다. 평균을 선택 등급으로 바꾸는 것만으로 모델의 오분류가 해결되지는 않습니다.
모델 없이 계산만 확인하는 예제
다음 코드는 Python 표준 라이브러리만 사용합니다. urgency_math.py로 저장해 python3 urgency_math.py로 실행할 수 있으며, Laya를 불러오거나 네트워크에 접속하지 않습니다. 고정된 가상 확률에서 평균 2.15, 최빈 번호 1, 반올림 번호 2, 그 번호의 확률 0, 3·4번 합계 0.40을 계산하도록 작성했습니다.
"""Authored synthetic arithmetic only. No Laya import, model, or network call."""
from decimal import Decimal, ROUND_HALF_EVEN
import json
probabilities = [Decimal(x) for x in ("0.05", "0.55", "0", "0", "0.40")]
assert sum(probabilities) == Decimal("1")
mean_index = sum(Decimal(i) * p for i, p in enumerate(probabilities))
mode_index = max(range(len(probabilities)), key=probabilities.__getitem__)
rounded_mean = int(mean_index.to_integral_value(rounding=ROUND_HALF_EVEN))
high_mass = sum(probabilities[3:])
assert mean_index == Decimal("2.15")
assert mode_index == 1
assert rounded_mean == 2
assert probabilities[rounded_mean] == Decimal("0")
assert high_mass == Decimal("0.40")
print(json.dumps({
"expected_index": str(mean_index),
"most_probable_index": mode_index,
"rounded_mean_index": rounded_mean,
"mass_at_rounded_mean": str(probabilities[rounded_mean]),
"mass_at_levels_3_and_4": str(high_mass),
}, ensure_ascii=False, indent=2))
Python 3.12.14에서 위 코드를 그대로 실행했고 모든 assert가 통과했습니다. 예상한 값과 실제 출력이 일치했습니다.
{
"expected_index": "2.15",
"most_probable_index": 1,
"rounded_mean_index": 2,
"mass_at_rounded_mean": "0",
"mass_at_levels_3_and_4": "0.40"
}
한국어 문의에 적용하기 전 확인할 조건
현재 공식 README의 한계 설명에는 다국어 체크포인트의 score가 첫 번째 등급을 잘 선택하지 않는 위치 편향이 적혀 있습니다. 공식 문서는 다국어 모델에 맞춘 온도 보정값도 기본 제공되지 않는다고 설명합니다. 따라서 한국어 지원 표기만 보고 긴급도를 자동 배정하기보다, 실제 한국어 문의를 사람이 분류한 별도 평가 자료로 먼저 확인해야 합니다.
작게 시작한다면 기존 처리 순서는 유지하고 추천 태그만 나란히 기록할 수 있습니다. 특히 실제 긴급 문의가 낮은 등급으로 내려간 비율과 사람이 검토해야 할 문의 수를 함께 보면, 어떤 요약값과 검토 기준이 팀에 맞는지 판단하기 쉽습니다. 보정값이나 기준을 맞춘 자료와 최종 평가 자료도 분리해야 합니다.
0.4.11 패키지는 Python 3.10 이상과 PyTorch·Transformers 등을 요구하며, 개발 상태는 Beta로 표시합니다. 기본 Hub 체크포인트 경로에서는 캐시에 없는 가중치를 처음 사용할 때 내려받습니다. 이미 캐시했거나 로컬 가중치를 지정했다면 다시 내려받을 필요가 없습니다. 이후에도 모델을 메모리에 올리고 CPU·GPU 연산을 수행하는 자원 비용이 있으므로, 무료 코드라는 이유만으로 운영 부담까지 사라지는 것은 아닙니다.
코드는 Apache-2.0이고, 공식 모델 카드도 Apache-2.0을 선언합니다. 재배포 시 고지 등 라이선스 조건과 별도 의존성 조건을 확인해야 합니다. 이 글에서는 체크포인트를 내려받거나 한국어 정확도를 측정하지 않았습니다.
한 개의 긴급도 숫자를 정하기 전에
선택 등급은 분류를, 평균은 명시적으로 정한 숫자 요약을, 전체 분포는 그 요약이 숨긴 차이를 보여 줍니다. Laya를 문의함에 붙이는 팀이라면 먼저 이 세 가지를 보존한 뒤, 어떤 값으로 표시하고 정렬하며 사람이 검토할지 결정하십시오. 그 구분이 있어야 추후 모델이나 정책을 바꿀 때도 결과를 설명할 수 있습니다.
검토 범위: d74e8be 커밋의 agent.py, structured.py, confidence.py가 PyPI 0.4.11 wheel의 해당 파일과 바이트 단위로 일치함을 확인했습니다. wheel SHA-256도 PyPI 기록과 대조했습니다. 관련 upstream 테스트는 읽기만 했습니다. 가상 분포의 산술과 실제 모델의 정확도·속도는 별개의 검증 대상입니다.
댓글
댓글 쓰기