@qfei-design/make-ai-assistant
v0.3.10
Published
Platform-wide Make AI Assistant contracts, React shell, and context-aware artifact templates.
Keywords
Readme
@qfei-design/make-ai-assistant
面向 Make Console 与所有 Make App 的 AI 助手前端包。它提供统一的右下角浮动入口、右侧抽屉、流式会话状态、版本化 Artifact 协议、平台默认结果模板和 App 扩展机制。
Install
pnpm add --save-exact @qfei-design/make-ai-assistant@VERSIONVERSION 替换为完成本地验收的发布版本,提交 lockfile 固定制品。
Quick start
import type {
ArtifactActionErrorHandler,
ArtifactActionHandler,
AssistantTransport,
MakeAssistantHostContext,
} from "@qfei-design/make-ai-assistant";
import { MakeAiAssistant } from "@qfei-design/make-ai-assistant/react";
import "@qfei-design/make-ai-assistant/styles.css";
// context、transport 和动作回调均由宿主创建并传入;此处不读取凭据或直接请求 Make API。
export function AppAssistant({ context, transport, onAction, onActionError }: {
context: MakeAssistantHostContext;
transport: AssistantTransport;
onAction?: ArtifactActionHandler;
onActionError?: ArtifactActionErrorHandler;
}) {
return (
<MakeAiAssistant
brandName={context.app.name}
theme={{ primary: "#0f8277" }}
context={context}
transport={transport}
suggestions={["总结当前页面", "找出需要关注的记录"]}
onAction={onAction}
onActionError={onActionError}
/>
);
}这是单 Agent 的最小接入:宿主提供带当前 App/路径/语言/时区的 context,以及已绑定 Agent 的
transport;需要 Artifact 动作时再提供经过宿主权限校验的回调。Make App 接入参考
完整宿主示例;各组件属性与功能开关见 公共 API。
MakeAiAssistant 默认渲染右下角的动效入口和右侧抽屉。宿主可以使用 open / onOpenChange
受控管理开关,也可以用 AssistantPanel 嵌入自己的容器。内置浮动入口可通过 launcherPosition
设置为 bottom-left、bottom-right、top-left、top-right、left-center 或 right-center;默认值为
bottom-right。launcher 自定义渲染器自行负责位置。浮动入口首次打开后,关闭抽屉只隐藏 Drawer,不卸载 AssistantPanel,因此不会中断
正在进行的生成;重新打开时会按用户关闭前的滚动意图恢复消息位置,空闲时聚焦输入框,生成中
聚焦停止按钮。切换会话、App/身份或卸载根组件会释放本地订阅;远端任务是否继续取决于 transport 与服务端语义。Make App 的显式停止通过独立取消命令等待服务端终态;关闭订阅不会取消后台 Run。
宿主挂载与生命周期
将 MakeAiAssistant 作为全局浮动入口且需要跨路由持续运行时,宿主应在统一登录完成后的 App Shell 中只挂载一个实例,并把它放在路由 Outlet 或页面组件子树之外。页面、对象或记录切换时只更新 context;不要用页面开关条件渲染组件,不要使用随路由变化的 key,也不要在每次渲染时重建同一 App、用户、租户和 Agent 作用域内的 client/transport。抽屉开关通过 open / onOpenChange 管理:关闭只隐藏界面,活动流继续运行,不会取消后端 Run;只有用户触发显式停止时才进入取消流程。
组件卸载、切换 App、用户、租户或 Agent 时,应释放旧 client/transport 和本地订阅。对于通用 AssistantTransport,远端 Run 是否继续由 transport 与服务端语义决定。使用 /make-app 适配器时,关闭本地订阅不会调用 responses.cancel;已被服务端受理的任务仍可在重新接入后通过会话历史或权威快照恢复。页面级 AssistantPanel 可以随页面卸载;如果它也需要跨路由保留实时流和本地状态,宿主再将其提升到稳定容器,而不是把页面级嵌入模式一律改成全局挂载。
function AuthenticatedAppShell() {
const location = useLocation();
const context = useAssistantContext(location);
return (
<>
<Outlet />
<MakeAiAssistant context={context} transport={stableTransport} />
</>
);
}对于需要跨路由持续运行的全局助手,错误示例包括在每个页面内分别渲染 MakeAiAssistant、写成 {open && <MakeAiAssistant ... />},或传入 key={location.pathname}。这些写法会在导航或关闭时卸载组件,使当前页面失去实时流与本地状态。
标准位置确定锚点后,宿主可在 MakeAiAssistant 外层通过 CSS 变量微调偏移:
--make-ai-launcher-left、--make-ai-launcher-right、--make-ai-launcher-top、
--make-ai-launcher-bottom;窄屏可使用对应的 --make-ai-launcher-mobile-left、
--make-ai-launcher-mobile-right、--make-ai-launcher-mobile-top、
--make-ai-launcher-mobile-bottom 覆盖。每个标准位置只读取对应锚点的变量,另一侧始终为
auto;例如 bottom-right 使用 right 与 bottom,避免无关变量改变位置。比如:
<div className="sales-ai">
<MakeAiAssistant launcherPosition="bottom-right" {...assistantProps} />
</div>.sales-ai {
--make-ai-launcher-right: 24px;
--make-ai-launcher-bottom: 24px;
}默认会话会显示“AI 助手”身份;用户侧默认不显示名称,宿主传入 userName 或
userAvatarUrl 后才会在用户气泡上方展示当前登录人的名称或头像。宿主可通过 brandName
覆盖默认的 Make 品牌,面板会显示为“{品牌名} AI 助手”;传入 title 时则使用完整标题覆盖。
assistantName 可覆盖 AI 消息显示名称。这些显示配置只用于 React UI,不会随会话上下文发送到
AI transport;进度和可重试错误会显示在对应的 AI 回合中。
头栏仅展示居中的助手图标与 AI 标题,长标题会省略显示并保留原文 title 属性。subtitle 与
privacyNotice 继续接受旧调用传入的值以保持兼容,但不会参与渲染或发送到 AI transport。头栏默认高度为 50px,
可通过 headerHeight 属性或 --make-ai-header-height 主题变量覆盖。
Theme integration
MakeAiAssistant、AssistantPanel 和 ArtifactRenderer 都支持同一个 theme 属性。标题栏、内容区背景与用户提问气泡默认跟随宿主主题色;输入承载面固定白色,正文、通用边框保持包内中性色。
除标题栏、内容区背景与用户提问气泡外,主题色只用于竖线、圆点、图标、输入框焦点、发送按钮和标签等强调元素;成功、告警和错误仍使用独立语义色。Markdown 表格滚动条固定为中性灰,不跟随主题色。
import type { MakeAiTheme } from "@qfei-design/make-ai-assistant/react";
const hostTheme: MakeAiTheme = {
primary: "#0f8277",
primaryHover: "#09685f",
onPrimary: "#f7fffc",
};
<MakeAiAssistant theme={hostTheme} {...assistantProps} />;primary 为必填项,primaryHover 与 onPrimary 可选;若主色偏浅,请传入具有足够对比度的
onPrimary。内置 UI 的强调色优先级为 theme 属性 > --make-ai-theme-primary、
--make-ai-theme-primary-hover、--make-ai-theme-on-primary > --make-ai-accent >
宿主 --make-color-primary > 包内默认值。background 会控制内容区基础背景;surface、text 等其余中性色字段为兼容保留,
不会改变内置默认样式;它们仍会写入 renderContext.themeStyle,供自定义 Artifact 模板自行使用。
桌面端抽屉默认宽度为 560px,宿主仍可通过 --make-ai-drawer-width 覆盖固定宽度。包内最大宽度规则为 min(1024px, 72vw),但不会压缩默认或宿主指定的固定宽度;宿主可通过 maxDrawerWidth 传入像素值设置明确上限。抽屉不再提供左侧拖拽或键盘调整宽度;标题栏右侧“新建对话”左侧提供全屏/退出全屏按钮,全屏只作用于本组件的抽屉布局,不调用浏览器原生全屏。不大于 560px 的视口保持全宽展示并隐藏全屏按钮;从桌面切换到该断点时会退出已有的全屏状态。
面板使用容器查询而非页面视口响应宽度:回答区、结构化结果、代码块和 Markdown 表格会随抽屉可用宽度扩展;达到 760px 时,表格优先按可用宽度分列并换行,长段落和列表仍限制在 96ch 内以保障阅读性。全屏或宽面板中的消息时间流与底部输入区共用默认 920px 的居中阅读列,宿主可通过 --make-ai-conversation-max-width 覆盖。窄容器中的 Markdown 表格保留横向滚动保护,并使用细窄的中性灰滚动条。宿主可通过 --make-ai-panel-gutter-wide 覆盖扩展抽屉的内边距。
成功的 AI 回答会显示仅含图标的复制按钮,鼠标悬浮或键盘聚焦时显示“复制”提示,按钮保留
“复制回答”的无障碍名称;持久回答的喜欢/不喜欢也使用同样的图标交互,并分别显示“喜欢”与“不喜欢”提示。复制只处理该回答的文本内容,失败时会在本地 UI 内提示,不会把回答正文写入日志。AI 回答正文会按标题、
段落、列表、强调、代码片段和 Markdown 表格行做轻量结构化展示,不执行 HTML,也不引入 Markdown
运行时依赖。对话排版默认分为四级:标题和用户问题为 14px、AI 正文与结构化结果数据为 13px、身份/状态/操作等辅助信息为
12px、来源/时间/复制反馈等非关键注释为 11px。宿主可通过
--make-ai-font-size-heading、--make-ai-font-size-body、--make-ai-font-size-markdown、
--make-ai-font-size-meta 与 --make-ai-font-size-caption 覆盖对应层级。旧
--make-ai-font-size-ui 仅保留给复制、重新生成与复制反馈的兼容范围,不会影响新的辅助与注释层。只有当宿主 transport
实现可安全替换旧回答的 regenerate() 时,UI 才显示仅含图标的“重新生成”按钮,悬浮或聚焦时
显示“重新生成”提示,并复用该回答前一条用户问题
替换旧回答,不额外插入重复的用户气泡。用户滚离底部后,面板会显示仅含图标的“回到最新消息”
按钮,避免在阅读历史时强制跳到底部。
用户提交问题后,UI 会立即在其下方展示一个处理中回合;10 秒未收到结果时提示“仍在处理中”。
未开启输入协调时,45 秒仍未收到结果会中止本地等待并提供“重新发送”;输入协调已受理的消息随原 Run 等待,不使用单条消息的 45 秒截止时间。网络连接、空闲和总时限仍由标准 SDK 控制。这些时限为组件内置交互兜底,
不会替代宿主服务端的超时策略。未开启输入协调的 transport 在生成完成、失败或停止前禁用输入。Make App 内置 Agent 声明 input.coordination 时,运行期间可继续发送:首条消息按普通任务受理,任务运行中再次发送的消息默认排队。页面不会常驻展示发送方式选择;只有服务端确认存在排队消息后,才会在输入框上方显示紧凑队列条,支持“引导”到当前任务、删除或在队列暂停后继续执行。消息被绑定到任务后回到对话时间流,并在原用户消息下展示“已追加到当前任务”等状态。
started 和 steered 受理会直接使用回执中的 responseId 进入处理状态并订阅回答,不等待输入队列快照。选择会话时只读取一次队列快照;仅在存在真实排队消息,或当前任务仍有未消费的追加输入时继续刷新,空闲队列不会持续请求 /inputs。
输入提交失败会显示在对应的用户消息下;结果未知时使用原消息 ID 重试,已受理但订阅中断时只按 responseId 重连,不会重复提交消息。队列快照临时失败会有限退避重试,达到上限后显示独立的“重试”入口,该入口只刷新队列状态。
run.progress 会累积为处理步骤,仅在对应的 AI 回合处于生成中时显示。
步骤默认收起为“已执行 N 步”,用户可在等待阶段展开查看,并在首个回答文本或 Artifact 到达后立即隐藏。步骤仅应表达可安全公开的执行状态,
不能传递模型原始推理;完成、失败或从历史恢复后也不展示这些处理步骤,且过程文本不进入最终回答正文。
单次处理步骤最多 50 条且累计 20,000 字符,超限会进入可重试错误状态。
Make App transport 会将 queued、dispatched、running 映射为“请求已提交”“请求已分发”“正在生成回答”等安全状态;
更细的业务步骤需由宿主通过 run.progress 明确提供。
suggestions 省略时使用包内默认推荐问题,传入自定义数组时使用宿主内容,传入 [] 时隐藏推荐问题。
默认头部右侧会展示“新建对话”图标按钮;宿主将 transport.features.newConversation 显式设为
false 时隐藏该按钮。Make App 点击新建会持久创建 Chat,成功后切换会话;通用 transport 按其能力清理本地视图,并调用可选的 onNewConversation 回调。
Package provides
- Artifact V1 类型、运行时校验与 JSON Schema;
- Artifact 模板注册、能力协商和稳定降级;
- 纯会话 reducer 与
AssistantTransport合同; /sse入口提供可直接配置 endpoint 的createSseAssistantTransport;/make-app入口提供现有 Make App AI 会话、历史消息和 SSE 事件的协议适配器;/make-console入口提供 Console Agent、Session、持久事件和 Run SSE 的协议适配器;MakeAiAssistant、AssistantPanel和ArtifactRenderer;- metric、comparison、trend、ranking、record-list、notice 六类默认模板;
- Mock transport 与 Gallery fixtures;
- 可由宿主覆盖的
--make-ai-*CSS 主题变量。
Host app provides
- 当前 App、路径、Entity/Record/View 等最小上下文;
- 实现
AssistantTransport,或向/make-app、/make-console适配器注入已认证的语义请求和事件订阅能力; - Artifact 动作到宿主路由、命令与权限检查的映射;
- Service
/api、认证、数据查询、模型运行和服务端权限校验; - 可选的 App 领域模板和主题变量。
包自身不导入 Make 认证 SDK、不持有或透传 Make token,也不执行服务端生成的 HTML、JSX、CSS 或 JavaScript。/make-app 与 /make-console 只负责协议归一化,所有网络 I/O 均由宿主注入。
React/ReactDOM 是 UI 入口的可选 peer dependency;只使用根入口、/sse、/make-app、/make-console 或 /testing
的 Service/Node 工具不需要安装 React。
只使用 AssistantPanel 做嵌入式接入,或单独使用 ArtifactRenderer 时,同一个
styles.css 入口仍会提供默认主题变量、盒模型、可见焦点和 reduced-motion 处理。
Artifact template extension
import {
createPlatformArtifactRegistry,
platformArtifactTemplates,
type ReactArtifactTemplate,
} from "@qfei-design/make-ai-assistant/react";
const appTemplate: ReactArtifactTemplate = {
id: "sales.metric.target-progress",
kinds: ["metric"],
priority: 50,
canRender: (artifact, context) =>
context.app.id === "sales-app" && artifact.meta?.semantic === "target-progress",
render: (artifact) => <SalesTargetProgress artifact={artifact} />,
};
const registry = createPlatformArtifactRegistry([
...platformArtifactTemplates,
appTemplate,
]);后端可以通过 presentation.template 请求某个已协商模板;若不匹配,前端会按优先级和 canRender 选择其他安全模板。
模板注册时会校验 kind、priority 和回调类型,并保存只读快照。
Transport
AssistantTransport.run() 接收消息、上下文和前端能力目录,返回 AsyncIterable<AssistantEvent>。
如后端支持“替换已有回答”语义,可额外实现 AssistantTransport.regenerate();UI 只在该能力存在时
显示“重新生成”。Mock、原生 SSE 和 AG-UI 都应适配到同一合同,因此更换后端不会改 UI。
标准 SSE 后端可直接使用:
import { createSseAssistantTransport } from "@qfei-design/make-ai-assistant/sse";
const assistantTransport = createSseAssistantTransport({
endpoint: "/api/assistant/runs",
credentials: "include",
maxEventCharacters: 1_000_000,
});客户端入口为 @qfei-design/make-ai-assistant/client。App 宿主提供正式认证 HTTP 传输,标准客户端统一管理 20 项接口、固定 /v1 路径、响应解码、分页、重试、字节上传与 SSE。/make-app 把客户端结果映射到已有助手 UI 状态机。
import { createMakeAgentClient } from "@qfei-design/make-ai-assistant/client";
import { createMakeAppAssistantTransport } from "@qfei-design/make-ai-assistant/make-app";
const client = createMakeAgentClient({
apiVersion: "v1",
scope: { appKey, identityKey },
transport: authenticatedTransport,
});
const assistantTransport = await createMakeAppAssistantTransport({ client, agentId });
// 将 assistantTransport 传入 AssistantPanel / MakeAiAssistant。
// App、用户或租户改变时,取消旧连接并 client.dispose(),重新创建实例。一个 Make App 只绑定一个应用内 Agent。宿主负责取得该 Agent 的 agentId,并创建对应的
transport 传给组件;包不提供 Agent 选择器。模型选择是独立能力,当前不在组件合同中。
AuthenticatedTransport.request 只执行一次已认证 I/O,返回 status、headers 和 AsyncIterable。retryOwner: "sdk" 由 SDK 按幂等语义做有限重试;retryOwner: "transport" 不叠加 SDK 重试。宿主沿正式 auth.api 处理登录、401/403 和 Cookie,SSE/二进制成功体不经过 JSON 或 text() 缓冲。SDK 不读取 Token、Cookie、LocalStorage 或 Provider key。
Make Console 可以使用独立 adapter:
import { createMakeConsoleAssistantTransport } from "@qfei-design/make-ai-assistant/make-console";
const assistantTransport = createMakeConsoleAssistantTransport({
listAgents: (request, options) =>
makeConsoleAiClient.listAgents(request, options),
getOrCreateSession: (request, options) =>
makeConsoleAiClient.getOrCreateSession(request, options),
loadEvents: (request, options) =>
makeConsoleAiClient.loadEvents(request, options),
sendMessage: (request, options) =>
makeConsoleAiClient.sendMessage(request, options),
eventSourceFactory: (subscription, init) =>
makeConsoleAiClient.subscribeRun(subscription, init),
});该 adapter 默认选择第一个 status=enabled 的 Agent;多个 Agent 场景可通过 selectAgent
按宿主上下文选择。它会幂等定位当前用户 Session、从持久事件恢复历史、把
output_text.delta 归一化为文本增量,并在 response.completed 后读取持久事件完成最终
对账。收到 fallback 或发送结果没有可订阅 Run 时,会从用户消息 seq + 1 开始轮询持久
事件。Console 输入按后端合同限制为 4000 个 Unicode 字符。当前 Console adapter 不提供
regenerate(),避免用普通 sendMessage 伪装重新生成后在持久历史中产生重复用户消息。
Console 宿主负责把这些语义回调映射到 /api/make/console/v1,并通过现有登录体系使用
credentials: "include" / withCredentials: true。包不会读取、写入或复制 zs_session。
Console BFF 与 Make App Browser API 是两个独立宿主协议,不应只替换 URL 强行复用。
需要兼容已有事件协议时,在宿主内实现薄 adapter;不要修改聊天组件或复制 Artifact 模板。
流必须以 run.complete、run.cancelled 或 error 终止;异常断流会进入可重试错误状态。
停止生成按钮悬浮或键盘聚焦时会显示“停止生成”提示;Make App 点击后等待远程终态;标准 SSE transport 停止时取消底层 reader 并释放本地输入锁。关闭抽屉只隐藏 UI,不会触发该取消流程。
/sse 的公开类型不依赖 TypeScript DOM lib,现代 Node 宿主也可直接消费。
调用前已经取消的 AbortSignal 不会触发网络请求。
助手只发送宿主提供的上下文,实际数据权限始终由宿主 Service 在服务端重新校验。
生产接口建议和 SSE 事件定义见仓库 docs/backend-contract.md。AG-UI 在这里是可选的前后端事件协议,不是生成 React 组件的 UI 库。
Validation
pnpm test
pnpm typecheck
pnpm build发布统一走 GitLab CI;不要从功能分支本地执行 npm publish。
会话自动标题
Make App 的 Chat 包含 titlePending。首条长消息的摘要完成后,助手通过每秒一次的待摘要会话详情刷新自动更新列表,独立于正文流式完成时间;短标题和手工命名结束等待,旧 revision 不覆盖当前状态。详情刷新经过标准 client.chats.get 和正式认证传输,不增加登录或直连模型逻辑。
