構(gòu)化方式向人類發(fā)起輸入請求的完整機(jī)制)
Dify Agent Ask Human Layer讓模型以結(jié)構(gòu)化方式向人類發(fā)起輸入請求的完整機(jī)制【免費(fèi)下載鏈接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/di/dify本篇圍繞 Dify Agent 的 ask-human layerdify.ask_human展開它向模型暴露一個 external deferred tool使 Agent 能在當(dāng)前 run 無法繼續(xù)時以結(jié)構(gòu)化請求形式向人類要信息并將該請求作為deferred_tool_call隨run_succeeded事件返回給客戶端。讀完本文你將掌握如何組裝包含該 layer 的CreateRunRequest、理解每個配置字段的護(hù)欄語義、處理延遲工具調(diào)用deferred call并攜人類結(jié)果恢復(fù) run 的完整閉環(huán)以及各故障癥狀的排查方法。層契約它做什么、不做什么ask-human layer 的定位是“模型可見的工具層”而不是消息投遞系統(tǒng)。層契約要點(diǎn)如下屬性值Type iddify.ask_human常用 layer 名稱ask_humanConfig DTODifyAskHumanLayerConfig模型可見工具默認(rèn)ask_human可用tool_name配置工具類型pydantic-aiexternaldeferred tool終止事件run_succeeded終止載荷分支run_succeeded.data.deferred_tool_call關(guān)鍵在于“不暫?!钡恼Z義Agent run 不會進(jìn)入 paused 狀態(tài)。當(dāng)模型調(diào)用 ask-human 工具時當(dāng)前 run 以deferred_tool_call代替普通output成功結(jié)束把延遲調(diào)用轉(zhuǎn)成面向人類的流程、收集結(jié)果、再發(fā)起帶deferred_tool_results的新 run全部是客戶端的職責(zé)。從源碼 layer.py 的模塊注釋也能印證這一設(shè)計layer 只貢獻(xiàn)“一個可選 external 工具 一段 prompt 提示”工具在首次 run 中絕不執(zhí)行 Python下游系統(tǒng)自行決定投遞、接收人、超時與授權(quán)?;A(chǔ)用法把 layer 加入 run composition將 ask-human layer 與 prompt、history、LLM 以及可選的結(jié)構(gòu)化輸出 layer 放在同一個 composition 中from agenton_collections.layers.plain import PromptLayerConfig from agenton_collections.layers.pydantic_ai import PYDANTIC_AI_HISTORY_LAYER_TYPE_ID from dify_agent.layers.ask_human import DIFY_ASK_HUMAN_LAYER_TYPE_ID, DifyAskHumanLayerConfig from dify_agent.layers.dify_plugin import DifyPluginLLMLayerConfig from dify_agent.layers.execution_context import ( DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID, DifyExecutionContextLayerConfig, ) from dify_agent.protocol import DIFY_AGENT_HISTORY_LAYER_ID, DIFY_AGENT_MODEL_LAYER_ID from dify_agent.protocol.schemas import CreateRunRequest, RunComposition, RunLayerSpec request CreateRunRequest( compositionRunComposition( layers[ RunLayerSpec( nameprompt, typeplain.prompt, configPromptLayerConfig( prefixYou can ask a human only when the missing decision is required to continue., userReview the deployment plan and proceed only after getting the required approval., ), ), RunLayerSpec( nameexecution_context, typeDIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID, configDifyExecutionContextLayerConfig( tenant_idreplace-with-tenant-id, user_idreplace-with-user-id, user_fromaccount, app_idreplace-with-app-id, agent_modesingle_step, invoke_fromdebugger, ), ), RunLayerSpec( nameDIFY_AGENT_HISTORY_LAYER_ID, typePYDANTIC_AI_HISTORY_LAYER_TYPE_ID, ), RunLayerSpec( nameask_human, typeDIFY_ASK_HUMAN_LAYER_TYPE_ID, configDifyAskHumanLayerConfig( max_fields4, max_actions2, allowed_field_types[paragraph, select], allow_file_fieldsFalse, ), ), RunLayerSpec( nameDIFY_AGENT_MODEL_LAYER_ID, typedify.plugin.llm, deps{execution_context: execution_context}, configDifyPluginLLMLayerConfig( plugin_idlanggenius/openai, model_provideropenai, modelgpt-5.2, ), ), ] ) )組合中各 layer 的角色plain.prompt負(fù)責(zé)業(yè)務(wù)指令execution_context提供租戶/用戶/應(yīng)用上下文PYDANTIC_AI_HISTORY_LAYER_TYPE_ID提供消息歷史dify.plugin.llm是模型 layer通過deps消費(fèi) execution_context layer。history layer 是恢復(fù)語義的硬前提。只要預(yù)期在人類回答后繼續(xù)對話就必須包含 history layer。懸而未決的工具調(diào)用被存放在 pydantic-ai 消息歷史里因此恢復(fù) run 需要兩樣?xùn)|西上一次返回的session_snapshot以及保持 history layer 仍存在的同一邏輯 composition。runner 側(cè)對此有明確校驗(yàn)——見 runner.pyif deferred_tool_results is not None and history_layer is None: raise AgentRunValidationError( Deferred tool results require a history layer with prior message history. )此外 composition 層面只允許存在一個 ask-human layervalidate_ask_human_layer_compositionlayer.py會拒絕同名 type 的多個 layer。配置字段與雙層護(hù)欄DifyAskHumanLayerConfig只控制“面向模型的工具身份與護(hù)欄”刻意不包含任何投遞配置。字段定義見 configs.py字段類型默認(rèn)值含義enabledboolTrue為 false 時該 layer 既不暴露工具也不注入 prompt 引導(dǎo)tool_namestrask_human模型可見的工具名必須是合法標(biāo)識符tool_descriptionstr \| None默認(rèn)描述文本可選的模型可見工具描述max_fieldsint8模型可請求的字段數(shù)上限0表示僅允許動作型請求max_actionsint4模型可請求的人類動作數(shù)上限allowed_field_typeslist[paragraph \| select \| file \| file-list][paragraph, select]運(yùn)行時校驗(yàn)接受的字段類型allow_file_fieldsboolFalse未開啟時文件字段類型直接拒絕開啟后還須列入allowed_field_typesmax_markdown_charsint8000可選markdown正文的最大長度max_question_charsint1000必填question的最大長度max_field_label_charsint120每個字段 label 的最大長度max_action_label_charsint80每個動作 label 的最大長度配置上限之上還有服務(wù)端硬上限兩者取較小值生效若配置超過硬上限請求校驗(yàn)階段即失敗、run 無法執(zhí)行。硬上限常量定義在 configs.py字段硬上限max_fields16max_actions8max_markdown_chars20000max_question_chars4000max_field_label_chars200max_action_label_chars120實(shí)現(xiàn)上有兩處值得注意tool_name必須是合法標(biāo)識符。字段校驗(yàn)器用正則^[A-Za-z_][A-Za-z0-9_]*$全匹配校驗(yàn)configs.py非法命名會在配置校驗(yàn)時直接報錯。文件字段的“雙開關(guān)”策略。_validate_file_field_policy模型校驗(yàn)器要求只有當(dāng)allow_file_fieldsTrue且allowed_field_types中包含file/file-list時文件字段才合法allow_file_fieldsFalse卻在允許列表里寫了文件類型配置本身就會被拒絕configs.py。文件字段變體目前屬于“為前向兼容預(yù)留的詞匯表”默認(rèn)不開放。layer 會把這些上限自動轉(zhuǎn)寫成 prompt 提示。build_prompt_hintlayer.py生成的文本包含允許的字段類型、文件上傳是否啟用、字段/動作數(shù)上限、question/markdown與 label 長度限制并告知模型“若省略 actions系統(tǒng)會自動補(bǔ)一個 primary 樣式的 Submit 動作”??蛻舳艘虼藷o需在系統(tǒng)提示里重復(fù)羅列限制但可追加業(yè)務(wù)側(cè)指導(dǎo)例如何時才適合問人。模型能請求什么AskHumanToolArgs 契約啟用后layer 暴露一個 external deferred tool其參數(shù)形狀為AskHumanToolArgsschema.py字段類型含義titlestr \| None面向人類請求的可選短標(biāo)題questionstr必填的問題/指令不能為空白markdownstr \| None可選的較長 Markdown 正文應(yīng)按不可信的用戶可見內(nèi)容對待fieldslist[AskHumanField]供人類填寫的可選結(jié)構(gòu)化字段name 全局唯一actionslist[AskHumanAction]可選的動作按鈕若省略Dify Agent 歸一化為單個 primarySubmit動作urgencynormal \| high給下游系統(tǒng)的提示不是投遞策略支持的字段變體判別字段為typeparagraph自由文本輸入支持placeholder、defaultselect單選輸入選項(xiàng)value必須非空且唯一default必須命中某個選項(xiàng)值schema.pyfile單文件輸入僅在允許文件字段時可用file-list多文件輸入僅在允許文件字段時可用可配max_filesschema.py。所有字段和動作都執(zhí)行extraforbid即不允許攜帶未知屬性字段name與動作id必須滿足標(biāo)識符規(guī)則。工具參數(shù)在模型調(diào)用后還會被再校驗(yàn)一次。校驗(yàn)入口_validate_tool_argslayer.py捕獲ValidationError/ValueError并轉(zhuǎn)成ModelRetry——也就是說模型一次越界的調(diào)用不會直接終結(jié) run而是觸發(fā)模型帶著錯誤信息重試直到產(chǎn)出合法請求后才可能發(fā)出終止成功事件。歸一化函數(shù)_validate_and_normalize_tool_args具體執(zhí)行的護(hù)欄包括字段數(shù)不超過max_fields動作為空時補(bǔ)默認(rèn)Submit動作數(shù)不超過max_actionsquestion/markdown/各 label 的長度上限字段類型必須在allowed_field_types內(nèi)未開allow_file_fields時拒絕文件字段layer.py。另一個值得理解的細(xì)節(jié)工具函數(shù)本體_never_executed_tool只會拋出RuntimeErrorlayer.py因?yàn)樵摴ぞ咄ㄟ^_prepare_tool_definition被改寫成kindexternal的 deferred 工具layer.py參數(shù) schema 直接取自AskHumanToolArgs.model_json_schema()。它的設(shè)計意圖就是“永遠(yuǎn)不該在首次 run 中執(zhí)行”真正的返回值來自人類經(jīng)后續(xù) run 注入。處理延遲的人類請求像處理普通事件一樣流式或輪詢 run 事件。成功的最終回答帶event.data.output成功的人類請求帶event.data.deferred_tool_call兩者恰好只有一個分支被置值runner 中以result_kind區(qū)分見 runner.pydeferred_call None snapshot None async for event in client.stream_events(run_id): if event.type ! run_succeeded: continue snapshot event.data.session_snapshot if event.data.deferred_tool_call is not None: deferred_call event.data.deferred_tool_call else: final_output event.data.output break if deferred_call is not None: # 渲染你自己的面向人類的表單、入隊(duì)通知、暫停外層工作流 # 或把請求存起來稍后處理。Dify Agent 不負(fù)責(zé)這部分。 print(deferred_call.tool_call_id, deferred_call.args)典型的延遲載荷長這樣{ tool_call_id: call_01H..., tool_name: ask_human, args: { title: Deployment approval, question: Can we deploy version 2026.06.10 to production now?, fields: [ { type: paragraph, name: comment, label: Approval comment, required: false } ], actions: [ {id: approve, label: Approve, style: primary}, {id: reject, label: Reject, style: destructive} ], urgency: normal }, metadata: { layer_type: dify.ask_human, tool_name: ask_human, schema_version: 1 } }metadata的構(gòu)造對應(yīng)build_deferred_tool_call_payloadlayer.py其中schema_version當(dāng)前為 1該方法還強(qiáng)制三條約束不支持 approval 請求、每次 run 恰好一個 deferred call當(dāng)前版本為 MVP 限制、工具名必須與配置的tool_name一致——這三條正是后續(xù)排障表中故障癥狀的直接來源。安全提醒a(bǔ)rgs是模型生成的內(nèi)容。渲染給最終用戶之前必須做校驗(yàn)與凈化尤其markdown字段按不可信的用戶可見內(nèi)容對待。用人類結(jié)果恢復(fù) run客戶端收集到人類答案后用三要素創(chuàng)建新 run上一次的session_snapshot仍然包含 history layer 與 ask-human layer 的匹配 composition同名同序deferred_tool_results.calls[tool_call_id]中放入人類結(jié)果from dify_agent.layers.ask_human import AskHumanToolResult from dify_agent.protocol import DeferredToolResultsPayload human_result AskHumanToolResult( statussubmitted, action{id: approve, label: Approve}, values{comment: Approved for the planned window.}, messageThe human approved the deployment., ) resume_request CreateRunRequest( compositioncomposition_with_same_layer_names_and_order, session_snapshotsnapshot, deferred_tool_resultsDeferredToolResultsPayload( calls{deferred_call.tool_call_id: human_result.model_dump(modejson)}, ), )恢復(fù)結(jié)果的形狀由AskHumanToolResult定義schema.pystatus取值為submitted/timeout/cancelled/unavailable之一除status外還有可選的action含合法標(biāo)識符id與非空label、values字段名到值、message與rendered_content。Dify Agent 會把提供的結(jié)果作為原 external 工具調(diào)用的返回值交回 pydantic-ai模型隨后繼續(xù)運(yùn)行。恢復(fù)的 run 可能產(chǎn)出最終output也可能再次產(chǎn)出deferred_tool_call——即 Agent 還需要又一輪人類交互。runner 在恢復(fù) run 中會跳過新的 user 輸入注入直接以deferred_tool_results驅(qū)動模型runner.py。超時與“人類不可用”也應(yīng)作為工具結(jié)果回傳而不是當(dāng)作 Agent run 失敗處理{ status: timeout, action: {id: __timeout, label: Timeout}, values: {}, message: The human did not respond before the workflow timeout. }客戶端職責(zé)邊界ask-human layer 刻意把產(chǎn)品決策留給調(diào)用方??蛻舳吮仨氉孕袥Q定如何持久化延遲調(diào)用并與面向人類的任務(wù)做關(guān)聯(lián)如何渲染并凈化請求中的字段/動作如何選擇接收人、渠道與超時策略授權(quán)誰可以作答如何把人類提交轉(zhuǎn)換為AskHumanToolResult如何攜返回的session_snapshot與匹配 composition 恢復(fù) run。反過來有一條安全紅線不要把收件人郵箱、workspace 成員 id、公開 URL、鑒權(quán) token、超時策略放進(jìn)工具參數(shù)。面向模型的請求是不可信內(nèi)容不應(yīng)讓它反過來控制投遞或授權(quán)——這正是DifyAskHumanLayerConfig文檔字符串強(qiáng)調(diào)“Delivery, recipient selection, timeout policy, and other operational behavior are intentionally out of scope”的動機(jī)configs.py。排障速查表癥狀檢查項(xiàng)run 報Deferred tool results require a history layer補(bǔ)上historylayer并攜帶上一次 snapshot 恢復(fù)對應(yīng) runner.py 的校驗(yàn)run 報pending tool call can be resumed在首次產(chǎn)生 deferred 調(diào)用的 run 中保持 history layer 處于激活狀態(tài)runner.pyrun 報exactly one deferred call當(dāng)前版本每次 run 僅支持一個 ask-human 調(diào)用應(yīng)提示模型一次只問一個問題layer.pyrun 報tool name must be ...使用配置的tool_name不要只在下游表單代碼里改名layer.py文件字段被拒絕設(shè)置allow_file_fieldsTrue并在allowed_field_types中包含file或file-listconfigs.pyrun_succeeded.data.output缺失檢查run_succeeded.data.deferred_tool_call——這是“人類請求成功”不是 run 失敗小結(jié)ask-human layer 把“Agent 何時該停下來問人”這件事收斂成一個模型可見的 external deferred tool配置層用DifyAskHumanLayerConfig聲明護(hù)欄服務(wù)端硬上限兜底運(yùn)行時層把工具改寫為kindexternal、以 prompt 提示 雙重校驗(yàn)ModelRetry 歸一化保證請求合法終止事件用deferred_tool_call與output互斥地表達(dá) run 結(jié)果。客戶端則承擔(dān)投遞、授權(quán)、渲染與恢復(fù)的全部產(chǎn)品職責(zé)用session_snapshot 相同 composition deferred_tool_results完成閉環(huán)。相關(guān)實(shí)現(xiàn)集中在 dify-agent/src/dify_agent/layers/ask_human/ 包c(diǎn)onfigs.py/schema.py/layer.pyrunner 側(cè)的分支與校驗(yàn)在 dify-agent/src/dify_agent/runtime/runner.py配套文檔為 dify-agent/docs/dify-agent/user-manual/ask-human-layer/index.md?!久赓M(fèi)下載鏈接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/di/dify創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考