助手入口、布局与任务入口
pnpm tutorial:dev r-13 --locale zh源码examples/tutorials/r-13-assistant-surfaces-layout-and-task-entry-points/待办列表已经支持新增、筛选、详情页和多步任务。到此为止,助手仍然只有一个固定的浮动按钮。用户要问“这个页面还有哪些未完成”,需要先找到按钮,再自己组织问题。
这对内容页尚可接受,对工作台就不够。设置页可能需要右侧常驻一栏;数据看板更适合紧凑的浮层;某一次业务操作则应当有明确入口,例如“总结未完成待办”。这些入口属于同一名助手,只是被放到不同位置、由不同事件触发。
本章处理助手与页面之间的这层关系。它不增加新的页面动作,也不改变权限边界。要解决的是三个产品问题:
- 对话出现在哪里;
- 用户如何找到它;
- 页面能否用一句业务语言把任务交给它。
多个入口不等于多个助手
最容易出现的实现方式是:列表旁挂一个助手,详情页再挂一个,每个业务按钮各自启动一次对话。局部看都合理,放在一起会产生一份并不必要的复杂度。
| 每个入口各自持有会话 | 一份会话,多个入口 |
|---|---|
| 切换页面或布局后历史消失 | 历史跟随应用,而不是跟随外壳 |
| 每个副本重复配置模型、限额与观测 | 模型和限额只有一份 |
| 用户在不同入口重复交代背景 | 后续入口能读到前面的对话 |
| 审批、审计散落在多个运行时 | 动作仍经过同一道策略边界 |
可以把它理解成桌面应用里的一个文档和多个窗口。窗口可以浮动、停靠或隐藏,文档本身只有一份。AgentDock 负责窗口,AgentInline 承载对话;任务入口只负责把一句请求递进去。
外壳与会话分开
React 适配器把这两种职责拆成两个组件。
AgentDock 是位置外壳。它负责浮动或贴边、展开与收起、启动按钮,以及登记任务入口。默认用法不变:
<main>
<TaskList />
<AgentDock title={copy.dockTitle} />
</main>AgentInline 是没有固定定位、没有启动按钮或收起动作、没有遮罩的会话界面。它适合占据页面栅格中的固定一栏,例如工单工作台的右侧助手列。
两者最重要的共同点是:外壳改变时,会话运行时没有被重新创建。收起 AgentDock 只是不再渲染面板,组件仍在页面上,历史、审批状态和当前模型配置都还在。
浮动与侧栏
示例先保留浮动入口,再允许切换为贴边侧栏:
<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:
<section className="workspace">
<OrderTable />
<AgentInline
className="workspace__copilot"
title="Order copilot"
surfaceId="order-copilot"
/>
</section>此时宽度、高度、滚动和响应式行为属于外层布局。AgentInline 只负责对话本身。className 加在组件外壳上,可以直接参与栅格或 flex 布局。
两种组件的选择标准不是哪个更“高级”,而是助手是否长期占用页面空间:
- 偶尔求助、内容阅读、表单填写:浮动入口更轻;
- 长时间编辑、审核队列、运营工作台:固定一栏更稳定;
- 助手就是主要工作对象:应当由页面布局为它预留空间。
任务入口是给人用的
通用启动按钮适合“我想问点什么”。具体业务仍需要更明确的入口。示例在页面上放了两个按钮:
<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()。如果需要带上触发元素,可传第二个参数:
pling.ask("解释这笔订单的风险。", { source: event.currentTarget });pling.ask() 返回 Promise。示例在不需要继续处理结果的地方显式捕获错误,避免 unhandled rejection;真正需要提示用户的调用方仍应处理失败。
入口可以针对当前页面使用业务语言,例如“总结未完成待办”,不必要求用户先理解动作清单或模型能力。请求进入助手后,仍由同一个会话循环读取当前页面快照、选择动作并执行。
为什么任务入口不注册成动作
任务入口与动作方向相反:
- 动作:智能体可以操作页面上的这个元素;
- 任务入口:人可以请智能体处理一件事。
如果 pling.ask() 也被登记成动作,模型会看到一个“给自己再发一句提示词”的工具。后果不是多一个方便功能,而是动作清单变噪、轮数可能递归、审批也会落在没有业务价值的节点上。
因此任务入口不进入注册表,也不应出现在模型上下文的动作列表里。模型需要完成业务操作时,应当调用真实动作;用户需要发起任务时,使用任务入口。
这不是权限捷径。任务入口只改变请求的来源,不会绕过动作声明、可用性判断、确认或模型网关。助手最终能做什么,仍由当前页面的动作与策略决定。
收起、展开与共享历史
示例默认收起浮动入口。点击“总结未完成待办”时,AgentDock 自动展开并发送请求。用户不需要先打开助手。
再点击“从页面代码提问”,第二条请求进入同一段对话。第二次模型调用可以看到第一次的用户消息和回复,而不是从一份全新的历史开始。测试直接检查这一点:第二次请求的消息中包含第一次任务和本次任务。
如果应用有多个具名 surface,调用时可以指定目标:
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。只有产品确实需要不同权限、不同历史或不同受众时,多运行时才有意义。
验证
启动示例:
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,依次检查:
- 在浮动入口与页面侧栏之间切换。已经收到的回复仍然存在。
- 选择侧栏后,切换左右边缘和收起入口的位置。
- 收起助手,再点击“总结未完成待办”。助手应自动展开并开始请求。
- 点击“从页面代码提问”。第二次请求应出现在同一段对话里。
- 查看模型工具清单。两个任务按钮不应出现。
确定性检查覆盖布局参数、收起后自动展开、共享历史、中英文文案和“不注册为动作”:
pnpm tutorial:check r-13常见误判
为每个入口建立独立会话。 从界面看只是两个小窗口,从行为看却是两段互不相干的上下文。用户需要重新交代背景,审批记录也无法形成一条完整链路。
把任务按钮同时声明为动作。 按钮是给人的,就不要让模型调用它。若模型需要执行某个业务操作,直接声明那个操作,而不是让它调用一个“发送提示词”的工具。
把布局参数当成能力开关。 改变位置和外观只影响可用性,不改变安全边界。权限设计应当落在动作、策略和确认上。

