本文目录导读:

在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。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播