优化:完善百家乐AI决策,增加历史记录上下文、底部仓位预留与智能观望广播功能
This commit is contained in:
@@ -43,16 +43,16 @@ class BaccaratPredictionService
|
||||
private ?int $aiUserId = null;
|
||||
|
||||
/**
|
||||
* 调用 AI 接口预测百家乐下注方向
|
||||
* 调用 AI 接口出具下注策略并预测百家乐方向
|
||||
*
|
||||
* 优先使用 PREFERRED_MODEL 对应的厂商,调用失败时自动按
|
||||
* sort_order 依次尝试其余已启用厂商(与 AiChatService 故障转移逻辑一致)。
|
||||
* 所有厂商均失败时返回 null,由调用方回退本地路单决策。
|
||||
*
|
||||
* @param array<int, string> $recentResults 近期已结算路单('big'|'small'|'triple',从最新到最旧)
|
||||
* @return string|null 预测结果:'big'|'small'|'triple',或 null(AI 全部不可用)
|
||||
* @param array $context 包含路单、资金以及历史下注记录的上下文数组
|
||||
* @return array|null 策略:['action' => '...', 'percentage' => 1-100, 'reason' => '...'] 或 null (不可用)
|
||||
*/
|
||||
public function predict(array $recentResults): ?string
|
||||
public function predict(array $context): ?array
|
||||
{
|
||||
// 获取所有已启用厂商,PREFERRED_MODEL 对应的排在最前面
|
||||
$providers = AiProviderConfig::getEnabledProviders();
|
||||
@@ -71,7 +71,7 @@ class BaccaratPredictionService
|
||||
->values(); // 重置索引,消除 IDE 类型推断警告
|
||||
}
|
||||
|
||||
$prompt = $this->buildPredictionPrompt($recentResults);
|
||||
$prompt = $this->buildPredictionPrompt($context);
|
||||
|
||||
// 依次尝试每个厂商,失败则切换下一个
|
||||
foreach ($providers as $provider) {
|
||||
@@ -102,14 +102,18 @@ class BaccaratPredictionService
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建发送给 AI 的预测提示词
|
||||
* 构建发送给 AI 的预测策略提示词
|
||||
*
|
||||
* 将路单数据转为人类可读格式,要求 AI 仅回复三种固定关键词之一。
|
||||
* 将路单数据转为人类可读格式,要求 AI 必须以特定 JSON 输出策略。
|
||||
*
|
||||
* @param array<int, string> $recentResults 路单数组
|
||||
* @param array $context 上下文数据
|
||||
*/
|
||||
private function buildPredictionPrompt(array $recentResults): string
|
||||
private function buildPredictionPrompt(array $context): string
|
||||
{
|
||||
$recentResults = $context['recent_results'] ?? [];
|
||||
$availableGold = $context['available_gold'] ?? 0;
|
||||
$history = $context['historical_bets'] ?? [];
|
||||
|
||||
// 将英文 key 转为中文展示,方便 AI 理解
|
||||
$labelMap = ['big' => '大', 'small' => '小', 'triple' => '豹子'];
|
||||
|
||||
@@ -122,20 +126,43 @@ class BaccaratPredictionService
|
||||
$roadmapText = implode(' → ', array_reverse($roadmap));
|
||||
$total = count($recentResults);
|
||||
|
||||
$historyText = '';
|
||||
if (empty($history)) {
|
||||
$historyText = '暂无下注历史记录。';
|
||||
} else {
|
||||
foreach ($history as $bet) {
|
||||
$betAction = $labelMap[$bet['bet_type']] ?? $bet['bet_type'];
|
||||
$profitText = $bet['profit'] > 0 ? "赢 +{$bet['profit']}" : "负 {$bet['profit']}";
|
||||
$historyText .= "- 第 {$bet['round_id']} 局:押 {$betAction} {$bet['amount']} 金币,结果:{$profitText}\n";
|
||||
}
|
||||
}
|
||||
|
||||
return <<<PROMPT
|
||||
你是一位百家乐路单分析专家。请根据以下最近 {$total} 局的开奖路单,预测下一局最可能的结果。
|
||||
你是一位百家乐投资规划与路单分析专家。请根据以下最近 {$total} 局的神奇开奖路单,以及你目前的可用资金和输赢历史,给出下一局最科学的下注策略。
|
||||
|
||||
路单(从最早到最新):{$roadmapText}
|
||||
【你的当前资产状态】
|
||||
可用金币余额:{$availableGold}
|
||||
|
||||
注意:
|
||||
【你的最近下注历史(前两把最新)】
|
||||
{$historyText}
|
||||
|
||||
【全服近期开奖路单(从最早到最新)】
|
||||
{$roadmapText}
|
||||
|
||||
【规则注意】
|
||||
- "大" 表示骰子总点数为 11~17
|
||||
- "小" 表示骰子总点数为 4~10
|
||||
- "豹子" 表示三颗骰子点数相同(极小概率)
|
||||
|
||||
请综合分析路单走势(如连庄、跳变、交替等特征),仅输出以下三个词之一:
|
||||
大 / 小 / 豹子
|
||||
请综合分析路单走势(如长龙跟进、跳变斩龙、交替特征),结合止损止盈策略(比如连输必须观望休息,连赢适当增加仓位百分比)。
|
||||
|
||||
【重要】只输出单个词,不要包含任何其他文字、标点、换行或解释。
|
||||
【重要!必须输出标准严格的 JSON 格式】
|
||||
你只能返回这样一个纯 JSON 字符串,包含三个字段,绝对不能加其他文字说明(支持 markdown code 块包裹):
|
||||
{
|
||||
"action": "big", // 只能在这四个词中选一个: big (大) / small (小) / triple (豹子) / pass (我不确定,这把空仓观望)
|
||||
"percentage": 5, // 这把下注你可用金币的百分比,取值范围整数 1 到 10。如果选择 pass,则必须填 0。
|
||||
"reason": "由于当前连续长龙且我方刚刚经历了连败,为了控制波段回撤我决定采取保守策略观望一下。" // 一句简单口语化、生动活泼的中文分析理由
|
||||
}
|
||||
PROMPT;
|
||||
}
|
||||
|
||||
@@ -144,11 +171,11 @@ PROMPT;
|
||||
*
|
||||
* @param AiProviderConfig $config AI 厂商配置
|
||||
* @param string $prompt 预测提示词
|
||||
* @return string 预测结果:'big'|'small'|'triple'
|
||||
* @return array 策略结果 ['action', 'percentage', 'reason']
|
||||
*
|
||||
* @throws \Exception 调用失败或无法解析结果时抛出
|
||||
*/
|
||||
private function callProvider(AiProviderConfig $config, string $prompt): string
|
||||
private function callProvider(AiProviderConfig $config, string $prompt): array
|
||||
{
|
||||
$startTime = microtime(true);
|
||||
$apiKey = $config->getDecryptedApiKey();
|
||||
@@ -164,12 +191,13 @@ PROMPT;
|
||||
->timeout(self::REQUEST_TIMEOUT)
|
||||
->post($endpoint, [
|
||||
'model' => $config->model,
|
||||
'temperature' => 0.3, // 预测任务偏确定性,使用较低温度
|
||||
'max_tokens' => 50, // 非推理模型只需输出单词,50 足够;推理模型请调高此值
|
||||
'temperature' => 0.6, // 预测任务需要生成推理和灵活评估,适当提高温度
|
||||
'max_tokens' => 300, // 输出 JSON 包含 reason,加大 token 限制
|
||||
'response_format' => ['type' => 'json_object'], // 强制平台尝试规范 JSON 输出(对某些模型自动生效)
|
||||
'messages' => [
|
||||
[
|
||||
'role' => 'system',
|
||||
'content' => '你是百家乐路单分析专家,只输出"大"、"小"或"豹子"三个词之一,不输出任何其他内容。',
|
||||
'content' => '你是百家乐资金策略专家,严格遵守只返回合格格式的 JSON 字符串。',
|
||||
],
|
||||
[
|
||||
'role' => 'user',
|
||||
@@ -213,24 +241,41 @@ PROMPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 AI 回复的中文词解析为系统内部下注键
|
||||
* 将 AI 回复的纯文本解析为内部的结构化策略数组
|
||||
*
|
||||
* @param string $reply AI 返回的原始文本
|
||||
* @return string 'big'|'small'|'triple'
|
||||
* @param string $reply AI 返回的原始文本 (可能带 Markdown 或推理过程)
|
||||
* @return array ['action', 'percentage', 'reason']
|
||||
*
|
||||
* @throws \Exception 无法识别时抛出
|
||||
* @throws \Exception 无法识别或格式错误时抛出
|
||||
*/
|
||||
private function parseReply(string $reply): string
|
||||
private function parseReply(string $reply): array
|
||||
{
|
||||
// 从回复中提取核心词(容忍多余空白/标点)
|
||||
$cleaned = trim(preg_replace('/\s+/', '', $reply));
|
||||
// 尝试剔除 Markdown JSON 块标签
|
||||
$jsonStr = preg_replace('/```json\s*(.*?)\s*```/s', '$1', $reply);
|
||||
$jsonStr = preg_replace('/```\s*(.*?)\s*```/s', '$1', $jsonStr);
|
||||
$jsonStr = trim($jsonStr);
|
||||
|
||||
return match ($cleaned) {
|
||||
'大' => 'big',
|
||||
'小' => 'small',
|
||||
'豹子' => 'triple',
|
||||
default => throw new \Exception("AI 返回无法识别的预测结果:{$reply}"),
|
||||
};
|
||||
// 如果 AI 有不可预测的开头结尾文字,可以尝试用正则仅抓取首尾的大括号
|
||||
if (preg_match('/\{.*\}/s', $jsonStr, $matches)) {
|
||||
$jsonStr = $matches[0];
|
||||
}
|
||||
|
||||
$decoded = json_decode($jsonStr, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($decoded)) {
|
||||
throw new \Exception("AI 返回的 JSON 格式非法无法解析:{$reply}");
|
||||
}
|
||||
|
||||
$action = $decoded['action'] ?? null;
|
||||
if (! in_array($action, ['big', 'small', 'triple', 'pass'], true)) {
|
||||
throw new \Exception("AI 返回了不合法的 action 指令:{$action}");
|
||||
}
|
||||
|
||||
return [
|
||||
'action' => $action,
|
||||
'percentage' => max(0, min(10, (int) ($decoded['percentage'] ?? 0))),
|
||||
'reason' => $decoded['reason'] ?? '局势迷离,默默操作',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user