optimize agent runner
This commit is contained in:
@@ -20,37 +20,39 @@ final readonly class AgentRunner
|
|||||||
private bool $systemMsgOn;
|
private bool $systemMsgOn;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private PromptBuilder $promptBuilder,
|
private PromptBuilder $promptBuilder,
|
||||||
private ThinkSuppressor $thinkSuppressor,
|
private ThinkSuppressor $thinkSuppressor,
|
||||||
private ContextService $contextService,
|
private ContextService $contextService,
|
||||||
private UrlAnalyzer $urlAnalyzer,
|
private UrlAnalyzer $urlAnalyzer,
|
||||||
private RetrieverInterface $retriever,
|
private RetrieverInterface $retriever,
|
||||||
private ShopSearchService $shopSearchService,
|
private ShopSearchService $shopSearchService,
|
||||||
private CommerceIntentLite $commerceIntentLite,
|
private CommerceIntentLite $commerceIntentLite,
|
||||||
private OllamaClient $ollamaClient,
|
private OllamaClient $ollamaClient,
|
||||||
private LoggerInterface $agentLogger,
|
private LoggerInterface $agentLogger,
|
||||||
private AgentRunnerConfig $agentRunnerConfig,
|
private AgentRunnerConfig $agentRunnerConfig,
|
||||||
private bool $debug,
|
private bool $debug,
|
||||||
private bool $logPrompt,
|
private bool $logPrompt,
|
||||||
private bool $logContext,
|
private bool $logContext,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->systemMsgOn = true;
|
$this->systemMsgOn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(string $prompt, string $userId, ?bool $includeFullContext = false): Generator
|
public function run(string $prompt, string $userId, bool $includeFullContext = false): Generator
|
||||||
{
|
{
|
||||||
$prompt = trim($prompt);
|
$prompt = trim($prompt);
|
||||||
$swagFullOutPut = '';
|
|
||||||
$firstThinkLoop = true;
|
|
||||||
$shopResults = [];
|
|
||||||
$sources = [];
|
|
||||||
|
|
||||||
if ($prompt === '') {
|
if ($prompt === '') {
|
||||||
yield '❌ Empty prompt.';
|
yield $this->systemMsg('❌ Empty prompt.', 'err');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$urlContent = '';
|
||||||
|
$knowledgeChunks = [];
|
||||||
|
$shopResults = [];
|
||||||
|
$sources = [];
|
||||||
|
$optimizedShopQuery = '';
|
||||||
|
$commerceIntent = CommerceIntentLite::NONE;
|
||||||
|
|
||||||
$this->agentLogger->info('Agent run started', [
|
$this->agentLogger->info('Agent run started', [
|
||||||
'userId' => $userId,
|
'userId' => $userId,
|
||||||
]);
|
]);
|
||||||
@@ -59,95 +61,63 @@ final readonly class AgentRunner
|
|||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 1) Context strategy
|
// 1) Context strategy
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
if ($includeFullContext) {
|
if ($includeFullContext) {
|
||||||
//Coming soon
|
// Full context mode is already passed to PromptBuilder.
|
||||||
|
// Additional context strategies can be added here later.
|
||||||
}
|
}
|
||||||
|
|
||||||
yield $this->systemMsg("Ich analysiere deine Anfrage...", "think");
|
yield $this->systemMsg('Ich analysiere deine Anfrage...', 'think');
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 2) Extract URL content (if present)
|
// 2) Extract URL content
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
yield $this->systemMsg("Ich prüfe auf Internet Quellen...", "think");
|
yield $this->systemMsg('Ich prüfe auf Internetquellen...', 'think');
|
||||||
|
|
||||||
$urlContent = $this->urlAnalyzer->extractContentFromPrompt($prompt);
|
$urlContent = $this->urlAnalyzer->extractContentFromPrompt($prompt);
|
||||||
if($urlContent){
|
if ($urlContent !== '') {
|
||||||
$sources[]= '<span class="badge bg-info text-black">Externe URL</span>';
|
$this->addSource($sources, 'Externe URL');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 3) Retrieve RAG knowledge
|
// 3) Retrieve RAG knowledge
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
yield $this->systemMsg("Ich hole relevante Daten aus meinem RAG Wissen...", "think");
|
yield $this->systemMsg('Ich hole relevante Daten aus meinem RAG-Wissen...', 'think');
|
||||||
|
|
||||||
$knowledgeChunks = $this->retriever->retrieve($prompt);
|
$knowledgeChunks = $this->retriever->retrieve($prompt);
|
||||||
if($knowledgeChunks){
|
if ($knowledgeChunks !== []) {
|
||||||
$sources[]= '<span class="badge bg-info text-black">RAG Wissen</span>';
|
$this->addSource($sources, 'RAG Wissen');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 4) commerce/shop search
|
// 4) Optional commerce/shop search
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
|
$commerceIntent = $this->detectCommerceIntent($prompt);
|
||||||
|
|
||||||
$commerceMeta = $this->commerceIntentLite->detect($prompt);
|
if ($this->isCommerceIntent($commerceIntent)) {
|
||||||
$commerceIntent = (string)($commerceMeta['intent'] ?? CommerceIntentLite::NONE);
|
yield $this->systemMsg('Ich optimiere die Recherche...', 'think');
|
||||||
|
|
||||||
if ($commerceIntent === CommerceIntentLite::PRODUCT_SEARCH || $commerceIntent === CommerceIntentLite::ADVISORY_PRODUCT_SEARCH) {
|
$optimizedShopQuery = $this->buildOptimizedShopQuery($prompt, $userId);
|
||||||
//PreOptimize swag search query
|
$shopSearchQuery = $optimizedShopQuery !== '' ? $optimizedShopQuery : $prompt;
|
||||||
$promptSwagSearch = $this->agentRunnerConfig->getShopPrompt($prompt);
|
|
||||||
|
|
||||||
//Reset thinkSuppressor
|
yield $this->systemMsg(
|
||||||
$this->thinkSuppressor->reset();
|
'Ich rufe Recherchedaten ab (type: ' . $commerceIntent . ')',
|
||||||
|
'think'
|
||||||
|
);
|
||||||
|
|
||||||
yield $this->systemMsg("Ich optimere die Recherche...", "think");
|
$shopResults = $this->searchShop($shopSearchQuery, $commerceIntent, $userId);
|
||||||
|
|
||||||
//Call AI for optimized swag query
|
if ($shopResults !== []) {
|
||||||
foreach ($this->ollamaClient->stream($promptSwagSearch) as $swagToken) {
|
$this->addSource($sources, 'Shopsystem');
|
||||||
|
|
||||||
if (!is_string($swagToken)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$swagCleanToken = $this->thinkSuppressor->filter($swagToken);
|
|
||||||
|
|
||||||
if ($swagCleanToken === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$swagFullOutPut .= $swagCleanToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield $this->systemMsg("Ich rufe Recherchedaten ab (type: " . $commerceIntent . ")", "think");
|
|
||||||
|
|
||||||
//Search in swag by AI optimized query
|
|
||||||
try {
|
|
||||||
$shopResults = $swagFullOutPut !== ''
|
|
||||||
? $this->shopSearchService->search($swagFullOutPut, $commerceIntent)
|
|
||||||
: [];
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
$this->agentLogger->warning('Shop search failed, continuing without shop results', [
|
|
||||||
'userId' => $userId,
|
|
||||||
'exception' => $e,
|
|
||||||
]);
|
|
||||||
|
|
||||||
yield $this->systemMsg('Shopdaten konnten nicht geladen werden, ich antworte mit Wissensbasis weiter...', 'think');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($shopResults){
|
$knowledgeChunks = $this->limitKnowledgeChunks($knowledgeChunks, $commerceIntent);
|
||||||
$sources[]= '<span class="badge bg-info text-black">Shopsystem</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($commerceIntent === CommerceIntentLite::PRODUCT_SEARCH) {
|
yield $this->systemMsg('Ich analysiere alle Informationen...', 'think');
|
||||||
$knowledgeChunks = array_slice($knowledgeChunks, 0, 2);
|
|
||||||
} elseif ($commerceIntent === CommerceIntentLite::ADVISORY_PRODUCT_SEARCH) {
|
|
||||||
$knowledgeChunks = array_slice($knowledgeChunks, 0, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
yield $this->systemMsg("Ich analysiere alle Informationen...", "think");
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 5) Build final prompt
|
// 5) Build final prompt
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
$finalPrompt = $this->promptBuilder->build(
|
$finalPrompt = $this->promptBuilder->build(
|
||||||
prompt: $prompt,
|
prompt: $prompt,
|
||||||
userId: $userId,
|
userId: $userId,
|
||||||
@@ -155,15 +125,20 @@ final readonly class AgentRunner
|
|||||||
knowledgeChunks: $knowledgeChunks,
|
knowledgeChunks: $knowledgeChunks,
|
||||||
shopResults: $shopResults,
|
shopResults: $shopResults,
|
||||||
fullContext: $includeFullContext,
|
fullContext: $includeFullContext,
|
||||||
swagFullOutPut: $swagFullOutPut
|
swagFullOutPut: $optimizedShopQuery
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($this->debug && $this->logPrompt) {
|
if ($this->debug && $this->logPrompt) {
|
||||||
$this->agentLogger->debug($finalPrompt);
|
$this->agentLogger->debug('Final prompt', [
|
||||||
|
'userId' => $userId,
|
||||||
|
'finalPrompt' => $finalPrompt,
|
||||||
|
'optimizedShopQuery' => $optimizedShopQuery,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->debug && $this->logContext) {
|
if ($this->debug && $this->logContext) {
|
||||||
$this->agentLogger->debug('Conversation context snapshot', [
|
$this->agentLogger->debug('Conversation context snapshot', [
|
||||||
|
'userId' => $userId,
|
||||||
'context' => $this->contextService->buildUserContext(
|
'context' => $this->contextService->buildUserContext(
|
||||||
$userId,
|
$userId,
|
||||||
$includeFullContext
|
$includeFullContext
|
||||||
@@ -171,71 +146,39 @@ final readonly class AgentRunner
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($sources !== []) {
|
||||||
|
yield $this->emitSources($sources, 'Genutzte Quellen: ');
|
||||||
|
}
|
||||||
|
print'<pre>';print_r($finalPrompt);exit;
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 6) Stream tokens from the LLM backend (chunked streaming)
|
// 6) Stream final LLM answer
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
$fullOutput = '';
|
$fullOutput = yield from $this->streamFinalAnswer($finalPrompt);
|
||||||
$chunker = new StreamChunker();
|
|
||||||
$chunker->flush();
|
|
||||||
$this->thinkSuppressor->reset();
|
|
||||||
|
|
||||||
if($sources){
|
if ($sources !== []) {
|
||||||
yield $this->systemMsg("Genutze Quellen: ".implode(' ',$sources), 'info');
|
yield $this->emitSources($sources, 'Quellen: ');
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->ollamaClient->stream($finalPrompt) as $token) {
|
|
||||||
|
|
||||||
if (!is_string($token)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cleanToken = $this->thinkSuppressor->filter((string)$token);
|
|
||||||
|
|
||||||
if ($cleanToken === '') {
|
|
||||||
if ($firstThinkLoop) {
|
|
||||||
yield $this->systemMsg("Denke nach...", "think");
|
|
||||||
$firstThinkLoop = false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vollständige Antwort weiter sammeln (für History)
|
|
||||||
$fullOutput .= $cleanToken;
|
|
||||||
|
|
||||||
// ⬇️ Token in Chunker geben
|
|
||||||
$chunk = $chunker->push($cleanToken);
|
|
||||||
if ($chunk !== null) {
|
|
||||||
yield $this->systemMsg($chunk, 'answer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⬇️ Rest flushen
|
|
||||||
$finalChunk = $chunker->flush();
|
|
||||||
if ($finalChunk !== null) {
|
|
||||||
yield $this->systemMsg($finalChunk, 'answer');
|
|
||||||
} elseif ($fullOutput === '') {
|
|
||||||
yield $this->systemMsg('❌ Es wurden keine Daten vom LLM empfangen.', 'err');
|
|
||||||
}
|
|
||||||
|
|
||||||
if($sources){
|
|
||||||
yield $this->systemMsg("Quellen: ".implode(' ',$sources), 'info');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 7) Persist conversation history
|
// 7) Persist conversation history
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
$this->contextService->appendHistory(
|
if ($fullOutput !== '') {
|
||||||
$userId,
|
$this->contextService->appendHistory(
|
||||||
$prompt,
|
$userId,
|
||||||
$fullOutput
|
$prompt,
|
||||||
);
|
$fullOutput
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$this->agentLogger->info('Agent run finished', [
|
$this->agentLogger->info('Agent run finished', [
|
||||||
'userId' => $userId,
|
'userId' => $userId,
|
||||||
'outputLength' => mb_strlen($fullOutput),
|
'outputLength' => mb_strlen($fullOutput),
|
||||||
'contextMode' => 'recent',
|
'contextMode' => $includeFullContext ? 'full' : 'recent',
|
||||||
'commerceIntent' => $commerceIntent,
|
'commerceIntent' => $commerceIntent,
|
||||||
'shopResultsCount' => count($shopResults),
|
'shopResultsCount' => count($shopResults),
|
||||||
|
'knowledgeChunkCount' => count($knowledgeChunks),
|
||||||
|
'hasUrlContent' => $urlContent !== '',
|
||||||
|
'usedOptimizedShopQuery' => $optimizedShopQuery !== '',
|
||||||
]);
|
]);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$this->agentLogger->error('Agent run failed', [
|
$this->agentLogger->error('Agent run failed', [
|
||||||
@@ -243,10 +186,168 @@ final readonly class AgentRunner
|
|||||||
'exception' => $e,
|
'exception' => $e,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
yield $this->systemMsg("\n❌ An internal error occurred while processing the request. \nError: " . $e->getMessage(), 'err');
|
yield $this->systemMsg($this->buildUserErrorMessage($e), 'err');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function detectCommerceIntent(string $prompt): string
|
||||||
|
{
|
||||||
|
$commerceMeta = $this->commerceIntentLite->detect($prompt);
|
||||||
|
|
||||||
|
return (string) ($commerceMeta['intent'] ?? CommerceIntentLite::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isCommerceIntent(string $commerceIntent): bool
|
||||||
|
{
|
||||||
|
return $commerceIntent === CommerceIntentLite::PRODUCT_SEARCH
|
||||||
|
|| $commerceIntent === CommerceIntentLite::ADVISORY_PRODUCT_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildOptimizedShopQuery(string $prompt, string $userId): string
|
||||||
|
{
|
||||||
|
$shopPrompt = trim($this->agentRunnerConfig->getShopPrompt($prompt));
|
||||||
|
|
||||||
|
if ($shopPrompt === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$optimizedQuery = '';
|
||||||
|
$this->thinkSuppressor->reset();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($this->ollamaClient->stream($shopPrompt) as $token) {
|
||||||
|
if (!is_string($token)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleanToken = $this->thinkSuppressor->filter($token);
|
||||||
|
|
||||||
|
if ($cleanToken === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$optimizedQuery .= $cleanToken;
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$this->agentLogger->warning('Shop query optimization failed, falling back to original prompt', [
|
||||||
|
'userId' => $userId,
|
||||||
|
'exception' => $e,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($optimizedQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function searchShop(string $query, string $commerceIntent, string $userId): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->shopSearchService->search($query, $commerceIntent);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$this->agentLogger->warning('Shop search failed, continuing without shop results', [
|
||||||
|
'userId' => $userId,
|
||||||
|
'commerceIntent' => $commerceIntent,
|
||||||
|
'query' => $query,
|
||||||
|
'exception' => $e,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function limitKnowledgeChunks(array $knowledgeChunks, string $commerceIntent): array
|
||||||
|
{
|
||||||
|
return match ($commerceIntent) {
|
||||||
|
CommerceIntentLite::PRODUCT_SEARCH => array_slice($knowledgeChunks, 0, 2),
|
||||||
|
CommerceIntentLite::ADVISORY_PRODUCT_SEARCH => array_slice($knowledgeChunks, 0, 3),
|
||||||
|
default => $knowledgeChunks,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Generator<int, string, mixed, string>
|
||||||
|
*/
|
||||||
|
private function streamFinalAnswer(string $finalPrompt): Generator
|
||||||
|
{
|
||||||
|
$fullOutput = '';
|
||||||
|
$firstThinkLoop = true;
|
||||||
|
$chunker = new StreamChunker();
|
||||||
|
|
||||||
|
$this->thinkSuppressor->reset();
|
||||||
|
|
||||||
|
foreach ($this->ollamaClient->stream($finalPrompt) as $token) {
|
||||||
|
if (!is_string($token)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleanToken = $this->thinkSuppressor->filter($token);
|
||||||
|
|
||||||
|
if ($cleanToken === '') {
|
||||||
|
if ($firstThinkLoop) {
|
||||||
|
yield $this->systemMsg('Denke nach...', 'think');
|
||||||
|
$firstThinkLoop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fullOutput .= $cleanToken;
|
||||||
|
|
||||||
|
$chunk = $chunker->push($cleanToken);
|
||||||
|
if ($chunk !== null) {
|
||||||
|
yield $this->systemMsg($chunk, 'answer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$finalChunk = $chunker->flush();
|
||||||
|
if ($finalChunk !== null) {
|
||||||
|
yield $this->systemMsg($finalChunk, 'answer');
|
||||||
|
} elseif ($fullOutput === '') {
|
||||||
|
yield $this->systemMsg('❌ Es wurden keine Daten vom LLM empfangen.', 'err');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fullOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $sources
|
||||||
|
*/
|
||||||
|
private function emitSources(array $sources, string $prefix): string
|
||||||
|
{
|
||||||
|
return $this->systemMsg($prefix . implode(' ', $sources), 'info');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $sources
|
||||||
|
*/
|
||||||
|
private function addSource(array &$sources, string $label): void
|
||||||
|
{
|
||||||
|
$badge = $this->badge($label);
|
||||||
|
|
||||||
|
if (!in_array($badge, $sources, true)) {
|
||||||
|
$sources[] = $badge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildUserErrorMessage(Throwable $e): string
|
||||||
|
{
|
||||||
|
if (!$this->debug) {
|
||||||
|
return '❌ Bei der Verarbeitung der Anfrage ist ein interner Fehler aufgetreten.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '❌ Interner Fehler: '
|
||||||
|
. htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function badge(string $label): string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'<span class="badge bg-info text-black">%s</span>',
|
||||||
|
htmlspecialchars($label, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function systemMsg(string $msg, string $type = ''): string
|
private function systemMsg(string $msg, string $type = ''): string
|
||||||
{
|
{
|
||||||
if (!$this->systemMsgOn) {
|
if (!$this->systemMsgOn) {
|
||||||
@@ -254,10 +355,11 @@ final readonly class AgentRunner
|
|||||||
}
|
}
|
||||||
|
|
||||||
return match ($type) {
|
return match ($type) {
|
||||||
'answer' => '' . $msg,
|
'answer' => $msg,
|
||||||
'err' => '<span class="text-danger">' . $msg . "</span>\n<hr>\n",
|
'err' => '<span class="text-danger">' . $msg . "</span>\n<hr>\n",
|
||||||
'think' => '<span class="text-info think">' . $msg . "</span>\n",
|
'think' => '<span class="text-info think">' . $msg . "</span>\n",
|
||||||
'info' => "\n\n<span class=\"text-info fw-bolder\">" . $msg . "</span>\n"
|
'info' => "\n\n<span class=\"text-info fw-bolder\">" . $msg . "</span>\n",
|
||||||
|
default => $msg,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ class AgentRunnerConfig
|
|||||||
- Preserve product names, brands, model numbers, and compound terms exactly if they are relevant.
|
- Preserve product names, brands, model numbers, and compound terms exactly if they are relevant.
|
||||||
- Numbers that belong to a product name or model must be preserved (e.g. Indikator 300, Testomat 808, Testomat 2000).
|
- Numbers that belong to a product name or model must be preserved (e.g. Indikator 300, Testomat 808, Testomat 2000).
|
||||||
- Separate terms using spaces only.
|
- Separate terms using spaces only.
|
||||||
|
- If a relevant product name is present, it must be placed at the beginning of the final search query.
|
||||||
|
|
||||||
Output format:
|
Output format:
|
||||||
Keyword1 Keyword2 Keyword3
|
Keyword1 Keyword2 Keyword3
|
||||||
|
|||||||
Reference in New Issue
Block a user