feat(roadmap): 업데이트 OpenAI-compatible Tool Call Boundary Hardening 마일스톤 상태를 검토중으로 변경

This commit is contained in:
toki 2026-07-04 19:50:39 +09:00
parent 53e09201a0
commit dccfbff9ff
2 changed files with 17 additions and 17 deletions

View file

@ -23,7 +23,7 @@ Ollama serving 경로와 운영 기반이 안정화된 뒤, 단계 호출, tool/
- 경로: `agent-roadmap/archive/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-think-control.md`
- 요약: OpenAI-compatible Chat Completions 요청에서 thinking/reasoning 생성과 응답 노출을 요청별로 제어하고 provider별 option 매핑과 unsupported 정책을 구현한다.
- [계획] OpenAI-compatible Tool Call Boundary Hardening
- [검토중] OpenAI-compatible Tool Call Boundary Hardening
- 경로: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md`
- 요약: provider-pool/OpenAI-compatible 응답에서 raw text tool-call block, unknown tool name, chat-template sentinel token이 클라이언트 화면으로 새지 않도록 Edge tool-call 경계를 검증/정규화한다.

View file

@ -12,7 +12,7 @@ OpenAI-compatible `tools[]`가 있는 요청에서 provider-pool 또는 provider
## 상태
[계획]
[검토중]
## 승격 조건
@ -41,25 +41,25 @@ OpenAI-compatible `tools[]`가 있는 요청에서 provider-pool 또는 provider
OpenAI-compatible provider 응답의 tool-call 후보를 요청 schema 기준으로 구조화하거나 차단하는 capability를 묶는다.
- [ ] [provider-raw-parse] provider-pool/provider route에서도 요청 `tools[]`가 있고 assistant content에 raw `<tool_call>` block이 있으면 기존 text tool-call parser를 적용한다. 검증: `bash` raw block은 `message.tool_calls` 또는 stream `delta.tool_calls`로 정규화되고 raw block이 content에 남지 않는다.
- [ ] [unknown-tool-error] raw tool-call block의 function name이 요청 tool 목록에 없으면 일반 assistant text로 flush하지 않고 validation error 또는 bounded retry로 처리한다. 검증: `TOOL_CALLS.push_changes` 형태의 unknown tool output이 Pi/Cline 화면으로 노출되지 않는다.
- [ ] [stream-flush-guard] streaming 경로에서 tool-call 후보를 보류한 뒤 terminal event에서 구조화/차단하며, 실패한 raw block을 마지막 flush로 내보내지 않는다. 검증: live SSE와 buffered SSE 테스트가 모두 raw `<tool_call>` leak 없이 `tool_calls` 또는 error로 종료된다.
- [x] [provider-raw-parse] provider-pool/provider route에서도 요청 `tools[]`가 있고 assistant content에 raw `<tool_call>` block이 있으면 기존 text tool-call parser를 적용한다. 검증: `bash` raw block은 `message.tool_calls` 또는 stream `delta.tool_calls`로 정규화되고 raw block이 content에 남지 않는다.
- [x] [unknown-tool-error] raw tool-call block의 function name이 요청 tool 목록에 없으면 일반 assistant text로 flush하지 않고 validation error 또는 bounded retry로 처리한다. 검증: `TOOL_CALLS.push_changes` 형태의 unknown tool output이 Pi/Cline 화면으로 노출되지 않는다.
- [x] [stream-flush-guard] streaming 경로에서 tool-call 후보를 보류한 뒤 terminal event에서 구조화/차단하며, 실패한 raw block을 마지막 flush로 내보내지 않는다. 검증: live SSE와 buffered SSE 테스트가 모두 raw `<tool_call>` leak 없이 `tool_calls` 또는 error로 종료된다.
- [x] [sentinel-sanitize] provider output normalization이 `<|mask_end|>` 같은 알려진 chat-template sentinel token을 assistant content/reasoning 최종 출력에서 제거한다. 검증: sentinel token이 tool-call 뒤나 단독 content로 들어와도 OpenAI-compatible 응답에 남지 않는다.
- [ ] [native-preserve] provider가 이미 native OpenAI-compatible `tool_calls`를 반환하는 경로는 기존 pass-through와 schema-normalization 동작을 유지한다. 검증: native `tool_calls` server tests와 forced/auto direct tool-call smoke가 회귀 없이 통과한다.
- [ ] [client-smoke] Pi/Cline형 긴 agent prompt와 `tools[]` 요청을 직접 Edge에 호출해 unknown tool hallucination이 raw text로 노출되지 않는 근거를 남긴다. 검증: 재현 요청, response finish reason/error type, Edge trace를 `agent-test/dev`에 기록한다.
- [ ] [contract-docs] OpenAI-compatible 계약 문서와 dev 운영 문서가 provider raw tool-call 후보의 정규화/차단, unknown tool 처리, sentinel sanitize 정책을 설명한다.
- [x] [native-preserve] provider가 이미 native OpenAI-compatible `tool_calls`를 반환하는 경로는 기존 pass-through와 schema-normalization 동작을 유지한다. 검증: native `tool_calls` server tests와 forced/auto direct tool-call smoke가 회귀 없이 통과한다.
- [x] [client-smoke] Pi/Cline형 긴 agent prompt와 `tools[]` 요청을 직접 Edge에 호출해 unknown tool hallucination이 raw text로 노출되지 않는 근거를 남긴다. 검증: 재현 요청, response finish reason/error type, Edge trace를 `agent-test/dev`에 기록한다.
- [x] [contract-docs] OpenAI-compatible 계약 문서와 dev 운영 문서가 provider raw tool-call 후보의 정규화/차단, unknown tool 처리, sentinel sanitize 정책을 설명한다.
## 완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 계획 Milestone이며 기능 Task가 아직 충족되지 않았다.
- 상태: 검토중
- 요청일: 2026-07-04
- 완료 근거: `01_provider_text_boundary/complete.log``02+01_contract_dev_smoke/complete.log`가 모든 기능 Task id의 PASS, Go 검증, dev-runtime smoke evidence를 기록한다. 현 파일/git sanity 확인에서도 `apps/edge/internal/openai`, `agent-contract/outer/openai-compatible-api.md`, `docs/edge-local-dev-guide.md`, `agent-test/dev/edge-smoke.md`가 해당 정책과 테스트를 유지한다.
- 검토 항목:
- [ ] valid raw text tool call은 구조화되고 raw block이 노출되지 않는다
- [ ] unknown/malformed tool call은 성공 content로 노출되지 않는다
- [ ] Pi/Cline형 OpenAI-compatible tools smoke 근거가 남아 있다
- [x] valid raw text tool call은 구조화되고 raw block이 노출되지 않는다
- [x] unknown/malformed tool call은 성공 content로 노출되지 않는다
- [x] Pi/Cline형 OpenAI-compatible tools smoke 근거가 남아 있다
- agent-ui 상태 반영: 해당 없음
- 리뷰 코멘트: 없음
- 리뷰 코멘트: 기능 Task와 구현 잠금은 완료 후보 조건을 충족했으며, `[완료]` 전환과 archive 이동은 별도 완료 승인 후 처리한다.
## 범위 제외
@ -74,8 +74,8 @@ OpenAI-compatible provider 응답의 tool-call 후보를 요청 schema 기준으
- 관련 경로: `apps/edge/internal/openai`, `apps/node/internal/adapters/openai_compat`, `agent-contract/outer/openai-compatible-api.md`, `docs/openai-compatible-api-contract.md`, `agent-test/dev`
- 표준선(선택): 명시적 `tools[]`가 있는 요청은 runtime-only validation을 우선한다. 요청 schema에 없는 tool name은 client-visible 성공 content가 아니라 validation failure로 다룬다.
- 표준선(선택): provider-pool은 native `tool_calls`만 신뢰하지 않고, raw text tool-call 후보가 보이면 같은 OpenAI-compatible boundary 정책으로 검증한다.
- 관측 메모(2026-07-04): 현재 구현/계약은 provider route 또는 provider-pool에서 fallback marker 없이 assistant content에 raw `<tool_call>` text가 들어온 경우 이를 `tool_calls`로 합성하지 않고 성공 content로 보존한다. 로컬 단위 재현은 `TestChatCompletionsDoesNotSynthesizeTextToolCallsForProviderRoute`가 고정하고 있다.
- 관측 메모(2026-07-04): unknown text tool-call도 현재는 client-visible content로 남는다. `TestChatCompletionsLeavesUnknownTextToolCallAsContent``TestChatCompletionsLeavesUnknownTemplateToolCallAsContent`가 이 동작을 고정한다.
- 완료 메모(2026-07-04): provider route 또는 provider-pool에서 assistant content에 raw `<tool_call>` text가 들어오면 요청 `tools[]` schema 기준으로 구조화하거나 차단한다. 관련 단위 테스트는 provider raw text synthesis, malformed/unknown blocking, multi-candidate validation을 포함한다.
- 완료 메모(2026-07-04): unknown text tool-call은 client-visible success content로 남기지 않고 bounded retry 또는 `tool_validation_error`로 처리한다. live SSE와 strict buffered stream 경로도 raw block을 flush하지 않는다.
- 완료 메모(2026-07-04): [sentinel-sanitize]는 `apps/edge/internal/openai/strict_output.go`, `stream.go`, `server_test.go`에서 non-stream Chat Completions, live SSE, Responses 출력의 `<|mask_end|>` 제거와 tool-call 뒤 sentinel 제거 회귀 테스트를 추가해 완료했다. 검증: `go test ./apps/edge/internal/openai`, `go test ./apps/edge/...`.
- 관측 메모(2026-07-04): dev-runtime Edge health는 응답했지만 `/v1/models`는 Bearer 인증을 요구해 secret 없이 live provider 재현은 수행하지 않았다. 실제 Pi/Cline형 긴 prompt에서 모델이 raw block을 자발 출력하는지는 credentialed client smoke로 확인해야 한다.
- 선행 작업: Tool Call Runtime 검증 재시도 MVP, OpenAI-compatible Think 제어 MVP에서 확인된 provider-pool/Pi raw leak 진단