PHP项目对话上下文如何拼接传递大模型

wen PHP项目 31

本文目录导读:

PHP项目对话上下文如何拼接传递大模型

  1. 核心数据结构(OpenAI格式)
  2. 上下文存储方案
  3. Token优化策略
  4. 完整调用示例
  5. 注意事项

在PHP项目中向大模型传递对话上下文,核心是维护一个结构化的消息数组,并按大模型的接口规范(通常是OpenAI格式)进行拼接,以下是几种常见的实现方案:

核心数据结构(OpenAI格式)

// 对话历史结构
$messages = [
    ['role' => 'system', 'content' => '你是一个AI助手'],
    ['role' => 'user', 'content' => '你好'],
    ['role' => 'assistant', 'content' => '你好!有什么可以帮助你的?'],
    ['role' => 'user', 'content' => '今天天气怎么样']
];

上下文存储方案

Session存储(简单场景)

session_start();
// 初始化或恢复对话
if (!isset($_SESSION['messages'])) {
    $_SESSION['messages'] = [
        ['role' => 'system', 'content' => '你是一个专业的技术支持']
    ];
}
// 添加用户新消息
$_SESSION['messages'][] = [
    'role' => 'user', 
    'content' => $_POST['message']
];
// 调用大模型
$response = callLLM($_SESSION['messages']);
// 保存AI回复
$_SESSION['messages'][] = [
    'role' => 'assistant',
    'content' => $response
];
// 限制上下文长度(避免Token溢出)
function trimContext(&$messages, $maxLength = 20) {
    if (count($messages) > $maxLength) {
        // 保留系统提示和最近的消息
        $system = array_shift($messages);
        $messages = array_slice($messages, -$maxLength + 1);
        array_unshift($messages, $system);
    }
}

数据库存储(持久化)

// 数据库表结构
// conversations: id, user_id, created_at
// messages: id, conversation_id, role, content, created_at
class ConversationManager {
    private $db;
    public function getContext($conversationId, $maxTokens = 4000) {
        $messages = $this->db->query(
            "SELECT role, content FROM messages 
             WHERE conversation_id = ? 
             ORDER BY created_at ASC",
            [$conversationId]
        );
        // 计算token大致数量并裁剪
        return $this->trimToTokenLimit($messages, $maxTokens);
    }
    private function trimToTokenLimit($messages, $maxTokens) {
        $totalTokens = 0;
        $trimmed = [];
        // 从最新的消息开始保留
        foreach (array_reverse($messages) as $msg) {
            $tokens = strlen($msg['content']) / 4; // 粗略估算
            if ($totalTokens + $tokens > $maxTokens) break;
            $totalTokens += $tokens;
            array_unshift($trimmed, $msg);
        }
        // 确保有系统提示
        if ($messages[0]['role'] === 'system' && !in_array($messages[0], $trimmed)) {
            array_unshift($trimmed, $messages[0]);
        }
        return $trimmed;
    }
}

Redis缓存(高性能)

class RedisConversation {
    private $redis;
    private $prefix = 'conversation:';
    public function addMessage($sessionId, $message) {
        $key = $this->prefix . $sessionId;
        // 使用List存储消息
        $this->redis->rPush($key, json_encode($message));
        // 设置过期时间(如1小时)
        $this->redis->expire($key, 3600);
        // 限制消息数量
        $length = $this->redis->lLen($key);
        if ($length > 50) { // 保留最近50条
            $this->redis->lPop($key);
        }
    }
    public function getContext($sessionId) {
        $key = $this->prefix . $sessionId;
        $messages = $this->redis->lRange($key, 0, -1);
        return array_map(function($msg) {
            return json_decode($msg, true);
        }, $messages);
    }
}

Token优化策略

智能上下文压缩

class ContextOptimizer {
    // 摘要历史对话
    public static function summarize($messages, $llmClient) {
        $recentMessages = array_slice($messages, -10); // 保留最近10条
        $historyToSummarize = array_slice($messages, 0, -10);
        if (empty($historyToSummarize)) return $messages;
        // 生成摘要
        $summaryPrompt = [
            ['role' => 'system', 'content' => '请用中文总结以下对话的核心内容和关键信息:'],
            ['role' => 'user', 'content' => json_encode($historyToSummarize)]
        ];
        $summary = $llmClient->chat($summaryPrompt);
        // 构建新的上下文
        return array_merge(
            [['role' => 'system', 'content' => "历史对话摘要:{$summary}"]],
            $recentMessages
        );
    }
    // 滑动窗口裁剪
    public static function slidingWindow($messages, $maxTokens = 4000) {
        $systemPrompt = null;
        if ($messages[0]['role'] === 'system') {
            $systemPrompt = array_shift($messages);
        }
        $totalTokens = 0;
        $context = [];
        // 从最新消息开始添加
        foreach (array_reverse($messages) as $msg) {
            $tokens = self::estimateTokens(json_encode($msg));
            if ($totalTokens + $tokens > $maxTokens) break;
            $totalTokens += $tokens;
            array_unshift($context, $msg);
        }
        if ($systemPrompt) {
            array_unshift($context, $systemPrompt);
        }
        return $context;
    }
    private static function estimateTokens($text) {
        // 更精确的token估算
        $words = preg_split('/\s+/', $text);
        $chars = mb_strlen($text, 'UTF-8');
        // 中文字符约占1.5 tokens,英文单词约1 token
        return ceil($chars * 0.75 + count($words) * 0.25);
    }
}

完整调用示例

class LLMClient {
    private $apiKey;
    private $apiUrl = 'https://api.openai.com/v1/chat/completions';
    public function chat($messages) {
        $data = [
            'model' => 'gpt-3.5-turbo',
            'messages' => $messages,
            'temperature' => 0.7
        ];
        $ch = curl_init($this->apiUrl);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $this->apiKey,
                'Content-Type: application/json'
            ],
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_RETURNTRANSFER => true
        ]);
        $response = curl_exec($ch);
        return json_decode($response, true)['choices'][0]['message']['content'];
    }
}
// 使用示例
$userId = $_SESSION['user_id'];
$conversationManager = new ConversationManager($db);
$optimizer = new ContextOptimizer();
// 获取历史上下文
$messages = $conversationManager->getContext($userId);
// 添加用户新消息
$messages[] = ['role' => 'user', 'content' => $_POST['message']];
// 优化上下文(如果必要)
if (count($messages) > 30) {
    $messages = $optimizer->slidingWindow($messages, 4000);
}
// 调用大模型
$llm = new LLMClient();
$response = $llm->chat($messages);
// 保存AI回复
$messages[] = ['role' => 'assistant', 'content' => $response];
$conversationManager->saveMessages($userId, $messages);

注意事项

  • Token限制:大模型有上下文窗口限制(如4K/8K/16K),需要合理裁剪
  • 角色标识:确保role字段正确(system/user/assistant)
  • 会话隔离:不同用户用不同sessionId/ConversationId
  • 安全过滤:对用户输入进行XSS、SQL注入等安全过滤
  • 性能考虑:长对话建议使用Redis或异步处理

选择哪种方案取决于你的具体需求:简单的单用户场景用Session,需要持久化的用数据库,高并发场景用Redis。

抱歉,评论功能暂时关闭!