workspace/ files existed on disk but were not included in previous incremental commit, causing git to record them as deleted. Re-adding all workspace card components, AgentWorkspace, ActivityTimeline, and WorkspaceCardRenderer to properly track them. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
396 lines
9.8 KiB
Markdown
396 lines
9.8 KiB
Markdown
# langgraphjs-gen-ui-examples 实跑与代码核对记录
|
||
|
||
## 1. 这次我实际做了什么
|
||
|
||
这次不是只看 README,我实际做了下面这些事:
|
||
|
||
1. clone 仓库
|
||
- 路径:`/Users/gongzhiyong/go/langgraphjs-gen-ui-examples`
|
||
2. 安装依赖
|
||
- 执行:`pnpm install`
|
||
- 已成功
|
||
3. 读取关键配置与代码
|
||
- `README.md`
|
||
- `package.json`
|
||
- `.env.example`
|
||
- `langgraph.json`
|
||
- `src/agent-uis/index.tsx`
|
||
- `src/agent-uis/writer/index.tsx`
|
||
- `src/agent/writer-agent/index.ts`
|
||
- `src/agent/open-code/index.ts`
|
||
- `src/agent/pizza-orderer/index.ts`
|
||
4. 核对该项目“交互到底由哪里承接”
|
||
|
||
## 2. 这次我没有做到什么
|
||
|
||
我还没有把它完整跑到真实可交互演示画面,原因不是偷懒,而是这个仓库本身不是一个“装完直接开网页就能看完整交互”的纯前端 demo。
|
||
|
||
它依赖:
|
||
- LangGraph server
|
||
- 模型 API key(OpenAI / Google,部分示例还要 Anthropic)
|
||
- Agent Chat UI 这一套承接壳
|
||
|
||
所以它的“完整交互形态”不是只靠本仓库单独就能闭环展示的。
|
||
|
||
因此这次能确认的是:
|
||
- 代码结构
|
||
- 交互承接机制
|
||
- gen-ui 的真实工作方式
|
||
|
||
但不能假装说:
|
||
- 我已经把所有示例一条条真实点过并完整体验完
|
||
|
||
这点必须说明白。
|
||
|
||
---
|
||
|
||
## 3. 关键结论:这个仓库不是独立完整聊天产品,而是“LangGraph + UI 组件映射层”
|
||
|
||
从 README 和代码看,`langgraphjs-gen-ui-examples` 的定位很明确:
|
||
|
||
> 这是给 Agent Chat UI 使用的一组 LangGraph.js generative UI 示例。
|
||
|
||
也就是说,它不是:
|
||
- 一个完整成品聊天应用
|
||
|
||
它更像:
|
||
- 一组 graph
|
||
- 一组 UI component map
|
||
- 一套 graph 向 UI 推送结构化组件的示例实现
|
||
|
||
这点非常关键。
|
||
|
||
### 证据 1:README 直接写明
|
||
README 原文核心意思是:
|
||
- `This repository contains a series of agents intended to be used with the Agent Chat UI`
|
||
|
||
这已经说明:
|
||
- 真正承接聊天壳和交互主框架的是 Agent Chat UI
|
||
- 这个 repo 负责的是 agent + generative UI 示例
|
||
|
||
### 证据 2:`langgraph.json`
|
||
代码里:
|
||
|
||
```json
|
||
{
|
||
"graphs": {
|
||
"agent": "./src/agent/supervisor/index.ts:graph",
|
||
"email_agent": "./src/agent/email-agent/index.ts:agent",
|
||
"chat": "./src/agent/chat-agent/index.ts:agent"
|
||
},
|
||
"ui": {
|
||
"agent": "./src/agent-uis/index.tsx"
|
||
}
|
||
}
|
||
```
|
||
|
||
这说明:
|
||
- graph 在后端/agent 侧
|
||
- UI 映射入口在 `src/agent-uis/index.tsx`
|
||
|
||
也就是它的核心不是“前端页面布局”,而是:
|
||
- graph 运行时能推送什么 UI
|
||
- UI 名字如何映射到 React 组件
|
||
|
||
---
|
||
|
||
## 4. 真实的 generative UI 工作方式
|
||
|
||
这是这次最重要的发现。
|
||
|
||
## 4.1 它不是 trace panel 升级版
|
||
它不是:
|
||
- tool_start
|
||
- tool_end
|
||
- 然后前端自己把这些事件渲染成卡片
|
||
|
||
它更接近:
|
||
- graph 在执行过程中直接推一个“UI 组件实例”
|
||
- 前端按 name + props 渲染这个组件
|
||
|
||
## 4.2 `ComponentMap` 是关键
|
||
`src/agent-uis/index.tsx`:
|
||
|
||
```ts
|
||
const ComponentMap = {
|
||
"stock-price": StockPrice,
|
||
portfolio: PortfolioView,
|
||
"accommodations-list": AccommodationsList,
|
||
"restaurants-list": RestaurantsList,
|
||
"buy-stock": BuyStock,
|
||
"code-plan": Plan,
|
||
"proposed-change": ProposedChange,
|
||
writer: Writer,
|
||
} as const;
|
||
```
|
||
|
||
这个文件明确说明:
|
||
- gen-ui 的核心单位不是“通用 trace item”
|
||
- 而是“命名组件”
|
||
- graph 推什么组件名,前端就渲染什么组件
|
||
|
||
这和我前面只讲“timeline + result card”的说法相比,更接近真实代码。
|
||
|
||
也就是说,如果你要无限接近它:
|
||
- 不能只做通用 workspace + 通用卡片系统
|
||
- 还要有“组件注册表 / 组件协议 / 组件 props 约定”
|
||
|
||
---
|
||
|
||
## 5. Writer 示例说明了什么
|
||
|
||
`src/agent/writer-agent/index.ts` 和 `src/agent-uis/writer/index.tsx` 是最关键的例子。
|
||
|
||
### 5.1 后端/graph 侧是怎么做的
|
||
writer graph 里用了:
|
||
|
||
```ts
|
||
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
|
||
```
|
||
|
||
然后在 graph 执行过程中:
|
||
|
||
```ts
|
||
ui.push(
|
||
{ id, name: "writer", props: { ...tool, isGenerating: true } },
|
||
{ message, merge: true },
|
||
);
|
||
```
|
||
|
||
后面内容流式生成时继续:
|
||
|
||
```ts
|
||
ui.push(
|
||
{ id, name: "writer", props: { content, isGenerating: true } },
|
||
{ message: lastMessage, merge: true },
|
||
);
|
||
```
|
||
|
||
最后结束时:
|
||
|
||
```ts
|
||
ui.push(
|
||
{ id, name: "writer", props: { isGenerating: false } },
|
||
{ message: lastMessage, merge: true },
|
||
);
|
||
```
|
||
|
||
这说明它的机制不是:
|
||
- 前端根据工具结果“猜”出该渲染什么
|
||
|
||
而是:
|
||
- graph 明确 push 一个叫 `writer` 的 UI 组件
|
||
- 并且持续 merge 更新它的 props
|
||
|
||
### 5.2 前端组件侧是怎么承接的
|
||
`src/agent-uis/writer/index.tsx` 中:
|
||
- 组件会根据 `isGenerating` 显示生成中
|
||
- 有 `Artifact` 侧边面板
|
||
- 内容流式写进 textarea
|
||
- 生成过程中还能自动打开 artifact panel
|
||
|
||
这个交互不是“消息上的 trace 面板”能替代的。
|
||
|
||
它本质上是:
|
||
- 聊天消息只是触发器/上下文
|
||
- 真正的内容承接在独立 artifact/workspace
|
||
|
||
这对 SOC 的启发非常大。
|
||
|
||
---
|
||
|
||
## 6. Pizza / Open Code / Email 示例分别说明什么
|
||
|
||
## 6.1 Pizza
|
||
README 和代码都说明:
|
||
- pizza 示例主要展示 tool call/result UI
|
||
|
||
这说明它有一条路线是:
|
||
- 工具调用本身也能被 UI 组件化展示
|
||
|
||
## 6.2 Open Code
|
||
Open Code 是一个假的代码生成 agent,用来演示:
|
||
- plan
|
||
- proposed changes
|
||
- 审批/继续
|
||
- 多步 UI 交互
|
||
|
||
这说明它不是只有“卡片展示”,还有:
|
||
- 多步状态机 UI
|
||
- 用户确认后继续 graph
|
||
|
||
## 6.3 Email Agent
|
||
Email agent 用的是 interrupt / HumanInterrupt 标准 schema。
|
||
|
||
说明它还支持:
|
||
- graph 中断
|
||
- 前端自动渲染 HITL UI
|
||
- 用户处理后恢复 graph
|
||
|
||
这比“只展示 trace”又高了一个层级。
|
||
|
||
---
|
||
|
||
## 7. 对 SOC 的真实结论
|
||
|
||
这部分必须收得很实。
|
||
|
||
## 7.1 现在 SOC 最接近的不是它的“完整形态”,而是最外层轮廓
|
||
SOC 现在已有:
|
||
- chat
|
||
- SSE token
|
||
- status
|
||
- tool trace
|
||
- message 绑定
|
||
|
||
这些只对应到它的最外层轮廓。
|
||
|
||
SOC 现在还没有真正拥有的,是下面这几层:
|
||
|
||
### 第一层:UI 组件注册机制
|
||
类似:
|
||
- `writer`
|
||
- `stock-price`
|
||
- `portfolio`
|
||
- `code-plan`
|
||
- `proposed-change`
|
||
|
||
SOC 现在没有这种“组件名 -> React 组件”的标准化注册表。
|
||
|
||
### 第二层:后端主动 push UI 组件实例
|
||
这个 repo 的关键能力是:
|
||
- graph 直接 `ui.push({ id, name, props })`
|
||
|
||
SOC 当前没有这层。
|
||
SOC 现在还是:
|
||
- 发 status/tool/token
|
||
- 前端自己猜怎么显示
|
||
|
||
这两者差异很大。
|
||
|
||
### 第三层:artifact/workspace 是一等公民
|
||
writer 示例里,artifact 侧边面板是正式交互层。
|
||
SOC 现在没有真正的一等 workspace / artifact 层。
|
||
|
||
### 第四层:interrupt / HITL / resume 机制
|
||
SOC 目前也没有把这一层做成产品 UI。
|
||
|
||
---
|
||
|
||
## 8. 历史消息 gen-ui 持久化,为什么会变成大问题
|
||
|
||
现在可以更准确回答你前面那个问题。
|
||
|
||
因为在这个例子里,gen-ui 不是简单 trace,而是:
|
||
- 一个或多个具名 UI 组件实例
|
||
- 每个实例都有 props
|
||
- props 还会增量更新
|
||
- 还可能挂在某条 message 上 merge
|
||
|
||
所以历史消息持久化时,最稳的不是只存 trace,而是要存:
|
||
|
||
```json
|
||
{
|
||
"message_id": "...",
|
||
"ui_instances": [
|
||
{
|
||
"id": "...",
|
||
"name": "writer",
|
||
"props": {
|
||
"title": "...",
|
||
"content": "...",
|
||
"isGenerating": false
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
也就是:
|
||
- 存“组件实例快照”
|
||
- 不只是存“工具事件日志”
|
||
|
||
否则历史消息重开时,根本无法接近它那个交互。
|
||
|
||
---
|
||
|
||
## 9. 我现在对“能不能无限接近它”的新判断
|
||
|
||
在实际读过这些代码后,答案比之前更明确:
|
||
|
||
### 9.1 如果 SOC 只是把现在的 trace panel 升级成右侧栏
|
||
那*不能无限接近*。
|
||
|
||
因为这只是在 UI 布局层面模仿。
|
||
没有触及它真正的核心机制:
|
||
- 后端推具名 UI 组件
|
||
- 前端组件注册映射
|
||
- artifact / workspace 一等化
|
||
- message 级别的 UI merge
|
||
|
||
### 9.2 如果 SOC 允许新增一层“GenUI 协议 + 组件注册 + workspace 持久化”
|
||
那*可以高相似度接近*。
|
||
|
||
但前提不是“小修小补”,而是要补以下能力:
|
||
|
||
1. 后端 UI 事件协议
|
||
2. 前端 component registry
|
||
3. workspace / artifact 容器
|
||
4. message -> ui instance 绑定
|
||
5. 历史会话快照恢复
|
||
|
||
### 9.3 所以结论不能再说虚的
|
||
最准确的话是:
|
||
|
||
- *单靠当前那套 status + trace + answer 方案,不能无限接近 `langgraphjs-gen-ui-examples`*
|
||
- *如果把协议层升级为“具名 UI 组件实例流”,再加 workspace 持久化,才有资格说高相似度接近*
|
||
|
||
---
|
||
|
||
## 10. 对我前面方案的修正
|
||
|
||
我前面那版 `uigoto.md` 有一个本质问题:
|
||
|
||
我把目标抽象成了:
|
||
- timeline
|
||
- result cards
|
||
- workspace
|
||
|
||
这没错,但还不够贴这次真实读到的代码。
|
||
|
||
缺的关键一层是:
|
||
|
||
> gen-ui 不是“结果卡片集合”这么简单,而是“后端驱动的具名 UI 组件实例系统”。
|
||
|
||
这会直接影响:
|
||
- 前后端协议怎么设计
|
||
- 持久化怎么做
|
||
- 组件如何注册
|
||
- 历史消息如何恢复
|
||
- 后续交互复杂度能否提升到 interrupt / HITL / artifact 级别
|
||
|
||
所以如果接下来重写 SOC 方案,必须把这一层补进去。
|
||
|
||
---
|
||
|
||
## 11. 当前可以下的最硬结论
|
||
|
||
### 结论 1
|
||
`langgraphjs-gen-ui-examples` 不是单纯展示 trace 的项目,它的核心是:
|
||
- graph 执行时直接推 UI 组件实例
|
||
- 前端按组件注册表渲染
|
||
|
||
### 结论 2
|
||
它的完整交互壳很大程度依赖 Agent Chat UI,不是这个 repo 自己单独包办全部页面壳。
|
||
|
||
### 结论 3
|
||
SOC 如果想无限接近它,不能只升级 trace panel,必须升级成:
|
||
- UI 组件协议
|
||
- component registry
|
||
- artifact/workspace
|
||
- message 绑定持久化
|
||
|
||
### 结论 4
|
||
历史消息 gen-ui 持久化,最稳方案不是只存 trace,而是存“message 级 UI 组件实例快照”。
|
||
|