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

137 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Agent;
use App\Context\ContextService;
use App\Context\UrlAnalyzer;
use App\Infrastructure\OllamaClient;
use App\Knowledge\Retrieval\RetrieverInterface;
use Generator;
use Psr\Log\LoggerInterface;
use Throwable;
use App\Agent\StreamChunker;
final readonly class AgentRunner
{
public function __construct(
private PromptBuilder $promptBuilder,
private ThinkSuppressor $thinkSuppressor,
private ContextService $contextService,
private UrlAnalyzer $urlAnalyzer,
private RetrieverInterface $retriever,
private OllamaClient $ollamaClient,
private LoggerInterface $agentLogger,
private bool $debug,
private bool $logPrompt,
private bool $logContext,
) {}
public function run(string $prompt, string $userId): Generator
{
$prompt = trim($prompt);
if ($prompt === '') {
yield '❌ Empty prompt.';
return;
}
$this->agentLogger->info('Agent run started', [
'userId' => $userId,
]);
try {
// ---------------------------------------------------------
// 1) Context strategy
// ---------------------------------------------------------
$includeFullContext = false;
// ---------------------------------------------------------
// 2) Extract URL content (if present)
// ---------------------------------------------------------
$urlContent = $this->urlAnalyzer->extractContentFromPrompt($prompt);
// ---------------------------------------------------------
// 3) Retrieve RAG knowledge
// ---------------------------------------------------------
$knowledgeChunks = $this->retriever->retrieve($prompt);
// ---------------------------------------------------------
// 4) Build final prompt
// ---------------------------------------------------------
$finalPrompt = $this->promptBuilder->build(
prompt: $prompt,
userId: $userId,
urlContent: $urlContent,
knowledgeChunks: $knowledgeChunks,
fullContext: $includeFullContext
);
if ($this->debug && $this->logPrompt) {
$this->agentLogger->debug($finalPrompt);
}
if ($this->debug && $this->logContext) {
$this->agentLogger->debug('Conversation context snapshot', [
'context' => $this->contextService->buildUserContext(
$userId,
$includeFullContext
),
]);
}
// ---------------------------------------------------------
// 5) Stream tokens from the LLM backend (chunked streaming)
// ---------------------------------------------------------
$fullOutput = '';
$chunker = new StreamChunker();
foreach ($this->ollamaClient->stream($finalPrompt) as $token) {
$cleanToken = $this->thinkSuppressor->filter($token);
if ($cleanToken === '') {
continue;
}
// Vollständige Antwort weiter sammeln (für History)
$fullOutput .= $cleanToken;
// ⬇️ Token in Chunker geben
$chunk = $chunker->push($cleanToken);
if ($chunk !== null) {
yield $chunk;
}
}
// ⬇️ Rest flushen
$finalChunk = $chunker->flush();
if ($finalChunk !== null) {
yield $finalChunk;
}
// ---------------------------------------------------------
// 6) Persist conversation history
// ---------------------------------------------------------
$this->contextService->appendHistory(
$userId,
$prompt,
$fullOutput
);
$this->agentLogger->info('Agent run finished', [
'userId' => $userId,
'outputLength' => mb_strlen($fullOutput),
'contextMode' => 'recent',
]);
} catch (Throwable $e) {
$this->agentLogger->error('Agent run failed', [
'userId' => $userId,
'exception' => $e,
]);
yield "\n❌ An internal error occurred while processing the request.";
}
}
}