一个默认零依赖、可离线运行的证据型 Research Agent。它把研究问题拆成受限子查询,编排本地检索、网页搜索、URL 读取和历史记忆,生成带逐条引用审计与运行指标的 Markdown 报告;也可选用本地真实 embedding 做语义召回。
项目没有依赖 LangChain 等 Agent 框架。编排、检索、引用验证、网页安全边界和评测均由 Python 标准库实现,便于直接检查每个决策和失败路径。
- 受限研究流程:
plan → collect → rank → draft → verify → report → memory,可配置研究步数、网页页数、并发数和最终证据数。 - 混合检索:BM25 与可替换 embedding provider 融合排序,再叠加 evidence quality、title overlap 和 MMR 风格去重;默认 hashed backend 用于离线回归,可选
multilingual-e5-small补充跨语言和同义表达候选。 - 多源证据:支持本地
.md/.txt、显式 URL 和 BochaAI Web Search;网页读取并发执行,单页失败不会中断整次报告。 - Audit-driven repair:Claim-level citation audit 发现缺失、无效或明显不受支持的引用时,会把原问题与 unsupported claim 组成 gap query,在共享预算内最多补检索一次并重新起草;仍失败则保留安全 fallback 或拒答。
- 网页安全边界:只接受公开 HTTP(S) 地址,拒绝 URL credentials、localhost、私网/环回/保留地址和内部域名;重定向重新校验,响应限制为允许的文本类型和 2 MB。
- 可观测性:报告记录每个 plan step 的本地命中、搜索 URL、网页成功/失败、最终证据,以及 steps、网页尝试数、耗时和 stop reason。
- 持久化记忆:默认使用 SQLite 保存跨运行任务历史,也可切回 JSON backend。
- 可复现验收:当前本地
71/71个测试通过;默认 hashed 冻结 eval 为20/20,Recall@3、MRR 和 negative rejection 均为1.00;真实 E5 只做了跨语言 retrieval smoke,尚未做消融实验。
question
-> bounded planner
-> evidence collection loop
-> local hybrid retriever
-> web search
-> concurrent safe URL readers
-> SQLite memory lookup
-> evidence deduplication and budget gate
-> grounded answer draft
-> claim-level citation verifier
-> unsupported claim? one bounded gap-query retrieval -> redraft
-> Markdown report + run metrics + trace
-> SQLite memory
完整模块职责和信任边界见 docs/ARCHITECTURE.md。
要求 Python 3.9+。默认离线模式不需要 API key,也不需要安装第三方依赖。
以下命令在 research-agent/ 目录执行:
PYTHONPATH=src python3 -m research_agent.main ingest
PYTHONPATH=src python3 -m research_agent.main report \
"What is tool calling?" \
--plan \
--max-steps 2 \
--max-web-pages 0 \
--show-trace也可以安装为本地 CLI:
python3 -m pip install -e .
local-research-agent ingest
local-research-agent report "What is tool calling?" --plan --max-steps 2PYTHONPATH=src python3 -m research_agent.main ingest命令读取 data/documents/ 下的 .md 和 .txt,切分 chunks,并把文本、deterministic hashed vectors 以及 provider/model 元数据写入 data/indexes/local_index.json。
真实 embedding 在本机运行,不需要外部 embedding API。首次安装和建索引会下载模型;查询阶段会根据索引元数据自动加载同一个 provider/model。
python3 -m pip install -e '.[embeddings]'
PYTHONPATH=src python3 -m research_agent.main ingest \
--embedding-provider sentence-transformers \
--embedding-model intfloat/multilingual-e5-small当前真实 provider 只支持 multilingual-e5-small,因为它的 query/passage prompt 和相似度分布已经明确校准;启用其他模型前需要先补齐对应 prompt 与阈值。该模型按 asymmetric retrieval 约定分别使用 query: 和 passage: 前缀。BM25 命中的 chunk 与一个受限 dense candidate pool 共同进入融合排序;切换 provider 或 model 后必须重新执行 ingest,避免查询向量与索引向量不一致。
PYTHONPATH=src python3 -m research_agent.main ask "Who executes the actual tool function?"PYTHONPATH=src python3 -m research_agent.main report \
"How does tool calling work?" \
--plan \
--max-steps 3 \
--max-evidence 12 \
--max-web-pages 0 \
--show-trace \
--output demo/tool_calling_trace_report.md关键预算参数:
--max-steps:最多执行多少个计划步骤。--max-web-pages:整次报告最多读取多少个网页;设为0可强制离线。--max-workers:网页读取的最大并发数。--max-evidence:去重后最多保留多少条证据。--max-citation-retries:引用审计失败后的补检索次数,只允许0或1,默认1。
复制 .env.example 为本地 .env 后配置 BochaAI:
BOCHA_SEARCH_API_KEY=replace-with-your-bochaai-search-key
BOCHA_SEARCH_ENDPOINT=https://api.bochaai.com/v1/web-search
联网报告示例:
PYTHONPATH=src python3 -m research_agent.main \
--env-file .env \
report "What is tool calling?" \
--plan \
--search-web \
--search-count 3 \
--max-web-pages 4 \
--max-workers 4 \
--show-trace也可重复传入显式公开 URL:
PYTHONPATH=src python3 -m research_agent.main report \
"What is grounded generation?" \
--url https://example.com/article-a \
--url https://example.com/article-b \
--max-web-pages 2URL reader 会在请求前和 HTTP 重定向时检查目标地址。它适合读取公开文本网页,不用于登录页、内部系统、浏览器渲染页面或二进制文件。
默认使用 ExtractiveAnswerClient,整个流程可离线复现。设置 MiMo 配置并增加 --use-mimo 后,模型负责基于已收集 evidence 起草回答;宿主仍会执行 citation guard 和 claim-level audit。
MIMO_API_KEY=replace-with-your-api-key
MIMO_BASE_URL=https://api.xiaomimimo.com/v1
MIMO_MODEL=mimo-v2-flash
MIMO_CHAT_PATH=/chat/completions
PYTHONPATH=src python3 -m research_agent.main \
--env-file .env \
report "What is RAG?" \
--plan \
--use-mimo模型生成不是信任边界。编号不存在、无有效引用或与引用证据缺少最小词项支持的声明不会直接进入最终报告。
默认 backend 是 SQLite:
data/memory/memory.sqlite
切换 JSON backend:
PYTHONPATH=src python3 -m research_agent.main \
--memory-backend json \
report "What is RAG?"当前 memory 只保存简短任务历史并做关键词匹配,不保存完整网页正文,也不把历史记忆当作可引用事实来源。
PYTHONPATH=src python3 -m unittest discover -s tests -v
PYTHONPATH=src python3 -m research_agent.main ingest
PYTHONPATH=src python3 -m research_agent.main eval当前本地结果:
Ran 71 tests
OK
Eval passed 20/20 cases.
Accuracy: 1.00
Recall@3: 1.00
MRR: 1.00
Negative rejection: 1.00
冻结 eval 包含 16 条正向检索和 4 条域外问题拒检。该结果只覆盖仓库内 4 份小型文档,不代表开放域检索质量。评测定义和指标边界见 docs/EVALUATION.md。
仓库包含 Python 3.9 / 3.12 的 GitHub Actions workflow;只有远端 workflow 实际运行成功后,才能将其表述为 hosted CI 通过。
research-agent/
src/research_agent/
agent.py # 受限研究编排、预算和运行指标
planner.py # deterministic 子查询计划
embeddings.py # hashed / sentence-transformers provider
retriever.py # BM25 + embedding + quality + diversity
citation_audit.py # claim-level 引用覆盖与支持度审计
tools.py # 本地检索、并发 URL、搜索和 memory 工具
url_reader.py # HTML 抽取、SSRF/重定向/体积边界
report.py # grounded answer、审计、报告和 trace
memory.py # SQLite / JSON memory
eval_runner.py # accuracy、Recall@k、MRR、negative rejection
data/
documents/ # 本地知识库
eval/ # 冻结评测集
docs/ # 架构、demo、评测与技术边界
demo/ # 可复现离线报告
tests/ # 单元与集成测试
- 默认 hashed vector 只用于 deterministic rerank,BM25 lexical match 仍是它的候选硬门;只有真实 semantic provider 能加入无 lexical overlap 的受限 dense candidates。
multilingual-e5-small的本地 smoke 已验证跨语言 Top-1 召回,但尚未在扩大后的数据集上完成 baseline/hybrid 消融,不能宣称普遍提升。- Claim-level audit 是词项支持度检查,不等同于自然语言推理或严格 entailment verifier。
- Citation repair 最多执行一次,gap query 由原问题和 unsupported claim 组成;它是受限反馈闭环,不是开放式自主规划。
- 网页抽取基于 HTML parser,不执行 JavaScript,也不覆盖 PDF、复杂 SPA、付费墙和登录态页面。
- Planner 是 deterministic bounded planner;模型可选地负责 grounded draft,但不会自主扩大工具权限或研究预算。
- SQLite memory 是关键词历史,不是向量记忆或事实数据库。
- 当前 eval 是仓库内小型冻结集,适合回归验证,不可外推到开放域 benchmark。