MiroFish/backend
666ghj d4fac63eb4 Enhance simulation management and logging features
- Registered a cleanup function for simulation processes to ensure proper termination on server shutdown.
- Improved logging during application startup to confirm the registration of the cleanup function.
- Updated simulation preparation checks to clarify the conditions for considering a simulation ready, enhancing error handling and user feedback.
- Added detailed logging for simulation status changes, improving traceability during the simulation lifecycle.
- Introduced new files for simulation configuration and profile data, supporting enhanced testing and visualization capabilities.
2025-12-02 17:11:47 +08:00
..
app Enhance simulation management and logging features 2025-12-02 17:11:47 +08:00
scripts Enhance simulation management and logging features 2025-12-02 17:11:47 +08:00
README.md Enhance OASIS simulation capabilities and profile generation 2025-12-01 19:40:07 +08:00
requirements.txt Enhance backend functionality with OASIS simulation features 2025-12-01 15:03:44 +08:00
run.py Enhance backend startup logging and API endpoint display 2025-11-28 18:59:36 +08:00

MiroFish Backend

社会舆论模拟系统后端服务基于Flask框架。

项目结构

backend/
├── app/
│   ├── __init__.py              # Flask应用工厂
│   ├── config.py                # 配置管理
│   ├── api/                     # API路由
│   │   ├── __init__.py          # Blueprint注册
│   │   ├── graph.py             # Step1: 图谱相关接口
│   │   └── simulation.py        # Step2: 模拟相关接口
│   ├── services/                # 业务逻辑层
│   │   ├── __init__.py          # 服务模块导出
│   │   ├── ontology_generator.py         # 本体生成服务
│   │   ├── graph_builder.py              # 图谱构建服务
│   │   ├── text_processor.py             # 文本处理服务
│   │   ├── zep_entity_reader.py          # Zep实体读取与过滤
│   │   ├── oasis_profile_generator.py    # Agent Profile生成器
│   │   ├── simulation_config_generator.py # LLM智能配置生成器核心
│   │   └── simulation_manager.py         # 模拟管理器
│   ├── models/                  # 数据模型
│   │   ├── task.py              # 任务状态管理
│   │   └── project.py           # 项目上下文管理
│   └── utils/                   # 工具模块
│       ├── file_parser.py       # 文件解析
│       ├── llm_client.py        # LLM客户端
│       └── logger.py            # 日志工具
├── scripts/                     # 预设模拟脚本
│   ├── run_twitter_simulation.py    # Twitter模拟脚本
│   ├── run_reddit_simulation.py     # Reddit模拟脚本
│   └── run_parallel_simulation.py   # 双平台并行脚本
├── uploads/                     # 上传文件存储
│   ├── projects/                # 项目文件
│   └── simulations/             # 模拟数据(含配置和脚本副本)
├── requirements.txt
└── run.py                       # 启动入口

安装

conda activate MiroFish
cd backend
pip install -r requirements.txt

配置

在项目根目录 MiroFish/.env 中配置:

# LLM配置统一使用OpenAI格式
LLM_API_KEY=your-llm-api-key
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_MODEL_NAME=gpt-4o-mini

# Zep配置
ZEP_API_KEY=your-zep-api-key

# OASIS模拟配置可选
OASIS_DEFAULT_MAX_ROUNDS=10

启动服务

python run.py

服务默认运行在 http://localhost:5001


系统架构

完整工作流程

┌─────────────────────────────────────────────────────────────────────────┐
│                           Step 1: 图谱构建                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   上传文档 ──→ 生成本体定义 ──→ 构建Zep图谱 ──→ 图谱数据               │
│   (PDF/MD/TXT)  (LLM分析)      (异步任务)      (节点/边)               │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                      Step 2: 实体读取与模拟准备                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   读取图谱节点 ──→ 过滤符合条件实体 ──→ 生成Agent Profile ──→ 生成脚本  │
│   (Zep API)       (按Labels筛选)      (LLM生成人设)      (OASIS启动)    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                      Step 3: 双平台并行模拟                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌─────────────────┐          ┌─────────────────┐                     │
│   │  Twitter模拟    │          │   Reddit模拟    │                     │
│   │  (短平快交互)   │ 并行运行 │  (深度话题讨论)  │                     │
│   └─────────────────┘          └─────────────────┘                     │
│                        │                                                │
│                        ▼                                                │
│               同一批智能体,模拟真实社交环境                             │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Step 1: 图谱构建 API

核心工作流程

1. 上传文件 + 生成本体
   POST /api/graph/ontology/generate
   → 返回 project_id
   
2. 构建图谱
   POST /api/graph/build
   → 返回 task_id
   
3. 查询任务进度
   GET /api/graph/task/{task_id}
   
4. 获取图谱数据
   GET /api/graph/data/{graph_id}

接口1生成本体定义

POST /api/graph/ontology/generate

上传文档,分析生成适合社会模拟的实体和关系类型定义。

请求form-data

字段 类型 必填 说明
files File PDF/MD/TXT文件可多个
simulation_requirement Text 模拟需求描述
project_name Text 项目名称
additional_context Text 额外说明

响应示例:

{
    "success": true,
    "data": {
        "project_id": "proj_abc123def456",
        "project_name": "武汉大学舆情分析",
        "ontology": {
            "entity_types": [
                {
                    "name": "Student",
                    "description": "Students enrolled in educational institutions",
                    "attributes": [
                        {"name": "student_id", "type": "text", "description": "Unique identifier"},
                        {"name": "major", "type": "text", "description": "Field of study"}
                    ]
                }
            ],
            "edge_types": [
                {
                    "name": "AFFILIATED_WITH",
                    "description": "Indicates affiliation between entities",
                    "source_targets": [
                        {"source": "Student", "target": "University"}
                    ]
                }
            ]
        },
        "analysis_summary": "分析说明...",
        "files": [{"filename": "报告.pdf", "size": 123456}],
        "total_text_length": 20833
    }
}

接口2构建图谱

POST /api/graph/build

根据 project_id 构建Zep知识图谱异步任务

请求JSON

{
    "project_id": "proj_abc123def456",
    "graph_name": "图谱名称",
    "chunk_size": 500,
    "chunk_overlap": 50
}
字段 类型 必填 说明
project_id string 来自接口1的返回
graph_name string 图谱名称
chunk_size int 文本块大小默认500
chunk_overlap int 块重叠字符默认50

响应:

{
    "success": true,
    "data": {
        "project_id": "proj_abc123def456",
        "task_id": "task_xyz789",
        "message": "图谱构建任务已启动"
    }
}

任务状态查询

GET /api/graph/task/{task_id}

{
    "success": true,
    "data": {
        "task_id": "task_xyz789",
        "status": "processing",
        "progress": 45,
        "message": "Zep处理中... 15/30 完成",
        "result": null
    }
}

状态值:

  • pending - 等待中
  • processing - 处理中
  • completed - 已完成
  • failed - 失败

项目管理接口

方法 路径 说明
GET /api/graph/project/{project_id} 获取项目详情
GET /api/graph/project/list 列出所有项目
DELETE /api/graph/project/{project_id} 删除项目

图谱数据接口

方法 路径 说明
GET /api/graph/data/{graph_id} 获取图谱节点和边
DELETE /api/graph/delete/{graph_id} 删除Zep图谱

Step 2: 实体读取与模拟运行 API

核心设计理念

全程自动化,无需人工设置参数:

  • 脚本是预设的,不是动态生成
  • 所有模拟参数由LLM智能生成
  • LLM读取模拟需求+文档+图谱信息,自动设置最佳参数
  • 通过API接口启动和监控模拟,前端可实时展示

核心工作流程

1. 创建模拟
   POST /api/simulation/create
   → 返回 simulation_id
   
2. 准备模拟环境(异步任务)
   POST /api/simulation/prepare
   Body: { "simulation_id": "sim_xxxx" }
   → 返回 task_id立即响应
   
   查询进度:
   POST /api/simulation/prepare/status
   Body: { "task_id": "task_xxxx" }
   → 返回 status, progress, result
   
3. 开始模拟
   POST /api/simulation/start
   Body: { "simulation_id": "sim_xxxx", "platform": "parallel" }
   → 在后台启动OASIS模拟进程
   → 返回运行状态

4. 实时监控(前端轮询)
   GET /api/simulation/{simulation_id}/run-status/detail
   → 返回当前进度、最近Agent动作
   
5. 停止模拟(可选)
   POST /api/simulation/stop
   Body: { "simulation_id": "sim_xxxx" }

实体读取接口

获取图谱实体(已过滤)

GET /api/simulation/entities/{graph_id}

获取图谱中符合预定义实体类型的节点。

实体过滤逻辑:

  • Zep对符合预定义类型的实体Labels为 ["Entity", "Student"]
  • 对不符合预定义类型的实体Labels仅为 ["Entity"]
  • 筛选规则只保留Labels中包含除"Entity"和"Node"之外标签的节点

Query参数

参数 类型 必填 说明
entity_types string 逗号分隔的实体类型,用于进一步过滤
enrich boolean 是否获取相关边信息默认true

响应示例:

{
    "success": true,
    "data": {
        "entities": [
            {
                "uuid": "node_uuid_123",
                "name": "杨景媛",
                "labels": ["Entity", "Student"],
                "summary": "武汉大学学生,图书馆事件当事人",
                "attributes": {
                    "student_id": "2021001",
                    "major": "计算机科学"
                },
                "related_edges": [
                    {
                        "direction": "outgoing",
                        "edge_name": "AFFILIATED_WITH",
                        "fact": "杨景媛是武汉大学的学生",
                        "target_node_uuid": "node_uuid_456"
                    }
                ],
                "related_nodes": [
                    {
                        "uuid": "node_uuid_456",
                        "name": "武汉大学",
                        "labels": ["Entity", "University"],
                        "summary": "中国著名高等学府"
                    }
                ]
            }
        ],
        "entity_types": ["Student", "University", "PublicFigure"],
        "total_count": 100,
        "filtered_count": 45
    }
}

获取单个实体详情

GET /api/simulation/entities/{graph_id}/{entity_uuid}

获取单个实体的完整信息,包含所有相关边和关联节点。


按类型获取实体

GET /api/simulation/entities/{graph_id}/by-type/{entity_type}

获取指定类型如Student、PublicFigure的所有实体。


模拟管理接口

创建模拟

POST /api/simulation/create

请求JSON

{
    "project_id": "proj_abc123def456",
    "graph_id": "mirofish_xxxx",
    "enable_twitter": true,
    "enable_reddit": true,
    "max_rounds": 10,
    "agents_per_round": -1
}
字段 类型 必填 说明
project_id string 项目ID
graph_id string 图谱ID不提供则从project获取
enable_twitter boolean 启用Twitter模拟默认true
enable_reddit boolean 启用Reddit模拟默认true
max_rounds int 最大模拟轮数默认10
agents_per_round int 每轮激活智能体数,-1表示全部

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_abc123def456",
        "config": {
            "project_id": "proj_xxxx",
            "graph_id": "mirofish_xxxx",
            "enable_twitter": true,
            "enable_reddit": true,
            "max_rounds": 10
        },
        "status": "created",
        "created_at": "2025-12-01T10:00:00"
    }
}

准备模拟环境(异步任务)

POST /api/simulation/prepare

异步接口:这是一个耗时操作,接口会立即返回task_id,通过/prepare/status查询进度。

执行模拟准备流程LLM智能生成所有参数带自动重试机制

  1. 从Zep图谱读取并过滤实体
  2. 为每个实体生成OASIS Agent Profile带重试
  3. LLM智能生成模拟配置带重试
  4. 保存配置文件和复制预设脚本

请求JSON

{
    "simulation_id": "sim_xxxx",
    "entity_types": ["Student", "PublicFigure"],
    "use_llm_for_profiles": true
}

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_xxxx",
        "task_id": "task_xxxx",
        "status": "preparing",
        "message": "准备任务已启动,请通过 /api/simulation/prepare/status 查询进度"
    }
}

查询准备进度

POST /api/simulation/prepare/status

查询准备任务的执行进度。

请求JSON

{
    "task_id": "task_xxxx"
}

响应示例:

{
    "success": true,
    "data": {
        "task_id": "task_xxxx",
        "task_type": "simulation_prepare",
        "status": "processing",
        "progress": 45,
        "message": "[2/4] 生成Agent人设: 35/93 - 生成 教授张三 的人设...",
        "progress_detail": {
            "current_stage": "generating_profiles",
            "current_stage_name": "生成Agent人设",
            "stage_index": 2,
            "total_stages": 4,
            "stage_progress": 38,
            "current_item": 35,
            "total_items": 93,
            "item_description": "生成 教授张三 的人设..."
        },
        "result": null,
        "error": null,
        "metadata": {
            "project_id": "proj_xxxx",
            "simulation_id": "sim_xxxx"
        }
    }
}

进度详情字段progress_detail

字段 类型 说明
current_stage string 当前阶段标识 (reading/generating_profiles/generating_config/copying_scripts)
current_stage_name string 当前阶段中文名称
stage_index int 当前阶段序号 (1-4)
total_stages int 总阶段数 (4)
stage_progress int 当前阶段内进度 (0-100)
current_item int 当前处理的项目序号
total_items int 当前阶段总项目数
item_description string 当前项目描述

阶段说明:

阶段 名称 权重 说明
1 读取图谱实体 0-20% 从Zep读取并过滤实体
2 生成Agent人设 20-70% 为每个实体生成OASIS Profile
3 生成模拟配置 70-90% LLM智能生成模拟参数
4 准备模拟脚本 90-100% 复制预设脚本到模拟目录

状态值status

  • pending - 等待中
  • processing - 处理中
  • completed - 已完成此时result包含结果
  • failed - 失败此时error包含错误信息

完成后的响应:

{
    "success": true,
    "data": {
        "task_id": "task_xxxx",
        "status": "completed",
        "progress": 100,
        "message": "任务完成",
        "result": {
            "simulation_id": "sim_xxxx",
            "project_id": "proj_xxxx",
            "graph_id": "mirofish_xxxx",
            "status": "ready",
            "entities_count": 93,
            "profiles_count": 93,
            "entity_types": ["University", "Student", ...],
            "config_generated": true,
            "error": null
        }
    }
}
字段 类型 必填 说明
entity_types array 指定实体类型进行过滤
use_llm_for_profiles boolean 是否使用LLM生成人设默认true

注意simulation_requirementdocument_text自动从项目中获取

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_abc123def456",
        "status": "ready",
        "entities_count": 45,
        "profiles_count": 45,
        "entity_types": ["Student", "PublicFigure", "University"],
        "config_generated": true,
        "config_reasoning": "根据武汉大学图书馆事件的特点设置72小时模拟时长...",
        "run_instructions": {
            "simulation_dir": "/path/to/sim_xxx",
            "commands": {...},
            "instructions": "..."
        }
    }
}

获取模拟状态

GET /api/simulation/{simulation_id}

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_abc123def456",
        "status": "ready",
        "entities_count": 45,
        "profiles_count": 45,
        "entity_types": ["Student", "PublicFigure"],
        "current_round": 0,
        "twitter_status": "not_started",
        "reddit_status": "not_started"
    }
}

列出所有模拟

GET /api/simulation/list

Query参数 类型 说明
project_id string 按项目ID过滤可选

获取Agent Profile

GET /api/simulation/{simulation_id}/profiles

Query参数 类型 说明
platform string 平台类型reddit 或 twitter

响应示例:

{
    "success": true,
    "data": {
        "platform": "reddit",
        "count": 45,
        "profiles": [
            {
                "user_id": 0,
                "user_name": "yangjingyuan_123",
                "name": "杨景媛",
                "bio": "武汉大学学生,关注教育公平与学生权益",
                "persona": "杨景媛是一名积极参与社会讨论的大学生,性格内敛但观点鲜明...",
                "karma": 1500,
                "age": 22,
                "gender": "female",
                "mbti": "INFJ",
                "country": "China",
                "profession": "Student",
                "interested_topics": ["Education", "Social Issues"]
            }
        ]
    }
}

获取模拟配置

GET /api/simulation/{simulation_id}/config

获取LLM智能生成的完整配置包含

  • time_config: 时间配置
  • agent_configs: 每个Agent的活动配置
  • event_config: 事件配置
  • generation_reasoning: LLM的配置推理说明

下载文件

接口 说明
GET /api/simulation/{id}/config/download 下载配置文件
GET /api/simulation/{id}/script/{script_name}/download 下载脚本文件

脚本名称:

  • run_twitter_simulation.py
  • run_reddit_simulation.py
  • run_parallel_simulation.py

直接生成Profile

POST /api/simulation/generate-profiles

不创建模拟直接从图谱生成Agent Profile。

{
    "graph_id": "mirofish_xxxx",
    "entity_types": ["Student", "PublicFigure"],
    "use_llm": true,
    "platform": "reddit"
}

模拟运行控制接口

开始模拟

POST /api/simulation/start

启动OASIS模拟在后台运行。

请求JSON

{
    "simulation_id": "sim_xxxx",  // 必填
    "platform": "parallel"         // 可选: twitter / reddit / parallel (默认)
}

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_xxxx",
        "runner_status": "running",
        "process_pid": 12345,
        "twitter_running": true,
        "reddit_running": true,
        "total_rounds": 144,
        "total_simulation_hours": 72,
        "started_at": "2025-12-01T10:00:00"
    }
}

停止模拟

POST /api/simulation/stop

停止正在运行的模拟。

请求JSON

{
    "simulation_id": "sim_xxxx"  // 必填
}

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_xxxx",
        "runner_status": "stopped",
        "completed_at": "2025-12-01T12:00:00",
        "twitter_actions_count": 500,
        "reddit_actions_count": 650
    }
}

实时状态监控接口

获取运行状态(基础)

GET /api/simulation/{simulation_id}/run-status

获取模拟运行的实时状态,用于前端轮询。

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_xxxx",
        "runner_status": "running",
        "current_round": 25,
        "total_rounds": 144,
        "progress_percent": 17.4,
        "simulated_hours": 12,
        "total_simulation_hours": 72,
        "twitter_running": true,
        "reddit_running": true,
        "twitter_actions_count": 150,
        "reddit_actions_count": 200,
        "total_actions_count": 350,
        "started_at": "2025-12-01T10:00:00",
        "updated_at": "2025-12-01T10:30:00"
    }
}

运行状态值runner_status

  • idle - 未运行
  • starting - 启动中
  • running - 运行中
  • paused - 已暂停
  • stopping - 停止中
  • stopped - 已停止
  • completed - 已完成
  • failed - 失败

获取运行状态(详细,含最近动作)

GET /api/simulation/{simulation_id}/run-status/detail

获取详细运行状态包含最近的Agent动作列表用于前端实时展示动态

响应示例:

{
    "success": true,
    "data": {
        "simulation_id": "sim_xxxx",
        "runner_status": "running",
        "current_round": 25,
        "progress_percent": 17.4,
        "recent_actions": [
            {
                "round_num": 25,
                "timestamp": "2025-12-01T10:30:00",
                "platform": "twitter",
                "agent_id": 3,
                "agent_name": "Entity Name",
                "action_type": "CREATE_POST",
                "action_args": {"content": "Post content..."},
                "result": null,
                "success": true
            },
            {
                "round_num": 25,
                "timestamp": "2025-12-01T10:29:55",
                "platform": "reddit",
                "agent_id": 7,
                "agent_name": "Another Entity",
                "action_type": "LIKE_POST",
                "action_args": {"post_id": 5},
                "success": true
            }
        ]
    }
}

获取动作历史

GET /api/simulation/{simulation_id}/actions

获取完整的Agent动作历史记录。

Query参数

参数 类型 说明
limit int 返回数量默认100
offset int 偏移量默认0
platform string 过滤平台twitter/reddit
agent_id int 过滤Agent ID
round_num int 过滤轮次

获取时间线

GET /api/simulation/{simulation_id}/timeline

获取按轮次汇总的时间线,用于前端展示进度条和时间线视图。

Query参数

参数 类型 说明
start_round int 起始轮次默认0
end_round int 结束轮次(默认全部)

响应示例:

{
    "success": true,
    "data": {
        "rounds_count": 25,
        "timeline": [
            {
                "round_num": 1,
                "twitter_actions": 10,
                "reddit_actions": 15,
                "total_actions": 25,
                "active_agents_count": 8,
                "active_agents": [0, 1, 3, 5, 7, 10, 12, 15],
                "action_types": {"CREATE_POST": 5, "LIKE_POST": 10, "LLM_ACTION": 10},
                "first_action_time": "2025-12-01T10:00:00",
                "last_action_time": "2025-12-01T10:05:00"
            }
        ]
    }
}

获取Agent统计

GET /api/simulation/{simulation_id}/agent-stats

获取每个Agent的活跃度统计用于展示排行榜。

响应示例:

{
    "success": true,
    "data": {
        "agents_count": 45,
        "stats": [
            {
                "agent_id": 3,
                "agent_name": "Active Agent",
                "total_actions": 50,
                "twitter_actions": 30,
                "reddit_actions": 20,
                "action_types": {"CREATE_POST": 10, "LIKE_POST": 25, "REPOST": 15},
                "first_action_time": "2025-12-01T10:00:00",
                "last_action_time": "2025-12-01T12:30:00"
            }
        ]
    }
}

数据库查询接口

获取帖子

GET /api/simulation/{simulation_id}/posts

从模拟数据库获取帖子列表。

Query参数

参数 类型 说明
platform string 平台类型twitter/reddit默认reddit
limit int 返回数量默认50
offset int 偏移量

获取评论

GET /api/simulation/{simulation_id}/comments

从Reddit模拟数据库获取评论列表。

Query参数

参数 类型 说明
post_id string 过滤帖子ID可选
limit int 返回数量默认50
offset int 偏移量

服务层实现细节

1. ZepEntityReaderZep实体读取服务

文件: app/services/zep_entity_reader.py

核心功能

方法 说明
get_all_nodes(graph_id) 获取图谱所有节点
get_all_edges(graph_id) 获取图谱所有边
filter_defined_entities(graph_id, ...) 筛选符合条件的实体
get_entity_with_context(graph_id, uuid) 获取实体完整上下文
get_entities_by_type(graph_id, type) 按类型获取实体

数据结构

@dataclass
class EntityNode:
    uuid: str                    # 节点UUID
    name: str                    # 实体名称
    labels: List[str]            # 标签列表 ["Entity", "Student"]
    summary: str                 # 实体摘要
    attributes: Dict[str, Any]   # 属性字典
    related_edges: List[Dict]    # 相关边信息
    related_nodes: List[Dict]    # 关联节点信息
    
    def get_entity_type(self) -> Optional[str]:
        """获取实体类型排除默认Entity标签"""

@dataclass
class FilteredEntities:
    entities: List[EntityNode]   # 实体列表
    entity_types: Set[str]       # 发现的实体类型
    total_count: int             # 总节点数
    filtered_count: int          # 过滤后数量

过滤逻辑示例

# Zep返回的节点Labels示例
# 符合预定义类型: ["Entity", "Student"]
# 不符合预定义类型: ["Entity"]

for node in all_nodes:
    labels = node.get("labels", [])
    custom_labels = [l for l in labels if l not in ["Entity", "Node"]]
    
    if not custom_labels:
        # 只有默认标签,跳过
        continue
    
    # 保留符合条件的实体
    entity_type = custom_labels[0]
    filtered_entities.append(node)

2. OasisProfileGeneratorAgent Profile生成器

文件: app/services/oasis_profile_generator.py

核心功能

方法 说明
generate_profile_from_entity(entity, user_id) 从实体生成单个Profile带详细人设
generate_profiles_from_entities(entities, graph_id) 批量生成Profile
save_profiles(profiles, path, platform) 保存Profile文件
_search_zep_for_entity(entity_name) 调用Zep检索获取额外上下文

优化特性v2.0

  1. Zep混合搜索功能:使用多种查询策略获取丰富的实体信息
  2. 区分实体类型:个人实体 vs 群体/机构实体,使用不同的提示词
  3. 详细人设生成生成500字以上的详细人设描述

Zep混合搜索策略

_search_zep_for_entity() 方法采用多种搜索策略获取丰富信息:

查询策略:

queries = [
    f"总结{entity_name}的全部活动、事件和行为",
    f"{entity_name}与其他实体的关系和互动",
    f"{entity_name}的背景、历史和重要信息",
    f"关于{entity_name}的所有事实和描述",
]

说明: Zep没有内置的混合搜索接口需要分别搜索edges和nodes。我们使用并行请求同时执行两个搜索:

# 并行执行edges和nodes搜索
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    edge_future = executor.submit(search_edges)  # scope="edges"
    node_future = executor.submit(search_nodes)  # scope="nodes"
    
    edge_result = edge_future.result(timeout=30)
    node_result = node_future.result(timeout=30)

搜索参数:

搜索类型 scope limit 说明
边搜索 edges 30 获取事实/关系信息
节点搜索 nodes 20 获取相关实体摘要

关键参数:

  • 必须传递 graph_id 参数否则Zep API会返回400错误
  • 使用 rrf (Reciprocal Rank Fusion) reranker稳定可靠
  • 使用线程池并行执行,提高效率

返回数据结构:

{
    "facts": [...],           # 事实列表来自edges
    "node_summaries": [...],  # 相关节点摘要来自nodes
    "context": "..."          # 综合上下文文本
}

LLM生成与JSON修复

为了避免LLM生成的JSON解析失败实现了以下优化

  1. 不限制max_tokens让LLM自由发挥充分利用模型的上下文能力
  2. 多次重试机制最多3次尝试每次降低temperature
  3. 截断检测与修复:检测finish_reason='length'自动闭合JSON
  4. 完善JSON修复机制
    • _fix_truncated_json(): 修复被截断的JSON闭合括号和字符串
    • _try_fix_json(): 多级修复策略
      • 提取JSON部分
      • 替换字符串中的换行符
      • 移除控制字符
      • 从损坏JSON中提取部分信息
  5. 字段验证确保必需字段存在缺失时使用entity_summary填充

错误处理流程

LLM调用 → 检查截断 → JSON解析 → 修复尝试 → 部分提取 → 规则生成

并行生成与实时输出

支持并行生成Agent人设提高生成效率

profiles = generator.generate_profiles_from_entities(
    entities=filtered.entities,
    use_llm=True,
    graph_id="mirofish_xxx",
    parallel_count=5  # 并行生成数量默认5
)

API参数

POST /api/simulation/prepare
{
    "simulation_id": "sim_xxx",
    "parallel_profile_count": 5,   // 可选并行生成人设数量默认5
    "force_regenerate": false      // 可选强制重新生成默认false
}

实时输出

  • 每生成一个人设,立即输出到控制台(完整内容不截断)
  • 包含用户名、简介、详细人设、年龄、性别、MBTI等信息
  • 方便实时监控生成进度和质量

避免重复生成

系统会自动检测已完成的准备工作,避免重复生成:

检测条件

  1. state.json 存在且 config_generated=true
  2. 必要文件存在:reddit_profiles.json, twitter_profiles.csv, simulation_config.json

API响应

// 已准备完成时
{
    "success": true,
    "data": {
        "simulation_id": "sim_xxx",
        "status": "ready",
        "message": "已有完成的准备工作,无需重复生成",
        "already_prepared": true,
        "prepare_info": {
            "entities_count": 93,
            "profiles_count": 93,
            "entity_types": ["Student", "Professor", ...],
            "existing_files": [...]
        }
    }
}

强制重新生成

POST /api/simulation/prepare
{
    "simulation_id": "sim_xxx",
    "force_regenerate": true  // 忽略已有准备,强制重新生成
}

实体类型分类

# 个人类型实体 - 生成具体人物设定
INDIVIDUAL_ENTITY_TYPES = [
    "student", "alumni", "professor", "person", "publicfigure", 
    "expert", "faculty", "official", "journalist", "activist"
]

# 群体/机构类型实体 - 生成官方账号设定
GROUP_ENTITY_TYPES = [
    "university", "governmentagency", "organization", "ngo", 
    "mediaoutlet", "company", "institution", "group", "community"
]

Profile数据结构

@dataclass
class OasisAgentProfile:
    # 基础字段
    user_id: int              # 用户ID
    user_name: str            # 用户名
    name: str                 # 显示名称
    bio: str                  # 简介max 150字符
    persona: str              # 详细人设描述500字以上
    
    # Reddit字段
    karma: int = 1000
    
    # Twitter字段
    friend_count: int = 100
    follower_count: int = 150
    statuses_count: int = 500
    
    # 人设详情
    age: Optional[int] = None
    gender: Optional[str] = None
    mbti: Optional[str] = None       # INTJ, ENFP等
    country: Optional[str] = None
    profession: Optional[str] = None
    interested_topics: List[str] = []
    
    # 来源信息
    source_entity_uuid: Optional[str] = None
    source_entity_type: Optional[str] = None

详细人设生成示例

个人实体人设结构:

## 一、基本信息
- 姓名/称呼、年龄、职业/身份
- 教育背景、所在地

## 二、人物背景
- 过去的重要经历
- 与事件的关联
- 社会关系网络

## 三、性格特征
- MBTI类型及表现
- 核心性格特点
- 情绪表达方式

## 四、社交媒体行为模式
- 发帖频率和时间
- 内容偏好类型
- 语言风格特点

## 五、立场与观点
- 对核心话题的态度
- 可能被激怒/感动的内容

## 六、独特特征
- 口头禅、个人爱好等

Profile生成策略

1. LLM生成默认

使用LLM根据实体信息生成详细人设

prompt = f"""
Entity: {entity_name} ({entity_type})
Summary: {entity_summary}
Context: {related_edges_and_nodes}

Generate a social media user profile with:
- bio (max 150 chars)
- persona (detailed description)
- age, gender, mbti, country
- profession, interested_topics
"""

2. 规则生成Fallback

根据实体类型使用预定义模板:

实体类型 生成策略
Student/Alumni 年龄18-30学生身份关注教育话题
PublicFigure/Expert 年龄35-60专业人士政治经济话题
MediaOutlet 媒体官方账号,新闻时事话题
University/GovernmentAgency 机构官方账号,政策公告话题

3. SimulationConfigGenerator模拟配置智能生成器

文件: app/services/simulation_config_generator.py

核心功能

使用LLM分析模拟需求、文档内容、图谱实体信息自动生成最佳的模拟参数配置。

采用分步生成策略(避免一次性生成过长内容导致失败):

  1. 生成时间配置(轻量级)
  2. 生成事件配置和热点话题
  3. 分批生成Agent配置每批5个,保证生成质量)
  4. 生成平台配置
方法 说明
generate_config(...) 智能生成完整模拟配置(分步)
_generate_time_config(...) 生成时间配置
_generate_event_config(...) 生成事件配置
_generate_agent_configs_batch(...) 分批生成Agent配置
_generate_agent_config_by_rule(...) 规则生成LLM失败时

中国人作息时间配置

系统针对中国用户群体,采用符合北京时间的作息习惯:

时段 时间范围 活跃度系数 说明
深夜 0:00-5:59 0.05 几乎无人活动
早间 6:00-8:59 0.4 逐渐醒来
工作 9:00-18:59 0.7 工作时段中等活跃
高峰 19:00-22:59 1.5 晚间最活跃
夜间 23:00-23:59 0.5 活跃度下降

LLM智能生成的配置内容

1. TimeSimulationConfig时间配置

@dataclass
class TimeSimulationConfig:
    total_simulation_hours: int = 72      # 模拟总时长(小时)
    minutes_per_round: int = 30           # 每轮代表的时间(分钟)
    agents_per_hour_min: int = 5          # 每小时激活Agent数量最小
    agents_per_hour_max: int = 20         # 每小时激活Agent数量最大
    peak_hours: List[int] = [19,20,21,22] # 高峰时段(晚间)
    off_peak_hours: List[int] = [0,1,2,3,4,5]  # 低谷时段(凌晨)
    peak_activity_multiplier: float = 1.5 # 高峰活跃度乘数
    off_peak_activity_multiplier: float = 0.05  # 凌晨活跃度极低
    morning_hours: List[int] = [6,7,8]    # 早间时段
    morning_activity_multiplier: float = 0.4
    work_hours: List[int] = [9-18]        # 工作时段
    work_activity_multiplier: float = 0.7

2. AgentActivityConfig每个Agent的活动配置

@dataclass
class AgentActivityConfig:
    agent_id: int
    entity_uuid: str
    entity_name: str
    entity_type: str
    
    activity_level: float = 0.5           # 整体活跃度 (0.0-1.0)
    posts_per_hour: float = 1.0           # 每小时发帖频率
    comments_per_hour: float = 2.0        # 每小时评论频率
    active_hours: List[int]               # 活跃时间段 (0-23)
    response_delay_min: int = 5           # 响应延迟最小值(分钟)
    response_delay_max: int = 60          # 响应延迟最大值(分钟)
    sentiment_bias: float = 0.0           # 情感倾向 (-1到1)
    stance: str = "neutral"               # 立场 (supportive/opposing/neutral/observer)
    influence_weight: float = 1.0         # 影响力权重

3. 不同实体类型的默认参数差异(符合中国人作息)

实体类型 活跃度 发帖频率 活跃时段 响应延迟 影响力
University/GovernmentAgency 0.2 0.1/小时 9:00-17:59工作时间 60-240分钟 3.0
MediaOutlet 0.5 0.8/小时 7:00-23:59全天 5-30分钟 2.5
Professor/Expert 0.4 0.3/小时 8:00-21:59工作+晚间) 15-90分钟 2.0
Student 0.8 0.6/小时 8-13, 18-23上午+晚间) 1-15分钟 0.8
Alumni 0.6 0.4/小时 12-13, 19-23午休+晚间) 5-30分钟 1.0
Person普通人 0.7 0.5/小时 9-13, 18-23白天+晚间) 2-20分钟 1.0

注意凌晨0-5点所有实体类型都几乎不活动符合中国人作息习惯


4. SimulationManager模拟管理器

文件: app/services/simulation_manager.py

核心功能

方法 说明
create_simulation(project_id, graph_id, ...) 创建模拟
prepare_simulation(simulation_id, ...) 准备模拟环境(调用配置生成器)
get_simulation(simulation_id) 获取模拟状态
get_profiles(simulation_id, platform) 获取Profile
get_simulation_config(simulation_id) 获取模拟配置
get_run_instructions(simulation_id) 获取运行说明

模拟状态流转

created → preparing → ready → running → completed
                ↓              ↓
             failed         paused

生成的文件结构

uploads/simulations/sim_xxxx/
├── state.json                      # 模拟状态
├── simulation_config.json          # LLM生成的模拟配置核心文件
├── reddit_profiles.json            # Reddit Agent ProfileJSON格式
├── twitter_profiles.csv            # Twitter Agent ProfileCSV格式
├── run_reddit_simulation.py        # 预设Reddit模拟脚本
├── run_twitter_simulation.py       # 预设Twitter模拟脚本
├── run_parallel_simulation.py      # 预设双平台并行脚本
├── reddit_simulation.db            # Reddit数据库运行后生成
└── twitter_simulation.db           # Twitter数据库运行后生成

重要OASIS平台的Profile格式要求不同

Twitter CSV格式符合OASIS官方要求

user_id,name,username,user_char,description
0,张教授,professor_zhang,"完整人设描述LLM内部使用","简短简介(外部显示)"
  • user_id: 从0开始的顺序ID
  • name: 真实姓名
  • username: 系统用户名
  • user_char: 完整人设bio + persona注入LLM系统提示指导Agent行为
  • description: 简短简介,显示在用户资料页面

Reddit JSON格式

[
  {
    "realname": "张教授",
    "username": "professor_zhang",
    "bio": "简短简介",
    "persona": "详细人设描述",
    "age": 42,
    "gender": "男",
    "mbti": "INTJ",
    "country": "中国",
    "profession": "教授",
    "interested_topics": ["高等教育", "学术诚信"]
  }
]

user_char vs description 区别

字段 用途 可见性
user_char LLM系统提示决定Agent如何思考和行动 内部使用
description 用户资料页面的简介 其他用户可见

配置文件示例 (simulation_config.json)

{
  "simulation_id": "sim_abc123",
  "project_id": "proj_xxx",
  "graph_id": "mirofish_xxx",
  "simulation_requirement": "分析武汉大学图书馆事件舆论传播",
  
  "time_config": {
    "total_simulation_hours": 72,
    "minutes_per_round": 30,
    "agents_per_hour_min": 5,
    "agents_per_hour_max": 15,
    "peak_hours": [9, 10, 11, 14, 15, 20, 21, 22],
    "off_peak_hours": [0, 1, 2, 3, 4, 5],
    "peak_activity_multiplier": 1.5,
    "off_peak_activity_multiplier": 0.3
  },
  
  "agent_configs": [
    {
      "agent_id": 0,
      "entity_name": "武汉大学",
      "entity_type": "University",
      "activity_level": 0.15,
      "posts_per_hour": 0.08,
      "comments_per_hour": 0.02,
      "active_hours": [9, 10, 11, 14, 15, 16, 17],
      "response_delay_min": 120,
      "response_delay_max": 360,
      "sentiment_bias": 0.1,
      "stance": "neutral",
      "influence_weight": 4.0
    },
    {
      "agent_id": 1,
      "entity_name": "杨景媛",
      "entity_type": "Student",
      "activity_level": 0.8,
      "posts_per_hour": 0.5,
      "comments_per_hour": 2.0,
      "active_hours": [7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
      "response_delay_min": 1,
      "response_delay_max": 15,
      "sentiment_bias": -0.3,
      "stance": "opposing",
      "influence_weight": 1.5
    }
  ],
  
  "event_config": {
    "initial_posts": [
      {
        "poster_agent_id": 1,
        "content": "今天在图书馆发生的事情让我非常失望..."
      }
    ],
    "hot_topics": ["图书馆事件", "学生权益", "校方回应"],
    "narrative_direction": "事件发酵后各方反应的模拟"
  },
  
  "generation_reasoning": "根据武汉大学图书馆事件的特点1)涉及学生与校方的冲突设置学生高活跃度、校方低频但高影响力2)事件性质属于短期热点设置72小时模拟时长3)主要当事人杨景媛设置为高活跃度且持opposing立场..."
}

---

## 5. 预设模拟脚本

**目录:** `backend/scripts/`

脚本是**预设的**,不是动态生成。每次准备模拟时,脚本会被复制到模拟目录。

### 脚本说明

| 脚本 | 说明 |
|------|------|
| `run_twitter_simulation.py` | Twitter单平台模拟 |
| `run_reddit_simulation.py` | Reddit单平台模拟 |
| `run_parallel_simulation.py` | 双平台并行模拟(推荐) |

### 脚本工作原理

```python
# 脚本读取配置文件,自动设置所有参数
class TwitterSimulationRunner:
    def __init__(self, config_path: str):
        self.config = self._load_config()  # 读取simulation_config.json
    
    def _get_active_agents_for_round(self, env, current_hour, round_num):
        """根据时间和配置决定本轮激活哪些Agent"""
        time_config = self.config.get("time_config", {})
        agent_configs = self.config.get("agent_configs", [])
        
        # 1. 检查是否高峰/低谷时段,调整激活数量
        # 2. 遍历每个Agent配置检查是否在活跃时间
        # 3. 根据activity_level计算激活概率
        # 4. 返回本轮应激活的Agent列表
        ...
    
    async def run(self):
        # 1. 创建LLM模型
        # 2. 加载Agent图
        # 3. 执行初始事件从event_config读取
        # 4. 主循环根据配置激活不同Agent
        ...

使用方式

# 进入模拟目录
cd backend/uploads/simulations/sim_xxxx/

# 运行模拟
python run_parallel_simulation.py --config simulation_config.json

# 其他选项
python run_parallel_simulation.py --config simulation_config.json --twitter-only
python run_parallel_simulation.py --config simulation_config.json --reddit-only

6. Profile文件格式说明

OASIS对两个平台的Profile格式有不同要求

Twitter Profile (CSV格式)

user_id,user_name,name,bio,friend_count,follower_count,statuses_count,created_at
0,user0,User Zero,I am user zero with interests in technology.,100,150,500,2023-01-01
1,user1,User One,Tech enthusiast and coffee lover.,200,250,1000,2023-01-02
字段 类型 说明
user_id int 用户ID
user_name string 用户名
name string 显示名称
bio string 简介
friend_count int 关注数
follower_count int 粉丝数
statuses_count int 发帖数
created_at string 创建日期

Reddit Profile (JSON详细格式)

[
  {
    "realname": "Test User",
    "username": "test_user_123",
    "bio": "A test user for validation",
    "persona": "Test User is an enthusiastic participant in social discussions.",
    "age": 25,
    "gender": "male",
    "mbti": "INTJ",
    "country": "China",
    "profession": "Student",
    "interested_topics": ["Technology", "Education"]
  }
]
字段 类型 必填 说明
realname string 真实姓名
username string 用户名
bio string 简介最大150字符
persona string 详细人设描述
age int 年龄
gender string 性别
mbti string MBTI人格类型
country string 国家
profession string 职业
interested_topics array 感兴趣话题列表

7. OASIS平台动作类型

Twitter可用动作

动作 说明
CREATE_POST 发布推文
LIKE_POST 点赞推文
REPOST 转发推文
FOLLOW 关注用户
QUOTE_POST 引用转发
DO_NOTHING 不执行动作

Reddit可用动作

动作 说明
CREATE_POST 发布帖子
CREATE_COMMENT 发表评论
LIKE_POST 点赞帖子
DISLIKE_POST 踩帖子
LIKE_COMMENT 点赞评论
DISLIKE_COMMENT 踩评论
SEARCH_POSTS 搜索帖子
SEARCH_USER 搜索用户
TREND 查看热门
REFRESH 刷新推荐
FOLLOW 关注用户
MUTE 屏蔽用户
DO_NOTHING 不执行动作

实体设计原则

本系统专为社会舆论模拟设计,实体必须是:

可以是:

  • 具体的个人(有名有姓)
  • 注册的公司、组织、机构
  • 媒体机构
  • 政府部门
  • 高校、NGO等

不可以是:

  • 抽象概念(如"技术"、"创新"
  • 情绪、观点、趋势
  • 泛指的群体(如"用户"、"消费者"

这是因为后续需要模拟各实体对舆论的反应和传播,抽象概念无法参与这种模拟。


项目状态流转

created → ontology_generated → graph_building → graph_completed
                                     ↓
                                  failed

运行模拟

准备完成后,进入模拟数据目录运行预设脚本:

# 激活conda环境
conda activate MiroFish

# 进入模拟目录
cd backend/uploads/simulations/sim_xxxx/

# 运行单平台模拟
python run_reddit_simulation.py --config simulation_config.json
# 或
python run_twitter_simulation.py --config simulation_config.json

# 运行双平台并行模拟(推荐)
python run_parallel_simulation.py --config simulation_config.json

脚本参数

参数 说明
--config 配置文件路径(必填)
--twitter-only 只运行Twitter模拟仅parallel脚本
--reddit-only 只运行Reddit模拟仅parallel脚本

输出文件

模拟运行后会生成:

  • twitter_simulation.db - Twitter模拟数据库
  • reddit_simulation.db - Reddit模拟数据库

可使用SQLite工具查看模拟结果帖子、评论、点赞等


API调用重试机制

文件: app/utils/retry.py

为LLM等外部API调用提供自动重试功能提高系统稳定性。

重试策略

  • 最大重试次数3次
  • 退避策略指数退避1s → 2s → 4s
  • 最大延迟30秒
  • 随机抖动:避免请求堆积

使用方式

装饰器方式:

from app.utils.retry import retry_with_backoff

@retry_with_backoff(max_retries=3)
def call_llm_api():
    return client.chat.completions.create(...)

客户端方式:

from app.utils.retry import RetryableAPIClient

retry_client = RetryableAPIClient(max_retries=3)
result = retry_client.call_with_retry(some_function, arg1, arg2)

批量处理(单项失败不影响其他):

results, failures = retry_client.call_batch_with_retry(
    items=entities,
    process_func=generate_profile,
    continue_on_failure=True
)

已应用重试机制的模块

模块 说明
OasisProfileGenerator LLM生成Agent人设
SimulationConfigGenerator LLM生成模拟配置

依赖说明

# Flask框架
flask>=3.0.0
flask-cors>=4.0.0

# Zep Cloud SDK
zep-cloud>=2.0.0

# OpenAI SDKLLM调用
openai>=1.0.0

# PDF处理
PyMuPDF>=1.24.0

# 环境变量
python-dotenv>=1.0.0

# 数据验证
pydantic>=2.0.0

# OASIS社交媒体模拟
oasis-ai>=0.1.0
camel-ai>=0.2.0