> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinecone.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a knowledge retrieval agent

> Build an AI agent that uses Pinecone to retrieve knowledge and answer questions accurately, with Pinecone as a tool inside the agent.

Build an AI agent that uses Pinecone to retrieve knowledge and answer questions accurately. This demo shows Pinecone as a tool inside an agent, which is the same pattern you'd use in production.

> [!NOTE]
>
> This guide requires an [Anthropic](https://console.anthropic.com/) or [OpenAI](https://platform.openai.com/api-keys) API key alongside your Pinecone API key.

**1. Install dependencies**

```bash Anthropic (Claude) theme={null}
pip install pinecone anthropic
```

```bash OpenAI theme={null}
pip install pinecone openai
```

**2. Create an index and load knowledge**

Create a Pinecone index with [integrated embedding](https://docs.pinecone.io/guides/indexes/indexing-overview.md#integrated-embedding) and load a small knowledge base. These are facts your LLM doesn't know on its own, so retrieval is the only way to answer accurately.

```python Anthropic (Claude) theme={null}
import anthropic
from pinecone import Pinecone

pc = Pinecone(api_key="{{YOUR_API_KEY}}")
llm = anthropic.Anthropic(api_key="YOUR_ANTHROPIC_API_KEY")

# Create an index with integrated embedding
if not pc.has_index("knowledge"):
    pc.create_index_for_model(
        name="knowledge",
        cloud="aws",
        region="us-east-1",
        embed={
            "model": "llama-text-embed-v2",
            "field_map": {"text": "content"}
        }
    )

index = pc.index(name="knowledge")

# Load your knowledge base
index.upsert_records(
    namespace="docs",
    records=[
        {"_id": "policy-1", "content": "Refund requests must be submitted within 30 days of purchase. After 30 days, only store credit is available.", "category": "policies"},
        {"_id": "policy-2", "content": "Enterprise customers get dedicated support with a 4-hour response time SLA. Standard support responds within 24 hours.", "category": "policies"},
        {"_id": "spec-1", "content": "The WonderVector 5000 supports up to 100,000 vectors per namespace with a maximum dimensionality of 4096.", "category": "specs"},
        {"_id": "spec-2", "content": "API rate limits: Free tier is 100 requests/minute, Pro tier is 1000 requests/minute, Enterprise is unlimited with fair use.", "category": "specs"},
        {"_id": "spec-3", "content": "Data is encrypted at rest using AES-256 and in transit using TLS 1.3. SOC2 Type II compliance is maintained.", "category": "security"},
        {"_id": "hr-1", "content": "New employees receive 15 days PTO in their first year, increasing to 20 days after 2 years and 25 days after 5 years.", "category": "hr"},
        {"_id": "hr-2", "content": "The company matches 401k contributions up to 4% of salary. Vesting is immediate for all employees.", "category": "hr"},
        {"_id": "proc-1", "content": "To request a new software license, submit a ticket in the IT portal. Approvals take 2-3 business days for standard software.", "category": "procedures"},
        {"_id": "proc-2", "content": "Production deployments require approval from the team lead and a passing CI/CD pipeline. Hotfixes can bypass the lead approval.", "category": "procedures"},
        {"_id": "proc-3", "content": "Vendor invoices over $10,000 require VP approval. Under $10,000 requires manager approval only.", "category": "procedures"},
    ]
)
```

```python OpenAI theme={null}
from openai import OpenAI
from pinecone import Pinecone

pc = Pinecone(api_key="{{YOUR_API_KEY}}")
llm = OpenAI(api_key="YOUR_OPENAI_API_KEY")

# Create an index with integrated embedding
if not pc.has_index("knowledge"):
    pc.create_index_for_model(
        name="knowledge",
        cloud="aws",
        region="us-east-1",
        embed={
            "model": "llama-text-embed-v2",
            "field_map": {"text": "content"}
        }
    )

index = pc.index(name="knowledge")

# Load your knowledge base
index.upsert_records(
    namespace="docs",
    records=[
        {"_id": "policy-1", "content": "Refund requests must be submitted within 30 days of purchase. After 30 days, only store credit is available.", "category": "policies"},
        {"_id": "policy-2", "content": "Enterprise customers get dedicated support with a 4-hour response time SLA. Standard support responds within 24 hours.", "category": "policies"},
        {"_id": "spec-1", "content": "The WonderVector 5000 supports up to 100,000 vectors per namespace with a maximum dimensionality of 4096.", "category": "specs"},
        {"_id": "spec-2", "content": "API rate limits: Free tier is 100 requests/minute, Pro tier is 1000 requests/minute, Enterprise is unlimited with fair use.", "category": "specs"},
        {"_id": "spec-3", "content": "Data is encrypted at rest using AES-256 and in transit using TLS 1.3. SOC2 Type II compliance is maintained.", "category": "security"},
        {"_id": "hr-1", "content": "New employees receive 15 days PTO in their first year, increasing to 20 days after 2 years and 25 days after 5 years.", "category": "hr"},
        {"_id": "hr-2", "content": "The company matches 401k contributions up to 4% of salary. Vesting is immediate for all employees.", "category": "hr"},
        {"_id": "proc-1", "content": "To request a new software license, submit a ticket in the IT portal. Approvals take 2-3 business days for standard software.", "category": "procedures"},
        {"_id": "proc-2", "content": "Production deployments require approval from the team lead and a passing CI/CD pipeline. Hotfixes can bypass the lead approval.", "category": "procedures"},
        {"_id": "proc-3", "content": "Vendor invoices over $10,000 require VP approval. Under $10,000 requires manager approval only.", "category": "procedures"},
    ]
)
```

> [!NOTE]
>
> Pinecone is eventually consistent. New records may take a few seconds to become searchable.

**3. Define Pinecone as a tool**

Wrap Pinecone search in a function your agent can call. Drop this into any agent codebase to add knowledge retrieval. Run all snippets in the same Python session so `index` and `llm` stay in scope.

**Agent tool: `search_knowledge_base`**

```python Python theme={null}
def search_knowledge_base(query: str) -> str:
    """Search the knowledge base for relevant information."""
    results = index.search(
        namespace="docs",
        # To scope by metadata, add "filter": {"category": {"$eq": "policies"}} to the query dict
        query={"top_k": 3, "inputs": {"text": query}},
        rerank={
            "model": "bge-reranker-v2-m3",
            "top_n": 3,
            "rank_fields": ["content"]
        }
    )
    return "\n\n".join(
        hit.fields["content"]
        for hit in results["result"]["hits"]
    )
```

**4. Wire the tool into your agent**

Give your LLM the ability to call the search function when it needs information.

```python Anthropic (Claude) theme={null}
tools = [{
    "name": "search_knowledge_base",
    "description": "Search the company knowledge base for policies, specs, HR info, and procedures.",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string", "description": "The search query"}
        },
        "required": ["query"]
    }
}]

def ask(question: str) -> str:
    messages = [{"role": "user", "content": question}]

    # disable_parallel_tool_use keeps this loop simple: with parallel calls,
    # every tool_use block would need a matching tool_result in the next message
    response = llm.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        tools=tools,
        tool_choice={"type": "auto", "disable_parallel_tool_use": True},
        messages=messages
    )

    # If the model wants to use a tool, call it and return the result
    while response.stop_reason == "tool_use":
        tool_block = next(b for b in response.content if b.type == "tool_use")
        tool_result = search_knowledge_base(tool_block.input["query"])

        messages += [
            {"role": "assistant", "content": response.content},
            {"role": "user", "content": [{
                "type": "tool_result",
                "tool_use_id": tool_block.id,
                "content": tool_result
            }]}
        ]

        response = llm.messages.create(
            model="claude-sonnet-5",
            max_tokens=1024,
            tools=tools,
            tool_choice={"type": "auto", "disable_parallel_tool_use": True},
            messages=messages
        )

    return next(b.text for b in response.content if hasattr(b, "text"))
```

```python OpenAI theme={null}
import json

tools = [{
    "type": "function",
    "function": {
        "name": "search_knowledge_base",
        "description": "Search the company knowledge base for policies, specs, HR info, and procedures.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "The search query"}
            },
            "required": ["query"]
        }
    }
}]

def ask(question: str) -> str:
    messages = [{"role": "user", "content": question}]

    # parallel_tool_calls=False keeps this loop simple: with parallel calls,
    # every tool call would need a matching tool message in the next turn
    response = llm.chat.completions.create(
        model="gpt-4o",
        tools=tools,
        parallel_tool_calls=False,
        messages=messages
    )

    # If the model wants to use a tool, call it and return the result
    while response.choices[0].finish_reason == "tool_calls":
        tool_call = response.choices[0].message.tool_calls[0]
        args = json.loads(tool_call.function.arguments)
        tool_result = search_knowledge_base(args["query"])

        messages += [
            response.choices[0].message,
            {"role": "tool", "tool_call_id": tool_call.id, "content": tool_result}
        ]

        response = llm.chat.completions.create(
            model="gpt-4o",
            tools=tools,
            parallel_tool_calls=False,
            messages=messages
        )

    return response.choices[0].message.content
```

**5. Ask your agent a question**

```python theme={null}
print(ask("What's the refund policy?"))
```

If the agent says it can't find the information, wait a few seconds and retry. Pinecone is eventually consistent, so freshly upserted records take a moment to become searchable.

Your agent searches Pinecone, retrieves the relevant policy, and answers:

```console Output theme={null}
Refund requests must be submitted within 30 days of purchase. After that
30-day window, you can still receive store credit but not a direct refund.
```

Try a few more questions:

```python theme={null}
print(ask("How much PTO do new employees get?"))
print(ask("What approval do I need for a $15,000 vendor invoice?"))
```

> [!NOTE]
>
> **What just happened:** Your LLM received a question, decided it needed more information, and called the `search_knowledge_base` tool. Pinecone returned the most relevant records with reranking, and the LLM synthesized an accurate answer from the retrieved context. Production RAG agents use this same pattern, and the `search_knowledge_base` function works in any agent framework.

### Next steps

> [card] [Use the MCP server](https://docs.pinecone.io/guides/agents/mcp-server.md)
>
> Give agents Pinecone access over the Model Context Protocol

> [card] [Search methods](https://docs.pinecone.io/guides/search/search-overview.md)
>
> Explore semantic, hybrid, and full-text search

> [card] [Data modeling](https://docs.pinecone.io/guides/indexes/data-modeling.md)
>
> Model your data for efficient retrieval
