Guides
Web search
Add Tavily, Brave, or Amazon Bedrock AgentCore web search to your agent loop with automatic compression.
Use WebSearchTool to add web search to your agent loop with automatic compression. It returns a real LangChain BaseTool. Pass it to any of the agent client interfaces and CompresrToolMiddleware compresses search results before they re-enter the model context. No extra wiring.
Three backends ship out of the box:
| Provider | Strengths | Bring your own key |
|---|---|---|
| Tavily (default) | LLM-tuned results, strong on recency and citations, allowed_domains / blocked_domains filtering, max_results forwarded verbatim | TAVILY_API_KEY |
| Brave | Generalist web index, independent crawler, native domain filtering uses Goggles (out of scope for v1) | BRAVE_SEARCH_API_KEY (fallback: BRAVE_API_KEY) |
| AgentCore | Amazon Bedrock web search over MCP + Cognito OAuth, max_results clamped 1..25 | Cognito client credentials (see AgentCore below) |
Why not Anthropic / OpenAI / Gemini server search?
Provider-native server search tools (Anthropic's web_search_20250305, OpenAI's web_search_preview, Gemini's google_search) run server-side and return opaque or encrypted content that Compresr cannot read or compress. The model sees the search content but the SDK doesn't, so there is nothing to intercept.
WebSearchTool calls the search API client-side instead. Plaintext results reach the SDK first, pass through CompresrToolMiddleware, and arrive at the model compressed. This is the only path where Compresr's compression actually fires for web search results.
Quick start: Tavily
The model decides when to call tavily_search (or brave_search / agentcore_web_search). The SDK calls the underlying API, and the middleware compresses the result before it re-enters the model context. Mechanics: How compression interacts below.
Construction options
All three backends take a small, deliberately narrow option set. Anything provider-specific (e.g. Tavily search_depth) goes through the extra / **extra escape hatch.
Tavily
Brave
No native domain filtering on Brave
Brave doesn't expose native allowed_domains / blocked_domains. Its filtering flows through Goggles, which is out of scope for v1. Python emits a UserWarning and proceeds; TypeScript silently ignores the kwargs. Use Tavily if you need real domain filtering.
TypeScript Brave + Claude tool_use
WebSearchTool.brave in TypeScript returns the upstream BraveSearch directly, which ships without an args_schema. Anthropic (Claude) tool_use calls can then fail with missing 1 required positional argument: 'query'. Workaround: wrap it in your own tool() factory that declares a query: z.string() schema, or use Tavily or AgentCore. Python wraps Brave with an explicit schema, so this is a TypeScript-only pitfall.
AgentCore
AgentCore is Amazon Bedrock web search over an MCP streamable-HTTP session, authenticated with a Cognito OAuth client-credentials handshake. Runtime deps are optional. Install the extra: pip install compresr[agentcore] (Python) or npm install @modelcontextprotocol/sdk (TypeScript).
Each config field resolves from the explicit argument first, then two env vars in order:
| Field | Primary env var | Fallback env var |
|---|---|---|
gateway_url / gatewayUrl | AGENTCORE_GATEWAY_MCP_URL | GATEWAY_MCP_URL |
cognito_token_url / cognitoTokenUrl | AGENTCORE_COGNITO_TOKEN_URL | COGNITO_TOKEN_URL |
client_id / clientId | AGENTCORE_COGNITO_CLIENT_ID | COGNITO_CLIENT_ID |
client_secret / clientSecret | AGENTCORE_COGNITO_CLIENT_SECRET | COGNITO_CLIENT_SECRET |
scope | AGENTCORE_COGNITO_SCOPE | COGNITO_SCOPE |
Runtime behaviour:
- HTTPS-only URLs (TypeScript).
gatewayUrlandcognitoTokenUrlmust start withhttps://. A plaintext URL throwsCompresrError('invalid_config')before any credential leaves the process. max_resultsclamp. TypeScript enforces1..25at build time. Python passes the value through to the underlying client, which applies the same bound.- Bearer-token cache. The Cognito token is minted once and cached on the shared client. A
401from the gateway triggers exactly one automatic re-mint before the call is retried. - Timeouts and response cap. Cognito requests time out at 30s, tool calls at 30s, and responses are hard-capped at 1 MB.
Constructor form
Use the classmethod factories above when you can. The constructor form is equivalent and useful when the provider is a runtime variable:
How compression interacts with web search
CompresrToolMiddleware runs on every tool return inside the agent loop:
- The model emits a tool call (e.g.
tavily_search({ query: "..." })). - The SDK invokes the provider and normalises the response via
_flatten_search_results/flattenSearchResults. The raw JSON becomes blank-line-separated plain-text blocks oftitle\nurl\ncontent. This is the shapelatte_v1can actually compress; JSON input is a no-op for compression. - The middleware checks the serialized string length. At or below
compression.min_tokens(default200), it's forwarded untouched. Above that, the middleware callsclient.compress(...)with the result body ascontextand the user's last message asquery. - The compressed body replaces the original in the agent state. The model never sees the uncompressed search results.
Two consequences:
- Tune
compression.min_tokensto your search backend. Tavily returns long content per hit and easily exceeds200tokens. Brave returns shorter snippets, so you may want to lowermin_tokensto e.g.100to catch them. - Compression uses the user's intent as the
query, not the model's tool-call query string. So compression stays aimed at what the user actually asked, even when the model rewords the search.
Errors & failure modes
| Scenario | Behaviour | Mitigation |
|---|---|---|
Missing peer dep (langchain-tavily / langchain-community / @modelcontextprotocol/sdk) | Python raises ImportError naming the extra. TypeScript raises CompresrError code missing_peer_dependency. | Install the extra: pip install compresr[agents-tavily\</td> <td>agents-brave\</td> <td>agentcore] or npm install @langchain/tavily @langchain/community @modelcontextprotocol/sdk. |
| Missing API key or config | Python raises ValueError naming the arg and env-var fallback. TypeScript raises CompresrError with code missing_api_key (Brave), missing_config (AgentCore), or invalid_config (AgentCore non-HTTPS URL), plus invalid_provider for the constructor form. | Pass the arg or set the env var. |
| Search-provider 401 / 403 / rate limit at call time | Python: the provider's exception propagates inside the agent loop. TypeScript: raised as CompresrError — for AgentCore specifically, codes agentcore_auth_error, agentcore_no_tool, agentcore_bad_response, agentcore_tool_error. | Rotate the key or back off / lower max_results. |
| Compresr backend down while the middleware fires | Returns the original (uncompressed) search result by default (on_error="passthrough" on the policy). | Set compression={"on_error": "raise"} if you want to fail loudly instead. |
| No tools fired (model answered without searching) | No compression call. The middleware only runs on tool returns. | Encourage the model to search via the user prompt or a system instruction. |
Research facade: client.research
Use client.research when you want web search, snippet compression, and citation extraction in one call, instead of wiring WebSearchTool into an agent loop by hand. It requires a client constructed with llm=; accessing it otherwise raises CompresrError. client.research.run(question) runs the full search-and-synthesize loop, up to max_steps. client.research.search(question) is the same loop capped at 2 steps for quick lookups.
Options (Python names; TypeScript uses camelCase):
| Option | Default | Effect |
|---|---|---|
search | "tavily" | "tavily" or "brave" (env-var key fallbacks apply), or a preconstructed WebSearchTool, which is the only way to use AgentCore here. |
max_steps | 10 | Upper bound on search / synthesize iterations. .search() overrides this to 2. |
model | client llm | Override the client-level model for this call. |
compress_snippets | true | Route each search snippet through the compression API before it enters the LLM context. |
compression_model | "latte_v1" | Which model compresses the snippets. |
min_compress_tokens | 100 | Skip compression for snippets shorter than this. |
max_context_tokens | 120,000 | Hard ceiling on total tokens across all compressed snippets before synthesis. |
system_prompt | built-in | Override the research system prompt. |
ResearchResult fields: answer, explanation, confidence, text, citations (each with url, title, snippet), trajectory, usage (input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, calls, search_calls), raw.
Next steps
- Agent client: the full client surface (Anthropic shape, OpenAI shape, native).
- LangChain integration: lower-level
CompresrToolMiddleware,wrap_tool_with_compression, andCompresrExtractorfor custom retrieval pipelines. - LangGraph integration: drop-in compression node for
StateGraph-style agents.