Deer-Flow(Deep Exploration and Efficient Research Flow)是字节跳动开源的深度工作 Agent,官方把 2.0 版本定位为一套 super agent harness:编排子 Agent、管理长期记忆、提供沙箱、挂载可扩展的技能工具箱。它和常见的大模型聊天产品有明显差异——不只是”回答问题”,而更像一个被严格约束、能分解任务并调用工具的执行系统。
我真正关心的是它背后的 Prompt 设计:它是怎么把”会聊天的模型”约束成”能执行的 Agent”的?本文是拆解 Deer-Flow 的第一篇,重点不放在 UI,而放在主 Agent 的系统提示词上。换句话说,要回答的是:Deer-Flow 的 Prompt,到底和我们平时在网页端直接聊天有什么不同。
分析方法
为了把这个问题看清楚,拆解按两步走。
第一步:改代码,把真实下发的 Prompt 落盘
在运行链路里增加日志,把 LLM 对话过程中真正下发的 Prompt 保存下来。这样拿到的不是文档里的示例,而是线上实际生效的完整文本。
第二步:回看代码,分析 Prompt 设计
与大模型对话的 Prompt 模板定义在 backend/packages/harness/deerflow/agents/lead_agent/prompt.py。其中 SYSTEM_PROMPT_TEMPLATE 接近 200 行,已经不是简单的几段角色设定,而是一份完整的 Agent 行为规约。
lead_agent/prompt.py 本质上是 Deer-Flow 主 Agent 的 system prompt assembler。它把身份、长期记忆、技能系统、工具发现、目录约束、citation 规范、clarification 策略、subagent 编排策略统一组装成一份最终 system prompt,再交给 create_agent() 使用。这份 prompt 既定义”你是谁”,也定义”你怎么工作”,还定义”你能访问什么、必须遵守什么规则”。
SYSTEM_PROMPT_TEMPLATE 的整体结构
从功能分层看,系统提示词大致是六层:
┌──────────────────────────────────────────────────────┐
│ 1. 角色层 Role / Soul / MemoryContext │
│ 2. 思考层 Thinking Style │
│ 3. 流程层 Clarification System(控制流引擎) │
│ 4. 能力层 Skills / 工具发现 │
│ 5. 环境层 Working Directory + Tools │
│ 6. 输出层 Response Style + Citation │
│ │
│ 兜底层 Critical Reminders + Current Date │
└──────────────────────────────────────────────────────┘
对应到模板标签,大致由以下内容按序拼装:
<role>{soul}(可选){memory_context}(可选)<thinking_style><clarification_system>{skills_section}{deferred_tools_section}{subagent_section}<working_directory><response_style><citations><critical_reminders>- 末尾额外追加
<current_date>
其中 {soul}、{memory_context}、{skills_section}、{subagent_section} 等都是条件注入的,是否出现取决于配置和运行时的开关状态。
重点分析的几个核心模块
下面重点看 memory_context、thinking_style、clarification_system、response_style、citations、critical_reminders 这几块提示词的构造方式。
memory_context:不是塞原始 JSON,而是二次整理的摘要
memory 注入函数是 _get_memory_context(),它会依次:
- 读取 memory 配置
- 检查
memory.enabled与memory.injection_enabled - 加载 memory 数据
- 格式化为文本
- 包成
<memory> ... </memory>
涉及的具体依赖:
| 职责 | 位置 |
|---|---|
| 读取 memory 配置 | config/memory_config.py 的 get_memory_config() |
| 加载 memory 数据 | agents/memory/updater.py 的 get_memory_data() |
| 格式化为注入文本 | agents/memory/prompt.py 的 format_memory_for_injection() |
format_memory_for_injection() 会把 memory 压缩成三大块:
User ContextHistoryFacts
并受 max_injection_tokens 限制。
所以这里的 memory 不是原样塞一整份 memory.json,而是二次整理后的紧凑摘要。这一点很关键:长期记忆如果没有预算约束和结构压缩,很快就会把上下文窗口吃掉,反而降低模型表现。
<thinking_style>:把”先想后做”写进系统层
这是 Agent 的内部推理行为约束层,核心要求包括:
- 先思考再行动
- 先检查是否缺信息或有歧义
- 如果需要澄清,必须优先澄清
- thinking 不等于 final answer
- 必须输出对用户可见的实际响应
当 subagent_enabled=true 时,这里还会额外插入 {subagent_thinking},由 apply_prompt_template() 动态生成。这部分会要求模型先数清楚 sub-task 数量,并严格控制每轮最多多少个 task 调用。
关键原文与含义对照:
| 原文 | 含义 |
|---|---|
Think concisely and strategically about the user's request BEFORE taking action | 采取行动之前,先对请求做简洁且有策略的思考 |
Break down the task: What is clear? What is ambiguous? What is missing? | 先拆解任务:哪些清楚、哪些有歧义、哪些缺失 |
If anything is unclear ... you MUST ask for clarification FIRST | 只要有不清楚、缺失、可多种解释的地方,必须先澄清,不能直接干 |
Never write down your full final answer or report in thinking process, but only outline | thinking 阶段只做提纲式规划,不把最终报告写进”思考” |
After thinking, you MUST provide your actual response to the user | 思考结束后必须给出用户可见的正式回答 |
这部分的设计含义有三层:
- 在系统 prompt 层把”先想后做”固定下来,避免模型一上来就盲目调用工具。
- 把”需求诊断”显式化,让模型在任何任务开始前都先判断信息完备性。
- 要求 thinking 与 final answer 分离,避免把内部规划直接泄漏成最终交付物。
<clarification_system>:整套 Prompt 里最强的约束
这是 prompt 中最强约束之一。它把工作流显式写成 CLARIFY → PLAN → ACT,并列出了 5 类必须先澄清的场景,还给出了 ask_clarification(...) 的工具调用格式示例。
也就是说,Deer-Flow 的设计不是”需要时可以澄清”,而是在系统 prompt 层硬编码了澄清优先级。
关键原文与含义对照:
| 原文 | 含义 |
|---|---|
WORKFLOW PRIORITY: CLARIFY → PLAN → ACT | 工作流优先级固定为:先澄清,再计划,最后执行 |
Clarification ALWAYS comes BEFORE action | 澄清必须先于行动,不能边做边问 |
MANDATORY Clarification Scenarios | 以下场景下澄清不是建议,而是强制要求 |
DO NOT start working and then ask for clarification mid-execution | 不允许先开工再中途补问 |
DO NOT make assumptions when information is missing | 信息缺失时禁止自行脑补 |
If you identify the need for clarification in your thinking, you MUST call the tool IMMEDIATELY | 只要内部判断需要澄清,就必须立刻调用 ask_clarification |
五类强制澄清场景:
| 场景 | 含义 |
|---|---|
missing_info | 缺少完成任务所必须的信息 |
ambiguous_requirement | 需求本身可被多种方式理解 |
approach_choice | 存在多种合理技术路线,需要用户拍板 |
risk_confirmation | 操作具有破坏性、不可逆性或高风险 |
suggestion | 模型有建议方案,但想先获得用户许可 |
这块的真正作用,不只是让回答更谨慎,而是把 Agent 的执行策略改造成:默认不做猜测、默认不抢跑、默认把需求确认看得比执行速度更重要。也正因为这块写得很重,Deer-Flow 对”先澄清后执行”的偏好会明显强于很多普通聊天型 assistant。
代价也很直接:简单任务上会多一轮交互。这是设计上的主动取舍,不是缺陷。
<response_style>:输出层的收束
这是最终回答风格约束,三个要点:
| 原文 | 含义 |
|---|---|
Clear and Concise: Avoid over-formatting unless requested | 表达清楚简洁,除非用户要求,否则不要过度排版 |
Natural Tone: Use paragraphs and prose, not bullet points by default | 语气自然,默认优先用段落和自然语言,而不是满屏项目符号 |
Action-Oriented: Focus on delivering results, not explaining processes | 重点是交付结果,而不是长篇解释自己做事的过程 |
这层不是执行规则,而是输出风格收束,体现的是对”工程型回答”的偏好:
- 默认不追求花哨格式,而追求信息密度。
- 默认不把回复写成官样清单,而尽量像自然交流。
- 默认把”结果导向”放在首位,减少无价值的过程汇报。
所以即便前面的 prompt 有很多严格约束,最后输出层仍被要求看起来像”一个直接、自然、能干活的工程助手”。
<citations>:把引用规范硬编码进 System Prompt
这是 Web 研究任务的重要结构块,强制要求:
- 使用
web_search/web_fetch后必须给 citation - 行内引用格式是
[citation:TITLE](URL) - 报告末尾要有
Sources段 - Sources 段必须是可点击的 markdown link
关键原文与含义对照:
| 原文 | 含义 |
|---|---|
CRITICAL: Always include citations when using web search results | 只要用了 web search 或外部信息源,就必须带引用 |
Format: [citation:TITLE](URL) immediately after the claim | 行内引用格式固定,且要紧跟在对应结论后面 |
Also collect all citations in a "Sources" section at the end | 报告末尾必须集中列出 Sources 来源区 |
DO NOT write research content without citations | 不允许写没有出处支撑的研究性内容 |
这块要解决两个问题:
- 防止模型在 research / report 类任务里写出”像研究、但没出处”的内容。
- 把”可追溯性”从可选项变成硬约束。
它很像一套嵌入在 system prompt 里的轻量引用规范:行内引用解决”这一句从哪来”,Sources 段解决”本次整体用了哪些资料”。因此研究类输出在理想情况下不该只是”总结”,而应该是带来源链路的总结。
<critical_reminders>:最后的收口层
这是收口层提醒,集中重复最关键的执行原则:
| 条目 | 含义 |
|---|---|
Clarification First | 永远先确认不清楚的点,再执行 |
Skill First | 复杂任务优先加载 skill,不要裸奔执行 |
Progressive Loading | 资源按需逐步加载,不要一次性灌入所有上下文 |
Output Files | 最终交付文件必须放在 /mnt/user-data/outputs |
Language Consistency | 保持和用户一致的语言 |
Always Respond | thinking 是内部的,最终必须给用户一个可见回答 |
当 subagent_enabled=True 时,这里还会注入 {subagent_reminder},再次强调 orchestrator 模式和并发上限。
这块的作用像”最后一道总开关”:它并不引入新能力,而是把最关键的原则再压一遍,防止模型在长 prompt 的后半程遗忘前面的约束。几个值得注意的点:
Skill First说明 skill 被定位为复杂任务的默认入口,而不是偶尔使用的插件。Progressive Loading与前面的 skill 系统配套,系统不鼓励一次性把所有参考资料都塞给模型。Language Consistency是很实用的 UX 约束,避免中英文混乱切换。Always Respond是防呆条款,防止模型停留在内部 reasoning 或只做工具动作却不回话。
总结
如果把整个 Prompt 看成一个多层控制系统,那么 critical_reminders 就是最后的”收口层”和”兜底层”。把前面几个模块连起来看,Deer-Flow 的设计思路就很清楚了:它并不是单纯给模型补充更多背景信息,而是在系统提示词里同时规定角色、流程、工具边界、输出风格和引用要求。
这才是 Deer-Flow 的 Prompt 最值得关注的地方。它真正想解决的不是”让模型回答得更聪明”,而是”让模型在复杂任务里更像一个可靠的执行系统”。从这个角度看,Prompt 在 Agent 体系里已经不只是提示词,更接近一份运行时行为规范。
几个可以迁移到自建 Agent 的经验:
- 先澄清后执行要写在系统层并给出强制场景,只在工具描述里提一句几乎不会生效。
- 记忆注入必须做预算与压缩,原始全量注入会反噬效果。
- thinking 与 final answer 必须显式分离,否则内部规划容易变成泄漏的交付物。
- 长 prompt 需要收口层,把最关键的原则在末尾重复一遍,抵消注意力衰减。
- 输出风格也是提示词的一部分,默认段落而非清单、默认结果而非过程,会显著改变体感。
后续如果继续拆 Deer-Flow,我会再看它的 skill system、subagent orchestration 和 memory 更新机制。这几部分和本文分析的系统 Prompt 结合起来,才更接近一个完整的 Agent 执行框架。