DeerFlow 行为测试实战:用 Monocle Test Tools 为 SuperAgent 编写基于 Trace 的离线与在线断言 DeerFlow 行为测试实战用 Monocle Test Tools 为 SuperAgent 编写基于 Trace 的离线与在线断言【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flowDeerFlow 的行为测试套件backend/tests/monocle/解决一个长期存在的 Agent 测试难题Agent 的行为路由、工具选择、token 成本是运行时产物难以用传统单测锁定。本篇基于该目录的 README 与源码讲解如何用 Monocle 将一次真实运行记录为结构化 trace再把 trace 变成黄金行为参考用离线测试固化正确行为长什么样用在线live测试强制后续每次运行都复现该行为。读完后你能够理解这套两层测试的分工与取舍掌握monocle_trace_asserter的完整流式断言 API并知道如何为新问题编写自己的行为守护测试。核心理念Trace 不是样本数据而是带标签的正确性参考该套件的工作方式在 README 中定义得很清晰用 Monocle 对 Agent 插桩让它回答一个具体问题当运行结果符合预期调用了预期的 agent、发出了预期的工具调用、token 消耗合理后把这次运行捕获为 trace这个 trace 就是该问题的黄金参考——它记录的是正确行为而不是一份普通样本数据把它转成断言离线示例演示了具体做法再把同样的断言指向在线 Agent对同一问题的真实运行于是之后每次运行都必须复现当初的行为。原文档对此的概括是离线测试用于锁定好行为的样子pin down what good looks like在线测试负责对真实运行强制执行它。Monocle 记录的 trace 是结构化的一次 agent 调用、每一次工具调用、token 用量、时间戳全部以 OpenTelemetry 风格的 span 表示。仓库里提交的 示例 trace 共包含 17 个 span其span.type分布为1 个workflow、1 个agentic.turn、1 个agentic.invocation对应langgraph.graph.state.CompiledStateGraph.stream、6 个agentic.tool.invocation1 次web_search 5 次web_fetch、4 个inference.framework与 4 个inference.modelapi。断言正是读这些 span 的name、entity.*.nameagent 名、工具名以及 input/output 事件来做的。两层测试的分工一个离线示例 两个在线守护套件由两层组成见 test_deerflow.py 的文件头注释一个离线示例test_assertion_api_example从文件加载已录制的 trace把该套件用到的全部流式断言词汇集中展示在一处。它不需要任何密钥和网络。但因为断言对象是冻结的 JSON它守护的是trace 格式与 asserter 接线而不是 DeerFlow 的行为本身——应当把它当作编写自定义断言的工作样例worked example。两个在线测试test_web_research_live与test_sandbox_write_file_live端到端驱动真实 Agent断言真实运行发出的 trace。它们才是真正的行为守护任何改动只要改变了路由、工具选择或 token 成本就会在这里被捕获。它们通过MONOCLE_LIVE_TESTS1显式 opt-in默认跳过——因此即使在一份凭证和config.yaml齐全的检出中普通运行也绝不会花模型 token、不触网、不写沙箱。在线测试还有一处容易被忽略的设计输出文本每次运行都会变化因此在线断言只校验结构 宽松的 token 预算并且刻意省略了时长断言——一个要做 LLM 调用与网络 I/O 的在线运行天然波动墙钟时间断言只会带来 flaky。这一点在 test_deerflow.py#L84-L87 的注释中写得很明白。目录结构与各文件职责文件职责test_deerflow.py离线示例 两个在线测试外加一个守卫 live 开关行为的门控测试conftest.pyrun_agentfixture仅供 live 路径使用_helpers.py路径常量与run_deerflow()traces/离线示例加载的已录制 tracerequirements.txt该套件的独立依赖conftest.py顶部有一处刻意的设计它把本目录插入sys.path镜像 backend 根conftest.py的做法让_helpers在任何 pytest import 模式下都可导入而.env的加载被限定在 live fixture 内部收集或运行离线测试时永远不会读取任何秘密。live_tests_enabled()默认关闭的门控_helpers.py#L20-L30 中_TRUTHY {1, true, yes, on} def live_tests_enabled() - bool: ... Off by default so the plain pytest backend/tests/monocle/ run can never spend model tokens, hit the network, or write to a sandbox — even on a fully configured checkout where credentials and config.yaml are present. return os.getenv(MONOCLE_LIVE_TESTS, ).strip().lower() in _TRUTHY这个门控本身还有一个自守护的测试test_live_gate_defaults_offtest_deerflow.py#L43-L55用monkeypatch验证未设置变量时返回False、设为1时返回True、设为0时又回到False。这就是普通pytest运行不可能发起模型调用、网络请求或沙箱写入这一承诺的测试级证据。run_agentfixture 与run_deerflow()conftest.py#L21-L42 的run_agentfixture 按顺序执行三层检查任何一层不满足就pytest.skiplive_tests_enabled()为假 → 跳过提示设置MONOCLE_LIVE_TESTS1deerflow不可导入例如只装了 test-tools 的 venv→ 跳过config.yaml不存在 → 跳过。通过后它load_dotenv(REPO_ROOT / .env)可选然后返回run_deerflow。后者[_helpers.py#L33-L42的要点是不做任何硬编码模型覆盖def run_deerflow(message: str) - str: from deerflow.client import DeerFlowClient client DeerFlowClient(config_pathstr(CONFIG_PATH)) return client.chat(message, thread_idfmonocle-test-{uuid.uuid4().hex[:8]})模型完全由config.yaml解析所以 live 测试走的是 DeerFlow 自己的模型解析路径——config.yaml可以选任意 providerOpenAI、Anthropic、Gemini 等没有任何硬编码的 key 要求。每次运行用一个随机thread_idmonocle-test-xxxx隔离会话。DeerFlowClient本体位于 client.py其 docstring 说明chat()在没有 checkpointer 时是无状态的、thread_id仅用于文件隔离——这正是 live 测试用一次性 thread 的底层原因。插桩由谁负责_helpers.py 的头注释点明了分工Monocle 插桩由 Test Tools 的 validator随monocle_trace_asserterfixture 安装所有run_deerflow只负责驱动 Agent已装好的插桩负责捕获运行中的 span。也就是说测试代码里看不到任何显式的tracer.start_span——trace 的产出完全由monocle_test_tools接管。提交的 Trace为什么整体提交、为什么改名即失败traces/web_research_ev_battery.json 是一次真实运行的完整、未修改的录制约 192 KB、17 个 span整体提交是为了让离线示例解析的是一份真迹而非手工拼造的 fixture。原文档明确指出它因此内嵌了录制当时点的 DeerFlow system prompt 以及该运行从网页抓取的内容从 trace 内容可以印证span 事件的data.input里就包含 You are DeerFlow 2.0, an open-source super agent 开头的系统提示以及entity.2.name gpt-4o这类录制时的模型信息。其中不含任何凭证。离线断言钉死在这份具体 trace 与monocle_apptrace0.8.8 的 span 形状上LangGraph这个 agent span 名、工具名、输入措辞三者中任何一个被重命名都会让离线测试在行为未变的情况下失败。这就是它的取舍它守护 trace 格式契约代价是 prompt、工具或模型变化时必须重新录制 trace。离线示例完整流式断言词汇一览test_deerflow.py#L61-L81 的test_assertion_api_example是整个套件断言词汇的集中展示断言对象是一份固态 EV 电池调研的录制运行def test_assertion_api_example(monocle_trace_asserter: TraceAssertion): # 从文件加载录制 trace离线数据源 monocle_trace_asserter.with_trace_source(file, trace_pathEXAMPLE_TRACE) # agent 断言名为 LangGraph 的 agent 被调用且输入包含指定措辞 monocle_trace_asserter.called_agent(LangGraph).contains_input(solid-state EV batteries) # 输出断言任意一个关键词出现在输出中即可 monocle_trace_asserter.contains_any_output(solid-state, battery, batteries, EV) # 工具断言指定 agent 下调用过 web_search monocle_trace_asserter.called_tool(web_search, LangGraph) # 工具调用次数用下限而非精确值源码注释见下 monocle_trace_asserter.called_tool(web_fetch, LangGraph, min_count2) # 否定断言不该出现的工具 monocle_trace_asserter.does_not_call_tool(image_search, LangGraph) # token / 时长预算 monocle_trace_asserter.under_token_limit(100_000) monocle_trace_asserter.under_duration(60, span_typeworkflow)其中called_tool(web_fetch, LangGraph, min_count2)的注释值得单独学习test_deerflow.py#L75-L78录制里实际有 5 次web_fetch但断言的意图是至少抓了几个来源fetch 次数运行之间真实会波动所以保持下限floor而不收紧到精确次数。这是写行为断言时断言意图而不是偶然数值的一个具体示范。在线测试把同一套断言指向真实运行两个在线测试展示了 live 路径的写法——用validator.test_workflow(run_agent, {test_input: (...)})驱动 Agent之后断言真实运行发出的 tracedef test_web_research_live(monocle_trace_asserter: TraceAssertion, run_agent): Live web-research path: the agent researches and uses web_search. monocle_trace_asserter.validator.test_workflow( run_agent, {test_input: (Research the current state of solid-state EV batteries in 2025 and write a 1-page markdown briefing with sources.,)}, ) monocle_trace_asserter.called_agent(LangGraph).contains_input(solid-state EV batteries) monocle_trace_asserter.contains_any_output(solid-state, battery, batteries, EV) monocle_trace_asserter.called_tool(web_search, LangGraph) monocle_trace_asserter.under_token_limit(200_000) def test_sandbox_write_file_live(monocle_trace_asserter: TraceAssertion, run_agent): Live sandbox path: the agent authors a file with write_file and stays off the web. monocle_trace_asserter.validator.test_workflow( run_agent, {test_input: (Write a Python script that prints the first 10 Fibonacci numbers and save it to a file named fib.py in the sandbox.,)}, ) monocle_trace_asserter.called_agent(LangGraph).contains_input(Fibonacci) monocle_trace_asserter.called_tool(write_file) monocle_trace_asserter.does_not_call_tool(web_search, LangGraph) monocle_trace_asserter.under_token_limit(100_000)两个 live 测试各锁定一条行为路径研究类问题必须走web_search沙箱写文件类问题必须调write_file且不上网does_not_call_tool(web_search, LangGraph)是路由层面的否定守护。token 预算在 live 下放宽到 200_000研究/ 100_000写文件比离线示例的 100_000 更宽松正对应live 波动更大的取舍。依赖隔离为什么这套测试不在 CI 里跑requirements.txt 只有两行实质内容其头注释解释了隔离动机# Kept out of backend/pyproject.toml on purpose: monocle_test_tools hard-depends # on the ML eval stack (bert-score, sentence-transformers, transformers - torch # the CUDA wheels, ~48 packages, 950 lines in uv.lock). monocle_test_tools0.8.8 python-dotenv1.0monocle_test_tools硬依赖 ML 评测栈torch、transformers、sentence-transformers 等所以它是独立的 requirements 安装而非 backend 依赖——把这份重量挡在应用锁定的依赖之外。版本钉在 0.8.8 另有原因离线示例用到的 file trace 数据源with_trace_source(file, trace_path...)在 0.7.x 中不存在。由此产生一个直接推论README 原话这些测试没有一条跑在 CI 里——make test会收集整个模块然后干净地跳过pytest.importorskip(monocle_test_tools, ...)见 test_deerflow.py#L30-L34连离线示例在内。这是一套按需on-demand套件在改动 Agent 行为、工具或路由时本地安装 requirements 后运行或者在专门的 CI job 里装上这些依赖后运行。另一个值得注意的接线细节monocle_trace_asserterfixture 由monocle_test_tools自带的 pytest 插件提供安装后通过pytest11entry point自动注册无需任何pytest_plugins配置。运行方式完整命令与适用前提从仓库根目录pip 方式# from the repo root pip install -r backend/tests/monocle/requirements.txt # offline — no network, no keys; the live tests skip unless opted in pytest backend/tests/monocle/ # opt in to the live behavioural tests (real model calls web requests) MONOCLE_LIVE_TESTS1 pytest backend/tests/monocle/按 backend 约定在backend/下用 uvuv pip install -r tests/monocle/requirements.txt uv run pytest tests/monocle/ # offline MONOCLE_LIVE_TESTS1 uv run pytest tests/monocle/ # live行为契约总结均来自 README 的 Run 一节live 测试是按设计 opt-in没有MONOCLE_LIVE_TESTS1时即便检出中凭证和config.yaml齐全也会跳过因此默认命令永远不花 token、不写沙箱opt-in 之后仍有兜底DeerFlow app 不可导入或config.yaml缺失时依然跳过模型凭证由配置选定的模型自身校验——config.yaml可选任意 provider不存在硬编码的 key 要求DeerFlow 的web_search基于 DuckDuckGo不需要自己的 key。如何添加你自己的行为测试README 给出的四步流程结合源码即可落地在 Monocle 下运行 DeerFlow捕获一次你满意的运行 traceMonocle 默认把 trace JSON 写到.monocle/。这与仓库的 tracing 配置一致MONOCLE_TRACING开关、MONOCLE_EXPORTERS选择导出器默认file即.monocle/下的 trace JSON实现与配置细节见 tracing/monocle.py、tracing 模块说明 及其测试 test_monocle_tracing.py离线示例把 trace 移入 traces/用monocle_trace_asserter.with_trace_source(file, trace_pathpath)加载行为测试通过run_agentfixture 驱动在线 Agent再调用monocle_trace_asserter.validator.test_workflow(run_agent, {test_input: (...)})用流式 API 断言called_agent(...)、called_tool(...)支持min_count下限、contains_input/contains_any_output(...)、under_token_limit(...)、under_duration(..., span_typeworkflow)以及否定式的does_not_call_tool(...)。实践上先照抄test_assertion_api_example的形状把断言在冻结 trace 上跑通再把同一组断言从文件源切到test_workflow的 live 源——这正是该套件离线固化、在线执法两步法的具体操作形态。边界说明为什么内容/质量评测没有接入套件只覆盖结构性断言。内容/质量评测evals刻意未接入原因是当前版本monocle_test_tools的本地 eval 与 file 加载的 trace 无法组合README Evaluations (note) 一节声明式的test_spans[].evalcomparer:metric会被validator.validate()静默忽略——_evaluate_span没有任何调用点于是本应失败的断言如缺失必需关键词会空洞地通过流式check_eval()路径是按 Okahu eval-service 的签名filtered_spans接的线而本地 evaluator如keyword_presence不接受该参数会抛TypeError。因此选择省略本地 eval而不是把它们加进来变成空洞的 no-op——宁缺毋滥的原则同样体现在评测层。若需要对回答内容做质量打分Okahu eval 层需要OKAHU_API_KEY仍是可选项这也与 tracing/monocle.py 中okahuexporter 要求OKAHU_API_KEY的配置校验相呼应见 test_monocle_tracing.py#L203-L216。小结这套backend/tests/monocle/套件提供了一个可复制的 Agent 行为测试范式用录制 trace 作为黄金行为参考用离线层守护 trace 格式与断言接线、用显式 opt-in 的在线层守护真实行为依赖被刻意隔离在独立的requirements.txt默认运行零副作用不花 token、不触网、不写沙箱。当你在 DeerFlow 中修改了路由逻辑、工具集或 prompt 之后这套按需套件就是验证Agent 是否仍按预期行为的合适工具pip install -r backend/tests/monocle/requirements.txt先跑离线再视需要以MONOCLE_LIVE_TESTS1打开在线守护。【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考