news 2026/9/10 8:45:32

OpenViking 检索机制深度解析:意图分析 + 层级检索 + Rerank 的三阶段召回管线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenViking 检索机制深度解析:意图分析 + 层级检索 + Rerank 的三阶段召回管线

OpenViking 检索机制深度解析:意图分析 + 层级检索 + Rerank 的三阶段召回管线

【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking

OpenViking 面向 AI Agent 提供了统一的自进化上下文数据库(Unify Agent Memory, Knowledge RAG and Skills),其检索机制是连接用户查询与记忆、资源、技能三层上下文的枢纽。本文基于 docs/zh/concepts/07-retrieval.md 展开,系统讲解find()search()的差异、IntentAnalyzer 意图分析、HierarchicalRetriever 层级递归检索与 Rerank 精排的完整调用链,并结合 openviking/retrieve/ 目录下的源码实现与配置指南,给出可直接落地的配置示例。读完本文,你将掌握 OpenViking 检索的完整数据流、关键参数调优方法,以及如何用少量配置让检索同时获得“高召回”与“精排序”。

一、整体架构:三阶段检索管线

OpenViking 的检索机制采用经典的三阶段设计:意图分析 → 层级检索 → Rerank。原始查询首先被拆解为多个带类型与优先级的子查询,随后按上下文类型确定根目录并递归搜索目录树,最后在 THINKING 模式下由精排模型对候选结果重新打分。

查询 → 意图分析 → 层级检索 → Rerank → 结果 ↓ ↓ ↓ TypedQuery 目录递归 精排评分

该流程的每一环都有独立模块承担,分布在 openviking/retrieve/ 目录下:

  • intent_analyzer.pyIntentAnalyzer,负责把会话上下文交给 LLM 生成查询计划;
  • hierarchical_retriever.pyHierarchicalRetriever,用优先队列实现目录树递归检索与分数传播;
  • retrieval_stats.py:检索统计收集器,记录每次查询的结果数、分数与延迟;
  • context_assembler/:上下文组装管线,把检索结果按预算、层级组装成最终注入 Agent 的上下文。

此外,核心数据结构(TypedQueryQueryPlanMatchedContextQueryResultFindResultThinkingTrace等)定义在 openviking_cli/retrieve/types.py 中,供检索服务与 SDK 共享。

二、两个入口:find() 与 search()

OpenViking 面向客户端提供两个检索入口,分别对应“简单查询”与“复杂任务”两类场景。两者在是否依赖会话上下文、是否使用 LLM 意图分析、查询数量与延迟上有本质区别:

特性find()search()
会话上下文不需要需要
意图分析不使用使用 LLM 分析
查询数量单一查询0-5 个 TypedQuery
延迟较高
适用场景简单查询复杂任务

从服务端实现看,二者在 openviking/service/search_service.py 中分属两条路径:find()(L139)是无会话的语义搜索,直接以原始查询执行向量召回;而search()(L91)在传入session且启用意图分析时,会先调用session.get_context_for_search(query)获取会话压缩摘要,再进入viking_fs.search。该开关由配置项retrieval.enable_intent控制,关闭后search()会跳过会话加载与IntentAnalyzer,退化为与无会话搜索相同的原始查询路径(见 retrieval_config.py)。

SDK 层的两个异步入口位于 sdk/python/openviking_sdk/client.py:find()请求/api/v1/search/find(L1361),search()请求/api/v1/search/search(L1384),并支持target_urilimitimageFindOptions/SearchOptions扩展参数。

使用示例

# find(): 简单查询,无需会话 results = await client.find( query="OAuth 认证", target_uri="viking://resources/", ) # search(): 复杂任务(需要会话上下文) session_info = await client.create_session() results = await client.search( query="帮我创建一个 RFC 文档", session_id=session_info["session_id"], )

create_session()同样来自 SDK(L1487),请求POST /api/v1/sessions,返回的session_idsearch()的会话凭证。需要说明:search()返回的FindResulttotalmemoriesresourcesskills四部分组成,其中totalFindResult.__post_init__中被自动计算为三类上下文数量之和(见 openviking_cli/retrieve/types.py)。

三、意图分析:从一句话到 0-5 个 TypedQuery

3.1 IntentAnalyzer 的职责

IntentAnalyzer位于 openviking/retrieve/intent_analyzer.py,其核心职责有三条(源码 docstring 明确声明):

  1. 整合会话上下文(压缩摘要 + 最近消息 + 当前消息);
  2. 调用 LLM 分析意图;
  3. 生成面向 memory / resources / skill 的多个 TypedQuery。

它通过get_openviking_config().get_query_planner()获取规划模型;未配置query_planner时回退到vlm。该阶段使用的模型可由query_planner配置项单独指定,意图分析会完整消费以下三部分输入:

  • 会话压缩摘要(compression_summary,超过 30000 字符会被截断,对应MAX_COMPRESSION_SUMMARY_CHARS,约为 10000 token 的预算);
  • 最近 5 条消息(messages[-5:]max_recent_messages默认值为 5);
  • 当前查询(current_message)。

3.2 输出:TypedQuery 与 QueryPlan

意图分析的输出是QueryPlan,其中包含 0-5 个TypedQuery。两者的字段定义在 openviking_cli/retrieve/types.py:

@dataclass class TypedQuery: query: str # 重写后的查询 context_type: ContextType # MEMORY/RESOURCE/SKILL intent: str # 查询目的 priority: int # 1-5 优先级 target_directories: List[str] # LLM 定位的目录 URI(可空)

源码中的ContextType是字符串枚举:MEMORY = "memory"RESOURCE = "resource"SKILL = "skill"(types.py#L16-L21)。TypedQuery.priority默认值为 3,范围 1-5,数字越小优先级越高。

QueryPlan额外携带session_context(会话上下文摘要)与reasoning(LLM 推理过程),前者会作为session_context返回给上层,后者可透出到FindResult.to_dict()query_plan字段,用于检索可观测性。

从源码实现看,LLM 返回的 JSON 被parse_json_from_response解析后,若顶层是数组会被包装为{"reasoning": "", "queries": [...]};每个查询项的context_type通过ContextType(...)严格校验,非法值回退为RESOURCEpriority缺失时取默认值 3(intent_analyzer.py#L89-L123)。

3.3 查询风格与特殊情况

意图分析会根据查询的语言形态决定context_type,官方文档给出如下风格约定:

类型风格示例
skill动词开头"创建 RFC 文档"、"提取 PDF 表格"
resource名词短语"RFC 文档模板"、"API 使用指南"
memory"用户XX""用户的代码规范偏好"

两种特殊情况需要特别注意:

  • 0 个查询:闲聊、问候等不需要检索的场景。此时QueryPlan.queries为空,检索管线直接短路,避免不必要的记忆注入与 token 消耗——这正是推荐用轻量小模型承担意图分析的核心价值之一。
  • 多个查询:复杂任务可能需要技能 + 资源 + 记忆三类上下文,例如“帮我创建一个 RFC 文档”可能同时生成 resource 查询(RFC 模板)与 skill 查询(文档创建技能)。

3.4 query_planner 配置:用小模型承担检索规划

意图分析默认走vlm,但官方强烈建议为search()单独配置一个轻量的本地 query planner 模型,以降低延迟与成本。推荐模型为 Ollama 上的guoxuter/ov_intent_analysis_sft:v7_q8(基于 Qwen3.5-0.8B 微调),此前版本v4_q8仍作为可选项支持。

ollama pull guoxuter/ov_intent_analysis_sft:v7_q8

然后在 OpenViking 配置中追加(完整配置说明见 query_planner 配置):

{ "query_planner": { "provider": "litellm", "model": "ollama/guoxuter/ov_intent_analysis_sft:v7_q8", "api_base": "http://127.0.0.1:11434", "temperature": 0.0, "timeout": 60, "extra_request_body": { "think": false } } }

一个值得关注的实现细节:OpenViking 内置了按模型映射的 prompt 选择机制QUERY_PLANNER_PROMPT_BY_MODEL表(intent_analyzer.py#L24-L27)把ollama/guoxuter/ov_intent_analysis_sft:v7_q8v4_q8分别映射到retrieval.ov_intent_analysis_sft_v7retrieval.ov_intent_analysis_sft_v4内置 prompt;未映射的模型继续使用默认的retrieval.intent_analysisprompt。因此使用官方推荐模型时不需要替换 prompt 文件,也无需设置prompts.templates_dir。该方案让 0.8B 级小模型承担检索规划,同时保留更强的vlm处理语义提取、记忆提取与多模态内容。

四、层级检索:用优先队列递归搜索目录树

4.1 五步流程

HierarchicalRetriever位于 openviking/retrieve/hierarchical_retriever.py,实现“目录树优先队列 + 分数传播 + 收敛检测”的递归检索。其整体流程为:

Step 1: 根据 context_type 确定根目录 ↓ Step 2: 全局向量搜索定位起始目录 ↓ Step 3: 合并起始点 + Rerank 评分 ↓ Step 4: 递归搜索(优先队列) ↓ Step 5: 转换为 MatchedContext

结合源码,retrieve()(L101-L351)的实现比文档描述更细致:Step 1 在target_dirs显式指定时直接作为根目录集合,否则调用default_target_directories(ctx, context_type=...)(定义于 openviking/core/retrieval_targets.py)按租户与 peer 解析默认目录;Step 2 的全局搜索限定level=[0, 1](即只召回目录层 L0/L1),步长取max(limit, GLOBAL_SEARCH_TOPK)

4.2 根目录映射

context_type根目录
MEMORYviking://~/memories
RESOURCEviking://resources
SKILLviking://~/skills

实际解析时,default_target_directories会结合用户身份做更精细的展开(retrieval_targets.py#L60-L83):MEMORY 展开为{user_root}/memories(有 peer 时追加{user_root}/peers/{peer}/memories),RESOURCE 展开为viking://resources{user_root}/resources与 peer 资源目录,SKILL 展开为viking://agent/skills与用户技能目录并去重。

4.3 递归搜索算法与分数传播

递归搜索的核心逻辑在_recursive_search()(L421-L588),文档给出的伪代码与源码高度一致:

while dir_queue: current_uri, parent_score = heapq.heappop(dir_queue) # 搜索子节点 results = await search(parent_uri=current_uri) for r in results: # 分数传播 final_score = score_propagation_alpha * embedding_score + (1 - score_propagation_alpha) * parent_score if final_score > threshold: collected.append(r) if not r.is_leaf: # 目录继续递归 heapq.heappush(dir_queue, (r.uri, final_score)) # 收敛检测 if topk_unchanged_for_3_rounds: break

从源码可以看到几个文档之外的工程细节:

  • 并行扇出:每轮从优先队列取出MAX_PARALLEL_CHILD_SEARCHES = 4个目录,用asyncio.gather并发搜索子节点,控制对远程向量库的并发请求量(L495-L513);
  • 分数传播公式final_score = alpha * score + (1 - alpha) * current_score if current_score else score——当父节点分数为 0(如显式根目录)时直接使用子节点自身分数(L531-L533);
  • 只对目录递归r.get("level", 2) != 2才入队继续展开,L2 文件是终端命中(L555-L556);
  • 按 URI 去重:同一 URI 只保留最高分候选(collected_by_uri字典);
  • 双重收敛检测:除了“topk 连续 3 轮不变”,还有“候选池规模停滞 3 轮”的stagnant_rounds兜底,二者任一达到MAX_CONVERGENCE_ROUNDS = 3即提前终止(L558-L581)。

4.4 关键参数

参数说明
retrieval.score_propagation_alpha1.0分数传播混合中子节点自身分数的权重;1.0表示仅使用子节点自身分数,忽略父节点分数
MAX_CONVERGENCE_ROUNDS3收敛检测轮数
GLOBAL_SEARCH_TOPK10全局搜索候选数

score_propagation_alpha是唯一暴露到配置中的检索行为参数,实际读取路径为retrieval_config.pyHierarchicalRetriever.__init__self.score_propagation_alpha(hierarchical_retriever.py#L81-L83)。其完整取值语义(retrieval_config.py#L19-L28):

  • 1.0:忽略父节点分数,只使用子节点自身语义相似度(默认);
  • 0.5:与父节点分数等权混合;
  • 0.0:只使用父节点分数。

其余两个参数是源码中的类常量:MAX_CONVERGENCE_ROUNDS = 3GLOBAL_SEARCH_TOPK = 10(L56-L59)。同处还有两个工程常量值得了解:DIRECTORY_DOMINANCE_RATIO = 1.2(目录分数须超过子节点最高分)与MAX_PARALLEL_CHILD_SEARCHES = 4

4.5 两种检索模式与分数阈值

RetrieverMode枚举定义在 hierarchical_retriever.py#L48-L50:

  • QUICK(快速模式)mode未指定且未配置 rerank 时自动选择,只做一次直接的向量召回(search_in_tenant),不进行递归展开与分数传播,延迟最低;
  • THINKING(思考模式):配置了 rerank 时默认启用,执行完整的多阶段流程(全局搜索定位起始点 → 递归搜索 → Rerank 精排),search()默认走此模式。

此外,retrieve()支持score_thresholdscore_gte两个阈值参数:前者可覆盖配置阈值,后者控制比较符(True>=False>),相关测试见 tests/retrieve/test_hierarchical_retriever_rerank.py。

五、Rerank 策略:THINKING 模式下的精排

5.1 触发条件与回退机制

Rerank 在 THINKING 模式下对候选结果精排,触发需同时满足:

  • 配置了 Rerank AK/SK(或 OpenAI 兼容凭据);
  • 使用 THINKING 模式(search()默认)。

若 rerank 返回无效结果或 API 调用失败,会自动回退到向量分数。回退逻辑在_rerank_scores()(L371-L419)中实现:调用异常、返回空结果、长度不匹配三种情况都会logger.warning后原样返回fallback_scores;即使部分文档为空串,也会先过滤再调用,空文档的位置保留原向量分数。对应测试test_rerank_scores_preserves_fallbacks_for_empty_documents(tests/retrieve/test_hierarchical_retriever_rerank.py#L282)验证了这一行为。

5.2 评分方式

if rerank_client and mode == THINKING: scores = rerank_client.rerank_batch(query, documents) else: scores = [r["_score"] for r in results] # 向量分数

Rerank 打分被用在两处(与文档描述一致):

  1. 起始点评估:对全局搜索召回的所有目录候选(global_results)按 abstract 重新打分,作为递归搜索的入口优先级;
  2. 递归搜索:对每层子节点的 abstract 重新打分,再与父节点分数做传播混合。

一个额外的工程细节:当max_input_tokens > 0时,_rerank_scores会对 query 与 document 做 token 预算截断——query 占用max_input_tokens * 3 // 4,其余预算分给 document,超长文本“保留开头和结尾”式截断(L388-L396)。

5.3 后端支持

后端模型
Volcenginedoubao-seed-rerank

从 openviking/models/rerank/ 目录看,Rerank 客户端实际支持四类统一派发:volcengine_rerank.py(VikingDB)、cohere_rerank.py(Cohere)、openai_rerank.py(OpenAI 兼容接口)与litellm_rerank.py(LiteLLM 聚合)。RerankClient.from_config(rerank_config)会按配置的 provider 自动选择实现(hierarchical_retriever.py#L88-L99)。

官方文档主推的火山引擎配置(rerank 配置):

{ "rerank": { "provider": "vikingdb", "ak": "your-access-key", "sk": "your-secret-key", "model_name": "doubao-seed-rerank", "model_version": "251028" } }

OpenAI 兼容提供方(如 DashScope)示例:

{ "rerank": { "provider": "openai", "api_key": "your-api-key", "api_base": "https://dashscope.aliyuncs.com/compatible-api/v1/reranks", "model": "qwen3-rerank", "timeout": 120, "max_input_tokens": 2048, "threshold": 0.1 } }

关键参数说明:

参数类型说明
providerstr"vikingdb""cohere""openai"。省略时基于字段自动识别。
ak/skstrVikingDB Access Key / Secret Key(仅vikingdb提供方使用)
model_namestr模型名称(仅vikingdb提供方使用,默认:doubao-seed-rerank
api_keystrAPI Key(用于openaicohere提供方)
api_basestr接口地址(用于openai提供方)
modelstr模型名称(用于openai提供方)
timeoutfloatOpenAI 兼容 provider 的 HTTP 超时(默认30.0,本地冷启动 rerank 可调大)
max_input_tokensint每个 query-document 对的最大估算 token 数;0表示不截断(默认0
thresholdfloat分数阈值0.0-1.0,低于此值的结果被过滤(默认0.1

注意:如果未配置 Rerank,搜索仅使用向量相似度(QUICK 模式),此时threshold退化为 0。

六、检索结果的数据结构

6.1 MatchedContext

递归搜索与精排结束后,候选结果经_convert_to_matched_contexts()(L590-L658)转换为统一的MatchedContext

@dataclass class MatchedContext: uri: str # 资源 URI context_type: ContextType is_leaf: bool # 是否文件 abstract: str # L0 摘要 score: float # 最终分数

源码中该数据类的字段更完整:level(0=L0 摘要、1=L1 概览、2=L2 文件)、categorymatch_reasonsearch_tags(types.py#L276-L289)。转换时有几个值得留意的行为:

  • 分数混合:THINKING 模式下若hotness_alpha > 0,最终分数 =(1 - alpha) * 语义分数 + alpha * hotness_score,其中 hotness 由active_countupdated_athotness_score()计算(memory_lifecycle.py),随后按混合分数重新排序;
  • L0/L1 预览净化:L0/L1 记录的 abstract 会经body_for_preview()只保留正文,剔除完整 OKF 文档内容;L2 用户 Markdown 保持原样;
  • URI 后缀重建_append_level_suffix按层级为展示 URI 补上.abstract.md(L0)或.overview.md(L1)后缀(L660-L672);
  • 阈值校验_finite_score会把 inf/nan 分数收敛为 0.0,避免向量库异常分数污染排序。

6.2 FindResult

find()search()最终都返回FindResult

@dataclass class FindResult: memories: List[MatchedContext] resources: List[MatchedContext] skills: List[MatchedContext] query_plan: Optional[QueryPlan] # search() 时有 query_results: Optional[List[QueryResult]] total: int

FindResult同时实现了__iter__(按 memories → resources → skills 顺序迭代全部命中)与to_dict(include_provenance=False)(types.py#L344-L410):query_plan输出reasoning与各 TypedQuery 明细;include_provenance=True时额外输出provenance,内含每个查询的searched_directories、命中层级(L0/L1/L2前缀)、match_reasonthinking_trace,用于检索过程的可视化与审计。ThinkingTrace是线程安全的队列式事件记录器(queue.Queue),可输出事件列表、统计信息(搜索目录数、候选收集/排除数、收敛轮数)与简单消息列表(types.py#L131-L235)。

七、检索可观测性:统计与调优依据

OpenViking 为检索提供了两层可观测能力:

  1. RetrievalStatsCollector(openviking/retrieve/retrieval_stats.py):在retrieve()结束处调用record_query()(hierarchical_retriever.py#L338-L345),按 context_type 累计查询次数、命中数、分数分布、延迟(avg/max),并标记rerank_used,可据此评估某类上下文的召回质量与 Rerank 的收益;
  2. ThinkingTraceQueryResult.thinking_trace保留完整的搜索决策链,配合to_dict(include_provenance=True)可在应用层做召回过程审计。

调优时建议按“先看 stats、再调参数”的顺序:若某类上下文的avg_latency_ms明显偏高,可考虑为search()配置本地 query planner 小模型;若希望高频访问或最近更新的上下文获得排序提升,可将retrieval.hotness_alpha从默认0.0调高(此时最终分数不再是纯语义相似度);若递归展开过深导致延迟上升,可调整limit或依赖收敛检测提前终止。完整配置项及默认值见 docs/zh/guides/01-configuration.md:

{ "retrieval": { "hotness_alpha": 0.0, "score_propagation_alpha": 1.0, "recall_intent_timeout_s": 5.0, "recall_rewrite_timeout_s": 30.0 } }
参数类型说明默认值
hotness_alphafloathotness 分数在最终召回分数中的混合权重。0.0关闭 hotness boost,1.0只使用 hotness。范围0.0-1.00.0
score_propagation_alphafloat层级检索中子节点自身分数权重。1.0忽略父节点分数;0.5等权混合;0.0只使用父节点分数。1.0
recall_intent_timeout_sfloat会话感知查询扩展超时,超时回退为用户原查询5.0
recall_rewrite_timeout_sfloatdigest 重写超时,超时后digest为空并照常返回rendered30.0

八、调用链总结与延伸阅读

把本文内容串成一条完整的调用链:SDKclient.search()(sdk/python/openviking_sdk/client.py)→ HTTP/api/v1/search/searchSearchService.search()(openviking/service/search_service.py)→ 会话上下文加载 →IntentAnalyzer.analyze()(openviking/retrieve/intent_analyzer.py)生成 0-5 个 TypedQuery →HierarchicalRetriever.retrieve()按 QUICK/THINKING 模式执行向量召回 + 递归搜索 + Rerank →_convert_to_matched_contexts()组装MatchedContext→ 汇总为FindResult返回。find()则跳过意图分析,直接走向量召回路径。

相关的源码与测试可继续深入探索:

  • 检索实现:openviking/retrieve/hierarchical_retriever.py、openviking/retrieve/intent_analyzer.py、openviking/retrieve/retrieval_stats.py;
  • 数据结构:openviking_cli/retrieve/types.py;
  • Rerank 客户端:openviking/models/rerank/;
  • 服务入口:openviking/service/search_service.py、openviking/core/retrieval_targets.py;
  • 测试用例:tests/retrieve/test_hierarchical_retriever_rerank.py、tests/retrieve/test_intent_analyzer_query_planner.py;
  • 关联文档:架构概述、存储架构、上下文层级、上下文类型、配置指南。

理解了三阶段管线的每个环节,你就能根据实际任务复杂度、延迟预算与排序精度要求,在find()/search()、QUICK/THINKING 模式、query planner 与 rerank 模型之间做出合理取舍,让 OpenViking 的记忆、知识与技能检索真正服务于 Agent 任务。

【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 8:42:32

context-mode:大模型对话上下文的工程化管理策略

1. 先搞清楚一件事:context-mode解决的是哪种“上下文焦虑”先说我遇到的实际问题。我一直在做智能助手类的应用,早期版本上线后收到最多的用户反馈不是“功能太少”,而是“AI怎么聊着聊着就忘了”。前五分钟还在讨论项目排期,聊到…

作者头像 李华
网站建设 2026/9/10 8:42:16

旧电视盒子免费看全网直播:TVBoxOSC 十分钟搭建指南

旧电视盒子免费看全网直播:TVBoxOSC 十分钟搭建指南 【免费下载链接】TVBoxOSC TVBoxOSC - 一个基于第三方项目的代码库,用于电视盒子的控制和管理。 项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC 想在家免费看电视直播&#xff0…

作者头像 李华