Skip to content

feat(search): prefer Tavily with Exa task routing - #700

Closed
hbui290 wants to merge 6 commits into
Panniantong:mainfrom
hbui290:feat/tavily-primary-search
Closed

hbui290 wants to merge 6 commits into
Panniantong:mainfrom
hbui290:feat/tavily-primary-search

Conversation

@hbui290

@hbui290 hbui290 commented Sep 18, 2026

Copy link
Copy Markdown

Summary

  • Make Tavily the default web-search backend while keeping Exa as a fallback.
  • Route paper, academic, company, people, financial-report, semantic, RAG, and similar-page tasks to Exa.
  • Normalize multi-word and category task labels and expose task-aware backend ordering/checks without adding a search wrapper.
  • Align Exa/Tavily setup docs, skill guidance, mcporter arguments, and runtime skill copies.
  • Remove the stale Exa API-key requirement and reject positional Tavily keys.
  • Treat web and MCP results as untrusted data in the skill guidance.

Verification

  • python -m pytest -q — 614 passed, 17 subtests.
  • ruff check agent_reach tests — passed.
  • Python compile check — passed.
  • uv build --wheel --offline — passed.
  • Live Tavily search — HTTP 200 with one result.
  • Live Exa MCP search via mcporter 0.12.3 — returned an arXiv result.

Agent Reach remains an installer/doctor/configuration glue layer; agents continue to call upstream Tavily and Exa tools directly.

Copilot AI lite review requested due to automatic review settings September 18, 2026 07:03

Copilot AI 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.

🟡 Changes recommended

Unresolved moderate findings affect Exa routing, quota fallback, and Tavily secret handling.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR makes Tavily the preferred search backend while routing specialized tasks to Exa and updating configuration, CLI safeguards, documentation, skills, and tests.

Changes:

  • Adds task-aware Tavily/Exa routing and health checks.
  • Adds Tavily configuration and removes Exa key requirements.
  • Updates setup guides, search guidance, mcporter wording, and safety instructions.
File summaries
File Summary Findings
tests/test_search_backends.py Tests backend routing and health checks. None.
tests/test_p0_cli.py Tests positional Tavily-key rejection. None.
tests/test_config.py Updates Exa configuration expectations. None.
README.md Documents Tavily-first search. Nit, 1 vote: Localized README copies retain stale Exa backend and credential guidance.
docs/install.md Updates installation and command guidance. Nit, 1 vote: --stdin is described as hidden despite echoing interactive terminal input.
agent_reach/skill/SKILL.md Adds routing and safety guidance. None.
agent_reach/skill/SKILL_en.md Adds English routing and safety guidance. None.
agent_reach/skill/references/search.md Documents backend-specific workflows. Nit, 1 vote: Clarify specialized-task precedence over active_backend.
Nit, 1 vote: Clarify that --stdin is for piped or automated input.
agent_reach/guides/setup-tavily.md Adds Tavily setup instructions. Nit, 1 vote: Use the hidden interactive prompt and reserve --stdin for automation.
agent_reach/guides/setup-exa.md Reframes Exa as fallback. Nit, 3 votes: Remove the contradictory claim that Exa is free and requires no API key.
agent_reach/config.py Adds Tavily configuration and external Exa handling. None.
agent_reach/cli.py Adds Tavily configuration and secret protections. Moderate, 1 vote: Reject positional Tavily keys before the --stdin early return.
agent_reach/channels/mcporter.py Generalizes mcporter compatibility wording. Nit, 1 vote: Fix the ungrammatical version-compatibility sentence.
agent_reach/channels/exa_search.py Implements Tavily/Exa routing and health checks. Moderate, 2 votes: Treat exhausted numeric quotas as unavailable.
Moderate, 1 vote: Honor EXA_SEARCH_BACKEND without a config object.
Moderate, 1 vote: Reject whitespace-only backend overrides.
Review details

Suppressed comments (9)

README.md:111

  • The routing update is only reflected in the Chinese README: docs/README_en.md:114, docs/README_ko.md:72, and docs/README_ja.md:72 still describe Exa as the auto-configured, no-key web-search backend. Users following those supported-language docs will receive stale backend and credential guidance; update the localized copies as part of this documentation change.
| 🔍 **全网搜索** | — | Tavily 研究搜索;Exa 语义搜索备选 | Tavily Key 可选;无 Key 自动走 Exa |

agent_reach/channels/exa_search.py:75

  • The documented EXA_SEARCH_BACKEND=exa override is ignored whenever this channel is used without a config object: the early return prevents _configured_backend from reading the environment, so backend_for_task("general"), ordered_backends(), and check() still prefer Tavily. Read the environment after checking config values.
        if not config:
            return None
        override = None
        for key in ("search_backend", "web_search_backend", f"{self.name}_backend"):
            override = config.get(key)
            if override:
                break
        if not override:

agent_reach/channels/exa_search.py:84

  • After stripping, a whitespace-only override becomes an empty target; because every backend starts with an empty string, it is interpreted as Tavily instead of ignored. This can suppress the Exa route for tasks such as paper, contrary to the unknown-override fallback behavior; reject an empty normalized target before prefix matching.
        target = aliases.get(str(override).strip().casefold(), str(override).strip())
        for backend in self.backends:
            if backend.casefold() == target.casefold() or backend.casefold().startswith(

agent_reach/channels/mcporter.py:38

  • The updated sentence reads “Otherwise mcporter supported mcporter versions...”, which is ungrammatical and makes the version qualifier unclear.
    supported mcporter versions load the first home config

agent_reach/cli.py:1409

  • The new rejection is below the --stdin early return, so agent-reach configure tavily-key tvly-secret --stdin still accepts a positional secret and leaves it exposed in the process arguments. Apply the Tavily positional-value guard before entering the stdin branch as well.
            if getattr(args, "key", None) == "tavily-key":
                print(
                    "Refusing a positional Tavily key because shell history and "
                    "process listings may expose it; omit the value for a hidden "
                    "prompt or use --stdin.",
                    file=sys.stderr,
                )
                raise SystemExit(2)

agent_reach/guides/setup-tavily.md:9

  • This setup command is shown as the normal interactive configuration path, but --stdin reads from the terminal without suppressing echo. Use the hidden prompt here, and document --stdin only for piped automation so the API key is not displayed while typing.
agent-reach configure tavily-key --stdin

agent_reach/skill/references/search.md:5

  • This opening instruction says to follow active_backend for the search, which overrides the task-routing table below: with a valid Tavily key, a paper query will see Tavily via REST and be sent to Tavily even though paper/academic tasks are supposed to proactively use Exa. Clarify that active_backend governs ordinary-search fallback and that specialized task labels take precedence.
网页搜索默认走 **Tavily**,Exa 作为 MCP 备选。先运行
`agent-reach doctor --json`;当 `exa_search.active_backend` 为
`Tavily via REST` 时用 Tavily,否则回退到 Exa。

agent_reach/skill/references/search.md:53

  • This reference also presents --stdin as hidden input, but the CLI only avoids argv/process-list exposure; terminal input is still echoed unless the value is piped. Use the hidden prompt for interactive setup and reserve this form for non-interactive stdin.
# 保存给 doctor 使用(隐藏输入,不要把 key 放进命令参数)
agent-reach configure tavily-key --stdin

docs/install.md:87

  • --stdin is not hidden when run interactively: _read_configure_value uses sys.stdin.read() without disabling terminal echo. Describing this as hidden input can lead users to paste a key into an echoing terminal; document the no-argument hidden prompt for interactive use and reserve --stdin for piped/automation input.
Tavily is optional and needs an API key. Configure it through hidden input with
`agent-reach configure tavily-key --stdin`; without a key, Exa remains the active
zero-config fallback.
  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +171 to +181
if response.status_code == 200:
try:
usage = response.json().get("key", {})
except (TypeError, ValueError):
usage = {}
used = usage.get("usage")
limit = usage.get("limit")
suffix = f"(已用 {used}/{limit} credits)" if isinstance(
used, (int, float)
) and isinstance(limit, (int, float)) else ""
return "ok", f"Tavily API 可用{suffix}"
Comment thread agent_reach/guides/setup-exa.md Outdated
Comment on lines 4 to 8
Agent Reach 自己不要求 `exa_api_key`;MCP 服务端是否需要认证由当前 Exa/mcporter 配置决定。
需要配置 Tavily 时请先阅读 `guides/setup-tavily.md`。

## 功能说明
Exa 是一个 AI 语义搜索引擎。通过 MCP 接入,**免费、无需 API Key**。配置后解锁:
@hbui290 hbui290 closed this Sep 21, 2026
@hbui290
hbui290 deleted the feat/tavily-primary-search branch September 21, 2026 03:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants