回答操作事件客户端协议

版本:1.0
状态:客户端已实现
适用范围:Lanque App 回答卡片操作区

1. 目标与边界

回答操作事件用于在最终回答中提供“查看详情、修改展示、继续处理、确认执行”等入口。

客户端只接受声明式 JSON,不接受可执行代码。事件分为两类:

  • 本地展示操作:只修改当前回答的展示,不请求模型、不调用 MCP。
  • 后续业务操作:转换为一条规范请求,通过当前 chat.send() 重新进入既有编排。

本协议不会建立新的工具执行通道。工具选择、MCP 调用、用户权限和高风险工具审批仍由现有编排负责。

2. actions 放置位置

客户端支持在以下两种结构中携带 actions

2.1 结构化 answer.data

{
  "schemaVersion": "1.0",
  "answerKind": "maintenance.report",
  "datasets": {
    "plans": [],
    "faults": []
  },
  "actions": [
    {
      "id": "continue-analysis",
      "type": "conversation.continue",
      "label": "继续分析",
      "params": {
        "prompt": "继续分析异常月份和可能原因"
      }
    }
  ]
}

2.2 固定 AnswerPayload

{
  "template": "report",
  "data": {
    "plans": [],
    "faults": []
  },
  "actions": [
    {
      "id": "show-detail",
      "type": "view.detail",
      "label": "查看详情"
    }
  ]
}

当两处同时存在时,优先使用 answerData.actions

3. 通用结构

interface AnswerAction {
  id: string;
  type: AnswerActionType;
  label: string;
  style?: 'primary' | 'default' | 'danger';
  title?: string;
  description?: string;
  riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
  requiresConfirmation?: boolean;
  target?: {
    dataset?: string;
    componentId?: string;
  };
  params?: {
    prompt?: string;
    chartType?: ChartEncoding;
  };
}

3.1 通用参数

参数必填限制用途
id非空,最多 80 字符,同一回答内不可重复操作稳定标识及状态持久化键
type必须是客户端白名单事件决定操作路由
label非空,最多 32 字符按钮文案
styleprimarydefaultdanger只控制按钮视觉,不影响权限
title最多 80 字符确认区标题或按钮提示
description最多 240 字符操作说明及确认区影响描述
riskLevelLOWMEDIUMHIGH客户端确认策略参考
requiresConfirmationtrue 有效强制在执行前确认
target.dataset最多 120 字符预留的数据集定位字段;当前版本打开整组数据明细
target.componentId最多 120 字符定位当前回答中的图表组件
params.prompt按事件非空,最多 800 字符交给现有编排的规范请求,或填入输入框的修改前缀
params.chartType按事件必须是受支持的图表类型本地切换图表形式

单条回答最多处理前 8 个合法动作。未知字段不会透传给执行层。

4. 已支持事件

4.1 view.detail

用途:展开当前回答下方的数据明细。

项目当前行为
必填参数无额外参数
本地执行
是否重新请求
是否需要确认默认不需要
持久化状态记录操作成功状态

示例:

{
  "id": "view-raw-records",
  "type": "view.detail",
  "label": "查看原始记录",
  "target": {
    "dataset": "faults"
  }
}

当前版本接受 target.dataset,但仍会展开本回答的完整数据明细,不会只筛选某一个数据集。

4.2 render.change

用途:在本地修改指定统计图的呈现形式。

项目当前行为
必填参数params.chartType
推荐参数target.componentId
本地执行
是否重新查询数据
是否改变统计口径
持久化图表覆盖和操作状态都随会话保存

支持的 chartType

展示形式
bar柱状图
row横向条形图
line折线图
area面积图
donut环形图
scatter散点图标识;当前固定图表渲染会按已有兼容策略处理
table表格

示例:

{
  "id": "monthly-plan-to-line",
  "type": "render.change",
  "label": "换成折线图",
  "target": {
    "componentId": "monthly-plan-chart"
  },
  "params": {
    "chartType": "line"
  }
}

执行限制:

  • componentId 找不到时操作失败。
  • 未提供 componentId 且存在多张兼容图表时,客户端要求进一步指定图表。
  • 图表与数据形态不兼容时拒绝切换,例如无序分类数据不能强行表现为时间趋势。

4.3 render.reset

用途:恢复当前回答中所有图表的客户端默认形式。

项目当前行为
必填参数
本地执行
是否重新请求
作用范围当前回答的全部图表覆盖
持久化保存恢复后的状态

示例:

{
  "id": "reset-charts",
  "type": "render.reset",
  "label": "恢复默认展示"
}

4.4 conversation.continue

用途:基于当前回答继续分析或继续查询。

项目当前行为
必填参数params.prompt
本地执行
执行去向当前运行模式的现有 chat.send() 链路
MCP 与鉴权完全沿用现有编排
是否确认默认不确认;可通过风险参数强制确认

示例:

{
  "id": "analyze-anomalies",
  "type": "conversation.continue",
  "label": "继续分析",
  "params": {
    "prompt": "继续分析故障高发部件,并说明可能原因"
  }
}

4.5 conversation.modify

用途:给用户提供修改当前结果的入口。

项目当前行为
params.prompt可选,缺省为“请修改当前结果:”
本地执行
是否自动发送
实际行为将修改前缀填入输入框,由用户补充并确认发送

示例:

{
  "id": "modify-current-view",
  "type": "conversation.modify",
  "label": "修改展示",
  "params": {
    "prompt": "请修改当前结果的展示方式:"
  }
}

4.6 business.execute

用途:发起创建、下发、修改等会产生业务影响的后续请求。

项目当前行为
必填参数params.prompt
客户端确认始终需要
执行去向确认后进入现有 chat.send() 编排
是否直接调用工具
后端鉴权和 MCP 审批仍然必须通过

示例:

{
  "id": "create-maintenance-order",
  "type": "business.execute",
  "label": "生成检修工单",
  "style": "primary",
  "title": "确认生成检修工单?",
  "description": "将为 HXN5-001 发起工单创建请求,最终仍需通过您的业务权限校验。",
  "riskLevel": "HIGH",
  "params": {
    "prompt": "为 HXN5-001 创建检修工单"
  }
}

4.7 business.cancel

用途:发起取消任务、取消工单等业务请求。

项目当前行为
必填参数params.prompt
客户端确认始终需要
推荐样式danger
执行去向确认后进入现有编排
是否直接调用工具

示例:

{
  "id": "cancel-task-2048",
  "type": "business.cancel",
  "label": "取消任务",
  "style": "danger",
  "description": "取消后任务将停止继续调度。",
  "params": {
    "prompt": "取消任务 TASK-2048"
  }
}

4.8 answer.refresh

用途:使用产生当前回答的原问题重新查询。

项目当前行为
必填参数
使用内容当前回答对应的原始用户问题
执行去向现有 chat.send() 编排
params.prompt当前版本忽略

示例:

{
  "id": "refresh-report",
  "type": "answer.refresh",
  "label": "刷新数据"
}

5. 客户端自动补充的入口

即使回答没有携带 actions,客户端也会根据当前内容补充安全操作:

条件自动入口事件
回答存在完整数据明细查看详情view.detail
当前回答注册了统计图修改展示conversation.modify
能找到产生回答的原问题继续处理conversation.continue

客户端不会自动生成 business.executebusiness.cancel,避免从数据内容猜测业务操作。

如果外部已经提供同类型动作,客户端不再重复补充对应默认入口。

6. 确认策略

满足以下任一条件时,客户端在执行前显示确认区:

  • requiresConfirmationtrue
  • riskLevelMEDIUM
  • riskLevelHIGH
  • 事件类型为 business.execute
  • 事件类型为 business.cancel

确认区会展示:

  • title,未提供时使用“确认{label}?”;
  • description,未提供时显示默认权限说明;
  • 即将提交的 params.prompt
  • “取消”和“确认执行”按钮。

这里的确认只代表用户允许客户端继续提交请求,不代替后端权限校验和 MCP 高风险审批。

7. 操作状态

type AnswerActionStatus =
  | 'idle'
  | 'confirming'
  | 'running'
  | 'success'
  | 'error'
  | 'cancelled';
状态含义
idle可操作或已将内容填入输入框
confirming正在等待用户确认
running正在本地处理或向现有编排提交
success本地处理完成或请求已被现有编排接收
error参数、目标、兼容性或提交过程失败
cancelled用户在确认区取消

状态结构:

{
  "status": "success",
  "message": "展示已修改",
  "updatedAt": "2026-08-27T07:00:00.000Z"
}

actionStateschartPresentation 都保存在回答消息的会话检查点中,因此本地展示修改在切换会话后仍可恢复。

“请求已被接收”不代表业务操作最终成功。最终业务结果仍以随后生成的回答及真实工具结果为准。

8. 校验与拒绝规则

以下动作会被直接丢弃,不渲染按钮:

  • actions 不是数组;
  • type 不在事件白名单中;
  • 缺少或超长的 idlabel
  • 同一回答中出现重复 id
  • conversation.continue 缺少 params.prompt
  • business.execute 缺少 params.prompt
  • business.cancel 缺少 params.prompt
  • render.change 缺少合法的 params.chartType

以下字段即使出现在输入中,也不会进入客户端执行对象:

  • 工具名,例如 tooltoolName
  • MCP 服务器名;
  • URL;
  • JavaScript 或回调;
  • RPC 方法名;
  • 任意业务参数对象;
  • 未列入本协议的 params 字段。

因此下面的内容不会直接调用 MCP:

{
  "id": "unsafe",
  "type": "business.execute",
  "label": "执行",
  "tool": "mcp__delete_all",
  "params": {
    "prompt": "执行指定业务操作",
    "arguments": {
      "admin": true
    }
  }
}

客户端最多只保留合法的 idtypelabel、视觉说明、风险信息、受控目标以及 prompt/chartType。其中 toolarguments 会被删除。

9. 完整示例

{
  "schemaVersion": "1.0",
  "answerKind": "maintenance.report",
  "datasets": {
    "plans": [],
    "faults": []
  },
  "actions": [
    {
      "id": "detail",
      "type": "view.detail",
      "label": "查看详情"
    },
    {
      "id": "chart-line",
      "type": "render.change",
      "label": "换成折线图",
      "target": {
        "componentId": "monthly-plan-chart"
      },
      "params": {
        "chartType": "line"
      }
    },
    {
      "id": "continue",
      "type": "conversation.continue",
      "label": "继续分析",
      "params": {
        "prompt": "继续分析故障高发月份及可能原因"
      }
    },
    {
      "id": "create-order",
      "type": "business.execute",
      "label": "生成工单",
      "style": "primary",
      "riskLevel": "HIGH",
      "description": "将发起工单创建请求。",
      "params": {
        "prompt": "根据当前检修结果生成待确认的检修工单"
      }
    }
  ]
}

10. 当前未实现或预留能力

  • target.dataset 精确展开单个数据集;当前展开全部数据明细。
  • 任意组件类型之间的通用替换;当前 render.change 主要处理已注册统计图。
  • 由客户端直接调用 MCP 或后端业务接口。
  • 在原回答内部展示后续业务操作的最终执行结果;当前状态表示“已提交”,最终结果作为下一条回答呈现。
  • 服务端签发的不可伪造 action token。
  • 跨设备同步动作状态;当前跟随本地会话检查点。

11. 对接检查清单

提供 actions 的一方只需确认:

  1. actionsdatasets 同级。
  2. 每个动作有唯一且稳定的 id
  3. 使用本协议列出的 type
  4. 按类型提供 promptchartType
  5. 业务动作的 prompt 必须明确对象和动作,不使用“它”“那个”等模糊指代。
  6. 高风险动作提供清晰的 description 和正确的 riskLevel
  7. 不传工具名、鉴权信息、令牌和任意执行参数。

客户端即使收到格式错误的动作,也只会忽略相应按钮,不会影响回答正文和数据渲染。