@@ -93,23 +93,18 @@ func (t *QuickSearch) Execute(ctx context.Context, params map[string]any) (any,
9393 }
9494 }
9595
96- gq := query .NewGraphQuery (queryStr ).(* query.GraphQuery )
97- gq .SetTextQuery ("" ) // Force vector search + entity enrichment, skip LLM text→Cypher path
98- gq .SetLimit (limit )
99- gq .SetDepth (1 )
100-
10196 // Apply projectDir filter — default to current working directory
10297 projectDir , _ := params ["projectDir" ].(string )
10398 if projectDir == "" {
10499 if cwd , err := os .Getwd (); err == nil {
105100 projectDir = cwd
106101 }
107102 }
108- if projectDir != "" {
109- regionID := fmt .Sprintf ("%x" , sha256 .Sum256 ([]byte (filepath .Clean (projectDir ))))
110- gq .AddFilter ("region_id" , regionID )
111103
112- // Pre-check: skip search if no indexed data for this region
104+ // Pre-check + 预计算 regionID 供 per-token 使用
105+ var regionID string
106+ if projectDir != "" {
107+ regionID = fmt .Sprintf ("%x" , sha256 .Sum256 ([]byte (filepath .Clean (projectDir ))))
113108 total , countErr := t .indexer .CountByRegion (ctx , projectDir )
114109 if countErr == nil && total == 0 {
115110 return map [string ]any {
@@ -118,23 +113,56 @@ func (t *QuickSearch) Execute(ctx context.Context, params map[string]any) (any,
118113 }
119114 }
120115
121- hits , err := t . indexer . Search ( ctx , gq )
122- if err != nil {
123- return nil , fmt . Errorf ( "QuickSearch 失败:%w" , err )
124- }
116+ // 将查询按空白符拆分为多个关键词,分别检索后合并去重。
117+ // LLM 倾向于输入空格分隔的关键词而非自然语句(如 "redis 迁移 配置"),
118+ // 多次查询比单次语义搜索能召回更全面的结果。
119+ tokens := splitQueryTokens ( queryStr )
125120
126- // Fallback: retry without region_id filter when region-filtered search yields nothing
127- if len (hits ) == 0 {
128- gq2 := query .NewGraphQuery (queryStr ).(* query.GraphQuery )
129- gq2 .SetTextQuery ("" )
130- gq2 .SetLimit (limit )
131- gq2 .SetDepth (1 )
132- hits2 , err2 := t .indexer .Search (ctx , gq2 )
133- if err2 == nil && len (hits2 ) > 0 {
134- hits = hits2
121+ var allHits []core.Hit
122+ seen := make (map [string ]bool )
123+
124+ for _ , token := range tokens {
125+ gq := query .NewGraphQuery (token ).(* query.GraphQuery )
126+ gq .SetTextQuery ("" ) // Force vector search + entity enrichment, skip LLM text→Cypher path
127+ gq .SetLimit (limit )
128+ gq .SetDepth (1 )
129+
130+ if regionID != "" {
131+ gq .AddFilter ("region_id" , regionID )
132+ }
133+
134+ hits , err := t .indexer .Search (ctx , gq )
135+ if err != nil {
136+ // 单个 token 查询失败,跳过
137+ continue
138+ }
139+
140+ // Fallback: retry without region_id filter when region-filtered search yields nothing
141+ if len (hits ) == 0 {
142+ gq2 := query .NewGraphQuery (token ).(* query.GraphQuery )
143+ gq2 .SetTextQuery ("" )
144+ gq2 .SetLimit (limit )
145+ gq2 .SetDepth (1 )
146+ hits2 , err2 := t .indexer .Search (ctx , gq2 )
147+ if err2 == nil && len (hits2 ) > 0 {
148+ hits = hits2
149+ }
150+ }
151+
152+ for _ , h := range hits {
153+ if seen [h .ID ] {
154+ continue
155+ }
156+ seen [h .ID ] = true
157+ allHits = append (allHits , h )
135158 }
136159 }
137160
161+ hits := allHits
162+ if len (hits ) > limit {
163+ hits = hits [:limit ]
164+ }
165+
138166 // Filter by tags if specified (post-filter)
139167 if raw , ok := params ["tags" ].([]any ); ok && len (raw ) > 0 {
140168 var filterTags []string
@@ -167,6 +195,29 @@ func (t *QuickSearch) Execute(ctx context.Context, params map[string]any) (any,
167195 return formatQuickSearchResults (queryStr , hits ), nil
168196}
169197
198+ // splitQueryTokens 将查询拆分为多个关键词。
199+ // 如果查询本身是自然语句(含空格但长度 > 50),视为完整查询不拆分。
200+ // 否则按空白符拆分为多个关键词,过滤掉过短的词。
201+ func splitQueryTokens (query string ) []string {
202+ if len (query ) > 50 {
203+ return []string {query }
204+ }
205+ parts := strings .Fields (query )
206+ if len (parts ) <= 1 {
207+ return []string {query }
208+ }
209+ tokens := make ([]string , 0 , len (parts ))
210+ for _ , p := range parts {
211+ if len (p ) >= 2 {
212+ tokens = append (tokens , p )
213+ }
214+ }
215+ if len (tokens ) == 0 {
216+ return []string {query }
217+ }
218+ return tokens
219+ }
220+
170221// ── QuickSearch output formatting ─────────────────────────────────────────────────
171222
172223func formatQuickSearchResults (query string , hits []core.Hit ) string {
0 commit comments