字节跳动 DeerFlow:Agent Harness 怎么让大模型主动向用户提问?
åèè·³å¨ DeerFlowï¼Agent Harness æä¹è®©å¤§æ¨¡å主å¨åç¨æ·æé®ï¼
æ å 2026-08-19 0 é 读5åéå颿们ä¸ç´å¨ç Agent æ¯æä¹è°ç¨å·¥å ·ãæä¹è®°å½æ§è¡è¿ç¨çã
è¿æ¬¡æ¢ä¸ä¸ªåºæ¯ï¼
å¦æå¤§æ¨¡åéå°äºèªå·±æ æ³ç¡®å®çäºæ ï¼éè¦è®©ç¨æ·åéæ©ï¼Harness åºè¯¥æä¹è®¾è®¡ï¼
æ¯å¦ç¨æ·è¯´ï¼
帮æå建ä¸ä¸ªé¡¹ç®
使²¡æåè¯ Agentï¼
项ç®å«ä»ä¹ï¼
ç¨ React è¿æ¯ Vueï¼
è¦ä¸è¦åå§å Gitï¼
Agent æ¾ç¶ä¸è½æ°¸è¿é çã
å®éè¦å䏿¥é®ç¨æ·ã
1. LLM æä¹åè¯ Harnessï¼âæéè¦é®ç¨æ·âï¼
ä»å¤§æ¨¡åçè§è§æ¥çï¼å®çæ£è½å¤æç¥å使ç¨çä¸è¥¿å ¶å®å¾æéï¼
Prompt
+
Messages
+
Tools
妿æä»¬å¸ææ¨¡å主å¨åèµ·ä¸ä¸ªå¨ä½ï¼æèªç¶çæ¹å¼è¿æ¯ Toolã
æä»¬å¹³æ¶è®¾è®¡ Agent åºæ¬ä¹æ¯è¿ä¸ªæè·¯ï¼
LLM = 大è
Tool = æè
æ³å°
â
Tool Call
â
åå°
æä»¥ DeerFlow ä¹å®ä¹äºä¸ä¸ªï¼
@tool("ask_clarification", parse_docstring=True, return_direct=True)
def ask_clarification_tool(
question: str,
clarification_type: Literal[
"missing_info",
"ambiguous_requirement",
"approach_choice",
"risk_confirmation",
"suggestion",
],
context: str | None = None,
options: list[str] | None = None,
fields: list[ClarificationFormField] | None = None,
) -> str:
"""Ask the user for clarification when you need more information to proceed."""
...
第ä¸ç¼çèµ·æ¥å¾æ£å¸¸ï¼
Agent 䏿¸
æ¥
â
è°ç¨ ask_clarification
â
询é®ç¨æ·
ä½ä»ç»çä¼åç°ä¸ä¸ªå¥æªçå°æ¹ï¼
...
è¿ä¸ª Tool æ ¹æ¬æ²¡æçæ£çæ§è¡ä»£ç ã
é£å®ä¹å®æä»ä¹æä¹ï¼
å ¶å®è¿éç Tool æ´åæ¯å¨å®ä¹ä¸ä»½ LLM å Harness ä¹é´çåè®®ï¼
彿¨¡åéè¦ç¨æ·è¡¥å ä¿¡æ¯æ¶ï¼ä¸è¦èªå·±é便è¾åºä¸æ®µæåï¼èæ¯è°ç¨
ask_clarificationï¼å¹¶æç §åºå®ç»æåè¯ Harnessï¼æè¦é®ä»ä¹ãæåªäºé项ãéè¦åªäºå段ã
æ¯å¦ï¼
ask_clarification(
question="è¯·éæ©é¡¹ç®ææ¯æ ",
clarification_type="approach_choice",
options=["React", "Vue", "Svelte"],
)
模åè´è´£è¡¨è¾¾ï¼
âæéè¦ç¨æ·åéæ©â
è³äºè¿ä»¶äºæ æåæä¹å±ç¤ºç»ç¨æ·ï¼å¹¶ä¸æ¯ Tool èªå·±è´è´£ã
2. Tool æ²¡ææ§è¡ï¼Middleware æå®æ¥ç®¡äº
çæ£å¤ç ask_clarification çå°æ¹å¨ï¼
clarification_middleware.py
æ ¸å¿ä»£ç å ¶å®å°±ä¸¤å¥ï¼
if request.tool_call.get("name") != "ask_clarification":
return handler(request)
return self._handle_clarification(request)
æ®é Toolï¼
LLM
â
ToolCall
â
Middleware
â
handler(request)
â
çæ£æ§è¡ Tool
使¯ ask_clarification ä¸ä¸æ ·ï¼
LLM
â
ask_clarification ToolCall
â
Middleware
â
ä¸è°ç¨ handler
â
ç´æ¥æ¥ç®¡
æä»¥è¿ä¸ª Tool ä¸¥æ ¼æ¥è¯´å¹¶ä¸æ¯ä¸ºäºæ§è¡æä¸ª Python 彿°ã
宿´åä¸ä¸ªï¼
LLM â Harness
çç»æåæ§å¶ä¿¡å·ã
模ååªè´è´£è¯´ï¼
æç°å¨éè¦äººç±»è¾å ¥ã
Harness åå³å®ï¼
飿åºè¯¥å±ç¤ºä¸ä¸ªè¾å ¥æ¡ãéæ©æ¡ï¼è¿æ¯æ´ä¸ªè¡¨åï¼å½å Agent è¦ä¸è¦åæ¢ï¼
è¿ä¹æ¯ä¸ºä»ä¹ DeerFlow æ²¡ææææé»è¾é½å¡è¿ Toolã
Tool å®ä¹çæ¯ï¼
模åæä¹è¡¨è¾¾æå¾
Middleware å¤ççæ¯ï¼
Runtime æ¶å°è¿ä¸ªæå¾ä»¥åæä¹å
3. ç¨æ·çé®é¢ï¼ä¸ä¸å®åªæ¯âä¸å¥è¯â
çæ£å¤ç请æ±çæ¯ï¼
_handle_clarification()
ä½å¨è¿å ¥è¿ä¸ªå½æ°ä¹åï¼DeerFlow 并䏿¯ç®åå°ï¼
print(question)
å ä¸ºç¨æ·è¾å ¥å¯è½æå¾å¤å½¢å¼ã
DeerFlow å®ä¹äºä¸å¥è¡¨ååæ®µï¼
class ClarificationFormField(TypedDict, total=False):
name: Required[str]
label: str
type: Literal[
"text",
"textarea",
"number",
"select",
"multi_select",
"checkbox",
"date",
]
required: bool
options: list[str]
placeholder: str
æä»¥ Agent å¯ä»¥é®ä¸ä¸ªç®åé®é¢ï¼
项ç®å«ä»ä¹ï¼
ä¹å¯ä»¥è®©ç¨æ·éï¼
ä½ å¸æä½¿ç¨åªä¸ªæ¡æ¶ï¼
â React
â Vue
â Svelte
çè³å¯ä»¥ä¸æ¬¡çæä¸ä¸ªè¡¨åï¼
项ç®åç§°
[________________]
ææ¯æ
[ React â¼ ]
åå§å Git
[â]
æªæ¢æ¥æ
[ 2026-08-20 ]
ä¾å¦æ¨¡åå¯è½äº§çï¼
{
"question": "请补å
项ç®é
ç½®",
"fields": [
{
"name": "project_name",
"label": "项ç®åç§°",
"type": "text",
"required": true
},
{
"name": "framework",
"label": "ææ¯æ ",
"type": "select",
"options": ["React", "Vue", "Svelte"],
"required": true
},
{
"name": "init_git",
"label": "åå§å Git",
"type": "checkbox"
}
]
}
妿åç«¯æ¯æè¿ç§ Schemaï¼å°±å¯ä»¥ç´æ¥æ¸²ææçæ£ç Formã
è¿æ¾ç¶æ¯è®©ç¨æ·èªå·±åå¤ï¼
项ç®å« xxxï¼æ¡æ¶é Reactï¼ç¶å Git è¦åå§å
ä½éªå¥½å¾å¤ã
4. 为ä»ä¹æ¢æ UI Schemaï¼åæçº¯ææ¬ï¼
é®é¢æ¥äºã
DeerFlow ä¸ä¸å®åªè¿è¡å¨èªå·±ç Web 页é¢ã
å®å¯è½æ¥å ¥ï¼
Web
IM
Slack
Feishu
GitHub
å
¶ä»å®¢æ·ç«¯
并䏿¯ææå®¢æ·ç«¯é½æ¯æå¨æ Formã
æä»¥ DeerFlow 没æåªçæä¸ä»½ UI Schemaï¼èæ¯åæ¶åå¤äºä¸¤ä»½å 容ã
ä¸ä»½æ¯ç»äººçççº¯ææ¬
æ¾å¨ï¼
ToolMessage.content
ä¾å¦ï¼
â 请补å
项ç®é
ç½®
1. 项ç®åç§° (required)
2. ææ¯æ (required) â options: React / Vue / Svelte
3. åå§å Git
Please reply with a value for each field.
å³ä½¿å®¢æ·ç«¯å®å ¨ä¸è®¤è¯ DeerFlow ç UI åè®®ï¼ä¹è³å°å¯ä»¥æè¿æ®µæåå±ç¤ºåºæ¥ã
å¦ä¸ä»½æ¯ç»æ¯æ UI çå端ï¼
ToolMessage.artifact["human_input"]
ä¾å¦ï¼
{
"version": 2,
"kind": "human_input_request",
"source": "ask_clarification",
"question": "请补å
项ç®é
ç½®",
"input_mode": "form",
"fields": [
{
"name": "project_name",
"label": "项ç®åç§°",
"type": "text",
"required": true
},
{
"name": "framework",
"label": "ææ¯æ ",
"type": "select",
"required": true,
"options": [
{
"id": "framework-option-1",
"label": "React",
"value": "React"
},
{
"id": "framework-option-2",
"label": "Vue",
"value": "Vue"
}
]
}
]
}
äºæ¯å䏿¬¡ clarificationï¼
ask_clarification
â
âââââââââââââââ´ââââââââââââââ
â â
ToolMessage.content artifact.human_input
â â
çº¯ææ¬ fallback UI Schema
â â
䏿¯æ UI ç客æ·ç«¯ æ¯æ UI çå端
å®ä¸æ¯ç»´æ¤ä¸¤å¥ä¸å¡é»è¾ã
èæ¯ï¼
ç»æå UI + çº¯ææ¬é级ã
5. æåï¼Agent 为ä»ä¹ä¼å䏿¥ï¼
æ ¼å¼åå®ä¹åï¼DeerFlow ä¼çæä¸ä¸ªç¹æ®ç ToolMessageï¼
tool_message = ToolMessage(
id=request_id,
content=formatted_message,
tool_call_id=tool_call_id,
name="ask_clarification",
artifact={
"human_input": human_input_payload
},
)
ç¶åè¿åï¼
return Command(
update={"messages": [tool_message]},
goto=END,
)
è¿éçï¼
goto=END
éå¸¸å ³é®ã
å¦ææ²¡æå®ï¼æµç¨å¯è½åæï¼
LLM
â
ask_clarification
â
çæ ToolMessage
â
LLM åç»§ç»è¿è¡
â
ç»§ç»è°ç¨å·¥å
·
使¨¡åææå·²ç»è¡¨ç¤ºï¼
æç¼ºä¿¡æ¯ï¼éè¦ç¨æ·åçã
æä»¥æ£ç¡®çæµç¨åºè¯¥æ¯ï¼
LLM
â
â ToolCall
â¼
ClarificationMiddleware
â
âââââââââââââ´ââââââââââââ
â â
æ®é Tool ask_clarification
â â
handler() normalize
â
âââââââââââââââ´ââââââââââââââ
â â
ToolMessage.content artifact.human_input
â â
ææ¬ fallback UI Schema
â â
âââââââââââââââ¬ââââââââââââââ
â
Command(update=...)
â
goto=END
â
âââââ å½å Run ç»æ âââââ
â
å端å±ç¤ºè¡¨å
â
ç¨æ·è¾å
¥
â
ä¸ä¸è½® Agent
ç»æå½åè¿ä¸æ¬¡ Agent Runã
ç¨æ·å¡«åå®ä»¥åï¼åéè¿ä¸ä¸è½®æ¶æ¯éæ°é©±å¨ Agentã
å¨ Agent Harness éï¼ToolCall ä¸ä¸å®åªä»£è¡¨âè°ç¨ä¸ä¸ªå½æ°âï¼å®ä¹å¯ä»¥æä¸º LLM å Runtime ä¹é´çä¸ç§æ§å¶åè®®ã
模åè´è´£å³å®ä»ä¹æ¶åéè¦äººã
Harness è´è´£å³å®äººåºè¯¥çå°ä»ä¹ï¼ä»¥åä»ä¹æ¶åææ§å¶æäº¤åºå»ã
Aitishiku.com