feat(rag): fb-41 PR-3a HopRecord wire + RagCfg multi-hop knobs #168

Merged
altair823 merged 1 commits from feat/fb-41-pr-3-dynamic-decide-loop into main 2026-05-25 07:18:29 +00:00
Owner

요약

fb-41 multi-hop RAG 의 PR-3a (PR-3 의 분할 첫 PR). wire additive (HopRecord + HopKind + Answer.hops field) + RagCfgmulti_hop_* 3 노브. RAG pipeline 동작 미변경 — 모든 Answer literal 의 hops = None. PR-3b (후속) 가 ask_multi_hop 의 happy path 에서 dynamic decide loop 구현 + hops trace 채움.

설계: docs/superpowers/specs/2026-05-25-p9-fb-41-multi-hop-rag-design.md
계획: docs/superpowers/plans/2026-05-25-p9-fb-41-multi-hop-rag.md (PR-3a 단락)

분할 이유 (사용자 결정 2026-05-25)

원래 PR-3 = wire + cfg + decide loop + ScriptedLm + helper refactor + 5+ tests 단일 PR. ~1500 줄 단일 patch 가 review 부담 + 회기 위험 ↑. PR-3a (additive foundation) + PR-3b (decide loop + ScriptedLm + tests) 로 분리.

변경

  • kebab_core::HopRecord (iter, kind, sub_queries, context_chunks_added, forced_stop, llm_call_ms) + HopKind (Decompose / Decide / Synthesize) — wire-additive shape, snake_case serde tag.
  • kebab_core::Answer.hops: Option<Vec<HopRecord>>#[serde(default, skip_serializing_if = "Option::is_none")]. single-pass / refusal path = None, PR-3b 의 multi-hop happy path = Some.
  • kebab_config::RagCfg 에 3 신규 노브:
    • multi_hop_max_depth: u32 (default 3) — KEBAB_RAG_MULTI_HOP_MAX_DEPTH
    • multi_hop_max_sub_queries_per_iter: u32 (default 5) — KEBAB_RAG_MULTI_HOP_MAX_SUB_QUERIES_PER_ITER
    • multi_hop_max_pool_chunks: u32 (default 30) — KEBAB_RAG_MULTI_HOP_MAX_POOL_CHUNKS
  • 9 Answer literal site 에 hops: None 명시 (kebab-rag/src/pipeline.rs ×6 + kebab-cli/src/main.rs + kebab-tui/tests/ask.rs + kebab-eval/src/metrics.rs). exhaustive field check 자동 guard.
  • plan 의 PR-3 단락 → PR-3a / PR-3b 분할 + scope 정정.

검증

  • cargo build -p kebab-core -p kebab-config -p kebab-rag -p kebab-store-sqlite -p kebab-tui -p kebab-cli -p kebab-eval -j 1 — clean.
  • cargo test -p kebab-config -p kebab-core -p kebab-rag -j 1 — 163 test 모두 녹색 (50 config + 57 core + 31 rag unit + 19 pipeline + 3 prompt + 3 streaming).
  • cargo clippy -p kebab-config -p kebab-core -p kebab-rag -p kebab-store-sqlite -p kebab-tui -p kebab-cli -p kebab-eval --all-targets -j 1 -- -D warnings — clean.
  • 16 GB RAM 직렬 build 규약 준수 (-j 1).

시험 항목 (Test Plan)

  • default_multi_hop_max_depth_is_3
  • default_multi_hop_max_sub_queries_per_iter_is_5
  • default_multi_hop_max_pool_chunks_is_30
  • env_overrides_multi_hop_knobs — 3 env var 모두 override
  • legacy_config_without_multi_hop_knobs_uses_defaultsLEGACY_PRE_TIMEOUT_TOML fixture 공유
  • 9 Answer literal site 모두 hops: None 추가 후 기존 RAG / TUI / CLI / eval test 자동 회귀 핀

Wire 영향

answer.v1 의 optional hops 필드. skip_serializing_if = None 이라 single-pass response 에 emit 안 됨 → 기존 single-pass consumer (CLI / MCP / TUI / agent) 자동 backward-compat. JSON Schema 갱신은 PR-3b 또는 PR-4 (실제 multi-hop happy path 가 Some 채우는 시점).

비범위 (PR-3b)

  • multi_hop_decompose 의 latency 측정 + multi_hop_decide helper 신규
  • ask_multi_hop 의 dynamic decide loop (iter 0 decompose → iter 1+ retrieve+decide → iter N synthesize, max_depth cap, max_pool_chunks cap, forced_stop signaling)
  • ScriptedLm helper (per-call 다른 response 반환) — happy-path multi-hop integration test 가능
  • 5+ integration test: multi_hop_decide_stop_triggers_synthesize, multi_hop_decide_continue_adds_more_chunks, multi_hop_max_depth_force_stops, multi_hop_pool_chunks_dedup_by_chunk_id, multi_hop_decide_parse_failure_falls_through_to_synthesize
  • MULTI_HOP_DECOMPOSE_USER_TEMPLATEformat! named arg 교체 (PR-2 회차 1 carry-over)
  • mirror refactor (ask + ask_multi_hop 의 §4-§9 helper 추출, PR-2 회차 1 carry-over) — 별 PR 후속 가능성

다음 단계

PR-3a 머지 후 PR-3b 시작. branch 명: feat/fb-41-pr-3b-decide-loop 권장 (현재 branch feat/fb-41-pr-3-dynamic-decide-loop 는 본 PR-3a 의 misnamed branch, 머지 후 cleanup).

Assisted-by: Claude Code

## 요약 fb-41 multi-hop RAG 의 **PR-3a** (PR-3 의 분할 첫 PR). wire additive (`HopRecord` + `HopKind` + `Answer.hops` field) + `RagCfg` 의 `multi_hop_*` 3 노브. **RAG pipeline 동작 미변경** — 모든 Answer literal 의 `hops = None`. PR-3b (후속) 가 `ask_multi_hop` 의 happy path 에서 dynamic decide loop 구현 + hops trace 채움. 설계: docs/superpowers/specs/2026-05-25-p9-fb-41-multi-hop-rag-design.md 계획: docs/superpowers/plans/2026-05-25-p9-fb-41-multi-hop-rag.md (PR-3a 단락) ## 분할 이유 (사용자 결정 2026-05-25) 원래 PR-3 = wire + cfg + decide loop + ScriptedLm + helper refactor + 5+ tests 단일 PR. ~1500 줄 단일 patch 가 review 부담 + 회기 위험 ↑. **PR-3a** (additive foundation) + **PR-3b** (decide loop + ScriptedLm + tests) 로 분리. ## 변경 - `kebab_core::HopRecord` (`iter`, `kind`, `sub_queries`, `context_chunks_added`, `forced_stop`, `llm_call_ms`) + `HopKind` (`Decompose` / `Decide` / `Synthesize`) — wire-additive shape, snake_case serde tag. - `kebab_core::Answer.hops: Option<Vec<HopRecord>>` — `#[serde(default, skip_serializing_if = "Option::is_none")]`. single-pass / refusal path = None, PR-3b 의 multi-hop happy path = Some. - `kebab_config::RagCfg` 에 3 신규 노브: - `multi_hop_max_depth: u32` (default 3) — `KEBAB_RAG_MULTI_HOP_MAX_DEPTH` - `multi_hop_max_sub_queries_per_iter: u32` (default 5) — `KEBAB_RAG_MULTI_HOP_MAX_SUB_QUERIES_PER_ITER` - `multi_hop_max_pool_chunks: u32` (default 30) — `KEBAB_RAG_MULTI_HOP_MAX_POOL_CHUNKS` - 9 Answer literal site 에 `hops: None` 명시 (`kebab-rag/src/pipeline.rs` ×6 + `kebab-cli/src/main.rs` + `kebab-tui/tests/ask.rs` + `kebab-eval/src/metrics.rs`). exhaustive field check 자동 guard. - plan 의 PR-3 단락 → PR-3a / PR-3b 분할 + scope 정정. ## 검증 - `cargo build -p kebab-core -p kebab-config -p kebab-rag -p kebab-store-sqlite -p kebab-tui -p kebab-cli -p kebab-eval -j 1` — clean. - `cargo test -p kebab-config -p kebab-core -p kebab-rag -j 1` — 163 test 모두 녹색 (50 config + 57 core + 31 rag unit + 19 pipeline + 3 prompt + 3 streaming). - `cargo clippy -p kebab-config -p kebab-core -p kebab-rag -p kebab-store-sqlite -p kebab-tui -p kebab-cli -p kebab-eval --all-targets -j 1 -- -D warnings` — clean. - 16 GB RAM 직렬 build 규약 준수 (`-j 1`). ## 시험 항목 (Test Plan) - [x] `default_multi_hop_max_depth_is_3` - [x] `default_multi_hop_max_sub_queries_per_iter_is_5` - [x] `default_multi_hop_max_pool_chunks_is_30` - [x] `env_overrides_multi_hop_knobs` — 3 env var 모두 override - [x] `legacy_config_without_multi_hop_knobs_uses_defaults` — `LEGACY_PRE_TIMEOUT_TOML` fixture 공유 - [x] 9 Answer literal site 모두 `hops: None` 추가 후 기존 RAG / TUI / CLI / eval test 자동 회귀 핀 ## Wire 영향 `answer.v1` 의 optional `hops` 필드. `skip_serializing_if = None` 이라 single-pass response 에 emit 안 됨 → 기존 single-pass consumer (CLI / MCP / TUI / agent) 자동 backward-compat. JSON Schema 갱신은 **PR-3b 또는 PR-4** (실제 multi-hop happy path 가 `Some` 채우는 시점). ## 비범위 (PR-3b) - `multi_hop_decompose` 의 latency 측정 + `multi_hop_decide` helper 신규 - `ask_multi_hop` 의 dynamic decide loop (iter 0 decompose → iter 1+ retrieve+decide → iter N synthesize, max_depth cap, max_pool_chunks cap, forced_stop signaling) - `ScriptedLm` helper (per-call 다른 response 반환) — happy-path multi-hop integration test 가능 - 5+ integration test: `multi_hop_decide_stop_triggers_synthesize`, `multi_hop_decide_continue_adds_more_chunks`, `multi_hop_max_depth_force_stops`, `multi_hop_pool_chunks_dedup_by_chunk_id`, `multi_hop_decide_parse_failure_falls_through_to_synthesize` - `MULTI_HOP_DECOMPOSE_USER_TEMPLATE` 의 `format!` named arg 교체 (PR-2 회차 1 carry-over) - mirror refactor (`ask` + `ask_multi_hop` 의 §4-§9 helper 추출, PR-2 회차 1 carry-over) — **별 PR 후속 가능성** ## 다음 단계 PR-3a 머지 후 PR-3b 시작. branch 명: `feat/fb-41-pr-3b-decide-loop` 권장 (현재 branch `feat/fb-41-pr-3-dynamic-decide-loop` 는 본 PR-3a 의 misnamed branch, 머지 후 cleanup). Assisted-by: Claude Code
altair823 added 1 commit 2026-05-25 07:15:39 +00:00
PR-3 의 분할 첫 PR. wire additive (HopRecord + HopKind + Answer.hops
field) + RagCfg 의 multi_hop_* 3 노브. RAG pipeline 동작 미변경 —
모든 Answer literal 의 `hops = None`. PR-3b (후속) 가 ask_multi_hop
의 happy path 에서 dynamic decide loop 구현 + hops trace 채움.

분할 이유: 원래 PR-3 가 wire + cfg + decide loop + ScriptedLm +
helper refactor + 5+ tests 단일 PR 였는데 ~1500 줄 단일 patch 가
review 부담 + 회기 위험 ↑. additive foundation 부터 ship 후 decide
loop 별 PR — 사용자 결정 (2026-05-25).

- `kebab_core::HopRecord` (iter, kind, sub_queries,
  context_chunks_added, forced_stop, llm_call_ms) + `HopKind`
  (Decompose / Decide / Synthesize) — wire-additive shape.
- `kebab_core::Answer.hops: Option<Vec<HopRecord>>` —
  `#[serde(default, skip_serializing_if = "Option::is_none")]`,
  single-pass / refusal path 는 None, PR-3b 의 multi-hop happy
  path 가 Some.
- `kebab_config::RagCfg` 에 3 신규 노브:
  - `multi_hop_max_depth: u32` (default 3)
  - `multi_hop_max_sub_queries_per_iter: u32` (default 5)
  - `multi_hop_max_pool_chunks: u32` (default 30)
  3 모두 `#[serde(default)]` + env override
  (`KEBAB_RAG_MULTI_HOP_MAX_*`) + legacy parse 핀
  (`LEGACY_PRE_TIMEOUT_TOML` 공유).
- 9 Answer literal site (pipeline.rs ×6 + kebab-cli + kebab-tui
  tests + kebab-eval test) 에 `hops: None` 명시 추가. exhaustive
  field check 가 자동 guard — 빠진 site 시 compile fail.
- plan 의 PR-3 단락 → PR-3a / PR-3b 분할 명시 + scope 정정.

Tests (163 passing across kebab-config + kebab-core + kebab-rag):
- 5 신규 multi-hop knob test (default / env override / legacy parse).
- 기존 50+57+31+19+3+3 test 모두 hops:None 추가 후도 통과.

Wire 영향: `answer.v1` 의 optional `hops` 필드 — `skip_serializing_
if = None` 이라 single-pass response 에 emit 안 됨. wire breaking
아님, JSON Schema 갱신은 PR-3b 또는 PR-4 (실제 emit 시점).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-25 07:17:51 +00:00
Dismissed
claude-reviewer-01 left a comment
Member

회차 1 — PR-3a 의 wire additive + cfg 노브 foundation 깔끔 ship. RAG pipeline 미변경 + skip_serializing_if 가 backward-compat invariant 유지. actionable 0 (모두 PR-3b carry-over) + suggestion 3 + question 1 (self-resolved) + 칭찬 1:

suggestion (PR-3b carry-over):

  • HopRecord.sub_queries doc 의 "this iter" ambiguity — PR-3b 의 실 동작 확정 후 보강
  • MULTI_HOP_MAX_SUB_QUERIES_DEFAULT const vs cfg field reconcile — PR-3b 가 const 제거 또는 default fn 으로 변경
  • RagCfg multi_hop_* 의 flat (현재) vs hierarchic ([rag.multi_hop]) — 사용자 선호 결정 (PR-3b 또는 별 PR)

question (resolved): wire schema additionalProperties default true → PR-3b emit 시 wire breaking 아님.

칭찬: Answer.hops 의 skip_serializing_if annotation 가 backward-compat invariant 정확 표현.

머지 후 PR-3b (decide loop) 진행 권장. branch rename: feat/fb-41-pr-3b-decide-loop 또는 현재 misnamed branch 삭제 후 fresh.

회차 1 — PR-3a 의 wire additive + cfg 노브 foundation 깔끔 ship. RAG pipeline 미변경 + skip_serializing_if 가 backward-compat invariant 유지. actionable 0 (모두 PR-3b carry-over) + suggestion 3 + question 1 (self-resolved) + 칭찬 1: suggestion (PR-3b carry-over): - HopRecord.sub_queries doc 의 "this iter" ambiguity — PR-3b 의 실 동작 확정 후 보강 - `MULTI_HOP_MAX_SUB_QUERIES_DEFAULT` const vs cfg field reconcile — PR-3b 가 const 제거 또는 default fn 으로 변경 - RagCfg multi_hop_* 의 flat (현재) vs hierarchic (`[rag.multi_hop]`) — 사용자 선호 결정 (PR-3b 또는 별 PR) question (resolved): wire schema `additionalProperties` default `true` → PR-3b emit 시 wire breaking 아님. 칭찬: Answer.hops 의 `skip_serializing_if` annotation 가 backward-compat invariant 정확 표현. 머지 후 PR-3b (decide loop) 진행 권장. branch rename: `feat/fb-41-pr-3b-decide-loop` 또는 현재 misnamed branch 삭제 후 fresh.
@@ -183,1 +183,4 @@
pub max_context_tokens: usize,
/// p9-fb-41: hard ceiling on the number of multi-hop iterations
/// (decompose iter + decide iters). When the LLM keeps returning
/// `continue` past this depth the pipeline cuts to `synthesize`

Suggestion — RagCfg.multi_hop_* field 의 flat vs hierarchic naming 결정:

현재 채택: flat (rag.multi_hop_max_depth, rag.multi_hop_max_sub_queries_per_iter, rag.multi_hop_max_pool_chunks).

대안: hierarchic [rag.multi_hop] sub-section:

[rag.multi_hop]
max_depth = 3
max_sub_queries_per_iter = 5
max_pool_chunks = 30

장점:

  • 향후 multi-hop 의 다른 노브 (decide prompt template, decompose temperature override 등) 가 자연스럽게 sub-section 안에 추가
  • env var prefix 도 KEBAB_RAG_MULTI_HOP_MAX_DEPTH 그대로 일관 (현재 flat 와 같음)

단점:

  • struct 가 RagCfg.multi_hop: MultiHopCfg 형태로 한 단계 더
  • 옛 config (flat 가정) 와 호환 안 됨 — serde 가 multi_hop_max_depthmulti_hop.max_depth 로 자동 마이그레이션 안 함

flat 채택은 옛 config (현재 fb-41 spec 시점) 호환 + 단순성. 향후 multi-hop 노브 더 늘어나면 cluster 가능성 ↑. 사용자 선호 확인 권장 — flat 유지 vs PR-3b 에서 hierarchic 으로 migrate (V008 config schema bump).

PR-3a 의 cleanup 으로 flat 유지가 default — 의도된 staging 이면 OK, 단순 default 였다면 PR-3b 또는 별 PR 에서 결정.

**Suggestion — `RagCfg.multi_hop_*` field 의 flat vs hierarchic naming 결정**: 현재 채택: flat (`rag.multi_hop_max_depth`, `rag.multi_hop_max_sub_queries_per_iter`, `rag.multi_hop_max_pool_chunks`). 대안: hierarchic `[rag.multi_hop]` sub-section: ```toml [rag.multi_hop] max_depth = 3 max_sub_queries_per_iter = 5 max_pool_chunks = 30 ``` 장점: - 향후 multi-hop 의 다른 노브 (decide prompt template, decompose temperature override 등) 가 자연스럽게 sub-section 안에 추가 - env var prefix 도 `KEBAB_RAG_MULTI_HOP_MAX_DEPTH` 그대로 일관 (현재 flat 와 같음) 단점: - struct 가 `RagCfg.multi_hop: MultiHopCfg` 형태로 한 단계 더 - 옛 config (flat 가정) 와 호환 안 됨 — serde 가 `multi_hop_max_depth` 를 `multi_hop.max_depth` 로 자동 마이그레이션 안 함 flat 채택은 옛 config (현재 fb-41 spec 시점) 호환 + 단순성. 향후 multi-hop 노브 더 늘어나면 cluster 가능성 ↑. 사용자 선호 확인 권장 — flat 유지 vs PR-3b 에서 hierarchic 으로 migrate (V008 config schema bump). PR-3a 의 cleanup 으로 flat 유지가 default — 의도된 staging 이면 OK, 단순 default 였다면 PR-3b 또는 별 PR 에서 결정.
@@ -32,0 +33,4 @@
/// Each entry records one hop (`decompose` / `decide` / `synthesize`)
/// — the LLM call category, the sub-queries emitted, retrieval
/// counts, and a `forced_stop` flag for cap-driven termination.
/// Wire-additive: `answer.v1` schema_version unchanged; consumers

칭찬Answer.hops: Option<Vec<HopRecord>>#[serde(default, skip_serializing_if = "Option::is_none")] annotation 가 backward-compat invariant 정확히 표현. (1) None 일 때 emit 안 됨 → 옛 single-pass consumer wire 변동 0, (2) serde(default) 로 옛 wire 가 본 schema 로 deserialize 시 None 자동, (3) Option<Vec<HopRecord>> 가 "hops 정보가 없을 수도 있다" 와 "hops 가 있지만 비어 있다" 를 type-level 로 구분 (multi-hop 일 때만 Some, refusal 이라도 trace 가 1+ entry 보존 시 Some(vec![...]) 가능).

**칭찬** — `Answer.hops: Option<Vec<HopRecord>>` 의 `#[serde(default, skip_serializing_if = "Option::is_none")]` annotation 가 backward-compat invariant 정확히 표현. (1) None 일 때 emit 안 됨 → 옛 single-pass consumer wire 변동 0, (2) `serde(default)` 로 옛 wire 가 본 schema 로 deserialize 시 None 자동, (3) `Option<Vec<HopRecord>>` 가 "hops 정보가 없을 수도 있다" 와 "hops 가 있지만 비어 있다" 를 type-level 로 구분 (multi-hop 일 때만 Some, refusal 이라도 trace 가 1+ entry 보존 시 `Some(vec![...])` 가능).
@@ -58,0 +83,4 @@
/// hop this is empty (no sub-queries — just the final answer).
#[serde(default)]
pub sub_queries: Vec<String>,
/// Number of *new* chunks the retrieval round contributed to the

Suggestion — HopRecord.sub_queries doc 의 "this iter" ambiguity (PR-3b 시점에 보강):

/// Sub-queries the LLM emitted at this iter. For the synthesize
/// hop this is empty (no sub-queries — just the final answer).

"emit at this iter" 의 의미가 다음 둘 중:

  1. Decompose / Decide hop: LLM 이 이번 hop 에서 emit 한 sub-queries (다음 iter 의 retrieve target).
  2. Synthesize hop: empty.

PR-3a 단계에서는 실 emit 안 됨 — PR-3b 가 채움. 실 동작 확정 후 doc 더 정확하게:

/// - Decompose hop: the initial sub-query set the LLM returned.
/// - Decide hop: the new sub-queries the LLM signaled — empty if
///   it signaled stop, non-empty if continue.
/// - Synthesize hop: always empty (no retrieval, just final answer).

PR-3b commit 에서 같이 정정 권장. 본 PR 의 doc 은 generic 한 상태 유지 가능.

**Suggestion — `HopRecord.sub_queries` doc 의 "this iter" ambiguity** (PR-3b 시점에 보강): ``` /// Sub-queries the LLM emitted at this iter. For the synthesize /// hop this is empty (no sub-queries — just the final answer). ``` "emit at this iter" 의 의미가 다음 둘 중: 1. **Decompose / Decide hop**: LLM 이 이번 hop 에서 emit 한 sub-queries (다음 iter 의 retrieve target). 2. **Synthesize hop**: empty. PR-3a 단계에서는 실 emit 안 됨 — PR-3b 가 채움. 실 동작 확정 후 doc 더 정확하게: ``` /// - Decompose hop: the initial sub-query set the LLM returned. /// - Decide hop: the new sub-queries the LLM signaled — empty if /// it signaled stop, non-empty if continue. /// - Synthesize hop: always empty (no retrieval, just final answer). ``` PR-3b commit 에서 같이 정정 권장. 본 PR 의 doc 은 generic 한 상태 유지 가능.

Suggestion — MULTI_HOP_MAX_SUB_QUERIES_DEFAULT (const) vs RagCfg.multi_hop_max_sub_queries_per_iter (cfg) reconciliation:

PR-2 에서 const = 5 (kebab-rag) 도입, 본 PR-3a 에서 cfg field = 5 (kebab-config) 도입. 둘이 동일 의미 (decompose / decide 의 sub-query 개수 cap), 다만 const 는 compile-time, cfg 는 runtime.

현재 multi_hop_decompose 가 const 사용:

.replace("{max_sub_queries}", &MULTI_HOP_MAX_SUB_QUERIES_DEFAULT.to_string());

PR-3b 의 decide loop 도입 시 cfg field 가 runtime knob 로 사용 (user 가 env / config 로 override). const 와 default 가 일치해야 wire 의 LLM 입장에서 일관된 cap 신호. 두 옵션:

  1. const 제거: PR-3b 가 multi_hop_decompose + multi_hop_decide 둘 다 cfg field 만 사용. const 는 backward-compat 안 됨.
  2. const 를 default fn 으로 변경: default_multi_hop_max_sub_queries_per_iter() 만 유지, kebab-rag 가 그 default 또는 cfg 값 사용. 명확.

PR-3b 책임 — 본 PR 명시만.

**Suggestion — `MULTI_HOP_MAX_SUB_QUERIES_DEFAULT` (const) vs `RagCfg.multi_hop_max_sub_queries_per_iter` (cfg) reconciliation**: PR-2 에서 const = 5 (kebab-rag) 도입, 본 PR-3a 에서 cfg field = 5 (kebab-config) 도입. 둘이 동일 의미 (decompose / decide 의 sub-query 개수 cap), 다만 const 는 compile-time, cfg 는 runtime. 현재 `multi_hop_decompose` 가 const 사용: ```rust .replace("{max_sub_queries}", &MULTI_HOP_MAX_SUB_QUERIES_DEFAULT.to_string()); ``` PR-3b 의 decide loop 도입 시 cfg field 가 runtime knob 로 사용 (user 가 env / config 로 override). const 와 default 가 일치해야 wire 의 LLM 입장에서 일관된 cap 신호. 두 옵션: 1. **const 제거**: PR-3b 가 `multi_hop_decompose` + `multi_hop_decide` 둘 다 cfg field 만 사용. const 는 backward-compat 안 됨. 2. **const 를 default fn 으로 변경**: `default_multi_hop_max_sub_queries_per_iter()` 만 유지, kebab-rag 가 그 default 또는 cfg 값 사용. 명확. PR-3b 책임 — 본 PR 명시만.

Wire schema additionalProperties 확인 결과:

docs/wire-schema/v1/answer.schema.jsonadditionalProperties 명시 안 함 → JSON Schema default true (unknown field 자동 허용). PR-3b 의 multi-hop happy path 가 Some(hops_trace) 채워 answer.v1 JSON 에 hops key 등장해도 wire breaking 아님 (additive minor).

그래도 PR-3b 가 schema 의 properties.hops 명시 추가 + description 갱신을 같은 PR 에 포함하는 게 schema consumer 측 발견성 ↑. 본 PR-3a 는 schema 변경 없이 ship 가능 — skip_serializing_if = None 가 single-pass wire 무변 보장.

(Question 자체는 self-resolved — 본 inline 의 답변. PR-3b 의 schema sweep checklist 에 명시 권장.)

**Wire schema `additionalProperties` 확인 결과**: `docs/wire-schema/v1/answer.schema.json` 가 `additionalProperties` 명시 안 함 → JSON Schema default `true` (unknown field 자동 허용). PR-3b 의 multi-hop happy path 가 `Some(hops_trace)` 채워 `answer.v1` JSON 에 `hops` key 등장해도 **wire breaking 아님** (additive minor). 그래도 PR-3b 가 schema 의 `properties.hops` 명시 추가 + description 갱신을 같은 PR 에 포함하는 게 schema consumer 측 발견성 ↑. 본 PR-3a 는 schema 변경 없이 ship 가능 — `skip_serializing_if = None` 가 single-pass wire 무변 보장. (Question 자체는 self-resolved — 본 inline 의 답변. PR-3b 의 schema sweep checklist 에 명시 권장.)
claude-reviewer-01 approved these changes 2026-05-25 07:18:15 +00:00
claude-reviewer-01 left a comment
Member

회차 2 — 회차 1 의 actionable 0 (모두 PR-3b carry-over) 확인. 본 PR 의 코드 변경 불필요. APPROVE.

회차 1 의 suggestion 3 + question 1 모두 본 PR scope 외:

  • HopRecord.sub_queries doc 보강 → PR-3b 의 실 동작 확정 후
  • MULTI_HOP_MAX_SUB_QUERIES_DEFAULT const vs cfg reconcile → PR-3b
  • RagCfg flat vs hierarchic → 사용자 선호 결정 후 PR-3b 또는 별 PR
  • wire schema additionalProperties → default true 확인 (resolved)

회차 1 등록 자체가 over-careful 였음 — 본 PR 의 직접 변경 필요한 actionable 0 였다면 회차 1 가 APPROVE 합리적. 회차 2 즉시 APPROVE 로 cadence 정정. plan 의 PR-3b 단락에 carry-over 항목 이미 명시됨 (회차 1 commit 의 plan 정정 부분).

PR 분할 자체 (PR-3a / PR-3b) 가 sequencing 안전성 ↑ — wire / cfg additive foundation 가 동작 변경 없이 ship, PR-3b 가 그 위에서 dynamic loop. 머지 후 PR-3b 시작 (사용자 의향 재확인 권장 — 작업 시간 큰 PR).

회차 2 — 회차 1 의 actionable 0 (모두 PR-3b carry-over) 확인. 본 PR 의 코드 변경 불필요. APPROVE. 회차 1 의 suggestion 3 + question 1 모두 본 PR scope 외: - HopRecord.sub_queries doc 보강 → PR-3b 의 실 동작 확정 후 - MULTI_HOP_MAX_SUB_QUERIES_DEFAULT const vs cfg reconcile → PR-3b - RagCfg flat vs hierarchic → 사용자 선호 결정 후 PR-3b 또는 별 PR - wire schema additionalProperties → default true 확인 (resolved) 회차 1 등록 자체가 over-careful 였음 — 본 PR 의 직접 변경 필요한 actionable 0 였다면 회차 1 가 APPROVE 합리적. 회차 2 즉시 APPROVE 로 cadence 정정. plan 의 PR-3b 단락에 carry-over 항목 이미 명시됨 (회차 1 commit 의 plan 정정 부분). PR 분할 자체 (PR-3a / PR-3b) 가 sequencing 안전성 ↑ — wire / cfg additive foundation 가 동작 변경 없이 ship, PR-3b 가 그 위에서 dynamic loop. 머지 후 PR-3b 시작 (사용자 의향 재확인 권장 — 작업 시간 큰 PR).
altair823 merged commit cd1d4fb807 into main 2026-05-25 07:18:29 +00:00
altair823 deleted branch feat/fb-41-pr-3-dynamic-decide-loop 2026-05-25 07:18:30 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#168