가이드
튜토리얼: HTTP 엔드포인트 뒤의 애플리케이션
코드를 import하지 않고 HTTP로 접근하는 서비스를 평가합니다. Oloproof에 URL을 지정하고, 스위트를 실행하고, 놓친 것을 찾고, 변경을 배포하고, 비교합니다. 작은 로컬 서비스가 여러분의 서비스를 대신하므로 모든 것이 오프라인으로 실행됩니다.
만들 것
고객 메시지를 읽고 두 필드를 추출하는 주문 접수 서비스입니다. intent(where_is_order, cancel, return 또는 other)와 order_id(네 자리 숫자 또는 null)입니다. 참조가 필요 없는 모든 응답의 형식을 확인하고, 참조가 필요한 각 필드의 정답 여부를 측정합니다. 케이스, 실행, 지표, 게이트 같은 용어는 핵심 개념에 정의되어 있습니다.
사전 준비
- Python 3.11 이상, 그리고 가상 환경에 설치된 Oloproof:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- 패키지와 함께 제공되는 예제 프로젝트. 새 디렉터리로 복사해 그곳에서 작업합니다(client.py가 import하는 httpx는 Oloproof와 함께 설치됩니다):
oloproof init --example http order-intake
cd order-intake- 이 머신에서 비어 있는 포트 8765. 사용 중이라면 다른 포트를 골라 서버 명령과 oloproof.yaml 모두에서 바꾸십시오.
여기서는 제공자, API 키, 인터넷을 전혀 사용하지 않습니다. 서비스는 127.0.0.1에서 대기합니다.
파일
order-intake/
server.py the stand-in service (Python standard library only)
client.py a callable that calls the service with a token (used near the end)
oloproof.yaml the suite: dataset, HTTP system, evaluators
release.yaml rules for a run
compare.yaml a rule for a comparison
data/messages.jsonl 20 cases명령은 order-intake/에서 실행하십시오. 두 번째 터미널에서 서비스를 시작하고 계속 실행해 두십시오:
python server.py --port 8765intake service (v1) on http://127.0.0.1:8765/extractHTTP 계약
Oloproof는 케이스마다 요청 하나를 보냅니다. 케이스의 input을 JSON 본문으로 하여, 선언한 메서드(기본값 POST)로 보냅니다. 응답은 JSON으로 읽습니다. 400 이상의 상태 코드, 시간 초과, 연결 거부는 그 케이스의 실행 오류로 기록되며, 결코 틀린 답으로 기록되지 않습니다.
한 케이스의 요청과 응답:
POST /extract
{"message": "Where is order 1042? It has not arrived."}
200 OK
{"result": {"intent": "where_is_order", "order_id": "1042"}, "service": {"rules": "v1"}}output_path: result는 Oloproof에 result만 케이스의 출력으로 남기라고 알립니다. 이것이 없으면 본문 전체가 출력입니다. data.answer처럼 점으로 이은 경로는 더 깊이 들어갑니다.
version: 1
project: order-intake
dataset: data/messages.jsonl
system:
name: order-intake
http:
url: http://127.0.0.1:8765/extract
method: POST
version: rules-v1
output_path: result
timeout_s: 30
evaluators:
- type: json_schema
criterion: format_valid
field: null
schema:
type: object
required: [intent, order_id]
properties:
intent: {enum: [where_is_order, cancel, return, other]}
order_id: {type: [string, "null"], pattern: '^\d{4}$'}
additionalProperties: false
- type: exact_match
criterion: intent_correct
field: intent
- type: exact_match
criterion: order_id_correct
field: order_idHTTP 시스템은 version을 선언해야 합니다. Oloproof는 배포를 볼 수 없습니다. 각 케이스의 출력을 URL, 메서드, 출력 경로, 그 버전으로 캐시하므로, 버전이 서비스가 바뀌었음을 알리는 방법입니다. 바꾸는 것을 잊으면 새 배포는 결코 호출되지 않습니다.
system.http에서는 아직 헤더와 인증을 구성할 수 없습니다. 아래 토큰에 관한 절에서 우회 방법을 보여 줍니다.
데이터셋
{"id":"m04","input":{"message":"Has order #5120 shipped yet?"},"expected":{"intent":"where_is_order","order_id":"5120"}}
{"id":"m07","input":{"message":"Do you ship to Canada?"},"expected":{"intent":"other","order_id":null}}
{"id":"m16","input":{"message":"Please refund and take back the lamp from order #1560."},"expected":{"intent":"return","order_id":"1560"}}input은 요청 본문 그대로입니다. expected는 각 필드의 참조를 담습니다. null은 "이 메시지에는 주문 id가 없다"는 뜻의 실제 참조 값입니다.
평가기 고르기
| 기준 | 평가기 | expected 필요 | 측정하는 것 |
|---|---|---|---|
| format_valid | 출력에 대한 json_schema | 아니요 | 형식: 알려진 intent와 올바른 형식의 id |
| intent_correct | intent에 대한 exact_match | 예 | 첫 번째 필드의 작업 성공 |
| order_id_correct | order_id에 대한 exact_match | 예 | 두 번째 필드의 작업 성공 |
스키마는 모든 메시지에 대해 {"intent": "other", "order_id": null}을 통과시킬 것입니다. 형식은 올바르지만 쓸모없습니다. 서비스가 제 일을 했는지는 참조 검사만이 말해 줍니다. 필드를 따로 채점하면 어느 쪽이 실패하는지 보이며, 하나로 합친 검사는 이를 숨깁니다.
release.yaml은 형식 실패를 허용하지 않고, 각 필드가 최소 70%는 맞을 것을 95% 구간으로 판단해 요구합니다:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: valid-format
metric: format_valid
kind: observed_count
max_failures: 0
- id: intent-floor
metric: intent_correct
min: 0.70
- id: order-id-floor
metric: order_id_correct
min: 0.70실행하기
oloproof runRun run_01M4... [DECIDED/COMPLETE]
Gate: BLOCK (exit 3)
│ valid-format │ format_valid │ PASS │ observed_failures_within_limit │
│ intent-floor │ intent_correct │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ order-id-floor │ order_id_correct │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ format_valid │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ intent_correct │ 75.0% │ [50.8%, 91.4%] │ 15 / 20 observed · 0 missing · 0 excluded │
│ order_id_correct │ 75.0% │ [50.8%, 91.4%] │ 15 / 20 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/20 miss; judgment 0 hit/60 miss모든 응답의 형식이 올바릅니다. 각 필드는 20번 중 15번 맞지만, 20개 케이스로는 구간이 50.8%까지 내려가므로 어느 쪽의 70% 하한도 입증되지 않습니다. INSUFFICIENT_EVIDENCE이고, 게이트는 종료 코드 3으로 차단합니다.
실패 살펴보기
oloproof inspect RUN_ID --failures8 of 20 cases failed, errored or did not finish
m04
output: {"intent": "where_is_order", "order_id": null}
order_id_correct: failed
m06
output: {"intent": "other", "order_id": "7011"}
intent_correct: failed
...
m18
output: {"intent": "other", "order_id": null}
intent_correct: failed
order_id_correct: failed옆에 입력을 함께 읽으면(oloproof inspect RUN_ID --case m04) 두 가지 결함이 드러납니다. id 패턴은 "order 1234"에만 일치하고 "order #5120", "order no. 8123", 단독 "#1673"에는 일치하지 않습니다. 그리고 "send back", "stop order", "where's my parcel" 같은 표현은 어떤 intent에도 대응되지 않습니다. 다음 행동은 두 규칙을 모두 넓히는 것입니다.
변경 배포하기
서비스를 멈추고 그 수정을 담은 후보를 시작하십시오:
python server.py --port 8765 --rules v2oloproof.yaml에서 version: rules-v1을 version: rules-v2로 바꾸십시오. URL이 바뀌지 않았으므로 그렇지 않으면 Oloproof가 이전 출력을 재사용합니다. 그런 다음:
oloproof runGate: ALLOW (exit 0)
│ intent_correct │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ order_id_correct │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │두 하한이 모두 통과하고 명령은 0으로 종료합니다.
두 배포 비교하기
compare.yaml은 후보의 intent가 기준선보다 나은지 묻습니다:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: intent-better
kind: superiority
metric: intent_correctoloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlformat_valid: +0.0 points [-23.6, +23.6] · 20 paired · 0 missing · 0 excluded
intent_correct: +25.0 points [-8.5, +58.3] · 20 paired · 0 missing · 0 excluded
order_id_correct: +25.0 points [-8.5, +58.3] · 20 paired · 0 missing · 0 excluded
Decisions
intent-better intent_correct superiority INSUFFICIENT_EVIDENCE interval_overlaps_zero
Gate: BLOCK (exit 3)후보는 자체 하한을 통과하지만, 비교는 더 낫다는 것을 보일 수 없습니다. 바뀐 케이스는 다섯 개이고, 20개의 짝지은 케이스로는 개선폭의 구간이 여전히 0을 포함합니다. 두 진술은 동시에 참입니다. "요구 사항을 충족한다"와 "기준선을 이긴다"는 별개의 질문이며, 이만큼 작은 스위트는 큰 효과에 대해서만 두 번째 질문에 답합니다. 해결책은 실제 메시지를 더 모으는 것입니다.
토큰이 필요한 서비스
bearer 토큰을 요구하도록 서비스를 시작하십시오:
INTAKE_TOKEN=s3cret python server.py --port 8765 --rules v2 --require-tokensystem.http는 사용자 정의 헤더를 보내지 않으므로, 새 version(예: rules-v2-auth)으로 실행하면 모든 호출이 거부됩니다:
Gate: BLOCK (exit 3)
│ valid-format │ format_valid │ INSUFFICIENT_EVIDENCE │ no_observations │
│ intent_correct │ │ [0.0%, 100.0%] │ 0 / 0 observed · 20 missing · 0 excluded │그리고 oloproof inspect RUN_ID --failures는 각 케이스에서 execution ERROR: TransientError: system returned HTTP 401을 보여 줍니다. 실행 오류는 실패가 아니라 누락된 근거입니다. 아무것도 관측되지 않았으므로 모든 규칙이 INSUFFICIENT_EVIDENCE입니다.
우회 방법은 요청을 직접 보내는 Python callable입니다. client.py는 환경 변수에서 헤더를 추가하므로, 토큰은 oloproof.yaml이나 저장된 어떤 기록에도 들어가지 않습니다:
URL = os.environ.get("INTAKE_URL", "http://127.0.0.1:8765/extract")
def extract(case: dict[str, Any]) -> dict[str, Any]:
headers = {"Authorization": f"Bearer {os.environ['INTAKE_TOKEN']}"}
response = httpx.post(URL, json=case, headers=headers, timeout=30)
response.raise_for_status()
return response.json()["result"]oloproof.yaml의 http: 블록을 다음으로 바꾸고:
system:
name: order-intake
version: rules-v2
callable: client:extract환경에 토큰을 넣고 실행하십시오:
INTAKE_TOKEN=s3cret oloproof run20개 케이스가 모두 다시 관측되고 게이트는 허용합니다. callable의 캐시는 그 뒤의 서비스가 아니라 자신의 소스와 version을 따르므로 같은 규칙이 적용됩니다. 배포할 때 version을 바꾸십시오.
문제 해결
| 증상 | 원인과 해결 |
|---|---|
| 모든 케이스에서 system connection failed (ConnectError) | 서비스가 실행 중이 아니거나 다른 포트에서 대기 중입니다. |
| system connection failed (RemoteProtocolError) | 그 포트에서 다른 것이 응답합니다. 비어 있는 포트를 고르십시오. |
| KeyError: "missing output path 'results'" | output_path가 응답에 없는 필드를 지정합니다. |
| system returned HTTP 401 또는 403 | 엔드포인트에 자격 증명이 필요합니다. callable 우회 방법을 사용하십시오. |
| 변경을 배포했는데 캐시 줄이 모두 적중으로 나옴 | version이 바뀌지 않아 저장된 출력이 재사용되었습니다. |
| an HTTP system needs a declared version | system 또는 system.http 아래에 version을 추가하십시오. |
한계
- system.http에는 사용자 정의 헤더, 인증, 쿼리 매개변수, 요청 템플릿이 없습니다. 케이스 input이 그대로 JSON 본문입니다. 그 밖의 것은 callable을 사용하십시오.
- 응답은 JSON이어야 합니다. 스트리밍 응답은 스트림으로 읽히지 않습니다.
- Oloproof는 배포를 감지할 수 없습니다. 선언된 version이 응답한 대상의 정체성 전부입니다.
- 서비스의 상태는 서비스가 관리합니다. Oloproof는 요청을 보내고 답을 기록할 뿐, 요청이 바꾼 것을 초기화하거나 샌드박스에 넣거나 되돌리지 않습니다.