学习笔记 · Obsidian
Deep Agents TypeScript:流式、前端、HITL、生产与应用
优先使用 event streaming
TypeScript 新应用应优先使用 typed projection 的 event streaming,而不是旧的 streamMode 分支协议。一个 run 可分别消费:
stream.messages:主协调者消息。stream.toolCalls:主 Agent 工具调用。stream.values:自定义 state 快照。stream.subagents:每个 task 委派的独立 handle。stream.output:最终输出。
每个 subagent handle 可继续提供 name、taskInput、messages、toolCalls、values、嵌套 subagents 与 output。投影是惰性的:只在访问后才打开对应子流,便于 UI 按需订阅。
主 Agent 与子代理事件会交错,通常并发消费各投影。若必须恢复整个树的精确到达顺序,使用 raw protocol events,并按 namespace 标识来源。stream.subagents 面向产品概念;stream.subgraphs 面向内部图执行,不宜直接作为用户 UI。
旧 streaming 页仍使用 stream_mode、stream_subgraphs 和 snake_case,且明确推荐迁移到 event streaming。它可用于理解 namespace、messages/updates/custom 模式,但不是 TypeScript 新代码模板。
HITL 决策模型
工具级 interruptOn 把敏感调用交给人工审查,支持四种决策:
| 决策 | 语义 | 使用边界 |
|---|---|---|
approve | 按原参数执行 | 用户确认副作用 |
edit | 改参数后执行 | 只做保守、可预期修改 |
reject | 不执行并给 Agent 反馈 | 拒绝副作用工具 |
respond | 人的文字作为合成工具结果 | 仅适合 ask-user 型工具 |
不能用 respond 代替拒绝:模型可能把它当成工具成功。一次出现多个需审工具时会批量返回 interrupts,恢复命令必须按顺序提供每个决定。拒绝消息应明确“未执行”以及是否放弃、询问或换安全方案。
HITL 需要 checkpointer 和稳定 thread ID。子代理可继承或覆盖 interruptOn,也可在自定义工具内部直接触发 LangGraph interrupt。被取消/中断时 PatchToolCalls middleware 会修复消息历史。
当前 TypeScript HITL 页 prose 仍以 interrupt_on、True 和 Python 字典描述,实际配置应使用 interruptOn 与 JS 对象。
前端架构
Deep Agents 前端不应只是单一聊天气泡,而应把“协调、委派、计划、工具与产物”分别呈现。v1 前端 SDK 在 React、Vue、Svelte、Angular 上都以 useStream 类 API 暴露同一运行时投影。
子代理卡片
根 stream.messages 只显示协调者;stream.subagents 是轻量 discovery snapshots。用 snapshot 调用 useMessages(stream, subagent)、useToolCalls(...) 等 selector,只在卡片展开时订阅子流。
UI 应把 subagent snapshot 的 ID 与发起 task 的 tool-call ID 对齐,把卡片挂到对应协调者消息下。推荐显示专家名、状态、工具数、短输出和错误,并在大量子代理时折叠已完成项。单个子代理失败应局部展示,不让整个 UI 崩溃。
Todo 面板
Todo state 是显式选择:只有加入 todoListMiddleware() 后才有 write_todos 与 stream.values.todos。状态通常为 pending → in_progress → completed。前端直接从 state 派生完成数与百分比,不轮询;没有 todos 时隐藏,创建计划时显示轻量 loading。
Sandbox IDE
三栏结构是文件树、代码/diff 与聊天。关键不是视觉布局,而是共享同一个 sandbox identity:
- Agent graph factory 按 thread metadata 解析 sandbox。
- 自定义 Hono API 也调用同一解析函数。
- frontend 把稳定 thread ID 保存在
sessionStorage,刷新后重连同一环境。
文件同步应监听 write_file / edit_file 的 ToolMessage 并刷新目标文件;execute 可能改任意文件,应刷新整个树。每次 run 前留一份快照,之后按内容比较标出 changed files,默认向用户展示 diff。自定义 HTTP route 优先于 LangGraph 默认 route,命名时避免覆盖 /threads、/runs 等系统路径。
生产架构
三个隔离维度
- Thread:一段会话、checkpoint 与临时文件。
- User:最终用户身份与私有记忆/文件。
- Assistant:一个 Agent 配置实例及可能共享的资源。
每次生产调用通常同时携带稳定 thread ID 与 runtime context。前者决定恢复哪段会话,后者承载 user ID、角色、连接和 feature flags;两者相互独立。
LangSmith Deployment 可提供 server、threads、runs、store、checkpointer、auth、webhook、cron 和 tracing。Managed Deep Agents 当前文档标为 private preview;需要自定义路由和高级认证时使用普通 Deployment,或按平台部署到 Next.js、SvelteKit、Nuxt、Cloudflare、Deno 等环境。
多租户与身份
认证确定用户是谁;授权 handler 通过 ownership metadata、过滤器与 403 控制 threads、assistants 与 store。团队 RBAC 与最终用户授权是两个不同层,不能互相替代。
用户代表授权优先用 Agent Auth 的 OAuth;共享服务密钥放 workspace secrets。Sandbox 调外部 API 时用认证代理注入 header,避免原始密钥进入 sandbox。
持久性与执行环境
LangGraph checkpoint 使失败、超时和 HITL 中断可从最后一步恢复,也支持 time travel。自托管时必须自行配置持久 checkpointer。
只需文件 I/O 时使用 State/Store/Composite backend;需要 shell、安装包或测试时使用 sandbox。部署 Agent 不应使用宿主 FilesystemBackend 或 LocalShellBackend。
Sandbox 生命周期常见两种:
- thread-scoped:每个会话新环境,TTL 清理,隔离更清晰。
- assistant-scoped:多个会话共享 repo、依赖和构建产物,但必须处理磁盘增长、污染和重置。
Graph factory 需要根据 run config 在调用时异步取得 sandbox。应用与 sandbox 间用文件传输 API;Agent 在 sandbox 内用普通文件工具。
Guardrails
生产防护至少包括:
- 内置文件工具的路径 permissions。
- 模型/工具重试与 fallback。
- 每 run / thread 调用上限,防止循环烧预算。
- PII middleware 的 redact、mask、hash 或 block。
- HITL 与持久 checkpoint。
- tracing 与每租户资源指标。
Fault tolerance
| 故障 | 策略 |
|---|---|
| 网络超时、限流 | 模型/工具指数退避重试 |
| 供应商整体故障 | model fallback |
| 工具或解析错误,模型可修正 | 转换为失败 ToolMessage |
| 缺少用户信息 | interrupt |
| 失控循环 | model/tool call limit |
| 无法分类的异常 | 向上抛出并诊断 |
重试应只包瞬态、幂等或可安全重复的动作,不应盲目重试文件修改、支付和发送消息。工具 retry 需按具体工具收窄。
TypeScript 当前没有 ToolErrorMiddleware;fault-tolerance 页明确写出这一点,却同时保留 Python rate_limiter、run_limit、thread_limit prose。JS 代码应以链接的 TypeScript middleware API 为准。
三类应用模式
Deep research
流程是:Todo 拆解问题 → 每个研究子代理做搜索与全文读取 → 主 Agent 评估缺口 → 汇总带引用报告。价值在于搜索噪音留在子代理上下文,而协调者只收短报告。并行与轮数要有硬限制;外部网页是非可信输入。
Content builder
设计目标是 AGENTS.md 固定品牌声音、Skills 表达博客/社交工作流、研究子代理搜集资料、工具生成图片、Filesystem backend 保存产物。该页面当前质量较差:关键概念仍为 TODO,混入 Python task(...) 片段、root_dir、不存在的 delete 能力,并指向 Python/Rich 示例。可借用架构思路,不能把它当作可运行 TypeScript 教程。
RAG
端到端模式:
- loader 获取文档。
- splitter 切分。
- embeddings 建向量。
- vector store 持久化。
- 查询工具取回相关 chunk,并写入与 Agent 相同的 backend。
- chunk analyst 子代理读文件、返回聚焦摘要。
- 主 Agent 基于报告合成并附来源链接。
该“retrieve → offload → delegate”模式减少主上下文污染。生产环境应持久化向量库并按源更新周期刷新,而不是每次启动重建。
RAG 仍受间接提示注入影响。给 chunk 加 # Source、提示模型把内容视为数据只能降低风险,不能提供可靠隔离;输出需要验证引用路径和事实是否与原 chunk 一致。
闭包发现页:Rubric
RAG 内部导航引用了 /oss/javascript/deepagents/rubric。该页当前返回 200,但未出现在 llms.txt 或 sitemap,且正文完全使用 Python 风格:deepagents>=0.6.5、RubricMiddleware 类、snake_case、asyncio 和 Python state 字段。它描述的通用模式是“独立 grader 按 criteria 判断 satisfied / needs_revision,并迭代到上限”,但不能证明 TypeScript SDK 已支持同一 middleware。
因此本笔记把它作为索引外闭包页记录,而不把其 API 列为 TypeScript 已验证能力。需要 TypeScript 运行时 rubric 时,应先核对当前 JS API Reference;否则用显式 LangGraph grader 节点实现。
Data analysis 应用页与语言错位
/oss/javascript/deepagents/data-analysis 展示的是“CSV → 规划 → 沙箱执行 → 生成图表/报告 → Slack 交付”的完整应用闭环。可复用的架构结论是:
- 把数据与产物放入 backend,代码执行放入生产沙箱;本地 shell 仅用于受控开发。
- 任务规划是 opt-in,用 Todo middleware 显式追踪探索、可视化和交付步骤。
- Slack token 留在 Agent 外部的自定义工具中;工具从 backend 下载产物再上传,避免把凭据注入沙箱。
- thread checkpointer 支持多轮与恢复;结束后还要按 provider 指南销毁 sandbox。
- Slack 可替换为本地下载或其他交付通道,外发消息和附件应加入审批、大小/类型校验与审计。
但该 TypeScript 路由当前正文、安装命令和示例全部是 Python:create_deep_agent、InMemorySaver、Python Slack SDK 以及同步 Python backend。它只能证明该应用模式,不能证明示例能在 JS SDK 直接运行;TypeScript 实现必须改用对应 JS API,并先验证 sandbox provider 与 event streaming 的实际签名。
当前文档质量边界
event-streaming是 TypeScript 新代码的权威入口;旧streaming页多为 Python 风格。content-builder与索引外rubric不能直接运行。going-to-production多处使用ainvoke、abefore_agent、thread_id等 Python 表述;架构结论可用,JS 签名需回查 API。- 前端四页的长内嵌 PatternEmbed 源码是文档演示组件,不是业务应用必须复制的代码。