` 编排子代理)
深度解析 deepagents 的 quickjs 系统提示词如何用evalREPL 与task()编排子代理【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents本文以langchain-quickjs中间件注入给 Agent 的系统提示词快照 quickjs_system_prompt_no_tools.md 为核心骨架逐段拆解这份提示词教给模型的每一件事eval工具的运行沙箱契约、状态持久化语义以及在 JavaScript REPL 内部通过task()分发子代理、用纯 JS 完成并发 fan-out、多阶段流水线与结果汇总的编排方法论。读完本文你将能理解该提示词每个段落背后的实现原理、三种持久化模式的差异以及如何通过CodeInterpreterMiddleware的配置项控制提示词的实际形态。这份快照是什么一段由模板渲染并提交到仓库的系统提示词quickjs_system_prompt_no_tools.md不是普通的手写文档而是CodeInterpreterMiddleware在每次模型调用前注入系统消息的实际提示词文本快照。它由 _prompt.py 中的_REPL_SYSTEM_PROMPT_TEMPLATE与_SUBAGENT_SYSTEM_PROMPT_TEMPLATE两个模板拼装而成经由wrap_model_call/awrap_model_call的append_to_system_message挂接到系统消息尾部。该快照由 test_system_prompt.py 渲染并逐字节比对。测试用create_deep_agent装配带CodeInterpreterMiddleware(modemode)的 Agent捕获模型收到的第一条SystemMessage再与snapshots/目录下的提交快照做相等断言。其目的正如测试模块 docstring 所说捕捉 deepagents SDK 的变化对 quickjs 中间件所拼装提示词的静默漂移——即便 quickjs 合作伙伴包自身未改动SDK 侧内置的write_todos提示、工具列表格式或 harness 脚手架变化也会改变最终提示词快照测试能在 CI 中及时报警。快照可用pytest --update-snapshots在有意的提示词变更后重新生成选项定义见 conftest.py。文件名中的 no tools 指 PTC程序化工具调用tools.*命名空间未启用因此提示词中不含 API Reference —toolsnamespace 段而 Dispatching Subagents withtask 段仍然存在因为该 Agent 暴露了 Deep Agents 的task工具且subagentsTrue为默认值。Interpretereval工具的运行时契约提示词开头的### Interpreter段落向模型完整声明了eval工具的约束。这些约束并非修辞而是 middleware.py 中CodeInterpreterMiddleware真实执行的运行参数声明含义对应实现/默认值持久化 REPL变量、函数跨工具调用及跨轮次存活modethread默认见_resolve_mode顶层awaitPromise 在调用返回前解析eval_handle_asyncawait_promise见 _repl.py运行时沙箱无文件系统、网络、标准库、墙钟 APIfetch/require/fs/process/真实Date.now()不可用或被 stub纯计算无法触达宿主工具、文件、网络PTC 关闭时的side_effects_line超时每次调用 5.0s_DEFAULT_TIMEOUT 5.0内存总计 64 MB_DEFAULT_MEMORY_LIMIT 64 * 1024 * 1024控制台捕获console.log输出随结果一并返回_ConsoleBuffer默认capture_consoleTrue其中超时与内存的中文表述Timeout: 5.0s per call. Memory: 64 MB total.由模板中的{timeout}与{memory_limit_mb}占位符填充——memory_limit传入时按memory_limit // (1024 * 1024)换算为 MB 单位。_repl.py中eval_async把超出预算的调用映射为模型可见的error typeTimeout与error typeOutOfMemory并在并发访问共享上下文时抛出ConcurrentEvalError。README 中的错误表还列出了PTCCallBudgetExceeded单次 eval 内tools.*调用预算默认 256 次与Deadlock顶层 Promise 永远无法解析且无进行中的宿主工作。三种持久化模式在提示词中的差异同一份 Interpreter 段落因mode取值不同而有三种渲染结果分别对应三个提交快照quickjs_system_prompt_no_tools.mdmodethread默认State persists across tool calls and across multiple turns for this conversation thread.——状态跨工具调用且跨多轮对话靠快照持久化支撑after_agent生成快照、before_agent恢复见middleware.py。quickjs_system_prompt_no_tools_turn.mdmodeturnpersists across tool calls within a single turn... DO NOT persist across multiple turns——仅回合内持久after_agent直接evict槽位不做快照。quickjs_system_prompt_no_tools_call.mdmodecallfresh sandboxed REPL for each invocation——每次调用全新环境eval_sync/eval_async的finally分支调用reset_repl。mode还会同步改变eval工具自身的code参数说明与描述文案分别由render_eval_tool_code_doc与render_eval_tool_description生成保证工具描述与系统提示词口径一致。task原语在 REPL 内部运行配置好的子代理提示词的第二大段落### Dispatching Subagents withtask 定义了一个 REPL 内可用的顶层全局函数。它的来源是 _subagent.py 中的find_subagent_task_tool当宿主 Agent 的工具集中存在名为task且输入字段覆盖description、subagent_type的 Deep Agents 工具时_repl.py 的_register_task_bridge会在 QuickJS 上下文里注册一个异步宿主函数并执行Object.freeze(globalThis.task)使其不可覆盖。原语签名await task({ description, // full autonomous task prompt subagentType, // configured subagent name label, // optional short UI label for this dispatch responseSchema, // optional JSON Schema for structured output }); // - PromiseunknownJS 侧使用 camelCase 键名桥接层_validate_task_payload在进入 Python 分发路径前将其校验并转换为 snake_casedescription必填非空字符串。它是子代理收到的唯一提示词因此必须自包含目标、约束、要检查的内容、期望返回的形状或详细程度都要写全。subagentType必填必须是已配置子代理的名字。label可选字符串。只用于直播进度 UI 的展示有显式label时用label否则从 description 截断派生不发送给子代理、不影响执行。responseSchema可选 JSON Schema。提供后解析结果直接是匹配 schema 的已类型化 JS 值模型无需再调JSON.parse除非子代理有意返回 JSON 字符串。关于responseSchema_subagent.py 还施加了三个硬限制序列化后不超过 4096 字节、嵌套深度不超过 5 层、属性总数不超过 32 个_SCHEMA_MAX_BYTES/_SCHEMA_MAX_DEPTH/_SCHEMA_MAX_PROPERTIES超限直接ValueError。此外动态 schema 可用于声明式declarative子代理runnable 支撑的子代理会拒绝动态 schema因为其 runnable 已编译完成。审批模型从 eval 内部发起的免审批分发提示词明确警告task从正在运行的eval调用内部发起分发不经过父 Agent 的ToolNode管理的task工具也不会为每次分发触发父级interrupt_on/ HITL 审批。声明式子代理仍会遵循其自身 spec 内配置的审批中间件。如果需要父级审批提示词给出的三条出路是在 JavaScript 之外使用普通的task工具确保eval调用本身被审批门控中间件层面设置subagentsFalse强制子代理走正常父级task路径。这与CodeInterpreterMiddleware参数文档中subagents的警告完全一致属于安全边界设计不是缺陷。编排心智模型数组进、数组出在 JS 里做多阶段提示词给出一个重要的思维框架Hold your work in JS: an array of items in, an array of results out.——把task的分发结果合并回对应条目多阶段分析就是在 JS 中对数组做过滤/重组后再跑下一轮。整个工作流可以放在一次eval调用内也可以拆成多次关键约束是不要跨调用重复劳动复用已在作用域中的中间变量见下文复用作用域。从实现看call_subagent_task_tool会为每次分发在 LangGraph 的 custom stream 上发出SubagentStartEvent/SubagentCompleteEvent/SubagentErrorEvent生命周期事件typesubagent、按phase判别携带subagent_type、展示用 label显式 label 截断到 120 字符description 派生回退截断到 60 字符description 事件字段截断到 200 字符、以及duration_ms耗时——这正对应提示词所说的live progress UI。有界并发 fan-out批量约 10硬上限 32提示词要求用Promise.all并行分发独立工作但按约 10 个一组显式分批避免一次性启动数百个子代理。桥接层在_repl.py中定义了_MAX_TASK_CALLS_PER_THREAD 32为每个 REPL 建立asyncio.Semaphore(32)硬性限制并发子代理调用数——hard per-REPL cap of 32 concurrent subagent calls即来源于此。提示词给出的安全审查示例对每个文件派一个 reviewer 子代理返回结构化漏洞清单const files [/src/a.ts, /src/b.ts, /src/c.ts]; // found while exploring const batchSize 10; const reviewed []; for (let i 0; i files.length; i batchSize) { const batch files.slice(i, i batchSize); reviewed.push(...(await Promise.all(batch.map(async (file) { const result await task({ description: Read file and review it for SQL injection. Cite line numbers., subagentType: reviewer, responseSchema: { type: object, properties: { vulnerabilities: { type: array, items: { type: object, properties: { type: { type: string }, line: { type: number }, evidence: { type: string }, }, required: [type, line, evidence], }, }, }, required: [vulnerabilities], }, }); return { file, ...result }; })))); }这里responseSchema的实战价值体现得淋漓尽致{ file, ...result }之后每个元素都是file {type, line, evidence}[]的确定形状后续排序、按行号比较、分支判断都可以直接编码无需解析自由文本。先用自己的工具探索再分发提示词强调模型自己已有的读文件、列目录、glob、grep 等普通工具调用与eval无关应在写编排脚本之前先用它们理解任务——读数据文件、列目录、grep 出关键点再决定如何切分工作。绝不要写eval代码去生成一个子代理来读文件或列目录那是确定性的步骤直接用一次工具调用就能完成为一个 agentic loop 花掉整个子代理纯属浪费。理解工作形态后有三种拆法可自由组合条目本就分离时每个文件或每条记录一次分发大输入自己分块读入、切分必要时把每块写成独立小文件每块分发一个子代理先做廉价分类只对值得深入的项目再做深度分发。提示词还有一个关键原则Hand each subagent a locator, not a payload.——子代理有自己的文件工具凡是落在文件里的东西要审查、重写、审计的文件只传路径让子代理自己去读不要为了把文件内容粘贴进 description 而通读整个文件那会让每次分发都膨胀并重复承载同一份内容。内联内容只留给没有路径的小数据或派生数据如单个解析出的记录大分块应写成文件再传路径。最终结果在 JS 中汇总。多阶段组合先分类过滤再深度审查提示词给出的两阶段示例完美示范了在 JS 数组间做流水线第一轮让子代理做廉价分类filter出 handler 且有风险的项目第二轮只对幸存者做深度安全审查const tagged await Promise.all(files.map((file) task({ description: Read file and classify it as handler, util, test, or config., subagentType: reviewer, responseSchema: { type: object, properties: { kind: { type: string }, risky: { type: boolean } }, required: [kind, risky], }, }).then((tag) ({ file, ...tag })) )); const riskyHandlers tagged.filter((it) it.kind handler it.risky); const deepReviews await Promise.all(riskyHandlers.map((it) task({ description: Deep security review of it.file . Cite line numbers., subagentType: reviewer, }).then((review) ({ ...it, review })) ));这种廉价通道 精挑通道的漏斗结构是用有限的子代理预算换取质量的关键手法也正是responseSchema让中间结果可编程的典型场景。用最后一个表达式返回结果而不是console.logeval调用的最后一个表达式或已解析的顶层await的值会被作为结果返回给模型。因此最终表达式应当指向持有结果的变量直接从那里读取。console.log只用于顺带调试其输出被截断而返回值不被截断永远不要用console.log输出真正的结果。实现上_repl.py 的eval_async会先eval_handle_async若句柄是 Promise 则await_promise后再 marshal可 marshal 的值渲染为result{json-ish}/result函数或不可 marshal 的值渲染为result kindhandle[Function] arity2/result。结果与 stdout 各自独立截断到max_result_chars默认 4000字符后回传模型_ConsoleBuffer在收集阶段就用同一上限约束缓冲。数值渲染遵循 Node REPL 惯例整值浮点42.0渲染为整数42避免模型被 JS 单一数值类型误导。大量中间集合应保留在 JS 变量里只返回紧凑摘要或小切片要持久化完整输出就让子代理写文件或用eval外部的文件工具写。复用上一次eval留在作用域里的变量由于 REPL 在同一轮内持久thread模式甚至跨轮每个顶层let/const/function/class都会被保留并提升到全局作用域下一次eval调用可直接按名引用。提示词给出自我诊断标准如果发现自己把上一轮产生的大数组/对象重新敲成字面量那就说明该变量还在作用域里直接引用它——重新输入既浪费 token又会与真实运行结果产生漂移。// An earlier eval bound this: // const auditResults await Promise.all(files.map(/* ...audit... */)); // A later eval — reference it; do NOT paste the findings back in as a literal: const findings auditResults.flatMap((r) r.findings.map((f) ({ ...f, file: r.file })) ); const verified await Promise.all(findings.map((f) task({ description: Verify this finding: f.evidence, subagentType: verifier, }).then((v) ({ ...f, ...v })) ));这一机制由 REPL 的模块式持久化实现支撑顶层声明存活于 QuickJS 上下文跨eval调用可见thread模式下after_agent还会将上下文快照写入 checkpointer、before_agent恢复让跨轮次复用得以成立。当用户要求 workflow 时提示词最后一条规则如果用户请求提到 workflow或以其他方式使用该词就应把工作 fan out 给子代理而不是自己逐个工具调用硬扛。先按需用自己的工具探索然后写 JavaScript 用task()分发、汇总结果——重点是把繁重的活并行摊开而不是一次工具调用一次地磨。这是对整份提示词编排哲学的一句收束eval是编排面task是劳动力池中间的 JS 是胶水。配置项如何塑造提示词形态提示词的内容不是写死的CodeInterpreterMiddleware的构造参数直接决定最终注入文本。完整参考见 README.md 的 Configuration reference 与middleware.py的 docstringCodeInterpreterMiddleware( memory_limit64 * 1024 * 1024, # bytes, shared across contexts timeout5.0, # per-call seconds max_ptc_calls256, # per-eval tools.* bridge calls, None disables (DoS risk) tool_nameeval, # what the model calls it max_result_chars4000, # result/stdout truncation, each capture_consoleTrue, # install console.log/warn/error bridge subagentsTrue, # expose global task(...) when host has a Deep Agents task tool modethread, # thread | turn | call max_snapshot_bytesNone, # defaults to memory_limit; larger snapshots are dropped ptcNone, # None | list[str] | list[BaseTool] )与本文主题直接相关的联动点mode决定 Interpreter 段落中持久化描述句的措辞以及eval工具 description/code-doc 的文案_prompt.py中三套分支subagents决定是否渲染整段 Dispatching Subagents withtask——关闭时该段完全不出现tool_name替换模板中所有{tool_name}占位符提示词里默认写作evalREADME 源码模板中即如此渲染timeout/memory_limit填充 Timeout: {timeout}s per call. Memory: {memory_limit_mb} MB total.ptc启用时render_repl_system_prompt(ptc_attachedTrue)会把 pure computation 那句替换为指向tools.*命名空间的说明并追加 API Reference —toolsnamespace 段——这正是no_tools快照与mixed_foreign_functions快照之间的区别所在。需要说明的是提示词中 Timeout: 5.0s 衡量的是 QuickJS VM 执行时间而非 Python 墙钟时间等待tools.*宿主调用Python 协程所花时间不计入预算因此慢速宿主调用可能让一次eval的实际墙钟耗时超过timeout。这是middleware.py参数文档明确标注的边界部署时应知悉。结语一份可运行的提示词快照quickjs_system_prompt_no_tools.md看似只是注入模型的文本实则是 quickjs 合作伙伴包与 deepagents SDK 之间契约的冻结切片每一句话都能在 _prompt.py、_repl.py、_subagent.py 与 middleware.py 中找到对应的实现锚点又通过 test_system_prompt.py 的快照断言持续防漂移。理解这份提示词就等于同时理解了模型端的编排方法论与宿主端的运行时保证——对想要在 deepagents 上构建一次 eval、多路子代理并行工作流的开发者来说它是性价比极高的起点。【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考