|
| 1 | +# Implementation Plan: Hybrid KG-First RLM-Assisted Search |
| 2 | + |
| 3 | +**Status**: Draft |
| 4 | +**Research Doc**: `.docs/research-hybrid-kg-rlm-search.md` |
| 5 | +**Author**: OpenCode |
| 6 | +**Date**: 2026-05-18 |
| 7 | +**Estimated Effort**: 4-6 days |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +### Summary |
| 12 | + |
| 13 | +Add a hybrid search workflow that reuses Terraphim's existing KG, haystack and relevance infrastructure, then invokes RLM only when deterministic search confidence is low or the caller requests synthesis. RLM may propose new KG concepts, but concept promotion is judge-gated or reviewable. |
| 14 | + |
| 15 | +### Approach |
| 16 | + |
| 17 | +Create a thin orchestrator over existing components rather than introducing a separate `rlmgrep` clone. The orchestrator will call existing search, calculate confidence, optionally call RLM with bounded evidence, validate output with a judge, and return a stable structured result. |
| 18 | + |
| 19 | +### Scope |
| 20 | + |
| 21 | +**In Scope:** |
| 22 | + |
| 23 | +- Shared hybrid search request/result types. |
| 24 | +- Service-level orchestrator that reuses `TerraphimService::search` and current haystack/ranking behaviour. |
| 25 | +- Confidence scoring over existing document evidence. |
| 26 | +- Optional RLM fallback using top-K retrieved evidence. |
| 27 | +- Judge-gated KG concept proposal output. |
| 28 | +- CLI/MCP JSON output suitable for agents. |
| 29 | +- Tests covering deterministic path, fallback trigger, no-LLM degraded path and concept proposal gating. |
| 30 | + |
| 31 | +**Out of Scope:** |
| 32 | + |
| 33 | +- Direct dependency on Python `rlmgrep` or DSPy. |
| 34 | +- New vector database or embedding store. |
| 35 | +- Replacing existing relevance functions. |
| 36 | +- Automatic unconditional KG writes. |
| 37 | +- Full non-text conversion parity with `rlmgrep`. |
| 38 | + |
| 39 | +**Avoid At All Cost:** |
| 40 | + |
| 41 | +- Duplicating haystack dispatch logic outside `terraphim_middleware`. |
| 42 | +- Duplicating relevance/ranking logic outside `terraphim_service` and existing score modules. |
| 43 | +- Letting RLM become the default retriever. |
| 44 | +- Allowing ungrounded RLM concepts into the KG. |
| 45 | + |
| 46 | +## Architecture |
| 47 | + |
| 48 | +### Component Diagram |
| 49 | + |
| 50 | +```text |
| 51 | +HybridSearchRequest |
| 52 | + -> HybridSearchOrchestrator |
| 53 | + -> TerraphimService::search / search_haystacks |
| 54 | + -> ConfidenceScorer |
| 55 | + -> if confident: HybridSearchResult |
| 56 | + -> if not confident: RlmFallback |
| 57 | + -> top-K evidence only |
| 58 | + -> Judge |
| 59 | + -> ConceptProposalBuilder |
| 60 | + -> HybridSearchResult |
| 61 | +``` |
| 62 | + |
| 63 | +### Data Flow |
| 64 | + |
| 65 | +```text |
| 66 | +query + role + options |
| 67 | + -> existing role KG and haystack retrieval |
| 68 | + -> existing ranking and KG preprocessing |
| 69 | + -> confidence assessment |
| 70 | + -> optional RLM synthesis and concept mining |
| 71 | + -> judge validation |
| 72 | + -> structured result with evidence and proposed concepts |
| 73 | +``` |
| 74 | + |
| 75 | +### Key Design Decisions |
| 76 | + |
| 77 | +| Decision | Rationale | Alternatives Rejected | |
| 78 | +|----------|-----------|----------------------| |
| 79 | +| Orchestrator over existing search | Minimises duplication and preserves current behaviour. | Direct `rlmgrep` port; separate search stack. | |
| 80 | +| RLM fallback only | Controls cost and keeps KG/haystack search as source of truth. | RLM-first retrieval. | |
| 81 | +| Top-K evidence to RLM | Bounds cost and improves grounding. | Whole repository/context loading. | |
| 82 | +| Proposed KG concepts first | Prevents KG pollution. | Direct automatic writes. | |
| 83 | + |
| 84 | +### Eliminated Options |
| 85 | + |
| 86 | +| Option Rejected | Why Rejected | Risk of Including | |
| 87 | +|-----------------|--------------|-------------------| |
| 88 | +| Python/DSPy dependency | Adds runtime and duplicates existing Rust services. | Operational complexity and divergent output semantics. | |
| 89 | +| New `ServiceType::Rlmgrep` | Makes RLM a haystack rather than an orchestrated fallback. | Expensive and confusing layering. | |
| 90 | +| Embeddings/vector DB | Not required for first increment. | Broad dependency and migration burden. | |
| 91 | +| Rewriting `TerraphimService::search` | Existing method is already large. | Regression risk. | |
| 92 | + |
| 93 | +### Simplicity Check |
| 94 | + |
| 95 | +The simplest design is an additive orchestrator module that calls current search, calculates confidence and conditionally calls RLM. It does not replace haystacks, relevance functions, rolegraph, persistence or CLI configuration. |
| 96 | + |
| 97 | +**Nothing Speculative Checklist:** |
| 98 | + |
| 99 | +- [x] No features the user did not request. |
| 100 | +- [x] No new abstraction unless it prevents duplication. |
| 101 | +- [x] No new datastore. |
| 102 | +- [x] No unconditional KG mutation. |
| 103 | +- [x] No direct dependency on `rlmgrep`. |
| 104 | + |
| 105 | +## File Changes |
| 106 | + |
| 107 | +### New Files |
| 108 | + |
| 109 | +| File | Purpose | |
| 110 | +|------|---------| |
| 111 | +| `crates/terraphim_types/src/hybrid_search.rs` | Shared request/result, evidence, confidence and concept proposal types. | |
| 112 | +| `crates/terraphim_service/src/hybrid_search.rs` | Orchestrator, confidence scoring and fallback decision logic. | |
| 113 | +| `crates/terraphim_service/tests/hybrid_search_test.rs` | Integration tests over existing service configuration and real local haystacks. | |
| 114 | + |
| 115 | +### Modified Files |
| 116 | + |
| 117 | +| File | Changes | |
| 118 | +|------|---------| |
| 119 | +| `crates/terraphim_types/src/lib.rs` | Re-export hybrid search types. | |
| 120 | +| `crates/terraphim_service/src/lib.rs` | Add `pub mod hybrid_search` and expose service entry point without expanding existing `search` internals. | |
| 121 | +| `crates/terraphim_rlm/src/llm_bridge.rs` | Replace or wrap stubbed query implementation with existing LLM routing, if this issue includes fallback execution. | |
| 122 | +| `crates/terraphim_mcp_server/src/lib.rs` | Add MCP tool or extend search tool with hybrid mode. | |
| 123 | +| `crates/terraphim_agent/src/main.rs` | Add CLI command or flag for hybrid search JSON output. | |
| 124 | + |
| 125 | +### Deleted Files |
| 126 | + |
| 127 | +None. |
| 128 | + |
| 129 | +## API Design |
| 130 | + |
| 131 | +### Public Types |
| 132 | + |
| 133 | +```rust |
| 134 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 135 | +pub struct HybridSearchRequest { |
| 136 | + pub query: SearchQuery, |
| 137 | + pub mode: HybridSearchMode, |
| 138 | + pub max_evidence: usize, |
| 139 | + pub min_confidence: f32, |
| 140 | + pub allow_rlm_fallback: bool, |
| 141 | + pub allow_concept_proposals: bool, |
| 142 | +} |
| 143 | + |
| 144 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 145 | +pub enum HybridSearchMode { |
| 146 | + SearchOnly, |
| 147 | + Answer, |
| 148 | + AnswerWithConceptMining, |
| 149 | +} |
| 150 | + |
| 151 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 152 | +pub struct HybridSearchResult { |
| 153 | + pub answer: Option<String>, |
| 154 | + pub evidence: Vec<SearchEvidence>, |
| 155 | + pub confidence: SearchConfidence, |
| 156 | + pub fallback_used: Option<RlmFallbackReason>, |
| 157 | + pub concept_proposals: Vec<KgConceptProposal>, |
| 158 | +} |
| 159 | + |
| 160 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 161 | +pub struct SearchEvidence { |
| 162 | + pub document_id: String, |
| 163 | + pub title: String, |
| 164 | + pub url: String, |
| 165 | + pub source_haystack: Option<String>, |
| 166 | + pub snippet: Option<String>, |
| 167 | + pub line: Option<u64>, |
| 168 | + pub rank: Option<u64>, |
| 169 | + pub score: Option<f32>, |
| 170 | + pub concepts_matched: Vec<String>, |
| 171 | +} |
| 172 | + |
| 173 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 174 | +pub struct SearchConfidence { |
| 175 | + pub score: f32, |
| 176 | + pub enough_results: bool, |
| 177 | + pub enough_kg_coverage: bool, |
| 178 | + pub top_result_separation: Option<f32>, |
| 179 | + pub requires_fallback: bool, |
| 180 | + pub reasons: Vec<String>, |
| 181 | +} |
| 182 | + |
| 183 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 184 | +pub struct KgConceptProposal { |
| 185 | + pub term: String, |
| 186 | + pub synonyms: Vec<String>, |
| 187 | + pub evidence_urls: Vec<String>, |
| 188 | + pub confidence: f32, |
| 189 | + pub status: ConceptProposalStatus, |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +### Public Functions |
| 194 | + |
| 195 | +```rust |
| 196 | +impl TerraphimService { |
| 197 | + pub async fn hybrid_search( |
| 198 | + &mut self, |
| 199 | + request: &HybridSearchRequest, |
| 200 | + ) -> Result<HybridSearchResult>; |
| 201 | +} |
| 202 | +``` |
| 203 | + |
| 204 | +### Error Types |
| 205 | + |
| 206 | +Prefer existing `ServiceError`. Add variants only if needed: |
| 207 | + |
| 208 | +```rust |
| 209 | +#[error("hybrid search fallback failed: {0}")] |
| 210 | +HybridFallback(String), |
| 211 | +``` |
| 212 | + |
| 213 | +## Test Strategy |
| 214 | + |
| 215 | +### Unit Tests |
| 216 | + |
| 217 | +| Test | Location | Purpose | |
| 218 | +|------|----------|---------| |
| 219 | +| `confidence_high_for_ranked_documents` | `hybrid_search.rs` | Does not invoke RLM when evidence is strong. | |
| 220 | +| `confidence_low_for_empty_documents` | `hybrid_search.rs` | Triggers fallback decision. | |
| 221 | +| `concept_proposals_default_to_proposed` | `hybrid_search.rs` | Prevents direct accepted KG writes. | |
| 222 | +| `evidence_preserves_source_haystack` | `hybrid_search.rs` | Keeps grounding metadata. | |
| 223 | + |
| 224 | +### Integration Tests |
| 225 | + |
| 226 | +| Test | Location | Purpose | |
| 227 | +|------|----------|---------| |
| 228 | +| `hybrid_search_reuses_existing_haystack_results` | `crates/terraphim_service/tests/hybrid_search_test.rs` | Confirms retrieval comes from configured haystacks. | |
| 229 | +| `hybrid_search_degrades_without_rlm` | `crates/terraphim_service/tests/hybrid_search_test.rs` | Returns deterministic evidence with fallback disabled/unavailable. | |
| 230 | +| `hybrid_search_concept_mining_is_gated` | `crates/terraphim_service/tests/hybrid_search_test.rs` | Ensures concepts are proposed, not written unconditionally. | |
| 231 | + |
| 232 | +### No Mocks |
| 233 | + |
| 234 | +Tests should use temporary local haystacks and real service configuration. Do not mock haystack indexers or RLM; use deterministic fallback-disabled tests first, then add feature-gated integration once the real LLM bridge is wired. |
| 235 | + |
| 236 | +## Implementation Steps |
| 237 | + |
| 238 | +### Step 1: Shared Types |
| 239 | + |
| 240 | +**Files:** `crates/terraphim_types/src/hybrid_search.rs`, `crates/terraphim_types/src/lib.rs` |
| 241 | +**Description:** Add serialisable request/result/evidence/confidence/concept proposal types. |
| 242 | +**Tests:** Type construction and serialisation tests. |
| 243 | +**Estimated:** 0.5 day |
| 244 | + |
| 245 | +### Step 2: Confidence Scorer |
| 246 | + |
| 247 | +**Files:** `crates/terraphim_service/src/hybrid_search.rs` |
| 248 | +**Description:** Add pure functions that score existing `Document` evidence without invoking RLM. |
| 249 | +**Tests:** Unit tests for high-confidence, low-confidence and edge cases. |
| 250 | +**Dependencies:** Step 1 |
| 251 | +**Estimated:** 0.5 day |
| 252 | + |
| 253 | +### Step 3: Orchestrator Over Existing Search |
| 254 | + |
| 255 | +**Files:** `crates/terraphim_service/src/hybrid_search.rs`, `crates/terraphim_service/src/lib.rs` |
| 256 | +**Description:** Implement `TerraphimService::hybrid_search` by calling existing `search` and converting documents to evidence. |
| 257 | +**Tests:** Integration tests with a real temporary haystack. |
| 258 | +**Dependencies:** Step 2 |
| 259 | +**Estimated:** 1 day |
| 260 | + |
| 261 | +### Step 4: RLM Fallback Adapter |
| 262 | + |
| 263 | +**Files:** `crates/terraphim_service/src/hybrid_search.rs`, `crates/terraphim_rlm/src/llm_bridge.rs` |
| 264 | +**Description:** Add bounded top-K evidence prompt path and wire real LLM routing if required for fallback. |
| 265 | +**Tests:** Feature-gated integration or fallback-disabled deterministic test until provider credentials are available. |
| 266 | +**Dependencies:** Step 3 |
| 267 | +**Estimated:** 1-2 days |
| 268 | + |
| 269 | +### Step 5: Judge and Concept Proposals |
| 270 | + |
| 271 | +**Files:** `crates/terraphim_service/src/hybrid_search.rs` |
| 272 | +**Description:** Parse RLM concept mining output into `KgConceptProposal`, mark as `Proposed`, and include evidence references. |
| 273 | +**Tests:** Validate ungrounded proposals are rejected or marked low confidence. |
| 274 | +**Dependencies:** Step 4 |
| 275 | +**Estimated:** 1 day |
| 276 | + |
| 277 | +### Step 6: CLI/MCP Exposure |
| 278 | + |
| 279 | +**Files:** `crates/terraphim_agent/src/main.rs`, `crates/terraphim_mcp_server/src/lib.rs` |
| 280 | +**Description:** Add agent-facing command/tool returning stable JSON. |
| 281 | +**Tests:** CLI/MCP integration tests if existing harness supports them. |
| 282 | +**Dependencies:** Step 5 |
| 283 | +**Estimated:** 1 day |
| 284 | + |
| 285 | +### Step 7: Documentation and Telemetry |
| 286 | + |
| 287 | +**Files:** `.docs/summary.md`, relevant command docs |
| 288 | +**Description:** Document KG-first behaviour, fallback semantics and concept proposal safety. |
| 289 | +**Tests:** Docs build/check if applicable. |
| 290 | +**Dependencies:** Step 6 |
| 291 | +**Estimated:** 0.5 day |
| 292 | + |
| 293 | +## Rollback Plan |
| 294 | + |
| 295 | +If issues are discovered: |
| 296 | + |
| 297 | +1. Disable CLI/MCP hybrid entry point. |
| 298 | +2. Keep existing `TerraphimService::search` unchanged and continue using current search endpoints. |
| 299 | +3. Ignore proposed concept outputs; no accepted KG mutations should need rollback. |
| 300 | + |
| 301 | +Feature flag candidate: `hybrid-rlm-search`. |
| 302 | + |
| 303 | +## Dependencies |
| 304 | + |
| 305 | +### New Dependencies |
| 306 | + |
| 307 | +None expected. |
| 308 | + |
| 309 | +### Dependency Updates |
| 310 | + |
| 311 | +None expected. |
| 312 | + |
| 313 | +## Performance Considerations |
| 314 | + |
| 315 | +### Expected Performance |
| 316 | + |
| 317 | +| Metric | Target | Measurement | |
| 318 | +|--------|--------|-------------| |
| 319 | +| Hot-path LLM calls | 0 | Telemetry field `fallback_used == None`. | |
| 320 | +| Evidence cap | Default top 10 | Request/result metadata. | |
| 321 | +| RLM fallback cost | Explicit and counted | Existing RLM budget/cost tracking. | |
| 322 | +| Search latency | Close to existing search latency when fallback unused | Integration benchmark or timed test. | |
| 323 | + |
| 324 | +## Open Items |
| 325 | + |
| 326 | +| Item | Status | Owner | |
| 327 | +|------|--------|-------| |
| 328 | +| Decide exact concept proposal persistence location | Pending | Maintainers | |
| 329 | +| Decide whether to expose as new MCP tool or mode on existing search | Pending | Maintainers | |
| 330 | +| Confirm real LLM bridge routing path | Pending | Implementer | |
| 331 | + |
| 332 | +## Approval |
| 333 | + |
| 334 | +- [ ] Technical review complete |
| 335 | +- [ ] Test strategy approved |
| 336 | +- [ ] Performance targets agreed |
| 337 | +- [ ] Human approval received |
0 commit comments