Skip to content

助手入口、布局与任务入口 ​

可运行示例在浏览器中运行本章示例

这里运行与本地夹具相同的 React 源码。模型请求由服务端网关转发,供应商密钥不会进入浏览器。

本地运行pnpm tutorial:dev r-13 --locale zh源码examples/tutorials/r-13-assistant-surfaces-layout-and-task-entry-points/

待办列表已经支持新增、筛选、详情页和多步任务。到此为止,助手仍然只有一个固定的浮动按钮。用户要问“这个页面还有哪些未完成”,需要先找到按钮,再自己组织问题。

这对内容页尚可接受,对工作台就不够。设置页可能需要右侧常驻一栏;数据看板更适合紧凑的浮层;某一次业务操作则应当有明确入口,例如“总结未完成待办”。这些入口属于同一名助手,只是被放到不同位置、由不同事件触发。

本章处理助手与页面之间的这层关系。它不增加新的页面动作,也不改变权限边界。要解决的是三个产品问题:

  • 对话出现在哪里;
  • 用户如何找到它;
  • 页面能否用一句业务语言把任务交给它。

多个入口不等于多个助手 ​

最容易出现的实现方式是:列表旁挂一个助手,详情页再挂一个,每个业务按钮各自启动一次对话。局部看都合理,放在一起会产生一份并不必要的复杂度。

每个入口各自持有会话一份会话,多个入口
切换页面或布局后历史消失历史跟随应用,而不是跟随外壳
每个副本重复配置模型、限额与观测模型和限额只有一份
用户在不同入口重复交代背景后续入口能读到前面的对话
审批、审计散落在多个运行时动作仍经过同一道策略边界

可以把它理解成桌面应用里的一个文档和多个窗口。窗口可以浮动、停靠或隐藏,文档本身只有一份。AgentDock 负责窗口,AgentInline 承载对话;任务入口只负责把一句请求递进去。

外壳与会话分开 ​

React 适配器把这两种职责拆成两个组件。

AgentDock 是位置外壳。它负责浮动或贴边、展开与收起、启动按钮,以及登记任务入口。默认用法不变:

tsx
<main>
  <TaskList />
  <AgentDock title={copy.dockTitle} />
</main>

AgentInline 是没有固定定位、没有启动按钮或收起动作、没有遮罩的会话界面。它适合占据页面栅格中的固定一栏,例如工单工作台的右侧助手列。

两者最重要的共同点是:外壳改变时,会话运行时没有被重新创建。收起 AgentDock 只是不再渲染面板,组件仍在页面上,历史、审批状态和当前模型配置都还在。

浮动与侧栏 ​

示例先保留浮动入口,再允许切换为贴边侧栏:

tsx
<AgentDock
  title={copy.dockTitle}
  debug={{ maxCalls: 8 }}
  layout={layout}
  side={side}
  launcherPlacement={launcherPlacement}
  launcherLabel={copy.launcherText}
  surfaceId="dock"
/>

layout 有两个常用值:

参数取值作用
layout"dock" / "sidebar"页面角落的浮层,或视口边缘的工作栏
side"right" / "left"侧栏贴在哪一侧
launcherPlacement"bottom" / "center"收起后入口位于底部角落,或作为边缘标签居中
launcherLabel字符串收起入口的文案;默认使用当前语言的“Ask Pling”
surfaceId字符串任务路由使用的稳定标识,默认是 "dock"

side、launcherPlacement 和 launcherLabel 只在侧栏模式下有意义。浮动模式忽略这些位置参数。布局可以随用户偏好或窗口尺寸改变,但不需要卸载后再挂载一个新的助手。

侧栏在窄屏下会降级为浮动面板。全高侧栏在手机上会挤掉工作区域;保留入口、改换形态比强行维持桌面布局更实用。这个降级由组件内部完成,业务代码不需要再写一套移动端分支。

把助手放进页面栅格 ​

当助手不是“随时可以收起的工具”,而是工作台的一栏时,使用 AgentInline:

tsx
<section className="workspace">
  <OrderTable />
  <AgentInline
    className="workspace__copilot"
    title="Order copilot"
    surfaceId="order-copilot"
  />
</section>

此时宽度、高度、滚动和响应式行为属于外层布局。AgentInline 只负责对话本身。className 加在组件外壳上,可以直接参与栅格或 flex 布局。

两种组件的选择标准不是哪个更“高级”,而是助手是否长期占用页面空间:

  • 偶尔求助、内容阅读、表单填写:浮动入口更轻;
  • 长时间编辑、审核队列、运营工作台:固定一栏更稳定;
  • 助手就是主要工作对象:应当由页面布局为它预留空间。

任务入口是给人用的 ​

通用启动按钮适合“我想问点什么”。具体业务仍需要更明确的入口。示例在页面上放了两个按钮:

tsx
<div className="surface-settings__tasks">
  <button
    type="button"
    data-task-entry="summarize"
    onClick={() => void pling.ask(copy.openTasksPrompt).catch(() => undefined)}
  >
    {copy.openTasksTask}
  </button>

  <button
    type="button"
    data-task-entry="ask"
    onClick={() => void pling.ask(copy.pageQuestion)}
  >
    {copy.pageQuestionTask}
  </button>
</div>

React 没有 v-task 指令。等价的写法就是一个普通按钮调用 pling.ask()。如果需要带上触发元素,可传第二个参数:

ts
pling.ask("解释这笔订单的风险。", { source: event.currentTarget });

pling.ask() 返回 Promise。示例在不需要继续处理结果的地方显式捕获错误,避免 unhandled rejection;真正需要提示用户的调用方仍应处理失败。

入口可以针对当前页面使用业务语言,例如“总结未完成待办”,不必要求用户先理解动作清单或模型能力。请求进入助手后,仍由同一个会话循环读取当前页面快照、选择动作并执行。

为什么任务入口不注册成动作 ​

任务入口与动作方向相反:

  • 动作:智能体可以操作页面上的这个元素;
  • 任务入口:人可以请智能体处理一件事。

如果 pling.ask() 也被登记成动作,模型会看到一个“给自己再发一句提示词”的工具。后果不是多一个方便功能,而是动作清单变噪、轮数可能递归、审批也会落在没有业务价值的节点上。

因此任务入口不进入注册表,也不应出现在模型上下文的动作列表里。模型需要完成业务操作时,应当调用真实动作;用户需要发起任务时,使用任务入口。

这不是权限捷径。任务入口只改变请求的来源,不会绕过动作声明、可用性判断、确认或模型网关。助手最终能做什么,仍由当前页面的动作与策略决定。

收起、展开与共享历史 ​

示例默认收起浮动入口。点击“总结未完成待办”时,AgentDock 自动展开并发送请求。用户不需要先打开助手。

再点击“从页面代码提问”,第二条请求进入同一段对话。第二次模型调用可以看到第一次的用户消息和回复,而不是从一份全新的历史开始。测试直接检查这一点:第二次请求的消息中包含第一次任务和本次任务。

如果应用有多个具名 surface,调用时可以指定目标:

ts
await pling.ask("总结当前订单风险。", { surface: "order-copilot" });

没有找到对应 surface 时会明确失败:

  • 页面没有挂载任何助手:PLING_TASK_SURFACE_MISSING;
  • 指定了名字但没有匹配项:PLING_TASK_SURFACE_UNKNOWN;
  • 提示词为空:PLING_TASK_EMPTY。

明确失败比静默丢弃更适合调试。任务入口通常是业务代码的一部分,调用方可以据此提示用户或记录诊断。

布局不等于权限 ​

layout="sidebar"、side="left" 或自定义启动文案只改变展示。它们不能限制助手能做什么,也不能扩大助手能做什么。

权限仍来自三处:

  • 当前页面上声明了哪些动作;
  • 动作的可用性与风险等级;
  • 应用级策略、确认通道和模型网关。

同理,任务入口只是发起请求。它既不是“更高权限的快捷方式”,也不是“模型自己的工具”。

选择入口时看工作流 ​

场景合理默认值
内容页上的偶尔求助浮动 dock
长时间编辑、密集信息工作区贴边侧栏
助手本身就是主要工作栏页面栅格中的 AgentInline
单条记录或单个业务操作页面上的任务入口
已有工具栏或命令面板从应用代码调用 pling.ask()

不要因为页面有几个区域就挂载几个 AgentDock。通常更好的做法是移动一个外壳,或者让多个任务入口指向同一个 surface。只有产品确实需要不同权限、不同历史或不同受众时,多运行时才有意义。

验证 ​

启动示例:

bash
pnpm tutorial:dev r-13 --locale zh

示例源码位于 examples/tutorials/r-13-assistant-surfaces-layout-and-task-entry-points。教程只要求安装本地工作区中的 Pling 包;示例本身不复制框架实现。

打开 http://127.0.0.1:5253/?lang=zh,依次检查:

  1. 在浮动入口与页面侧栏之间切换。已经收到的回复仍然存在。
  2. 选择侧栏后,切换左右边缘和收起入口的位置。
  3. 收起助手,再点击“总结未完成待办”。助手应自动展开并开始请求。
  4. 点击“从页面代码提问”。第二次请求应出现在同一段对话里。
  5. 查看模型工具清单。两个任务按钮不应出现。

确定性检查覆盖布局参数、收起后自动展开、共享历史、中英文文案和“不注册为动作”:

bash
pnpm tutorial:check r-13

常见误判 ​

为每个入口建立独立会话。 从界面看只是两个小窗口,从行为看却是两段互不相干的上下文。用户需要重新交代背景,审批记录也无法形成一条完整链路。

把任务按钮同时声明为动作。 按钮是给人的,就不要让模型调用它。若模型需要执行某个业务操作,直接声明那个操作,而不是让它调用一个“发送提示词”的工具。

把布局参数当成能力开关。 改变位置和外观只影响可用性,不改变安全边界。权限设计应当落在动作、策略和确认上。