docs: add repetition guard milestone detail

This commit is contained in:
toki 2026-07-14 18:12:01 +09:00
parent eab8bad73d
commit f8978dbf70
2 changed files with 34 additions and 20 deletions

View file

@ -7,8 +7,8 @@
## 목표
OpenAI-compatible Chat Completions provider 경로에서 모델 출력 이상을 요청 단위 필터로 감지하고, 사용자 경험을 해치지 않는 방식으로 중단/재시도/검증한다.
반복 출력 루프는 streaming passthrough를 유지한 채 upstream만 교체해 continuation repair로 이어 쓰고, `metadata.scheme` JSON 출력 계약은 검증 전 downstream content streaming을 막는 `contract_schema` 경로로 처리한다.
OpenAI-compatible Chat Completions provider 경로에서 모델 출력/행동 이상을 요청 및 짧은 session 단위 필터로 감지하고, 사용자 경험을 해치지 않는 방식으로 중단/재시도/검증한다.
반복 루프는 content streaming 반복과 동일 tool/action 반복을 모두 포함한다. content 반복은 streaming passthrough를 유지한 채 upstream만 교체해 continuation repair로 이어 쓰고, tool/action 반복은 `tool name + normalized args` fingerprint와 동일/no-progress 결과를 기준으로 감지해 side-effect 안전성이 없으면 repair 대신 안전 중단한다. `metadata.scheme` JSON 출력 계약은 검증 전 downstream content streaming을 막는 `contract_schema` 경로로 처리한다.
## 상태
@ -35,7 +35,7 @@ OpenAI-compatible Chat Completions provider 경로에서 모델 출력 이상을
- OpenAI-compatible `/v1/chat/completions` provider route의 출력 검증 필터 모듈과 response path 선택
- 출력 검증 filter별 enable/disable 정책을 environment(`dev`, `dev-corp`), model group/model/provider, 기능 단위로 평가하는 config/registry 계층
- 반복 출력 루프 감지용 rolling stream inspector, upstream abort, continuation repair, 1회 repair 제한
- 반복 출력 루프 감지용 single-stream rolling inspector, cross-request tool/action fingerprint inspector, tool call delta 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 출력 검증 경로와 분리하는 책임 경계
@ -48,10 +48,10 @@ OpenAI-compatible provider 응답을 사용자에게 노출하기 전에 필터
- [ ] [contract-doc] OpenAI-compatible 계약 문서와 구현 타입이 `metadata.scheme`, 내부 `contract_schema`/`passthrough_guarded` path, normalized CLI-only 경계를 설명한다. 검증: [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md)와 관련 Go 타입/handler 테스트가 새 계약과 일치한다.
- [ ] [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 조합 테스트가 통과한다.
- [ ] [filter-policy] 각 출력 검증 filter는 공통 interface/registry를 통해 enable/disable 정책을 평가하고, environment(`dev`, `dev-corp`)와 model group/model/provider별로 반복루프 guard, schema contract 같은 기능을 독립적으로 켜고 끌 수 있다. 검증: qwen/gemma fixture 기반 config/handler tests에서 필터별 활성/비활성, 정책 우선순위, disabled filter observation이 통과한다.
- [ ] [repeat-guard] streaming 응답에서 반복 루프를 rolling window로 감지하면 downstream SSE를 닫지 않고 upstream provider request만 abort한 뒤 emitted safe prefix와 bad tail summary로 continuation repair 요청을 이어 붙인다. 검증: 반복 chunk fixture 기반 stream test가 1회 repair, safe prefix 보존, `[DONE]` 단일 종료, tool side-effect 구간 차단을 확인한다.
- [ ] [filter-policy] 각 출력 검증 filter는 공통 interface/registry를 통해 enable/disable 정책을 평가하고, environment(`dev`, `dev-corp`)와 model group/model/provider별로 반복루프 guard, schema contract 같은 기능을 독립적으로 켜고 끌 수 있다. 검증: qwen/gemma/ornith fixture 기반 config/handler tests에서 필터별 활성/비활성, 정책 우선순위, disabled filter observation이 통과한다.
- [ ] [repeat-guard] content 반복은 단일 provider stream의 rolling window로 감지하고, tool/action 반복은 incoming `messages`의 이전 assistant tool call 및 tool result history와 현재 provider tool call delta를 결합해 감지한다. content 반복은 downstream SSE를 닫지 않고 upstream provider request만 abort한 뒤 emitted safe prefix와 bad tail summary로 continuation repair 요청을 이어 붙인다. tool/action 반복은 완성 전 tool call delta를 짧게 hold해 full call fingerprint를 만든 뒤 release/block을 결정하고, `tool name + normalized args` fingerprint, 짧은 TTL/session state, 동일/no-progress tool result를 함께 본다. 이미 downstream으로 tool call이 나갔거나 side effect 가능성이 있으면 자동 repair하지 않고 guard terminal error 또는 안전 중단으로 끝낸다. 검증: 반복 chunk fixture와 반복 action fixture가 1회 repair, safe prefix 보존, `[DONE]` 단일 종료, tool call delta hold/release, side-effect 구간 차단, `dev-corp-iop-ornith`/`ornith:35b` 유형의 동일 toolUse 루프 감지를 확인한다.
- [ ] [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에서 원인 축을 구분할 수 있게 남는다. 검증: dev-corp Pi TUI smoke에서 반복루프 중단/재요청 또는 schema validation 결과가 model/provider/IOP/CLI 축과 함께 관찰된다.
- [ ] [ops-evidence] 출력 필터 결과가 요청 실행 로그와 smoke에서 원인 축을 구분할 수 있게 남는다. 검증: dev-corp Pi TUI smoke와 Ornith 반복 toolUse fixture에서 반복루프 중단/재요청/안전 중단 또는 schema validation 결과가 model/provider/IOP/CLI 축과 함께 관찰된다.
## 완료 리뷰
@ -78,6 +78,9 @@ OpenAI-compatible provider 응답을 사용자에게 노출하기 전에 필터
- 관련 경로: `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만 남긴다.
- 표준선(선택): tool/action 반복은 단일 stream 안에서만 판단하지 않는다. 이전 request `messages`의 assistant tool call과 tool result, 현재 provider tool call delta, consecutive 동일 fingerprint, 동일/no-progress tool result, threshold, 짧은 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로 승격한다.
- 표준선(선택): Pi agent는 실제 local tool invocation을 보므로 별도 보조 guard를 둘 수 있지만, IOP 필터는 Pi 구현에 의존하지 않고 provider stream의 tool call delta와 request/message history에서 감지 가능한 action 반복을 우선 방어한다.
- 표준선(선택): schema 출력 계약은 `metadata.scheme` 하나로 표현하고 wrapper/options를 추가하지 않는다. 계약이 있으면 streaming 요청보다 contract validation을 우선하며, 검증 전 content delta를 흘리지 않는다.
- 표준선(선택): `metadata.scheme`은 JSON schema로 간주하며 IOP가 마지막 user message에 출력 계약 블록을 append해 provider로 전달한다.
- 표준선(선택): 출력 검증 filter 확장은 Go class 상속보다 공통 interface와 공유 policy/base helper를 기준으로 묶는다. 모든 filter는 동일 enablement context를 받고, 모델/환경별 정책은 registry에서 일관되게 평가한다.

View file

@ -18,7 +18,7 @@
## 문제 / 비목표
- 문제: provider stream에서 반복 출력 루프가 발생하면 Pi TUI 같은 client는 이미 열린 SSE를 계속 받으므로, IOP가 모델/provider 축의 이상을 감지해 upstream만 중단하고 같은 downstream stream에 안전하게 repair 출력을 이어 붙여야 한다. 동시에 `metadata.scheme` JSON 출력 계약은 전체 결과 검증 없이는 성공 여부를 알 수 없어 streaming passthrough와 다른 gated 경로가 필요하다.
- 문제: provider stream에서 반복 출력 루프가 발생하면 Pi TUI 같은 client는 이미 열린 SSE를 계속 받으므로, IOP가 모델/provider 축의 이상을 감지해 upstream만 중단하고 같은 downstream stream에 안전하게 repair 출력을 이어 붙여야 한다. 또한 `dev-corp-iop-ornith`/`ornith:35b` 유형처럼 모델이 같은 tool/action을 동일하거나 진전 없는 결과 뒤에 반복 생성하면 단일 응답 content 반복만으로는 감지하기 어렵기 때문에, tool call delta와 request/message history에서 action fingerprint 반복을 별도로 감시해야 한다. 동시에 `metadata.scheme` JSON 출력 계약은 전체 결과 검증 없이는 성공 여부를 알 수 없어 streaming passthrough와 다른 gated 경로가 필요하다.
- 비목표:
- normalized 실행 경로를 OpenAI-compatible provider 출력 검증 경로와 합친다.
- CLI adapter protocol을 이 Milestone에서 변경한다.
@ -34,20 +34,21 @@
| Code | `apps/edge/internal/openai`, `apps/node/internal/adapters/openai_compat` | OpenAI-compatible Chat Completions provider route와 stream bridge 구현 기준 |
| Contract | [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md) | `metadata.scheme`, 내부 response path, streaming/gated 정책 원문 |
| External Provider | OpenAI-compatible provider pool | provider 원본 요청/응답은 IOP 필터 정책에 따라 upstream abort/retry 대상이 된다. |
| User Decision | 현재 사용자 요청 | 사용자가 `metadata.scheme` 단일 계약, normalized CLI-only 경계, streaming guard 방향, 모델/환경별 filter enable/disable와 공통 interface/base 계층을 확정했다. |
| User Decision | 현재 사용자 요청 | 사용자가 `metadata.scheme` 단일 계약, normalized CLI-only 경계, streaming guard 방향, 동일 tool/action 반복 guard, 모델/환경별 filter enable/disable와 공통 interface/base 계층을 확정했다. |
## State Machine
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| `ingress` | `/v1/chat/completions` 요청 수신 | `passthrough`, `guarded_stream`, `contract_schema`, `policy_rejected` | request metadata, environment/model/provider별 internal filter path policy |
| `ingress` | `/v1/chat/completions` 요청 수신 | `repeat_guard_preflight`, `passthrough`, `guarded_stream`, `contract_schema`, `policy_rejected` | request metadata, environment/model/provider별 internal filter path policy |
| `repeat_guard_preflight` | incoming `messages`가 이미 repeated action threshold를 넘겼거나 짧은 session state와 일치한다 | `guard_error`, `guarded_stream` | action fingerprint history event |
| `passthrough` | `metadata.scheme` 없음, guarded filter 비활성 또는 적용 대상 아님 | `done`, `provider_error` | provider SSE/non-stream 응답 |
| `guarded_stream` | streaming 응답에 반복루프 등 online filter 적용 | `repairing`, `done`, `guard_error` | rolling inspector event |
| `repairing` | 반복루프 감지 후 upstream provider request abort 성공 | `guarded_stream`, `guard_error` | continuation repair provider 응답 |
| `guarded_stream` | streaming 응답에 content 반복루프 또는 tool/action 반복 online filter 적용 | `repairing`, `done`, `guard_error` | rolling inspector 또는 action fingerprint event |
| `repairing` | content 반복루프 감지 후 upstream provider request abort 성공 | `guarded_stream`, `guard_error` | continuation repair provider 응답 |
| `contract_schema` | `metadata.scheme` 있음 | `schema_retry`, `done`, `schema_error` | JSON parse/schema validation |
| `schema_retry` | schema validation 실패이며 retry budget 남음 | `contract_schema`, `schema_error` | retry counter |
| `policy_rejected` | caller가 요청한 필수 filter가 선택된 environment/model/provider에서 비활성 또는 미지원 | `done` | OpenAI-compatible invalid_request_error |
| `guard_error` | repair 불가, retry exhausted, tool side-effect 구간 진입 | `done` | OpenAI-compatible error or terminal SSE error |
| `guard_error` | repair 불가, retry exhausted, tool side-effect 구간 진입, side-effect 가능 action 반복 감지 | `done` | OpenAI-compatible error or terminal SSE error |
| `schema_error` | retry 후에도 JSON/schema 불일치 | `done` | OpenAI-compatible validation error |
| `done` | 성공 응답 또는 terminal error 전송 | 없음 | final chunk/body |
@ -57,11 +58,11 @@
- 입력:
- `metadata.scheme`: optional JSON schema object. 있으면 IOP는 요청 출력 계약으로 간주한다.
- `stream`: caller 요청값. `metadata.scheme`이 있으면 downstream content streaming보다 schema validation이 우선한다.
- `messages`: 마지막 user message에 IOP output scheme instruction을 append할 대상이다.
- `tools`: tool call side effect 안전성 판단에 사용한다.
- `messages`: 마지막 user message에 IOP output scheme instruction을 append할 대상이며, 명시 session id가 없을 때 이전 assistant tool call과 tool result를 이용해 action 반복 context를 추정하는 요청 history 기준이다.
- `tools`: tool name/argument 구조와 side effect 안전성 판단에 사용한다.
- 출력:
- `passthrough`: 기존 provider-compatible 응답을 유지한다.
- `passthrough_guarded`: downstream SSE 연결을 유지하되 IOP가 upstream stream을 감시하고 필요 시 upstream만 abort/retry한다. 내부 실행/로그 path이며 provider-original byte-identical로 표시하지 않는다.
- `passthrough_guarded`: downstream SSE 연결을 유지하되 IOP가 upstream content delta와 tool call delta를 감시하고 필요 시 upstream만 abort/retry하거나 안전 중단한다. action guard가 활성화된 tool call delta는 full call fingerprint 판정 전까지 해당 tool call fragment만 hold할 수 있다. 내부 실행/로그 path이며 provider-original byte-identical로 표시하지 않는다.
- `contract_schema`: 전체 응답을 수집/검증한 뒤 valid JSON만 반환한다. `stream=true` 요청에서도 검증 전 `delta.content`를 흘리지 않는다.
- `tool_validation_error`, `schema_validation_error`, `guard_error`: 복구 불가 또는 retry exhausted terminal error.
- 내부 filter interface/policy:
@ -71,6 +72,10 @@
- enablement 정책은 filter feature별로 평가하며, 더 구체적인 규칙이 우선한다. 우선순위는 `environment + model/provider + feature`, `model/provider + feature`, `environment + feature`, filter 기본값 순서다.
- 반복루프 guard 같은 optional online filter가 비활성화되면 해당 filter만 skip하고 pure passthrough 또는 남은 filter path로 진행한다.
- `metadata.scheme`처럼 caller가 필수 출력 계약을 요청한 filter가 비활성화되면 silent passthrough로 낮추지 않고 `policy_rejected`로 종료한다.
- action 반복 guard는 `tool name + normalized args`를 안정 fingerprint로 만들고, 이전 request history의 tool call/result, 현재 provider tool call delta, consecutive 동일 fingerprint, 동일/no-progress tool result, threshold, 짧은 TTL/session state를 함께 만족할 때만 repeated action으로 판정한다.
- tool call delta hold는 full call fingerprint 판정에 필요한 최소 fragment에만 적용하며, content delta 전체 buffering으로 확장하지 않는다. 판정이 safe이면 held tool call delta를 release하고, repeated action이면 downstream으로 release하지 않는다.
- 명시 session id가 없고 request history만으로 반복 여부를 안정적으로 판정할 수 없으면 guard mode로 단정하지 않고 observe-only decision을 남긴다.
- action 반복 observation은 filter id, feature name, model/provider, fingerprint hash, repeat count, threshold, decision, redacted summary를 남기되 Prometheus label은 낮은 cardinality 값으로 제한한다.
- 내부 path 주의:
- `passthrough_guarded``contract_schema`는 caller가 지정하는 공개 request field가 아니라 IOP 내부 실행/로그 path 이름이다.
- 공개 OpenAI-compatible 경로 선택은 [계약 원문](../../../../agent-contract/outer/openai-compatible-api.md)에 따라 request `model`이 가리키는 provider capability로 결정한다.
@ -80,6 +85,8 @@
- normalized path를 CLI 외 OpenAI-compatible provider 출력 검증에 사용하지 않는다.
- 반복루프 repair 안내 문구를 assistant content chunk로 주입하지 않는다.
- 이미 downstream으로 tool call을 보내 실행 side effect 가능성이 생긴 뒤 자동 continuation repair를 수행하지 않는다.
- tool/action fingerprint raw args나 secret 가능 문자열을 metric label, request id, provider id 같은 장기 식별자에 넣지 않는다.
- side-effect 가능 tool/action을 자동 replay하거나 continuation repair로 재실행하지 않는다.
## Acceptance Scenarios
@ -88,11 +95,12 @@
| S01 | `contract-doc` | OpenAI-compatible Chat Completions 계약 문서와 구현 타입 | `metadata.scheme` 및 내부 response path 경계가 추가된다 | 계약 문서, request 타입, handler 정책이 wrapper 없는 `metadata.scheme`과 normalized CLI-only 경계를 동일하게 설명한다 |
| S02 | `filter-pipeline` | provider route 요청이 들어온다 | request metadata와 filter 정책을 평가한다 | pure passthrough, guarded stream, contract schema 중 하나의 내부 path로 결정되고 unsupported 조합은 400으로 거부된다 |
| S03 | `repeat-guard` | provider SSE가 같은 문장/단락을 반복 출력한다 | rolling inspector가 threshold를 넘긴다 | downstream SSE는 유지되고 upstream만 abort되며 safe prefix 보존과 bad tail summary 기반 continuation repair가 1회 이어진다 |
| S04 | `repeat-guard` | tool call delta가 이미 downstream으로 흘렀거나 tool side effect 가능 구간이다 | 반복루프가 감지된다 | 자동 repair를 수행하지 않고 guard terminal error 또는 안전한 중단으로 끝낸다 |
| S04 | `repeat-guard` | tool call delta가 이미 downstream으로 흘렀거나 tool side effect 가능 구간이다 | content 반복 또는 action fingerprint 반복이 감지된다 | 자동 repair를 수행하지 않고 guard terminal error 또는 안전한 중단으로 끝낸다 |
| S05 | `schema-contract` | request `metadata.scheme`에 JSON schema가 있다 | provider가 schema-valid JSON을 반환한다 | IOP가 JSON parse/schema validation 후 valid JSON만 반환한다 |
| S06 | `schema-contract` | request `metadata.scheme`에 JSON schema가 있다 | provider가 invalid JSON 또는 schema 위반 JSON을 반환한다 | IOP가 schema와 validation error summary로 1회 재요청하고, 실패가 반복되면 schema validation error를 반환한다 |
| S07 | `ops-evidence` | dev-corp Pi TUI에서 반복루프 또는 schema 계약 smoke를 실행한다 | 로그와 TUI 출력을 확인한다 | 문제 축이 model/provider, IOP guard, CLI/normalized 경계로 구분되고 최종 stream/body가 오염되지 않는다 |
| S08 | `filter-policy` | dev/dev-corp에서 qwen/gemma model/provider별 반복루프 guard와 schema-contract enablement가 다르게 설정되어 있다 | 요청이 선택된 route로 들어온다 | filter registry가 가장 구체적인 정책을 적용하고, optional disabled filter는 skip하며, required disabled schema-contract는 unsupported/400으로 거부한다 |
| S08 | `filter-policy` | dev/dev-corp에서 qwen/gemma/ornith model/provider별 반복루프 guard와 schema-contract enablement가 다르게 설정되어 있다 | 요청이 선택된 route로 들어온다 | filter registry가 가장 구체적인 정책을 적용하고, optional disabled filter는 skip하며, required disabled schema-contract는 unsupported/400으로 거부한다 |
| S09 | `repeat-guard` | `dev-corp-iop-ornith`/`ornith:35b` 유형 요청 history에 같은 tool/action fingerprint와 동일/no-progress tool result가 누적되고 현재 provider 응답이 같은 tool call delta를 다시 생성한다 | action repetition threshold를 넘긴다 | IOP가 held tool call delta를 downstream으로 release하지 않고 repeated_action observation을 남기며, side-effect 가능 구간에서는 자동 repair 없이 guard terminal error 또는 안전 중단으로 무한 toolUse 루프를 끊는다 |
## Evidence Map
@ -101,11 +109,12 @@
| S01 | 계약 문서 diff, Go request/metadata type tests | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``contract-doc`, `go test ./apps/edge/internal/openai -count=1` 결과 |
| S02 | mode selection unit tests, unsupported 조합 tests | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``filter-pipeline`, handler test 결과 |
| S03 | 반복 chunk fixture stream test | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``repeat-guard`, upstream abort/repair/single `[DONE]` assertion |
| S04 | tool side-effect guard fixture | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``repeat-guard`, automatic repair block assertion |
| S04 | tool side-effect/action repeat guard fixture | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``repeat-guard`, automatic repair block assertion |
| S05 | valid JSON schema fixture | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``schema-contract`, validated JSON body/SSE assertion |
| S06 | invalid-then-repair, retry-exhausted fixtures | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``schema-contract`, retry count/error assertion |
| S07 | dev-corp Pi TUI smoke log, Edge provider log | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``ops-evidence`, dev-corp smoke evidence path 또는 로그 요약 |
| S08 | qwen/gemma environment policy fixture, handler tests | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``filter-policy`, per-env/per-model enable/disable, policy precedence, disabled required filter 400 assertion |
| S08 | qwen/gemma/ornith environment policy fixture, handler tests | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``filter-policy`, per-env/per-model enable/disable, policy precedence, disabled required filter 400 assertion |
| S09 | cross-request repeated tool/action fingerprint fixture, Ornith 반복 toolUse 로그 재현 | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion``repeat-guard`, tool call delta hold/block/release assertion, repeated_action observation, no-progress threshold, observe-only 불확실성 처리, side-effect safe stop assertion |
## Cross-repo Dependencies
@ -125,5 +134,7 @@
## 작업 컨텍스트
- 표준선: OpenAI-compatible provider 출력 검증은 Edge OpenAI handler/stream bridge에 두고, normalized는 CLI 전용으로 유지한다. 반복루프는 streaming repair filter, `metadata.scheme`은 buffered contract validation으로 분리한다.
- 표준선: filter 확장은 공통 interface, registry, shared policy/base helper를 통해 추가한다. enable/disable는 `dev`/`dev-corp`, qwen/gemma 같은 model group/model/provider, filter feature 단위로 선언하고 handler는 같은 `FilterContext`로 평가한다.
- 표준선: filter 확장은 공통 interface, registry, shared policy/base helper를 통해 추가한다. enable/disable는 `dev`/`dev-corp`, qwen/gemma/ornith 같은 model group/model/provider, filter feature 단위로 선언하고 handler는 같은 `FilterContext`로 평가한다.
- 표준선: action 반복 guard는 단일 응답 텍스트 반복과 별도 축이다. Edge는 provider stream의 tool call delta와 request/message history를 이용해 `tool name + normalized args` fingerprint 반복을 감시하고, Pi agent 쪽 local tool invocation guard는 보조 방어로 둘 수 있지만 IOP 필터의 필수 의존성으로 두지 않는다.
- 표준선: action 반복 guard는 false positive를 줄이기 위해 observe-only evidence 수집을 먼저 허용하고, consecutive 동일 fingerprint와 동일/no-progress 결과가 threshold를 넘을 때 guard mode에서 중단한다. read-only 반복 action은 model/provider/tool별 threshold 또는 allow policy로 조정한다.
- 후속 SDD: 없음