资讯动态

PostHog AI 前端双运行时架构解析:从 LangGraph 冻结到 Sandbox Surface 迁移指南

发布时间:2026/9/13 8:48:46 来源:尧图企业网站定制
PostHog AI 前端双运行时架构解析从 LangGraph 冻结到 Sandbox Surface 迁移指南【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthogPostHog AI 是 PostHog 产品中的 AI 助手用户侧产品名曾为 Max。本文以仓库中 frontend/src/scenes/max/CLAUDE.md 为骨架结合 products/posthog_ai/frontend/AGENTS.md、products/posthog_ai/frontend/README.md 与scenes/max下的实际实现完整讲解 PostHog AI 前端的两套并存运行时、逐关注点迁移映射、在哪添加 X 的工程决策路径以及 sandbox 运行时可组合 UI 库的四层公共 API 设计。读完你将掌握 PostHog AI 前端代码的组织原则、双运行时的切换机制以及如何为 sandbox 路径安全地贡献新功能。1. 两个并存的运行时LangGraph冻结与 Sandbox新开发PostHog AI 场景scene内部同时托管两套运行时runtime具体走哪一套由每条对话的conversation.agent_runtime字段决定取值langgraph | sandbox运行时定位状态langgraph旧运行时legacy基于 LangGraph 的 EventSource 流式线程冻结不新增功能待 parity 达成后移除sandbox新运行时基于 Claude Code / Codex agent-server 的SSE协议新功能全部在此构建两个运行时在表面层前端 UI上各有对应实现代码组织上有一个明确原则所有新建行为——新工具、新上下文类型、新 UI 交互——都落在 sandbox 路径上。LangGraph 运行时一旦与 sandbox 达到功能对等parity就会被整体删除因此刻意不让它的表面继续生长是为了让将来的删除是一次干净的移除而非拆解缠绕。命名约定PostHog AI而非 Max命名规则用户可见的产品名是PostHog AI曾用名 Max。所有面向用户的文案必须使用 PostHog AI。但目录名保持为scenes/max/已有内部标识符Max*组件、MaxUIContext、maxThreadLogic、maxContextLogic、Max Context 子系统不做批量重命名。新代码不要在文案中引入新的 Max 品牌字样。从源码可见frontend/src/scenes/max/Thread.tsx 中反馈面板文案、frontend/src/scenes/max/maxLogic.tsx 等文件均保留了Max内部标识但产品文案统一为 PostHog AI。与 Max Context 子系统的边界Max Context 子系统场景如何向助手暴露 dashboard / insight / event 等上下文由 frontend/src/scenes/max/README.md 单独成文本文不重复展开。该 README 展示了场景通过maxContextselector 暴露上下文的标准写法import { MaxContextInput, createMaxContextHelpers } from scenes/max/maxTypes selectors({ maxContext: [ (s) [s.dashboard], (dashboard): MaxContextInput[] { if (!dashboard) { return [] } return [createMaxContextHelpers.dashboard(dashboard)] }, ], })当前支持的上下文实体包括 Dashboards、Insights、Events、Actions、Error tracking issues。注意其 Caveat目前后端仅支持 trends、funnels、retention、custom SQL 这几种 insight 类型因此暴露含自定义查询的 dashboard 时前端逻辑可见、但后端对 Max 实际不可用。2. 逐关注点映射LangGraph 路径 → Sandbox 路径CLAUDE.md 给出了一张逐关注点per-concern的迁移对照表是理解两套实现如何共存的关键关注点langgraphLEGACY冻结sandboxNEW在此构建运行时开关agent_runtime langgraphagent_runtime sandbox流逻辑maxThreadLogic.tsxLangGraph 流循环runStreamLogicSSE位于products/posthog_ai/frontend/logics/活动渲染器components/Activity/LangGraphActivity.tsxRunActivity位于products/posthog_ai/frontend/components/线程渲染器Thread.tsx默认路径Thread.tsx绑定 surface 的ThreadView上下文形态富对象MaxUIContext完整对象扁平AttachedContext类型化引用由 agent 自行获取详情审批DangerousOperationApprovalCard.tsx/approvalOperationUtils.tssurface 的PermissionInput/QuestionInput工具组件messages/LangGraph presenterssurface 的toolRegistry中央渲染器清单manifest工具渲染器tool widget的注册遵循一个关键机制Max 只消费组件不注册它们。中央清单 frontend/src/posthogAiToolRenderers.ts 在渲染前合并各产品自有的声明import { posthogAiToolRenderers as cdpToolRenderers } from products/cdp/frontend/posthogAiToolRenderers import { posthogAiToolRenderers as errorTrackingToolRenderers } from products/error_tracking/frontend/posthogAiToolRenderers import { posthogAiToolRenderers as logsToolRenderers } from products/logs/frontend/posthogAiToolRenderers import type { ToolRegistryEntry } from products/posthog_ai/frontend/api/tools import { posthogAiToolRenderers as dataToolRenderers } from products/posthog_ai/frontend/posthogAiToolRenderers export const posthogAiToolRenderers: ToolRegistryEntry[] [ ...dataToolRenderers, ...cdpToolRenderers, ...errorTrackingToolRenderers, ...logsToolRenderers, ]而 PostHog 产品数据工具渲染器insight、dashboard、recordings、notebook、query定义在 products/posthog_ai/frontend/posthogAiToolRenderers.tsx通过懒加载工厂lazyWithRetry保持注册表 eager 导入图之外const DATA_TOOLS [ { keys: [insight-create, insight-update, insight-get], displayName: Insight, icon: IconGraph /, Renderer: InsightRenderer, }, { keys: [dashboard-create, dashboard-update], displayName: Dashboard, icon: IconDashboard /, Renderer: DashboardRenderer, }, // ... session recordings / notebook 等 ]每个条目带requiresPostHogOrigin: true与keepVisible: true声明其来源约束与可见性。error tracking 通过 products/error_tracking/frontend/posthogAiToolRenderers.ts 提供声明列表因此其卡片也能在 sandbox 路径渲染而 replay vision 目前尚无声明其扫描组件ReplayVisionScanWidget只在 LangGraph 路径渲染直到补上声明为止。3. 硬性规则不要扩展 LangGraph 路径除非被明确要求CLAUDE.md 对 LangGraph 路径划定了一条清晰的边界✅允许保持现有 LangGraph 对话正常工作的 Bugfix。❌禁止在 LangGraph 路径上新增任何能力。❌禁止为 sandbox 功能在MaxUIContext上新增字段——改用AttachedContext。❌禁止新增 MaxTools。若某任务确实需要扩展 LangGraph 路径必须在触碰maxThreadLogic.tsx、LangGraphActivity.tsx、max-constants.tsxEnhancedToolCall、maxContextLogic.ts的MaxUIContext半区或messages/之前明确确认该意图。这条规则背后的工程动机很实际LangGraph 运行时已被标记为待移除任何在其表面新增的功能都会成为未来删除时的债务。保持其表面不再生长是干净删除而非拆解缠绕的前提。4. Sandbox 架构代码在 surface约定也在 surfacesandbox 运行时的核心与开发约定不再位于scenes/max之下而是整体迁移到了products/posthog_ai/frontend——一个可组合的 PostHog AI agent-run 库。其架构与约定完整记录在 products/posthog_ai/frontend/AGENTS.md核心内容包括runStreamLogicSSE 连接 线程投影详见下节逻辑不进组件wire 解析、log 折叠、SSE 处理、遥测、权限路由都属于runStreamLogic或其policy/兄弟模块组件只消费 selector、派发 actionUI 与运行时无关渲染组件接收纯 propsThreadItem、ToolInvocation、PermissionRequestRecord对 langgraph vs sandbox、对 conversation 一无所知原子化组件单一职责、以稳定 itemid为 key 的小型 memoized 叶子 presenter而非巨型内联 switch纯投影foldLogToThread是纯且确定性的函数item id 在反复折叠中保持稳定监听器只触发副作用且都带 fire-once 守卫、在source: replay时被抑制。Max 特有的部分Thread.tsx 的高层分支scenes/max中留在原位的、属于 sandbox 路径的 Max 专属代码体现在 frontend/src/scenes/max/Thread.tsx 对conversation.agent_runtime的高层分支上。其核心逻辑为const isSandboxRuntime conversation?.agent_runtime sandbox // 原生 sandbox 对话无遗留历史只渲染 sandbox 线程 if (isSandboxRuntime !isConvertedConversation) { return ( BindLogic logic{runStreamLogic} props{{ streamKey: sandboxConversationKey, conversationId: sandboxConversationKey }} ThreadView virtualized{false} renderTurnTrailer{renderTurnTrailer} / /BindLogic ) } // 已转换的对话完整遗留线程 history was converted 分隔线 实时 sandbox 线程 if (isConvertedConversation) { return ( LegacyThread showTrailers{false} / LemonDivider dashed labelMessage history was converted to the new format / BindLogic logic{runStreamLogic} props{{ streamKey: sandboxConversationKey, conversationId: sandboxConversationKey }} ThreadView virtualized{false} renderTurnTrailer{renderTurnTrailer} / /BindLogic / ) } // 纯 LangGraph 对话 return LegacyThread showTrailers /关键约束Max 组件只负责把解析好的 props 传递下去绝不在 Max 组件内解析 wire 帧或持有 SSE 状态。runStreamLogic以对话 id 为streamKey绑定ThreadView从 surface 渲染——这条桥接代码留在scenes/max并随scenes/max一并删除。5. Sandbox surface四层公共 APIapi/module facadeproducts/posthog_ai/frontend被设计为对话无关conversation-agnostic的库被 Max 场景与 signals inbox 共同消费并可在任何需要展示/交互 agent run 的地方内嵌。它对外暴露的是分层的api/modulefacade——外部消费者绝不从深层路径components/...、logics/...导入而是选择能满足需求的最窄层级层级模块内容1 — 预装 surfaceapi/readableRunapi/runSurfaceapi/runnerReadonlyRunSurface懒加载、代码分割的只读内嵌RunSurface复合组件Root 插槽eager供自定义布局EmbeddedRunner懒加载 TaskTracker 产品供内嵌宿主2 — 复合原语api/primitivesThread 原子组件、ThreadView、Composer.*、QueuedMessageList、RunLogSkeleton、活动原语 RunActivity、消息 presenter、权限/提问 surface3 — 无头逻辑 类型api/logicsapi/typesrunStreamLogic、runInteractionLogic、状态与思考辅助函数折叠线程 工具类型4 — 扩展接缝api/toolstoolRegistry、lookupToolRenderer、GenericMcpToolRenderer、DataToolRow、ToolActivity、FilePath、diff 辅助函数各模块的代表性导出见 products/posthog_ai/frontend/api/tools.ts 等 facade 文件api/readableRun导出ReadonlyRunSurface懒加载、代码分割的只读内嵌运行中interactionlive实时流式渲染新帧只读read-only时重放一次快照Suspense 回退为RunLogSkeleton。所有内嵌场景如 inbox 详情视图都用它。api/runSurface导出RunSurface复合组件Root.Thread/.Composer/.ContextUsage插槽eager供构建自定义布局的消费者使用没有默认布局——ReadonlyRunSurface只是其一种具体组合。api/runner导出EmbeddedRunner懒加载的 TaskTracker 产品供需要内嵌整个/tasks体验的宿主使用。api/logics导出runStreamLogic、runInteractionLogic、attachedContextLogicuseAttachedContext、toolStreamEventsLogicuseToolStreamListener、状态辅助函数isTerminalRunStatus等。该层级只导入logics/*hooks/*utils/*绝不导入组件或注册表保持无头通道headless lane无 React/注册表依赖。api/tools导出toolRegistry、ToolRegistryEntry声明契约、lookupToolRenderer、GenericMcpToolRenderer、DataToolRow、ToolActivity、FilePath、diff/exec 辅助函数。之所以单独隔离是因为导入它会拉入有副作用的注册表 chunk。为什么分层而不是一个扁平 barrel工具注册表在模块加载时从内置项和中央 manifest 初始化这是一个不会被 tree-shaking 掉的顶层副作用。若用一个总 barrel 静态地把它与 markdown/虚拟化重的线程、无头逻辑一起再导出那么只想用isTerminalRunStatus做状态徽章的消费者也会把注册表 presenter 拖进自己的 chunk。将副作用注册表隔离在api/tools、让无头通道api/logicsapi/types保持无 React/注册表导入才能把每个消费者的 bundle 限制在其子树内。因此项目刻意不提供根index.tsbarrel——任何聚合再导出都会让消费端 chunk 重新变肥每个公共符号都经由某个api/module入口到达新增导出应加到对应层级模块而非新建 barrel。耦合边界面向 tasks runs绝不面向 Max该 surface按设计耦合到 tasks run APIproducts/tasks/frontend/generated/api一个 task 其 run SSE 流共同构成 agent-run surface。但它必须保持与 Max 场景和对话编排无关——禁止从products/posthog_ai/frontend下任何位置导入scenes/max/*、maxThreadLogic、maxContextLogic、MaxUIContext或 conversations API。Max 是 surface 的消费者而非其依赖。这一点由 grep 门禁强制grep -rE scenes/max|maxThreadLogic|MaxUIContext products/posthog_ai/frontend该命令的输出必须为空无导入。若 Max 需要 surface 未通用表达的能力做法是提升为这里的通用 prop/selector 并由 Max 适配绝不在此目录特判 Max。runStreamLogic以通用streamKey为 keyMax 用 conversation idtask viewer 用 run/task id保持泛化、无 Max 专属分支。6. 流式架构深潜runStreamLogic 的设计runStreamLogic是 sandbox surface 的心脏位于 products/posthog_ai/frontend/logics/runStreamLogic.ts其设计要点如下SSE 连接一个fetchbody reader 经eventsource-parser泵送断线重连通过Last-Event-ID从最后一条 Redis stream id 续传有上限的指数退避 累计上限。连接状态stream-end哨兵、续传游标、代理 token 预算属于单个连接每次openSseForRun都全新开启且当被开启的 run 与游标来源不一致时丢弃游标。发送永远不会复活已死的流——stream-end哨兵之后run 的 Redis stream 持有服务端停止写入的完成条目只有后继 run 才能承载下一轮sseStatus: error既覆盖历史引导失败也覆盖退避预算耗尽重开连接会继承已耗尽的退避预算。有序、只追加的log是唯一事实来源每条 wire 帧外加少量合成客户端条目都被追加不做按 key 或逐条去重——唯一例外是超集的tool_call_update帧按toolCallId做字段级合并appendToRunLog。每条 update 携带完整累积的rawOutput/content快照因此全部保留会以数量级膨胀内存而折叠fold只渲染合并后的最新值。纯投影foldLogToThread(entries) → { threadItems, toolInvocations }以log身份identity做 memo派生渲染线程。按streamKey键控并发流彼此独立。权限链路解析parsePermissionRequestFrame→ 路由policy/toolPolicy.ts 自动批准内置工具 只读 PostHogexec否则弹出卡片→ 应答respondToPermission→ 解析钉住 id使重连重放不会再次弹出。权限/提问工具在policy/wire 形状在types/streamTypes 折叠线程形状wireTypes ACP wire 形状 守卫。wire 类型是弱类型——必须在解析边界用运行时检查守卫绝不假设字段必然存在。上下文注入AttachedContext 接缝attachedContextLogicproducts/posthog_ai/frontend/logics/attachedContextLogic.ts是屏幕上下文on-screen context的全局注册表。挂载的useAttachedContexthook /AttachedContextProvider组件或通过cache.disposables注册的 kea logicsetup 派发registerContext(providerId, items)cleanup 派发deregisterContextpauseOnPageHidden: false因为隐藏时暂停的注册会让 tab 隐藏期间的发送丢失上下文贡献抽象AttachedContextItem——type是任意字符串insight、dashboard、trace、text……绝不是枚举联合外加key/label/value。contextItems按${type}:${key ?? value}扁平化并去重。发送时发送路径runInteractionLogic.sendNow/startNewRun、taskTrackerSceneLogic.submitNewTask用wrapWithPosthogContextutils/posthogContextBlock.ts包裹出站消息前缀posthog_trusted_contexttype: instructions项和/或posthog_untrusted_context其余一切——可内嵌用户/摄入文本的数据渲染在加固提示文案之后。这些块对用户不可见实时回显pushHumanMessage携带原始文本unwrapUserMessageContent在历史重放时剥离每个前导块按标签剥离而非正文包括旧的posthog_context包装。发送路径还会剪除任务续传链中任何已发送过的上下文通过attachedContextLogic中两个按任务键控的层内存层sentContextKeysByTask每次发送后立即标记覆盖发送→回显窗口与持久层seenContextLinesByTaskrunStreamLogic.ingestAcpFrame记录每个上下文块的渲染行logs/快照重放完整续传链因此跨刷新、跨 tab、跨用户会话都能存活与后端_collect_seen_entity_refs/prune_repeated_entity_refs对应。text项永不去重——重复文本是有意为之。用户挑选上下文与工具流事件用户挑选上下文composer 的 -触发contextPickerLogic.ts components/composer/AttachedContextBar.tsx。AttachedContextBar渲染TaxonomicPopover选择由taxonomicItemToAttachedContext投影为扁平引用——不加载实体agent 自行获取详情——存入contextPickerLogic它只是attachedContextLogic上的另一个 provideruser-picker。关闭自己挑选的 chip 会从 picker 移除关闭其他 provider 的 chip 则派发dismissContext(key)且关闭状态跨 provider 重新注册仍然存活。工具流事件toolStreamEventsLogictoolStreamEventsLogic.ts是runStreamLogic发布工具调用生命周期事件的全局总线——phase: started/updated/completed/failedtoolName经toolResolver解析内部 PostHog MCP 工具如create_dashboard。订阅方式useToolStreamListener({ tools, onEvent })或 kea 原生连接总线监听toolStreamEventsLogic.actionTypes.emitToolEvent。重放来源的事件默认被抑制除非订阅设置includeReplay——刷新不得重新触发 UI 反应。注意exec 包装的 PostHog 工具在started时解析名可能为__posthog_exec_unknown__command经 updates 流入到completed时才可靠。订阅者回调被隔离——抛异常的监听器会被捕获绝不破坏摄入。7. 在哪添加 X功能落点决策清单这是 CLAUDE.md 中操作性最强的一节。前三项现在都位于products/posthog_ai/frontend共享 surface——在那里改而不是在scenes/max要加的东西落点新的线程项类型products/posthog_ai/frontend/types/streamTypes.tsThreadItem联合在foldLogToThread中处理并新增接入ThreadView/ThreadRow的 memoized 叶子渲染器新的权限交互 / 自动批准规则products/posthog_ai/frontend/policy/toolPolicy.ts然后通过PermissionInput呈现新的流遥测在相关runStreamLogic监听器中加守卫式posthog.capturefire-once重放时抑制新的产品工具渲染器渲染 PostHog 实体所属产品的前端 在其products/product/frontend/posthogAiToolRenderers.tsx声明中央 manifest 在任何场景渲染前就使其可用Agent 应看到的新上下文通过 surface 的上下文接缝注册useAttachedContext/AttachedContextProvider/attachedContextLogic.registerContext见 products/posthog_ai/frontend/AGENTS.md §3。不是MaxUIContext也不在遗留 store 上新增字段。场景如今将屏幕上下文原生注册进该接缝maxThreadLogic的 sandbox 发送将其合并进attached_context——这条桥接位于scenes/max随其一起删除UI 响应 agent 调用工具sandbox 路径通过 surface 的useToolStreamListener/toolStreamEventsLogic订阅解析后的工具名默认抑制重放。遗留useMaxTool的callback只在 LangGraph 上触发工具卡片布局约定每个工具渲染器都用ToolActivity包裹其内容它只暴露两行始终可见的头部title和subtitle唯一要点输入——命令、路径、repo、分支。工具产生的任何其他可呈现信息——解析输出、commit/repo 列表、文件内容、diff、原始文本——都必须放进可折叠的bodyActivity 手风琴绝不放进始终可见的children。手风琴在工具运行期间自动展开、完成后折叠保证线程可扫读每个工具一两行读者只展开关心的卡片。始终可见的children仅保留给真正需要交互、折叠即无用的负载如用户必须处理的AskUserQuestion回顾——拿不准就放手风琴。8. Donts红线清单最后是 CLAUDE.md 明确列出的禁止事项可作为贡献前的自检清单不要扩展 LangGraph 运行时除非被明确要求。不要在一个组件内解析 wire 帧或持有 SSE 状态。不要从监听器里变更线程/调用invocation状态——在投影中派生它。不要为 sandbox 功能新增MaxUIContext字段——使用AttachedContext。当只需要一个 sandbox selector 时不要订阅全部。不要在活动分支上做大爆炸式文件移动big-bang move。9. 面向未来的工程姿态Replacement-readinessproducts/posthog_ai/frontend/AGENTS.md 最后明确指出该 surface 是 Max 遗留 LangGraph 线程的既定替代品设计上为最终的干净切换做好了准备新旧切换是消费者关注点而非 surface 自身的。Max 已通过conversation.agent_runtime sandbox路由到runStreamLogic以conversationId为 key并经由该 surface 渲染threadItems而 LangGraphEventSource路径渲染 Max 自有线程。该 surface 绝不分支于 Max/LangGraph/conversation——由 §2 的 grep 门禁强制。完整替换后LangGraph 路径拥有而 surface 未通用表达的内容——agent 模式、计费上下文、场景附件/上下文——留在 Max 作为消费者通过通用 props / 工具注册表 /streamKey馈入 surface。若 surface 需要它们就提升为这里的通用接缝绝不特判 Max。设置开关在Max一侧翻转默认实现无论开关处于哪一侧surface 的四层契约§1都保持不变。综上PostHog AI 前端正处于一次明确的双运行时过渡期冻结的 LangGraph 路径维持存量对话新的 sandbox surface 承载全部增量开发而scenes/max与products/posthog_ai/frontend之间以消费者 / 库的清晰边界、四层api/modulefacade 与 grep 门禁共同保障这次过渡的整洁与可逆。理解这套组织原则是向 PostHog AI 前端贡献代码的第一课。【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价