Files
kebab/docs/wire-schema/v1/answer.schema.json
altair823 c56242d04f chore(cli): PR #171 회차 1 리뷰 반영
`answer.schema.json` 의 `refusal_reason` description 의 PR 번호 정정:
`multi_hop_decompose_failed` 도입 시점 = PR-2 (#167, RefusalReason variant
+ ask_multi_hop decompose-failure 분기). PR-3a (#168) 는 `Answer.hops`
field + RagCfg knob 만 — refusal variant 와 무관.

검증
- `cargo test -p kebab-cli -j 1 --test wire_ask_multi_hop` 4 모두 통과.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 08:47:35 +00:00

103 lines
4.4 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://kb.local/wire/v1/answer.schema.json",
"title": "Answer v1",
"description": "Stub schema — declares the schema_version label and the required fields per design §2.3.",
"type": "object",
"required": [
"schema_version",
"answer",
"citations",
"grounded",
"model",
"prompt_template_version",
"retrieval",
"usage",
"created_at"
],
"properties": {
"schema_version": { "const": "answer.v1" },
"answer": { "type": "string" },
"citations": { "type": "array" },
"grounded": { "type": "boolean" },
"refusal_reason": {
"anyOf": [
{
"type": "string",
"enum": [
"score_gate",
"llm_self_judge",
"no_index",
"no_chunks",
"llm_stream_aborted",
"multi_hop_decompose_failed"
]
},
{ "type": "null" }
],
"description": "p9-fb-41: `multi_hop_decompose_failed` added in PR-2 alongside the multi-hop pipeline skeleton (only emitted when AskOpts.multi_hop = true and the decompose LLM call fails to parse). Other variants are unchanged from earlier phases."
},
"model": { "type": "object" },
"embedding": { "type": ["object", "null"] },
"prompt_template_version": { "type": "string" },
"retrieval": { "type": "object" },
"usage": { "type": "object" },
"created_at": { "type": "string", "format": "date-time" },
"conversation_id": {
"type": ["string", "null"],
"description": "p9-fb-15: same conversation 의 turn 들이 공유. CLI single-shot / TUI 첫 turn 은 null."
},
"turn_index": {
"type": ["integer", "null"],
"minimum": 0,
"description": "p9-fb-15: 같은 conversation 안 0-based 순서. null 이면 single-shot."
},
"hops": {
"anyOf": [
{
"type": "array",
"items": { "$ref": "#/$defs/HopRecord" }
},
{ "type": "null" }
],
"description": "p9-fb-41 multi-hop trace. Present (non-null array) only when the ask routed through the multi-hop pipeline (`AskOpts.multi_hop = true`); single-pass answers omit the field entirely (serde `skip_serializing_if = None`). Each entry records one LLM hop — decompose / decide / synthesize — with sub-queries, retrieval count, and per-hop latency. Wire-additive: pre-fb-41 readers tolerate the missing field; new readers branch on its presence to render the per-hop trace."
}
},
"$defs": {
"HopRecord": {
"type": "object",
"required": ["iter", "kind", "context_chunks_added", "forced_stop", "llm_call_ms"],
"properties": {
"iter": {
"type": "integer",
"minimum": 0,
"description": "0-based hop index. iter=0 is always the initial decompose; subsequent iters are decide calls; the final iter is the synthesize call."
},
"kind": {
"type": "string",
"enum": ["decompose", "decide", "synthesize"]
},
"sub_queries": {
"type": "array",
"items": { "type": "string" },
"description": "Per-kind semantics. Decompose: the initial sub-queries the LLM produced. Decide: the *new* sub-queries to retrieve next iter (empty when the LLM signalled stop or when forced_stop=true). Synthesize: always empty."
},
"context_chunks_added": {
"type": "integer",
"minimum": 0,
"description": "Number of *new* chunks the retrieval round contributed to the pool (deduped by chunk_id). 0 for decompose / synthesize hops."
},
"forced_stop": {
"type": "boolean",
"description": "True when the pipeline cut the loop short due to a safety cap (max_depth / max_pool_chunks) rather than the LLM's own stop signal. Tracing signal, not a refusal."
},
"llm_call_ms": {
"type": "integer",
"minimum": 0,
"description": "Wall-clock latency of the LLM call for this hop. `0` is overloaded — means 'no LLM call happened' when (a) the Decide hop was skipped due to forced_stop or (b) the pool was empty before any decide could run. Treat 0 as absent or instantaneous."
}
}
}
}
}