Original task【免费下载链接】nanobotUltra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps项目地址: https://gitcode.com/gh_mirrors/nanob/nanobot{{ task_context }}Agent response{{ response }}渲染时由调用方传入 task_context原始任务全文与 responseAgent 的实际响应全文。task_context 在 gateway 心跳场景下就是拼接了 _HEARTBEAT_PREAMBLE 与 HEARTBEAT.md 内容的任务提示词见下文调用链response 则是 process_direct 返回的完整文本结果。两者被并排交给 evaluator 模型供其对照任务要求了什么与Agent 实际做了什么来裁决。 ## 二、底层实现evaluate_response 与 evaluate_notification 工具契约 模板只是台词真正执行判定的是 [nanobot/utils/evaluator.py](https://link.gitcode.com/i/2f277eb9fd89be93885e4b651884d780) 中的 evaluate_response 异步函数。该模块的模块级 docstring 写明了它的定位Post-run notification evaluation for heartbeat checks——在 heartbeat 执行完内部检查之后发起一次**轻量 LLM 调用**来决定结果是否值得通知用户。 ### 2.1 工具契约结构化输出而非自由文本 evaluator 调用时携带一个唯一的函数工具 evaluate_notification其 JSON Schema 如下[evaluator.py#L60-L82](https://link.gitcode.com/i/2f277eb9fd89be93885e4b651884d780#L60-L82) - should_notifyboolean必填true 表示结果包含可操作/重要的信息用户应当看到false 表示例行公事或内容为空可以安全抑制。 - reasonstring可选一句话说明裁决理由。 evaluate_response 使用 provider.chat_stream_with_retry 发起请求消息列表为 system即本模板渲染结果加 user本模板 user 部分渲染结果并传入工具定义调用参数为 max_tokens4096、temperature0.0。**temperature 固定为 0** 意味着裁决过程被刻意设计为确定性判定而非创造性生成——这是一个二值化、可审计的门控不是风格化写作。 ### 2.2 裁决结果的解析与失败闭合 拿到 LLM 响应后代码按以下顺序处理[evaluator.py#L84-L138](https://link.gitcode.com/i/2f277eb9fd89be93885e4b651884d780#L84-L138) 1. 若 should_execute_tools 为假——无论是因为 finish_reason 异常导致工具调用被忽略还是模型干脆没有发起工具调用——都记 warning 并**回退到 default_notify** 2. 若工具调用正常则取出 arguments 中的 should_notify缺省时用 default_notify与 reason记录结构化日志 evaluate_response: should_notify{}, reason{} 后返回布尔结果 3. 任何异常provider 崩溃、网络错误等都会被捕获logger.exception 记录后同样**回退到 default_notify**。 这里的核心设计是 **fail-safe 语义由调用方决定**函数签名中的 default_notify: bool False 表示默认失败即静默而 heartbeat 场景正是以 default_notifyFalse 调用见下文实现**失败闭合fail closed**——判读模型挂了宁可保持沉默也不误报。测试用例 [test_evaluator.py#L123-L133](https://link.gitcode.com/i/c5cbf6838103e1d0a70717195a26a313) 的 test_fail_closed_on_error 与 test_fail_closed_on_no_tool_call 分别验证了provider 抛错与模型只回文本未调用工具两种情况下均返回 False而 test_fallback_on_error 与 test_no_tool_call_fallback 则验证了当 default_notifyTrue 时同样的故障会回退为 True。这组对称的测试精确刻画了回退值由调用方掌控的契约。 ## 三、完整调用链heartbeat 任务如何被门控 通知门控挂在 gateway 的心跳heartbeatcron 任务上调用链位于 [nanobot/cli/gateway_runtime.py](https://link.gitcode.com/i/a29e97e9e0258e7bffb97e773edceecc)。心跳任务的完整流程如下[gateway_runtime.py#L621-L686](https://link.gitcode.com/i/a29e97e9e0258e7bffb97e773edceecc#L621-L686) 1. cron 服务触发名为 heartbeat 的系统任务读取工作区下的 HEARTBEAT.md文件缺失或经 _heartbeat_has_active_tasks 判断没有活动任务时直接跳过 2. 选定投递目标渠道_pick_heartbeat_targetCLI 渠道直接跳过 3. 组装任务提示词_HEARTBEAT_PREAMBLE Read the active tasks below, perform each one, and report what you did: HEARTBEAT.md 全文 4. **关键防绕过设计**通过 message_tool.set_suppress_delivery(True) 临时抑制 message 工具的直接投递能力强制整个回合的输出都只能走门控——注释明确写道 Internal check: funnel all output through the post-run gate so the turn cant deliver directly via the message tool and skip it杜绝了 Agent 在回合内偷偷把结果发出去绕过判定的可能 5. agent.process_direct(prompt, session_keyheartbeat, ...) 执行任务拿到 resp.content 作为待裁决文本 6. 调用 resolve_evaluator_prompt(config.workspace_path) 解析当前生效的 evaluator 提示词覆盖或内置默认再以 default_notifyFalse 调用 evaluate_response调用计入 llm_usage_source(cron) 用量统计 7. 若 should_notify 为真则通过 _deliver_to_channel 把完整响应投递到目标渠道并记录否则仅记日志 Heartbeat: silenced by post-run evaluation。 从这条链路可以看出 evaluator.md 模板的实际作用位置它正是第 6 步里作为 system 提示词喂给判读模型的裁决规则书而 user 部分则承载第 3 步的任务全文与第 5 步的响应全文。 ## 四、自定义覆盖workspace 级 prompts/evaluator.md ### 4.1 覆盖优先级与截断保护 resolve_evaluator_prompt(workspace)[evaluator.py#L44-L58](https://link.gitcode.com/i/2f277eb9fd89be93885e4b651884d780#L44-L58)按以下优先级解析生效提示词 1. 若 workspace/prompts/evaluator.md 存在且非空使用该文件内容workspace 覆盖优先 2. 否则使用内置默认即 render_template(agent/evaluator.md, partsystem, stripTrue) 的渲染结果[evaluator.py#L39-L41](https://link.gitcode.com/i/2f277eb9fd89be93885e4b651884d780#L39-L41)。 覆盖文件经由 workspace_prompts.load_workspace_prompt_override 读取并受 EVALUATOR_PROMPT_MAX_CHARS继承自 WORKSPACE_PROMPT_MAX_CHARS长度上限约束超过上限的覆盖会被截断并追加 ... (truncated)同时记录 warning——防止一个失控的超大文件把判读调用撑爆[evaluator.py#L26-L36](https://link.gitcode.com/i/2f277eb9fd89be93885e4b651884d780#L26-L36)。测试 [test_evaluator.py#L43-L75](https://link.gitcode.com/i/a6720c79585dc5881faaa8498b781986) 验证了四个边界覆盖优先、空覆盖回退默认、**无法解码如 UTF-16的覆盖回退默认**、超长覆盖被截断。 ### 4.2 通过命令初始化可编辑副本 [prompts/README.md](https://link.gitcode.com/i/c566b2ed820cc63829ddb485c4cc00ae) 中明确把 evaluator.md 归类为高级覆盖advanced override并指出绝大多数用户无需触碰。若确实需要定制在聊天会话中执行 text /evaluator-prompt init该命令由 builtin.py 中的cmd_evaluator_prompt实现builtin.py#L574-L621它会以内置默认提示词为内容在工作区下创建prompts/evaluator.md文件已存在时返回提示不会覆盖。不带参数执行/evaluator-prompt则报告当前状态——使用的是默认还是自定义提示词、对应文件路径、以及如何恢复默认。删除或清空该文件即可回到内置行为。命令返回消息中反复强调一条硬性约束与模板语义一脉相承It must still instruct the model to call theevaluate_notificationtool; otherwise the gate fails closed and stays silent.即自定义覆盖必须保留调用evaluate_notification工具的指令。这是唯一不可违背的底线门控依赖结构化工具调用来产出结果如果覆盖后的提示词让模型改为输出自由文本、或干脆不调用工具evaluate_response会落入should_execute_tools为假的回退分支在 heartbeat 的default_notifyFalse语义下失败闭合、永远沉默。4.3 配置开关与参数速查心跳任务本身由 gateway 配置控制configuration.md#L2209-L2234{ gateway: { heartbeat: { enabled: true, intervalS: 1800, keepRecentMessages: 8 } } }【免费下载链接】nanobotUltra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps项目地址: https://gitcode.com/gh_mirrors/nanob/nanobot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 SEO 优化官网定制响应式建站教育培训建站