大数跨境
0
0

Human In the Loop竟然可以是个MCP?

Human In the Loop竟然可以是个MCP? 阿里云开发者
2025-12-08
0

阿里妹导读

本文基于实践经验,介绍如何在阿里大模型研发平台(OpenLM)及其他Agent平台上,通过统一工程方案实现“Human In The Loop”场景下的大模型产品设计与开发。

作者:彭世昕、亚男、闫科忠

一、背景

1.1 什么是Human In The Loop?

Human In The Loop(人机回路)指人类监督并参与Agent活动的交互方式。常规场景中,人类行为主要包括输入任务或上下文信息,以及中断Agent执行。中断后可选择继续运行、补充信息重新决策,或终止流程。

此外,在Agent执行过程中,人类还可提供答疑或授权。当输入存在歧义时,Agent可通过发起“疑问”请求澄清;在需执行敏感操作(如访问文件系统)前,也可请求人类授权,确保安全可控。

以下以阿里内部平台OpenLM为例,展示用户在“AI搜索助手”Agent中触发天气查询时的人机回路流程。

1.2 在客户端/服务端模式下的困难

Cline、Cursor、Claude Code及iFlow CLI等工具之所以能快速实现人机回路,关键在于其Agent运行于本地设备,调用UI相对简单。但在远端服务部署场景下,若无特殊工程设计,面对并发、分布式和多端协同挑战,用户体验将显著下降。

MCP(Model Context Protocol)Server最初采用stdio Transport,因其“单进程单客”架构简单而被广泛采纳。即便后续引入HTTP+SSE支持远程访问,仍难以应对微服务环境中的复杂问题。Streamable Transport虽缓解部分难题,但整体实现依然面临挑战。

当前多数Agent框架基于React构建,缺乏类似LangGraph的HITL节点能力。同时,SSE流式响应适配HITL需引入图状态持久化与恢复机制,增加了系统复杂度。例如,以下为FastAPI实现的状态管理示例:

from fastapi import FastAPI
from chatagent import graph_with_menu
from langgraph.types import Command
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"message": "Welcome to your FastAPI backend!"}

@app.get("/chat_initiate")
def read_item(thread_id: str, response: str = None):
    thread_config = {"configurable": {"thread_id": thread_id}}
    state = graph_with_menu.invoke(
        {"messages": [], "order": [], "finished": False},
        config=thread_config)
    
    return {
        "AIMessage": state["messages"][-1].content,
        "state": state,
        "thread_id": thread_id
    }

@app.get("/chat-continue")
def continue_chat(thread_id: str, response: str):
    thread_config = {"configurable": {"thread_id": thread_id}}
    state = graph_with_menu.invoke(Command(resume = response), config=thread_config)

    return {
        "AIMessage": state["messages"][-1].content,
        "state": state,
        "thread_id": thread_id
    }

该方案不仅涉及API重构,还需支持多阶段执行流程。在分布式环境下,图状态跨节点同步与恢复进一步提升系统复杂性,对Agent框架改造要求高,技术成本大。因此,借助MCP协议实现HITL成为自然选择。

二、解决方案

2.1 用MCP实现人类“确认”能力

Agent对MCP Tool的tool/call调用可类比为“问询—答复”过程,其中大模型为问询方,人类为答复方。当模型需要确认信息时,可向专用MCP Server的inquiry工具发送请求,待人类回应后再返回结果,完成闭环。

技术上,需定义一个名为send_inquiry的MCP工具,接收prompt参数作为提问内容。该工具接收到请求后挂起,直至收到人类答复才结束等待,并将结果返回给大模型。

1)UI与接口

人类参与始终依赖UI,无论是命令行、Web还是App界面。HTTP协议作为主流交互媒介,适合承载“人类答复”逻辑。可在MCP Server所在微服务中暴露HTTP接口,仅需接收response字符串参数即可实现答复入口。

在多客户端场景下,需解决不同Agent请求与人类答复的关联问题。由于数据结构简单,可通过ID与哈希表实现映射。但难点在于:如何在Agent发起请求与人类答复之间建立有效信道传递ID?

Agent处理单位为毫秒级,而人类响应通常以秒计,本质上是异步任务。按惯例应由服务端返回任务ID供调用方追踪状态。然而MCP Tool默认为同步调用,响应必须在同一tool/call内完成。为保持架构一致性,不能更改此模式,否则失去使用MCP实现HITL的意义。

2)MCP的Notification

MCP协议中的Notification机制可用于解决上述问题。服务端可通过Notification帧实时推送进度与状态。在Agent调用send_inquiry后,MCP Server可通过Notification发送包含ID(凭条)的消息,通知调用方需人类确认,并触发UI渲染。

以下为MCP Notification帧示例(来自SSE信道):

{
  "method": "notifications/progress",
  "params": {
    "meta": {
      "question": "明天北京天气如何?",
      "inquiryId": "a4cecc76-2fb3-41bc-97ae-e809059ad68a",
      "type": "INQUIRY"
    },
    "progressToken": 1,
    "progress": 0,
    "method": "notifications/progress"
  },
  "jsonrpc": "2.0"
}

Notification中除ID外还可携带问题内容,使端侧一次性完成UI渲染。该ID通过已建立的Agent服务接口(如ChatCompletions)的SSE帧转发给调用方。

以下为OpenAI兼容协议的SSE响应帧结构示例:

{
  "id": "chatcmpl-202d02d5-68cd-40d1-bd5f-0dc82751ba89",
  "created": 1756272472,
  "object": "chat.completion.chunk",
  "choices": [
    {
      "index": 0,
      "delta": {
        "chatos_additional_data": {
          "mcp_progress_notification_data": "{\"method\":\"notifications/progress\",\"params\":{\"meta\":{\"question\":\"明天北京天气如何?\",\"inquiryId\":\"a4cecc76-2fb3-41bc-97ae-e809059ad68a\",\"type\":\"INQUIRY\"},\"progressToken\":1,\"progress\":0.0,\"total\":null,\"message\":null,\"method\":\"notifications/progress\"},\"jsonrpc\":\"2.0\"}"
        }
      }
    }
  ],
  "agent_info": {
    "name": "Tool Calling Agent",
    "run_id": "202d02d5-68cd-40d1-bd5f-0dc82751ba89"
  }
}

mcp_progress_notification_data即原始MCP Notification帧内容,表明Agent平台无需解析,可交由调用方处理。

完整单端流程如下图所示:

用户提交“确认”结果至MCP Server的路径,既可通过HTTP请求,也可通过tool/call直接调用实现。

2.2 代理现有工具以支持“确认”

当大模型上下文不足时,可通过前述方案请求人类补充信息。若需在每一步操作前均进行“执行确认”,则可在每个MCP Tool调用前插入确认流程。

根据人类回复“是”或“否”决定是否继续执行。若拒绝,则tool/call返回TextContent标注“人类拒绝执行”;若同意,则按原逻辑执行并返回结果。

此模式类似设计模式中的代理模式:前置逻辑判断是否放行,否则中断执行。该模式为行为标准化提供了理论基础。

可构建专用MCP Proxy服务,实现方式如下:

1)透传tools/list等请求。流程如下图:

2)代理tool/call请求,并视情况调用上游MCP Server。流程如下图:

接入MCP Proxy后,将代理URL集成至Agent,即可实现在不改动现有架构的前提下增加人工确认环节。

2.3 关于YOLO模式

YOLO(You Only Look Once)模式由Cursor于2024年末推出,允许Agent自动执行本需人工确认的操作(如终端命令),开启后可实现真正意义上的自动化助理。

启用YOLO模式后,Agent面临新的决策挑战:

  • 当大模型提出疑问时,由谁决策?
  • 如何做出决策?

2.3.1 服务端决策

若由服务端决策,因“YOLO开关”为静态配置,Agent代码通常已连接HITL MCP Server或MCP Proxy。开启YOLO后,只能通过Prompt约束模型绕过HITL调用,或完全跳过Proxy逻辑,但这可能导致行为偏离预期。

若Agent代码灵活,可在YOLO开启时断开HITL连接,减少Prompt干预,但仍难解决Proxy代理工具的阻断问题。

2.3.2 客户端决策

多数服务端Agent对应的客户端为“瘦客户端”,仅负责UI渲染,无推理能力。若由客户端决策,策略仅限三种:“随机”、“YOLO”或“请求外援”。

“随机”相当于抛硬币,结果不可控。“YOLO”表示授权Agent自主决策,对HITL Server而言仍是服务端决策的一种形式;对Proxy而言则代表立即执行。

另一种策略是客户端主动回复“超时未回答”,作为人类第三种选择。若在YOLO模式下,客户端收到确认请求后不渲染UI而直接返回“超时”,Agent可据此判断需自行决策。

2.3.3 Agent决策方法

即使不考虑YOLO模式,Agent在遭遇“拒绝回答”或“超时”时也需具备自主决策能力。可通过Prompt引导模型自我判断:

也可引入专用“决策Agent”代替人类作答。当前Agent在无法获取答复时,可将背景、目标及问题转交决策Agent,获得回应后再继续执行。此举可保持主Agent纯净,便于审计日志,并逐步形成个性化助理。

从架构看,该动作可由Agent执行循环触发。发现HITL响应异常时主动调用决策Agent,但会侵入执行逻辑。

亦可将决策Agent封装为MCP Server(如Double MCP Server),描述为“用于补充人类未答场景”。需调整Prompt以适配调用逻辑。

此外,“服务端决策”也可由客户端主动触发,即“请求外援”:当人类未答复时,客户端将信息提交至专用代理接口,强制生成答复并回传。原Agent无需感知来源,无需适配。

2.4 端侧方案

2.4.1 常规处理

Web、App或桌面端通过解析Agent主入口(如ChatCompletions)的SSE流帧进行UI渲染。根据帧类型决定即时或批量渲染。

收到代表问询的帧时,端侧渲染UI等待人类答复。业务服务需提供独立API接收答复,对接HITL接口。用户作答后由端侧调用该API完成提交。

等待期间需启动定时器,超时后触发“超时未作答”机制。若主SSE连接中断,端侧应自动关闭UI,防止误操作。

2.4.2 多端协同

类比Apple iCloud登录验证机制,一次确认可在任意设备完成,完成后其他设备同步失效。

在多端场景中,虽仅主界面接收任务,但其他已登录设备可通过服务端通知机制同步获取Agent下发的问询内容。各端可提示用户,点击任一设备即可渲染UI并答复。

任一端完成答复(正常、拒绝或超时)后,服务端通知其余端撤回问询,自动关闭界面。同时Agent发送完成帧通知主界面关闭UI。

注意:主界面通过SSE帧接收所有消息,而其他端通过各自通知通道接收,数据结构可根据业务自定义。

2.5 超时配置

在Agent作为MCP客户端调用工具时,常需设置tool/call响应等待时间,以防用户长时间等待或系统连锁故障。

OpenLM平台默认超时为30秒,满足多数工具调用需求,但不足以支撑人机交互。应适当延长MCP Client超时时间,使其大于人机回路服务超时,再大于端侧“超时未作答”时限,形成三级超时体系:

MCP Client等待时间 > 人机回路服务超时 > 端侧超时未作答时间

三、人类的回答

3.1 直接回答

直接回答看似简单,实际工程实现复杂。若仅有一个问题,可让用户直接填写答复;若多个问题并行,则需考虑传递方式、UI渲染及用户体验。

3.1.1 现实问题

部分用户希望Agent激发创意,倾向提前明确任务细节以防偏差;另一些用户期望Agent成为全能助理,若频繁提问易产生“不如自己动手”心理,导致弃用。

从答复质量看,人类答复的“充分性”值得关注。例如询问“查天气”,回答“北京”即可;但若列出多项具体问题,人类未必逐一细致作答。

或更复杂的多问题列表:

3.1.2 多轮单问题

采用“多轮次、每次一问”的模式,类似人类与下属沟通任务:逐步澄清模糊点,再执行任务。技术实现简单,每次传输一个疑问和答复字符串。

优势在于逐步完善信息,甚至可对答复中的模糊点再次追问。缺点是轮次过多易引发烦躁情绪;限制轮次又可能导致信息不足即开始执行。

3.1.3 单轮多问题

AI一次性提出多个问题,减少交互次数。需在Prompt中特别约束,且问题呈现方式(结构化或非结构化)影响工程实现。

1)结构化数据

使用JSON等结构化格式有利于UI开发,但要求大模型输出严格符合Schema,对Prompt工程挑战大。微小错误即可导致渲染失败。

可行方案是由专用Agent动态生成UI渲染代码,实时编译执行,避免预设Schema限制。但该Agent能否稳定生成无错、符合风格的代码仍存疑。

2)非结构化数据

采用Markdown等富文本格式,技术实现与“多轮单问题”一致。端侧将其放入可编辑富文本框,人类逐项作答后连同原文一并提交。

此方案利用大模型文本处理能力,工程简单,跨端一致性强,支持人类“作弊”修改建议答案。

3.1.4 渲染方式

“依次作答”适用于单轮多问题场景。无论数据结构如何,均可一次性渲染多个问题。

使用Markdown渲染,将问题与答案置于同一富文本编辑器,最简实现,支持AI生成初稿供修改。

渲染为“问题+文本框”形式仅适用于结构化数据,形如调查问卷,支持单/多行输入,提交时封装为结构化数据。

进阶形式为提供选项供选择,使用复选或单选按钮。操作简便,但若候选答案不符预期,灵活性受限。

总体而言,UI渲染需千人千面,针对不同用户群体探索个性化方案。

3.2 其他回答

3.2.1 拒绝回答

人类点击“取消”或“不想回答”即构成一种答复。系统应在用户明确拒绝时返回特定文本,如:

或:

此类答复无法消除歧义,低智能模型可能陷入重复提问循环。需优化Prompt,使其在收到拒绝后停止调用HITL。

在工具代理场景中,若人类拒绝执行,MCP Proxy将拦截tool/call并返回TextContent类型响应。示例答复如下:

配套Prompt应做相应调整:

3.2.2 超时未作答

无论从工程稳定性还是功能需求出发,Agent在未获答复时应继续推进任务,体现“智能”特性。因此需设计“超时未作答”机制。

参考答复如下:

可复用“拒绝回答”场景的Prompt优化策略,帮助Agent在缺失信息时继续工作。

3.2.3 默认作答

“拒绝”与“超时”策略本质是用固定“兜底”答复搪塞Agent,易脱离上下文,造成局限。

更优方案是启用另一助理Agent针对疑问自动生成建议答案,并与原问题一同展示给人类。用户可直接提交该默认答案,避免通用答复的不匹配问题。

四、提示词倾向性

4.1 Agent的Prompt

通用Prompt下,Agent发现需澄清时会直接输出疑问,或将问题纳入“深度思考”尝试自答。

通过轻微引导,可让Agent调用MCP工具发起正式问询。以下是OpenLM“AI搜索助手”的Prompt片段:

  <instruction>
   请严格遵循以下思考和操作步骤:
   1.  **意图分析 (Intent Analysis):** 首先,深入分析用户的提问,判断其意图是否清晰、具体。
   2.  **澄清意图 (Clarification - If Needed):** 如果符合 `clarify_user_intent` 指令的条件,立即停止并严格按照该指令的行动指南调用 `send_inquiry`。
   3.  **信息检索 (Information Retrieval):** 一旦意图明确,判断是否需要外部信息。如果需要,则必须调用搜索工具。
   4.  **结果验证 (Verification - If Needed):** 在获取搜索结果后,进行审阅。如果对搜索结果的准确性、完整性或存在矛盾之处有疑问,可以进行**补充搜索**(再次调用搜索工具)来交叉验证或获取更多维度的信息。
   5.  **综合回答 (Synthesis & Response):** 结合你的内部知识和经过验证的外部信息,形成一个完整、结构化、准确的答案,并按指定格式输出。
  </instruction>

接着定义clarify_user_intent指令:

<!-- 指令的核心部分,定义了最重要的行为准则。 此处的指令拥有最高优先级,必须严格遵守。 -->
<directive id="clarify_user_intent" importance="critical">
  <description>在对用户意图不明确时,必须优先澄清,这是所有后续操作的前提。</description>
  <condition>
    当你对用户的提问感到困惑、或问题包含歧义以至于无法进行下一步操作时。
  </condition>
  <action>
    1. 你必须(MUST)调用 `send_inquiry` 工具,向用户提出具体、有针对性的问题,以澄清其真实意图。
    2. 严禁通过直接生成文本内容(即直接回复)的方式向用户提问以澄清意图。所有澄清问题的行为都必须、且只能通过调用 `send_inquiry` 工具来完成。
    3. 在澄清意图之前,严禁进行猜测或执行任何其他工具(如搜索)。
  </action>
  <example name="correct_behavior">
    - 用户提问:“帮我查一下最新的‘泰坦’。”
    - 你的判断:意图困惑(是指电影、游戏还是科技产品?)。
    - 正确行动:调用工具 send_inquiry with question 如 ("您好,您提到的‘泰坦’具体是指什么呢?是电影、游戏还是某个科技产品?")
  </example>
  <example name="incorrect_behavior_to_avoid">
    - 用户提问:“帮我查一下最新的‘泰坦’。”
    - 你的判断:意图困惑。
    - **错误行动:** 直接生成并返回文本内容 "您好,您提到的‘泰坦’具体是指什么呢?是电影、游戏还是某个科技产品?"
    - **此行为是严格禁止的。**
  </example>
</directive>

若过度强调send_inquiry重要性,可能导致本应通过检索获取的信息也被转为人工问询。为此需区分“意图识别”与“补充信息”,引导模型正确选择下一步动作。

新增information_gathering提示:

<directive id="information_gathering" importance="high">
  <description>在明确用户意图后,主动通过网络搜索来补充和验证信息,以确保回答的准确性和时效性。</description>
  <condition>
    当你的内部知识不足以回答问题,或问题涉及需要最新、实时信息(如新闻、股价、事件进展、产品发布)时。
  </condition>
  <action>
    在完全理解用户问题后,你必须(MUST)调用搜索工具(如 `GoogleWebSearch` 等)来获取相关信息。不要仅依赖可能过时的内部知识回答此类问题。
  </action>
  <example>
    - 用户提问:“请总结一下上周关于人工智能领域的主要新闻。”
    - 你的判断:意图明确,但需要最新的外部信息。
    - 你的行动:调用 `GoogleWebSearch(query="上周 人工智能领域 主要新闻 总结")`
  </example>
</directive>

4.2 工具的Description

根据MCP协议,tools/list响应中的description用于帮助大模型理解工具用途,并决定是否调用。该描述与Agent Prompt共同构成上下文,同等重要。

以下为典型tools/list响应,含get_weather工具及其描述:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "title": "Weather Information Provider",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name or zip code"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}

对于“人机回路”工具send_inquiry,其地位与其他工具相同。若需突出其作用,应在Description中详述使用场景,如同Sequential-Thinking工具所做:

何时使用此工具:
1. 将复杂问题分解为步骤
2. 留有修改空间的规划与设计
...
核心功能:
1. 可根据进展调整总思维次数
2. 可质疑或修改之前的思维
...
操作建议:
1. 从所需思维的初始估算开始,但需准备好调整
2. 随时可以质疑或修改之前的思维
...

甚至包括参数说明。

1)“人机回路”工具本身的Description

与通用工具一样,“人机回路”工具的Description需详尽且普适。但若与主Prompt冲突,大模型难以判断优先级。

私有服务可通过更新Description解决冲突;公共或复用型服务则受限于迭代效率与部署成本,极少独立部署。

随着QueryString参数广泛应用,动态参数已取代环境变量,实现从“部署级”到“会话级”的细粒度控制,广泛用于OpenLM MCP市场的一方与三方工具。

“人机回路”工具也可通过QueryString动态传入description,按需调整响应内容。优点是无需改客户端,可随时调优;缺点是干预粒度有限。

MCP协议允许为任意参数配置Description,指导模型填写。若初期设计未覆盖全部场景,则后期干预易陷入“不上不下”困境。

2)客户端覆盖Description

另一种方式是由客户端在获取tools/list响应后,动态替换Schema部分内容,再结合Prompt提交给模型。

优势是高度个性化,控制精细;劣势是维护成本高,违背MCP协议“即插即用”初衷。

4.3 Agent也是“人”

在与Agent交流中,模型常视自身为人类个体。因此设计提示词时应将其当作真实对话对象,避免使用“人”、“工具”、“LLM”等术语,防止理解偏差,提升交互自然性。

我们是阿里巴巴智能引擎团队,是阿里集团内AI工程系统的建设者与维护者,主导设计了大数据AI工程体系AI·OS。团队聚焦于大模型全链路工程能力建设,持续优化研发范式,专注大模型训推性能优化、引擎平台、Agent应用平台等关键组件,为阿里集团各业务提供高效稳定的AI工程基础设施。

【声明】内容源于网络
0
0
阿里云开发者
阿里巴巴官方技术号,关于阿里的技术创新均呈现于此。
内容 3576
粉丝 0
阿里云开发者 阿里巴巴官方技术号,关于阿里的技术创新均呈现于此。
总阅读19.1k
粉丝0
内容3.6k