cd /news/large-language-models/09-rag-in-php-answer-from-your-own-d… · home › topics › large-language-models › article
[ARTICLE · art-140622] src=dev.to ↗ pub= topic=large-language-models verified=true sentiment=↑ positive

09 - RAG in PHP - Answer From Your Own Docs, Not the Model's Memory

A developer demonstrated a retrieval-augmented generation (RAG) pattern in PHP using NanoAgent, in which a knowledge-base search is exposed to an LLM as a callable tool rather than relying on the model's memory. The example uses a case-insensitive substring match over a PHP array of policy documents and a system prompt instructing the agent to answer only from retrieved results, cite document titles, and state when nothing is found. The author notes the agent code stays unchanged as retrieval scales from keyword search to SQL or a vector store such as Qdrant or pgvector.

by read3 min views1 publishedSep 27, 2026

The failure mode you hit the moment you point an LLM at internal questions: it hallucinates policy that sounds right but was never written. "Can I work from Hawaii for two months?" is exactly the kind of question where a confident wrong answer costs you real money.

Retrieval-Augmented Generation (RAG) fixes it: retrieve the relevant doc first, then answer from that - and say so when there's no answer. In NanoAgent, RAG is just a search tool. No vector database required to start.

$knowledgeBase = [
    'policy_wfh' => [
        'title' => 'Remote Work Policy 2024',
        'content' => 'Employees may work remotely up to 3 days a week. Full remote work '
                    . 'requires Director approval. Working from international locations is '
                    . 'limited to 30 days per year due to tax implications.'
    ],
    'policy_holiday' => [
        'title' => 'Holiday Schedule 2024',
        'content' => 'Closed on New Year\'s Day, Memorial Day, Independence Day, Labor Day, '
                    . 'Thanksgiving, and Christmas Day.'
    ],
    'it_support' => [
        'title' => 'IT Support Contacts',
        'content' => 'Urgent: ext 5555. Non-urgent: support@company.com. Password resets via portal.'
    ]
];

The tool takes one argument - free-text keywords - and describes itself as a policy search, so the model knows to reach for it on anything HR-shaped.

use NanoAgent\Agent;
use NanoAgent\Tools\FunctionTool;

$searchTool = new FunctionTool(
    name: 'search_knowledge_base',
    description: 'Searches the internal knowledge base for policy documents. Input keywords.',
    parameters: [
        'type' => 'object',
        'properties' => [
            'query' => ['type' => 'string', 'description' => 'Keywords to search for']
        ],
        'required' => ['query']
    ],

The callable does a plain case-insensitive substring match against title and content, and returns either the matching documents or an explicit "nothing found" - never a guess.

    callable: function (array $args) use ($knowledgeBase) {
        $q = strtolower($args['query']);
        $hits = [];
        foreach ($knowledgeBase as $doc) {
            if (str_contains(strtolower($doc['title']), $q)
                || str_contains(strtolower($doc['content']), $q)) {
                $hits[] = "Title: {$doc['title']}\nContent: {$doc['content']}";
            }
        }
        return $hits
             ? implode("\n\n---\n\n", $hits)
             : "No relevant documents found.";
    }
);
php
$agent = new Agent(
    llm: $llmConfig,
    systemPrompt: "You are a professional HR assistant. You must ONLY answer based on the "
                . "search results. If the information is not present, politely say so. "
                . "Always cite the document title.",
    tools: [$searchTool]
);

$response = $agent->chat("Can I work from Hawaii for two months?");
echo $response;

That last prompt line is the whole safety model. Three rules do the heavy lifting:

For the Hawaii question, the tool retrieves Remote Work Policy 2024 (it contains "international locations"), and the agent answers: limited to 30 days/year, so two months isn't allowed - per Remote Work Policy 2024. Grounded, cited, correct.

The only thing that changes as you scale is what the callable does:

Stage The search_knowledge_base callable does
Demo keyword scan of a PHP array
Real (small) query your SQL/Postgres docs table
Real (large) embed the query + docs, hit a vector store (Qdrant, pgvector)

The agent code never changes. You keep the same tool name and the same prompt; you just make retrieval smarter inside the function. Start with the dumb keyword search - it's enough to prove the pattern and to handle small corpora - and upgrade the retrieval when you need to.

You could stuff the whole doc into the prompt. That blows the context window and wastes tokens on irrelevant content. A tool lets the model decide what to retrieve and when, pulling only what's relevant. As your corpus grows, that's the difference between "works" and "doesn't fit."

Part of the NanoAgent examples series. Landing + demos.

── more in #large-language-models 4 stories · sorted by recency
── more on @nanoagent 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/09-rag-in-php-answer…] indexed:0 read:3min 2026-09-27 · —