deploy: auto commit server changes 2026-06-12 17:18:58

This commit is contained in:
hajimi
2026-06-12 17:18:58 +08:00
parent de2a36107a
commit b9669269b5
90 changed files with 1504 additions and 85 deletions
@@ -0,0 +1,148 @@
<?php
namespace app\common\service\ai;
use app\common\model\ai\AiConfig;
class AiAssistantWebNewsRetriever
{
public static function retrieve(string $message, array $plan, array $historyMessages = []): array
{
if (!self::shouldRetrieve($message, $plan)) {
return [];
}
$client = new DeepSeekClient();
$result = $client->chatJson(self::systemPrompt(), self::userPrompt($message, $plan, $historyMessages), self::modelOptions());
if (empty($result['success']) || !is_array($result['parsed'] ?? null)) {
return [];
}
return self::normalizeItems($result['parsed'], $message);
}
private static function shouldRetrieve(string $message, array $plan): bool
{
if ((int) AiConfig::getVal('assistant_enable_web_news_search', '1') !== 1) {
return false;
}
$intent = (string) ($plan['intent'] ?? '');
$preferredDomain = (string) ($plan['preferred_domain'] ?? '');
$domains = (array) ($plan['domains'] ?? []);
if (in_array($intent, ['crypto_quote', 'match_schedule', 'match_result', 'lottery_analysis'], true)) {
return false;
}
if ($preferredDomain === 'article' || in_array('article', $domains, true) || in_array('post', $domains, true)) {
return true;
}
return (bool) preg_match('/资讯|新闻|消息|报道|热点|刚刚|最新|头条/u', $message);
}
private static function systemPrompt(): string
{
$today = date('Y-m-d');
return '你是世博头条 AI 助手的联网资讯检索器。'
. '请调用 web search 工具搜索与用户问题最相关、尽量最新的体育资讯或新闻。'
. '今天日期是 ' . $today . '。'
. '只输出 JSON{"items":[{"title":"","source":"","url":"","summary":"","published_at":""}]}。'
. '要求:最多返回 5 条;title 和 url 必填;summary 只写 1-2 句;没有可靠结果时返回 {"items":[]}。';
}
private static function userPrompt(string $message, array $plan, array $historyMessages): string
{
$history = [];
foreach (array_slice($historyMessages, -4) as $row) {
$content = trim((string) ($row['content'] ?? ''));
if ($content === '') {
continue;
}
$history[] = [
'role' => (string) ($row['role'] ?? 'user'),
'content' => mb_substr($content, 0, 200),
];
}
return "用户问题:\n"
. $message
. "\n\n查询规划:\n"
. json_encode([
'intent' => $plan['intent'] ?? '',
'rewritten_query' => $plan['rewritten_query'] ?? '',
'alternate_queries' => $plan['alternate_queries'] ?? [],
'entities' => $plan['entities'] ?? [],
'date_range' => $plan['date_range'] ?? [],
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
. "\n\n最近对话:\n"
. json_encode($history, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
. "\n\n请检索最新外网资讯并输出 JSON。";
}
private static function modelOptions(): array
{
return DeepSeekClient::defaultModelOptions([
'max_tokens' => 900,
'temperature' => 0.2,
'retry_times' => 1,
'retry_sleep_ms' => 600,
'tools' => [
['type' => 'web_search'],
],
'tool_choice' => 'required',
'include' => ['web_search_call.action.sources'],
]);
}
private static function normalizeItems(array $parsed, string $message): array
{
$items = [];
foreach ((array) ($parsed['items'] ?? []) as $row) {
$title = trim((string) ($row['title'] ?? ''));
$url = trim((string) ($row['url'] ?? ''));
if ($title === '' || $url === '' || !preg_match('#^https?://#i', $url)) {
continue;
}
$sourceName = trim((string) ($row['source'] ?? ''));
$publishedAt = trim((string) ($row['published_at'] ?? ''));
$summary = trim((string) ($row['summary'] ?? ''));
$contextSummary = trim(implode(' ', array_filter([
$sourceName !== '' ? '来源:' . $sourceName : '',
$publishedAt !== '' ? '时间:' . $publishedAt : '',
$summary,
])));
$items[] = [
'context' => [
'domain' => 'web_news',
'title' => $title,
'summary' => $contextSummary !== '' ? $contextSummary : $message,
'url' => $url,
'source' => $sourceName,
'published_at' => $publishedAt,
],
'source' => [
'type' => 'web_news',
'title' => $title,
'summary' => self::clipText($contextSummary !== '' ? $contextSummary : $summary, 140),
'path' => '/pages/webview/webview?url=' . urlencode($url),
'source_id' => 0,
],
];
}
return array_slice($items, 0, 5);
}
private static function clipText(string $text, int $length): string
{
$text = trim(preg_replace('/\s+/u', ' ', $text) ?: '');
if (mb_strlen($text) <= $length) {
return $text;
}
return mb_substr($text, 0, $length) . '...';
}
}