1. Go to this page and download the library: Download murkrow/laravel-rag library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
murkrow / laravel-rag example snippets
Rag::ask('Who convened the council, and when?')->answer;
// "The podestà Guido Novello convened the general council in March. [#1]"
// app/Knowledge/BookSource.php
namespace App\Knowledge;
use App\Models\Book;
use Illuminate\Database\Eloquent\Builder;
use Murkrow\Rag\Sources\{EloquentSource, Filter, PositionLabels, SegmentMap};
final class BookSource extends EloquentSource
{
public function key(): string { return 'books'; } // stored on every document
public function label(): string { return 'Library'; }
public function icon(): ?string { return 'heroicon-o-book-open'; }
protected function model(): string { return Book::class; }
protected function keyColumn(): string { return 'id'; } // becomes external_id
protected function titleColumn(): ?string { return 'title'; }
protected function metadata(): array { return ['author', 'isbn']; }
protected function segmentMap(): SegmentMap
{
return SegmentMap::relation('pages', text: 'content', position: 'number', batchSize: 200);
}
/** How a citation reads. */
protected function positionLabels(): PositionLabels
{
return new PositionLabels('Pages :start-:end', 'Page :start');
}
/** Only index what is worth indexing. */
protected function scope(Builder $query): void
{
$query->whereNotNull('published_at');
}
/** Drives both `--filter=` on the CLI and the ingestion form. */
protected function filters(): iterable
{
return [
Filter::ids('ids', 'id', label: 'Specific IDs'),
Filter::range('id_range', 'id', label: 'ID range'),
Filter::like('title', label: 'Title contains'),
Filter::boolean('bad_ocr', label: 'Include badly scanned books', default: false),
];
}
/** Deep link back into your app. */
public function url(Document $document, ?Chunk $chunk = null): ?string
{
return route('books.show', ['book' => $document->external_id, 'page' => $chunk?->position_start]);
}
}
protected function chunkingOverrides(): ChunkingOverrides
{
return new ChunkingOverrides(targetTokens: 320, overlapTokens: 40);
}
final class ToponymSource extends GroupedEloquentSource
{
public function key(): string { return 'toponyms'; }
protected function model(): string { return Toponym::class; }
protected function groupBy(): string { return 'upper(substr(name, 1, 1))'; } // one document per initial
protected function textColumn(): string { return 'name'; }
protected function documentTitle(string $group): string
{
return "Toponyms - {$group}";
}
public function chunkingOverrides(): ChunkingOverrides
{
// Entries are independent: no fact spans a boundary, so overlap is
// pure cost -- and bridging would stitch the whole letter into one
// sentence, since names carry no closing punctuation.
return new ChunkingOverrides(targetTokens: 256, overlapTokens: 0, bridgeSegments: false);
}
}
use Murkrow\Rag\Facades\Rag;
use Murkrow\Rag\Data\{AnswerOptions, RetrievalOptions};
// Retrieval only — no model call, no cost.
$chunks = Rag::search('who convened the council?');
// Grounded answer with citations.
$result = Rag::ask('who convened the council?', new AnswerOptions(
retrieval: new RetrievalOptions(
sourceKeys: ['books'],
externalIds: ['42'], // one book
positionFrom: 10, // pages 10–20
positionTo: 20,
topK: 6,
),
));
$result->answer; // the text
$result->refused; // true when the corpus could not support it
$result->usedCitations(); // only the ones the model actually cited
$result->usage->costUsd();