学习笔记 · Obsidian
Deep Agents TypeScript:后端、工具、权限、沙箱与协议
执行能力不是一个开关
生产系统应把 Agent 的能力拆成四层:
| 层 | 主要职责 | 默认隔离 |
|---|---|---|
| 自定义 / MCP 工具 | 调用 API、数据库和业务动作 | 由工具自身决定 |
| Backend | 为内置文件工具提供统一命名空间 | 由具体后端决定 |
| QuickJS interpreter | 在 Agent 循环内用 JavaScript 编排数据、工具和子代理 | 默认无宿主文件、网络、shell、时钟 |
| Sandbox | 隔离文件系统并提供 shell | 隔离宿主,但不防提示注入或网络外泄 |
安全审查必须沿“模型可见工具 → middleware → backend/外部系统 → 凭证和网络”追踪。提示词不是权限边界。
内置工具与 TypeScript 差异
TypeScript 当前公开的内置 harness 工具有:ls、read_file、write_file、edit_file、glob、grep、条件可见的 execute 与同步委派 task。任务计划的 write_todos 需要显式加入 TodoList middleware。
与 Python 页不同,TypeScript 的工具表没有 delete,write_file 文案是“创建新文件”。后端协议的 write() 又说明 create-only 语义;因此不要假定 TypeScript SDK 可覆盖或删除文件,修改现有文件应使用 edit_file,具体能力以当前 API 类型为准。
工具页 prose 中仍出现 create_deep_agent、tools= 和 Python LangChain 链接,是语言复制残留;TypeScript 实际入口是 createDeepAgent({ tools })。
后端选择
| 后端 | 生命周期 | 适用场景 | 关键风险 |
|---|---|---|---|
StateBackend | 同一 thread,经 checkpoint 延续 | 草稿、结果卸载、临时文件 | 大文件会放大 graph state |
StoreBackend | 跨 thread,namespace 决定隔离 | 用户记忆、长期资料 | namespace 错误导致串租户 |
FilesystemBackend | 真实本地磁盘 | 可信本地 CLI、受控 CI | secrets、永久修改、路径逃逸 |
LocalShellBackend | 真实磁盘 + 宿主 shell | 可信本地开发 | 任意宿主命令,无资源隔离 |
ContextHubBackend | LangSmith Hub repo | 有提交历史的共享上下文 | 乐观并发冲突、远端依赖 |
CompositeBackend | 按最长路径前缀路由 | 临时区与持久区混合 | 路由与权限需要共同设计 |
| Sandbox backend | 隔离文件系统 + execute | 生产代码执行、数据分析 | 提示注入、网络外传、成本 |
重要实践:
- 默认后端是线程内的
StateBackend,父子代理共享其中的文件。 - 多租户
StoreBackend必须显式从 runtime 构造 user / tenant / assistant namespace;不要依赖旧的 assistant 默认空间。 FilesystemBackend的 root 目录只有在安全路径解析模式下才可能限制路径;文档此处夹杂 Python 的virtual_mode=True写法,TypeScript 实现需按 API Reference 核对真实字段。LocalShellBackend的 shell 可访问宿主其他路径,任何虚拟路径开关都不是安全边界。- 不要让真实项目目录承载
/large_tool_results/、/conversation_history/等内部文件。常见设计是CompositeBackend默认走 State,只把/workspace/路由到磁盘。 ContextHubBackend写入使用父提交进行乐观并发;冲突时重新拉取再重试,非 UTF-8 上传会按路径失败。
Backend Protocol V2
TypeScript V2 后端统一返回结构化 Result,不应通过抛异常表达“文件不存在”或无匹配:
ls(path)read(filePath, offset?, limit?)readRaw(filePath)write(filePath, content)edit(filePath, oldString, newString, replaceAll?)glob(pattern, path?)grep(pattern, path?, glob?)
实现 shell 时扩展 SandboxBackendProtocolV2.execute(),可选提供 uploadFiles() / downloadFiles() 作为应用侧跨边界传输 API。V1 会在运行时自动适配到 V2;迁移重点是查询方法改为 Result、lsInfo/globInfo/grepRaw 改名,以及二进制内容使用 Uint8Array + MIME type。
后端工厂模式在 1.9.0 起被标记弃用:新代码传入已构造的 backend 实例,运行时上下文由框架解析。文档的“更新到 V2”段落同时含 Python 与 TypeScript 版本,实际签名应以 camelCase TypeScript 表格为准。
文件权限
permissions 只拦截内置文件工具:
read覆盖ls、read_file、glob、grep。write覆盖write_file、edit_file。- 规则按声明顺序,首个匹配项获胜。
- 无匹配时默认 允许。
- 路径必须绝对,禁止
..与~。 - 子代理默认继承;子代理显式配置会整体替换父规则,空数组代表无约束。
权限不覆盖自定义工具、MCP 工具,也不覆盖 sandbox 内的 shell。CompositeBackend 默认路由到 sandbox 时,permission 路径必须落在已知非 sandbox 路由前缀,否则构造时拒绝。
TypeScript 权限页只定义 allow / deny,没有 Python 的路径级 interrupt。技能页中出现 mode="interrupt" 属于跨语言残留;TypeScript 人工审批应通过工具级 interruptOn,不能据此实现按路径中断。
Interpreter:循环内编排,不是操作系统
Interpreter 使用 WASM 隔离的 QuickJS,增加 eval 工具,适合:
- 循环、分支、重试和确定性数据转换。
- 在变量中保留中间结果,避免全部进入模型上下文。
- Programmatic Tool Calling(PTC)批量调用显式 allowlist 中的工具。
- 通过
task()动态扇出子代理。
默认没有网络、宿主文件、shell、包管理器或墙上时钟。PTC 工具名会转换为 camelCase,但参数对象仍遵循原工具 schema。关键配置包括 64 MB 默认堆上限、5 秒单次执行超时、4000 字符结果上限、每次 eval 默认最多 256 个 PTC 调用。
安全边界:PTC 与 task() 从解释器桥接调用,不经过普通 tool-call 路径,所以父 Agent 的 interruptOn 不会逐次执行。需要审批时应 gate eval 本身,并收窄 PTC allowlist;对敏感系统、付费动作和任意网络工具尤其如此。
Sandbox:宿主隔离与数据边界
TypeScript 文档列出的当前集成包括 LangSmith、Deno、Daytona、Leap0、Modal 与 Node VFS;具体可用性、计费和生命周期仍要以各提供方文档为准。
两种架构模式:
- Agent 在 sandbox 内:接近本地运行,但密钥与 Agent 进程都进入容器,升级需重建镜像。
- Sandbox as tool:Agent 在服务端运行,经远程 API 调用 sandbox。密钥可留在外部、Agent 状态不随 sandbox 故障丢失,代价是每次操作的网络延迟。
生产通常使用第二种。应用侧 uploadFiles/downloadFiles 负责播种与回收;模型侧文件工具负责任务过程。两条通道不能混为一谈。
Sandbox 只隔离宿主,不自动解决:
- 间接提示注入。
- sandbox 内敏感文件读取。
- 允许出网时的 HTTP/DNS 外传。
- 资源生命周期与持续费用。
不要把长期密钥放进 sandbox 环境变量或文件。优先把认证封装在宿主工具中,或使用出站代理注入凭证。确实需要注入时仍不能把 HITL 和出网限制视为绝对防护。
多模态文件
Backend V2 的 read_file 可按 MIME 返回图片、音频、视频和文档 content blocks。大媒体应存储在 backend 或对象存储,只把路径/URL和短说明放入消息。
压缩有两个局限:
- offloading 主要按文本 token 计数,纯图片不会因二进制大小自动卸载。
- summarization 把较早消息变成文本摘要;其中媒体 block 不会原样保留。
因此长时间多模态任务应保留可重新读取的原始文件,并让专门子代理做媒体检查后返回短文本结论。
ACP、MCP 与 A2A 的边界
| 协议 | 方向 | 主要用途 |
|---|---|---|
| ACP | 编辑器 ↔ Agent server | 把 Deep Agent 暴露给 Zed、JetBrains、VS Code、Neovim |
| MCP | Agent client → 工具服务器 | 加载外部工具和多模态工具结果 |
| A2A | Agent server ↔ Agent server | 标准化跨 Agent 请求、流式与任务状态 |
ACP 是 TypeScript 独有的强项之一:deepagents-acp 支持 stdio CLI、程序化服务器、多 Agent、slash commands、skills、memory 与 IDE 内 HITL。客户端能力不一;例如部分编辑器没有多 Agent 选择 UI。
/oss/javascript/deepagents/mcp 当前 307 跳转到通用 /oss/javascript/langchain/mcp。该页只覆盖工具加载、HTTP/stdio、认证和多模态工具内容;TypeScript adapter 遇到 MCP isError 会抛 ToolException,不像 Python adapter 那样直接给模型失败 ToolMessage。
/oss/javascript/deepagents/a2a 当前 307 跳转到 /langsmith/server-a2a,是 Agent Server 能力而非 Deep Agents SDK 专属 API。服务端支持 message/send、message/stream 与 tasks/get,要求消息型 state;contextId 映射到 LangSmith thread_id 用于跨 Agent 追踪。可在 langgraph.json 中关闭 A2A 端点。