news 2026/9/16 18:49:22

local-deep-research 端到端性能评测实战:live-service 测试与人工评测 harness 完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
local-deep-research 端到端性能评测实战:live-service 测试与人工评测 harness 完全指南

local-deep-research 端到端性能评测实战:live-service 测试与人工评测 harness 完全指南

【免费下载链接】local-deep-research~95% on SimpleQA (e.g. Qwen3.6-27B on a 3090). Supports all local and cloud LLMs (llama.cpp, Ollama, Google, ...). 10+ search engines - arXiv, PubMed, your private documents. Everything Local & Encrypted.项目地址: https://gitcode.com/GitHub_Trending/lo/local-deep-research

tests/performance/是 local-deep-research 项目面向真实外部服务(arXiv、OpenAlex、Ollama 等)的实时端到端测试与人工评测(eval)工具箱:这里不关心"函数是否返回正确类型",而关心"整条研究流水线在真实网络上跑起来效果如何"。读完本文你将掌握:如何在本机运行这套 live-service 测试与评测脚本、如何解读 KEPT/REMOVED 过滤决策与跨模型/跨提示词对比结果、如何通过run_full_search.py一键生成可复现的 Quick Summary 报告,以及如何为新的子系统扩展同类评测。

一、定位与设计哲学:为什么单独存在一个 performance 测试目录

与仓库内大量使用 mock 的单元测试不同,tests/performance/下的代码真实命中外部服务并测量端到端行为。这意味着它天然具有不确定性——arXiv 可能限流、Ollama 可能未启动、网页可能改版——因此这套测试被刻意排除在 CI 之外

CI 排除机制有两层(详见 .github/workflows/docker-tests.yml):

  1. pytest 标记过滤:CI 运行pytest -m 'not integration ...',所有被打上integration标记的测试都不会进入 CI;
  2. 命名约定:非test_*.pyeval_*.pyrun_*.pybuild_*.py评测脚本根本不会被 pytest 收集,它们是被当作普通 Python 程序直接执行的。

这套"测试 + 评测"双轨设计的核心理念是:pass/fail 断言只能证明"没崩",而人工阅读输出才能判断"好不好"。因此该目录下的产物主要分为两类:

类型典型文件成功信号
pytest 集成测试test_live.pytest_extraction_benchmark.pytest_new_adapters_integration.py断言通过,同时print()输出供人审阅
人工评测 harnesseval_prompt.pyeval_models.pyrun_full_search.pybuild_eval_dataset.py没有断言,人类阅读输出并做出判断

二、目录布局:一个子系统一个评测文件夹

以下是 README 规划的完整目录结构(当前仓库实际存在_sharedrelevance_filtercontent_fetchersearch_enginesdatabaseapi_auth等文件夹,strategies等目录按同一规范逐步落盘):

tests/performance/ ├── _shared/ — 通用管线 harness,不绑定单一子系统 │ ├── run_full_search.py — 围绕 quick_summary() 的薄 CLI;--engine / --model 参数 │ └── build_eval_dataset.py — 查询 × 引擎 × 模型的叉积批量运行器(驱动 run_full_search.py) ├── relevance_filter/ — LLM-as-judge 相关性过滤(实时 arXiv + Ollama) │ ├── test_live.py — pytest 实时 arXiv + Ollama 测试(integration + requires_llm + slow) │ ├── eval_prompt.py — 人工判断 harness:固定 judge 与 arXiv,切换提示词变体 │ └── eval_models.py — 人工判断 harness:固定提示词与 arXiv,切换 judge 模型 ├── strategies/ — 分解 / 迭代推理策略对比(实时 LLM + meta_search) │ └── compare_strategies_visual.py — 人工判断 harness:跨策略时间线可视化绘图 ├── content_fetcher/ — 200+ 真实 URL 的 HTML 提取质量基准(实时网络) ├── search_engines/ — 新适配器对接实时 API 的集成测试(Open Library、Zenodo 等) ├── mcp/ — MCP 客户端并发测试(真实 subprocess echo server) ├── database/ — 加密数据库向后兼容(安装上一版 PyPI 发行版) └── api_auth/ — 带认证的研究 API 校验(需运行中的服务器 + Puppeteer)

值得注意的扩展规范(README 原文要求):为某个新子系统新增性能测试/评测时,应在其旁边创建与relevance_filter/平级的兄弟文件夹(例如 embeddings、rate limiting、search-engine latency),而任何真正与子系统无关的通用内容放入_shared/

三、运行前准备:环境变量是这套评测的开关

tests/performance/的几乎所有脚本都通过环境变量控制行为,默认值面向"典型本地开发机":

环境变量默认值作用
LDR_TESTING_WITH_MOCKStrue(见 tests/conftest.py)必须设为false,否则任何带requires_llm标记的测试会被自动跳过
LDR_TEST_OLLAMA_BASE_URLhttp://localhost:11434Ollama 服务地址
LDR_TEST_OLLAMA_MODELqwen3.5:9b默认 judge/生成模型标签
LDR_TEST_ARXIV_MAX40每次查询拉取的 arXiv 结果条数
LDR_TEST_OLLAMA_MODELS(无)eval_models.py专用:逗号分隔覆盖默认模型集合
LDR_BOOTSTRAP_ALLOW_UNENCRYPTED(无)本机无 TLS 环境下允许 Ollama 明文连接,dev 脚本均要求true
LDR_APP_DEBUG(无)置为true开启 DEBUG 日志,可看到过滤器的 KEPT/REMOVED 决策

其中最关键的是第一个:tests/conftest.pyLDR_TESTING_WITH_MOCKS默认设为true,这会让所有requires_llm测试在未显式覆盖时自动跳过——这正是 README 强调"运行本套测试必须显式传false"的原因。

四、运行 pytest 集成测试:命令与编写规范

4.1 标准运行命令

在仓库根目录执行:

LDR_TESTING_WITH_MOCKS=false \ LDR_TEST_OLLAMA_BASE_URL=http://localhost:11434 \ LDR_TEST_OLLAMA_MODEL=qwen3.5:9b \ pdm run pytest tests/performance/ -v -s -m integration
  • -s用于显示测试内print()的输出——这是审阅决策质量的关键;
  • -m integration只选中实时测试(slowrequires_llm子集可另行组合选择)。

4.2 pytest 测试的三条硬性规范

README 明确要求本目录下所有 pytest 测试必须遵守:

  1. 打标记:携带@pytest.mark.integration,通常还要加@pytest.mark.requires_llm@pytest.mark.slow,从而保证被排除在 CI 之外(CI 用-m 'not integration'过滤);
  2. 优雅跳过而非失败:当所依赖的外部服务不可达时应skip,而不是fail,保证在没有本地 Ollama 端点时跑本地性能套件不会红一片;
  3. print()输出决策:打印 KEPT/REMOVED(或等价细节),配合-s供人审阅质量。

以 relevance_filter/test_live.py 为例:其ollama_llmfixture 先通过safe_get(带allow_private_ips=True,允许访问用户配置的本地/私有网络端点)探测/api/tags,不可达即pytest.skip,可达才惰性导入langchain_ollama.ChatOllama构造真实 judge(num_ctx=8192)。随后对两条精心挑选的查询("LLM interpretability latest research"、"sparse autoencoders for mechanistic interpretability")参数化运行filter_previews_for_relevance,打印 KEPT/REMOVED 清单,并设置两条"软断言"兜底:

  • assert kept:过滤器不能把 40 条结果全拒掉(judge 坏了或提示词过度拒绝);
  • assert len(kept) < len(previews):过滤器不能全保留(提示词已失去过滤作用)。

这两条软断言刻意宽松——测试的主要价值是"把决策浮出水面给人看",而非用严格阈值钳死行为。

五、人工评测 harness:提示词与 judge 的可控变量实验

eval_*.py脚本是人类判断工具:没有断言,成功信号是"人读输出后做出判断"。它们的共同设计是每次查询只抓取一次 arXiv 结果,然后只切换单一变量,保证对比干净。

5.1 eval_prompt.py:固定 judge,切换提示词

运行方式(eval_prompt.py):

LDR_BOOTSTRAP_ALLOW_UNENCRYPTED=true \ LDR_TEST_OLLAMA_BASE_URL=http://localhost:11434 \ LDR_TEST_OLLAMA_MODEL=qwen3.5:9b \ pdm run python tests/performance/relevance_filter/eval_prompt.py

脚本围绕同一个共享模板脚手架展开,各变体只替换中间一段 guidance,查询块、日期块、结果块与输出契约完全一致,从而隔离提示词措辞这一唯一变量:

def _make_template(guidance: str) -> str: return ( "This is a relevance-filtering step. Kept results move forward ...\n\n" 'Query: "{query}"\n' "Current date: {current_date}\n\n" "Search results:\n" "{preview_text}\n\n" f"{guidance}\n\n" "Output ONLY the 0-based indices of relevant results as a comma-separated list, nothing else.\n" "Example: 0, 2, 5" )

内置的 5 个提示词变体各自对应一段真实历史教训:

变体guidance 要点背景
V0_current直接主题匹配比关键词匹配更重要当前 main 分支基线(选择性偏差修复后),用于验证新变体
V1_prefer_smaller倾向更小的高置信度选择曾因引入选择性偏差被移除,保留以量化回归
V2_inclusive_adjacent保留主题密切相关的工作,拒绝仅共享关键词者针对"只共享查询词之一导致的假阴性"
V3_keep_framing反过来强调"保留什么"阅读它是否有助于回答查询;边缘相关也值得保留
V4_terse更简洁,接近历史 "MUST directly address" 表述纯关键词重叠不属于相关

执行时每个变体以batch_size=10max_parallel_batches=4调用 relevance_filter.py 的filter_previews_for_relevance,最后输出三组对比统计:

  • Per-variant counts:每个变体保留了 40 条中的多少;
  • Consensus(全部变体都保留)Rejected by ALL(全部变体都拒绝):稳定性锚点;
  • Disagreements(部分保留部分拒绝):真正值得人眼逐个审阅的争议项。

5.2 eval_models.py:固定提示词,切换 judge

运行方式(eval_models.py):

LDR_BOOTSTRAP_ALLOW_UNENCRYPTED=true \ LDR_TEST_OLLAMA_BASE_URL=http://localhost:11434 \ pdm run python tests/performance/relevance_filter/eval_models.py

默认模型集合按"家族 × 尺寸多样性"挑选,用于回答"提示词质量能否跨模型泛化":

  • qwen3:4b—— 最小模型,测试能力下限;
  • qwen3.5:9b—— 当前默认;
  • gemma3:12b—— Gemma 家族;
  • ministral-3:14b—— Mistral 家族;
  • gpt-oss:20b—— OpenAI 系开源模型;
  • qwen3.5:27b—— 更大的 Qwen,观察规模是否带来增益

脚本会先通过/api/tags枚举 Ollama 已安装模型,自动跳过未安装的标签(可通过LDR_TEST_OLLAMA_MODELS覆盖模型列表),然后逐模型记录 keep 数量与耗时,输出与 5.1 相同的 Consensus / Rejected by ALL / Disagreements 分析,外加每个模型的运行秒数,方便在"判断质量"与"推理成本"之间做权衡。

六、一键端到端评测:run_full_search.py 生成 Quick Summary 报告

_shared/run_full_search.py 是围绕项目编程接口quick_summary()的薄 CLI:产出与 Web UI "Quick Summary" 模式同风格的报告,但只需一条命令,从而可以用同一查询在不同引擎之间做 diff(例如 arxiv vs openalex),用于在真实场景中评估相关性过滤提示词与综合生成质量。

6.1 用法与全部参数

LDR_BOOTSTRAP_ALLOW_UNENCRYPTED=true LDR_TESTING_WITH_MOCKS=false \ pdm run python tests/performance/_shared/run_full_search.py \ --query "LLM interpretability latest research" \ --engine openalex \ --output /tmp/ldr_report_openalex.md

参数表(均带默认值,--query必填):

参数默认值说明
--query(必填)研究查询
--enginearxiv搜索引擎,可选arxiv/openalex/wikipedia/searxng
--modelqwen3.5:9b(或LDR_TEST_OLLAMA_MODELOllama 模型标签
--ollama-urlhttp://localhost:11434Ollama 端点
--iterations1对应search.iterations,与 REST API 的 Quick Summary 默认一致
--output/tmp/ldr_report_<engine>.md报告输出路径
--verbose开启LDR_APP_DEBUG=true,显示过滤器的 KEPT/REMOVED 决策

6.2 底层实现:从探活到加密写盘

该脚本的实现细节揭示了整条评测管线的关键环节:

  1. 启动前探活check_ollama()safe_get(超时 5s、allow_private_ips=True)探测{url}/api/tags,不可达则向 stderr 报错并返回退出码 1——这是所有 dev 脚本共用的"优雅降级"模式;
  2. 设置快照:通过create_settings_snapshot()一次性覆盖llm.provider=ollamallm.modelllm.ollama.urlsearch.toolsearch.iterationsapi.allow_file_output=True等键(见 run_full_search.py),以programmatic_mode=True调用quick_summary()
  3. 报告头元数据:输出文件以 Markdown 头记录EngineModelIterationsSources(来源数量)、Elapsed(耗时秒数)、Generated(生成时间)——这段头正是后续build_eval_dataset.py解析"来源数"的契约;
  4. 加密写盘:文件最终经write_file_verified()落盘(对应api.allow_file_output配置门控),与 Web UI 的文件输出走同一安全路径。

七、批量评测数据集构建:build_eval_dataset.py

_shared/build_eval_dataset.py 把 6.1 的脚本提升到"实验矩阵"级别:对每个(查询 × 引擎 × 模型)单元格端到端运行quick_summary,同时保存格式化报告与包含 KEPT/REMOVED 决策的详细日志,最后汇总成 CSV 供按来源数、耗时排序并目视检查离群值。

LDR_BOOTSTRAP_ALLOW_UNENCRYPTED=true LDR_TESTING_WITH_MOCKS=false \ pdm run python tests/performance/_shared/build_eval_dataset.py \ --output-dir ./ldr_eval_output

默认配置即产生一个4 查询 × 2 引擎 × 3 模型 = 12 单元格的网格(README 注明在qwen3.5:9b上全程约 60–90 分钟):

默认值覆盖方式
--queriesLLM interpretability 相关 4 条(如 "mechanistic interpretability of transformer language models"、"sparse autoencoders for neural network feature discovery"、"safety alignment and refusal in large language models")"q1|q2"|分隔,因查询内可能含逗号)
--enginesarxiv,openalex逗号分隔
--modelsqwen3.5:9b,gemma3:12b,ministral-3:14b逗号分隔
--output-dir./ldr_eval_outputreports/logs/summary.csv
--parallel1并发单元格数;慎用——每个并发槽都会在 Ollama 端加载一个 LLM,同时加载 2+ 个不同模型标签可能 OOM;同模型并行通常安全(Ollama 排队)
--force强制重跑已存在报告文件的单元格

该脚本在工程上有三个值得借鉴的细节:

  • 可断点续跑(resumable)already_done()检查reports/<key>.md是否存在,存在即跳过;--force可重跑,用于从被打断的运行中恢复或扩展数据集;
  • 子进程隔离:每个单元格以subprocess.run独立调用run_full_search.py,保证单元格间互不污染,并设置 30 分钟硬超时;超时或异常时删除半截报告文件,避免下次运行误判"已完成"而跳过;
  • 增量 CSV 汇总summary.csv按(query, engine, model)upsert,跑完一行写一行,实时可查,字段含sourceselapsed_sexit_codereport_pathlog_path

八、其他子系统的实时评测矩阵

8.1 content_fetcher:200+ 真实 URL 的提取质量基准

test_extraction_benchmark.py 对 200+ 个来自新闻、技术、学术、政府、教育、购物、社交、金融、国际站点等十余个类目的真实页面跑提取管线(源码注释口径为 17 个类目,ALL_CATEGORIES字典实际含 misc 杂项共 18 个分组),并对比两种下载模式:

  • 静态下载器HTMLDownloader,超时 20s):无需 JS 渲染的常规页面;
  • 自动下载器AutoHTMLDownloader,超时 20s,基准中显式enable_js_rendering=True以覆盖 JS 渲染回退路径):面向 JS 重型页面。

每个页面记录内容长度、boilerplate 命中数(13 个关键词:cookie、sign up、newsletter、subscribe、accept all、privacy policy、log in、add to cart……)与耗时,按类目打印成功/失败的进度条与平均值。断言刻意宽松以容忍反爬、地理封锁与付费墙:静态子集允许 20% 失败率,全量基准要求整体成功率不低于 60%。同一文件还验证了ContentFetcher的 URL 路由正确性——arXiv、PubMed、PMC、Semantic Scholar、OpenAlex、bioRxiv 学术 URL 应分别被路由到专用下载器。

8.2 search_engines:新适配器的实时 API 集成

test_new_adapters_integration.py 覆盖 5 个新增搜索适配器:Open Library、Project Gutenberg、Zenodo、Stack Exchange、PubChem。每个适配器验证:

  • 字段契约:结果必须含titlelink,且source为期望值(如 "Open Library"、"Project Gutenberg"、"Stack Overflow");
  • 领域特有字段:Stack Exchange 的score(整数)与tags、PubChem 的molecular_formula/molecular_weight/cid/smiles、Zenodo 的doi
  • 全内容路径search_snippets_only=False时结果应含content字段且清理掉_raw

最值得关注的是TestFactoryIntegration类:它不 mock 安全白名单,而是走完整生产路径settings snapshot → search_config() → get_safe_module_class()(安全白名单)→ 引擎实例化,验证新适配器确实已在 module_whitelist.py 中登记(ALLOWED_MODULE_PATHS/ALLOWED_CLASS_NAMES)。若工厂返回 None,最可能的原因就是白名单缺失——这为"新增引擎"提供了明确的可执行检查清单。

8.3 database:加密数据库的向后兼容

test_backwards_compatibility.py 守护加密存储的兼容性:数据库由旧版创建、新版必须仍能打开,防止 salt 修改之类的破坏性变更悄悄引入。该文件包含两类测试:

  • 快路径(CI 内):加密常量稳定性测试(README 注明已迁移至test_encryption_constants.py),能捕获约 99% 的破坏性变更;
  • 慢路径(人工触发)TestBackwardsCompatibility通过pip index versions local-deep-research获取上一版本号,在隔离 venv中安装上一版 PyPI 发行版并生成数据库,再以当前代码打开、读取写入的UserSettings记录验证数据完整。该测试标记slow并受RUN_SLOW_TESTS=true门控(完整运行需pytest ... -m slow),总超时 10 分钟——绝大多数时间耗在依赖安装上。

8.4 api_auth:带认证的研究 API 校验

test_research_validation.py 依赖运行中的服务器 + Puppeteer 认证(见同目录conftest.py),验证/api/start_research接口行为:缺query、空query应返回 400/422;有query但缺省/空model应返回 200(走数据库默认模型);未认证请求应被拒绝(400/401)。它代表的是"完整部署环境下的验收测试"这一评测场景。

九、快速上手决策表:什么场景用哪个工具

目标使用工具关键标志
快速验证相关性过滤器没崩、决策合理pytest tests/performance/relevance_filter/test_live.py -v -s打印 KEPT/REMOVED,两条软断言兜底
比较多个提示词措辞eval_prompt.py固定 judge,5 个变体一次跑完
比较多个本地 LLM 的判断力eval_models.py固定提示词,自动跳过未安装模型
生成一份可复现的 Quick Summary 报告run_full_search.py单条命令,报告含来源数与耗时元数据
构建跨引擎/跨模型的评测数据集build_eval_dataset.py12 单元格默认网格,可断点续跑
验证新增搜索适配器test_new_adapters_integration.py字段契约 + 工厂安全白名单路径
守护加密数据库兼容性test_backwards_compatibility.pyCI 快路径 + 隔离 venv 慢路径
验收带认证的 research APItest_research_validation.py需运行中的服务器 + Puppeteer

十、扩展指南:为新子系统添加性能评测

遵循 README 的规范,当你要为 embeddings、rate limiting、search-engine latency 等新子系统添加评测时:

  1. 新建平级兄弟文件夹,如tests/performance/embeddings/,不要堆进relevance_filter/或其它既有目录;
  2. 通用、与子系统无关的 harness 放_shared/(如新的批量 runner);
  3. pytest 测试记得三件套标记(integration+requires_llm/slow)、外部服务不可达时skip、用print()输出决策细节供人审阅;
  4. 纯评测脚本(无断言)用run_*.py/eval_*.py命名,避免被 pytest 误收集。

这套"实时集成测试 + 人工评测 harness + 可复现报告"的组合,正是 local-deep-research 在本地 LLM 与多搜索引擎组合下持续保障研究质量的关键基础设施。

【免费下载链接】local-deep-research~95% on SimpleQA (e.g. Qwen3.6-27B on a 3090). Supports all local and cloud LLMs (llama.cpp, Ollama, Google, ...). 10+ search engines - arXiv, PubMed, your private documents. Everything Local & Encrypted.项目地址: https://gitcode.com/GitHub_Trending/lo/local-deep-research

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 18:48:33

MATLAB语音增强三大经典算法原理与实现对比

简介&#xff1a;本资源是一套面向信号处理与语音算法初学者的MATLAB语音增强仿真实验包&#xff0c;聚焦噪声环境下提升语音清晰度的核心问题&#xff0c;适用于高校通信/音频工程课程实践、毕业设计及算法入门学习。压缩包共21个文件&#xff0c;含8个核心MATLAB源码&#xf…

作者头像 李华
网站建设 2026/9/16 18:46:09

Go函数调用瞬间:栈帧构造、栈拷贝与逃逸分析全解析

写 Go 写了几年&#xff0c;我越来越觉得&#xff0c;搞懂一次函数调用里发生的事&#xff0c;是区分“会写 Go”和“懂 Go”的一条分水岭。表面上看&#xff0c;你只是在代码里敲了一行f(x)&#xff0c;但底层牵动的东西一点不少&#xff1a;栈帧&#xff08;stack frame&…

作者头像 李华