Ekko Studio Coding Agent MCP 用户澄清机制:`ekko-studio-interaction` 交互工具的原理与实战
2026/9/23 22:03:27 网站建设 项目流程
  • AI 应用
  • 人工智能
  • AI Agent
  • 本地部署
  • 前端
  • 后端
  • 工作流自动化

【免费下载链接】hermes-studio

Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.

项目地址:https://gitcode.com/gh_mirrors/he/hermes-studio
点击查看免费下载

导读

在 Ekko Studio(本地优先的多智能体 AI 工作空间,同时支持桌面端与 Web)中,Claude Code、Codex、Pi、Grok、OpenCode 与 DSH 等无头(headless)编码代理运行时不具备终端交互能力,当任务遇到缺失的用户决策时往往只能猜测或卡死。本文基于仓库内变更记录 docs/chat-chain-changes/2026-09-17-coding-agent-mcp-clarification.md 并结合服务端与 MCP 网关源码,系统讲解ekko-studio-interactionMCP 服务器如何通过ekko_studio_clarifyekko_studio_update_plan两个工具,让编码代理在 Studio/App 既有界面中向用户提问并等待回答、继续执行。读完本文,你将掌握该交互工具的传输范围与客户端分发矩阵、context_id绑定与指令注入机制、服务端澄清会话的生命周期与超时规则、四种显式reason语义,以及对应的测试覆盖与升级注意事项。

一、为什么需要"用户澄清":无头编码代理的交互缺口

编码代理(Coding Agent)在被 Studio 托管运行时,通常以无头方式启动,没有自己的交互式终端可供用户输入。传统上这类工具遇到"缺少关键决策"的场景(例如选择部署环境、确认删除路径、挑选实现方案)时,要么用默认值代替(可能产生错误结果),要么停在原地等待一个永远不会出现的输入。

该特性的核心思路是:复用 Studio/App 中已经存在的提问/任务卡片界面,把"向用户提问"封装为一个 MCP 工具,编码代理通过既有的 managed MCP 通道调用它,问题以clarify.requested事件呈现在界面中,用户在既有界面上回答后,答案以clarify.respond事件回流,ekko_studio_clarify的 HTTP 请求随之返回真实答案并显式给出reason。原文档将其影响概括为:"无头编码代理可以在现有 Studio/App 界面中提出问题,并在拿到用户回答后继续执行。"

二、传输与范围:一个重命名的共享 MCP 服务器

2.1ekko-studio-interaction:两个工具、一个服务器

ekko-studio-plan(任务卡片)MCP 条目被重命名ekko-studio-interaction,但不注册第二个服务器:改名而非新增,避免同一个启动命令被重复注册。改名后的服务器同时暴露两个直接工具:

  • ekko_studio_update_plan:创建或更新当前轮次的任务计划卡片(原ekko-studio-plan的能力,内部planlaunch 参数被保留以兼容既有配置);
  • ekko_studio_clarify:向用户提出一个必要的澄清问题并等待回答。

从 packages/server/src/modules/hermes/services/mcp/studio-autoinject.ts 的MANAGED_SERVERS列表可以看到,ekko-studio-interaction的 toolset 为plan,与apibrowserdevicesuse并列;其旧名ekko-studio-plan出现在LEGACY_SERVER_NAMES中,用于识别并迁移存量配置(L10-L33)。在 packages/server/src/modules/coding-agents/services/mcp-manager.ts 的readServers()中有一行servers.delete('ekko-studio-plan')L299),明确把旧条目从读取结果中剔除,再统一注入新名ekko-studio-interaction

MCP 服务器本体实现于 bin/ekko-studio-mcp.mjs(stdio 协议,TOOLSETS = api/browser/devices/use/plan,按首个位置参数或HERMES_MCP_TOOLSET决定暴露哪个工具集)。plan工具集下同时注册ekko_studio_update_planekko_studio_clarify,其中ekko_studio_clarify内部 POST 到/api/studio/clarifications/requestekko_studio_update_plan内部 POST 到/api/studio/task-plans/updateL1959-L1966)。

2.2 客户端分发矩阵:谁拿到什么

原文档明确了各客户端的接收范围,这是一张值得完整保留的对照表:

客户端接收ekko-studio-interaction可用工具说明
Claude Code / Codex / Pi / Grok / OpenCode / DSHekko_studio_update_plan+ekko_studio_clarify通过既有 managed 配置路径注入,无需新增客户端组件
Hermesekko_studio_update_plan澄清工具从 discovery 中隐藏、从指令中省略、直接调用被拒绝
Ekko保留其原生工具(native tools),不接收该服务器
Pi两个工具同时保留其原生 RPC UI 支持

2.3 开关环境变量:HERMES_MCP_USER_CLARIFICATION

工具是否可见由一个环境变量控制,默认关闭:

  • 默认值:off(未设置即不启用澄清);
  • 只有 Coding Agent 注入路径显式设置HERMES_MCP_USER_CLARIFICATION=1
  • Hermes 的自动注入路径显式设置为0

在 packages/server/src/modules/coding-agents/services/index.ts 中,hermesMcpServerConfig()的 env 固定写入HERMES_MCP_USER_CLARIFICATION: '1'L1194),而managedHermesMcpServerConfig()在 toolset 为plan时进一步确保该变量为'1'L1213)。反观 Hermes 侧的 studio-autoinject.ts 的managedConfig(),env 中写入的是HERMES_MCP_USER_CLARIFICATION: '0'L189)——从源码可以确认,这就是"只有 Coding Agent 注入开启、Hermes 显式关闭"的实现落点。

MCP 服务器侧在 bin/ekko-studio-mcp.mjs 用如下表达式计算开关(L70-L71):

const SHARED_TASK_PLAN_ENABLED = process.env.HERMES_MCP_NATIVE_TASK_PLAN !== '1' const USER_CLARIFICATION_ENABLED = SHARED_TASK_PLAN_ENABLED && process.env.HERMES_MCP_USER_CLARIFICATION === '1'

并在activeToolsetTools()中过滤:(USER_CLARIFICATION_ENABLED || tool.name !== 'ekko_studio_clarify')L1804-L1808)。也就是说,即使进程以plantoolset 启动,只要该变量不是'1'tools/list就不会暴露ekko_studio_clarifytools/call也会拒绝调用——这正是"Hermes 端澄清被隐藏、被省略、被拒绝"的三重防护。serverInstructions()L1822-L1826)在plantoolset 下只把澄清指令拼进系统提示当且仅当USER_CLARIFICATION_ENABLED为真,对应"从指令中省略"。

三、ekko_studio_clarify工具契约与参数校验

3.1 参数定义

工具接受context_idquestion和可选的字符串数组choices即使提供了choices,也仍然允许用户以自由文本回答。以下是 bin/ekko-studio-mcp.mjs 中ekko_studio_clarify的完整 input schema(L1013-L1021):

{ "context_id": { "type": "string", "description": "Current turn interaction context supplied by Studio." }, "question": { "type": "string", "minLength": 1, "maxLength": 4000 }, "choices": { "type": "array", "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 500 } } }

context_idquestion为必填;choices可选。工具描述明确要求"只使用最新的 interaction context_id",并强调"超时、取消、关闭不等于同意(consent),先检查 reason 再继续"。

3.2 服务端校验边界

服务端 packages/server/src/modules/studio/services/clarification-runs.ts 的parseQuestion()L14-L23)在显示任何问题之前做严格校验,任一不满足即抛ClarificationError(HTTP 400),且不会发出clarify.requested

  • question必须是去空白后长度 1~4000 的字符串;
  • choices若提供:必须是数组、最多 20 项、每项为去空白后长度 1~500 的非空字符串;重复项会被去重([...new Set(...)])。

测试 tests/server/clarification-runs.test.ts 的validates input before displaying a prompt用例覆盖了空问题、超长问题、非字符串选项、纯空白选项、超长选项、21 个选项等非法输入,并断言这些输入全部抛错且不产生clarify.requested事件。

四、交互绑定:context_idstudio_interaction_context与作用范围

4.1 capability id 与独立 interaction binding

每个交互式 coding-agent turn 的最新输入会附带一段studio_interaction_context指令。该指令使用的context_id与任务卡片(task card)共用同一个 capability id,但使用独立的 interaction binding(互不干扰)。从 packages/server/src/modules/studio/services/clarification-runs.ts 的clarificationTurnInstruction()L113-L114)可以看到注入文本的完整内容,要点包括:

  • 当缺失的用户决策实质性影响任务时,从同一个ekko-studio-interactionMCP 服务器调用ekko_studio_clarify(与任务卡片同源),问题要简洁并可选提供选项;
  • 该工具会显示既有 Studio/App 提问界面并等待用户响应,用于替代无头模式下不可用的终端输入或原生交互工具;
  • 若工具被延迟(deferred),应搜索ekko-studio-interaction / clarify并使用发现到的工具名;
  • 超时、关闭、取消不是用户批准
  • 禁止在委托子代理(delegated subagents)或后台任务中使用
  • 指令末尾给出Current turn context_id="...",并要求"只使用这个交互上下文,绝不要使用更早消息里的 id"。

4.2 注入时机与作用范围

在 packages/server/src/modules/studio/services/chat-run/handle-coding-agent-run.ts 中,interactionContext = mcpCapabilities.interaction ? data.interaction_context_id : undefinedL74),随后运行时输入被拼接为:

const runtimeInput = interactionContext ? `${plannedInput}\n\n${clarificationTurnInstruction(interactionContext)}` : plannedInput

即指令只附加到该轮次最新输入上,而不是写进稳定的系统提示;context_id不会出现在存储/展示的用户输入里。从 packages/server/src/modules/studio/sockets/chat-run.ts 的编排逻辑(L1709-L1714)可以确认作用范围:

const planContext = isCommand || !mcpCapabilities.interaction ? undefined : this.beginTaskPlanRun(data.session_id, profile) const interactionContext = planContext && source !== 'workflow' && data.session_source !== 'workflow' && source !== 'global_agent' && data.session_source !== 'global_agent' ? planContext : undefined if (interactionContext && data.session_id) { this.clarificationRuns.begin(interactionContext, data.session_id, profile, () => this.sessionMap.get(data.session_id!)) }

据此可以总结出清晰的绑定范围:

场景是否获得 interaction binding
直接聊天(direct chat)
群聊(group chat)是(经既有 manager relay 回答)
工作流(workflow)
全局后台代理(global background agent)
独立终端(standalone terminal)否(无 binding)
委托子代理被指令明确禁止使用父级交互工具

4.3 会话与轮次解析:不信任调用方提供的 id

ekko_studio_clarify的 HTTP 请求体里只带context_idquestionchoices不携带 session/run id。Studio 从 binding(内存映射)解析出 session 与 history turn marker,而不是接受调用方提供的 session/run ids——这从根本上防止了跨会话、跨轮次的伪造或越权。绑定校验发生在 clarification-runs.ts 的active()L37-L47):binding 不存在或 profile 不匹配返回 409("Interaction context is unavailable or has expired"),轮次状态不是 working、处于 aborting、没有活动 run marker、或 run id 与 binding 记录的 run id 不一致,均返回 409("Interaction context has no active turn")。

以下情况都会被拒绝:

  • 其他 profile:binding 绑定到启动轮次时的 profile,active()会比对binding.profile !== profile
  • 过期 context:binding 不存在或已被清除;
  • idle/aborting 轮次isWorking为假或isAborting为真;
  • 无效输入:见 3.2 节校验规则;
  • 同一 session 内并发问题request()会检查 pending 表中是否已有同 session 的问题,存在则抛 409("Another clarification is already pending for this session")。

五、澄清会话的生命周期与事件流

5.1 状态机:begin → request → respond / 结算

ClarificationRuns维护两个内存 Map:bindings(contextId →{ sessionId, profile, resolve })与pending(clarifyId →{ contextId, sessionId, finish })。核心流程如下:

  1. begin(contextId, sessionId, profile, resolve):为新轮次注册绑定,同时调用finishSession(sessionId)结算该 session 之前的旧等待;
  2. request(contextId, profile, input, signal?):校验绑定与输入,生成clarifyId = randomUUID(),注册 pending,发布clarify.requested,然后阻塞等待CLARIFICATION_TIMEOUT_MS = 300_000(5 分钟,L8),定时器到期自动以timeout结算;AbortSignal触发时以cancelled结算;
  3. respond(sessionId, clarifyId, response?):仅当 pending 存在且 session 匹配时结算——有回答文本 →reason: 'response';空回答 →reason: 'dismissed';binding 失效 →reason: 'cancelled'
  4. finishSession(sessionId, contextId?):清除该 session 的 bindings,并以cancelled结算所有 pending。

5.2 事件与 reason 语义

  • clarify.requested:沿用现有 question、choices、requested-at、timeout、remaining-time 字段(payload 含run_idclarify_idquestionchoicestimeout_msremaining_timeout_msrequested_at);
  • clarify.respond:直接聊天由客户端通过 socket 事件回答;群聊复用既有 manager relay;
  • clarify.resolved:结算时向 session 广播,payload 含run_idclarify_idresolved: truereason,并清除 pending replay 状态(即使答案来自另一个客户端也要同步清理);
  • HTTP 返回值{ clarify_id, response, reason },其中reason为四值枚举:response|dismissed|timeout|cancelledclarification-runs.tsL5)。

一个必须强调的安全语义:"缺席的响应绝不等于批准"。超时、关闭、取消都会以非response的 reason 返回,Agent 若把这三种情况当成用户同意继续执行,将违反工具与指令的明确约束。

5.3 边界条件:哪些事件会使绑定失效、等待结算

原文档列出了完整的失效条件,对应源码中finishSession的调用点(chat-run.tsbeginTaskPlanRunfinishTaskPlanRun、以及各 socket 处理分支):

  • 轮次完成(run.completed)、失败(run.failed)、停止(abort.completed);
  • 被新轮次替换(begin()会先finishSession旧 session);
  • session 处置(disposal);
  • 服务器关闭;
  • MCP 取消notifications/cancelled通知会 abort 对应请求(bin/ekko-studio-mcp.mjsL2199-L2201);
  • stdio 关闭:stdin close 时遍历 abort 所有 pending interactions(L2243);
  • HTTP 断开:controller 中ctx.res.once('close', ...)触发 abort(packages/server/src/modules/studio/controllers/clarifications.tsL13-L15)。

例外:单独的客户端 UI 断开不会取消问题——binding 保留,客户端重连/恢复后仍可继续回答。

由于 bindings 与 pending 都是内存态,服务器重启后不保留(有意设计):重启意味着所有等待以取消告终,这是可预期的、安全的默认行为。

六、超时预算与 HTTP 传输细节

澄清问题最长等待 5 分钟,因此整条链路的超时预算都必须覆盖这个业务期限:

  • MCP 工具级预算:所有受管 CLI 的 tool-call 预算至少 6 分钟(360 秒)。各 CLI 家族的字段名不同,从 packages/server/src/modules/coding-agents/services/index.ts(L1215-L1216)与测试 tests/server/coding-agent-mcp-manager.test.ts(L86-L89)可看到映射:

    CLI 家族预算字段下限
    claude-code / opencodetimeout≥ 360_000 ms
    codex / groktool_timeout_sec≥ 360 s
    pirequestTimeoutMs≥ 360_000 ms
    dshtoolCallTimeoutMs≥ 360_000 ms
  • HTTP 传输级/api/studio/clarifications/request走的是自定义fetchMobileConsent(bin/ekko-studio-mcp.mjsL188-L213),使用330 秒(330_000 ms)的 deadline,而不是 fetch 默认的 300 秒 response-header deadline——因为用户交互可能在产生响应头之前就等待 5 分钟,330 秒给业务期限留了 30 秒缓冲,避免"业务未超时、传输先超时"的竞态。

七、升级与启用注意事项

原文档给出的运维要点,在部署时缺一不可:

  1. 升级后必须重启 Studio 与所有既有 coding-agent 进程,让它们重新加载更新后的 interaction MCP 工具目录(否则进程仍持有旧的ekko-studio-plan工具集);
  2. 工具是否真正被使用,取决于两个条件:
    • Agent 是否遵循注入的studio_interaction_context指令(模型需自行决定调用该工具,系统不保证每个模型都会调用);
    • managed interaction 服务器是否被启用(用户可通过 managed MCP 开关/override 禁用,例如测试coding-agent-mcp-manager.test.tsdisabled: { codex: { default: ['ekko-studio-plan'] } }的用例展示了禁用 override 会跟随新名生效)。

此外,存量用户对ekko-studio-plan的 override 与禁用设置会自动跟随新名ekko-studio-interaction(mcp-overrides 与 grok 配置中的MANAGED_MCP_NAMES同时收录两个名字,packages/server/src/modules/coding-agents/services/grok/config.tsL8-L23),不需要用户手动迁移;内部planlaunch 参数继续保留,兼容旧配置。

八、验证体系:测试如何证明这条链路可靠

原文档列出了五类测试,仓库中均有对应实现:

  • Service/controller 测试:tests/server/clarification-runs.test.ts 覆盖选项/自由文本回答、关闭(dismissal)、输入校验、超时、取消、profile/session 隔离、陈旧轮次(stale turn)被拒绝;tests/server/clarifications-controller.test.ts 覆盖 HTTP controller 路径;
  • Socket 测试:tests/server/chat-run-bridge-readiness.test.ts 覆盖直接聊天与群聊两种clarify.respond入口、pending replay 清理、abort 清理;
  • MCP 子进程测试:验证直接 discovery、阻塞直到 HTTP 回答返回、响应转发、取消通知(对应 bin/ekko-studio-mcp.mjs 中notifications/cancelled→ abort →reason: cancelled的闭环);
  • 配置测试:tests/server/coding-agent-mcp-manager.test.ts 用it.each(['claude-code', 'codex', 'pi', 'grok', 'opencode', 'dsh'])逐一断言六个 CLI family 都拿到ekko-studio-interaction、旧名ekko-studio-plan不再出现、managed为 true、预算字段 ≥ 360、HERMES_MCP_USER_CLARIFICATION'1'
  • 既有浏览器澄清测试:复用既有的提问界面组件,无需新增客户端组件。

需要明确的是:这些测试模拟 Agent 调用与用户回答,不依赖真实模型账号,也不会断言"每个模型都一定会调用该工具"——模型是否调用属于推理行为,不在服务端测试的承诺范围内。

九、总结

ekko-studio-interaction是 Ekko Studio 把"编码代理向用户提问"产品化的关键桥梁:通过一个重命名的共享 MCP 服务器、一个受环境变量严格控制的工具开关、一套以context_id为核心的内存绑定机制,以及"5 分钟业务超时 + 6 分钟工具预算 + 330 秒传输 deadline"的三级超时设计,让 Claude Code、Codex、Pi、Grok、OpenCode、DSH 在无头模式下也能安全地与用户在既有界面上交互。其设计中最值得借鉴的三点:binding 而非调用方自报 session/run id(杜绝跨会话越权)、显式reason而非布尔结果(让"缺席 ≠ 批准"成为可编程语义)、客户端 UI 断开与传输断开分离(支持重连恢复)。无论是排查澄清不生效、评估超时配置,还是为其他 Agent 家族接入同类交互能力,本文梳理的源码路径与测试用例都可以作为直接的入手点。

  • AI 应用
  • 人工智能
  • AI Agent
  • 本地部署
  • 前端
  • 后端
  • 工作流自动化

【免费下载链接】hermes-studio

Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.

项目地址:https://gitcode.com/gh_mirrors/he/hermes-studio
点击查看免费下载

相关推荐

上一篇:5分钟快速上手ItemSlide.js:从零构建第一个触屏轮播图
下一篇:BiliPai Material You设计揭秘:液态玻璃与iOS风格底栏的视觉革命

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询