Files
MtoRagSystem/src/Agent/PromptBuilder.php
2026-02-11 14:15:08 +01:00

137 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Agent;
use App\Context\ContextService;
use App\Context\UrlAnalyzer;
use DateTimeImmutable;
final class PromptBuilder
{
public function __construct(
private readonly ContextService $contextService,
private readonly UrlAnalyzer $urlAnalyzer,
)
{
}
/**
* Build the final prompt string for the LLM.
*
* @param string $prompt
* @param string $userId
* @param string $urlContent
* @param string[] $knowledgeChunks
* @param bool $fullContext
*/
public function build(
string $prompt,
string $userId,
string $urlContent,
array $knowledgeChunks,
bool $fullContext = false,
): string
{
$now = (new DateTimeImmutable())->format('Y-m-d H:i:s');
// ------------------------------------------------------------
// 1) SYSTEM INSTRUCTIONS
// ------------------------------------------------------------
$systemLines = [
'You are a conversational AI assistant.',
'Respond clearly, precisely, and in context of the ongoing conversation.',
'The conversation context is authoritative and must be respected.',
'External knowledge is supporting information only.',
'If the user asks for contact details such as phone number, email address, postal address or contact person, and the provided context contains such information, answer explicitly with the concrete data.',
'Do not omit contact details.',
'It is allowed and desired to quote contact data verbatim if it appears in the context.',
"Current date and time: {$now}",
'',
'IMPORTANT FORMATTING RULES:',
'- Always answer in valid Markdown.',
'- Use headings, lists, and paragraphs where appropriate.',
'- Insert line breaks early and often.',
'- Never write long paragraphs without newlines.',
'- Each list item must start on a new line.',
'- Prefer short paragraphs over dense text blocks.',
'',
'IMPORTANT LANGUAGE RULES:',
'- If the user input contains misspellings, silently use the correct canonical terms in your answer.',
'- Never mention, explain, or point out spelling mistakes.',
'- Do not ask clarifying questions about possible misspellings.',
'- Do not repeat or quote misspelled terms from the user input.',
'- Always use the correct technical spelling found in the provided context.',
'- Answer directly and confidently using always correct canonical terminology.'
];
$systemBlock = "SYSTEM:\n" . implode("\n", $systemLines);
// ------------------------------------------------------------
// 2) CONVERSATION CONTEXT (AUTHORITATIVE)
// ------------------------------------------------------------
$history = $this->contextService->buildUserContext(
userId: $userId,
full: $fullContext
);
$contextBlock = '';
if ($history !== '') {
$contextBlock =
"CONVERSATION CONTEXT (authoritative):\n" .
"The following messages are the previous turns of this conversation.\n" .
"They must be considered when answering the next question.\n\n" .
$history;
}
// ------------------------------------------------------------
// 3) EXTERNAL KNOWLEDGE (SUPPORTING)
// ------------------------------------------------------------
$knowledgeParts = [];
if ($knowledgeChunks !== []) {
$lines = [];
foreach ($knowledgeChunks as $i => $chunk) {
$n = $i + 1;
$lines[] = "[{$n}] {$chunk}";
}
$knowledgeParts[] =
"RETRIEVED KNOWLEDGE (supporting):\n" .
implode("\n\n", $lines);
}
if ($urlContent !== '') {
$knowledgeParts[] =
"CONTENT FROM URL (supporting):\n" .
$urlContent;
}
$knowledgeBlock = '';
if ($knowledgeParts !== []) {
$knowledgeBlock = implode("\n\n", $knowledgeParts);
}
// ------------------------------------------------------------
// 4) USER QUESTION
// ------------------------------------------------------------
$userBlock =
"USER QUESTION:\n" .
$prompt;
// ------------------------------------------------------------
// 5) FINAL PROMPT ASSEMBLY
// ------------------------------------------------------------
$blocks = array_filter([
$systemBlock,
$contextBlock,
$knowledgeBlock,
$userBlock,
]);
return implode("\n\n", $blocks);
}
}