# Milestone: OpenAI-compatible 출력 검증 필터

## 위치

- Roadmap: [ROADMAP.md](../../../ROADMAP.md)
- Phase: [PHASE.md](../PHASE.md)

## 목표

OpenAI-compatible Chat Completions provider 경로에서 모델 출력/행동 이상을 caller 종류와 무관하게 request history와 provider response stream으로 감지하고, 사용자 경험을 해치지 않는 방식으로 관찰/보정/중단/재시도/검증한다.
반복 루프는 단일 stream content 반복, request history에 누적된 assistant-only anchor 반복, 동일 tool/action 반복을 모두 포함한다. 단일 stream content 반복은 streaming passthrough를 유지한 채 upstream만 교체해 continuation repair로 이어 쓴다. assistant history anchor는 표준 role과 `content`/provider reasoning alias를 기준으로 탐지하고, caller가 reasoning history를 재전송하지 않거나 conversation identity가 없으면 존재하지 않는 cross-request state를 추론하지 않는다. 진행 중 reasoning/history를 실제로 제거할지, 무진전 반복을 repair로 승격할지는 [SDD 사용자 리뷰](../../../sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/USER_REVIEW.md)에서 확정한다. tool/action 반복은 `tool name + normalized args` fingerprint와 완료된 이전 tool result의 동일/no-progress 신호를 기준으로 감지해 side-effect 안전성이 없으면 repair 대신 안전 중단한다. `metadata.scheme` JSON 출력 계약은 검증 전 downstream content streaming을 막는 `contract_schema` 경로로 처리한다.

## 상태

[계획]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 잠금
- SDD: 필요
- SDD 문서: [SDD.md](../../../sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/SDD.md)
- SDD 사유: OpenAI-compatible metadata schema, streaming retry/abort 상태 전이, provider 응답 검증 계약이 바뀌는 Milestone이다.
- 잠금 해제 조건:
  - [ ] SDD 잠금이 해제되어 있다
  - [ ] SDD 사용자 리뷰가 없거나 승인/해결되었다
  - [x] Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다
  - [x] Evidence Map이 완료 시 `Roadmap Completion`과 최종 검증 evidence로 검증 가능하게 연결되어 있다
- 결정 필요:
  - [ ] [D01] assistant history anchor의 preflight history sanitation, live reasoning dedupe, no-progress repair 승격 정책
  - [ ] [D02] 이번 Milestone의 endpoint 범위를 `/v1/chat/completions`로 유지할지 `/v1/responses`까지 확장할지
  - [ ] [D03] 기존 assembled content/reasoning 운영 로그의 raw 보존과 기본 redaction 정책

## 범위

- OpenAI-compatible `/v1/chat/completions` provider route의 출력 검증 필터 모듈과 response path 선택
- caller/agent 이름이 아닌 OpenAI-compatible role, message field, response delta, tool contract capability만 사용하는 caller-neutral 판정 경계
- 출력 검증 filter별 enable/disable 정책을 environment(`dev`, `dev-corp`), model group/model/provider, 기능 단위로 평가하는 config/registry 계층
- `/v1/chat/completions` provider SSE의 chunk 조립, bounded look-behind/tail 보류, terminal/tool event 판정, 안전한 release와 상위 filter/policy 계층이 전달한 replacement 적용을 담당하는 stream 처리 모듈
- 반복 출력 루프 감지용 single-stream rolling inspector, incoming request-history 기반 assistant anchor inspector, cross-request tool/action fingerprint inspector, bounded text/tool-call fragment hold/release 판정, upstream abort, continuation repair, 1회 repair 제한, side-effect 구간 안전 중단
- `metadata.scheme` JSON schema 계약 수신, 마지막 user message prompt append, buffered validation, schema 위반 시 bounded retry
- `passthrough`, `passthrough_guarded`, `contract_schema` 내부 response path 구분과 실행 로그/관측 기준. 이 이름들은 caller가 지정하는 공개 request field가 아니라 IOP 내부 경로/로그 기준이다.
- normalized 실행 경로와 CLI adapter 경로를 OpenAI-compatible provider 출력 검증 경로와 분리하는 책임 경계

## 기능

### Epic: [output-filter] Output Validation Filters

OpenAI-compatible provider 응답을 사용자에게 노출하기 전에 필터별 정책으로 감시, 중단, 재시도, 검증하는 capability를 묶는다.

- [ ] [contract-doc] OpenAI-compatible 계약 문서와 구현 타입이 `metadata.scheme`, 내부 `contract_schema`/`passthrough_guarded` path, normalized CLI-only 경계, caller-neutral 반복 guard 입력, conversation identity가 없을 때의 degraded behavior를 설명한다. 검증: [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md)와 관련 Go 타입/handler 테스트가 새 계약과 일치하고 Pi/Codex/특정 SDK 이름을 runtime 조건으로 사용하지 않는다.
- [ ] [filter-pipeline] Edge Chat Completions provider route에 여러 출력 검증 filter를 붙일 수 있는 모듈 파이프라인이 생기고, 요청별로 pure passthrough, guarded stream, schema contract 경로를 결정한다. 검증: `go test ./apps/edge/internal/openai -count=1`에서 path selection과 unknown/unsupported 조합 테스트가 통과한다.
- [ ] [stream-tail-module] stream 처리 모듈이 `/v1/chat/completions` SSE chunk 조립, bounded look-behind/tail 보류, terminal/tool event 판정, safe release와 상위 filter/policy 계층이 전달한 replacement 적용을 담당하고, 반복 보정·workflow routing 같은 정책 결정은 호출 계층에 남긴다. 다른 endpoint와 agent-family codec은 이번 구현 범위에 포함하지 않는다. 검증: Chat Completions split chunk, terminal marker, tool-call fragment, buffer 한도, pass-through release, terminal replacement fixture가 전체 응답 buffering 없이 통과한다.
- [ ] [filter-policy] 각 출력 검증 filter는 공통 interface/registry를 통해 enable/disable 정책을 평가하고, environment(`dev`, `dev-corp`)와 model group/model/provider/protocol capability별로 반복루프 guard, schema contract 같은 기능을 독립적으로 켜고 끌 수 있다. 검증: generic Chat Completions client와 qwen/gemma/ornith fixture 기반 config/handler tests에서 필터별 활성/비활성, 정책 우선순위, disabled filter observation이 통과하고 caller/agent 제품명에 따른 분기가 없음을 확인한다.
- [ ] [repeat-guard] content 반복은 단일 provider stream의 rolling window로 감지한다. assistant history anchor는 현재 incoming `messages`의 `role=user|assistant`, `content`, `reasoning_content`, `reasoning`, `reasoning_text`를 raw Chat Completions payload에서 role/channel별로 분리해 user 입력에는 없고 assistant history에 N회 누적된 plain-text fingerprint를 provider dispatch 전에 감지한다. 이 request-history 판정은 Pi session이나 특정 caller SDK에 의존하지 않는다. 명시적 conversation identity 계약이 없는 요청에는 stable lineage를 추정하거나 caller 간 TTL state를 공유하지 않으며, caller가 reasoning history를 재전송하지 않으면 current request/stream에서 관찰 가능한 범위로 낮춘다. history sanitation과 live reasoning dedupe는 [D01](../../../sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/USER_REVIEW.md)의 승인 범위에서만 수행하고, assistant final `content`, tool call, signed/encrypted/unknown reasoning field는 조용히 변경하지 않는다. progress는 current response가 아니라 incoming history에서 완료된 이전 tool call/result/error만으로 판정하며 서로 다른 action 자체를 progress로 단정하지 않는다. 현재 provider tool call delta는 완성 전 최소 fragment만 hold해 release/block을 결정하고, 이미 downstream으로 tool call이 나갔거나 side effect 가능성이 있으면 자동 repair하지 않고 guard terminal error 또는 안전 중단으로 끝낸다. 검증: generic raw HTTP/OpenAI SDK fixture가 single-stream 반복, assistant-history anchor, reasoning alias 조합, reasoning-history 미전송 caller, conversation identity 부재, bounded hold, progress/no-progress, 1회 repair 후보, safe prefix, `[DONE]` 단일 종료, tool call release/side-effect 경계를 확인한다. 2026-07-16 Pi/Ornith evidence는 이 generic fixture의 입력 사례로만 사용한다.
- [ ] [schema-contract] `metadata.scheme`이 있으면 `stream=true` 요청이어도 downstream content streaming을 보류하고, 마지막 user message에 scheme 계약 block을 append한 뒤 JSON parse/schema validation, 실패 시 1회 재요청, 성공 시 validated JSON만 반환한다. 검증: valid JSON, invalid-then-repair, retry-exhausted, multimodal user content append fixture가 통과한다.
- [ ] [ops-evidence] 출력 필터 결과가 요청 실행 로그와 smoke에서 원인 축을 구분할 수 있게 남고, 실제 incident는 raw prompt/tool args/result를 제외한 별도 sanitized evidence log로 generic 회귀 fixture에 연결된다. 검증: generic raw HTTP/OpenAI SDK smoke를 필수 기준으로 실행하고, Pi TUI는 선택적 caller field smoke로 추가한다. role/channel provenance, reasoning history 미전송, provider 전환, 반복 fragment 관찰/보정/중단 또는 schema validation 결과가 model/provider/IOP/protocol 축과 함께 관찰되며, raw assembled output 로그 정책은 D03 결정과 일치한다.

## 완료 리뷰

- 상태: 없음
- 요청일: 없음
- 완료 근거: 기능 Task가 아직 충족되지 않았다.
- 검토 항목:
  - [ ] `complete.log`의 `Roadmap Completion`이 각 기능 Task id를 기록한다.
  - [ ] 최종 검증 출력이 SDD Evidence Map과 일치한다.
  - [ ] generic raw HTTP/OpenAI SDK 기준 single-stream 반복, assistant history anchor 반복, reasoning-history 미전송 caller, 동일 action 반복과 `metadata.scheme` 케이스가 모두 확인되고 Pi TUI 결과는 선택적 field evidence로 분리된다.
- agent-ui 상태 반영: 해당 없음
- 리뷰 코멘트: 없음

## 범위 제외

- normalized 실행 경로를 provider 출력 검증 필터와 섞는 작업
- CLI adapter 전용 normalized protocol 변경
- Pi session JSONL, Pi SDK 내부 message type, Pi local tool invocation을 IOP 반복 guard의 runtime 입력이나 필수 의존성으로 사용하는 방식
- 명시적 conversation identity 없이 caller/model을 조합한 hash를 대화 식별자로 간주하거나 caller 간 TTL 반복 state를 공유하는 방식
- 반복루프 감지를 위해 전체 응답을 buffer한 뒤 사용자에게 늦게 보내는 방식
- cross-request 반복 증거에 raw user prompt, raw tool args/result, 전체 reasoning/content를 저장하는 방식
- 진행 신호가 있는 서로 다른 tool/action을 assistant history anchor 반복만으로 차단하는 방식
- assistant final `content`를 history anchor fingerprint만으로 조용히 삭제하는 방식
- schema 계약이 있는 요청에서 검증 전 content delta를 사용자에게 먼저 노출하는 방식
- validator 모델을 이용한 자연어/tool-call 판정 gate
- 누적 요청 컨텍스트 최적화, 장기 RAG, advisor/Context Hook, workflow routing 정책

## 작업 컨텍스트

- 관련 경로: `apps/edge/internal/openai`, `apps/edge/internal/service`, `apps/node/internal/adapters/openai_compat`, `apps/node/internal/runtime`, `packages/go/config`, [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md)
- 표준선(선택): 반복루프 필터는 streaming passthrough UX를 유지하는 `passthrough_guarded` 경로이며, 이미 흘린 정상 prefix를 버리지 않고 continuation repair로 이어 쓴다.
- 표준선(선택): 반복루프 필터는 텍스트 n-gram/문단 반복뿐 아니라 tool/action 반복도 본다. action fingerprint는 `tool name + normalized args`를 안정적으로 정규화해 만들고, 로그/metric label에는 raw args나 secret 가능 문자열을 넣지 않고 hash/redacted summary만 남긴다.
- 표준선(선택): cross-request history anchor guard의 1차 source of truth는 현재 incoming Chat Completions `messages`다. user와 assistant provenance를 분리하고 user 입력에 같은 fingerprint가 있으면 assistant-only로 단정하지 않는다. caller가 assistant reasoning을 history에 재전송하지 않으면 해당 channel의 과거 반복을 만들어내지 않으며, 명시 conversation identity가 없는 MVP에서는 별도 TTL lineage를 추정하지 않는다.
- 표준선(선택): progress 판정은 validator 모델 없이 incoming history에 이미 완료된 tool/action fingerprint와 result/error hash, terminal 상태만 사용한다. 현재 response의 tool result는 아직 존재하지 않으므로 progress 근거로 사용하지 않는다. history sanitation, live reasoning dedupe, no-progress repair의 활성 조합은 D01 결정 후 filter policy로 고정한다.
- 표준선(선택): incident 회귀 증거는 [2026-07-16 Pi/Ornith cross-request history anchor](../../../sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/evidence/2026-07-16-pi-ornith-cross-request-history-anchor.log)처럼 SDD와 함께 보존한다. exact anchor는 비민감 예시로 명시 승인된 경우만 남기고, 일반 로그에는 hash, redacted preview, role/channel, repeat/progress/decision만 기록한다.
- 표준선(선택): tool/action 반복은 단일 stream 안에서만 판단하지 않는다. 현재 incoming `messages` 안의 assistant tool call과 완료된 tool result, 현재 provider tool call delta, consecutive 동일 fingerprint, 동일/no-progress tool result, threshold를 함께 만족해야 한다. 명시적 conversation identity가 추가되기 전에는 request 외 TTL/session state를 보충 근거로 사용하지 않는다. action guard가 활성화된 tool call delta는 full fingerprint 판정 전까지 해당 tool call fragment만 hold하며 전체 content stream을 buffer하지 않는다. `git status`, `ls`, polling처럼 정상적으로 반복될 수 있는 read-only action은 model/provider/tool별 threshold/allow policy로 조정하고, dev-corp 초기 적용은 observe-only evidence를 먼저 남긴 뒤 guard mode로 승격한다.
- 표준선(선택): IOP 필터는 caller-neutral OpenAI-compatible contract만 소비한다. Pi는 incident evidence와 선택적 field smoke 중 하나이며, Codex나 다른 SDK/curl caller와 동일한 protocol fixture를 통과해야 한다. caller별 보조 guard는 이 Milestone의 IOP 구현과 분리한다.
- 표준선(선택): schema 출력 계약은 `metadata.scheme` 하나로 표현하고 wrapper/options를 추가하지 않는다. 계약이 있으면 streaming 요청보다 contract validation을 우선하며, 검증 전 content delta를 흘리지 않는다.
- 표준선(선택): `metadata.scheme`은 JSON schema로 간주하며 IOP가 마지막 user message에 출력 계약 블록을 append해 provider로 전달한다.
- 표준선(선택): 이번 stream 모듈 적용 범위는 `/v1/chat/completions`다. 모듈은 chunk parsing, bounded look-behind/tail, terminal/tool event 판정, release/replacement 적용만 소유하고 반복 감지, repair, schema validation 결정은 상위 filter/policy 계층이 소유한다. 후속 workflow terminal hook은 이 event mechanics를 재사용할 수 있지만 Responses/Claude/agent-family codec 구현은 D02와 후속 Milestone에서 별도로 정한다.
- 표준선(선택): 출력 검증 filter 확장은 Go class 상속보다 공통 interface와 공유 policy/base helper를 기준으로 묶는다. 모든 filter는 동일 enablement context를 받고, 모델/환경별 정책은 registry에서 일관되게 평가한다.
- 표준선(선택): optional online filter가 비활성화된 모델은 pure passthrough로 처리할 수 있지만, caller가 `metadata.scheme`처럼 필수 계약을 요청했는데 해당 filter가 비활성화된 모델은 silent passthrough가 아니라 unsupported/400으로 거부한다.
- 표준선(선택): OpenAI-compatible provider 출력 검증은 normalized 경로로 전환하지 않는다. normalized는 CLI 전용으로 유지한다.
- 큐 배치: [에이전트 작업성 중심 저장소 구조 리팩터링](../../../archive/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md) 뒤, [OpenAI-compatible Incomplete Tool Call Syntax Gate](openai-compatible-incomplete-tool-call-syntax-gate.md) 앞
- 선행 작업: [OpenAI-compatible Tool Call Boundary Hardening](../../../archive/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md), [OpenAI-compatible Raw Tunnel 기반](../../../archive/phase/routing-policy-model-orchestration/milestones/openai-compatible-raw-tunnel-sideband-passthrough.md)
- 후속 작업: 단계 호출과 검증 최적화 MVP, Tool Call 판정 모델 Gate 리뷰
- 확인 필요: [SDD 사용자 리뷰 D01-D03](../../../sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/USER_REVIEW.md)
