Skip to content

【No.10】DeepEyes-V2 真实网页搜索工具 - #346

Open
Szy5 wants to merge 5 commits into
redai-studio:mainfrom
Szy5:feat/no-10-deepeyes-v2-real-web-search
Open

Szy5 wants to merge 5 commits into
redai-studio:mainfrom
Szy5:feat/no-10-deepeyes-v2-real-web-search

Conversation

@Szy5

@Szy5 Szy5 commented Sep 20, 2026

Copy link
Copy Markdown

⭐ Feature DeepEyes-V2 真实网页搜索工具

本 PR 完成任务 【No.10】DeepEyes-V2 真实网页搜索工具,将 examples/deepeyes_v2_agentic/app/search_utils.py 中原先固定返回占位内容的 search() 改为可插拔搜索后端。

主要改动:

  • 新增可配置的文本搜索 backend 机制,通过 DEEPEYES_V2_SEARCH_BACKEND 选择后端。
  • 支持 3 种搜索后端:
    • mock:默认后端,确定性离线 mock,无需检索服务、网络或密钥。
    • retriever:兼容 Search-R1 POST /retrieve 协议的 HTTP retriever。
    • brave:接入 Brave Search API,作为外部搜索 API 后端示例。
  • 统一不同后端的返回结构:
    • 顶层包含 elapsed_timedata
    • data 中每条结果包含 titlelinksnippetdate
    • date 允许为空。
  • 保持现有环境的失败处理约定:
    • 超时、服务异常、非法 JSON、缺少必要配置、非预期异常等情况均返回 "Error"
    • 后端失败不会导致 agent 进程崩溃。
  • 为 HTTP backend 增加可配置的超时、重试次数和 retry budget。
  • 更新 examples/deepeyes_v2_agentic/env.sh.example,补充搜索后端相关环境变量示例。
  • 新增单元测试覆盖 3 个后端的适配逻辑和异常处理路径。

✅ Tests

覆盖搜索后端和异常路径

新增测试文件:

  • tests/examples/deepeyes_v2_agentic/test_search_utils.py

覆盖内容:

  • 默认 mock 后端可离线、确定性运行。
  • 未知 backend 会 fallback 到 mock
  • retriever 后端可适配 Search-R1 兼容服务返回。
  • retriever 缺少 URL 配置时返回 "Error"
  • brave 后端可适配 Brave Search API 返回。
  • brave 缺少 API key 时返回 "Error"
  • brave 支持自定义 endpoint。
  • HTTP timeout 返回 "Error"
  • 非法 JSON 返回 "Error"
  • 服务端 503 错误返回 "Error"
  • 配置真实服务 backend 时不再返回原先的固定 placeholder 文本。

What

本 PR 将 DeepEyes-V2 示例中的 search() 从固定 placeholder 实现改为可插拔后端实现。

修改文件:

  • examples/deepeyes_v2_agentic/app/search_utils.py
  • examples/deepeyes_v2_agentic/env.sh.example
  • tests/examples/deepeyes_v2_agentic/test_search_utils.py

具体实现包括:

  • 新增 SearchBackend 协议,用于抽象不同搜索后端。
  • 新增 MockBackend,作为默认离线后端。
  • 新增 RetrieverBackend,兼容 Search-R1 风格的 POST /retrieve 服务。
  • 新增 BraveBackend,通过 Brave Search API 接入外部网页搜索。
  • 新增 _request_json(),统一处理 HTTP 请求、timeout、重试和 5xx transient error。
  • 新增 _normalize(),将不同后端返回结果统一转换为 DeepEyes-V2 使用的搜索结果结构。
  • 新增 _reset_backend_cache(),方便测试时清理 backend cache。
  • env.sh.example 中补充搜索后端相关配置示例。

统一返回结构如下:

{
    "elapsed_time": float,
    "data": [
        {
            "title": str,
            "link": str,
            "snippet": str | None,
            "date": str | None,
        }
    ],
}

# ⭐ Feature

## Add configurable text search backends

- Add mock, Search-R1 retriever, and Brave search backend support for DeepEyesV2 agentic search.
- Normalize backend responses into the existing search result shape and degrade failures to "Error".
- Document backend environment variables in the example env file.

---

# ✅ Tests

## Cover backend behavior

- Add unit tests for mock, retriever, Brave, custom endpoint, and error handling paths.
@rai-studio-bot

rai-studio-bot commented Sep 20, 2026

Copy link
Copy Markdown

Nyanpasu 审查看板

审查状态: ✅ 已通过

审查版本: 7549a0e817a8e77ed08ce3da1705fb5f10a1dcd3

三项已有问题均已修复,审查通过。26 项示例测试、启动脚本语法及实际配置构造和密钥日志保护验证通过。GitHub 无检查结果;未进行真实搜索服务或 Ray/GPU 训练集成验证。

编号 问题 优先级 状态 规则来源
F1 向 Ray worker 传递新增搜索配置 P1 ✅ 已解决
F2 预算耗尽后停止重试并约束请求超时 P2 ✅ 已解决
F3 避免 shell tracing 输出 Brave API 密钥 P2 ✅ 已解决
Powered by Nyanpasu with gpt-6-astra medium, please check the suggestions carefully.

@rai-studio-bot rai-studio-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要修改:已有 Ray 集群的启动路径尚未接通真实搜索配置;另有重试预算失效问题,详见行级意见。

已检查全部 3 个改动文件、调用与启动路径;示例测试 22 项通过。GitHub 当前没有检查结果。未调用真实 Brave/retriever 服务,也未运行需要集群和 GPU 的训练集成验证。

Powered by Nyanpasu with gpt-6-astra medium, please check the suggestions carefully.

Comment on lines +68 to +70
# --- retriever: a Search-R1 compatible POST /retrieve service --------------
# export DEEPEYES_V2_SEARCH_BACKEND=retriever
# export DEEPEYES_V2_SEARCH_RETRIEVER_URL=http://your-retriever:17389/retrieve

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

请把新增搜索配置同步传入两个训练脚本的 Ray runtime env。使用已有集群的入口(例如 bash scripts/entrypoint/ray-job.sh examples/deepeyes_v2_agentic/run_deepeyes_v2_agentic.sh)时,这里的 export 只作用于提交进程;两个脚本的 env_vars 目前只传递旧的 DEEPEYES_V2_SEARCH_CACHE_PATHS,没有 backend、retriever URL、Brave key 或 HTTP 配置。run_agent_app.sh 也不读取 env.sh,agent 继承的是 worker 环境。因此按此示例选择 retriever/brave 后,未预先配置这些变量的集群仍会静默使用 mock,真实检索不会发生。请显式转发新增变量,并补充启动配置传递测试。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 9498bb1 修复:两个启动脚本现已转发全部新增搜索变量。实际执行两套 JSON 构造片段,配置/未配置共四种情况均通过,原先已有集群缺少搜索配置的问题已解决。未运行真实 Ray 集群验证。

Comment on lines +138 to +141
remaining = deadline - time.monotonic()
if attempts >= max(1, max_retries) or remaining <= 0:
break
time.sleep(min(_INITIAL_RETRY_DELAY * attempts, 5.0, max(0.01, remaining)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 优先级:P2

重试预算耗尽后仍会发起下一次请求:这里只在 sleep 前检查 remaining,循环入口没有再次检查 deadline,而且每次都使用完整 timeout。用可控时钟复现:retry_budget=1、timeout=30,第一次请求在 0.95 秒超时,sleep 到 1 秒后仍发起 timeout=30 的第二次请求,总耗时达到 31 秒。调用方直接 await 搜索线程,因此配置的短预算无法及时结束失败搜索。请在每次重试前检查剩余预算,并让请求超时和退避受剩余预算约束;补充预算耗尽时不再请求的测试。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 9498bb1 修复:每次请求前检查剩余预算,timeout 和退避均按剩余预算截断。新增可控时钟回归测试通过,原先预算耗尽后再次请求的场景不再发生。

# 🐛 Bug Fix

## Propagate search backend config

- Forward DeepEyes-V2 search backend, retriever, Brave, and HTTP retry env vars through the Ray runtime env in both launch scripts.
- Keep agent subprocesses on existing Ray clusters from silently falling back to the default mock backend when real search is configured.

## Enforce retry budget before each request

- Check remaining retry budget before every HTTP attempt.
- Bound per-request timeout and retry sleep by the remaining budget so exhausted budgets do not trigger another long request.

---

# ✅ Tests

## Cover review regressions

- Add launch-script coverage for search env propagation.
- Add a controlled-clock retry-budget test proving exhausted budget prevents the next request.

@rai-studio-bot rai-studio-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

原有配置传递和重试预算问题已修复;本轮无阻塞问题,新增一项 Brave 密钥被 shell tracing 输出的 P2 意见,详见行级评论。

示例测试 24 项通过,两个启动脚本的语法和实际 JSON 构造验证通过。GitHub 当前无检查结果;未调用真实搜索服务,也未运行需要 Ray 集群和 GPU 的训练集成验证。

Powered by Nyanpasu with gpt-6-astra medium, please check the suggestions carefully.

"DEEPEYES_V2_SEARCH_BACKEND": "${DEEPEYES_V2_SEARCH_BACKEND:-}",
"DEEPEYES_V2_SEARCH_RETRIEVER_URL": "${DEEPEYES_V2_SEARCH_RETRIEVER_URL:-}",
"DEEPEYES_V2_SEARCH_TOPK": "${DEEPEYES_V2_SEARCH_TOPK:-}",
"DEEPEYES_V2_SEARCH_BRAVE_API_KEY": "${DEEPEYES_V2_SEARCH_BRAVE_API_KEY:-}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 优先级:P2

新增的 Brave API key 会被 shell tracing 明文输出。普通脚本启用了 set -ex,因此这里构造 RUNTIME_ENV_JSON 的赋值就会把完整 key 写到 stderr;KLX 脚本新增的同类赋值也有此问题,其提交前的 set +x 已经太晚。使用公开测试占位值执行两个实际构造片段均可复现,即使 key 从外部环境传入、没有 env.sh 也会泄露。请在读取含密钥配置、构造 runtime JSON 和提交命令期间关闭 xtrace,并补充 stderr 不含测试密钥的验证。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已根据 review 意见修复并推送 commit 9498bb1

修改内容:

  1. 已将新增搜索配置同步传入两个 DeepEyes-V2 训练脚本的 Ray runtime env:

    • run_deepeyes_v2_agentic.sh
    • run_deepeyes_v2_agentic_klx.sh

    现在会显式转发 DEEPEYES_V2_SEARCH_BACKEND、retriever URL、top-k、Brave API key/endpoint,以及 timeout/retry/trust_env 等 HTTP 配置,避免已有 Ray 集群路径下 agent worker 静默回退到 mock backend。

  2. 已修复 _request_json() 的 retry budget 问题:

    • 每次请求前检查剩余 budget;
    • 单次 request timeout 受剩余 budget 约束;
    • retry sleep 也受剩余 budget 约束;
    • budget 耗尽后不会再发起下一次请求。
  3. 已补充回归测试:

    • 新增启动脚本 runtime env 传递测试;
    • 新增可控时钟测试,验证 retry budget 耗尽后不会继续请求。

本地验证:

  • pytest tests/examples/deepeyes_v2_agentic:24 passed
  • ruff check examples/deepeyes_v2_agentic/app/search_utils.py tests/examples/deepeyes_v2_agentic/test_search_utils.py tests/examples/deepeyes_v2_agentic/test_runtime_env.py:passed
  • ruff format --check examples/deepeyes_v2_agentic/app/search_utils.py tests/examples/deepeyes_v2_agentic/test_search_utils.py tests/examples/deepeyes_v2_agentic/test_runtime_env.py:passed
  • SKIP=docformatter uvx pre-commit run --all-files --show-diff-on-failure:除本地 Python 3.13 不兼容的 docformatter 外,其余 hooks passed

未运行真实 Brave/retriever 服务验证,也未运行需要 Ray 集群和 GPU 的训练集成验证。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你列出的配置传递和 retry budget 修复已在上一轮复查中确认,F1/F2 已标记为已解决,24 项测试也已通过。

本线程讨论的是随后发现的 F3:在同一个 9498bb1 中,Brave key 被加入 runtime JSON 时,两个脚本仍开启着 xtrace,赋值会把密钥输出到 stderr;KLX 提交前才关闭 tracing,无法保护前面的赋值。因此本项 P2 尚未修复。请保护读取密钥配置、构造 JSON 和提交这几个阶段的 tracing,并验证 stderr 不包含测试密钥。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修复 F3 并推送 commit 7549a0e

本次修改:

  1. 在两个 DeepEyes-V2 启动脚本中保护 env.sh 读取阶段:

    • source env.sh 前临时关闭 xtrace;
    • source 后按原状态恢复;
    • 避免本地 env.sh 中的 DEEPEYES_V2_SEARCH_BRAVE_API_KEYset -x 打到 stderr。
  2. 保护 runtime env JSON 构造阶段:

    • run_deepeyes_v2_agentic.sh 中构造 RUNTIME_ENV_JSON 时关闭 xtrace;
    • run_deepeyes_v2_agentic_klx.sh 中构造 EXTRA_ENV_VARS_JSON 并 source runtime-env-klx.sh 时关闭 xtrace;
    • 避免 Brave key 在 shell 赋值展开时泄漏。
  3. 保护 Ray job submit 阶段:

    • 普通脚本现在也会在 ray job submit --runtime-env-json ... 阶段关闭 xtrace;
    • KLX 脚本保留提交阶段关闭 xtrace 的行为,并改为按原 xtrace 状态恢复。

新增回归测试:

  • tests/examples/deepeyes_v2_agentic/test_runtime_env.py 中新增 shell 级测试;
  • 测试会创建包含假 Brave key 的临时 env.sh
  • 使用 fake ray 命令让两个启动脚本在 bash -x 下执行到 submit 路径;
  • 断言测试密钥不会出现在 stdout 或 stderr。

本地验证:

  • pytest tests/examples/deepeyes_v2_agentic/test_runtime_env.py -q:3 passed
  • pytest tests/examples/deepeyes_v2_agentic:26 passed
  • bash -n examples/deepeyes_v2_agentic/run_deepeyes_v2_agentic.sh:passed
  • bash -n examples/deepeyes_v2_agentic/run_deepeyes_v2_agentic_klx.sh:passed
  • ruff check tests/examples/deepeyes_v2_agentic/test_runtime_env.py:passed
  • SKIP=docformatter uvx pre-commit run --all-files --show-diff-on-failure:除本地 Python 3.13 不兼容的 docformatter 外,其余 hooks passed,包括 gitleaks

未运行真实 Brave/retriever 服务验证,也未运行需要 Ray 集群和 GPU 的训练集成验证。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 7549a0e 修复:两个启动脚本在读取 env.sh、构造 runtime JSON 和提交期间关闭 xtrace。使用公开测试密钥验证两个脚本的配置构造,并执行未替换的 KLX runtime-env helper,密钥正确传入 JSON 且未出现在 stdout/stderr。新增回归测试通过,本项已解决。

# 🐛 Bug Fix

## Protect search credentials in launch scripts

- Disable xtrace while sourcing DeepEyes-V2 env.sh so local Brave credentials are not printed.
- Disable xtrace while constructing Ray runtime env JSON in both standard and KLX launch scripts.
- Keep xtrace disabled across job submission because --runtime-env-json carries secret-bearing values.

---

# ✅ Tests

## Verify secrets stay out of trace output

- Add a shell-level regression test that runs both launch scripts with a fake Brave key and fake ray executable.
- Assert the test key does not appear in captured stdout or stderr while the scripts reach the submit path.

@rai-studio-bot rai-studio-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查通过。此前三项问题均已修复,本轮未发现新的可操作问题。

26 项示例测试通过,两个启动脚本语法检查及实际配置构造、密钥日志保护验证通过。GitHub 当前无检查结果;未调用真实 Brave/retriever 服务,也未运行需要 Ray 集群和 GPU 的训练集成验证。

Powered by Nyanpasu with gpt-6-astra medium, please check the suggestions carefully.

@Szy5

Szy5 commented Sep 20, 2026

Copy link
Copy Markdown
Author

@SigureMo 您好,Task 10完成啦,辛苦您如果有时间可以Review一下~

@SigureMo

Copy link
Copy Markdown
Member

先确保 CI 通过吧

# 🎨 Style

## Apply CI docformatter output

- Wrap DeepEyesV2 search helper docstrings using the formatting produced by the CI docformatter hook.
- Keep the change limited to docstring formatting; no runtime behavior changes.

---

# ✅ Tests

## Verify formatting-only change

- Run DeepEyesV2 example tests.
- Run ruff check and ruff format checks on the touched Python files.
- Run prek with docformatter skipped locally due Python 3.13 lib2to3 incompatibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants