site logo

Marico's space

消除AI幻觉:Laravel中的RAG架构 🧠

编程技术 2026-08-26 17:34:13 11

随着企业纷纷把GPT-4这类大语言模型(LLM,大语言模型)集成到自家平台,他们很快会遇到一个致命问题:LLM会一本正经地胡说八道。这种现象叫"幻觉"(hallucination),本质原因是LLM本质上只是个概率预测引擎。如果你问一个现成的LLM关于昨天刚写的某条绝密公司政策,它不知道答案——但它不会说"我不知道",而是会自信满满地编出一段语法完美、逻辑通顺、但完全虚构的内容。

在企业软件领域,一句编造的法律条款或一个错误的财务数字可能导致巨额赔偿,这种幻觉是完全不可接受的。同样,你也不可能把公司5万页的wiki文档全塞进LLM的提示框——那会瞬间超过模型的token上限,然后收到一张天文数字的API账单。

我们做企业级AI系统时,核心目标就是让AI的回答严格基于真实、私有的数据。为此,我们放弃了微调方案,转而在Laravel中构建检索增强生成(RAG,Retrieval-Augmented Generation)管道,结合向量数据库和嵌入模型来实现。

RAG的核心理念

RAG的思路简洁而巧妙:在让LLM回答用户问题之前,先充当一个高度智能的搜索引擎。我们从私有数据库中搜索与用户问题最相关的文档,提取出相关段落,附加到提示词里,然后告诉LLM:"只能用下面提供的上下文来回答用户的问题。"

这个架构需要两条独立的管道:摄取管道(存储数据)和检索管道(获取数据)。

第一阶段:摄取管道(分块与嵌入)

直接把一份100页的PDF存进数据库,然后指望能按语义搜索它,这是不现实的。我们必须把文档拆分成更小的片段(Chunk,分块),然后把这些片段转换成数学上可搜索的数字(Vector Embedding,向量嵌入)。

在Laravel中,我们创建一个后台任务来处理文档加工。把文本分块后,发送给嵌入模型(比如OpenAI的text-embedding-3-small),模型会返回一个浮点数数组,这些数字代表文本的语义含义。然后我们把这个数组存进向量数据库,比如Pinecone、Milvus,或者启用了pgvector扩展的PostgreSQL。


namespace App\Jobs; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Http;
use App\Services\VectorDatabaseService; class IngestDocumentIntoVectorDB implements ShouldQueue
{ use Dispatchable, Queueable; public function __construct( public readonly int $documentId, public readonly string $rawText ) {} public function handle(VectorDatabaseService $vectorDb): void { // 1. 分块处理:将大段文本拆分成500词的段落 // 实际应用中应该使用重叠分块来保留上下文 $chunks = str_split($this->rawText, 2000); foreach ($chunks as $index => $chunk) { // 2. 通过OpenAI生成向量嵌入 $response = Http::withToken(config('services.openai.key')) ->post('https://api.openai.com/v1/embeddings', [ 'model' => 'text-embedding-3-small', 'input' => $chunk, ]); $embedding = $response->json('data.0.embedding'); // 3. upsert到向量数据库(比如Pinecone或pgvector) // 存储向量、原始文本和用于过滤的元数据 $vectorDb->upsert( id: "doc_{$this->documentId}_chunk_{$index}", vector: $embedding, metadata: [ 'document_id' => $this->documentId, 'text' => $chunk // 关键:存储文本以便后续检索! ] ); } }
}

第二阶段:检索管道(语义搜索)

当用户提问时,传统的SQL LIKE '%keyword%'搜索根本不够用。比如用户问"如何重置密码?",SQL数据库匹配不到标题为"凭证恢复流程"的文档,因为关键词对不上。但向量数据库不一样,它通过余弦相似度(Cosine Similarity)搜索——找到的是数学意义上语义相近的文本。

用户提问时,我们立刻把问题转换成向量,与数据库中的向量做比较,检索出最相关的3个文本块。


namespace App\Services; use Illuminate\Support\Facades\Http; class RAGQueryService
{ public function __construct(private VectorDatabaseService $vectorDb) {} public function retrieveContext(string $userQuestion): string { // 1. 将用户问题转换为向量 $response = Http::withToken(config('services.openai.key')) ->post('https://api.openai.com/v1/embeddings', [ 'model' => 'text-embedding-3-small', 'input' => $userQuestion, ]); $questionEmbedding = $response->json('data.0.embedding'); // 2. 查询向量数据库,获取最相关的3个匹配结果 $matches = $this->vectorDb->query( vector: $questionEmbedding, topK: 3 ); // 3. 从匹配结果中提取文本,合并成单个字符串 $context = ""; foreach ($matches as $match) { $context .= $match['metadata']['text'] . "\n\n"; } return $context; }
}

第三阶段:生成(提示词注入)

从数据库中检索到高度相关的上下文后,我们就可以与LLM交互了。我们构建一个严格 system prompt,限制LLM访问外部训练数据,强制它只能把我们提供的上下文当摘要来用。


namespace App\Http\Controllers; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use App\Services\RAGQueryService; class EnterpriseChatController extends Controller
{ public function ask(Request $request, RAGQueryService $ragService) { $userQuestion = $request->input('question'); // 1. 检索相关的私有数据 $context = $ragService->retrieveContext($userQuestion); // 2. 构建严格的RAG提示词 $systemPrompt = " 你是一个有用的企业助手。 只能用下面提供的上下文来回答用户的问题。 如果答案不在上下文中,你必须回复: '我在公司文档中找不到这个问题的答案。' 不要使用外部知识。 上下文: {$context} "; // 3. 安全地生成回复 $llmResponse = Http::withToken(config('services.openai.key')) ->post('https://api.openai.com/v1/chat/completions', [ 'model' => 'gpt-4o', 'messages' => [ ['role' => 'system', 'content' => $systemPrompt], ['role' => 'user', 'content' => $userQuestion] ], 'temperature' => 0.1, // 降低创造性以保证事实准确性 ]); return response()->json([ 'answer' => $llmResponse->json('choices.0.message.content') ]); }
}

工程投入产出比

在Laravel中构建RAG管道,你就能实现企业级AI的圣杯:事实准确性、数据隐私和无限扩展性。相比微调模型(贵得离谱,而且每次文档更新都得重新训练),RAG允许你实时更新知识库。如果某条政策变了,只需从Pinecone里删掉旧向量,摄入新文档,LLM立刻就能给出更新后的答案。掌握好分块策略、嵌入技术和向量相似度搜索,你就能把标准的Laravel API变成高度安全、具备领域专家能力的AI引擎。