@@ -188,6 +188,7 @@ type GeminiBlock struct {
188188type RetrievalConfig struct {
189189 Strategy string `yaml:"strategy"`
190190 ChunkedTree ChunkedTreeBlock `yaml:"chunked_tree"`
191+ Agentic AgenticBlock `yaml:"agentic"`
191192 Cache CacheBlock `yaml:"cache"`
192193}
193194
@@ -212,6 +213,23 @@ type ChunkedTreeBlock struct {
212213 IncludeSiblingBreadcrumb bool `yaml:"include_sibling_breadcrumbs"`
213214}
214215
216+ // AgenticBlock configures the agentic-navigation strategy.
217+ //
218+ // The agentic loop trades sequential latency for the ability to handle
219+ // arbitrarily large trees: the model issues outline/expand/read actions
220+ // until it picks a final set of section IDs or hits MaxHops.
221+ type AgenticBlock struct {
222+ // MaxHops caps the number of LLM turns one query consumes, counting
223+ // the terminal "done" turn. Default: 6.
224+ MaxHops int `yaml:"max_hops"`
225+
226+ // Model optionally overrides the budget's model for navigation
227+ // turns. Empty means use the budget's model. Useful when the
228+ // retrieval engine wants the navigation loop on a fast/cheap
229+ // model while answering is on a stronger one.
230+ Model string `yaml:"model"`
231+ }
232+
215233// LogConfig configures logging.
216234type LogConfig struct {
217235 Level string `yaml:"level"`
@@ -244,6 +262,9 @@ func Default() Config {
244262 MaxParallelCalls : 8 ,
245263 IncludeSiblingBreadcrumb : true ,
246264 },
265+ Agentic : AgenticBlock {
266+ MaxHops : 6 ,
267+ },
247268 Cache : CacheBlock {
248269 Enabled : true ,
249270 MaxEntries : 1024 ,
@@ -352,6 +373,14 @@ func applyEnvOverrides(c *Config) {
352373 if v := os .Getenv ("VLE_TLS_KEY_FILE" ); v != "" {
353374 c .Server .TLS .KeyFile = v
354375 }
376+ if v := os .Getenv ("VLE_RETRIEVAL_AGENTIC_MAX_HOPS" ); v != "" {
377+ if n , err := strconv .Atoi (v ); err == nil && n >= 0 {
378+ c .Retrieval .Agentic .MaxHops = n
379+ }
380+ }
381+ if v := os .Getenv ("VLE_RETRIEVAL_AGENTIC_MODEL" ); v != "" {
382+ c .Retrieval .Agentic .Model = v
383+ }
355384 // Ingest / HyDE knobs. Booleans accept the usual truthy strings —
356385 // kept narrow so a typo doesn't silently flip the flag.
357386 if v := os .Getenv ("VLE_INGEST_HYDE_ENABLED" ); v != "" {
@@ -428,11 +457,15 @@ func (c Config) Validate() error {
428457 }
429458
430459 switch c .Retrieval .Strategy {
431- case "single-pass" , "chunked-tree" :
460+ case "single-pass" , "chunked-tree" , "agentic" :
432461 default :
433462 return fmt .Errorf ("unknown retrieval.strategy: %q" , c .Retrieval .Strategy )
434463 }
435464
465+ if c .Retrieval .Agentic .MaxHops < 0 {
466+ return fmt .Errorf ("retrieval.agentic.max_hops must be >= 0, got %d" , c .Retrieval .Agentic .MaxHops )
467+ }
468+
436469 if c .Server .TLS .CertFile != "" && c .Server .TLS .KeyFile == "" {
437470 return errors .New ("server.tls.key_file is required when cert_file is set" )
438471 }
0 commit comments