docs(roadmap): 승인 tail pacing 정책을 반영한다
This commit is contained in:
parent
4cafc91323
commit
3a3c2aa530
2 changed files with 34 additions and 5 deletions
|
|
@ -10,6 +10,7 @@
|
|||
OpenAI-compatible Chat Completions와 Responses 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는 endpoint codec이 보존한 표준 role/channel provenance를 기준으로 탐지하고, caller가 reasoning history를 재전송하지 않거나 conversation identity가 없으면 존재하지 않는 cross-request state를 추론하지 않는다. D01은 반복된 plain reasoning만 sanitize/live dedupe하고 no-progress·tool 미release·side-effect 비해당일 때 원문 safe prefix를 보존한 단계 복구를 허용하도록 확정됐다. D05는 언어 판별·번역 모델 호출을 제거하고 고정 영어 지시문으로 복구 요청을 직접 조립하도록 확정됐다. tool/action 반복은 `tool name + normalized args` fingerprint와 완료된 이전 tool result의 동일/no-progress 신호를 기준으로 감지해 side-effect 안전성이 없으면 repair 대신 안전 중단한다. `metadata.scheme` JSON 출력 계약은 검증 전 downstream content streaming을 막는 `contract_schema` 경로로 처리한다.
|
||||
실제 repeat/schema/provider-error 의미 필터 구현에 앞서 deterministic diagnostic filter로 pass, observe-only violation, blocking violation과 단일 recovery를 실제 codec/Core/Arbiter/ReleaseSink 경로에서 한 명령으로 관측하는 smoke를 선행 gate로 둔다.
|
||||
rolling evidence threshold를 통과한 tail은 검증 안전성을 낮추지 않으면서 provider가 해당 window를 생성한 시간에 맞춰 균일하게 전달한다. 시간은 filter 승인 전 release eligibility로 사용하지 않고, 승인된 출력의 delivery scheduling에만 사용한다.
|
||||
|
||||
## 상태
|
||||
|
||||
|
|
@ -37,6 +38,8 @@ OpenAI-compatible Chat Completions와 Responses provider 경로에서 모델 출
|
|||
- [x] [D04] 기존 Tool Call Runtime 검증 재시도와 공유하는 request-local exact-replay 최대 3회 및 commit 경계 정책
|
||||
- [x] [D05] 언어 판별·번역 모델 호출 없이 사용하는 고정 영어 반복 복구 지시문
|
||||
- [x] [D06] 오류 사건의 중복 집계와 LLM 기반 소스 분석·수정 제안·승인·변경 요청·병합·배포를 별도 범용 플랫폼으로 분리하는 책임 경계
|
||||
- [x] [D07] provider output-cap `length`를 작은 attempt cap의 managed continuation으로 처리하고, 원본 요청·assistant prefix를 보존한 context-window 기반 논리 trajectory를 fault recovery 최대 3회와 분리하는 정책
|
||||
- [x] [D08] filter 승인을 받은 pending tail만 수집 시간 기준으로 비동기 pacing하고, 시간은 release eligibility로 사용하지 않는 정책
|
||||
|
||||
## 범위
|
||||
|
||||
|
|
@ -46,6 +49,7 @@ OpenAI-compatible Chat Completions와 Responses provider 경로에서 모델 출
|
|||
- [Stream Evidence Gate Core](../../../archive/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md)가 제공하는 response-start 포함 normalized event, rolling look-behind, bounded terminal/fragment gate, transport commit과 recovery mechanics를 OpenAI Chat Completions와 Responses consumer가 함께 채택한다. 이 Milestone은 endpoint별 raw codec, lossless `RequestRebuilder`, Edge `AttemptDispatcher`/`ReleaseSink` adapter와 반복·schema·provider 오류의 의미 판정/typed `RecoveryIntent`만 소유한다. release/terminal/recovery arbitration과 budget은 Core가 소유하며 공통 gate를 재구현하지 않는다. provider의 attempt별 출력 상한 도달은 외부 `length` 실패가 아니라, 모델 context window 안에서 작은 attempt 상한으로 계속 생성하는 managed continuation 후보로 판정한다.
|
||||
- provider terminal reason `length`를 `managed_length_continuation` 후보로 판정한다. IOP는 managed profile에서 provider의 작은 attempt별 `max_tokens`만 사용하고, 외부 caller에는 중간 `length`/`[DONE]`을 노출하지 않는다. Core가 이미 release한 content/think/reasoning safe prefix와 cursor를 보존한 뒤 endpoint별 Rebuilder가 원본 요청과 channel별 assistant prefix를 다음 attempt로 조립한다. attempt 상한과 재구성된 request prompt의 실제 token 수, reserve, caller가 명시했다면 남은 논리 output cap이 모두 허용할 때만 이어 간다. provider attempt cap은 내부 운영값이고 caller output cap은 논리 요청 전체에 한 번만 적용한다. assistant prefix는 rebuilt prompt에 이미 포함되므로 별도의 누적 output과 이중 계상하지 않는다. context 여유·논리 trajectory 예산이 소진되거나 완성된 tool call이 생기면 최종 terminal을 한 번만 전달한다. 이 정책은 Core의 오류 recovery 3회 상한과 분리된 context-window 기반 trajectory budget을 소비하며, 의미 요약·문장 경계 절단·별도 모델 호출은 사용하지 않는다.
|
||||
- 반복 출력 루프 감지용 single-stream rolling inspector, incoming request-history 기반 assistant anchor 및 tool/action fingerprint inspector, bounded text/tool-call fragment hold/release 판정, upstream abort, continuation repair. repair는 반복 전까지 사용자에게 전달된 원문을 보존하고 반복 구간만 제외하며, 사용자 지정 온도가 없을 때 `[0.2, 0.4, 0.6]` 순서로 시도하고 배열 소진 시 종료한다. 의미 요약·임의 절단과 side-effect 구간 자동 복구는 금지한다. all-complete Arbiter가 단일 plan을 고르고 current attempt ownership이 끝나면 endpoint별 Rebuilder가 반복 전 content와 think/reasoning 원문을 channel별로 구분해 고정 영어 지시문과 직접 조립한다. 사용자 요청·message, 언어 판별·번역·별도 모델 호출은 포함하지 않으며 문맥 한도를 넘으면 자동 복구하지 않는다.
|
||||
- rolling window의 첫 eligible text/reasoning delta부터 evidence threshold 또는 terminal 도달까지 monotonic 수집 시간 `T`를 측정하고, all-complete filter pass 뒤 immutable `approved` queue로 옮긴 tail의 승인된 rune 수를 `T` 동안 frame tick별 누적 quota로 균일하게 전달한다. 다음 window의 provider 수집·검증은 이전 approved window 전달과 겹쳐 실행하고, bounded queue와 backpressure로 메모리를 제한한다. `pending -> approved -> delivered` 경계를 분리하며 recovery는 unapproved tail만 폐기·교체할 수 있다. structural/tool fragment는 임의 분할하지 않고 endpoint event/channel 순서를 보존하며, terminal은 approved queue drain 뒤 한 번만 전달하고 cancel·sink failure는 timer와 provider ownership을 정리한다.
|
||||
- `code`와 `message`만 가진 `filters[]`에 매칭된 provider 오류가 Core의 downstream commit 전에 끝난 경우, 완료된 [Tool Call Runtime 검증 재시도 MVP](../../../archive/phase/knowledge-tool-optimization-extension/milestones/tool-call-runtime-validation-retry.md)의 request-local raw request snapshot·bounded exact replay 경로를 확장한다. provider 오류와 Tool Call Runtime 검증은 최초 실행을 제외하고 합쳐 최대 3회 재실행하며, commit 뒤 오류는 남은 500-rune tail과 무관하게 안전 종료한다.
|
||||
- `metadata.scheme` JSON schema 계약 수신, 마지막 user message prompt append, hard bound가 있는 `terminal_gate` validation, schema 위반 시 bounded retry
|
||||
- `passthrough`, `passthrough_guarded`, `contract_schema` 내부 response path 구분과 실행 로그/관측 기준. 이 이름들은 caller가 지정하는 공개 request field가 아니라 IOP 내부 경로/로그 기준이다.
|
||||
|
|
@ -70,6 +74,12 @@ OpenAI-compatible 출력 필터의 계약, 공통 pipeline, endpoint codec, Stre
|
|||
- [x] [responses-codec] `/v1/responses`의 input item history와 response-start/text/reasoning/function-call/terminal event를 normalized event와 repair input으로 변환하고, Core의 기본값/절대 상한 16 MiB `max_ingress_snapshot_bytes` 안에서 raw body 하나를 canonical source로 unknown caller item/field를 보존하는 bounded lossless Responses `RequestRebuilder`를 제공한다. raw parser와 serializer는 Chat과 분리하고 concrete model/auth rewrite는 dispatcher admission에 둔다. 검증: Responses raw body round-trip/unknown field, stream item split, staged opening event, reasoning/encrypted reasoning 보존, function-call, terminal/error, path-switch recovery가 Chat과 같은 semantic decision/plan을 내고 endpoint shape를 유지하며 retained/rebuild limit 초과는 no-dispatch로 끝난다.
|
||||
- [x] [filter-policy] filter enable/disable, `blocking|observe_only`, hold mode/bound를 environment(`dev`, `dev-corp`)와 model group/model/provider/protocol capability별로 평가한다. request 시작 시 config generation과 required capability를 고정해 schema 같은 필수 filter를 지원하지 않는 provider 후보는 admission 전에 제외하고, actual target별 active set은 attempt마다 같은 snapshot으로 다시 resolve한다. 검증: Chat/Responses와 qwen/gemma/ornith fixture에서 policy precedence, mid-request reload 격리, provider 전환 re-resolution, required no-candidate 400, disabled/duplicate filter 미평가와 caller-neutral 분기가 통과한다.
|
||||
|
||||
### Epic: [approved-tail-delivery] Approved Tail Delivery Pacing
|
||||
|
||||
filter가 승인한 rolling tail의 전달 속도를 provider 수집 cadence에 맞추되 검증·복구 경계와 upstream 수집을 분리한다.
|
||||
|
||||
- [ ] [approved-tail-pacing] Core와 Edge release adapter가 rolling epoch를 `pending`, immutable `approved`, `delivered`로 분리한다. 첫 eligible text/reasoning delta부터 threshold 또는 terminal까지의 monotonic 수집 시간 `T`와 approved rune 수를 queue item에 고정하고, 비동기 scheduler가 endpoint-safe text/reasoning delta를 Unicode rune 경계에서 frame tick별 누적 quota로 나눠 `T` 동안 전달한다. filter 평가 시간과 provider prefill은 `T`에서 제외하며 시간 경과만으로 pending을 승인하거나 idle fail-closed를 우회하지 않는다. 이전 approved window를 전달하는 동안 다음 provider window를 계속 수집·평가하고, bounded approved queue가 찰 때만 upstream backpressure를 적용한다. recovery는 unapproved tail만 폐기·교체하고 approved payload는 순서·내용을 바꾸지 않으며, structural/tool fragment는 원자적으로 유지한다. terminal은 queue drain 뒤 한 번만 전달하고 cancel·deadline·partial sink failure에서 timer, queue, provider ownership을 정리한다. 검증: fake monotonic clock으로 200/500/1000-rune pass와 short terminal tail을 실행해 각 window의 전달 시간이 `T`의 ±10%이고 전체 rune/event/channel 순서와 UTF-8이 동일하며 `collect T + deliver T`의 직렬 지연 없이 다음 수집이 겹치는지 확인한다. blocking/recovery, idle, bounded queue/backpressure, tool fragment, cancel과 sink failure fixture에서 승인 전 방출·중복 terminal·timer leak이 없어야 한다. Chat Completions/Responses의 tunnel·normalized 경로를 모두 검증하고, dev `ornith:35b` direct OneX 대비 Edge smoke에서 2ms 미만 gap 비율과 threshold burst가 감소하며 raw 출력은 ignored `agent-test/runs/**`에만 보관한다. 같은 변경에서 [Stream Evidence Gate 구현 스펙](../../../../agent-spec/runtime/stream-evidence-gate.md), [OpenAI-compatible API 계약](../../../../agent-contract/outer/openai-compatible-api.md), 설정 field를 추가하거나 바꾸는 경우 [Edge config 계약](../../../../agent-contract/inner/edge-config-runtime-refresh.md)을 현재 동작·기본값·관측 지표와 함께 갱신한다.
|
||||
|
||||
### Epic: [output-filter-recovery] Output Filter Recovery and Evidence
|
||||
|
||||
반복·provider 오류·schema 위반의 감지·복구 안전 경계와 운영 관측·회귀 evidence를 묶는다.
|
||||
|
|
@ -94,7 +104,10 @@ OpenAI-compatible 출력 필터의 계약, 공통 pipeline, endpoint codec, Stre
|
|||
- 검토 항목:
|
||||
- [ ] `complete.log`의 `Roadmap Completion`이 각 기능 Task id를 기록한다.
|
||||
- [ ] 최종 검증 출력이 SDD Evidence Map과 일치한다.
|
||||
- [ ] 모든 기능 Task 구현과 1차 검증이 끝난 뒤 비동기 collection/delivery 분리, filter 승인 경계, rune/event 순서, queue/backpressure, terminal/cancel/timer cleanup, Chat/Responses 양쪽 codec과 문서·계약 drift를 다시 검토한다.
|
||||
- [ ] 재검토에서 발견된 actionable issue를 수정하고 영향 테스트와 관련 전체 검증을 다시 실행한 뒤, 추가 actionable issue가 없을 때까지 review-fix-retest를 반복한다. 최종 `complete.log`에는 review findings, 수정 내역, 재검증 evidence와 잔여 위험을 기록한다.
|
||||
- [ ] generic raw HTTP/OpenAI SDK 기준 staged response-start, single-stream 반복 continuation, assistant history anchor, provider error/Tool Call validation의 최초 실행 제외 공통 최대 3회 exact budget, 모든 strategy를 합산한 request 전체 recovery cap, bounded ingress snapshot/schema terminal gate, same action과 provider/path switch가 확인된다. 반복 검증에는 multi-byte 한국어 장문, 200/500-rune rolling/look-behind, idle no-release, stream-open continuation과 dev `ornith:35b` 다회 smoke가 포함된다.
|
||||
- [ ] approved tail pacing은 filter 승인 전 release 조건을 바꾸지 않고 window별 수집 시간과 전달 시간을 맞추며, 다음 window 수집과 비동기 전달의 중첩, bounded queue/backpressure, 정확한 rune/event 순서, 단일 terminal/cancel cleanup을 deterministic clock과 dev `ornith:35b` 비교 evidence로 확인한다.
|
||||
- [ ] managed `length` continuation은 작은 provider attempt cap으로 중간 terminal을 숨기고 context-window 기반 trajectory를 같은 stream에 연결하며, fault recovery cap·tool boundary·최종 usage/terminal 관측을 보존한다.
|
||||
- agent-ui 상태 반영: 해당 없음
|
||||
- 리뷰 코멘트: 없음
|
||||
|
|
@ -105,7 +118,7 @@ OpenAI-compatible 출력 필터의 계약, 공통 pipeline, endpoint codec, Stre
|
|||
- OpenAI-compatible standard inference/provider response 계약 밖의 agent·terminal·workspace protocol 도입
|
||||
- Pi session JSONL, Pi SDK 내부 message type, Pi local tool invocation을 IOP 반복 guard의 runtime 입력이나 필수 의존성으로 사용하는 방식
|
||||
- 명시적 conversation identity 없이 caller/model을 조합한 hash를 대화 식별자로 간주하거나 caller 간 TTL 반복 state를 공유하는 방식
|
||||
- 반복루프 감지를 위해 전체 응답을 buffer한 뒤 사용자에게 늦게 보내는 방식
|
||||
- 반복루프 감지를 위해 전체 응답을 buffer하거나, filter 승인 전에 경과 시간을 이유로 pending 출력을 release하는 방식. approved tail pacing은 all-complete pass 뒤의 bounded rolling window에만 적용한다.
|
||||
- cross-request 반복 증거에 raw user prompt, raw tool args/result, 전체 reasoning/content를 저장하는 방식
|
||||
- 진행 신호가 있는 서로 다른 tool/action을 assistant history anchor 반복만으로 차단하는 방식
|
||||
- assistant final `content`를 history anchor fingerprint만으로 조용히 삭제하는 방식
|
||||
|
|
@ -122,6 +135,7 @@ OpenAI-compatible 출력 필터의 계약, 공통 pipeline, endpoint codec, Stre
|
|||
- 표준선(선택): `provider_length_gate`는 provider가 한 attempt의 output cap에 도달한 terminal reason만 caller-neutral하게 판정한다. managed profile에서는 그 terminal을 외부 오류로 전달하지 않고 Core의 stream-open continuation을 요청한다. provider attempt cap은 작은 운영 단위로 두고, IOP의 논리 trajectory는 original request와 assistant prefix를 조립한 뒤 측정한 rebuilt prompt와 reserve를 뺀 context window 여유, 그리고 caller가 명시한 논리 output cap까지만 확장한다. provider attempt cap은 이와 별개인 내부 운영 단위다. provider가 assistant prefill을 지원하면 우선 사용하고, 지원하지 않으면 고정 internal continuation directive로 같은 assistant prefix를 재구성한다. 이는 일반 오류 재시도와 다른 progress continuation이므로 Core의 fault recovery 3회 cap을 소비하지 않으며, context 여유·취소·완성 tool call·side effect·미완성 fragment 실패에서는 final terminal로 수렴한다. context 여유 또는 caller logical cap 소진의 경우에만 endpoint가 지원하는 logical `length` terminal과 단일 `[DONE]`을 한 번 전달하며, attempt 중간 `length`는 전달하지 않는다.
|
||||
- 표준선(선택): 반복루프 필터는 `rolling_window` passthrough consumer이며 이미 흘린 정상 prefix를 버리지 않는다. 반복 감지 시 continuation directive/온도 후보/반복 span만 반환하고, Core가 committed look-behind/release cursor를 보존해 current attempt abort, 고정 영어 지시문을 포함한 endpoint별 rebuild와 cycle별 single re-admission을 수행한다. 새 attempt의 response-start/role과 이미 보낸 prefix는 downstream에 중복하지 않는다.
|
||||
- 표준선(선택): 기본 streaming filter는 provider 출력 전체를 buffer하지 않는다. Core가 response-start staging, rolling pending/look-behind, active filters single-flight evaluation과 all-complete Arbiter를 소유한다. schema처럼 전체 결과가 필요한 명시적 `terminal_gate`만 hard bound 안에서 content를 terminal까지 보류하며, 시간 경과는 어느 mode에서도 release 조건이 아니다.
|
||||
- 표준선(선택): rolling release는 `pending -> approved -> delivered`를 분리한다. 첫 eligible delta부터 threshold/terminal까지의 monotonic 수집 시간만 delivery pacing 기준으로 고정하고 prefill·filter 평가 시간은 제외한다. all-complete pass 전에는 scheduler에 넣지 않으며, 승인 뒤에는 synchronous whole-batch release나 `ReleaseSafe` 내부 sleep 대신 bounded 비동기 queue와 frame tick 누적 rune quota를 사용한다. 이전 approved window 전달과 다음 provider window 수집을 겹치고 queue full에서만 backpressure를 건다. structural/tool event는 원자성과 순서를 보존하고 terminal은 drain 뒤 한 번만 전달하며 cancel·sink failure는 timer와 provider ownership을 함께 정리한다.
|
||||
- 표준선(선택): provider 오류 filter는 `filters[] = [{ code, message }]`만 사용해 `exact_replay` intent를 반환한다. Core는 staged response-start를 포함해 `transport_uncommitted`인지 판정하고 Tool Call validation과 최초 실행 제외 request-local 최대 3회를 공유한다. exact/continuation/schema는 Core의 request 전체 `max_recovery_attempts_total` 안에서만 실행되고 cap 소진을 다른 strategy로 우회하지 않는다. stream-open 뒤에는 exact replay하지 않으며 current attempt abort가 끝난 뒤에만 기존 provider-pool admission을 다시 거친다. provider 선택은 pool 정책에 맡기고 filter는 retry loop/budget/snapshot/rebuild/submit을 소유하지 않는다.
|
||||
- 표준선(선택): Chat/Responses ingress snapshot과 repair 결과는 Core의 기본값/절대 상한 16 MiB `max_ingress_snapshot_bytes` 안에서 OpenAI JSON raw body 하나를 canonical source로 보존한다. handler는 body를 읽기 전에 limit를 적용하고 typed view/rebuild 임시 allocation까지 retained bytes에 계상한다. limit 초과는 provider dispatch와 recovery budget 소비 전에 fail-closed하고 raw request를 filter/관측 로그에 남기지 않는다.
|
||||
- 표준선(선택): 반복루프 필터는 텍스트 n-gram/문단 반복뿐 아니라 tool/action 반복도 본다. action fingerprint는 `tool name + normalized args`를 안정적으로 정규화해 만들고, 로그/metric label에는 raw args나 secret 가능 문자열을 넣지 않으며 fingerprint hash, action 종류, 반복 횟수만 남긴다.
|
||||
|
|
|
|||
|
|
@ -21,14 +21,17 @@
|
|||
- [x] [D05] 언어 판별·번역 모델 호출 없이 사용하는 고정 영어 반복 복구 지시문
|
||||
- [x] [D06] 오류 사건 집계와 LLM 기반 수정 오케스트레이션을 별도 범용 플랫폼으로 분리하는 책임 경계
|
||||
- [x] [D07] provider output-cap `length`를 작은 attempt cap의 managed continuation으로 처리하고, 원본 요청·assistant prefix를 보존한 context-window 기반 논리 trajectory를 fault recovery 최대 3회와 분리하는 정책
|
||||
- [x] [D08] filter 승인을 받은 pending tail만 수집 시간 기준으로 비동기 pacing하고, 시간은 release eligibility로 사용하지 않는 정책
|
||||
|
||||
## 문제 / 비목표
|
||||
|
||||
- 문제: OpenAI-compatible caller가 provider stream에서 반복 출력을 받으면 이미 열린 SSE가 계속 유지되므로 IOP가 caller 제품명과 무관하게 request history와 provider response에서 이상을 감지하고 안전하게 관찰·보정·중단해야 한다. 동일/no-progress tool action은 content 반복과 별도로 tool delta와 history fingerprint를 감시해야 한다. `llama-server` parse error 같은 matched provider 오류는 response-start/status/header/body가 staged된 `transport_uncommitted` 상태에서 bounded lossless request snapshot으로 exact replay해야 하며, status/header/role/body 중 하나가 commit된 stream-open 뒤에는 pending tail이 남아도 exact replay하지 않는다. 단, content 반복의 continuation은 이미 보낸 safe prefix/cursor를 보존해 같은 stream을 이어가는 별도 전략이다. exact replay는 [Tool Call Runtime 검증 재시도 MVP](../../../archive/phase/knowledge-tool-optimization-extension/milestones/tool-call-runtime-validation-retry.md)의 경로/counter를 Core Coordinator가 흡수해 provider 오류와 validation이 최초 실행 제외 최대 3회를 공유하고 filter별 loop를 만들지 않는다. exact/schema와 일반 failure recovery는 Core의 최초 실행 제외 기본값이자 절대 상한 3회 fault cap을 함께 소비한다. provider output-cap `length`는 외부 오류가 아니라 managed profile의 진행 terminal로 판정한다. Core가 이미 release한 safe prefix/cursor를 보존하고 endpoint Rebuilder가 원본 요청과 content/think/reasoning assistant prefix를 channel별로 조립해 같은 stream을 이어 간다. provider가 assistant prefill을 지원하면 우선 사용하고, 아니면 endpoint-private 고정 continuation directive만 더한다. 작은 provider attempt cap은 유지하되 다음 allowance는 original request와 assistant prefix를 조립한 뒤 측정한 `rebuilt_prompt_tokens`에서 reserve를 뺀 실제 context window 및 caller가 명시했다면 남은 논리 output cap으로 제한한다. provider attempt cap은 내부 운영값이고 caller cap은 논리 요청 전체에 한 번만 적용한다. assistant prefix는 rebuilt prompt에 포함되므로 누적 output을 별도 합산하지 않으며, 이 logical trajectory는 fault cap과 별도다. 중간 `length`/`[DONE]`은 caller에게 노출하지 않고 context 여유 또는 caller logical cap 소진 때만 endpoint-native logical `length` terminal과 단일 종료 marker를 전달한다. cancel, complete tool call/side effect 또는 미완성 fragment 실패에서는 endpoint별 final terminal 하나로 수렴한다. 2026-07-16 Pi/Ornith incident는 user가 입력하지 않은 assistant anchor가 assistant reasoning history에 누적돼 user 발화처럼 재인용된 사례이며, generic payload 재구성에서 같은 anchor의 이전 message 11개는 모두 assistant이고 user occurrence는 0이었다. 이를 특정 caller가 아닌 raw HTTP/OpenAI SDK protocol fixture로 일반화한다. `metadata.scheme`은 전체 결과 검증이 필요하므로 hard bound가 있는 terminal gate를 사용한다.
|
||||
- 문제: rolling evidence threshold를 통과한 pending event를 한 loop에서 모두 release하면 provider가 수집하는 동안의 자연스러운 cadence가 사라지고 threshold마다 짧은 burst와 긴 무응답 구간이 반복된다. 이 문제는 filter 승인 전 hold를 약화하지 않고, 승인된 tail의 전달 lifecycle과 다음 window 수집 lifecycle을 분리해 해결해야 한다.
|
||||
- 비목표:
|
||||
- raw tunnel provider를 normalized RunEvent 실행 경로로 강제 전환하거나 두 path의 raw parser를 합친다.
|
||||
- 활성 [OpenAI-compatible API 계약](../../../../agent-contract/outer/openai-compatible-api.md) 밖의 agent·terminal·workspace response protocol을 도입한다.
|
||||
- 전체 streaming 응답을 기본적으로 buffer해 사용자 경험을 늦춘다.
|
||||
- 전체 streaming 응답을 기본적으로 buffer하거나, 경과 시간을 filter 승인 전 release/fail-open 조건으로 사용한다.
|
||||
- provider 수집을 멈춘 채 `collect T + deliver T`를 직렬로 수행하거나 synchronous release path 안에서 sleep해 매 window latency를 두 배로 만든다.
|
||||
- schema 계약이 있는 요청에서 검증 전 partial content를 성공 출력으로 노출한다.
|
||||
- validator 모델로 애매한 자연어/tool-call 후보를 판정한다.
|
||||
- Pi session JSONL, Pi SDK 내부 type, Pi local tool invocation을 IOP filter runtime 입력으로 사용한다.
|
||||
|
|
@ -42,6 +45,7 @@
|
|||
|------|------|------|
|
||||
| Roadmap | [Milestone 문서](../../../phase/knowledge-tool-optimization-extension/milestones/openai-compatible-output-validation-filters.md) | 범위, Task, 완료 evidence 기준 |
|
||||
| Code | `packages/go/streamgate`, `apps/edge/internal/openai`, `apps/edge/internal/service`, `apps/node/internal/adapters/openai_compat` | 공통 gate와 OpenAI endpoint별 codec/rebuilder, Edge dispatcher/release adapter 구현 기준 |
|
||||
| Implementation Spec | [Stream Evidence Gate 구현 스펙](../../../../agent-spec/runtime/stream-evidence-gate.md) | 현재 구현의 pending/approved/delivered lifecycle, 지원 endpoint와 pacing/terminal/관측 동작을 구현과 같은 변경에서 갱신한다. |
|
||||
| Contract | [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md) | `metadata.scheme`, 내부 response path, streaming/gated 정책 원문 |
|
||||
| Config Contract | [edge-config-runtime-refresh.md](../../../../agent-contract/inner/edge-config-runtime-refresh.md) | limit policy의 config field/default/range/refresh 분류는 구현과 함께 갱신하며 active 계약에 미구현 field를 선반영하지 않는다. |
|
||||
| Stream Mechanics | [Stream Evidence Gate Core SDD](../../../archive/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md) | response-start staging, rolling/terminal/fragment hold, transport commit, recovery와 terminal sequence의 공통 source of truth |
|
||||
|
|
@ -53,6 +57,7 @@
|
|||
| Diagnostic Smoke Decision | 현재 사용자 요청 | 실제 의미 필터보다 먼저 local/dev 전용 deterministic diagnostic `Filter` mock으로 pass, observe-only violation, pre-release blocking violation과 단일 recovery를 관측한다. mock은 판정만 제어하고 Chat/Responses codec, Core, Arbiter, Recovery Coordinator, ReleaseSink, raw-free observation sink는 실제 구현을 사용한다. 외부 caller가 활성화할 수 없고 production 기본 Registry에는 등록하지 않는다. |
|
||||
| Failure Contract | [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md), [Stream Evidence Gate Core SDD](../../../archive/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md) | 내부는 최대 4단계의 raw-free `FailureCauseChain`을 보존하고, endpoint host는 HTTP/Chat SSE/Responses에 endpoint별 단일 terminal 오류만 직렬화한다. |
|
||||
| Managed Length Decision | 현재 사용자 요청 | managed profile은 provider의 작은 attempt `max_tokens`를 사용한다. output-cap `length`는 중간 terminal로 내보내지 않고 original request와 channel별 assistant prefix로 다음 attempt를 rebuild하며, original request와 assistant prefix를 조립한 뒤 측정한 rebuilt prompt token 및 reserve로 계산한 context-window 여유가 논리 trajectory의 유일한 장문 상한이다. prefix는 rebuilt prompt에 이미 포함되므로 별도의 누적 output과 이중 계상하지 않는다. fault recovery 3회 cap과 별도다. caller가 명시한 output cap은 provider attempt cap으로 취급하지 않고 논리 요청 전체에 한 번만 적용한다. |
|
||||
| Approved Tail Delivery Decision | 현재 사용자 요청 | rolling window의 first eligible text/reasoning delta부터 evidence threshold 또는 terminal까지 수집 시간 `T`를 측정한다. all-complete filter pass를 받은 payload만 immutable approved queue로 이동하고, 다음 provider window 수집과 겹쳐 `T` 동안 rune-weighted frame pacing한다. 시간은 release eligibility가 아니며 recovery는 unapproved tail만 폐기·교체한다. |
|
||||
|
||||
## State Machine
|
||||
|
||||
|
|
@ -65,9 +70,11 @@
|
|||
| `provider_error_retry` | provider 오류의 `code` exact-match와 `message` 포함-match가 같은 `filters[]` 원소에 맞고 Core `CommitState=transport_uncommitted`다 | `exact_replay`, `provider_error` | filter는 `exact_replay` intent만 반환한다. staged response-start나 pending이 있어도 user-visible commit이 없으면 eligible이고 stream-open이면 들어가지 않는다 |
|
||||
| `exact_replay` | Core Arbiter가 provider error 또는 Tool Call validation intent를 하나의 recovery 후보로 선택했다 | `passthrough`, `guarded_stream`, `provider_error` | RecoveryPlan Coordinator가 최초 실행 제외 공통 최대 3회 exact budget, 모든 strategy를 합산한 기본값/절대 상한 3회 request 전체 cap과 commit/cancel/side-effect를 확인하고 current attempt abort 뒤 bounded raw-canonical Rebuilder와 기존 provider-pool admission으로 cycle당 outbound attempt 하나를 수행한다 |
|
||||
| `provider_error` | 오류가 retry 불가, 공통 3회 budget 소진 또는 stream-open 뒤 발생했다 | `done` | uncommitted이면 staged provider start를 버리고 caller-facing HTTP error, stream-open이면 terminal SSE error/close; exact replay 금지 |
|
||||
| `guarded_stream` | streaming 응답에 repeat/history/action/provider-error filter 중 active set 적용 | `observe_continue`, `dedupe_continue`, `repairing`, `provider_error_retry`, `done`, `guard_error` | Core가 response-start와 rolling/fragment evidence를 보류하고 same immutable batch를 single-flight 병렬 평가한다. all-complete 뒤 하나의 action만 선택한다 |
|
||||
| `guarded_stream` | streaming 응답에 repeat/history/action/provider-error filter 중 active set 적용 | `observe_continue`, `dedupe_continue`, `approved_tail`, `repairing`, `provider_error_retry`, `done`, `guard_error` | Core가 response-start와 rolling/fragment evidence를 보류하고 same immutable batch를 single-flight 병렬 평가한다. all-complete 뒤 하나의 action만 선택한다 |
|
||||
| `observe_continue` | guard가 후보를 감지했지만 D01이 observe-only이거나 판정 근거가 부족하다 | `guarded_stream`, `done`, `guard_error` | response mutation 없는 observation |
|
||||
| `dedupe_continue` | D01에서 live dedupe가 승인됐고 assistant-only anchor가 plain non-final reasoning에 재등장했다 | `guarded_stream`, `done`, `guard_error` | bounded candidate fragment suppression과 `assistant_history_anchor` observation |
|
||||
| `approved_tail` | rolling epoch의 active filter outcome이 all-complete pass이고 eligible text/reasoning delta가 threshold 또는 terminal에 도달했다 | `guarded_stream`, `paced_delivery`, `guard_error` | first eligible delta부터 threshold/terminal까지의 monotonic 수집 시간 `T`, approved rune 수와 ordered endpoint event를 immutable queue item으로 고정한다. prefill/filter 평가 시간은 제외하고 recovery는 이 상태 이후 payload를 폐기·교체하지 않는다 |
|
||||
| `paced_delivery` | approved queue가 비어 있지 않다 | `paced_delivery`, `done`, `guard_error` | 비동기 scheduler가 frame tick별 누적 rune quota로 approved text/reasoning delta를 `T` 동안 전달한다. 다음 provider window는 동시에 `guarded_stream`에서 수집·평가하며 queue bound에 도달할 때만 upstream backpressure를 적용한다. structural/tool event는 원자적으로 순서를 보존하고 terminal은 queue drain 뒤 한 번만 전달한다 |
|
||||
| `repairing` | single-stream content 반복 또는 D01에서 승인된 무진전 assistant-history 반복을 filter가 감지했다 | `repair_prompt_preparing`, `guard_error` | filter는 반복 전 원문 보존·반복 구간 제외와 다음 온도 후보를 typed directive로 가진 `continuation_repair` RecoveryIntent를 반환하고 직접 abort/retry하지 않는다. Core는 all-complete 뒤 plan 하나를 고르고 current attempt ownership을 먼저 종료한다 |
|
||||
| `repair_prompt_preparing` | Core Arbiter가 continuation intent를 선택했고 terminal/tool side effect·문맥 한도 때문에 차단되지 않았다 | `guarded_stream`, `guard_error` | endpoint Rebuilder가 반복 전 content와 think/reasoning 원문을 channel별로 구분하고 고정 영어 지시문을 더해 새 요청을 직접 조립한다. 사용자 요청·message, 언어 판별·번역·별도 모델 호출은 포함하지 않는다. Core는 stream-open safe prefix/look-behind/release cursor를 보존하고 이미 종료한 current attempt를 다시 abort하지 않으며, 새 response-start/role/기존 prefix는 중복 release하지 않는다 |
|
||||
| `length_continuation` | codec이 provider output-cap `length` terminal을 전달하고 `provider_length_gate`가 managed profile·context trajectory·tool/cancel 경계를 통과시켰다 | `guarded_stream`, `length_terminal` | Core는 중간 terminal을 commit하지 않고 safe prefix/cursor를 보존한다. current attempt ownership 종료 뒤 endpoint Rebuilder가 original request와 channel별 assistant prefix를 조립하고, prefill 지원 시 우선 사용해 다음 small-cap attempt 하나를 dispatch한다 |
|
||||
|
|
@ -94,6 +101,7 @@
|
|||
- 출력:
|
||||
- `passthrough`: 기존 provider-compatible 응답을 유지한다.
|
||||
- `passthrough_guarded`: Core `rolling_window`/`fragment_gate` 위에서 repeat/history/action filter가 observe, bounded dedupe, violation/fatal/replacement 또는 `continuation_repair` intent를 반환한다. 모든 active outcome 전에는 response-start/role/content를 release하지 않고, stream-open continuation은 기존 safe prefix를 보존한 채 새 attempt opening/prefix를 중복하지 않는다.
|
||||
- `approved_tail_delivery`: `passthrough_guarded`의 rolling window가 all-complete pass한 뒤 text/reasoning payload만 `pending`에서 immutable `approved` queue로 이동한다. scheduler는 수집 시간 `T`와 rune 수로 계산한 frame별 누적 quota만큼 endpoint-safe delta를 비동기 전달하며 structural/tool event는 분할하지 않는다. 다음 window 수집은 delivery와 겹치고 terminal은 queue drain 뒤 한 번만 전달된다.
|
||||
- `provider_error_retry`: `filters[]` matched error가 Core `CommitState=transport_uncommitted`일 때만 `exact_replay` intent를 반환한다. staged status/header/body는 commit이 아니며 Coordinator가 Tool Call validation과 최초 실행 제외 최대 3회를 공유하고 current attempt abort 뒤 lossless Rebuilder/provider-pool admission을 cycle마다 한 번 호출한다. stream-open 뒤에는 pending 유무와 관계없이 만들지 않는다.
|
||||
- `contract_schema`: content channel을 configured hard bound의 `terminal_gate`로 수집/검증한 뒤 valid JSON만 반환한다. invalid는 `schema_repair` intent, overflow는 partial release 없는 terminal error이며 `stream=true`에서도 response-start/`delta.content`를 검증 전에 commit하지 않는다.
|
||||
- `managed_length_continuation`: 중간 `length`/`[DONE]`을 caller에 보내지 않고 same-stream safe prefix/cursor를 보존한다. original request와 content/think/reasoning assistant prefix를 lossless endpoint shape로 rebuild하며, prefill 불가 시 endpoint-private 고정 directive만 사용하고 새 user message·요약·문장 경계 절단·별도 모델 호출은 만들지 않는다. 미완성 tool fragment는 endpoint Rebuilder가 assistant continuation prefix로 lossless하게 직렬화할 수 있을 때만 내부에 포함하며, 그렇지 않으면 release 없이 최종 오류로 끝낸다. context 또는 caller logical cap으로 trajectory가 끝날 때만 endpoint-native logical `length` terminal과 종료 marker를 한 번 보내며, tool boundary가 닫히면 endpoint별 final terminal 하나만 보낸다. attempt 중간 `length`는 보내지 않는다.
|
||||
|
|
@ -103,7 +111,8 @@
|
|||
- diagnostic `Filter` mock은 local/dev smoke의 명시적 test seam에서만 등록하고 deterministic decision만 반환한다. 외부 request/config field로 선택할 수 없고 production 기본 Registry에는 포함하지 않으며, codec/Core/Arbiter/recovery/ReleaseSink/observation sink는 mock으로 대체하지 않는다.
|
||||
- repeat/schema/provider-error처럼 사용자 출력 안전성에 직접 관여하는 filter는 기본 `blocking`, dev-corp 사전 관찰용 action rule은 명시적 `observe_only`로 등록할 수 있다. blocking error/deadline은 fatal, observe-only error/deadline은 `observe_error`로 정규화하며 어느 경우에도 silent pass하지 않는다.
|
||||
- Chat/Responses codec은 response-start를 포함한 normalized event와 lossless `RequestRebuilder`를 제공하고 Edge adapter는 Core `AttemptDispatcher`/`AttemptController`/`ReleaseSink`를 구현한다. Core는 hold, all-complete, commit, recovery budget/abort/rebuild 호출/dispatch를 담당하고 filter는 반복/schema/provider-error 의미와 typed intent만 담당한다.
|
||||
- 기본 repeat는 `stream_hold.evidence_runes=500` rolling window, schema는 explicit bounded terminal gate, tool fragment는 fragment gate다. terminal gate 외 기본 경로는 전체 응답을 모으지 않으며 response status/header와 opening role/event도 첫 safe release까지 stage한다. 시간 경과는 release 조건이 아니다.
|
||||
- 기본 repeat는 `stream_hold.evidence_runes=500` rolling window, schema는 explicit bounded terminal gate, tool fragment는 fragment gate다. terminal gate 외 기본 경로는 전체 응답을 모으지 않으며 response status/header와 opening role/event도 첫 safe release까지 stage한다. 시간 경과는 release eligibility가 아니며 idle evidence 미충족은 계속 fail-closed한다.
|
||||
- rolling release lifecycle은 `pending -> approved -> delivered`다. Core는 첫 eligible text/reasoning delta부터 threshold/terminal까지 monotonic 수집 시간 `T`를 재고 all-complete pass 뒤에만 immutable approved queue item을 만든다. delivery scheduler는 별도 비동기 ownership에서 고정 frame tick과 누적 rune quota로 `T`에 맞춰 endpoint-safe text/reasoning event를 나누고, 다음 window collection/filter evaluation은 동시에 진행한다. bounded queue가 찰 때만 upstream을 backpressure하며 prefill/filter latency는 `T`에 포함하지 않는다. recovery는 unapproved tail만 폐기·교체할 수 있고 approved item의 payload/order/duration은 변경하지 않는다.
|
||||
- stream consumer filter는 Core의 `FilterObservation` timeline에 stable `consumer_id`/`filter_id`/`rule_id`, effective/pending rune, decision, commit/terminal state와 sanitized fingerprint/count/offset evidence, 최대 4개의 sanitized failure cause code만 남긴다. request/run/provider/model correlation은 Core가 전파하며 raw output/prompt/tool args/result/auth, raw stack trace, provider endpoint/body는 넣지 않는다.
|
||||
- `/v1/chat/completions`와 `/v1/responses`는 이번 Milestone 범위에 포함한다. Chat Completions는 message/delta/tool-call parser와 Chat `RequestRebuilder`를, Responses는 item/reasoning/function-call parser와 Responses `RequestRebuilder`를 각각 제공하며, Coordinator가 동일 filter decision/intent에서 endpoint별 구현을 호출한다. Claude stream과 agent-family별 codec은 이번 범위에 포함하지 않는다.
|
||||
- filter input과 decision에는 caller/agent 제품명을 넣지 않는다. 동일한 OpenAI-compatible payload와 provider capability는 raw HTTP, OpenAI SDK, Pi 등 caller가 달라도 같은 판정을 내린다.
|
||||
|
|
@ -136,6 +145,8 @@
|
|||
- provider-pool이 선택한 tunnel/normalized 실행 path를 filter가 바꾸거나 raw parser를 공유하지 않는다. 두 path가 codec 뒤 같은 Core event를 쓰는 것은 허용한다.
|
||||
- 반복루프 repair 안내 문구를 assistant content chunk로 주입하지 않는다.
|
||||
- 이미 downstream으로 tool call을 보내 실행 side effect 가능성이 생긴 뒤 자동 continuation repair를 수행하지 않는다.
|
||||
- 승인 전 pending payload를 timer 만료로 release하거나, synchronous `ReleaseSafe`/sink call 안에서 sleep해 provider 수집을 직렬로 막지 않는다.
|
||||
- structural event나 tool-call JSON fragment를 pacing용 rune chunk로 분할하지 않고, approved queue drain 전에 terminal 또는 `[DONE]`을 전달하지 않는다.
|
||||
- tool/action fingerprint raw args나 secret 가능 문자열을 metric label, request id, provider id 같은 장기 식별자에 넣지 않는다.
|
||||
- side-effect 가능 tool/action을 자동 replay하거나 continuation repair로 재실행하지 않는다.
|
||||
- assistant history anchor 증거를 위해 raw prompt, raw tool args/result, 전체 reasoning/content를 장기 로그에 복제하지 않는다.
|
||||
|
|
@ -172,6 +183,7 @@
|
|||
| S21 | `stream-gate-adoption` | Chat/Responses ingress raw body가 limit-1, limit, limit+1이거나 typed view/rebuild를 포함한 current peak retained bytes가 limit을 넘는다 | endpoint host가 body를 읽기 전에 overflow를 판정하고 SnapshotBuilder/Rebuilder가 typed view 추가 직후와 rebuild 할당 전후에 다시 계상한다 | limit-1/limit은 pre-read body gate를 통과하고 limit+1은 즉시 거부된다. 이후 initial retained overflow는 raw snapshot을 release한 HTTP 413 `invalid_request_error`, rebuild overflow는 commit state에 맞는 terminal recovery error 하나로 끝난다. 어느 overflow도 provider dispatch/recovery budget 소비/raw request 로그를 만들지 않는다 |
|
||||
| S22 | `length-continuation` | managed profile의 Chat/Responses provider가 16K attempt cap에서 두 번 이상 `length` terminal을 반환하고 safe prefix가 release됐으며 rebuilt prompt/context/reserve와 (설정된 경우) caller logical cap allowance가 남아 있다 | `provider_length_gate`가 `managed_continuation` intent를 반환하고 Rebuilder가 original request와 channel별 assistant prefix를 다음 attempt로 조립한다 | 중간 `length`/`[DONE]`, response-start/role/prefix 중복 없이 같은 stream을 이어 간다. fault recovery 3회 cap은 소비하지 않으며 context 또는 caller logical cap exhaustion 때만 logical `length` terminal과 종료 marker를 한 번 보내고, complete tool boundary, cancel 또는 lossless fragment serialization 불가에서는 endpoint별 final terminal 하나로 수렴한다 |
|
||||
| S23 | `observable-core-smoke` | local/dev diagnostic smoke가 deterministic Chat/Responses provider stream과 `pass`, `observe_only` violation, pre-release `blocking` violation을 반환하는 diagnostic `Filter` mock을 사용한다 | 한 명령으로 실제 codec, Core, all-complete Arbiter, Recovery Coordinator, ReleaseSink와 observation sink를 통과시킨다 | 모든 batch가 평가 전에 stage된다. pass와 observe-only는 stage/evaluate/arbitrate/release/single-terminal로 수렴하고 observe-only violation은 출력 차단 없이 관측된다. blocking은 current attempt를 abort하고 recovery를 한 번 dispatch한 뒤 새 attempt의 출력과 terminal만 한 번 전달한다. 전체 timeline의 correlation은 안정적이고 attempt 전환은 명시되며 raw prompt/output/tool args/result/auth는 구조화된 결과나 일반 로그에 남지 않는다 |
|
||||
| S24 | `approved-tail-pacing` | fake monotonic clock의 200/500/1000-rune rolling pass window, short terminal tail, blocking recovery, tool fragment, cancel/sink failure와 bounded queue fixture가 Chat/Responses tunnel·normalized path에 입력된다 | filter pass로 승인된 이전 window를 scheduler가 전달하는 동안 다음 provider window가 계속 수집·평가된다 | 시간은 승인 전 release eligibility나 idle fail-open으로 사용되지 않는다. 각 approved window의 delivery duration은 수집 시간 `T`의 ±10%이고 exact rune/event/channel 순서와 UTF-8을 보존하며 threshold whole-batch burst와 `collect T + deliver T` 직렬 지연이 없다. recovery는 unapproved tail만 교체하고 structural/tool event는 원자적이며 queue drain 뒤 terminal 하나만 전달한다. cancel·sink failure는 timer/queue/provider ownership을 정리한다 |
|
||||
|
||||
## Evidence Map
|
||||
|
||||
|
|
@ -200,6 +212,7 @@
|
|||
| S21 | Chat/Responses raw-body limit-1/limit/limit+1 pre-read, exact-limit body+typed-view overflow, no-full-read overflow, rebuild pre/post-allocation peak and release fixtures | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion`에 `stream-gate-adoption`, body-gate-vs-total-retained 구분/initial 413/rebuild commit-aware terminal/no-dispatch/no-budget/no-raw-log assertion |
|
||||
| S22 | 16K output-cap `length` 2회 이상, original request + channel prefix/prefill rebuild, same-stream cursor/opening/prefix suppression, context/reserve 또는 caller logical cap exhaustion과 complete tool-call, lossless fragment serialization 가능/불가, cancel fixture | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion`에 `length-continuation`, fault cap과 trajectory budget 분리·no summary/truncation/new user message·single final terminal assertion |
|
||||
| S23 | 한 명령 local/dev diagnostic smoke, deterministic pass/observe-only/blocking provider fixtures, Chat/Responses 실제 codec/Core/Arbiter/recovery/ReleaseSink 연결, ordered raw-free observation timeline | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion`에 `observable-core-smoke`, 모든 batch의 stage-before-evaluate/release, observe-only nonblocking, single abort/recovery, stable correlation/attempt switch, output·terminal 중복 부재와 prompt/output/tool/auth 미기록 assertion |
|
||||
| S24 | fake clock 200/500/1000-rune·short-tail duration fixture, overlapping collection/delivery trace, bounded queue/backpressure, recovery/tool/cancel/sink-failure tests, dev `ornith:35b` direct OneX 대 Edge gap 비교, active spec/contract diff | `agent-task/m-openai-compatible-output-validation-filters/...` | `Roadmap Completion`에 `approved-tail-pacing`, 승인 전 no-release, window별 `T` ±10%, exact rune/event/channel order, no serial double-latency, single terminal/timer cleanup, 2ms 미만 gap 비율과 threshold burst 감소, 구현 스펙·API/config 계약 동기화 assertion |
|
||||
|
||||
## Cross-repo Dependencies
|
||||
|
||||
|
|
@ -210,7 +223,7 @@
|
|||
- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
|
||||
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
|
||||
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
|
||||
- [x] 사용자 결정과 최종 승인이 [user_review_0.log](user_review_0.log)와 SDD에 반영됐다.
|
||||
- [x] 사용자 결정과 최종 승인이 [user_review_0.log](user_review_0.log), 현재 요청의 D08과 SDD에 반영됐다.
|
||||
|
||||
## 사용자 리뷰 이력
|
||||
|
||||
|
|
@ -238,6 +251,7 @@
|
|||
- 2026-07-24: 사용자가 provider의 큰 `max_tokens`가 local scheduling·동시성에 주는 부작용을 작은 attempt cap과 IOP managed continuation으로 분리하도록 확정했다. `length` 중간 terminal은 숨기고 원본 요청과 channel별 assistant prefix를 보존해 context-window/reserve가 허용하는 논리 trajectory를 같은 stream에 이어 간다. 이는 fault recovery 3회 cap과 별도이며 요약·문장 경계 절단·새 user message 방식은 사용하지 않는다.
|
||||
- 2026-07-25: 재검증에서 rebuilt prompt가 assistant prefix를 이미 포함하므로 누적 output 이중 계상을 제거했다. provider attempt cap은 내부 운영 단위로만 쓰고 caller 명시 output cap은 논리 요청 전체에 한 번 적용한다. context 또는 caller logical cap 소진은 중간 provider terminal이 아닌 endpoint-native logical `length` terminal 한 번으로 표현하며, 미완성 tool fragment는 lossless serializer가 있는 경우에만 내부 continuation prefix로 사용한다.
|
||||
- 2026-07-28: 사용자가 실제 의미 필터 구현 전에 pipeline 자체를 관측·검증할 deterministic diagnostic mock smoke를 선행 조건으로 확정했다. pass, observe-only violation, blocking violation 뒤 단일 recovery가 실제 codec/Core/Arbiter/ReleaseSink와 raw-free observation 경로를 통과해야 하며 production 기본 등록과 caller 활성화는 금지한다.
|
||||
- 2026-08-02: 사용자가 threshold whole-batch burst를 완화하기 위해 window 수집 시간만큼 승인된 tail을 균일하게 전달하는 정책을 확정했다. 시간은 filter 승인 전 release 조건이 아니며 `pending -> approved -> delivered`를 분리하고, 이전 approved delivery와 다음 provider collection을 겹쳐 직렬 `collect T + deliver T` 지연을 만들지 않는다.
|
||||
|
||||
## 작업 컨텍스트
|
||||
|
||||
|
|
@ -246,6 +260,7 @@
|
|||
- 표준선: `provider_length_gate`는 provider의 output-cap terminal만 caller-neutral하게 판정한다. managed profile은 작은 attempt cap으로 dispatch하고, original request와 content/think/reasoning assistant prefix를 endpoint별 shape로 rebuild한다. prefill capability가 있으면 우선 사용하며 없으면 고정 internal continuation directive만 더한다. Core의 `ManagedTrajectoryBudget`은 rebuilt prompt token과 reserve를 뺀 실제 context-window 여유 및 caller가 명시한 논리 output cap을 장문 상한으로 사용하며, prefix를 누적 output으로 이중 계상하지 않고 fault recovery 3회 cap과 분리한다. provider attempt cap은 내부 운영 단위다.
|
||||
- 표준선: Chat/Responses codec은 response-start 포함 raw parser와 lossless `RequestRebuilder`를, Edge는 `AttemptDispatcher`/`AttemptController`/`ReleaseSink`를 제공한다. Core가 active filter single-flight fan-out/all-complete, deterministic action, commit, budget/abort/rebuild 호출/single re-admission을 담당한다.
|
||||
- 표준선: provider 출력은 `host dispatch → response-start/event codec → Core hold → active filters parallel evaluation → all-complete Arbiter → staged release 또는 RecoveryPlan → current attempt abort → rebuild → single re-admission` 순서다. repeat는 500-rune rolling, schema는 hard-bound terminal gate이므로 기본 streaming 경로만 전체 응답을 모으지 않는다.
|
||||
- 표준선: rolling pass의 release는 synchronous whole-batch sink loop가 아니라 bounded approved queue와 비동기 pacing scheduler를 사용한다. 첫 eligible delta부터 threshold/terminal까지의 monotonic 수집 시간 `T`와 approved rune 수를 immutable item에 고정하고 frame tick 누적 quota로 전달한다. 이전 approved item 전달 중 다음 window를 계속 수집·평가하며 queue full에서만 backpressure를 건다. 시간은 승인 전 eligibility가 아니고 recovery는 unapproved tail만 교체하며 structural/tool event 순서, drain 뒤 단일 terminal, cancel/sink-failure cleanup을 보존한다.
|
||||
- 표준선: request 시작 시 Registry/config generation과 required capability를 고정한다. actual provider/path별 active set은 같은 snapshot에서 attempt마다 다시 resolve하고 optional filter만 skip할 수 있다. blocking/observe-only failure는 명시적 outcome이며 filter는 concurrency, request mutation, retry loop/counter, submit을 소유하지 않는다.
|
||||
- 표준선: 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로 조정한다.
|
||||
|
|
|
|||
Loading…
Reference in a new issue