戒酒的李白

A more comprehensive large model analysis setup, front-end interface optimization.

... ... @@ -14,9 +14,26 @@ class AIAnalyzer:
openai.api_key = self.api_key
# 系统提示词,限制AI的输出格式
self.system_prompt = """你是一个专业的舆情分析助手。你的任务是分析每条消息的情感倾向、关键词和潜在影响。
请严格按照以下JSON格式返回分析结果:
# 不同深度的分析提示词
self.prompt_templates = {
'basic': """你是一个专业的舆情分析助手。请对每条消息进行基础的情感分析。
请按以下JSON格式返回:
{
"analysis_results": [
{
"message_id": "消息ID",
"sentiment": "情感倾向 (积极/消极/中性)",
"sentiment_score": "情感分数 (0-1)",
"keywords": ["关键词1", "关键词2"],
"key_points": "简要概述",
"influence_analysis": "基础影响分析",
"risk_level": "风险等级 (低/中/高)",
"timestamp": "分析时间戳"
}
]
}""",
'standard': """你是一个专业的舆情分析助手。请对每条消息进行标准深度的分析。
请按以下JSON格式返回:
{
"analysis_results": [
{
... ... @@ -30,41 +47,69 @@ class AIAnalyzer:
"timestamp": "分析时间戳"
}
]
}
请确保每个字段都有值,并保持JSON格式的一致性。"""
}""",
'deep': """你是一个专业的舆情分析助手。请对每条消息进行深度分析。
请按以下JSON格式返回:
{
"analysis_results": [
{
"message_id": "消息ID",
"sentiment": "情感倾向 (积极/消极/中性)",
"sentiment_score": "情感分数 (0-1)",
"keywords": ["关键词1", "关键词2", "关键词3", "关键词4", "关键词5"],
"key_points": "详细的核心观点分析",
"influence_analysis": "深度影响分析,包括短期和长期影响",
"risk_factors": ["风险因素1", "风险因素2", "风险因素3"],
"risk_level": "风险等级 (低/中/高)",
"suggestions": ["建议1", "建议2", "建议3"],
"timestamp": "分析时间戳"
}
]
}"""
}
async def analyze_messages(self, messages: List[Dict]) -> List[Dict]:
async def analyze_messages(self, messages: List[Dict], batch_size: int = 50,
model_type: str = "gpt-3.5-turbo",
analysis_depth: str = "standard") -> List[Dict]:
"""分析一批消息并返回分析结果"""
try:
# 构建输入消息
formatted_messages = []
for msg in messages:
formatted_messages.append(f"消息ID: {msg['id']}\n内容: {msg['content']}")
messages_text = "\n---\n".join(formatted_messages)
# 调用OpenAI API
response = await openai.ChatCompletion.acreate(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": f"请分析以下消息:\n{messages_text}"}
],
temperature=0.3, # 降低随机性
max_tokens=2000,
n=1
)
all_results = []
# 解析返回结果
try:
result = json.loads(response.choices[0].message.content)
# 验证结果格式
if not isinstance(result, dict) or 'analysis_results' not in result:
raise ValueError("AI返回格式不正确")
return result['analysis_results']
except json.JSONDecodeError:
logging.error("AI返回结果解析失败")
return []
# 分批处理消息
for i in range(0, len(messages), batch_size):
batch = messages[i:i + batch_size]
formatted_messages = []
for msg in batch:
formatted_messages.append(f"消息ID: {msg['id']}\n内容: {msg['content']}")
messages_text = "\n---\n".join(formatted_messages)
# 获取对应深度的提示词
system_prompt = self.prompt_templates.get(analysis_depth, self.prompt_templates['standard'])
# 调用OpenAI API
response = await openai.ChatCompletion.acreate(
model=model_type,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"请分析以下消息:\n{messages_text}"}
],
temperature=0.3, # 降低随机性
max_tokens=2000 if analysis_depth != 'deep' else 3000,
n=1
)
try:
result = json.loads(response.choices[0].message.content)
if isinstance(result, dict) and 'analysis_results' in result:
all_results.extend(result['analysis_results'])
else:
logging.error(f"API返回格式不正确: {response.choices[0].message.content}")
except json.JSONDecodeError as e:
logging.error(f"JSON解析失败: {e}")
continue
return all_results
except Exception as e:
logging.error(f"AI分析过程出错: {e}")
... ... @@ -72,7 +117,7 @@ class AIAnalyzer:
def format_analysis_for_display(self, analysis: Dict) -> Dict:
"""将分析结果格式化为前端显示格式"""
return {
base_result = {
'id': analysis['message_id'],
'sentiment': analysis['sentiment'],
'sentiment_score': f"{float(analysis['sentiment_score']):.2%}",
... ... @@ -84,6 +129,15 @@ class AIAnalyzer:
float(analysis['timestamp'])
).strftime('%Y-%m-%d %H:%M:%S')
}
# 如果是深度分析,添加额外信息
if 'risk_factors' in analysis:
base_result.update({
'risk_factors': analysis['risk_factors'],
'suggestions': analysis['suggestions']
})
return base_result
# 创建全局AI分析器实例
ai_analyzer = AIAnalyzer()
\ No newline at end of file
... ...
... ... @@ -308,11 +308,33 @@ def articleChar(id):
@pb.route('/api/analyze_messages', methods=['POST'])
async def analyze_messages():
try:
# 获取最近50条消息
messages = getRecentMessages(50) # 需要实现这个函数
# 获取请求参数
data = request.get_json()
batch_size = data.get('batch_size', 50)
model_type = data.get('model_type', 'gpt-3.5-turbo')
analysis_depth = data.get('analysis_depth', 'standard')
# 获取最近的消息
messages = getRecentMessages(batch_size)
if not messages:
return jsonify({
'success': False,
'error': '没有找到需要分析的消息'
}), 404
# 调用AI进行分析
analysis_results = await ai_analyzer.analyze_messages(messages)
analysis_results = await ai_analyzer.analyze_messages(
messages=messages,
batch_size=batch_size,
model_type=model_type,
analysis_depth=analysis_depth
)
if not analysis_results:
return jsonify({
'success': False,
'error': '分析过程中出现错误'
}), 500
# 保存到数据库
with Session(engine) as session:
... ... @@ -337,7 +359,14 @@ async def analyze_messages():
return jsonify({
'success': True,
'data': display_results
'data': display_results,
'meta': {
'total_messages': len(messages),
'analyzed_messages': len(analysis_results),
'batch_size': batch_size,
'model_type': model_type,
'analysis_depth': analysis_depth
}
})
except Exception as e:
... ...
... ... @@ -454,11 +454,74 @@
<div class="header-title">
<h4 class="card-title">AI深度分析</h4>
</div>
<button class="btn btn-primary" onclick="requestAIAnalysis()">
开始AI分析
</button>
<div class="analysis-controls">
<!-- 批量处理设置 -->
<div class="d-flex align-items-center">
<div class="form-group mx-2 mb-0">
<select id="batchSize" class="form-control form-control-sm">
<option value="10">每批10条</option>
<option value="20">每批20条</option>
<option value="50" selected>每批50条</option>
<option value="100">每批100条</option>
</select>
</div>
<div class="form-group mx-2 mb-0">
<select id="modelType" class="form-control form-control-sm">
<option value="gpt-3.5-turbo" selected>GPT-3.5</option>
<option value="gpt-4">GPT-4</option>
</select>
</div>
<div class="form-group mx-2 mb-0">
<select id="analysisDepth" class="form-control form-control-sm">
<option value="basic">基础分析</option>
<option value="standard" selected>标准分析</option>
<option value="deep">深度分析</option>
</select>
</div>
<div class="custom-control custom-switch mx-2">
<input type="checkbox" class="custom-control-input" id="autoUpdate">
<label class="custom-control-label" for="autoUpdate">自动更新</label>
</div>
<button class="btn btn-primary btn-sm" onclick="requestAIAnalysis()">
开始分析
</button>
</div>
</div>
</div>
<div class="card-body">
<!-- 进度显示 -->
<div id="analysis-progress" class="mb-3" style="display: none;">
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
</div>
<small class="text-muted mt-1 d-block">正在分析: <span id="progress-text">0/0</span></small>
</div>
<!-- 分析结果过滤器 -->
<div class="mb-3">
<div class="d-flex align-items-center">
<div class="form-group mb-0 mr-2">
<input type="text" class="form-control form-control-sm" id="keywordFilter" placeholder="按关键词过滤">
</div>
<div class="form-group mb-0 mr-2">
<select class="form-control form-control-sm" id="sentimentFilter">
<option value="">全部情感</option>
<option value="积极">积极</option>
<option value="消极">消极</option>
<option value="中性">中性</option>
</select>
</div>
<div class="form-group mb-0">
<select class="form-control form-control-sm" id="riskFilter">
<option value="">全部风险等级</option>
<option value="高">高风险</option>
<option value="中">中风险</option>
<option value="低">低风险</option>
</select>
</div>
</div>
</div>
<div id="ai-analysis-results" class="analysis-container">
<!-- 分析结果将在这里动态显示 -->
</div>
... ... @@ -467,7 +530,7 @@
</div>
</div>
<!-- 添加必要的CSS样式 -->
<!-- 更新CSS样式 -->
<style>
.analysis-container {
max-height: 600px;
... ... @@ -481,10 +544,12 @@
margin-bottom: 15px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.analysis-card:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
transform: translateY(-2px);
}
.risk-level {
... ... @@ -521,32 +586,167 @@
padding: 4px 8px;
border-radius: 16px;
font-size: 0.9em;
transition: all 0.2s ease;
}
.keyword-tag:hover {
background-color: #1976d2;
color: #fff;
}
.sentiment-score {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
margin-left: 8px;
}
.sentiment-positive {
background-color: #e8f5e9;
color: #2e7d32;
}
.sentiment-negative {
background-color: #ffebee;
color: #c62828;
}
.sentiment-neutral {
background-color: #f5f5f5;
color: #616161;
}
.progress {
height: 0.5rem;
}
.analysis-controls {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
}
@media (max-width: 768px) {
.analysis-controls {
flex-direction: column;
align-items: stretch;
}
}
</style>
<!-- 添加必要的JavaScript代码 -->
<!-- 更新JavaScript代码 -->
<script>
let currentAnalysis = {
inProgress: false,
totalMessages: 0,
processedMessages: 0,
autoUpdateInterval: null
};
async function requestAIAnalysis() {
if (currentAnalysis.inProgress) {
return;
}
try {
currentAnalysis.inProgress = true;
showProgress();
const batchSize = parseInt(document.getElementById('batchSize').value);
const modelType = document.getElementById('modelType').value;
const analysisDepth = document.getElementById('analysisDepth').value;
const response = await fetch('/page/api/analyze_messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
},
body: JSON.stringify({
batch_size: batchSize,
model_type: modelType,
analysis_depth: analysisDepth
})
});
const result = await response.json();
if (result.success) {
displayAnalysisResults(result.data);
setupAutoUpdate();
} else {
alert('分析失败: ' + result.error);
showError(result.error);
}
} catch (error) {
console.error('AI分析请求失败:', error);
alert('请求失败,请稍后重试');
showError('请求失败,请稍后重试');
} finally {
currentAnalysis.inProgress = false;
hideProgress();
}
}
function showProgress() {
const progressDiv = document.getElementById('analysis-progress');
progressDiv.style.display = 'block';
updateProgress(0, 100);
}
function hideProgress() {
const progressDiv = document.getElementById('analysis-progress');
progressDiv.style.display = 'none';
}
function updateProgress(current, total) {
const progressBar = document.querySelector('.progress-bar');
const progressText = document.getElementById('progress-text');
const percentage = (current / total) * 100;
progressBar.style.width = `${percentage}%`;
progressText.textContent = `${current}/${total}`;
}
function setupAutoUpdate() {
const autoUpdate = document.getElementById('autoUpdate').checked;
if (autoUpdate && !currentAnalysis.autoUpdateInterval) {
currentAnalysis.autoUpdateInterval = setInterval(requestAIAnalysis, 300000); // 5分钟更新一次
} else if (!autoUpdate && currentAnalysis.autoUpdateInterval) {
clearInterval(currentAnalysis.autoUpdateInterval);
currentAnalysis.autoUpdateInterval = null;
}
}
function filterResults() {
const keyword = document.getElementById('keywordFilter').value.toLowerCase();
const sentiment = document.getElementById('sentimentFilter').value;
const risk = document.getElementById('riskFilter').value;
const cards = document.querySelectorAll('.analysis-card');
cards.forEach(card => {
let show = true;
// 关键词过滤
if (keyword) {
const content = card.textContent.toLowerCase();
show = show && content.includes(keyword);
}
// 情感过滤
if (sentiment) {
const cardSentiment = card.querySelector('.sentiment-text').textContent;
show = show && cardSentiment.includes(sentiment);
}
// 风险等级过滤
if (risk) {
const cardRisk = card.querySelector('.risk-level').textContent;
show = show && cardRisk.includes(risk);
}
card.style.display = show ? 'block' : 'none';
});
}
function displayAnalysisResults(results) {
const container = document.getElementById('ai-analysis-results');
container.innerHTML = ''; // 清空现有结果
... ... @@ -558,6 +758,10 @@ function displayAnalysisResults(results) {
const riskLevelClass =
analysis.risk_level === '高' ? 'risk-high' :
analysis.risk_level === '中' ? 'risk-medium' : 'risk-low';
const sentimentClass =
analysis.sentiment === '积极' ? 'sentiment-positive' :
analysis.sentiment === '消极' ? 'sentiment-negative' : 'sentiment-neutral';
card.innerHTML = `
<div class="d-flex justify-content-between align-items-center">
... ... @@ -567,8 +771,9 @@ function displayAnalysisResults(results) {
</span>
</div>
<div class="mb-2">
<strong>情感倾向:</strong> ${analysis.sentiment}
<span class="ml-2">(${analysis.sentiment_score})</span>
<strong>情感倾向:</strong>
<span class="sentiment-text">${analysis.sentiment}</span>
<span class="sentiment-score ${sentimentClass}">${analysis.sentiment_score}</span>
</div>
<div class="keywords-container">
${analysis.keywords.split(',').map(keyword =>
... ... @@ -590,10 +795,28 @@ function displayAnalysisResults(results) {
container.appendChild(card);
});
// 应用当前的过滤器
filterResults();
}
// 页面加载完成后自动请求一次AI分析
document.addEventListener('DOMContentLoaded', requestAIAnalysis);
function showError(message) {
alert(message);
}
// 设置事件监听器
document.addEventListener('DOMContentLoaded', () => {
// 自动更新切换
document.getElementById('autoUpdate').addEventListener('change', setupAutoUpdate);
// 过滤器变化监听
document.getElementById('keywordFilter').addEventListener('input', filterResults);
document.getElementById('sentimentFilter').addEventListener('change', filterResults);
document.getElementById('riskFilter').addEventListener('change', filterResults);
// 首次加载时请求分析
requestAIAnalysis();
});
</script>
{% endblock %}
... ...