ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenHuman Summarizer Agent 系统提示词与运行时设计深度解析:为 Orchestrator 压缩超量工具结果的内置子代理

OpenHuman Summarizer Agent 系统提示词与运行时设计深度解析:为 Orchestrator 压缩超量工具结果的内置子代理 OpenHuman Summarizer Agent 系统提示词与运行时设计深度解析为 Orchestrator 压缩超量工具结果的内置子代理【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman导读在 OpenHuman 的 Agent 编排链路中工具调用的返回体动辄是几十 KB 甚至上百万 token 的 JSON、日志或网页抓取结果直接灌入上下文会迅速烧穿 context budget。本文以 summarizer 系统提示词 为骨架完整剖析其提取契约—输出格式—边界情况—Token 预算四层设计并结合 payload_summarizer.rs、context.rs 等源码讲清它的触发阈值、熔断机制、配置项与只对 Orchestrator 生效的接线逻辑。读完你既能按契约复刻一个高质量结果压缩器也能理解 OpenHuman 如何在代码层面防止该内部子代理被误调用、误展示、误递归。一、Summarizer 是什么一次调用、无工具、只做一件事summarizer是 OpenHuman 注册在 agent registry 中的一个内置 Agent。它的定位在提示词开头一句话讲完Your one job is to compress a single oversized tool result into a compact, information-dense note that the Orchestrator can use without re-invoking the tool.翻译成工程语言就是它把单个超量工具结果压成一张信息密度极高的便签让 Orchestrator 不必重新调用工具就能继续推理。三个设计约束与源码一一对应约束提示词表述对应实现单次运行You run exactly once per invocationagent.toml 中max_iterations 1无工具可用with no tools and no follow-up iterations同一文件[tools] named []即工具白名单为空直接返回Return the summary directly as your only response运行时通过invoke_with_events以非流式 unary 方式取run.text()值得注意的一个细节agent.toml的when_to_use明确写着Do NOT call from an LLM — this agent is runtime-dispatched only。也就是说它不是给用户或其他 Agent 通过spawn_subagent之类工具手动拉起的公开子代理而是由运行时在特定条件下自动分发的内部工具。temperature 0.2的低温度设定同样服务于压缩任务对稳定性的要求——每次压缩都应严格遵循契约而不是发挥创意。agent.toml 中omit_identity、omit_memory_context、omit_safety_preamble、omit_profile、omit_memory_md全部为true配合model.hint summarization运行时据此解析一个偏便宜/偏快的模型说明这是一个刻意剥离了一切个人化上下文的纯管道型 Agent——它不需要记住用户是谁、不需要加载记忆文件只需执行机械的压缩函数。二、提取契约Extraction Contract输入三要素提示词把 Summarizer 的输入定义为一个三元组这也是它在一次运行中唯一能看到的东西tool name——产生该返回体的工具名如GITHUB_LIST_ISSUES、GMAIL_FETCH_MESSAGE、file_readparent task hint可选——一句话描述 Orchestrator 当时想完成什么任务raw tool output——工具的原始输出。这三者在运行时如何被组装成一条用户消息在 payload_summarizer.rs 的build_summarizer_prompt中有清晰的代码佐证fn build_summarizer_prompt(tool_name: str, parent_task_hint: Optionstr, raw: str) - String { let hint_line parent_task_hint .map(|h| format!(Parent task hint: {}\n\n, h)) .unwrap_or_default(); format!( Tool name: {}\n\n{}Raw tool output (summarize per the extraction contract in your system prompt):\n\n--- BEGIN ---\n{}\n--- END ---, tool_name, hint_line, raw ) }这里有两个工程细节值得展开--- BEGIN ---/--- END ---边界标记原始 payload 被明确包裹在标记之间子代理可以无歧义地区分payload 本体与提示词脚手架不会把Tool name:那几行当成待压缩内容。hint 出现在 payload 之前这样 Summarizer 在读到正文前就掌握了父任务意图可以在压缩时优先挑选与该意图相关的事实。注意parent_task_hint是Optionstr——不是每次工具调用都有任务提示。没有时该行整体省略提示词对此的处理是如果没有 hint就依靠 payload 自身结构推断重点。三、压缩的黄金法则保留什么、丢弃什么提取契约是这份提示词的灵魂它把压缩从玄学变成可执行的三条指令3.1 必须保留Required facts标识符是最高优先级Identifiers are the single most important thing. Never drop them.任何 Orchestrator 在后续工具调用中可能需要用来动手操作的标识符都必须完整保留ID、哈希、URL、文件路径、邮箱地址、用户名、SKU、订单号等。逻辑很直白Orchestrator 拿到摘要后若要继续操作比如把 issue #42 关闭回复这封邮件依赖的正是这些标识符——丢一个就等于切断一次后续动作。3.2 按需保留Optional supporting context从 payload 中挑出3–5 个人类回答 parent task 时最关心的事实且优先级必须服从 parent task hint。提示词给了两个对照例子hint 是找出最紧急的未关闭 issue→ 优先保留紧急度 / 严重性 / labelhint 是总结昨天的邮件→ 优先保留主题 / 发件人 / 时间戳。这条规则的实质是压缩不是均匀抽样而是朝任务方向倾斜的信息筛选。3.3 保留结构线索Structural hints如果 payload 是列表说明共有多少项如果分页说明页边界如果是文件给出行数或章节标题。这些结构信息让 Orchestrator 能判断要不要用更窄的查询重新抓取——例如第一页 30 条共 3 页足以让 Orchestrator 决定是直接基于现有数据继续还是再拉一页。3.4 必须丢弃原始标记/格式噪音HTML 标签、CSS、JSON 包裹、样板化的表头——除非标记本身就是信息比如你要总结的就是一个 HTML 页面的结构项与项之间无差异的重复字段100 条记录里每条都带同一个常量字段保留一个即可Provider 元数据Orchestrator 无法据其行动的字段如X-Request-ID头、毫秒级时间戳、内部服务器 ID。3.5 违反规则的失败判据注意 payload_summarizer.rs 的handle_summarizer_result给了压缩一条硬性验收线若摘要不小于原始 payload视为失败并回退summary.len() raw.len()时record_failure()并返回Unavailable(Failed)。这与提示词里 If the summary is the same size as the payload, you have failed 完全互为表里——代码把提示词中的失败定义落实成了可自动判定的守卫。四、输出格式规范标准摘要模板提示词要求只输出摘要文本本身没有 Here is the summary... 式开场白、没有 Let me know if you need more details 式收尾、没有 JSON 包裹纯 Markdown为 Orchestrator 的下一步推理优化。规范模板如下完整继承自提示词[Tool output summary — tool_name] 1-2 sentence overview: what the payload is, how many items/how much data ## Key facts - fact 1 with identifier - fact 2 with identifier - ... ## Identifiers preserved - id_1: one-line description - id_2: one-line description - ... (Only include this section if the payload contained IDs/URLs/hashes. Skip otherwise.) ## Original size original_bytes bytes → summary of this note模板各节的设计意图首行[Tool output summary — tool_name]一个可 grep 的元信息头让 Orchestrator 一眼识别这是一份压缩摘要以及它源自哪个工具1–2 句总览payload 是什么、多少项 / 多少数据Key facts带标识符的关键事实列表——上文的 3–5 条 supporting context 落在这里Identifiers preserved仅当 payload 含 ID / URL / hash 时才出现每行一个id: 一句话说明。把标识符单独成节而非混在 Key facts 里是为了让 Orchestrator 能机械地扫描、复制这些 ID 用于后续工具调用Original size原始字节数 → 摘要长度既满足观测需要也形成压缩率自证。五、边界情况处理提示词用四条规则覆盖了压缩中最容易翻车的场景场景处理规则payload 已经很短产出短摘要不要注水Dont padpayload 完全是错误输出在摘要顶部逐字保留错误信息——Orchestrator 需要看到精确错误才能决定下一步路由含二进制噪音base64、hex dump只总结其存在与长度不要尝试解码parent task hint 与 payload 矛盾要邮件却给了 GitHub issues以 payload 为准——你报告的是工具实际返回了什么而不是被要求了什么最后一条尤其精辟它把 Summarizer 从任务执行器定位成事实报告器。压缩器无权因为 hint 与 payload 不符就篡改内容或强行圆场忠实报告返回体是它的职业底线。六、Token 预算提示词给出明确的量化指标大多数 payload 的目标800–1500 输出 token硬上限2000 token绝不超出。这个预算和运行时如何配合在 payload_summarizer.rs 中子代理分发时用MaxTokensModel::new(source.build_summarizer(model, ...), max_output_tokens)包装模型max_output_tokens取definition.max_turn_output_tokens否则回落到AGENT_TURN_MAX_OUTPUT_TOKENS——即提示词定的 2000 上限在运行时还有一道模型层的强制闸门。更妙的是模型提示词中的omit_memory_md true、构建提示时agents_md_global / agents_md_local传None源码注释解释得直白AGENTS.md 之类的项目指令对压缩工具返回体这个狭窄内部任务是纯噪音would waste the tight token budget this summary path is trying to reclaim。七、禁止事项清单行为边界提示词用六个 Do not 圈定了 Summarizer 的能力边界每一条都是对 Agent 失控风险的具体防御Do not ask clarifying questions——你只有一次机会没有澄清回合Do not emit tool calls——你没有任何工具Do not try to solve the parent task——你是预处理器preprocessor不是 OrchestratorDo not fabricate information——payload 里没有的字段写(no value)或直接省略严禁编造Do not copy the raw payload verbatim——摘要与原文等长即失败与 3.5 的代码守卫呼应隐含的Do not recurse——这正是 agent.toml 注释与 context.rs 源码中反复强调的为 summarizer 构建的窄子代理提示词不含spawn_subagent等工具[tools] named []从而在源头杜绝了压缩器递归调用自身的经典故障。context.rs 的注释记录了这段历史阈值默认值曾因递归分发根因被置为 0 关闭修复后重新以 4000 tokens 启用。八、运行时剖析SubagentPayloadSummarizer 如何调度它提示词定义了怎么做而 payload_summarizer.rs 定义了何时做、失败怎么办。核心是SubagentPayloadSummarizer它实现PayloadSummarizertrait 的唯一入口maybe_summarize_in_parent执行流程如下8.1 四个 pass-through 判定按顺序低于阈值estimate_tokens(raw) threshold_tokens→ 直接放行返回SummarizeOutcome::NotNeeded这是唯一安静的退出模型被告知任何信息——小结果上任何提示都是噪音高于上限tokens max_payload_tokens→ 跳过 LLM 调用返回Unavailable(PayloadTooLarge)交给下游既有的tool_result_budget_bytes截断兜底对百万级 token 的 blob 付一次 LLM 调用没有经济性熔断器已跳闸连续失败 ≥ 3 次 → 本会话内 Summarizer 变成 no-op返回Unavailable(Disabled)防止一个坏掉的压缩器拖垮每一次工具调用分发失败 / 返回空 / 未缩小回退到原始 payload返回Unavailable(Failed)作为安全网。其中estimate_tokens的实现是text.len().div_ceil(4)即按约每 4 字符 1 个 token 估算与tree_summarizer::estimate_tokens的启发式一致。8.2 三态结果模型SummarizeOutcome刻意把不需要压缩和压缩没发生区分成两个状态Summarized(SummarizedPayload)用summary替换原始 payload 进入 agent history同时记录original_bytes与summary_bytes供观测NotNeededpayload 本就不大保持原样且不打扰模型Unavailable(UnavailableReason)raw payload 原样给模型但在截断阶段全部跑完之后把一段notice文本前缀到工具结果上。8.3 给模型的通知为什么强调 Do not re-run the tool for a summaryUnavailableReason::notice()为三种失败各生成一条以[openhuman: summarization unavailable — ...]开头、以Do not re-run the tool for a summary.结尾的说明。源码注释详细记录了这个措辞的演化如果模型面对被截断的大输出最合理的本能反应就是再调一次同一个工具拿摘要——这会在用户侧呈现为无声的重分发挂起hang。因此通知必须是前置的notice在调用方的截断阶段之后、且以 prefix 而非 append 方式应用否则先被截断的是通知本身并且必须是无理由的纯指令——注释用大段篇幅论证了三个候选理由重新调用会返回相同结果重新调用不会产生摘要完整输出已经在这里了分别对时间变化型 API 工具、Failed 变体、截断场景为何都是假的。8.4 只对 Orchestrator 生效的接线模块文档明确只有 orchestrator 会话会接入PayloadSummarizer。在 factory.rs 中构造条件写得很直白if agent_id orchestrator config.context.summarizer_payload_threshold_tokens 0 { // ... 用 threshold_tokens 构造 SubagentPayloadSummarizer 并注入 }Welcome、integrations_agent、researcher、planner、archivist 等其他类型子代理得到None工具结果不被触碰Summarizer 自身也是None——它永远无法递归压缩自己的输入。8.5 静默执行内部文本不进用户视野invoke_tinyagents_summarizer_in_parent中有一处极易被忽视但极重要的选择子代理用invoke_with_eventsunary、streaming false而非invoke_in_parent运行。原因写在了源码注释里invoke_in_parent会继承父会话的streaming true于是压缩器的内部文本如[Tool output summary — tool]会以AgentProgress::TextDelta形式流到共享EventSink进而被 Web 桥渲染成chat_interim气泡展示给用户——这恰恰违背了 Summarizer 只为 Orchestrator 上下文而存在的初衷。unary 路径在共享 sink 的前提下子代理 start/completed 生命周期事件仍可达观察者对父事件流保持静默与reprompt_for_required_block的内部修复调用同样克制。九、配置项三个旋钮一个开关Summarizer 的所有行为都可通过 ContextConfig 调整对应配置段为[context]配置项默认值语义context.summarizer_payload_threshold_tokens4000约 16000 字符触发压缩的下界单位是估算 tokenchars / 4。设为 0 可完全禁用。存在历史别名summarizer_payload_threshold_bytescontext.summarizer_max_payload_tokens2_000_000硬上限超过则跳过 LLM 压缩交给tool_result_budget_bytes截断兜底。存在历史别名summarizer_max_payload_bytescontext.summarizer_modelNone跟随调用方当前模型可选覆盖为自动压缩指定更便宜/更快的模型降低长会话下压缩成本两点事实校准两个阈值配置项本身不依赖估算函数配置值直接按估算 token解释estimate_tokenschars / 4只在运行时判定 payload 大小是否越界时使用agent.toml 的when_to_use文案中写有 default 500000 的旧注释但 context.rs 中default_summarizer_payload_threshold_tokens()的实际返回值为4000。以 schema 源码为准当前仓库中默认触发阈值为 4000 估算 token且该默认值曾在递归分发根因修复后被重新启用见 7.6 节及 context.rs 注释。十、从提示词到代码这套设计的工程启示最后把这份提示词与它的运行时实现合起来看它其实是提示工程 系统工程双层防御的范例提示词层定义正确行为提取契约标识符优先、标准模板、边界规则、token 预算、禁止清单——全部是可被模型直接执行的指令也是ARCHETYPE常量include_str!(prompt.md)见 prompt.rs被pub暴露的原因它邀请 embedder 提供自定义 summarizer 时不必重新发明这套来之不易的契约代码层定义不可能越界的行为max_iterations 1、空工具白名单防递归、temperature 0.2、三态结果模型、熔断器、非流式静默执行、max_output_tokens强制闸门、仅在 orchestrator 会话接线——把提示词里的每一条软约束都落成了硬约束。对一个想要自行实现超量工具结果压缩的开发者来说这份设计最值得抄走的四件事是标识符永远不能丢、摘要必须显著小于原文否则回退原文、内部修复调用必须对用户静默、连续失败要熔断而不是无限重试。这四点中的任何一点缺失都可能让一个看似精巧的压缩器在实际 Agent 编排中变成上下文杀手或挂起制造机。延伸阅读本文涉及的源码与配置相对路径汇总——summarizer 系统提示词、summarizer 注册配置、提示词构建器 prompt.rs含其单元测试 prompt_tests.rs、运行时压缩器 payload_summarizer.rs、上下文配置 schema/context.rs、Orchestrator 会话接线 factory.rs。【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表