Introduce Project ID for context management, finalizing the stateful API pipeline from file submission to graph construction.

This commit is contained in:
666ghj
2025-11-28 17:21:08 +08:00
parent 9657061b26
commit 08f417f3b7
20 changed files with 2850 additions and 1 deletions

View File

@@ -0,0 +1,361 @@
"""
本体生成服务
接口1分析文本内容生成适合社会模拟的实体和关系类型定义
"""
import json
from typing import Dict, Any, List, Optional
from ..utils.llm_client import LLMClient
# 本体生成的系统提示词
ONTOLOGY_SYSTEM_PROMPT = """你是一个专业的知识图谱本体设计专家。你的任务是分析给定的文本内容和模拟需求,设计适合**社交媒体舆论模拟**的实体类型和关系类型。
**重要你必须输出有效的JSON格式数据不要输出任何其他内容。**
## 核心任务背景
我们正在构建一个**社交媒体舆论模拟系统**。在这个系统中:
- 每个实体都是一个可以在社交媒体上发声、互动、传播信息的"账号""主体"
- 实体之间会相互影响、转发、评论、回应
- 我们需要模拟舆论事件中各方的反应和信息传播路径
因此,**实体必须是现实中真实存在的、可以在社媒上发声和互动的主体**
**可以是(鼓励多样化划分)**
- 具体的个人(公众人物、当事人、意见领袖、专家学者)
- 公司、企业(包括其官方账号)
- 组织机构大学、协会、NGO、工会等
- 政府部门、监管机构
- 媒体机构(报纸、电视台、自媒体、网站)
- 社交媒体平台本身
- 特定群体代表(如校友会、粉丝团、维权群体等)
**不可以是**
- 抽象概念(如"舆论""情绪""趋势"
- 主题/话题(如"学术诚信""教育改革"
- 观点/态度(如"支持方""反对方"
- 泛指群体(如"网友""公众""学生群体"
## 输出格式
请输出JSON格式包含以下结构
```json
{
"entity_types": [
{
"name": "实体类型名称英文PascalCase",
"description": "简短描述英文不超过100字符",
"attributes": [
{
"name": "属性名英文snake_case",
"type": "text",
"description": "属性描述"
}
],
"examples": ["示例实体1", "示例实体2"]
}
],
"edge_types": [
{
"name": "关系类型名称英文UPPER_SNAKE_CASE",
"description": "简短描述英文不超过100字符",
"source_targets": [
{"source": "源实体类型", "target": "目标实体类型"}
],
"attributes": []
}
],
"analysis_summary": "对文本内容的简要分析说明(中文)"
}
```
## 设计指南
1. **实体类型设计(重要!请尽量多划分)**
- **数量要求至少5个最多10个实体类型**
- 每个实体类型代表一类可以在社媒上发声的主体
- 尽量细分不同角色,例如:
- 不要只用"Person",可以细分为"PublicFigure""Expert""Whistleblower"
- 不要只用"Organization",可以细分为"University""Company""NGO"
- description必须清晰说明什么样的实体应该被提取
- 每个类型提供2-3个具体示例
2. **关系类型设计**
- 关系应该反映社媒互动中的真实联系
- 关注可能影响舆论传播的关系:
- 信息传播REPORTS_ON, COMMENTS_ON, SHARES
- 组织关系AFFILIATED_WITH, WORKS_FOR, REPRESENTS
- 互动关系RESPONDS_TO, SUPPORTS, OPPOSES
- 关系类型5-10个为宜
3. **属性设计**
- 每个实体类型1-3个关键属性
- 属性应有助于识别实体的社媒影响力如role、influence_level等
## 实体类型参考(请根据文本内容灵活选择和扩展)
- Person: 普通个人
- PublicFigure: 公众人物(明星、网红、意见领袖)
- Expert: 专家学者
- Journalist: 记者
- Company: 公司企业
- University: 高校
- GovernmentAgency: 政府机构
- MediaOutlet: 传统媒体
- SelfMedia: 自媒体账号
- SocialPlatform: 社交媒体平台
- NGO: 非政府组织
- IndustryAssociation: 行业协会
- AlumniAssociation: 校友会
- FanGroup: 粉丝群体/支持群体
## 关系类型参考
- WORKS_FOR: 工作于
- AFFILIATED_WITH: 隶属于
- REPRESENTS: 代表
- REGULATES: 监管
- REPORTS_ON: 报道
- COMMENTS_ON: 评论
- RESPONDS_TO: 回应
- SUPPORTS: 支持
- OPPOSES: 反对
- COLLABORATES_WITH: 合作
- COMPETES_WITH: 竞争
"""
class OntologyGenerator:
"""
本体生成器
分析文本内容,生成实体和关系类型定义
"""
def __init__(self, llm_client: Optional[LLMClient] = None):
self.llm_client = llm_client or LLMClient()
def generate(
self,
document_texts: List[str],
simulation_requirement: str,
additional_context: Optional[str] = None
) -> Dict[str, Any]:
"""
生成本体定义
Args:
document_texts: 文档文本列表
simulation_requirement: 模拟需求描述
additional_context: 额外上下文
Returns:
本体定义entity_types, edge_types等
"""
# 构建用户消息
user_message = self._build_user_message(
document_texts,
simulation_requirement,
additional_context
)
messages = [
{"role": "system", "content": ONTOLOGY_SYSTEM_PROMPT},
{"role": "user", "content": user_message}
]
# 调用LLM
result = self.llm_client.chat_json(
messages=messages,
temperature=0.3,
max_tokens=4096
)
# 验证和后处理
result = self._validate_and_process(result)
return result
# 传给 LLM 的文本最大长度5万字
MAX_TEXT_LENGTH_FOR_LLM = 50000
def _build_user_message(
self,
document_texts: List[str],
simulation_requirement: str,
additional_context: Optional[str]
) -> str:
"""构建用户消息"""
# 合并文本
combined_text = "\n\n---\n\n".join(document_texts)
original_length = len(combined_text)
# 如果文本超过5万字截断仅影响传给LLM的内容不影响图谱构建
if len(combined_text) > self.MAX_TEXT_LENGTH_FOR_LLM:
combined_text = combined_text[:self.MAX_TEXT_LENGTH_FOR_LLM]
combined_text += f"\n\n...(原文共{original_length}字,已截取前{self.MAX_TEXT_LENGTH_FOR_LLM}字用于本体分析)..."
message = f"""## 模拟需求
{simulation_requirement}
## 文档内容
{combined_text}
"""
if additional_context:
message += f"""
## 额外说明
{additional_context}
"""
message += """
请根据以上内容,设计适合社会舆论模拟的实体类型和关系类型。
记住:所有实体类型必须是现实中可以发声的主体,不能是抽象概念。
"""
return message
def _validate_and_process(self, result: Dict[str, Any]) -> Dict[str, Any]:
"""验证和后处理结果"""
# 确保必要字段存在
if "entity_types" not in result:
result["entity_types"] = []
if "edge_types" not in result:
result["edge_types"] = []
if "analysis_summary" not in result:
result["analysis_summary"] = ""
# 验证实体类型
for entity in result["entity_types"]:
if "attributes" not in entity:
entity["attributes"] = []
if "examples" not in entity:
entity["examples"] = []
# 确保description不超过100字符
if len(entity.get("description", "")) > 100:
entity["description"] = entity["description"][:97] + "..."
# 验证关系类型
for edge in result["edge_types"]:
if "source_targets" not in edge:
edge["source_targets"] = []
if "attributes" not in edge:
edge["attributes"] = []
if len(edge.get("description", "")) > 100:
edge["description"] = edge["description"][:97] + "..."
return result
def generate_python_code(self, ontology: Dict[str, Any]) -> str:
"""
将本体定义转换为Python代码类似ontology.py
Args:
ontology: 本体定义
Returns:
Python代码字符串
"""
code_lines = [
'"""',
'自定义实体类型定义',
'由MiroFish自动生成用于社会舆论模拟',
'"""',
'',
'from pydantic import Field',
'from zep_cloud.external_clients.ontology import EntityModel, EntityText, EdgeModel',
'',
'',
'# ============== 实体类型定义 ==============',
'',
]
# 生成实体类型
for entity in ontology.get("entity_types", []):
name = entity["name"]
desc = entity.get("description", f"A {name} entity.")
code_lines.append(f'class {name}(EntityModel):')
code_lines.append(f' """{desc}"""')
attrs = entity.get("attributes", [])
if attrs:
for attr in attrs:
attr_name = attr["name"]
attr_desc = attr.get("description", attr_name)
code_lines.append(f' {attr_name}: EntityText = Field(')
code_lines.append(f' description="{attr_desc}",')
code_lines.append(f' default=None')
code_lines.append(f' )')
else:
code_lines.append(' pass')
code_lines.append('')
code_lines.append('')
code_lines.append('# ============== 关系类型定义 ==============')
code_lines.append('')
# 生成关系类型
for edge in ontology.get("edge_types", []):
name = edge["name"]
# 转换为PascalCase类名
class_name = ''.join(word.capitalize() for word in name.split('_'))
desc = edge.get("description", f"A {name} relationship.")
code_lines.append(f'class {class_name}(EdgeModel):')
code_lines.append(f' """{desc}"""')
attrs = edge.get("attributes", [])
if attrs:
for attr in attrs:
attr_name = attr["name"]
attr_desc = attr.get("description", attr_name)
code_lines.append(f' {attr_name}: EntityText = Field(')
code_lines.append(f' description="{attr_desc}",')
code_lines.append(f' default=None')
code_lines.append(f' )')
else:
code_lines.append(' pass')
code_lines.append('')
code_lines.append('')
# 生成类型字典
code_lines.append('# ============== 类型配置 ==============')
code_lines.append('')
code_lines.append('ENTITY_TYPES = {')
for entity in ontology.get("entity_types", []):
name = entity["name"]
code_lines.append(f' "{name}": {name},')
code_lines.append('}')
code_lines.append('')
code_lines.append('EDGE_TYPES = {')
for edge in ontology.get("edge_types", []):
name = edge["name"]
class_name = ''.join(word.capitalize() for word in name.split('_'))
code_lines.append(f' "{name}": {class_name},')
code_lines.append('}')
code_lines.append('')
# 生成边的source_targets映射
code_lines.append('EDGE_SOURCE_TARGETS = {')
for edge in ontology.get("edge_types", []):
name = edge["name"]
source_targets = edge.get("source_targets", [])
if source_targets:
st_list = ', '.join([
f'{{"source": "{st.get("source", "Entity")}", "target": "{st.get("target", "Entity")}"}}'
for st in source_targets
])
code_lines.append(f' "{name}": [{st_list}],')
code_lines.append('}')
return '\n'.join(code_lines)