- 人工智能
- 大模型
- AI Agent
- RAG
- 深度研究
- 搜索引擎
- 后端
- 代码智能体
【免费下载链接】deepsearch
openJiuwen DeepSearch是一款知识增强的深度搜索与研究框架,有业界领先的片段级引用和溯源推理能力,提供精准Agentic搜索与研究能力
本篇技术指南以 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 入口实现 可以还原完整链路:
preprocess_report移除报告文末的参考文献章节,保留可溯源正文;preprocess_search_record将搜索记录清洗、去重、裁剪并分配索引;recognize_content_to_cite通过content_recognitionPrompt 识别报告中需要添加引用的句子;match_sources将"待引用句子列表"与"预处理后的搜索记录"连接起来,执行来源匹配(即本主题);generate_source_datas依据匹配结果生成引用数据,add_source_references把引用标记插入句末;- 最后经 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 明确列出的约束,是解析与后处理代码得以稳定工作的前提:
- 每条句子最多 3 个
matched_source_indices——防止单句引用来源过多,保证引用精度与版面可读性; sentence字段必须与输入句子保持一致——这是后续validate_trace_results能通过"句子是否存在于内容识别结果"来过滤的关键(见第四节);matched_source_indices必须对应搜索记录中的索引——索引语义由预处理阶段写入的index字段保证(见第五节);- 无法匹配任何搜索记录时,
matched_source_indices置为[]——即"宁缺毋滥",不伪造引用; - 直接输出正确格式的 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。解析逻辑做了三层防御:
- 顶层必须是 dict,且必须含
source_traced_results键,否则返回空; source_traced_results必须是 list;- 每个元素必须是 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搜索与研究能力
相关推荐
openJiuwen DeepSearch如何实现片段级溯源:引用匹配与校验算法完整解析
openJiuwen DeepSearch如何实现片段级溯源:引用匹配与校验算法完整解析 openJiuwen DeepSearch 是一款知识增强的深度搜索与
人工智能大模型AI AgentRAG深度研究搜索引擎后端代码智能体openJiuwen DeepSearch 的 Prompt 模板与 Workflow 节点工程规范:从 prompt-workflow 规则文件到源码级落地
openJiuwen DeepSearch 的 Prompt 模板与 Workflow 节点工程规范:从 prompt workflow 规则文件到源码级落地
人工智能大模型AI AgentRAG深度研究搜索引擎后端代码智能体openJiuwen DeepSearch 来源匹配(Source Matching):从提示模板到引用落地的完整技术解析
openJiuwen DeepSearch 来源匹配(Source Matching):从提示模板到引用落地的完整技术解析 导读 :本文聚焦 openJiuwe
人工智能大模型AI AgentRAG深度研究搜索引擎后端代码智能体
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考