彻底解决搜索歧义:Khoj上下文感知查询处理技术详解
你是否遇到过这样的困境?当你在知识库中搜索"如何优化Khoj性能"时,系统返回的结果却充斥着与"性能"无关的内容?或者当你追问"那内存占用呢"时,AI完全忘记了你上一个问题的上下文?这些问题的根源在于传统搜索系统缺乏对查询上下文的理解和处理能力。
Khoj作为一款为第二大脑设计的AI助手(AI copilot for your second brain),其最新的在线搜索功能优化通过上下文感知的查询处理技术,彻底改变了这一现状。本文将深入剖析这一技术背后的实现原理,展示它如何让搜索变得更智能、更精准。
上下文感知查询处理的核心挑战
在探讨解决方案之前,我们首先需要理解传统搜索系统面临的核心挑战:
- 查询歧义性:同样的词语在不同上下文中可能有完全不同的含义。例如,"苹果"既可以指水果,也可以指科技公司。
- 上下文依赖:许多查询只有结合历史对话才能准确理解。例如,当用户先问"如何配置Firecrawl",然后问"那Serper呢",系统需要理解"那"指代的是配置过程。
- 信息过载:互联网上的信息爆炸式增长,如何从海量数据中快速找到与当前查询真正相关的内容,成为一大难题。
Khoj的上下文感知查询处理技术正是为了解决这些挑战而设计的。它不仅考虑当前查询,还会分析整个对话历史,甚至用户的地理位置等信息,从而提供更精准的搜索结果。
在线搜索功能界面
技术架构:四大核心模块
Khoj的上下文感知查询处理技术由四个核心模块组成,它们协同工作,实现了从原始查询到精准搜索结果的转变。
1. 查询理解与子查询生成
第一个关键模块是查询理解与子查询生成。当用户输入一个复杂查询时,系统需要先理解其意图,然后将其分解为多个更具体的子查询。这一过程在src/khoj/routers/helpers.py中实现,主要通过generate_online_subqueries函数完成。
该函数的核心思想是利用大型语言模型(LLM)的理解能力,结合对话历史和用户信息,将原始查询分解为多个独立的子查询。例如,对于"2025年人工智能领域的最新进展及其对医疗行业的影响"这样的复杂查询,系统可能会生成以下子查询:
- 2025年人工智能领域最新进展
- 人工智能在医疗诊断中的应用2025
- AI对医疗行业的影响分析
这种分解不仅降低了每个子查询的复杂度,还能确保搜索结果的全面性。代码中的关键实现如下:
async def generate_online_subqueries(
q: str,
chat_history: List[ChatMessageModel],
location_data: LocationData,
user: KhojUser,
query_images: List[str] = None,
query_files: str = None,
max_queries: int = 3,
agent: Agent = None,
tracer: dict = {},
) -> Set[str]:
# 构建提示词,包含查询、对话历史、地理位置等信息
location = f"{location_data}" if location_data else "Unknown"
username = prompts.user_name.format(name=user.get_full_name()) if user.get_full_name() else ""
chat_history_str = construct_chat_history(chat_history)
utc_date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
personality_context = prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
online_queries_prompt = prompts.online_search_conversation_subqueries.format(
query=q,
chat_history=chat_history_str,
max_queries=max_queries,
current_date=utc_date,
location=location,
username=username,
personality_context=personality_context,
)
# 调用LLM生成子查询
agent_chat_model = AgentAdapters.get_agent_chat_model(agent, user) if agent else None
raw_response = await send_message_to_model_wrapper(
online_queries_prompt,
query_files=query_files,
query_images=query_images,
response_type="json_object",
response_schema=OnlineQueries,
fast_model=False,
agent_chat_model=agent_chat_model,
user=user,
tracer=tracer,
)
# 解析并返回子查询
response = clean_json(raw_response.text)
response = pyjson5.loads(response)
return {q.strip() for q in response["queries"] if q.strip()}
这一过程中,系统不仅考虑了当前查询,还融入了对话历史、用户地理位置、当前日期等多维度信息,从而生成更具针对性的子查询。
2. 多引擎搜索集成
生成子查询后,系统需要利用这些子查询进行实际的网络搜索。Khoj的独特之处在于它集成了多种搜索引擎,包括Serper、Google、Firecrawl、Jina和Searxng等,用户可以根据自己的需求和API访问权限进行选择。这一模块在src/khoj/processor/tools/online_search.py中实现。
系统会根据可用的API密钥自动选择合适的搜索引擎。例如,如果用户提供了Serper API密钥,系统会优先使用Serper进行搜索;如果没有,会依次尝试其他可用的搜索引擎。这种设计不仅提高了系统的灵活性,还增强了其健壮性——即使某个搜索引擎暂时不可用,系统仍能通过其他引擎获取结果。
async def search_online(
query: str,
conversation_history: List[ChatMessageModel],
location: LocationData,
user: KhojUser,
send_status_func: Optional[Callable] = None,
custom_filters: List[str] = [],
max_online_searches: int = 3,
max_webpages_to_read: int = 1,
query_images: List[str] = None,
query_files: str = None,
previous_subqueries: Set = set(),
agent: Agent = None,
tracer: dict = {},
):
# ... 省略前面的代码 ...
search_engines = []
if SERPER_DEV_API_KEY:
search_engine = "Serper"
search_engines.append((search_engine, search_with_serper))
if GOOGLE_SEARCH_API_KEY and GOOGLE_SEARCH_ENGINE_ID:
search_engine = "Google"
search_engines.append((search_engine, search_with_google))
if FIRECRAWL_API_KEY:
search_engine = "Firecrawl"
search_engines.append((search_engine, search_with_firecrawl))
if JINA_API_KEY:
search_engine = "Jina"
search_engines.append((search_engine, search_with_jina))
if SEARXNG_URL:
search_engine = "Searxng"
search_engines.append((search_engine, search_with_searxng))
# ... 省略中间的代码 ...
response_dict = {}
for search_engine, search_func in search_engines:
logger.info(f"🌐 Searching the Internet with {search_engine} for {subqueries}")
with timer(f"Internet searches with {search_engine} for {subqueries} took", logger):
try:
search_tasks = [search_func(subquery, location) for subquery in subqueries]
search_results = await asyncio.gather(*search_tasks)
response_dict = {subquery: search_result for subquery, search_result in search_results if search_result}
if not is_none_or_empty(response_dict):
break
except Exception as e:
logger.error(f"Error searching with {search_engine}: {e}")
response_dict = {}
每个搜索引擎都有其独特的优势。例如,Serper提供了更精准的搜索结果和丰富的数据格式,而Searxng则注重隐私保护。通过集成多种引擎,Khoj能够在不同维度上优化搜索结果。
3. 网页内容提取与相关性分析
获取搜索结果后,下一步是从网页中提取相关信息。这一过程面临两大挑战:如何高效地从各种网页中提取结构化信息,以及如何判断这些信息与原始查询的相关性。Khoj通过多种网页抓取工具和LLM辅助的信息提取解决了这些问题。
在src/khoj/processor/tools/online_search.py中,read_webpage_and_extract_content函数实现了这一功能。系统会尝试多种网页抓取工具(如Firecrawl、Jina等),直到成功提取内容。然后,利用LLM对提取的内容进行分析,找出与查询最相关的部分。
async def read_webpage_and_extract_content(
subqueries: set[str],
url: str,
content: str = None,
user: KhojUser = None,
agent: Agent = None,
tracer: dict = {},
) -> Tuple[set[str], str, Union[None, str]]:
# 选择合适的网页抓取工具
web_scrapers = await ConversationAdapters.aget_enabled_webscrapers()
if is_internal_url(url):
web_scrapers = [scraper for scraper in web_scrapers if scraper.type == WebScraper.WebScraperType.DIRECT]
# 尝试不同的抓取工具,直到成功获取内容
extracted_info = None
for scraper in web_scrapers:
try:
if is_none_or_empty(content):
content, extracted_info = await read_webpage(
url, scraper.type, scraper.api_key, scraper.api_url, subqueries, agent
)
# 如果需要,使用LLM提取相关信息
if is_none_or_empty(extracted_info):
extracted_info = await extract_relevant_info(
subqueries, content, user=user, agent=agent, tracer=tracer
)
if not is_none_or_empty(extracted_info):
break
except Exception as e:
logger.warning(f"Failed to read web page with {scraper.type} at '{url}' with {e}")
return subqueries, url, extracted_info
其中,extract_relevant_info函数利用LLM的理解能力,从网页内容中提取与查询最相关的信息:
async def extract_relevant_info(
qs: set[str], corpus: str, user: KhojUser = None, agent: Agent = None, tracer: dict = {}
) -> Union[str, None]:
if is_none_or_empty(corpus) or is_none_or_empty(qs):
return None
personality_context = prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
extract_relevant_information = prompts.extract_relevant_information.format(
query=", ".join(qs),
corpus=corpus.strip(),
personality_context=personality_context,
)
response = await send_message_to_model_wrapper(
extract_relevant_information,
system_message=prompts.system_prompt_extract_relevant_information,
fast_model=True,
agent_chat_model=AgentAdapters.get_agent_chat_model(agent, user) if agent else None,
user=user,
tracer=tracer,
)
return response.text.strip()
这种方法不仅提高了信息提取的准确性,还大大减少了后续处理的数据量,为最终的答案生成奠定了基础。
4. 结果整合与答案生成
最后一个模块是结果整合与答案生成。在获取了各个子查询的搜索结果后,系统需要将这些分散的信息整合成一个连贯、准确的回答。这一过程同样利用了LLM的强大能力,在src/khoj/routers/helpers.py中实现。
系统会将所有相关的搜索结果汇集起来,形成一个综合的知识库,然后让LLM基于这个知识库生成最终答案。这种方法确保了答案的全面性和准确性,同时保持了回答的连贯性和可读性。
性能优化:搜索效率与结果质量的平衡
在实现上下文感知查询处理的同时,Khoj团队还面临着如何平衡搜索效率和结果质量的挑战。为了解决这一问题,他们采用了多种优化策略:
1. 搜索结果去重
由于不同的子查询可能返回相同或高度相似的网页,系统需要进行去重处理,以避免冗余信息。这一功能在src/khoj/processor/tools/online_search.py中的deduplicate_organic_results函数实现:
def deduplicate_organic_results(online_results: dict) -> dict:
seen_links = set()
deduplicated_results = {}
for query, results in online_results.items():
filtered_organic = []
for result in results.get("organic") or []:
link = result.get("link")
if link and link not in seen_links:
seen_links.add(link)
filtered_organic.append(result)
deduplicated_results[query] = {**results, "organic": filtered_organic}
return deduplicated_results
这种去重策略不仅减少了后续处理的数据量,还避免了用户看到重复的信息,提高了整体用户体验。
2. 并行搜索与异步处理
为了提高搜索效率,Khoj采用了并行搜索和异步处理的策略。在src/khoj/processor/tools/online_search.py中,search_online函数使用asyncio.gather同时发送多个搜索请求:
search_tasks = [search_func(subquery, location) for subquery in subqueries]
search_results = await asyncio.gather(*search_tasks)
这种并行处理大大减少了整体搜索时间,使得即使处理多个子查询,用户也能在合理的时间内获得结果。
3. 智能选择搜索深度
为了在搜索深度和响应速度之间取得平衡,Khoj引入了max_webpages_to_read参数,控制每个子查询最多读取的网页数量。这一参数可以根据用户的需求和系统资源进行动态调整,确保在提供足够信息的同时,保持较快的响应速度。
实际应用场景与案例分析
为了更好地理解Khoj上下文感知查询处理技术的实际效果,我们来看几个具体的应用案例:
案例一:学术研究辅助
假设一位研究人员正在撰写一篇关于"量子计算在密码学中的应用"的论文。她需要了解最新的研究进展、主要技术挑战以及未来的发展趋势。使用Khoj,她可以输入一个复杂查询:"2024-2025年量子计算在密码学领域的最新研究进展,重点关注后量子密码算法和量子密钥分发技术"。
Khoj的上下文感知查询处理系统会将这个复杂查询分解为多个子查询:
- 2024-2025量子计算密码学研究进展
- 后量子密码算法最新研究
- 量子密钥分发技术发展现状
然后,系统会并行搜索这些子查询,从学术数据库、技术博客和权威机构网站获取最新信息。在提取相关信息后,Khoj会将这些分散的信息整合成一个连贯的回答,帮助研究人员快速掌握该领域的最新动态。
案例二:软件开发问题排查
一位软件开发人员遇到了一个关于Firecrawl API集成的问题。他先问:"如何在Python中使用Firecrawl API?",在得到初步解答后,他接着问:"如何处理API返回的JSON数据?"。
传统的搜索系统可能无法理解第二个问题与第一个问题的关联,返回一般性的JSON处理教程。而Khoj的上下文感知系统会识别出这两个问题的关联性,理解用户现在关注的是Firecrawl API返回的特定JSON数据的处理方法。系统会生成针对性的子查询,搜索Firecrawl API的响应格式和相关处理示例,提供更精准的帮助。
这个案例展示了Khoj如何利用对话历史来理解上下文,从而提供更相关、更有用的搜索结果。
未来展望:上下文感知的下一代搜索
Khoj的上下文感知查询处理技术为智能搜索树立了新的标准,但这仅仅是开始。未来,我们可以期待更多创新:
-
多模态上下文理解:除了文本,系统还能理解图像、音频等多种模态的上下文信息,进一步提高查询理解的准确性。
-
个性化搜索体验:基于用户的搜索历史、偏好和专业背景,提供更加个性化的搜索结果。
-
实时上下文更新:系统能够实时学习新的概念和术语,确保即使在快速变化的领域(如人工智能、区块链等),也能提供最新、最准确的信息。
-
增强型推理能力:不仅理解查询的表面含义,还能推断用户的潜在需求,提供超越直接查询的洞察和建议。
随着这些技术的不断发展,我们可以期待Khoj在未来能够提供更加智能、更加个性化的搜索体验,真正成为每个人的"第二大脑"AI助手。
结语
Khoj的上下文感知查询处理技术通过创新的查询理解、多引擎搜索、智能信息提取和结果整合,彻底改变了传统搜索的模式。它不仅理解查询的字面含义,还能深入理解其上下文和潜在意图,从而提供更精准、更相关的搜索结果。
无论是学术研究、软件开发,还是日常学习,这项技术都能帮助用户更高效地获取信息,节省宝贵的时间和精力。随着技术的不断迭代和优化,我们有理由相信,Khoj将在未来的智能搜索领域发挥越来越重要的作用。
如果你还在为信息过载和搜索效率低下而困扰,不妨尝试一下Khoj,体验上下文感知查询处理带来的全新搜索体验。仓库地址:https://gitcode.com/GitHub_Trending/kh/khoj
如果你觉得这篇文章对你有帮助,请点赞、收藏并关注我们,获取更多关于Khoj的技术解析和使用技巧。下一期,我们将深入探讨Khoj的本地知识库管理功能,敬请期待!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




