987 lines
45 KiB
Python
987 lines
45 KiB
Python
"""
|
|
Trình tạo tạo ra cấu hình Simulation tự động
|
|
Sử dụng LLM theo yêu cầu mô phỏng, nội dung tài liệu và thông tin đồ thị để tự động thiết lập chi tiết các tham số
|
|
Tất cả đều tự động mà không cần can thiệp thủ công tạo tham số
|
|
|
|
Áp dụng chiến lược tạo từng bước để tránh lỗi do cố gắng tạo nội dung quá dài cùng một lúc:
|
|
1. Tạo cấu hình thời gian
|
|
2. Tạo cấu hình các Event
|
|
3. Tạo cấu hình cho các Agent theo đợt
|
|
4. Tạo cấu hình nền tảng
|
|
"""
|
|
|
|
import json
|
|
import math
|
|
from typing import Dict, Any, List, Optional, Callable
|
|
from dataclasses import dataclass, field, asdict
|
|
from datetime import datetime
|
|
|
|
from openai import OpenAI
|
|
|
|
from ..config import Config
|
|
from ..utils.logger import get_logger
|
|
from .zep_entity_reader import EntityNode, ZepEntityReader
|
|
|
|
logger = get_logger('mirofish.simulation_config')
|
|
|
|
# Cấu hình thời gian thói quen Trung Quốc (Theo giờ Bắc Kinh)
|
|
CHINA_TIMEZONE_CONFIG = {
|
|
# Khung giờ khuya (Hầu như không có hoạt động)
|
|
"dead_hours": [0, 1, 2, 3, 4, 5],
|
|
# Khung giờ sáng (Dần thức dậy)
|
|
"morning_hours": [6, 7, 8],
|
|
# Khung giờ làm việc
|
|
"work_hours": [9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
# Khung giờ cao điểm buổi tối (Hoạt động mạnh nhất)
|
|
"peak_hours": [19, 20, 21, 22],
|
|
# Khung giờ ban đêm (Hoạt động giảm sút)
|
|
"night_hours": [23],
|
|
# Hệ số hoạt động tương ứng với mỗi thời điểm
|
|
"activity_multipliers": {
|
|
"dead": 0.05, # Gần như không có ai lúc rạng sáng
|
|
"morning": 0.4, # Sáng sớm bắt đầu dần sôi động
|
|
"work": 0.7, # Mức trung bình trong giờ làm việc
|
|
"peak": 1.5, # Cao điểm tối
|
|
"night": 0.5 # Giảm sút đêm khuya
|
|
}
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class AgentActivityConfig:
|
|
"""Cấu hình hoạt động cho một Agent"""
|
|
agent_id: int
|
|
entity_uuid: str
|
|
entity_name: str
|
|
entity_type: str
|
|
|
|
# Mức độ hoạt động (0.0-1.0)
|
|
activity_level: float = 0.5 # Hoạt động tổng thể
|
|
|
|
# Tần suất phát ngôn (Số lần comment dự kiến mỗi giờ)
|
|
posts_per_hour: float = 1.0
|
|
comments_per_hour: float = 2.0
|
|
|
|
# Khoảng thời gian hoạt động (Hệ 24 giờ, 0-23)
|
|
active_hours: List[int] = field(default_factory=lambda: list(range(8, 23)))
|
|
|
|
# Tốc độ phản hồi (Độ trễ phản ứng với sự kiện nóng, đơn vị: phút mô phỏng)
|
|
response_delay_min: int = 5
|
|
response_delay_max: int = 60
|
|
|
|
# Khuynh hướng cảm xúc (-1.0 đến 1.0, từ tiêu cực đến tích cực)
|
|
sentiment_bias: float = 0.0
|
|
|
|
# Lập trường (Thái độ đối với chủ đề cụ thể)
|
|
stance: str = "neutral" # supportive, opposing, neutral, observer
|
|
|
|
# Trọng số ảnh hưởng (Xác định mức độ bài đăng được Agent khác nhìn thấy)
|
|
influence_weight: float = 1.0
|
|
|
|
|
|
@dataclass
|
|
class TimeSimulationConfig:
|
|
"""Cấu hình thời gian mô phỏng (Dựa trên thói quen sinh hoạt của người Trung)"""
|
|
# Tổng thời gian mô phỏng (Giờ)
|
|
total_simulation_hours: int = 72 # Mặc định là chạy mô phỏng 72 tiếng (3 ngày)
|
|
|
|
# Số phút đại diện cho mỗi vòng - Mặc định 60 phút (1 giờ), đẩy nhanh thời gian
|
|
minutes_per_round: int = 60
|
|
|
|
# Phạm vi số lượng Agent kích hoạt mỗi giờ
|
|
agents_per_hour_min: int = 5
|
|
agents_per_hour_max: int = 20
|
|
|
|
# Giờ cao điểm (19-22 giờ tối, thời gian sôi động nhất)
|
|
peak_hours: List[int] = field(default_factory=lambda: [19, 20, 21, 22])
|
|
peak_activity_multiplier: float = 1.5
|
|
|
|
# Khung giờ chết (0-5 giờ, hầu như không ai on)
|
|
off_peak_hours: List[int] = field(default_factory=lambda: [0, 1, 2, 3, 4, 5])
|
|
off_peak_activity_multiplier: float = 0.05 # Rạng sáng gần như bằng không
|
|
|
|
# Khung giờ buổi sáng
|
|
morning_hours: List[int] = field(default_factory=lambda: [6, 7, 8])
|
|
morning_activity_multiplier: float = 0.4
|
|
|
|
# Khung giờ làm việc
|
|
work_hours: List[int] = field(default_factory=lambda: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18])
|
|
work_activity_multiplier: float = 0.7
|
|
|
|
|
|
@dataclass
|
|
class EventConfig:
|
|
"""Cấu hình sự kiện cho Simulation"""
|
|
# Các bài Post/Sự kiện khởi đầu (Bắt đầu ngay khi chạy mô phỏng)
|
|
initial_posts: List[Dict[str, Any]] = field(default_factory=list)
|
|
|
|
# Các sự kiện được lập lịch vào các thời điểm nhất định
|
|
scheduled_events: List[Dict[str, Any]] = field(default_factory=list)
|
|
|
|
# Từ khóa dành cho các chủ đề đang hot (Hot topics)
|
|
hot_topics: List[str] = field(default_factory=list)
|
|
|
|
# Hướng dẫn dư luận / Đường lối thảo luận
|
|
narrative_direction: str = ""
|
|
|
|
|
|
@dataclass
|
|
class PlatformConfig:
|
|
"""Cấu hình đặc thù dành riêng cho các nền tảng"""
|
|
platform: str # twitter or reddit
|
|
|
|
# Trọng số cho các thuật toán đề xuất
|
|
recency_weight: float = 0.4 # Độ mới của bài
|
|
popularity_weight: float = 0.3 # Mức độ phổ biến truyền miệng
|
|
relevance_weight: float = 0.3 # Mức độ quan tâm / tương quan
|
|
|
|
# Ngưỡng lan truyền virus (Cần bao nhiêu tương tác để nội dung bắt đầu phát tán mạnh)
|
|
viral_threshold: int = 10
|
|
|
|
# Độ mạnh của hiệu ứng lan truyền trong nhóm chung chí hướng (buồng phản âm)
|
|
echo_chamber_strength: float = 0.5
|
|
|
|
|
|
@dataclass
|
|
class SimulationParameters:
|
|
"""完整的模拟参数配置"""
|
|
# 基础信息
|
|
simulation_id: str
|
|
project_id: str
|
|
graph_id: str
|
|
simulation_requirement: str
|
|
|
|
# Cấu hình thời gian
|
|
time_config: TimeSimulationConfig = field(default_factory=TimeSimulationConfig)
|
|
|
|
# Danh sách cấu hình Agent
|
|
agent_configs: List[AgentActivityConfig] = field(default_factory=list)
|
|
|
|
# Cấu hình Event
|
|
event_config: EventConfig = field(default_factory=EventConfig)
|
|
|
|
# Cấu hình nền tảng
|
|
twitter_config: Optional[PlatformConfig] = None
|
|
reddit_config: Optional[PlatformConfig] = None
|
|
|
|
# Cấu hình LLM
|
|
llm_model: str = ""
|
|
llm_base_url: str = ""
|
|
|
|
# Dữ liệu metadata khi tạo
|
|
generated_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
generation_reasoning: str = "" # Giải thích suy luận từ LLM
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert sang định dạng Dictionary"""
|
|
time_dict = asdict(self.time_config)
|
|
return {
|
|
"simulation_id": self.simulation_id,
|
|
"project_id": self.project_id,
|
|
"graph_id": self.graph_id,
|
|
"simulation_requirement": self.simulation_requirement,
|
|
"time_config": time_dict,
|
|
"agent_configs": [asdict(a) for a in self.agent_configs],
|
|
"event_config": asdict(self.event_config),
|
|
"twitter_config": asdict(self.twitter_config) if self.twitter_config else None,
|
|
"reddit_config": asdict(self.reddit_config) if self.reddit_config else None,
|
|
"llm_model": self.llm_model,
|
|
"llm_base_url": self.llm_base_url,
|
|
"generated_at": self.generated_at,
|
|
"generation_reasoning": self.generation_reasoning,
|
|
}
|
|
|
|
def to_json(self, indent: int = 2) -> str:
|
|
"""Convert sang định dạng chuỗi JSON"""
|
|
return json.dumps(self.to_dict(), ensure_ascii=False, indent=indent)
|
|
|
|
|
|
class SimulationConfigGenerator:
|
|
"""
|
|
Trình tạo cấu hình Simulation tự động bằng LLM
|
|
|
|
Sử dụng LLM phân tích yêu cầu mô phỏng, nội dung tài liệu, Entity từ đồ thị,
|
|
Tự động xây dựng các thông số cấu trúc tối ưu cho đợt Simulation
|
|
|
|
Áp dụng chiến lược tạo từng bước:
|
|
1. Tạo cấu hình thời gian và cấu hình Event (Nhẹ, chạy nhanh)
|
|
2. Phân nhỏ đợt tạo cấu hình cho Agent (Khoảng 10-20 agent mỗi đợt)
|
|
3. Tạo cấu hình nền tảng
|
|
"""
|
|
|
|
# Số lượng ký tự tối đa của bộ context
|
|
MAX_CONTEXT_LENGTH = 50000
|
|
# Số lượng Agent để gen cho một lần
|
|
AGENTS_PER_BATCH = 15
|
|
|
|
# Số lượng ký tự giới hạn ở các bước để cắt chuỗi (Ký tự đoạn)
|
|
TIME_CONFIG_CONTEXT_LENGTH = 10000 # Cấu hình thời gian
|
|
EVENT_CONFIG_CONTEXT_LENGTH = 8000 # Cấu hình sự kiện
|
|
ENTITY_SUMMARY_LENGTH = 300 # Tóm tắt các thực thể
|
|
AGENT_SUMMARY_LENGTH = 300 # Tóm tắt cấu hình Agent
|
|
ENTITIES_PER_TYPE_DISPLAY = 20 # Lượng thực thể cho mổi loại để hiển thị
|
|
|
|
def __init__(
|
|
self,
|
|
api_key: Optional[str] = None,
|
|
base_url: Optional[str] = None,
|
|
model_name: Optional[str] = None
|
|
):
|
|
self.api_key = api_key or Config.LLM_API_KEY
|
|
self.base_url = base_url or Config.LLM_BASE_URL
|
|
self.model_name = model_name or Config.LLM_MODEL_NAME
|
|
|
|
if not self.api_key:
|
|
raise ValueError("LLM_API_KEY has not been configured")
|
|
|
|
self.client = OpenAI(
|
|
api_key=self.api_key,
|
|
base_url=self.base_url
|
|
)
|
|
|
|
def generate_config(
|
|
self,
|
|
simulation_id: str,
|
|
project_id: str,
|
|
graph_id: str,
|
|
simulation_requirement: str,
|
|
document_text: str,
|
|
entities: List[EntityNode],
|
|
enable_twitter: bool = True,
|
|
enable_reddit: bool = True,
|
|
progress_callback: Optional[Callable[[int, int, str], None]] = None,
|
|
) -> SimulationParameters:
|
|
"""
|
|
Tạo cấu hình Simulation thông minh tự động hoàn chỉnh (Bằng tư duy chia từng bước)
|
|
|
|
Args:
|
|
simulation_id: Nhận dạng quy trình chạy Simulation
|
|
project_id: Mã định danh dự án
|
|
graph_id: Đồ thị đồ thị
|
|
simulation_requirement: Yêu cầu của quá trình mô phỏng
|
|
document_text: Nội dung file tài liệu nguồn
|
|
entities: Danh sách các thực thể đã được lọc
|
|
enable_twitter: Cờ hiệu để bật Twitter
|
|
enable_reddit: Cờ hiệu để bật Reddit
|
|
progress_callback: Hàm callback lấy trạng thái tiến trình hiện tại (current_step, total_steps, message)
|
|
|
|
Returns:
|
|
SimulationParameters: Bộ tổng cấu hình thông số đầy đủ
|
|
"""
|
|
logger.info(f"Start generating simulation configuration: simulation_id={simulation_id}, entity_count={len(entities)}")
|
|
|
|
# Tính toán tổng số bước
|
|
num_batches = math.ceil(len(entities) / self.AGENTS_PER_BATCH)
|
|
total_steps = 3 + num_batches # Cấu hình tgian + Sự kiện + Nx(Agent Batch) + Nền tảng
|
|
current_step = 0
|
|
|
|
def report_progress(step: int, message: str):
|
|
nonlocal current_step
|
|
current_step = step
|
|
if progress_callback:
|
|
progress_callback(step, total_steps, message)
|
|
logger.info(f"[{step}/{total_steps}] {message}")
|
|
|
|
# 1. Xây dựng thông tin ngữ cảnh cơ bản
|
|
context = self._build_context(
|
|
simulation_requirement=simulation_requirement,
|
|
document_text=document_text,
|
|
entities=entities
|
|
)
|
|
|
|
reasoning_parts = []
|
|
|
|
# ========== Bước 1: Tạo bộ cấu hình về Thời Gian ==========
|
|
report_progress(1, "Generating time configuration...")
|
|
num_entities = len(entities)
|
|
time_config_result = self._generate_time_config(context, num_entities)
|
|
time_config = self._parse_time_config(time_config_result, num_entities)
|
|
reasoning_parts.append(f"Time config reasoning: {time_config_result.get('reasoning', 'Success')}")
|
|
# ========== Bước 2: Tạo cấu hình Event ==========
|
|
report_progress(2, "Generating event configuration and hot topics...")
|
|
event_config_result = self._generate_event_config(context, simulation_requirement, entities)
|
|
event_config = self._parse_event_config(event_config_result)
|
|
reasoning_parts.append(f"Event config reasoning: {event_config_result.get('reasoning', 'Success')}")
|
|
|
|
# ========== Bước 3-N: Chia thành các đợt để lấy cấu hình Agent ==========
|
|
all_agent_configs = []
|
|
for batch_idx in range(num_batches):
|
|
start_idx = batch_idx * self.AGENTS_PER_BATCH
|
|
end_idx = min(start_idx + self.AGENTS_PER_BATCH, len(entities))
|
|
batch_entities = entities[start_idx:end_idx]
|
|
|
|
report_progress(
|
|
3 + batch_idx,
|
|
f"Generating agent configuration ({start_idx + 1}-{end_idx}/{len(entities)})..."
|
|
)
|
|
|
|
batch_configs = self._generate_agent_configs_batch(
|
|
context=context,
|
|
entities=batch_entities,
|
|
start_idx=start_idx,
|
|
simulation_requirement=simulation_requirement
|
|
)
|
|
all_agent_configs.extend(batch_configs)
|
|
|
|
reasoning_parts.append(f"Agent config reasoning: Successfully generated {len(all_agent_configs)} agents")
|
|
|
|
# ========== Tiến hành gán người (Agent) để đăng các bài Initial Post ==========
|
|
logger.info("Assigning poster agents for initial posts...")
|
|
event_config = self._assign_initial_post_agents(event_config, all_agent_configs)
|
|
assigned_count = len([p for p in event_config.initial_posts if p.get("poster_agent_id") is not None])
|
|
reasoning_parts.append(f"Initial post assignment: {assigned_count} posts have been assigned to publishers")
|
|
|
|
# ========== Bước cuối: Thiết lập nền tảng ==========
|
|
report_progress(total_steps, "Generating platform configuration...")
|
|
twitter_config = None
|
|
reddit_config = None
|
|
|
|
if enable_twitter:
|
|
twitter_config = PlatformConfig(
|
|
platform="twitter",
|
|
recency_weight=0.4,
|
|
popularity_weight=0.3,
|
|
relevance_weight=0.3,
|
|
viral_threshold=10,
|
|
echo_chamber_strength=0.5
|
|
)
|
|
|
|
if enable_reddit:
|
|
reddit_config = PlatformConfig(
|
|
platform="reddit",
|
|
recency_weight=0.3,
|
|
popularity_weight=0.4,
|
|
relevance_weight=0.3,
|
|
viral_threshold=15,
|
|
echo_chamber_strength=0.6
|
|
)
|
|
|
|
# Xây dựng các tham số cuối cùng kết thúc quy trình
|
|
params = SimulationParameters(
|
|
simulation_id=simulation_id,
|
|
project_id=project_id,
|
|
graph_id=graph_id,
|
|
simulation_requirement=simulation_requirement,
|
|
time_config=time_config,
|
|
agent_configs=all_agent_configs,
|
|
event_config=event_config,
|
|
twitter_config=twitter_config,
|
|
reddit_config=reddit_config,
|
|
llm_model=self.model_name,
|
|
llm_base_url=self.base_url,
|
|
generation_reasoning=" | ".join(reasoning_parts)
|
|
)
|
|
|
|
logger.info(f"Simulation configuration generation complete: {len(params.agent_configs)} agent configs created")
|
|
|
|
return params
|
|
|
|
def _build_context(
|
|
self,
|
|
simulation_requirement: str,
|
|
document_text: str,
|
|
entities: List[EntityNode]
|
|
) -> str:
|
|
"""Thực hiện xây dựng nội dung Prompt Ngữ cảnh cho LLM, với độ dài có thể bị giới hạn"""
|
|
|
|
# Tóm tắt lại Thực thể
|
|
entity_summary = self._summarize_entities(entities)
|
|
|
|
# Xây dựng nội dung
|
|
context_parts = [
|
|
f"## Simulation Requirements\n{simulation_requirement}",
|
|
f"\n## Entity Information ({len(entities)} entities)\n{entity_summary}",
|
|
]
|
|
|
|
current_length = sum(len(p) for p in context_parts)
|
|
remaining_length = self.MAX_CONTEXT_LENGTH - current_length - 500 # Dành sẵn 500 ký tự trống
|
|
|
|
if remaining_length > 0 and document_text:
|
|
doc_text = document_text[:remaining_length]
|
|
if len(document_text) > remaining_length:
|
|
doc_text += "\n...(Document Truncated)"
|
|
context_parts.append(f"\n## Original Document Content\n{doc_text}")
|
|
|
|
return "\n".join(context_parts)
|
|
|
|
def _summarize_entities(self, entities: List[EntityNode]) -> str:
|
|
"""Tạo chuỗi văn bản Tóm tắt cho các Thực thể"""
|
|
lines = []
|
|
|
|
# Phân nhóm bằng Loại
|
|
by_type: Dict[str, List[EntityNode]] = {}
|
|
for e in entities:
|
|
t = e.get_entity_type() or "Unknown"
|
|
if t not in by_type:
|
|
by_type[t] = []
|
|
by_type[t].append(e)
|
|
|
|
for entity_type, type_entities in by_type.items():
|
|
lines.append(f"\n### {entity_type} ({len(type_entities)} entity)")
|
|
# Số lượng đã được thiết lập mặc định và Giới hạn chiều dài của bảng tóm tắt
|
|
display_count = self.ENTITIES_PER_TYPE_DISPLAY
|
|
summary_len = self.ENTITY_SUMMARY_LENGTH
|
|
for e in type_entities[:display_count]:
|
|
summary_preview = (e.summary[:summary_len] + "...") if len(e.summary) > summary_len else e.summary
|
|
lines.append(f"- {e.name}: {summary_preview}")
|
|
if len(type_entities) > display_count:
|
|
lines.append(f" ... and {len(type_entities) - display_count} more entities")
|
|
|
|
return "\n".join(lines)
|
|
|
|
def _call_llm_with_retry(self, prompt: str, system_prompt: str) -> Dict[str, Any]:
|
|
"""Tích hợp cơ chế retry mỗi lúc gọi Request LLM bị lỗi và Logic sửa lỗi JSON string"""
|
|
import re
|
|
|
|
max_attempts = 3
|
|
last_error = None
|
|
|
|
for attempt in range(max_attempts):
|
|
try:
|
|
response = self.client.chat.completions.create(
|
|
model=self.model_name,
|
|
messages=[
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": prompt}
|
|
],
|
|
response_format={"type": "json_object"},
|
|
temperature=0.7 - (attempt * 0.1) # Giảm temperature cho mỗi lần retry
|
|
# Không đặt max_tokens, cho AI sáng tạo tự do tối đa
|
|
)
|
|
|
|
content = response.choices[0].message.content
|
|
finish_reason = response.choices[0].finish_reason
|
|
|
|
# Kiểm tra nội dung trã về xem có phải bị chặn vì thiếu token (Length vượt qua max) hay không
|
|
if finish_reason == 'length':
|
|
logger.warning(f"LLM output was truncated (attempt {attempt+1})")
|
|
content = self._fix_truncated_json(content)
|
|
|
|
# Phân tích nội dung JSON
|
|
try:
|
|
return json.loads(content)
|
|
except json.JSONDecodeError as e:
|
|
logger.warning(f"Failed to parse JSON (attempt {attempt+1}): {str(e)[:80]}")
|
|
|
|
# Tiến hành sửa chữa nội dung JSON nếu bị lỗi
|
|
fixed = self._try_fix_config_json(content)
|
|
if fixed:
|
|
return fixed
|
|
|
|
last_error = e
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Failed to call LLM (attempt {attempt+1}): {str(e)[:80]}")
|
|
last_error = e
|
|
import time
|
|
time.sleep(2 * (attempt + 1))
|
|
|
|
raise last_error or Exception("LLM connection completely failed")
|
|
|
|
def _fix_truncated_json(self, content: str) -> str:
|
|
"""Đóng dấu ngoặc JSON một cách an toàn cho các string bị cắt ngang"""
|
|
content = content.strip()
|
|
|
|
# Đếm các dấu ngoặc mở bị bỏ sót chưa đóng
|
|
open_braces = content.count('{') - content.count('}')
|
|
open_brackets = content.count('[') - content.count(']')
|
|
|
|
# Đảm bảo các thuộc tính string đã được bọc đủ dấu ngoặc kép
|
|
if content and content[-1] not in '",}]':
|
|
content += '"'
|
|
|
|
# Thêm ngoặc đóng cho toàn bộ
|
|
content += ']' * open_brackets
|
|
content += '}' * open_braces
|
|
|
|
return content
|
|
|
|
def _try_fix_config_json(self, content: str) -> Optional[Dict[str, Any]]:
|
|
"""Cố gắng khôi phục, chắp ghép lại file cấu trúc config JSON"""
|
|
import re
|
|
|
|
# Điền những dấu ngoặc vào chuỗi bị cắt
|
|
content = self._fix_truncated_json(content)
|
|
|
|
# Regex ra đúng phần ruột nội dung JSON
|
|
json_match = re.search(r'\{[\s\S]*\}', content)
|
|
if json_match:
|
|
json_str = json_match.group()
|
|
|
|
# Loại bỏ các đoạn tab, ngắt line cho string
|
|
def fix_string(match):
|
|
s = match.group(0)
|
|
s = s.replace('\n', ' ').replace('\r', ' ')
|
|
s = re.sub(r'\s+', ' ', s)
|
|
return s
|
|
|
|
json_str = re.sub(r'"[^"\\]*(?:\\.[^"\\]*)*"', fix_string, json_str)
|
|
|
|
try:
|
|
return json.loads(json_str)
|
|
except:
|
|
# Tìm và xóa các control character
|
|
json_str = re.sub(r'[\x00-\x1f\x7f-\x9f]', ' ', json_str)
|
|
json_str = re.sub(r'\s+', ' ', json_str)
|
|
try:
|
|
return json.loads(json_str)
|
|
except:
|
|
pass
|
|
|
|
return None
|
|
|
|
def _generate_time_config(self, context: str, num_entities: int) -> Dict[str, Any]:
|
|
"""Tạo cấu hình thời gian (Time config) cho các tiến trình"""
|
|
# Áp dụng nội dung ngữ cảnh đã được giới hạn chiều dài
|
|
context_truncated = context[:self.TIME_CONFIG_CONTEXT_LENGTH]
|
|
|
|
# Cắt lấy số lượng Tối đa số lượng (Chiếm 80% từ số lượng lượng Agent thực thể)
|
|
max_agents_allowed = max(1, int(num_entities * 0.9))
|
|
|
|
prompt = f"""Dựa vào yêu cầu Mô phỏng này, hãy tự động gen cho 1 file Thông số thời gian
|
|
|
|
{context_truncated}
|
|
|
|
## Task công việc
|
|
Vui lòng xuất ra kết quả Thời gian dưới định dạng format JSON
|
|
|
|
### Logic cơ bản có thể cần để tham khảo (Hãy dựa trên nhu cầu của user và hoàn cảnh để suy ra):
|
|
- Vị trí của người tham gia là người dùng mạng Trung Quốc, cần sinh hoạt bằng thói quen sinh học giờ chuẩn Bắc Kinh (China Time: GMT+8).
|
|
- Không xuất hiện hay có dấu hiệu online của người dùng từ 0-5 giờ sáng (Hệ số Active 0.05).
|
|
- Tăng nhẹ số lượng truy cập lại mức trung thành khoảng giữa 6-8 giờ sáng (Hệ số Active 0.4).
|
|
- Số lượng active hoạt động ở mức bình ổn khoảng từ 9-18 giờ sáng (Hệ số Active 0.7).
|
|
- Khung giờ sôi động nhất sẽ tập trung quanh 19-22 giờ tối (Hệ số Active 1.5).
|
|
- Tỷ lệ giảm lại sau 23 giờ (Hệ số Active 0.5).
|
|
- Cơ chế bình thường: Đêm không online, sáng bắt đầu đăng bài, giờ hành chính bình bình và cao trào trong buổi tối thức đêm
|
|
- **Chỉ Dẫn Rất Quan Trọng**: Những thông tin từ list được lấy để tham chiếu. Còn thông số thật sự còn phải tùy theo Đặc điểm, Tình Huống đối tượng ở Mạng và Thời Điểm Sự Kiện để gen ra
|
|
- Ví dụ: Số lượng sinh viên thức đêm ở từ 21-23 giờ thường là lớn; Media báo đài thì hay đăng tin liên tục cả ngày theo ca; Tài khoản văn phòng của các Cơ Quan chức năng chỉ trả lời giờ làm việc Hành Chính...
|
|
- Hoặc ví dụ: Các Biến Cố hoặc Drama xảy ra trong đêm khuya thì sẽ dẫn đến Lượng truy cập ban đêm có dấu hiệu đi lên, trong khi đó Off_peak_hours vì lẽ đó mà sẽ có khi co lại cho ngắn...
|
|
|
|
### Định dạng Format của JSON Return (Lưu ý Tuyệt đối Không Return Markdown code block, chỉ Return Format Json Thuần Túy)
|
|
|
|
Ví dụ Format như sau:
|
|
{{
|
|
"total_simulation_hours": 72,
|
|
"minutes_per_round": 60,
|
|
"agents_per_hour_min": 5,
|
|
"agents_per_hour_max": 50,
|
|
"peak_hours": [19, 20, 21, 22],
|
|
"off_peak_hours": [0, 1, 2, 3, 4, 5],
|
|
"morning_hours": [6, 7, 8],
|
|
"work_hours": [9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
"reasoning": "Một đoạn văn lời nói cho biết Bạn đã dựa theo yêu cầu như thế nào để gen các Thông Số trên"
|
|
}}
|
|
|
|
Các Khóa của Json có nghĩa là:
|
|
- total_simulation_hours (int): Mô tả tổng giới hạn thời gian (Đơn vị giờ), Có giá trị trong khung 24-168h, Tùy vào biến cố Drama nóng để chọn. Các chủ đề chạy Drama ít hơn thì nên cấp ngắn
|
|
- minutes_per_round (int): Số Time trên mỗi Khung Đợt Thời Gian Thực Của Simulation để mô phỏng cho 1 phiên trong game, lấy giá trị 30-120 phút. Đề xuất: 60 (1 giờ)
|
|
- agents_per_hour_min (int): Số Agent online tối thiểu trong một tiếng mô phỏng (Phạm vi {1}-{max_agents_allowed})
|
|
- agents_per_hour_max (int): Số lượng lên mạng tối đa (Phạm vi {1}-{max_agents_allowed})
|
|
- peak_hours (mảng int list): Thời điểm đỉnh sóng Cao Điểm, cân nhắc theo Đối tượng để quyết định
|
|
- off_peak_hours (mảng int list): Đỉnh sóng Đáy, ít ai quan tâm
|
|
- morning_hours (mảng int list): Khoảng thời điểm đầu buổi sáng
|
|
- work_hours (mảng int list): Khung hành chính công việc
|
|
- reasoning (string): Sự giải thích từ LLM"""
|
|
|
|
system_prompt = "Bạn là 1 Tool chuyên mô phỏng môi trường làm việc trên mxh bằng thuật toán LLM để cung cấp ra Cấu hình Time. Hãy xuất JSON."
|
|
|
|
try:
|
|
return self._call_llm_with_retry(prompt, system_prompt)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to generate Time Config through LLM {e}. Returning the basic default rules...")
|
|
return self._get_default_time_config(num_entities)
|
|
|
|
def _get_default_time_config(self, num_entities: int) -> Dict[str, Any]:
|
|
"""Tạo sẵn file chuẩn nếu bị đơ để trả ra theo múi giờ chuẩn sinh hoạt China"""
|
|
return {
|
|
"total_simulation_hours": 72,
|
|
"minutes_per_round": 60, # 1 Hour / Vòng -> Rút gắn Time
|
|
"agents_per_hour_min": max(1, num_entities // 15),
|
|
"agents_per_hour_max": max(5, num_entities // 5),
|
|
"peak_hours": [19, 20, 21, 22],
|
|
"off_peak_hours": [0, 1, 2, 3, 4, 5],
|
|
"morning_hours": [6, 7, 8],
|
|
"work_hours": [9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
"reasoning": "Mặc định sử dụng Thời gian làm việc của người dùng Trung Quốc (1 Giờ/vòng)"
|
|
}
|
|
|
|
def _parse_time_config(self, result: Dict[str, Any], num_entities: int) -> TimeSimulationConfig:
|
|
"""Phân tích nội dung được định hình của JSON qua hàm parse kiểm tra, Xác nhận nếu lượng agents_per_hour vượt ngưỡng giới hạn """
|
|
# Lấy giá trị chưa chỉnh sửa
|
|
agents_per_hour_min = result.get("agents_per_hour_min", max(1, num_entities // 15))
|
|
agents_per_hour_max = result.get("agents_per_hour_max", max(5, num_entities // 5))
|
|
|
|
# Tiến hành kiểm tra xác minh: Đảm bảo độ lớn không lớn hơn con số Total Agent
|
|
if agents_per_hour_min > num_entities:
|
|
logger.warning(f"agents_per_hour_min ({agents_per_hour_min}) exceeds total number of Agents ({num_entities}), corrected.")
|
|
agents_per_hour_min = max(1, num_entities // 10)
|
|
|
|
if agents_per_hour_max > num_entities:
|
|
logger.warning(f"agents_per_hour_max ({agents_per_hour_max}) exceeds total number of Agents ({num_entities}), corrected.")
|
|
agents_per_hour_max = max(agents_per_hour_min + 1, num_entities // 2)
|
|
|
|
# Đảm bảo min luôn luôn nhỏ hơn max
|
|
if agents_per_hour_min >= agents_per_hour_max:
|
|
agents_per_hour_min = max(1, agents_per_hour_max // 2)
|
|
logger.warning(f"agents_per_hour_min >= max, modified to {agents_per_hour_min}")
|
|
|
|
return TimeSimulationConfig(
|
|
total_simulation_hours=result.get("total_simulation_hours", 72),
|
|
minutes_per_round=result.get("minutes_per_round", 60), # Mặc định mỗi vòng = 1 giờ
|
|
agents_per_hour_min=agents_per_hour_min,
|
|
agents_per_hour_max=agents_per_hour_max,
|
|
peak_hours=result.get("peak_hours", [19, 20, 21, 22]),
|
|
off_peak_hours=result.get("off_peak_hours", [0, 1, 2, 3, 4, 5]),
|
|
off_peak_activity_multiplier=0.05, # Gần như 0 mạng sáng rạng sáng
|
|
morning_hours=result.get("morning_hours", [6, 7, 8]),
|
|
morning_activity_multiplier=0.4,
|
|
work_hours=result.get("work_hours", list(range(9, 19))),
|
|
work_activity_multiplier=0.7,
|
|
peak_activity_multiplier=1.5
|
|
)
|
|
|
|
def _generate_event_config(
|
|
self,
|
|
context: str,
|
|
simulation_requirement: str,
|
|
entities: List[EntityNode]
|
|
) -> Dict[str, Any]:
|
|
"""Tạo ra cho các thông số Event config"""
|
|
|
|
# Tự liệt kê các Loại có thể xuất hiện để LLM tham khảo
|
|
entity_types_available = list(set(
|
|
e.get_entity_type() or "Unknown" for e in entities
|
|
))
|
|
|
|
# Ghi các Thực thể điển hình của mổi loại
|
|
type_examples = {}
|
|
for e in entities:
|
|
etype = e.get_entity_type() or "Unknown"
|
|
if etype not in type_examples:
|
|
type_examples[etype] = []
|
|
if len(type_examples[etype]) < 3:
|
|
type_examples[etype].append(e.name)
|
|
|
|
type_info = "\n".join([
|
|
f"- {t}: {', '.join(examples)}"
|
|
for t, examples in type_examples.items()
|
|
])
|
|
|
|
# Có chặn để lấy chuỗi theo cấu hình chiều dài giới hạn
|
|
context_truncated = context[:self.EVENT_CONFIG_CONTEXT_LENGTH]
|
|
|
|
prompt = f"""Gen cấu hình Event dưới các tham chiếu từ Yêu cầu (Requirements):
|
|
|
|
Simulation Requirements: {simulation_requirement}
|
|
|
|
{context_truncated}
|
|
|
|
## Các Entity Type có cung cấp & VD minh họa:
|
|
{type_info}
|
|
|
|
## Task công việc
|
|
Vui lòng xuất ra kết quả Thời gian dưới định dạng format JSON:
|
|
- Chỉ định List các Hot Keyword để kéo trend
|
|
- Miêu tả định hướng thảo luận cho trend hiện tại
|
|
- Đăng tải Post đầu tiên (Initial_Post) lên với nguyên tắc: **Phải đi kèm với tham số người up Post (poster_type)**
|
|
|
|
**RẤT QUAN TRỌNG**: Người Poster Type (poster_type) Phải trùng khớp/được lấy từ danh mục từ mục "Các Entity Type" đã cho để gán. Tránh báo lỗi cho Agent
|
|
Ví dụ: official announcements should be posted by Official/University type, news by MediaOutlet, and student opinions by Student.
|
|
|
|
Format trả ra (Tuyệt đối Không Markdown, chỉ lấy format chuỗi chuẩn):
|
|
{{
|
|
"hot_topics": ["Keyword1", "Keyword2", ...],
|
|
"narrative_direction": "<Đoạn Text dài định hướng Dư luận (Narrative)>",
|
|
"initial_posts": [
|
|
{{"content": "Post Content...", "poster_type": "Người sẽ Post ra nội dung (Hạn chế tùy tiện vì nó lấy từ mảng danh sách Loại Entity cho trước)"}},
|
|
...
|
|
],
|
|
"reasoning": "<Short Explanation>"
|
|
}}"""
|
|
|
|
system_prompt = "Bạn là 1 Chuyên gia về Data Dư luận, Yêu cầu làm việc trên chuỗi JSON nghiêm ngặt. Tránh Lỗi."
|
|
|
|
try:
|
|
return self._call_llm_with_retry(prompt, system_prompt)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to load LLM Event configurations: {e}, using default configs instead.")
|
|
return {
|
|
"hot_topics": [],
|
|
"narrative_direction": "",
|
|
"initial_posts": [],
|
|
"reasoning": "Sử dụng Config mặc định do LLM lỗi"
|
|
}
|
|
|
|
def _parse_event_config(self, result: Dict[str, Any]) -> EventConfig:
|
|
"""Parse lấy các Thuộc Tính cấu hình Event"""
|
|
return EventConfig(
|
|
initial_posts=result.get("initial_posts", []),
|
|
scheduled_events=[],
|
|
hot_topics=result.get("hot_topics", []),
|
|
narrative_direction=result.get("narrative_direction", "")
|
|
)
|
|
|
|
def _assign_initial_post_agents(
|
|
self,
|
|
event_config: EventConfig,
|
|
agent_configs: List[AgentActivityConfig]
|
|
) -> EventConfig:
|
|
"""
|
|
Khớp quyền Agent với loại Poster_type cho các Bài Post đầu
|
|
|
|
So sánh cho phù hợp của mỗi post để phân bố Agent id tối ưu nhất
|
|
"""
|
|
if not event_config.initial_posts:
|
|
return event_config
|
|
|
|
# Build hệ thống agent index bằng kiểu loại
|
|
agents_by_type: Dict[str, List[AgentActivityConfig]] = {}
|
|
for agent in agent_configs:
|
|
etype = agent.entity_type.lower()
|
|
if etype not in agents_by_type:
|
|
agents_by_type[etype] = []
|
|
agents_by_type[etype].append(agent)
|
|
|
|
# Bảng Alias ánh xạ tương đương (Cho phép LLM sử dụng nhiều quy ước format khác nhau)
|
|
type_aliases = {
|
|
"official": ["official", "university", "governmentagency", "government"],
|
|
"university": ["university", "official"],
|
|
"mediaoutlet": ["mediaoutlet", "media"],
|
|
"student": ["student", "person"],
|
|
"professor": ["professor", "expert", "teacher"],
|
|
"alumni": ["alumni", "person"],
|
|
"organization": ["organization", "ngo", "company", "group"],
|
|
"person": ["person", "student", "alumni"],
|
|
}
|
|
|
|
# Ghi chú từng loại agent đã dùng index nào, tránh dùng lại cùng 1 agent lặp đi lặp lại
|
|
used_indices: Dict[str, int] = {}
|
|
|
|
updated_posts = []
|
|
for post in event_config.initial_posts:
|
|
poster_type = post.get("poster_type", "").lower()
|
|
content = post.get("content", "")
|
|
|
|
# Khớp tìm agent phù hợp
|
|
matched_agent_id = None
|
|
|
|
# 1. Trùng khớp trực tiếp lấy luôn
|
|
if poster_type in agents_by_type:
|
|
agents = agents_by_type[poster_type]
|
|
idx = used_indices.get(poster_type, 0) % len(agents)
|
|
matched_agent_id = agents[idx].agent_id
|
|
used_indices[poster_type] = idx + 1
|
|
else:
|
|
# 2. Sử dụng bí danh alias để khớp nếu dùng sai keyword
|
|
for alias_key, aliases in type_aliases.items():
|
|
if poster_type in aliases or alias_key == poster_type:
|
|
for alias in aliases:
|
|
if alias in agents_by_type:
|
|
agents = agents_by_type[alias]
|
|
idx = used_indices.get(alias, 0) % len(agents)
|
|
matched_agent_id = agents[idx].agent_id
|
|
used_indices[alias] = idx + 1
|
|
break
|
|
if matched_agent_id is not None:
|
|
break
|
|
|
|
# 3. Nếu xui xẻo vẫn không tìm thấy, lấy thẳng Agent có điểm Influence (Sức ảnh hưởng) cao nhất
|
|
if matched_agent_id is None:
|
|
logger.warning(f"Could not find matching Agent type '{poster_type}', assigning to highest influence Agent instead")
|
|
if agent_configs:
|
|
# Sort ảnh hưởng giảm dần, lấy index [0]
|
|
sorted_agents = sorted(agent_configs, key=lambda a: a.influence_weight, reverse=True)
|
|
matched_agent_id = sorted_agents[0].agent_id
|
|
else:
|
|
matched_agent_id = 0
|
|
|
|
updated_posts.append({
|
|
"content": content,
|
|
"poster_type": post.get("poster_type", "Unknown"),
|
|
"poster_agent_id": matched_agent_id
|
|
})
|
|
|
|
logger.info(f"Initial post assignment: poster_type='{poster_type}' -> agent_id={matched_agent_id}")
|
|
|
|
event_config.initial_posts = updated_posts
|
|
return event_config
|
|
|
|
def _generate_agent_configs_batch(
|
|
self,
|
|
context: str,
|
|
entities: List[EntityNode],
|
|
start_idx: int,
|
|
simulation_requirement: str
|
|
) -> List[AgentActivityConfig]:
|
|
"""Chia đợt gửi lên gọi tạo Cấu hình mạng lưới Agents"""
|
|
|
|
# Build các node Entity (Dựa trên cấu hình lượng chữ giới hạn)
|
|
entity_list = []
|
|
summary_len = self.AGENT_SUMMARY_LENGTH
|
|
for i, e in enumerate(entities):
|
|
entity_list.append({
|
|
"agent_id": start_idx + i,
|
|
"entity_name": e.name,
|
|
"entity_type": e.get_entity_type() or "Unknown",
|
|
"summary": e.summary[:summary_len] if e.summary else ""
|
|
})
|
|
|
|
prompt = f"""Tạo profile Social Media Activity Configs cho từng Thực thể sau.
|
|
|
|
Nhu cầu: {simulation_requirement}
|
|
|
|
## List các thực thể
|
|
```json
|
|
{json.dumps(entity_list, ensure_ascii=False, indent=2)}
|
|
```
|
|
|
|
## Task Công Việc
|
|
Trả ra cho Từng Entity các bộ Activity Profile tham chiều theo các quy tắc ngầm sau:
|
|
- **Tập quán Sinh hoạt Trung Quốc**: 0-5h sáng gần như sẽ hiếm ai onl, 19-22h tối lượng tương tác rất sôi nổi
|
|
- **Đại diện Cơ quan (University/GovernmentAgency)**: Tần suất (0.1-0.3), làm việc trong giờ hành chính (9-17h), delay hơi trễ (60-240 phút), Trọng lượng lời nói cao (2.5-3.0)
|
|
- **Truyền Thông Báo Đài (MediaOutlet)**: Tần suất TB (0.4-0.6), Hầu như online nguyên ngày (8-23h), Trễ ít (5-30 phút), Trọng lượng cũng Cao (2.0-2.5)
|
|
- **Người Dùng Bình thường (Student/Person/Alumni)**: Tần suất cao (0.6-0.9), Onl chủ yếu để cãi nhau buổi tối (18-23h), Tương tác lẹ như hack (1-15 min), Uy tín lời nói khá lèo tèo (0.8-1.2)
|
|
- **Học giả/Chuyên gia/Kols**: Tần suất TB (0.4-0.6), Uy tín tương đối (1.5-2.0)
|
|
|
|
Trả đúng 1 object JSON ko format MD:
|
|
{{
|
|
"agent_configs": [
|
|
{{
|
|
"agent_id": <Bắt buộc giống Input gửi vào>,
|
|
"activity_level": <0.0-1.0>,
|
|
"posts_per_hour": <Tần suất mổi 1 hour đăng Post>,
|
|
"comments_per_hour": <Tần suất mổi 1 hour phản hồi CMTS>,
|
|
"active_hours": [<Mảng giờ hoạt động theo như quy tắc múi giờ trên kia>],
|
|
"response_delay_min": <Phút tối thiểu để cho thấy Reaction vs event>,
|
|
"response_delay_max": <Phút Tối đa nhây cmt>,
|
|
"sentiment_bias": <-1.0 đến 1.0 (Tiêu cực sang Tích Cực)>,
|
|
"stance": "<supportive/opposing/neutral/observer>",
|
|
"influence_weight": <Bậc sức nặng lời nói Influence>
|
|
}},
|
|
...
|
|
]
|
|
}}"""
|
|
|
|
system_prompt = "Hệ thống Analysis Chuyên gia. Luôn trả về Format Object Array bằng JSON. Và tuân thủ sinh học."
|
|
|
|
try:
|
|
result = self._call_llm_with_retry(prompt, system_prompt)
|
|
llm_configs = {cfg["agent_id"]: cfg for cfg in result.get("agent_configs", [])}
|
|
except Exception as e:
|
|
logger.warning(f"Failed LLM generating Agent batch configs: {e}, falling back to default manual rules.")
|
|
llm_configs = {}
|
|
|
|
# Tạo object list cho AgentActivityConfig
|
|
configs = []
|
|
for i, entity in enumerate(entities):
|
|
agent_id = start_idx + i
|
|
cfg = llm_configs.get(agent_id, {})
|
|
|
|
# Gán Manual tự động nếu Bot LLM thiếu xót
|
|
if not cfg:
|
|
cfg = self._generate_agent_config_by_rule(entity)
|
|
|
|
config = AgentActivityConfig(
|
|
agent_id=agent_id,
|
|
entity_uuid=entity.uuid,
|
|
entity_name=entity.name,
|
|
entity_type=entity.get_entity_type() or "Unknown",
|
|
activity_level=cfg.get("activity_level", 0.5),
|
|
posts_per_hour=cfg.get("posts_per_hour", 0.5),
|
|
comments_per_hour=cfg.get("comments_per_hour", 1.0),
|
|
active_hours=cfg.get("active_hours", list(range(9, 23))),
|
|
response_delay_min=cfg.get("response_delay_min", 5),
|
|
response_delay_max=cfg.get("response_delay_max", 60),
|
|
sentiment_bias=cfg.get("sentiment_bias", 0.0),
|
|
stance=cfg.get("stance", "neutral"),
|
|
influence_weight=cfg.get("influence_weight", 1.0)
|
|
)
|
|
configs.append(config)
|
|
|
|
return configs
|
|
|
|
def _generate_agent_config_by_rule(self, entity: EntityNode) -> Dict[str, Any]:
|
|
"""Tự động gen cấu hình 1 người (agent) dựa trên bộ rule cứng có sẵn nếu gọi bot LLM bị fail (Luật theo múi giờ sinh học)"""
|
|
entity_type = (entity.get_entity_type() or "Unknown").lower()
|
|
|
|
if entity_type in ["university", "governmentagency", "ngo"]:
|
|
# Cơ quan chức năng Nhà nước / Doanh nghiệp: làm việc trong khung giờ chuẩn hành chính, trả lời ít nhưng nặng đô
|
|
return {
|
|
"activity_level": 0.2,
|
|
"posts_per_hour": 0.1,
|
|
"comments_per_hour": 0.05,
|
|
"active_hours": list(range(9, 18)), # 9:00-17:59
|
|
"response_delay_min": 60,
|
|
"response_delay_max": 240,
|
|
"sentiment_bias": 0.0,
|
|
"stance": "neutral",
|
|
"influence_weight": 3.0
|
|
}
|
|
elif entity_type in ["mediaoutlet"]:
|
|
# Báo đài truyền thông: cả ngày đưa tin, ra bài lẹ giật tít, tốc độ cao
|
|
return {
|
|
"activity_level": 0.5,
|
|
"posts_per_hour": 0.8,
|
|
"comments_per_hour": 0.3,
|
|
"active_hours": list(range(7, 24)), # 7:00-23:59
|
|
"response_delay_min": 5,
|
|
"response_delay_max": 30,
|
|
"sentiment_bias": 0.0,
|
|
"stance": "observer",
|
|
"influence_weight": 2.5
|
|
}
|
|
elif entity_type in ["professor", "expert", "official"]:
|
|
# Giáo sư đại học/Người phát biểu: Chỉ nói ban ngày và tối, ra bài ít
|
|
return {
|
|
"activity_level": 0.4,
|
|
"posts_per_hour": 0.3,
|
|
"comments_per_hour": 0.5,
|
|
"active_hours": list(range(8, 22)), # 8:00-21:59
|
|
"response_delay_min": 15,
|
|
"response_delay_max": 90,
|
|
"sentiment_bias": 0.0,
|
|
"stance": "neutral",
|
|
"influence_weight": 2.0
|
|
}
|
|
elif entity_type in ["student"]:
|
|
# Tần suất cho lứa Sinh viên: hay ra bài / cãi nhau liên tục ban đêm rất nhiều
|
|
return {
|
|
"activity_level": 0.8,
|
|
"posts_per_hour": 0.6,
|
|
"comments_per_hour": 1.5,
|
|
"active_hours": [8, 9, 10, 11, 12, 13, 18, 19, 20, 21, 22, 23], # Sáng + Đêm Tối
|
|
"response_delay_min": 1,
|
|
"response_delay_max": 15,
|
|
"sentiment_bias": 0.0,
|
|
"stance": "neutral",
|
|
"influence_weight": 0.8
|
|
}
|
|
elif entity_type in ["alumni"]:
|
|
# Cựu sinh viên: Thường online đêm là chính
|
|
return {
|
|
"activity_level": 0.6,
|
|
"posts_per_hour": 0.4,
|
|
"comments_per_hour": 0.8,
|
|
"active_hours": [12, 13, 19, 20, 21, 22, 23], # Giờ nghỉ trưa + Buổi tối
|
|
"response_delay_min": 5,
|
|
"response_delay_max": 30,
|
|
"sentiment_bias": 0.0,
|
|
"stance": "neutral",
|
|
"influence_weight": 1.0
|
|
}
|
|
else:
|
|
# Thuộc cho số đông (Cư dân mạng / Người Qua Đường): Phấn khích về đêm
|
|
return {
|
|
"activity_level": 0.7,
|
|
"posts_per_hour": 0.5,
|
|
"comments_per_hour": 1.2,
|
|
"active_hours": [9, 10, 11, 12, 13, 18, 19, 20, 21, 22, 23], # Ban Ngày rảnh + Buổi tối rảnh
|
|
"response_delay_min": 2,
|
|
"response_delay_max": 20,
|
|
"sentiment_bias": 0.0,
|
|
"stance": "neutral",
|
|
"influence_weight": 1.0
|
|
}
|
|
|
|
|