☰
openJiuwen DeepSearch 来源匹配(source_matching)Prompt 契约与实现解析:从评分规则到片段级引用溯源落地
2026/10/13 2:30:15 网站建设 项目流程
  • 人工智能
  • 大模型
  • AI Agent
  • RAG
  • 深度研究
  • 搜索引擎
  • 后端
  • 代码智能体

【免费下载链接】deepsearch

openJiuwen DeepSearch是一款知识增强的深度搜索与研究框架,有业界领先的片段级引用和溯源推理能力,提供精准Agentic搜索与研究能力

项目地址:https://gitcode.com/openJiuwen/deepsearch
点击查看免费下载

本篇技术指南以 openJiuwen DeepSearch 全局溯源链路中的核心 Prompt 模板source_matching/system.md为主体,系统讲解"来源匹配(Source Matching)"这一能力:它如何把内容识别阶段产出的待引用句子,与预处理后的搜索记录逐一比对并打分,最终输出可被下游参考文献生成与 Citation 校验直接消费的 JSON 匹配结果。读完本文,你将掌握该 Prompt 的完整数据契约(输入变量、评分体系、输出 Schema、约束条件)、它如何被message_builder渲染注入、match_sources调用链的源码级实现,以及 exact/fuzzy/classify 三类辅助匹配算法与搜索记录预处理的底层原理,可直接用于理解或复现 openJiuwen DeepSearch 的片段级引用溯源能力。

一、来源匹配在溯源链路中的定位

来源匹配(source_matching)是 openJiuwen DeepSearch 全局溯源流程中承上启下的关键环节。其上游是"溯源内容识别(content recognition)",下游是"参考文献生成(reference generation)"与"Citation 校验(citation checking)"。从 SourceTracer 入口实现 可以还原完整链路:

  1. preprocess_report移除报告文末的参考文献章节,保留可溯源正文;
  2. preprocess_search_record将搜索记录清洗、去重、裁剪并分配索引;
  3. recognize_content_to_cite通过content_recognitionPrompt 识别报告中需要添加引用的句子;
  4. match_sources将"待引用句子列表"与"预处理后的搜索记录"连接起来,执行来源匹配(即本主题);
  5. generate_source_datas依据匹配结果生成引用数据,add_source_references把引用标记插入句末;
  6. 最后经 Citation 校验与参考文献追加完成整篇报告的溯源。

可见来源匹配决定了"哪句话能获得哪个来源的引用",是整个片段级引用能力的核心决策点。若此处匹配为空或错误,下游不会伪造引用(边界处理明确"空 source list 时返回空匹配,不应伪造引用"),因此匹配质量直接影响报告的可信度。

二、source_matching Prompt 契约详解

关联文档 system.md 定义了模型扮演的角色与硬性规则,user.md 定义了单次请求的输入变量。两者共同构成完整 Prompt 模板。

2.1 系统角色与任务目标

system.md 将模型设定为"专业的来源匹配助手(professional source matching assistant)",核心职责是:将研究报告中需要引用的内容与搜索记录进行精确匹配,只选择最相关的来源。这与 openJiuwen DeepSearch 追求"精准 Agentic 搜索与研究"、提供片段级引用的产品定位一致——引用不是随便贴一个来源,而是逐句找到支撑它的证据片段。

2.2 匹配算法评分体系(1-10 分)

system.md 规定按相关性评分(1-10 分制)对匹配进行排序,共三档:

匹配类型分值区间判定语义
Exact Match(精确匹配)10 分文本完全一致
Semantic Match(语义匹配)8-9 分含义相同但措辞不同
Keyword Match(关键词匹配)6-7 分核心事实一致

该评分体系是 Prompt 层面的"排序语言",它指导 LLM 在多个候选来源中优先输出文本重合度最高、语义最贴近的记录。需要注意:此处的 1-10 分是给 LLM 内部排序用的语义化分值,与 辅助算法 中面向引用质量打分的 0.0-1.0 连续分值(exact ≥ 0.85、fuzzy ∈ [0.3, 0.7])是两个独立体系,后者用于代码侧的引用片段质量判断,二者职责解耦。

2.3 输出格式契约(source_traced_results)

system.md 要求模型严格输出如下 JSON 结构:

{ "source_traced_results": [ { "sentence": "Sentence requiring citation", "matched_source_indices": [matching record index 1, matching record index 2, matching record index 3] } ] }

字段语义:

  • sentence:需要引用的句子原文,必须与输入保持一致(见 2.4 约束);
  • matched_source_indices:命中的搜索记录索引数组,索引对应预处理后搜索记录在各自来源类型列表中的位置(index字段)。

2.4 硬性约束(Important Notes)

system.md 明确列出的约束,是解析与后处理代码得以稳定工作的前提:

  1. 每条句子最多 3 个matched_source_indices——防止单句引用来源过多,保证引用精度与版面可读性;
  2. sentence字段必须与输入句子保持一致——这是后续validate_trace_results能通过"句子是否存在于内容识别结果"来过滤的关键(见第四节);
  3. matched_source_indices必须对应搜索记录中的索引——索引语义由预处理阶段写入的index字段保证(见第五节);
  4. 无法匹配任何搜索记录时,matched_source_indices置为[]——即"宁缺毋滥",不伪造引用;
  5. 直接输出正确格式的 JSON,不得附带任何额外字符,包括```json标记——配合代码侧的normalize_json_output清洗,确保可被json.loads直接解析。

2.5 输入变量(user.md)

user.md 只定义了两个 Jinja 变量,也是 Prompt 契约测试 中source_matching的 required vars:

  • {{ content_recognition_result }}:内容识别结果,即需要引用的句子列表;
  • {{ search_record }}:搜索记录(JSON 形式)。

从 source_matcher.py 调用处 可见,实际构建上下文时search_record会先json.dumps(..., ensure_ascii=False)序列化为字符串再注入模板,content_recognition_result直接传入列表。两者顺序与 user.md 一致,渲染后即为发送给 LLM 的 user 消息。

三、Prompt 如何被渲染与注入:message_builder 源码剖析

source_matching 模板由统一的 message_builder.py 加载渲染。关键机制如下:

  • build_prompt_messages(prompt_name, context)按prompt_name定位prompts目录下的子目录:先渲染system.md(纯静态模板,不允许任何运行时变量),再按需渲染user.md(Jinja 变量来自传入的 context);
  • _assert_static_template_tree会递归校验 system 模板及其静态 include不包含运行时变量——这正是 source_matching/system.md 全文没有{{ }}占位符的原因:系统级指令必须恒定,只有 user 消息携带当次输入;
  • 渲染结果组装为[{"role": "system", "content": ...}, {"role": "user", "content": ...}]的消息列表,供ainvoke_llm_with_stats调用;
  • 模板名必须匹配^[A-Za-z0-9_-]+$,上下文不得占用messages/history/prior_messages/images等预留键。

仓库通过 test_deepresearch_prompt_contracts.py 以参数化方式对全部 Prompt 目录做契约校验:断言source_matching/system.md不含任何模板变量、user.md的变量集合恰为{"content_recognition_result", "search_record"},并且每个有system.md的目录都必须在契约表中注册(test_contracts_cover_every_directory_prompt)。这意味着若有人误改 system.md 引入变量,或改动 user.md 的输入字段,测试会立即失败,从而保证 Prompt 契约与调用方代码长期稳定。

四、match_sources 调用链:从输入到校验后的匹配结果

核心实现位于 source_matcher.py,由match_sources作为入口,依次经历四个阶段。

4.1 按来源类型分组处理(match_sources)

async def match_sources(content_recognition_result, preprocessed_search_record, chunk_size, llm_model_name): # 空输入直接返回 [] for source_type, source_list in preprocessed_search_record.items(): source_results = await process_source_type(...) all_trace_results.extend(source_results) merged_results = merge_trace_results(all_trace_results) validated_results = validate_trace_results(merged_results, content_recognition_result) return validated_results

预处理后的搜索记录是按来源类型组织的字典(如web_page_search_record、web_image_search_record、local_text_search_record、local_image_search_record,参见 测试夹具)。match_sources遍历每种类型分别匹配,再统一合并与校验。这样设计的好处是:不同来源类型的记录相互隔离,索引只在各自列表内有效,避免跨类型引用错位。

4.2 分片与 LLM 调用(process_source_type / call_llm_for_trace)

  • process_source_type:当len(source_list) <= chunk_size时整体处理(process_single_chunk),否则按chunk_size切片逐个处理(process_chunked_source),分片结果最终合并。在 SourceTracer 默认参数 中_chunk_size = 40,即单批最多 40 条搜索记录;
  • call_llm_for_trace:构建source_tracer_context(search_record序列化 +content_recognition_result),调用build_prompt_messages("source_matching", context),再经ainvoke_llm_with_stats发起调用,统计代理名使用AgentLlmName.SOURCE_TRACER_SOURCE_MATCHING(定义于 node_constants.py)。异常被抑制并返回空列表,保证单类型匹配失败不影响整篇报告流程。

4.3 响应解析(parse_trace_response)

LLM 原始输出先经normalize_json_output清洗(剥离```json等围栏),再json.loads。解析逻辑做了三层防御:

  1. 顶层必须是 dict,且必须含source_traced_results键,否则返回空;
  2. source_traced_results必须是 list;
  3. 每个元素必须是 dict(过滤非 dict 项),随后统一注入result["source"] = source_type,即给每条结果标注其来源类型。

这正好呼应 system.md 第 5 条约束:即使模型输出带了多余字符,代码侧也能容错清洗,但契约要求模型自身直接输出纯 JSON。

4.4 合并去重与有效性校验(merge_trace_results / validate_trace_results)

  • merge_trace_results:以f"{sentence}|{source}"为唯一键,把分片或多次调用产生的同句同源结果合并,matched_source_indices取并集、去重并升序排列;同时只保留纯 int 索引(过滤 bool 等误类型);
  • validate_trace_results:仅保留sentence确实存在于content_recognition_result中的结果,其余丢弃并告警。这一步把 system.md "sentence 必须与输入一致"的约束落到了工程校验上,防止 LLM 幻觉出报告中不存在的句子。

对应测试见 test_source_matcher.py:覆盖成功解析、非法 JSON 抛错、缺失键返回空、多结果解析、重复句源对合并去重等场景。

五、搜索记录预处理:索引从何而来

要让matched_source_indices语义成立,搜索记录必须先被预处理并写入index字段。实现位于 source_tracer_preprocessors.py:

  • preprocess_search_record:仅处理值为 list 的来源类型,非 list 原样保留;
  • process_search_record_list对每条记录执行三道过滤:
    • 必填字段:必须同时包含title、url、content三字段(has_required_fields),缺一即丢弃;
    • 去重:以url/title/content三元组做全等比较(is_duplicate_record),重复项仅保留首条;
    • 长内容分片:content长度超过max_content_len(SourceTracer 默认_search_record_max_content_len = 3000)时,按 3000 字符为单位切分为多条记录,每条继承原url/title;
  • 每条通过校验的记录被追加item["index"] = len(processed_list),即预处理后列表内的位置下标就是 Prompt 中matched_source_indices所指向的索引。

SourceTracer.transform_search_record负责把子报告生成阶段的 top-K 证据(含passage_text或original_content)转换为{url, title, content}格式,且优先采用非空passage_text片段而非父文档全文,避免把整篇文档重复塞入匹配上下文(测试用例 明确验证了这一降级规则)。

六、辅助匹配算法:代码侧的精确/模糊/分类匹配

除 LLM 匹配外,仓库还提供不依赖模型的辅助算法 source_match_algo.py,用于"事实与引用内容的精确/模糊匹配"及引用片段质量判断:

  • exact_match:将 fact 按中文标点分句,逐句剥离末尾标点后在 citation_content 中做子串精确搜索;命中片段取 citation 原文而非 fact 原文(消除标点差异),覆盖率映射为score = 0.85 + coverage_rate × 0.15,恒 ≥ 0.85;
  • fuzzy_match:基于difflib.SequenceMatcher做句子级模糊匹配(非字符级滑动窗口),默认阈值threshold = 0.8;覆盖率映射为score = coverage_rate × 0.7,有命中时钳制在[0.3, 0.7],无命中为 0.0;
  • classify_and_match:综合策略——先 exact,覆盖率 ≥ 0.85 判为exact;否则 fuzzy,有任何命中判为fuzzy;均无命中返回no_match。模块设计上明确与防篡改用途的citation_verify_research.py#find_matches解耦,并复用split_into_sentences与content_analyzer.py保持一致的分句口径。

该模块与 Prompt 评分体系的关系可以这样理解:Prompt 让 LLM 在"句子 → 来源"层面做语义取舍(对应 8-9 分语义匹配);辅助算法则在"引用片段 → 来源正文"层面做确定性的质量校验(exact/fuzzy 分数),二者共同构成"LLM 语义匹配 + 确定性算法校验"的双保险,这也是 openJiuwen DeepSearch 片段级引用能力的技术底色。

七、匹配结果的下游消费与整体编排

匹配结果离开match_sources后进入 add_source.py:

  • generate_source_datas:SourceReferenceProcessor.extract_source_info依据source类型定位对应记录列表,按matched_source_indices逐个extract_source_item_info提取 title/url/content/score 等字段,构建标准引用数据项,并做索引越界与字段缺失校验("匹配索引必须落在对应来源列表范围内"的工程落实);
  • merge_source_datas:把溯源新生成的数据与报告原文自带的引用数据合并,按 chunk 在报告中的位置过滤、排序;
  • add_source_references:以句末插入[source_tracer_result]标题的形式把引用写回报告,插入前会对 title/url 做 HTML 实体转义与 URL scheme 校验(仅允许 http/https,见 add_source.py)。

整体编排由 SourceTracer 类 完成:research_trace_source产出_trace_source_datas,add_source_to_report完成引用注入;其pre_check_origin_coverage还以_min_origin_coverage_count = 10、_min_origin_coverage_ratio = 0.3两个阈值做前置判定——若原文引用覆盖已达标则跳过溯源,避免无效算力消耗。

八、配置开关与局部溯源场景

来源匹配不仅服务于整篇报告的全局溯源,还被"用户反馈局部溯源"复用。在 local_source_trace.py 中,改写后的段落文本会走同一套recognize_content_to_cite(0.9) → match_sources(chunk_size=40) → generate_source_datas → add_source_references → CitationCheckerResearch流程,参数与全局溯源保持一致(相似度阈值 0.9、单记录内容上限 3000、分片 40),保证两种场景行为一致。

此外,config.py 提供source_tracer_research_trace_source_switch溯源功能开关(默认True),在 SourceTracerNode 中当search_mode == "research"且开关为 False 时直接跳过溯源,可用于按需关闭引用生成以节省调用成本。

九、测试与验证

仓库为来源匹配提供了多层次测试保障,推荐按 source-matching 文档 给出的命令执行:

uv run pytest deepsearch/tests/source_tracer/test_source_matcher.py uv run pytest deepsearch/tests/source_tracer/test_source_match_algo.py uv run pytest deepsearch/tests/algorithm/prompts/test_deepresearch_prompt_contracts.py

关键覆盖点:

  • Prompt 契约:source_matching的 user 变量恰为content_recognition_result、search_record,system 模板必须零变量(test_deepresearch_prompt_contracts.py);
  • 匹配主流程:成功路径、异常传播、空输入返回空、空内容识别返回空(test_source_matcher.py);
  • 响应解析:source_traced_results缺失、非法 JSON、围栏包裹 JSON 的容错(test_source_matcher.py);
  • 结果合并:同句同源多批结果的索引并集去重排序(test_source_matcher.py);
  • 整体链路:匹配失败时_trace_source_datas保持空、异常包装为CustomValueException(SOURCE_TRACER_TRACE_SOURCE_ERROR)(test_source_tracer.py)。

十、总结

source_matching Prompt 是 openJiuwen DeepSearch 片段级引用能力的"决策契约":它以 1-10 分的匹配评分体系指导 LLM 做句子到来源的语义取舍,以严格的 JSON Schema(source_traced_results)和五条硬性约束保证输出可解析、可校验、不越界,再由message_builder静态渲染、match_sources分片调用与合并校验、辅助算法兜底打分、下游引用注入完成闭环。理解这一契约及其源码实现,是深入 openJiuwen DeepSearch 溯源体系、乃至二次定制引用精度策略的最佳切入点。

  • 人工智能
  • 大模型
  • AI Agent
  • RAG
  • 深度研究
  • 搜索引擎
  • 后端
  • 代码智能体

【免费下载链接】deepsearch

openJiuwen DeepSearch是一款知识增强的深度搜索与研究框架,有业界领先的片段级引用和溯源推理能力,提供精准Agentic搜索与研究能力

项目地址:https://gitcode.com/openJiuwen/deepsearch
点击查看免费下载

相关推荐

上一篇:expo-navigation-bar 完全指南:在 Android 上控制与监听系统导航栏
下一篇:Gofile批量下载工具深度解析:高效自动化文件获取实战指南

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询