Skip to content

ChatPanel:顶层布局与工具装配

源码版本v0.73.1

ChatPanelpi-web-ui 的顶层 customElement(<pi-chat-panel>)。它本身不跑会话,而是把 AgentInterfaceArtifactsPaneltoolsFactory 几样东西拼到一起:接收外部传进来的 Agent,创建 agent-interface 子元素并赋上 session,根据屏幕宽度在「并排」和「覆盖」两种 artifacts 布局间切换,调用 toolsFactory 让宿主注入额外的 AgentTool

职责

  1. 持有 agent 与 agentInterface:@state agent@state agentInterface 两个字段,agent 由 host 传入,agentInterfacesetAgentdocument.createElement 创建,见 packages/web-ui/src/ChatPanel.ts:17-25
  2. 装配会话宿主:setAgent 创建 AgentInterface、设置 enableAttachments/onApiKeyRequired/onBeforeSend 等回调,把 agent 作为 session 挂上去,见 packages/web-ui/src/ChatPanel.ts:56-85
  3. 装配 artifacts 面板:new ArtifactsPanel()、注入 sandboxUrlProvider、注册 ArtifactsToolRenderer 到 renderer registry,见 packages/web-ui/src/ChatPanel.ts:86-94
  4. 构造 runtime providers:runtimeProvidersFactory 把会话历史里的附件聚合成 AttachmentsRuntimeProvider,加上可读写的 ArtifactsRuntimeProvider,供 REPL 工具消费,见 packages/web-ui/src/ChatPanel.ts:96-116
  5. 响应式布局:windowWidth < 800 切到 overlay,否则并排 50/50,artifacts 折叠时显示浮动 pill,见 packages/web-ui/src/ChatPanel.ts:159-208

设计动机

为什么把 ArtifactsPanel 的装配放在 ChatPanel 而不是 AgentInterface?因为 artifacts 是「会话产物」维度的概念,与渲染消息列表解耦:AgentInterface 只看消息流,ArtifactsPanel 只看 artifact 消息并维护自己的 artifacts Map。两者都通过 agent.state 共享数据,但生命周期、UI 位置都不同。ChatPanel 作为布局层把它们组装起来,同时把 toolsFactory 暴露给 host,让 host 能注入业务专属工具(比如某个 app 的 query_database),而不需要改 pi-web-ui 源码。

BREAKPOINT = 800 是经验值:窄屏下 artifacts 全屏覆盖聊天区,避免两列都被挤压到不可读;宽屏下并排显示便于边聊边看产物。overlaycollapsedChatPanelrender 里根据状态推到 ArtifactsPanel,而不是让 panel 自己监听窗口,这样布局决策集中在顶层。

关键文件

setAgentAgentInterface 的创建与回调注入,是 host 与会话宿主的接缝:

typescript
// packages/web-ui/src/ChatPanel.ts:74-84
this.agentInterface = document.createElement("agent-interface") as AgentInterface;
this.agentInterface.session = agent;
this.agentInterface.enableAttachments = true;
this.agentInterface.enableModelSelector = true;
this.agentInterface.enableThinkingSelector = true;
this.agentInterface.showThemeToggle = false;
this.agentInterface.onApiKeyRequired = config?.onApiKeyRequired;
this.agentInterface.onModelSelect = config?.onModelSelect;
this.agentInterface.onBeforeSend = config?.onBeforeSend;
this.agentInterface.onCostClick = config?.onCostClick;

toolsFactory 让 host 注入额外工具,而 artifacts 工具始终第一个被加入:

typescript
// packages/web-ui/src/ChatPanel.ts:141-144
const additionalTools =
    config?.toolsFactory?.(agent, this.agentInterface, this.artifactsPanel, runtimeProvidersFactory) || [];
const tools = [this.artifactsPanel.tool, ...additionalTools];
this.agent.state.tools = tools;

数据流

setAgent 一条主线把 host 传入的 Agent 装配成可用的 UI:

布局切换由 render 里的状态判断驱动:

边界与失败

小结

ChatPanel 是布局层 + 装配车间:装配 AgentInterfaceArtifactsPaneltoolsFactory,按窗口宽度切布局。往里看是 AgentInterface 会话宿主,工具渲染的注册机制看 工具渲染器注册表