Skip to content

Agent Tools

ragwise exposes its document index as a ready-made tool for Claude and OpenAI agents. Your agent handles conversation and reasoning; ragwise handles retrieval.

Overview

ragwise provides two patterns:

API Use when
as_claude_tool(rag) / as_openai_tool(rag) Single-turn, stateless tool call
as_claude_tool_suite(rag) + AgentSession Multi-turn, stateful — deduplication + loop detection

AgentSession (Multi-Turn)

AgentSession tracks retrieved chunks across calls, deduplicates by chunk_id, and warns when the same query is submitted twice (loop detection):

from ragwise.agent import as_claude_tool_suite, AgentSession

async with RAG(llm="openai/gpt-4o-mini") as rag:
    await rag.ingest("./docs/")

    # Returns 3 tools: search_documents, get_document_context, check_context_budget
    tools = as_claude_tool_suite(rag, max_iterations=5)

    response = anthropic.messages.create(
        model="claude-opus-4-6",
        tools=tools,
        messages=[{"role": "user", "content": "Compare policy A to policy B"}],
    )

The three tools: - search_documents — retrieves top-k chunks for a query - get_document_context — fetches the full parent chunk for a given chunk_id - check_context_budget — returns how many tokens of context remain before the session is full

Multi-Hop Example

# Agent makes 3 tool calls:
# 1. search_documents("maternity leave policy")   → retrieves policy A
# 2. search_documents("paternity leave policy")   → retrieves policy B
# 3. check_context_budget()                       → confirms budget before answer

# AgentSession deduplicates: if policy A appears in both searches, it's only
# passed to the LLM once. Loop detection warns if query 2 is too similar to query 1.

Single-Turn Tools

Claude Agent (Single-Turn)

import asyncio
import anthropic
from ragwise import RAG
from ragwise.agent import as_claude_tool

async def main():
    async with RAG(llm="openai/gpt-4o-mini") as rag:
        await rag.ingest("./docs/")

        tool = as_claude_tool(rag)

        client = anthropic.Anthropic()

        # Agent turn 1 — model decides to use the tool
        response = client.messages.create(
            model="claude-opus-4-6",
            max_tokens=1024,
            tools=[tool],
            messages=[{"role": "user", "content": "What is the refund policy?"}],
        )

        # Handle tool call
        if response.stop_reason == "tool_use":
            tool_call = next(b for b in response.content if b.type == "tool_use")
            results = await rag.search(
                tool_call.input["query"],
                top_k=tool_call.input.get("top_k", 5),
            )

            # Format results as tool result
            tool_result_content = "\n\n".join(
                f"[{r.source}]\n{r.text}" for r in results
            )

            # Agent turn 2 — model generates answer from retrieved context
            final = client.messages.create(
                model="claude-opus-4-6",
                max_tokens=1024,
                tools=[tool],
                messages=[
                    {"role": "user", "content": "What is the refund policy?"},
                    {"role": "assistant", "content": response.content},
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "tool_result",
                                "tool_use_id": tool_call.id,
                                "content": tool_result_content,
                            }
                        ],
                    },
                ],
            )
            print(final.content[0].text)

asyncio.run(main())

OpenAI Agent (Single-Turn)

import asyncio
import json
from openai import AsyncOpenAI
from ragwise import RAG
from ragwise.agent import as_openai_tool

async def main():
    async with RAG(llm="openai/gpt-4o-mini") as rag:
        await rag.ingest("./docs/")

        tool = as_openai_tool(rag)
        client = AsyncOpenAI()

        messages = [{"role": "user", "content": "What is the refund policy?"}]

        # Agent turn 1
        response = await client.chat.completions.create(
            model="gpt-4o-mini",
            tools=[tool],
            messages=messages,
        )

        msg = response.choices[0].message

        if msg.tool_calls:
            tool_call = msg.tool_calls[0]
            args = json.loads(tool_call.function.arguments)
            results = await rag.search(args["query"], top_k=args.get("top_k", 5))

            context = "\n\n".join(f"[{r.source}]\n{r.text}" for r in results)

            messages += [
                msg,
                {
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": context,
                },
            ]

            # Agent turn 2
            final = await client.chat.completions.create(
                model="gpt-4o-mini",
                tools=[tool],
                messages=messages,
            )
            print(final.choices[0].message.content)

asyncio.run(main())

Tool schema reference

as_claude_tool(rag) output

{
  "name": "search_documents",
  "description": "Search the document index and return the most relevant passages. Use this to retrieve context before answering questions.",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "The search query"
      },
      "top_k": {
        "type": "integer",
        "description": "Number of passages to return (default: 5)",
        "default": 5
      }
    },
    "required": ["query"]
  }
}

as_openai_tool(rag) output

{
  "type": "function",
  "function": {
    "name": "search_documents",
    "description": "Search the document index and return the most relevant passages.",
    "parameters": {
      "type": "object",
      "properties": {
        "query": {"type": "string"},
        "top_k": {"type": "integer", "default": 5}
      },
      "required": ["query"]
    }
  }
}

Multi-tenant isolation in agents

Pass QueryConfig to rag.search() to restrict results to a specific tenant:

from ragwise import QueryConfig

results = await rag.search(
    tool_call.input["query"],
    top_k=tool_call.input.get("top_k", 5),
    config=QueryConfig(tenant_id=current_user_org_id),
)

Results are filtered post-retrieval — the agent never sees documents from other tenants.

rag.search() directly

rag.search() is the raw retrieval method. Call it directly when you want full control:

from ragwise import RAG, SearchResult

async with RAG(...) as rag:
    results: list[SearchResult] = await rag.search("error code 0x80004005", top_k=5)

    for r in results:
        print(f"[{r.score:.3f}] {r.source}: {r.text[:120]}")

SearchResult fields:

Field Type Description
id str Unique chunk identifier
text str Chunk text content
source str Source file path
score float RRF fusion score
metadata dict Arbitrary metadata (tenant_id, content_hash, etc.)