news 2026/9/5 19:53:10

DeerFlow 引用渲染机制重构:从 JSONL `<citations>` 块到原生 Markdown 引用的全链路拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeerFlow 引用渲染机制重构:从 JSONL `<citations>` 块到原生 Markdown 引用的全链路拆解

DeerFlow 引用渲染机制重构:从 JSONL<citations>块到原生 Markdown 引用的全链路拆解

【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow

本文基于仓库中的按文件变更总结 CODE_CHANGE_SUMMARY_BY_FILE.md,完整解析 DeerFlow 一次「引用(citations)机制瘦身」变更:后端 Gateway 的 artifacts 下载接口如何移除引用剥离逻辑、Agent 提示词如何删除<citations_format>协议、前端 5 个组件与整个core/citations模块如何被统一的MarkdownContent组件替代,并结合当前仓库源码说明这次重构落地后的最终形态与架构意义。读完本文,你可以掌握一次典型「删除一个跨端渲染协议」的重构涉及的全部触点,以及如何在仓库中验证每一处变更的最终状态。

变更背景:旧机制为什么被移除

旧版 DeerFlow 的引用链路是一套跨前后端约定的私有格式

  1. 提示词协议层:Lead Agent 的系统提示词(prompt.py)中包含一整段<citations_format>区块,要求模型在web_search后输出<citations>JSONL 块(每行一个含id/title/url/snippet的 JSON 对象),并在正文中使用Title完整 Markdown 链接;子代理general_purpose的提示词(general_purpose.py)同样内嵌了该协议。
  2. 后端剥离层:Gateway 的 artifacts 路由(artifacts.py)提供_extract_citation_urlsremove_citations_block两个函数——下载 Markdown 产物时正则解析<citations>JSONL 块,剥离块本身、[cite-N]标记以及引用 URL 对应的外链,再返回「净化」后的内容。
  3. 前端解析层:整个core/citations目录(index.tsuse-parsed-citations.tsutils.ts)负责解析 JSONL、构建citationMap、生成cleanContentSafeCitationContent组件承载引用解析 + loading 占位(「正在整理 N 个引用...」)逻辑,被消息列表、消息组、子任务卡片、产物详情等多处复用。

这套机制的问题在于:JSONL 块与渲染层强耦合,后端下载路径要为 Markdown 单独走「读文件 → 正则清洗 → 手动Response」分支,前端要在多组件中重复解析。本次变更的方向是彻底放弃 JSONL 协议与清洗逻辑,统一退化为原生 Markdown 链接渲染,净删 894 行、新增 62 行。

后端:artifacts 下载路径去清洗化

变更对象是 artifacts.py(路由前缀/api/threads/{id}/artifacts,见 CLAUDE.md 的 API 表格)。diff 涉及三处:

  1. 导入调整:删除import jsonimport reResponsefastapi改为从fastapi.responses导入(保留二进制 inline 返回用)。
  2. 删除两个清洗函数
# 被删除(变更前) def _extract_citation_urls(content: str) -> set[str]: """Extract URLs from <citations> JSONL blocks. Format must match frontend core/citations/utils.ts.""" urls: set[str] = set() for match in re.finditer(r"<citations>([\s\S]*?)</citations>", content): for line in match.group(1).split("\n"): line = line.strip() if line.startswith("{"): try: obj = json.loads(line) if "url" in obj: urls.add(obj["url"]) except (json.JSONDecodeError, ValueError): pass return urls def remove_citations_block(content: str) -> str: """Remove ALL citations from markdown (blocks, [cite-N], and citation links). Used for downloads.""" # 依次移除 <citations> 块、未闭合块、[cite-N] 标记、以及引用 URL 对应的外链

注释中明确写有「Format must match frontend core/citations/utils.ts」——这正是前后端私有格式耦合的直接证据:前端正则一旦变化,后端下载清洗就会静默失配。

  1. get_artifact下载分支简化:删除is_markdown判断以及「markdown 时读文件 +remove_citations_block+ 手工构造Response」的分支,?download=true一律走FileResponse
# 变更后:所有下载统一由 FileResponse 流式返回 if request.query_params.get("download"): return FileResponse( path=actual_path, filename=actual_path.name, media_type=mime_type, headers={"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"}, )

值得强调的是,当前仓库中该文件已经比 diff 记录的版本进一步演进:现在get_artifact把所有响应统一交给FileResponse流式处理,_read_artifact_payload只返回处置计划(file/inline_file),并支持 RFC 9110 的Range请求、由 Gateway 计算 SHA-256 作为ETag(供浏览器在非安全上下文跳过crypto.subtle),活跃内容类型(HTML/XHTML/SVG)强制附件下载(见 artifacts.py)。这次重构正是该演进的第一步——下载路径不再读取、改写文件内容,为后续纯流式返回扫清了障碍。同时 CLAUDE.md 中 API 表的描述同步从「download with citation removal」改为「file download」。

提示词层:删除<citations_format>协议

变更对象是 Lead Agent 提示词(prompt.py)与 general-purpose 子代理提示词(general_purpose.py):

  • Lead Agent:删除<citations_format>...</citations_format>整段(原约 243–266 行),内容包括 JSONL 块要求、Short Title完整链接的「CRITICAL」格式规则与示例;删除critical_reminders中「Web search citations」一条;删除apply_prompt_template中面向子代理编排模式的「Citations when synthesizing」提醒行。
  • general_purpose 子代理:删除其提示词中的<citations_format>整段;output_format第 2 条由「Key findings or results (with citation links when from web search)」改为「Key findings or results」。

从源码结构看,当前仓库中 Lead Agent 提示词的引用规范已经演进为内联citation:TITLE链接格式(prompt.py 中的<citations>区块:「Use Markdown link formatcitation:TITLEimmediately after the claim」,并要求报告末尾汇总 Sources 一节)——JSONL 结构化块被完全放弃,引用回归为对 Markdown 渲染器而言零特殊化的普通链接。

前端:消息与产物渲染统一为MarkdownContent

这是本次变更中触点最多的一层。核心动作是:新建一个无引用解析逻辑的纯 Markdown 渲染组件MarkdownContent,用它替换所有SafeCitationContent调用点,然后整目录删除core/citations与两个引用专用组件

新增markdown-content.tsx,删除safe-citation-content.tsxinline-citation.tsx

新增文件 markdown-content.tsx 在 diff 中是纯 Markdown 渲染薄封装(MessageResponse+streamdownPlugins,空内容返回null)。当前仓库中的版本已扩展为带流式体验的完整渲染层:useSmoothStreamingContent以 50ms 节拍、每次最少 64 字符做平滑揭示,StreamingPre/StreamingCode处理流式代码块,SafeMessageResponse+streamdownPluginsWithoutRawHtml规避 raw HTML(见 markdown-content.tsx)。其对外 API 恰与旧SafeCitationContent兼容(content/isLoading/rehypePlugins/className),因此各调用点只是组件名替换、props 不变。

被删除的两个组件:

  • safe-citation-content.tsx(原约 85 行):引用解析、loading 占位、renderBody/loadingOnlycleanContent/citationMap,已被MarkdownContent替代;
  • inline-citation.tsx(原约 289 行):createCitationMarkdownComponents,把[cite-N]/URL 渲染为可点击引用,仅产物预览使用。

artifact-file-detail.tsx:产物详情去引用化

变更对象 artifact-file-detail.tsx:

- const parsed = useParsedCitations(language === "markdown" ? (content ?? "") : ""); - const cleanContent = language === "markdown" && content ? parsed.cleanContent : (content ?? ""); - const contentWithoutCitations = language === "markdown" && content - ? contentWithoutCitationsFromParsed(parsed) : (content ?? ""); + const displayContent = content ?? "";
  • 预览分支不再经SafeCitationContent+renderBody间接层,直接<ArtifactFilePreview filepath threadId content={displayContent} language />
  • 复制按钮由clipboard.writeText(contentWithoutCitations)改为写原始displayContent;代码视图CodeEditorvaluecleanContent改为displayContent
  • ArtifactFilePreview签名删除cleanContent/citationMap两个 prop,内部不再调用createCitationMarkdownComponents,直接Streamdown渲染content
  • 同时删除React命名空间导入及core/citationsSafeCitationContentuseThread等全部相关 import。

消息侧四个组件的等价替换

组件变更点
message-group.tsx两处推理内容(ChainOfThoughtSteplabel)由SafeCitationContent改为MarkdownContent;内部ToolCall去掉rehypePluginsprop 及useThread/fileContentwrite_file分支删除 Markdown 预览块(isMarkdown+SafeCitationContent),只保留ChainOfThoughtStep+ path
message-list-item.tsx删除removeAllCitations导入,复制按钮clipboardDataremoveAllCitations(...)包裹改为直接取消息原始内容;正文渲染改为MarkdownContent(保留 KaTeX rehype 插件)
message-list.tsximport 与两处渲染由SafeCitationContent改为MarkdownContent,props 不变
subtask-card.tsxtask.result的渲染由SafeCitationContent改为MarkdownContent

删除core/citations目录与 i18n 键

core/citations三个文件全部删除,其导出面即旧机制的全部 API 边界:

  • index.ts(13 行):contentWithoutCitationsFromParsedextractDomainFromUrlisExternalUrlparseCitationsremoveAllCitationsshouldShowCitationLoadingsyntheticCitationFromLinkuseParsedCitationsCitation/ParseCitationsResult/UseParsedCitationsResult类型;
  • use-parsed-citations.ts(28 行):useParsedCitations(content)hook;
  • utils.ts(226 行):<citations>/[cite-N]解析、buildCitationMapremoveAllCitationscontentWithoutCitationsFromParsed

i18n 三处同步删除citations.loadingCitations/loadingCitationsWithCount命名空间(「Organizing citations...」/「正在整理 N 个引用...」),涉及 types.ts、en-US.ts、zh-CN.ts;三份前端文档(AGENTS.md、CLAUDE.md、README.md)的目录树中删去core/citations/一行,CLAUDE.md 的描述句删去「andcitations」;utils.ts 中externalLinkClassNoUnderline的注释从「For streaming / loading state when link may be a citation (no underline)」改为中性的「Link style without underline by default (e.g. for streaming/loading)」,导出值未变。

技能与 Demo 数据同步

  • github-deep-research/SKILL.md:第 6 条原则由「Cite inline - Reference sources near claims」改为「Reference sources - Add source references near claims where applicable」,措辞去引用化。
  • market-analysis/SKILL.md:核心能力、输入表、Data Citation、Citations & References 小节与完成检查项全部改为「references」表述,去掉[[N]](URL)内联引用格式要求,改为「使用 External Search Findings 时以 Markdown 链接引用来源」,References 章节仍按 GB/T 7714-2015 格式化。
  • Demo 线程数据(research_deerflow_20260201.md 与同目录thread.json):删除报告开头的 9 行<citations>…</citations>JSONL 块,正文从# DeerFlow Deep Research Report开始;thread.jsonwrite_fileargs.content同步去掉 JSONL 块,另有一处present_filesfilepaths由单行数组改为多行格式、文件末尾换行统一,消息顺序与结构未动。

当前仓库中已可验证 demo 产物不再含<citations>块——前端不再依赖它,Demo 数据也随之「去协议化」。

变更统计与架构意义

原文档给出的 diff 统计如下:

项目数量
修改文件18
新增文件1(markdown-content.tsx)
删除文件5(safe-citation-content.tsx、inline-citation.tsx、core/citations/* 共 3 个)
总行数变化+62 / -894

从这次变更可以提炼出三条对 DeerFlow(以及任何 LLM 应用前端)有参考价值的架构结论:

  1. 提示词协议应尽量退化为标准格式。JSONL<citations>块让「模型输出 → 后端下载清洗 → 前端解析渲染」三方都背上了同一份私有 schema 的维护成本(后端函数注释甚至要求「与前端 utils.ts 保持一致」)。删除后,引用就是普通 Markdown 链接,渲染链路零特殊化。
  2. 单一渲染入口收敛多组件。5 个调用点共享的SafeCitationContent(解析 + loading + renderBody 回调)被一个 props 兼容的MarkdownContent替换后,消息、推理、子任务、产物预览全部走同一条 Streamdown 渲染路径,后续演进(如当前仓库中平滑流式揭示、流式代码块、raw HTML 防护)只需改动一处。
  3. 下载与渲染解耦。Gateway 不再为 Markdown 下载维护一套正则清洗逻辑,FileResponse流式返回成为唯一路径——「给用户看什么」由前端决定,「交付什么字节」由后端决定,职责边界清晰。

需要说明的适用前提:本文描述的是 CODE_CHANGE_SUMMARY_BY_FILE.md 记录的变更集;其中部分文件(artifacts 路由、Lead Agent 提示词、MarkdownContent组件、i18n 键)在后续提交中又叠加了新能力(流式下载与 ETag、内联citation:TITLE引用规范、引用来源证据面板 sources.ts 与 citation-sources-panel.tsx),当前仓库代码是「本次瘦身 + 后续演进」的叠加结果,引用相关能力并未消失,而是换成了与标准 Markdown 渲染兼容的实现。

【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow

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

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

技术写作切忌凭空编造:4类可落地的技术选题方向指南

很抱歉&#xff0c;我无法根据这个项目标题生成一篇有真实技术价值和技术深度的CSDN技术博客。原因是&#xff1a;20op24gp24hp24bp 1压抑这组输入既不是一个可辨认的技术项目名称&#xff0c;也不包含任何可理解的技术上下文、功能描述、关键词或应用场景。如果强行围绕它“扩…

作者头像 李华
网站建设 2026/9/5 19:48:42

技术文档生成失败的原因与信息补充策略

标题 20op24gp24hp24bp 1压抑 无法识别为可具体展开的技术项目、开源模型、开发工具或部署方案&#xff0c;也没有提供项目正文、关键词、摘要描述或网络背景材料。为避免编造功能、参数和操作步骤&#xff0c;这篇 CSDN 技术博文本次无法生成。 如果你希望我继续&#xff0…

作者头像 李华
网站建设 2026/9/5 19:47:05

Emlog6.0资源模板源码深度解析与部署实战

简介&#xff1a;这是一套基于Emlog 6.0开发的完整资源类网站模板源码&#xff0c;面向个人站长、小型内容平台运营者及PHP入门开发者&#xff0c;解决从零建站、内容自动聚合与广告变现一体化落地的刚需。资源包共49个文件&#xff0c;涵盖13个核心PHP逻辑文件&#xff08;如i…

作者头像 李华
网站建设 2026/9/5 19:45:11

SG90连续旋转舵机:开环速度控制,不是角度舵机

如果你手里的元件 是 SG90 的“360 度连续旋转”版本&#xff0c;那么你多半会经历一个很拧巴的阶段&#xff1a;把原来驱动普通舵机的那套writeMicroseconds代码原样搬过来&#xff0c;发现舵机要么一直转&#xff0c;要么转起来没有停的意思&#xff0c;甚至你给它传了一个“…

作者头像 李华
网站建设 2026/9/5 19:42:07

免费开源中文字体霞鹜文楷完整指南:2万余字库与安装方法

免费开源中文字体霞鹜文楷完整指南&#xff1a;2万余字库与安装方法 【免费下载链接】LxgwWenKai An open-source Chinese font derived from Fontworks Klee One. 一款开源中文字体&#xff0c;基于 FONTWORKS 出品字体 Klee One 衍生。 项目地址: https://gitcode.com/Git…

作者头像 李华