马一丁

Prevent unbounded graph results from empty queries

@@ -17,6 +17,10 @@ class QueryParams: @@ -17,6 +17,10 @@ class QueryParams:
17 node_types: Optional[List[str]] = None # None 表示全部类型 17 node_types: Optional[List[str]] = None # None 表示全部类型
18 engine_filter: Optional[List[str]] = None # 限定引擎来源 18 engine_filter: Optional[List[str]] = None # 限定引擎来源
19 depth: int = 1 # 扩展深度 19 depth: int = 1 # 扩展深度
  20 + # 结果数量限制(防止空关键词时返回整个图谱)
  21 + max_sections: int = 15
  22 + max_queries: int = 20
  23 + max_sources: int = 10
20 24
21 25
22 @dataclass 26 @dataclass
@@ -125,7 +129,9 @@ class QueryEngine: @@ -125,7 +129,9 @@ class QueryEngine:
125 def _matches_keywords(self, node: Node, keywords: List[str]) -> bool: 129 def _matches_keywords(self, node: Node, keywords: List[str]) -> bool:
126 """检查节点是否匹配关键词""" 130 """检查节点是否匹配关键词"""
127 if not keywords: 131 if not keywords:
128 - return True # 无关键词时全部匹配 132 + # 无关键词时:只匹配 section 类型(避免返回整个图谱)
  133 + # 这样至少能获取到各引擎的段落摘要
  134 + return node.type == 'section'
129 135
130 # 构建搜索文本 136 # 构建搜索文本
131 search_text = f"{node.name} {node.get('title', '')} {node.get('query_text', '')} {node.get('summary', '')}" 137 search_text = f"{node.name} {node.get('title', '')} {node.get('query_text', '')} {node.get('summary', '')}"
@@ -192,11 +198,16 @@ class QueryEngine: @@ -192,11 +198,16 @@ class QueryEngine:
192 matched_queries.sort(key=lambda x: x.get('query_text', '')) 198 matched_queries.sort(key=lambda x: x.get('query_text', ''))
193 matched_sources.sort(key=lambda x: x.get('title', '')) 199 matched_sources.sort(key=lambda x: x.get('title', ''))
194 200
  201 + # 应用结果数量限制(防止过多节点被注入提示词,超出 token 限制)
  202 + limited_sections = matched_sections[:params.max_sections]
  203 + limited_queries = matched_queries[:params.max_queries]
  204 + limited_sources = matched_sources[:params.max_sources]
  205 +
195 return QueryResult( 206 return QueryResult(
196 - matched_sections=matched_sections,  
197 - matched_queries=matched_queries,  
198 - matched_sources=matched_sources,  
199 - total_nodes=len(node_ids), 207 + matched_sections=limited_sections,
  208 + matched_queries=limited_queries,
  209 + matched_sources=limited_sources,
  210 + total_nodes=len(node_ids), # 保留原始总数用于统计
200 query_params={ 211 query_params={
201 'keywords': params.keywords, 212 'keywords': params.keywords,
202 'node_types': params.node_types, 213 'node_types': params.node_types,