Fix/language unification (#283)
* [changes]add user_summary language unification * [add]Entity extraction, user memory, emotion suggestions, unified language type for writing * [add]Complete the switch between Chinese and English for the emotion labels and emotion suggestions fields. * [changes]add user_summary language unification * [add]Entity extraction, user memory, emotion suggestions, unified language type for writing * [add]Complete the switch between Chinese and English for the emotion labels and emotion suggestions fields. * [changes]Modify the code based on the AI review
This commit is contained in:
@@ -11,6 +11,7 @@ Routes:
|
||||
"""
|
||||
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.response_utils import fail, success
|
||||
from app.dependencies import get_current_user, get_db
|
||||
@@ -45,11 +46,14 @@ emotion_service = EmotionAnalyticsService()
|
||||
@router.post("/tags", response_model=ApiResponse)
|
||||
async def get_emotion_tags(
|
||||
request: EmotionTagsRequest,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
|
||||
try:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
api_logger.info(
|
||||
f"用户 {current_user.username} 请求获取情绪标签统计",
|
||||
extra={
|
||||
@@ -57,7 +61,8 @@ async def get_emotion_tags(
|
||||
"emotion_type": request.emotion_type,
|
||||
"start_date": request.start_date,
|
||||
"end_date": request.end_date,
|
||||
"limit": request.limit
|
||||
"limit": request.limit,
|
||||
"language_type": language
|
||||
}
|
||||
)
|
||||
|
||||
@@ -67,7 +72,8 @@ async def get_emotion_tags(
|
||||
emotion_type=request.emotion_type,
|
||||
start_date=request.start_date,
|
||||
end_date=request.end_date,
|
||||
limit=request.limit
|
||||
limit=request.limit,
|
||||
language=language
|
||||
)
|
||||
|
||||
api_logger.info(
|
||||
@@ -97,11 +103,14 @@ async def get_emotion_tags(
|
||||
@router.post("/wordcloud", response_model=ApiResponse)
|
||||
async def get_emotion_wordcloud(
|
||||
request: EmotionWordcloudRequest,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
|
||||
try:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
api_logger.info(
|
||||
f"用户 {current_user.username} 请求获取情绪词云数据",
|
||||
extra={
|
||||
@@ -144,11 +153,14 @@ async def get_emotion_wordcloud(
|
||||
@router.post("/health", response_model=ApiResponse)
|
||||
async def get_emotion_health(
|
||||
request: EmotionHealthRequest,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
|
||||
try:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
# 验证时间范围参数
|
||||
if request.time_range not in ["7d", "30d", "90d"]:
|
||||
raise HTTPException(
|
||||
@@ -199,7 +211,7 @@ async def get_emotion_health(
|
||||
@router.post("/suggestions", response_model=ApiResponse)
|
||||
async def get_emotion_suggestions(
|
||||
request: EmotionSuggestionsRequest,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
@@ -214,6 +226,9 @@ async def get_emotion_suggestions(
|
||||
缓存的个性化情绪建议响应
|
||||
"""
|
||||
try:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
api_logger.info(
|
||||
f"用户 {current_user.username} 请求获取个性化情绪建议(缓存)",
|
||||
extra={
|
||||
@@ -265,7 +280,7 @@ async def get_emotion_suggestions(
|
||||
@router.post("/generate_suggestions", response_model=ApiResponse)
|
||||
async def generate_emotion_suggestions(
|
||||
request: EmotionGenerateSuggestionsRequest,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
@@ -280,6 +295,9 @@ async def generate_emotion_suggestions(
|
||||
新生成的个性化情绪建议响应
|
||||
"""
|
||||
try:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
api_logger.info(
|
||||
f"用户 {current_user.username} 请求生成个性化情绪建议",
|
||||
extra={
|
||||
@@ -290,7 +308,8 @@ async def generate_emotion_suggestions(
|
||||
# 调用服务层生成建议
|
||||
data = await emotion_service.generate_emotion_suggestions(
|
||||
end_user_id=request.end_user_id,
|
||||
db=db
|
||||
db=db,
|
||||
language=language
|
||||
)
|
||||
|
||||
# 保存到缓存
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import List, Optional
|
||||
|
||||
from app.celery_app import celery_app
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.rag.llm.cv_model import QWenCV
|
||||
from app.core.response_utils import fail, success
|
||||
@@ -118,6 +119,7 @@ async def download_log(
|
||||
@cur_workspace_access_guard()
|
||||
async def write_server(
|
||||
user_input: Write_UserInput,
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
@@ -126,13 +128,17 @@ async def write_server(
|
||||
|
||||
Args:
|
||||
user_input: Write request containing message and end_user_id
|
||||
language_type: 语言类型 ("zh" 中文, "en" 英文),通过 X-Language-Type Header 传递
|
||||
|
||||
Returns:
|
||||
Response with write operation status
|
||||
"""
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
config_id = user_input.config_id
|
||||
workspace_id = current_user.current_workspace_id
|
||||
api_logger.info(f"Write service: workspace_id={workspace_id}, config_id={config_id}")
|
||||
api_logger.info(f"Write service: workspace_id={workspace_id}, config_id={config_id}, language_type={language}")
|
||||
|
||||
# 获取 storage_type,如果为 None 则使用默认值
|
||||
storage_type = workspace_service.get_workspace_storage_type(
|
||||
@@ -169,7 +175,8 @@ async def write_server(
|
||||
config_id,
|
||||
db,
|
||||
storage_type,
|
||||
user_rag_memory_id
|
||||
user_rag_memory_id,
|
||||
language
|
||||
)
|
||||
|
||||
return success(data=result, msg="写入成功")
|
||||
@@ -188,6 +195,7 @@ async def write_server(
|
||||
@cur_workspace_access_guard()
|
||||
async def write_server_async(
|
||||
user_input: Write_UserInput,
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
@@ -196,14 +204,18 @@ async def write_server_async(
|
||||
|
||||
Args:
|
||||
user_input: Write request containing message and end_user_id
|
||||
language_type: 语言类型 ("zh" 中文, "en" 英文),通过 X-Language-Type Header 传递
|
||||
|
||||
Returns:
|
||||
Task ID for tracking async operation
|
||||
Use GET /memory/write_result/{task_id} to check task status and get result
|
||||
"""
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
config_id = user_input.config_id
|
||||
workspace_id = current_user.current_workspace_id
|
||||
api_logger.info(f"Async write service: workspace_id={workspace_id}, config_id={config_id}")
|
||||
api_logger.info(f"Async write service: workspace_id={workspace_id}, config_id={config_id}, language_type={language}")
|
||||
|
||||
# 获取 storage_type,如果为 None 则使用默认值
|
||||
storage_type = workspace_service.get_workspace_storage_type(
|
||||
@@ -228,7 +240,7 @@ async def write_server_async(
|
||||
|
||||
task = celery_app.send_task(
|
||||
"app.core.memory.agent.write_message",
|
||||
args=[user_input.end_user_id, messages_list, config_id, storage_type, user_rag_memory_id]
|
||||
args=[user_input.end_user_id, messages_list, config_id, storage_type, user_rag_memory_id, language]
|
||||
)
|
||||
api_logger.info(f"Write task queued: {task.id}")
|
||||
|
||||
@@ -653,7 +665,6 @@ async def get_knowledge_type_stats_api(
|
||||
@router.get("/analytics/hot_memory_tags/by_user", response_model=ApiResponse)
|
||||
async def get_hot_memory_tags_by_user_api(
|
||||
end_user_id: Optional[str] = Query(None, description="用户ID(可选)"),
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
limit: int = Query(20, description="返回标签数量限制"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session=Depends(get_db),
|
||||
@@ -661,28 +672,18 @@ async def get_hot_memory_tags_by_user_api(
|
||||
"""
|
||||
获取指定用户的热门记忆标签
|
||||
|
||||
注意:标签语言由写入时的 X-Language-Type 决定,查询时不进行翻译
|
||||
|
||||
返回格式:
|
||||
[
|
||||
{"name": "标签名", "frequency": 频次},
|
||||
...
|
||||
]
|
||||
"""
|
||||
|
||||
workspace_id=current_user.current_workspace_id
|
||||
workspace_repo = WorkspaceRepository(db)
|
||||
workspace_models = workspace_repo.get_workspace_models_configs(workspace_id)
|
||||
|
||||
if workspace_models:
|
||||
model_id = workspace_models.get("llm", None)
|
||||
else:
|
||||
model_id = None
|
||||
|
||||
api_logger.info(f"Hot memory tags by user requested: end_user_id={end_user_id}")
|
||||
try:
|
||||
result = await memory_agent_service.get_hot_memory_tags_by_user(
|
||||
end_user_id=end_user_id,
|
||||
language_type=language_type,
|
||||
model_id=model_id,
|
||||
limit=limit
|
||||
)
|
||||
return success(data=result, msg="获取热门记忆标签成功")
|
||||
|
||||
@@ -3,6 +3,7 @@ import time
|
||||
import uuid
|
||||
from uuid import UUID
|
||||
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.memory.storage_services.reflection_engine.self_reflexion import (
|
||||
ReflectionConfig,
|
||||
@@ -211,11 +212,13 @@ async def start_reflection_configs(
|
||||
@router.get("/reflection/run")
|
||||
async def reflection_run(
|
||||
config_id: UUID|int,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""Activate the reflection function for all matching applications in the workspace"""
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
api_logger.info(f"用户 {current_user.username} 查询反思配置,config_id: {config_id}")
|
||||
config_id = resolve_config_id(config_id, db)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status,Header
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.response_utils import success
|
||||
from app.db import get_db
|
||||
@@ -20,10 +21,13 @@ router = APIRouter(
|
||||
@router.get("/short_term")
|
||||
async def short_term_configs(
|
||||
end_user_id: str,
|
||||
language_type:str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type:str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
# 获取短期记忆数据
|
||||
short_term=ShortService(end_user_id)
|
||||
short_result=short_term.get_short_databasets()
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.response_utils import fail, success
|
||||
from app.db import get_db
|
||||
@@ -31,7 +32,7 @@ from app.services.memory_storage_service import (
|
||||
search_entity,
|
||||
search_statement,
|
||||
)
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, Header
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -280,17 +281,21 @@ def read_all_config(
|
||||
@router.post("/pilot_run", response_model=None)
|
||||
async def pilot_run(
|
||||
payload: ConfigPilotRun,
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> StreamingResponse:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
api_logger.info(
|
||||
f"Pilot run requested: config_id={payload.config_id}, "
|
||||
f"dialogue_text_length={len(payload.dialogue_text)}"
|
||||
f"dialogue_text_length={len(payload.dialogue_text)}, language={language}"
|
||||
)
|
||||
payload.config_id = resolve_config_id(payload.config_id, db)
|
||||
svc = DataConfigService(db)
|
||||
return StreamingResponse(
|
||||
svc.pilot_run_stream(payload),
|
||||
svc.pilot_run_stream(payload, language=language),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
|
||||
@@ -25,12 +25,12 @@ from fastapi import APIRouter, Depends, HTTPException, Header
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.response_utils import fail, success
|
||||
from app.db import get_db
|
||||
from app.dependencies import get_current_user
|
||||
from app.models.user_model import User
|
||||
from app.services.memory_base_service import Translation_English
|
||||
from app.core.memory.models.ontology_models import OntologyClass
|
||||
from typing import List
|
||||
from app.schemas.ontology_schemas import (
|
||||
@@ -63,72 +63,6 @@ router = APIRouter(
|
||||
)
|
||||
|
||||
|
||||
async def translate_ontology_classes(
|
||||
classes: List[OntologyClass],
|
||||
model_id: str
|
||||
) -> List[OntologyClass]:
|
||||
"""翻译本体类列表
|
||||
|
||||
将本体类的中文字段翻译为英文,包括:
|
||||
- name_chinese: 中文名称
|
||||
- description: 描述
|
||||
- examples: 示例列表
|
||||
|
||||
Args:
|
||||
classes: 本体类列表
|
||||
model_id: LLM模型ID,用于翻译
|
||||
|
||||
Returns:
|
||||
List[OntologyClass]: 翻译后的本体类列表
|
||||
"""
|
||||
translated_classes = []
|
||||
|
||||
for ontology_class in classes:
|
||||
# 创建类的副本,避免修改原对象
|
||||
translated_class = ontology_class.model_copy(deep=True)
|
||||
|
||||
# 翻译 name_chinese 字段
|
||||
if translated_class.name_chinese:
|
||||
try:
|
||||
translated_class.name_chinese = await Translation_English(
|
||||
model_id,
|
||||
translated_class.name_chinese
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to translate name_chinese: {e}")
|
||||
# 保留原文
|
||||
|
||||
# 翻译 description 字段
|
||||
if translated_class.description:
|
||||
try:
|
||||
translated_class.description = await Translation_English(
|
||||
model_id,
|
||||
translated_class.description
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to translate description: {e}")
|
||||
# 保留原文
|
||||
|
||||
# 翻译 examples 列表
|
||||
if translated_class.examples:
|
||||
translated_examples = []
|
||||
for example in translated_class.examples:
|
||||
try:
|
||||
translated_example = await Translation_English(
|
||||
model_id,
|
||||
example
|
||||
)
|
||||
translated_examples.append(translated_example)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to translate example: {e}")
|
||||
translated_examples.append(example) # 保留原文
|
||||
translated_class.examples = translated_examples
|
||||
|
||||
translated_classes.append(translated_class)
|
||||
|
||||
return translated_classes
|
||||
|
||||
|
||||
def _get_ontology_service(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
@@ -243,7 +177,7 @@ def _get_ontology_service(
|
||||
@router.post("/extract", response_model=ApiResponse)
|
||||
async def extract_ontology(
|
||||
request: ExtractionRequest,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
@@ -260,31 +194,6 @@ async def extract_ontology(
|
||||
db: 数据库会话
|
||||
current_user: 当前用户
|
||||
|
||||
Returns:
|
||||
ApiResponse: 包含提取结果的响应
|
||||
|
||||
Response format:
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "本体提取成功",
|
||||
"data": {
|
||||
"classes": [
|
||||
{
|
||||
"id": "147d9db50b524a9e909e01a753d3acdd",
|
||||
"name": "Patient",
|
||||
"name_chinese": "患者",
|
||||
"description": "在医疗机构中接受诊疗、护理或健康管理的个体",
|
||||
"examples": ["糖尿病患者", "术后康复患者", "门诊初诊患者"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Person",
|
||||
"domain": "Healthcare"
|
||||
},
|
||||
...
|
||||
],
|
||||
"domain": "Healthcare",
|
||||
"extracted_count": 7
|
||||
}
|
||||
}
|
||||
"""
|
||||
api_logger.info(
|
||||
f"Ontology extraction requested by user {current_user.id}, "
|
||||
@@ -296,6 +205,9 @@ async def extract_ontology(
|
||||
)
|
||||
|
||||
try:
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
# 获取当前工作空间ID
|
||||
workspace_id = current_user.current_workspace_id
|
||||
if not workspace_id:
|
||||
@@ -314,33 +226,11 @@ async def extract_ontology(
|
||||
scenario=request.scenario,
|
||||
domain=request.domain,
|
||||
scene_id=request.scene_id,
|
||||
workspace_id=workspace_id
|
||||
workspace_id=workspace_id,
|
||||
language=language
|
||||
)
|
||||
|
||||
# ===== 新增:翻译逻辑 =====
|
||||
# 如果需要英文,则翻译数据
|
||||
if language_type != 'zh':
|
||||
api_logger.info(f"Translating extraction result to English")
|
||||
|
||||
# 翻译 classes 列表
|
||||
result.classes = await translate_ontology_classes(
|
||||
result.classes,
|
||||
request.llm_id
|
||||
)
|
||||
|
||||
# 翻译 domain 字段
|
||||
if result.domain:
|
||||
try:
|
||||
result.domain = await Translation_English(
|
||||
request.llm_id,
|
||||
result.domain
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to translate domain: {e}")
|
||||
# 保留原文
|
||||
# ===== 翻译逻辑结束 =====
|
||||
|
||||
# 构建响应
|
||||
# 构建响应(语言已在提取时通过模板控制,无需二次翻译)
|
||||
response = ExtractionResponse(
|
||||
classes=result.classes,
|
||||
domain=result.domain,
|
||||
|
||||
@@ -8,11 +8,11 @@ from sqlalchemy.orm import Session
|
||||
from fastapi import APIRouter, Depends,Header
|
||||
|
||||
from app.db import get_db
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.response_utils import success, fail
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.api_key_utils import timestamp_to_datetime
|
||||
from app.services.memory_base_service import Translation_English
|
||||
from app.services.user_memory_service import (
|
||||
UserMemoryService,
|
||||
analytics_memory_types,
|
||||
@@ -45,7 +45,6 @@ router = APIRouter(
|
||||
@router.get("/analytics/memory_insight/report", response_model=ApiResponse)
|
||||
async def get_memory_insight_report_api(
|
||||
end_user_id: str,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
@@ -55,18 +54,10 @@ async def get_memory_insight_report_api(
|
||||
此接口仅查询数据库中已缓存的记忆洞察数据,不执行生成操作。
|
||||
如需生成新的洞察报告,请使用专门的生成接口。
|
||||
"""
|
||||
workspace_id = current_user.current_workspace_id
|
||||
workspace_repo = WorkspaceRepository(db)
|
||||
workspace_models = workspace_repo.get_workspace_models_configs(workspace_id)
|
||||
|
||||
if workspace_models:
|
||||
model_id = workspace_models.get("llm", None)
|
||||
else:
|
||||
model_id = None
|
||||
api_logger.info(f"记忆洞察报告查询请求: end_user_id={end_user_id}, user={current_user.username}")
|
||||
try:
|
||||
# 调用服务层获取缓存数据
|
||||
result = await user_memory_service.get_cached_memory_insight(db, end_user_id,model_id,language_type)
|
||||
result = await user_memory_service.get_cached_memory_insight(db, end_user_id)
|
||||
|
||||
if result["is_cached"]:
|
||||
api_logger.info(f"成功返回缓存的记忆洞察报告: end_user_id={end_user_id}")
|
||||
@@ -82,7 +73,7 @@ async def get_memory_insight_report_api(
|
||||
@router.get("/analytics/user_summary", response_model=ApiResponse)
|
||||
async def get_user_summary_api(
|
||||
end_user_id: str,
|
||||
language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
@@ -91,7 +82,14 @@ async def get_user_summary_api(
|
||||
|
||||
此接口仅查询数据库中已缓存的用户摘要数据,不执行生成操作。
|
||||
如需生成新的用户摘要,请使用专门的生成接口。
|
||||
|
||||
语言控制:
|
||||
- 使用 X-Language-Type Header 指定语言
|
||||
- 如果未传 Header,默认使用中文 (zh)
|
||||
"""
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
workspace_id = current_user.current_workspace_id
|
||||
workspace_repo = WorkspaceRepository(db)
|
||||
workspace_models = workspace_repo.get_workspace_models_configs(workspace_id)
|
||||
@@ -103,7 +101,7 @@ async def get_user_summary_api(
|
||||
api_logger.info(f"用户摘要查询请求: end_user_id={end_user_id}, user={current_user.username}")
|
||||
try:
|
||||
# 调用服务层获取缓存数据
|
||||
result = await user_memory_service.get_cached_user_summary(db, end_user_id,model_id,language_type)
|
||||
result = await user_memory_service.get_cached_user_summary(db, end_user_id,model_id,language)
|
||||
|
||||
if result["is_cached"]:
|
||||
api_logger.info(f"成功返回缓存的用户摘要: end_user_id={end_user_id}")
|
||||
@@ -119,6 +117,7 @@ async def get_user_summary_api(
|
||||
@router.post("/analytics/generate_cache", response_model=ApiResponse)
|
||||
async def generate_cache_api(
|
||||
request: GenerateCacheRequest,
|
||||
language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
@@ -127,7 +126,14 @@ async def generate_cache_api(
|
||||
|
||||
- 如果提供 end_user_id,只为该用户生成
|
||||
- 如果不提供,为当前工作空间的所有用户生成
|
||||
|
||||
语言控制:
|
||||
- 使用 X-Language-Type Header 指定语言 ("zh" 中文, "en" 英文)
|
||||
- 如果未传 Header,默认使用中文 (zh)
|
||||
"""
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
workspace_id = current_user.current_workspace_id
|
||||
|
||||
# 检查用户是否已选择工作空间
|
||||
@@ -139,7 +145,7 @@ async def generate_cache_api(
|
||||
|
||||
api_logger.info(
|
||||
f"缓存生成请求: user={current_user.username}, workspace={workspace_id}, "
|
||||
f"end_user_id={end_user_id if end_user_id else '全部用户'}"
|
||||
f"end_user_id={end_user_id if end_user_id else '全部用户'}, language={language}"
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -148,10 +154,10 @@ async def generate_cache_api(
|
||||
api_logger.info(f"开始为单个用户生成缓存: end_user_id={end_user_id}")
|
||||
|
||||
# 生成记忆洞察
|
||||
insight_result = await user_memory_service.generate_and_cache_insight(db, end_user_id, workspace_id)
|
||||
insight_result = await user_memory_service.generate_and_cache_insight(db, end_user_id, workspace_id, language=language)
|
||||
|
||||
# 生成用户摘要
|
||||
summary_result = await user_memory_service.generate_and_cache_summary(db, end_user_id, workspace_id)
|
||||
summary_result = await user_memory_service.generate_and_cache_summary(db, end_user_id, workspace_id, language=language)
|
||||
|
||||
# 构建响应
|
||||
result = {
|
||||
@@ -185,7 +191,7 @@ async def generate_cache_api(
|
||||
# 为整个工作空间生成
|
||||
api_logger.info(f"开始为工作空间 {workspace_id} 批量生成缓存")
|
||||
|
||||
result = await user_memory_service.generate_cache_for_workspace(db, workspace_id)
|
||||
result = await user_memory_service.generate_cache_for_workspace(db, workspace_id, language=language)
|
||||
|
||||
# 记录统计信息
|
||||
api_logger.info(
|
||||
@@ -385,10 +391,13 @@ async def update_end_user_profile(
|
||||
return fail(BizCode.INTERNAL_ERROR, "用户信息更新失败", error_msg)
|
||||
|
||||
@router.get("/memory_space/timeline_memories", response_model=ApiResponse)
|
||||
async def memory_space_timeline_of_shared_memories(id: str, label: str,language_type: str = Header(default="zh", alias="X-Language-Type"),
|
||||
async def memory_space_timeline_of_shared_memories(id: str, label: str,language_type: str = Header(default=None, alias="X-Language-Type"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
# 使用集中化的语言校验
|
||||
language = get_language_from_header(language_type)
|
||||
|
||||
workspace_id=current_user.current_workspace_id
|
||||
workspace_repo = WorkspaceRepository(db)
|
||||
workspace_models = workspace_repo.get_workspace_models_configs(workspace_id)
|
||||
@@ -398,7 +407,7 @@ async def memory_space_timeline_of_shared_memories(id: str, label: str,language_
|
||||
else:
|
||||
model_id = None
|
||||
MemoryEntity = MemoryEntityService(id, label)
|
||||
timeline_memories_result = await MemoryEntity.get_timeline_memories_server(model_id, language_type)
|
||||
timeline_memories_result = await MemoryEntity.get_timeline_memories_server(model_id, language)
|
||||
|
||||
return success(data=timeline_memories_result, msg="共同记忆时间线")
|
||||
@router.get("/memory_space/relationship_evolution", response_model=ApiResponse)
|
||||
|
||||
82
api/app/core/language_utils.py
Normal file
82
api/app/core/language_utils.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""语言处理工具模块
|
||||
|
||||
本模块提供集中化的语言校验和处理功能,确保整个应用中语言参数的一致性。
|
||||
|
||||
Functions:
|
||||
validate_language: 校验语言参数,确保其为有效值
|
||||
get_language_from_header: 从请求头获取并校验语言参数
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from app.core.logging_config import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# 支持的语言列表
|
||||
SUPPORTED_LANGUAGES = {"zh", "en"}
|
||||
|
||||
# 默认回退语言
|
||||
DEFAULT_LANGUAGE = "zh"
|
||||
|
||||
|
||||
def validate_language(language: Optional[str]) -> str:
|
||||
"""
|
||||
校验语言参数,确保其为有效值。
|
||||
|
||||
Args:
|
||||
language: 待校验的语言代码,可以是 None、"zh"、"en" 或其他值
|
||||
|
||||
Returns:
|
||||
有效的语言代码("zh" 或 "en")
|
||||
|
||||
Examples:
|
||||
>>> validate_language("zh")
|
||||
'zh'
|
||||
>>> validate_language("en")
|
||||
'en'
|
||||
>>> validate_language("EN") # 大小写不敏感
|
||||
'en'
|
||||
>>> validate_language(None) # None 回退到默认值
|
||||
'zh'
|
||||
>>> validate_language("fr") # 不支持的语言回退到默认值
|
||||
'zh'
|
||||
"""
|
||||
if language is None:
|
||||
return DEFAULT_LANGUAGE
|
||||
|
||||
# 标准化:转小写并去除空白
|
||||
lang = str(language).lower().strip()
|
||||
|
||||
if lang in SUPPORTED_LANGUAGES:
|
||||
return lang
|
||||
|
||||
logger.warning(
|
||||
f"无效的语言参数 '{language}',已回退到默认值 '{DEFAULT_LANGUAGE}'。"
|
||||
f"支持的语言: {SUPPORTED_LANGUAGES}"
|
||||
)
|
||||
return DEFAULT_LANGUAGE
|
||||
|
||||
|
||||
def get_language_from_header(language_type: Optional[str]) -> str:
|
||||
"""
|
||||
从请求头获取并校验语言参数。
|
||||
|
||||
这是一个便捷函数,用于在 controller 层统一处理 X-Language-Type Header。
|
||||
|
||||
Args:
|
||||
language_type: 从 X-Language-Type Header 获取的语言值
|
||||
|
||||
Returns:
|
||||
有效的语言代码("zh" 或 "en")
|
||||
|
||||
Examples:
|
||||
>>> get_language_from_header(None) # Header 未传递
|
||||
'zh'
|
||||
>>> get_language_from_header("en")
|
||||
'en'
|
||||
>>> get_language_from_header("invalid") # 无效值回退
|
||||
'zh'
|
||||
"""
|
||||
return validate_language(language_type)
|
||||
@@ -10,7 +10,7 @@ async def write_node(state: WriteState) -> WriteState:
|
||||
Write data to the database/file system.
|
||||
|
||||
Args:
|
||||
state: WriteState containing messages, end_user_id, and memory_config
|
||||
state: WriteState containing messages, end_user_id, memory_config, and language
|
||||
|
||||
Returns:
|
||||
dict: Contains 'write_result' with status and data fields
|
||||
@@ -18,6 +18,7 @@ async def write_node(state: WriteState) -> WriteState:
|
||||
messages = state.get('messages', [])
|
||||
end_user_id = state.get('end_user_id', '')
|
||||
memory_config = state.get('memory_config', '')
|
||||
language = state.get('language', 'zh') # 默认中文
|
||||
|
||||
# Convert LangChain messages to structured format expected by write()
|
||||
structured_messages = []
|
||||
@@ -35,6 +36,7 @@ async def write_node(state: WriteState) -> WriteState:
|
||||
messages=structured_messages,
|
||||
end_user_id=end_user_id,
|
||||
memory_config=memory_config,
|
||||
language=language,
|
||||
)
|
||||
logger.info(f"Write completed successfully! Config: {memory_config.config_name}")
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ class WriteState(TypedDict):
|
||||
memory_config: object
|
||||
write_result: dict
|
||||
data: str
|
||||
language: str # 语言类型 ("zh" 中文, "en" 英文)
|
||||
|
||||
class ReadState(TypedDict):
|
||||
"""
|
||||
|
||||
@@ -33,17 +33,17 @@ async def write(
|
||||
memory_config: MemoryConfig,
|
||||
messages: list,
|
||||
ref_id: str = "wyl20251027",
|
||||
language: str = "zh",
|
||||
) -> None:
|
||||
"""
|
||||
Execute the complete knowledge extraction pipeline.
|
||||
|
||||
Args:
|
||||
user_id: User identifier
|
||||
apply_id: Application identifier
|
||||
end_user_id: Group identifier
|
||||
memory_config: MemoryConfig object containing all configuration
|
||||
messages: Structured message list [{"role": "user", "content": "..."}, ...]
|
||||
ref_id: Reference ID, defaults to "wyl20251027"
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
"""
|
||||
# Extract config values
|
||||
embedding_model_id = str(memory_config.embedding_model_id)
|
||||
@@ -99,6 +99,7 @@ async def write(
|
||||
connector=neo4j_connector,
|
||||
config=pipeline_config,
|
||||
embedding_id=embedding_model_id,
|
||||
language=language,
|
||||
)
|
||||
|
||||
# Run the complete extraction pipeline
|
||||
@@ -147,7 +148,7 @@ async def write(
|
||||
step_start = time.time()
|
||||
try:
|
||||
summaries = await memory_summary_generation(
|
||||
chunked_dialogs, llm_client=llm_client, embedder_client=embedder_client
|
||||
chunked_dialogs, llm_client=llm_client, embedder_client=embedder_client, language=language
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -95,6 +95,7 @@ class ExtractionOrchestrator:
|
||||
config: Optional[ExtractionPipelineConfig] = None,
|
||||
progress_callback: Optional[Callable[[str, str, Optional[Dict[str, Any]]], Awaitable[None]]] = None,
|
||||
embedding_id: Optional[str] = None,
|
||||
language: str = "zh",
|
||||
):
|
||||
"""
|
||||
初始化流水线编排器
|
||||
@@ -108,6 +109,7 @@ class ExtractionOrchestrator:
|
||||
- 接受 (stage: str, message: str, data: Optional[Dict[str, Any]]) 并返回 Awaitable[None]
|
||||
- 在管线关键点调用以报告进度和结果数据
|
||||
embedding_id: 嵌入模型ID,如果为 None 则从全局配置获取(向后兼容)
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
"""
|
||||
self.llm_client = llm_client
|
||||
self.embedder_client = embedder_client
|
||||
@@ -116,6 +118,7 @@ class ExtractionOrchestrator:
|
||||
self.is_pilot_run = False # 默认非试运行模式
|
||||
self.progress_callback = progress_callback # 保存进度回调函数
|
||||
self.embedding_id = embedding_id # 保存嵌入模型ID
|
||||
self.language = language # 保存语言配置
|
||||
|
||||
# 保存去重消歧的详细记录(内存中的数据结构)
|
||||
self.dedup_merge_records: List[Dict[str, Any]] = [] # 实体合并记录
|
||||
@@ -127,7 +130,7 @@ class ExtractionOrchestrator:
|
||||
llm_client=llm_client,
|
||||
config=self.config.statement_extraction,
|
||||
)
|
||||
self.triplet_extractor = TripletExtractor(llm_client=llm_client)
|
||||
self.triplet_extractor = TripletExtractor(llm_client=llm_client, language=language)
|
||||
self.temporal_extractor = TemporalExtractor(llm_client=llm_client)
|
||||
|
||||
logger.info("ExtractionOrchestrator 初始化完成")
|
||||
|
||||
@@ -10,38 +10,11 @@ from app.core.memory.models.base_response import RobustLLMResponse
|
||||
from app.core.memory.models.graph_models import MemorySummaryNode
|
||||
from app.core.memory.models.message_models import DialogData
|
||||
from app.core.memory.utils.prompt.prompt_utils import render_memory_summary_prompt
|
||||
from app.core.language_utils import validate_language # 使用集中化的语言校验
|
||||
from pydantic import Field
|
||||
|
||||
logger = get_memory_logger(__name__)
|
||||
|
||||
# 支持的语言列表和默认回退值
|
||||
SUPPORTED_LANGUAGES = {"zh", "en"}
|
||||
FALLBACK_LANGUAGE = "en"
|
||||
|
||||
|
||||
def validate_language(language: Optional[str]) -> str:
|
||||
"""
|
||||
校验语言参数,确保其为有效值。
|
||||
|
||||
Args:
|
||||
language: 待校验的语言代码
|
||||
|
||||
Returns:
|
||||
有效的语言代码("zh" 或 "en")
|
||||
"""
|
||||
if language is None:
|
||||
return FALLBACK_LANGUAGE
|
||||
|
||||
lang = str(language).lower().strip()
|
||||
if lang in SUPPORTED_LANGUAGES:
|
||||
return lang
|
||||
|
||||
logger.warning(
|
||||
f"无效的语言参数 '{language}',已回退到默认值 '{FALLBACK_LANGUAGE}'。"
|
||||
f"支持的语言: {SUPPORTED_LANGUAGES}"
|
||||
)
|
||||
return FALLBACK_LANGUAGE
|
||||
|
||||
|
||||
class MemorySummaryResponse(RobustLLMResponse):
|
||||
"""Structured response for summary generation per chunk.
|
||||
@@ -60,7 +33,7 @@ class MemorySummaryResponse(RobustLLMResponse):
|
||||
async def generate_title_and_type_for_summary(
|
||||
content: str,
|
||||
llm_client,
|
||||
language: str = None
|
||||
language: str = "zh"
|
||||
) -> Tuple[str, str]:
|
||||
"""
|
||||
为MemorySummary生成标题和类型
|
||||
@@ -70,17 +43,14 @@ async def generate_title_and_type_for_summary(
|
||||
Args:
|
||||
content: Summary的内容文本
|
||||
llm_client: LLM客户端实例
|
||||
language: 生成标题使用的语言 ("zh" 中文, "en" 英文),如果为None则从配置读取
|
||||
language: 生成标题使用的语言 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Returns:
|
||||
(标题, 类型)元组
|
||||
"""
|
||||
from app.core.memory.utils.prompt.prompt_utils import render_episodic_title_and_type_prompt
|
||||
from app.core.config import settings
|
||||
|
||||
# 如果没有指定语言,从配置中读取,并校验有效性
|
||||
if language is None:
|
||||
language = settings.DEFAULT_LANGUAGE
|
||||
# 验证语言参数
|
||||
language = validate_language(language)
|
||||
|
||||
# 定义有效的类型集合
|
||||
@@ -188,6 +158,7 @@ async def _process_chunk_summary(
|
||||
chunk,
|
||||
llm_client,
|
||||
embedder: OpenAIEmbedderClient,
|
||||
language: str = "zh",
|
||||
) -> Optional[MemorySummaryNode]:
|
||||
"""Process a single chunk to generate a memory summary node."""
|
||||
# Skip empty chunks
|
||||
@@ -195,9 +166,8 @@ async def _process_chunk_summary(
|
||||
return None
|
||||
|
||||
try:
|
||||
# 从配置中获取语言设置(只获取一次,复用),并校验有效性
|
||||
from app.core.config import settings
|
||||
language = validate_language(settings.DEFAULT_LANGUAGE)
|
||||
# 验证语言参数
|
||||
language = validate_language(language)
|
||||
|
||||
# Render prompt via Jinja2 for a single chunk
|
||||
prompt_content = await render_memory_summary_prompt(
|
||||
@@ -267,13 +237,21 @@ async def memory_summary_generation(
|
||||
chunked_dialogs: List[DialogData],
|
||||
llm_client,
|
||||
embedder_client: OpenAIEmbedderClient,
|
||||
language: str = "zh",
|
||||
) -> List[MemorySummaryNode]:
|
||||
"""Generate memory summaries per chunk, embed them, and return nodes."""
|
||||
"""Generate memory summaries per chunk, embed them, and return nodes.
|
||||
|
||||
Args:
|
||||
chunked_dialogs: 分块后的对话数据
|
||||
llm_client: LLM客户端
|
||||
embedder_client: 嵌入客户端
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
"""
|
||||
# Collect all tasks for parallel processing
|
||||
tasks = []
|
||||
for dialog in chunked_dialogs:
|
||||
for chunk in dialog.chunks:
|
||||
tasks.append(_process_chunk_summary(dialog, chunk, llm_client, embedder_client))
|
||||
tasks.append(_process_chunk_summary(dialog, chunk, llm_client, embedder_client, language=language))
|
||||
|
||||
# Process all chunks in parallel
|
||||
results = await asyncio.gather(*tasks, return_exceptions=False)
|
||||
|
||||
@@ -64,6 +64,7 @@ class OntologyExtractor:
|
||||
llm_max_tokens: int = 2000,
|
||||
max_description_length: int = 500,
|
||||
timeout: Optional[float] = None,
|
||||
language: str = "zh",
|
||||
) -> OntologyExtractionResponse:
|
||||
"""Extract ontology classes from a scenario description.
|
||||
|
||||
@@ -84,6 +85,7 @@ class OntologyExtractor:
|
||||
llm_max_tokens: LLM max tokens parameter (default: 2000)
|
||||
max_description_length: Maximum description length (default: 500)
|
||||
timeout: Optional timeout in seconds for LLM call (default: None, no timeout)
|
||||
language: Language for output ("zh" for Chinese, "en" for English)
|
||||
|
||||
Returns:
|
||||
OntologyExtractionResponse containing validated ontology classes
|
||||
@@ -134,6 +136,7 @@ class OntologyExtractor:
|
||||
max_classes=max_classes,
|
||||
llm_temperature=llm_temperature,
|
||||
llm_max_tokens=llm_max_tokens,
|
||||
language=language,
|
||||
),
|
||||
timeout=timeout
|
||||
)
|
||||
@@ -156,6 +159,7 @@ class OntologyExtractor:
|
||||
max_classes=max_classes,
|
||||
llm_temperature=llm_temperature,
|
||||
llm_max_tokens=llm_max_tokens,
|
||||
language=language,
|
||||
)
|
||||
|
||||
llm_duration = time.time() - llm_start_time
|
||||
@@ -260,6 +264,7 @@ class OntologyExtractor:
|
||||
max_classes: int,
|
||||
llm_temperature: float,
|
||||
llm_max_tokens: int,
|
||||
language: str = "zh",
|
||||
) -> OntologyExtractionResponse:
|
||||
"""Call LLM to extract ontology classes from scenario.
|
||||
|
||||
@@ -272,6 +277,7 @@ class OntologyExtractor:
|
||||
max_classes: Maximum number of classes to extract
|
||||
llm_temperature: LLM temperature parameter
|
||||
llm_max_tokens: LLM max tokens parameter
|
||||
language: Language for output ("zh" for Chinese, "en" for English)
|
||||
|
||||
Returns:
|
||||
OntologyExtractionResponse from LLM
|
||||
@@ -286,6 +292,7 @@ class OntologyExtractor:
|
||||
domain=domain,
|
||||
max_classes=max_classes,
|
||||
json_schema=OntologyExtractionResponse.model_json_schema(),
|
||||
language=language,
|
||||
)
|
||||
|
||||
logger.debug(f"Rendered prompt length: {len(prompt_content)}")
|
||||
|
||||
@@ -17,13 +17,15 @@ logger = get_memory_logger(__name__)
|
||||
class TripletExtractor:
|
||||
"""Extracts knowledge triplets and entities from statements using LLM"""
|
||||
|
||||
def __init__(self, llm_client: OpenAIClient):
|
||||
def __init__(self, llm_client: OpenAIClient, language: str = "zh"):
|
||||
"""Initialize the TripletExtractor with an LLM client
|
||||
|
||||
Args:
|
||||
llm_client: OpenAIClient instance for processing
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
"""
|
||||
self.llm_client = llm_client
|
||||
self.language = language
|
||||
|
||||
def _get_language(self) -> str:
|
||||
"""Get the configured language for entity descriptions
|
||||
@@ -31,8 +33,7 @@ class TripletExtractor:
|
||||
Returns:
|
||||
Language code ("zh" or "en")
|
||||
"""
|
||||
from app.core.config import settings
|
||||
return settings.DEFAULT_LANGUAGE
|
||||
return self.language
|
||||
|
||||
async def _extract_triplets(self, statement: Statement, chunk_content: str) -> TripletExtractionResponse:
|
||||
"""Process a single statement and return extracted triplets and entities"""
|
||||
|
||||
@@ -283,7 +283,8 @@ async def render_emotion_extraction_prompt(
|
||||
async def render_emotion_suggestions_prompt(
|
||||
health_data: dict,
|
||||
patterns: dict,
|
||||
user_profile: dict
|
||||
user_profile: dict,
|
||||
language: str = "zh"
|
||||
) -> str:
|
||||
"""
|
||||
Renders the emotion suggestions generation prompt using the generate_emotion_suggestions.jinja2 template.
|
||||
@@ -292,6 +293,7 @@ async def render_emotion_suggestions_prompt(
|
||||
health_data: 情绪健康数据
|
||||
patterns: 情绪模式分析结果
|
||||
user_profile: 用户画像数据
|
||||
language: 输出语言 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
Rendered prompt content as string
|
||||
@@ -310,7 +312,8 @@ async def render_emotion_suggestions_prompt(
|
||||
health_data=health_data,
|
||||
patterns=patterns,
|
||||
user_profile=user_profile,
|
||||
emotion_distribution_json=emotion_distribution_json
|
||||
emotion_distribution_json=emotion_distribution_json,
|
||||
language=language
|
||||
)
|
||||
|
||||
# 记录渲染结果到提示日志
|
||||
@@ -328,7 +331,8 @@ async def render_emotion_suggestions_prompt(
|
||||
async def render_user_summary_prompt(
|
||||
user_id: str,
|
||||
entities: str,
|
||||
statements: str
|
||||
statements: str,
|
||||
language: str = "zh"
|
||||
) -> str:
|
||||
"""
|
||||
Renders the user summary prompt using the user_summary.jinja2 template.
|
||||
@@ -337,6 +341,7 @@ async def render_user_summary_prompt(
|
||||
user_id: User identifier
|
||||
entities: Core entities with frequency information
|
||||
statements: Representative statement samples
|
||||
language: The language to use for summary generation ("zh" for Chinese, "en" for English)
|
||||
|
||||
Returns:
|
||||
Rendered prompt content as string
|
||||
@@ -345,7 +350,8 @@ async def render_user_summary_prompt(
|
||||
rendered_prompt = template.render(
|
||||
user_id=user_id,
|
||||
entities=entities,
|
||||
statements=statements
|
||||
statements=statements,
|
||||
language=language
|
||||
)
|
||||
|
||||
# 记录渲染结果到提示日志
|
||||
@@ -354,7 +360,8 @@ async def render_user_summary_prompt(
|
||||
log_template_rendering('user_summary.jinja2', {
|
||||
'user_id': user_id,
|
||||
'entities_len': len(entities),
|
||||
'statements_len': len(statements)
|
||||
'statements_len': len(statements),
|
||||
'language': language
|
||||
})
|
||||
|
||||
return rendered_prompt
|
||||
@@ -363,7 +370,8 @@ async def render_user_summary_prompt(
|
||||
async def render_memory_insight_prompt(
|
||||
domain_distribution: str = None,
|
||||
active_periods: str = None,
|
||||
social_connections: str = None
|
||||
social_connections: str = None,
|
||||
language: str = "zh"
|
||||
) -> str:
|
||||
"""
|
||||
Renders the memory insight prompt using the memory_insight.jinja2 template.
|
||||
@@ -372,6 +380,7 @@ async def render_memory_insight_prompt(
|
||||
domain_distribution: 核心领域分布信息
|
||||
active_periods: 活跃时段信息
|
||||
social_connections: 社交关联信息
|
||||
language: The language to use for report generation ("zh" for Chinese, "en" for English)
|
||||
|
||||
Returns:
|
||||
Rendered prompt content as string
|
||||
@@ -380,7 +389,8 @@ async def render_memory_insight_prompt(
|
||||
rendered_prompt = template.render(
|
||||
domain_distribution=domain_distribution,
|
||||
active_periods=active_periods,
|
||||
social_connections=social_connections
|
||||
social_connections=social_connections,
|
||||
language=language
|
||||
)
|
||||
|
||||
# 记录渲染结果到提示日志
|
||||
@@ -389,7 +399,8 @@ async def render_memory_insight_prompt(
|
||||
log_template_rendering('memory_insight.jinja2', {
|
||||
'has_domain_distribution': bool(domain_distribution),
|
||||
'has_active_periods': bool(active_periods),
|
||||
'has_social_connections': bool(social_connections)
|
||||
'has_social_connections': bool(social_connections),
|
||||
'language': language
|
||||
})
|
||||
|
||||
return rendered_prompt
|
||||
@@ -424,7 +435,8 @@ async def render_ontology_extraction_prompt(
|
||||
scenario: str,
|
||||
domain: str | None = None,
|
||||
max_classes: int = 15,
|
||||
json_schema: dict | None = None
|
||||
json_schema: dict | None = None,
|
||||
language: str = "zh"
|
||||
) -> str:
|
||||
"""
|
||||
Renders the ontology extraction prompt using the extract_ontology.jinja2 template.
|
||||
@@ -434,6 +446,7 @@ async def render_ontology_extraction_prompt(
|
||||
domain: Optional domain hint for the scenario (e.g., "Healthcare", "Education")
|
||||
max_classes: Maximum number of classes to extract (default: 15)
|
||||
json_schema: JSON schema for the expected output format
|
||||
language: Language for output ("zh" for Chinese, "en" for English)
|
||||
|
||||
Returns:
|
||||
Rendered prompt content as string
|
||||
@@ -443,7 +456,8 @@ async def render_ontology_extraction_prompt(
|
||||
scenario=scenario,
|
||||
domain=domain,
|
||||
max_classes=max_classes,
|
||||
json_schema=json_schema
|
||||
json_schema=json_schema,
|
||||
language=language
|
||||
)
|
||||
|
||||
# 记录渲染结果到提示日志
|
||||
|
||||
@@ -1,19 +1,83 @@
|
||||
===Task===
|
||||
{% if language == "zh" %}
|
||||
从给定的场景描述中提取本体类,遵循本体工程标准。
|
||||
{% else %}
|
||||
Extract ontology classes from the given scenario description following ontology engineering standards.
|
||||
{% endif %}
|
||||
|
||||
===Role===
|
||||
{% if language == "zh" %}
|
||||
你是一位专业的本体工程师,精通知识表示和OWL(Web本体语言)标准。你的任务是从场景描述中识别抽象类和概念,而不是具体实例。
|
||||
{% else %}
|
||||
You are a professional ontology engineer with expertise in knowledge representation and OWL (Web Ontology Language) standards. Your task is to identify abstract classes and concepts from scenario descriptions, not concrete instances.
|
||||
{% endif %}
|
||||
|
||||
===Scenario Description===
|
||||
{{ scenario }}
|
||||
|
||||
{% if domain -%}
|
||||
===Domain Hint===
|
||||
{% if language == "zh" %}
|
||||
此场景属于 **{{ domain }}** 领域。提取类时请考虑领域特定的概念和术语。
|
||||
{% else %}
|
||||
This scenario belongs to the **{{ domain }}** domain. Consider domain-specific concepts and terminology when extracting classes.
|
||||
{% endif %}
|
||||
{%- endif %}
|
||||
|
||||
===Extraction Rules===
|
||||
|
||||
{% if language == "zh" %}
|
||||
**1. 抽象类,而非实例:**
|
||||
- 提取抽象类别和概念(如"医疗程序"、"患者"、"诊断")
|
||||
- 不要提取具体实例(如"张三"、"301房间"、"2024-01-15")
|
||||
- 以"事物的类型"而非"具体事物"的角度思考
|
||||
|
||||
**2. 命名规范:**
|
||||
- "name"字段使用中文名称
|
||||
- 使用清晰、描述性的中文名称
|
||||
- 示例:"医疗程序"、"医疗服务提供者"、"诊断测试"
|
||||
|
||||
**3. 领域相关性:**
|
||||
- 专注于场景领域的核心类
|
||||
- 优先提取代表关键概念、实体或关系的类
|
||||
- 避免过于通用的类(如"事物"、"对象"),除非它们在领域中有特定含义
|
||||
|
||||
**4. 类数量:**
|
||||
- 提取5到{{ max_classes }}个类
|
||||
- 目标是覆盖场景主要概念的平衡集合
|
||||
- 质量优于数量:优先选择定义明确的类
|
||||
|
||||
**5. 清晰的描述:**
|
||||
- 用中文提供简洁、信息丰富的描述(最多500字)
|
||||
- 描述类代表什么,而不是具体实例
|
||||
- 使用清晰、自然的中文解释类在领域中的作用
|
||||
|
||||
**6. 具体示例:**
|
||||
- 为每个类提供2-5个中文具体实例示例
|
||||
- 示例应该是该类的具体、现实的实例
|
||||
- 示例有助于阐明类的范围和含义
|
||||
- 示例格式:["示例1", "示例2", "示例3"]
|
||||
|
||||
**7. 类层次结构:**
|
||||
- 在适用的情况下识别父子关系
|
||||
- 使用parent_class字段指定继承关系
|
||||
- 父类必须是提取的类之一或标准OWL类
|
||||
- 顶级类的parent_class设为null
|
||||
|
||||
**8. 实体类型:**
|
||||
- 为每个类分配适当的entity_type
|
||||
- 常见类型:"人物"、"组织"、"地点"、"事件"、"概念"、"过程"、"对象"、"角色"
|
||||
- 选择最具体的适用类型
|
||||
|
||||
**9. 语言一致性:**
|
||||
- 所有字段内容必须使用中文
|
||||
- "name"字段使用中文名称
|
||||
- "description"字段使用中文描述
|
||||
- "examples"字段使用中文示例
|
||||
- "entity_type"字段使用中文类型名称
|
||||
- "domain"字段使用中文领域名称
|
||||
|
||||
{% else %}
|
||||
**1. Abstract Classes, Not Instances:**
|
||||
- Extract abstract categories and concepts (e.g., "MedicalProcedure", "Patient", "Diagnosis")
|
||||
- Do NOT extract concrete instances (e.g., "John Smith", "Room 301", "2024-01-15")
|
||||
@@ -24,8 +88,6 @@ This scenario belongs to the **{{ domain }}** domain. Consider domain-specific c
|
||||
- Examples: "MedicalProcedure", "HealthcareProvider", "DiagnosticTest"
|
||||
- Avoid: "medical procedure", "healthcare_provider", "diagnostic-test"
|
||||
- Use clear, descriptive names in English
|
||||
- Avoid abbreviations unless they are standard in the domain (e.g., "API", "DNA")
|
||||
- Provide Chinese translation in the "name_chinese" field (e.g., "医疗程序", "医疗服务提供者", "诊断测试")
|
||||
|
||||
**3. Domain Relevance:**
|
||||
- Focus on classes that are central to the scenario's domain
|
||||
@@ -38,16 +100,15 @@ This scenario belongs to the **{{ domain }}** domain. Consider domain-specific c
|
||||
- Quality over quantity: prefer well-defined classes over exhaustive lists
|
||||
|
||||
**5. Clear Descriptions:**
|
||||
- Provide concise, informative descriptions in Chinese (max 500 characters)
|
||||
- Provide concise, informative descriptions in English (max 500 characters)
|
||||
- Describe what the class represents, not specific instances
|
||||
- Use clear, natural Chinese language that explains the class's role in the domain
|
||||
- Use clear, natural English language
|
||||
|
||||
**6. Concrete Examples:**
|
||||
- Provide 2-5 concrete instance examples in Chinese for each class
|
||||
- Provide 2-5 concrete instance examples in English for each class
|
||||
- Examples should be specific, realistic instances of the class
|
||||
- Examples help clarify the class's scope and meaning
|
||||
- Use natural Chinese language for examples
|
||||
- Example format: ["示例1", "示例2", "示例3"]
|
||||
- Example format: ["Example1", "Example2", "Example3"]
|
||||
|
||||
**7. Class Hierarchy:**
|
||||
- Identify parent-child relationships where applicable
|
||||
@@ -60,20 +121,119 @@ This scenario belongs to the **{{ domain }}** domain. Consider domain-specific c
|
||||
- Common types: "Person", "Organization", "Location", "Event", "Concept", "Process", "Object", "Role"
|
||||
- Choose the most specific type that applies
|
||||
|
||||
**9. OWL Reserved Words:**
|
||||
- Do NOT use OWL reserved words as class names
|
||||
- Reserved words include: "Thing", "Nothing", "Class", "Property", "ObjectProperty", "DatatypeProperty", "AnnotationProperty", "Ontology", "Individual", "Literal"
|
||||
- If a reserved word is needed, add a domain-specific prefix (e.g., "MedicalClass" instead of "Class")
|
||||
|
||||
**10. Language Consistency:**
|
||||
- Extract all class names in English (PascalCase format) for the "name" field
|
||||
- Provide Chinese translation for class names in the "name_chinese" field
|
||||
- Descriptions MUST be in Chinese (中文)
|
||||
- Examples MUST be in Chinese (中文)
|
||||
- Use clear, natural Chinese language for descriptions and examples
|
||||
**9. Language Consistency:**
|
||||
- All field content must be in English
|
||||
- "name" field uses English PascalCase names
|
||||
- "description" field uses English descriptions
|
||||
- "examples" field uses English examples
|
||||
- "entity_type" field uses English type names
|
||||
- "domain" field uses English domain names
|
||||
{% endif %}
|
||||
|
||||
===Examples===
|
||||
|
||||
{% if language == "zh" %}
|
||||
**示例1(医疗领域):**
|
||||
场景:"一家医院管理患者记录,安排预约,并协调医疗程序。医生诊断病情并开具治疗方案。"
|
||||
|
||||
输出:
|
||||
{
|
||||
"classes": [
|
||||
{
|
||||
"name": "患者",
|
||||
"description": "在医疗机构接受医疗护理或治疗的人",
|
||||
"examples": ["张三", "李四", "患有糖尿病的老年患者"],
|
||||
"parent_class": null,
|
||||
"entity_type": "人物",
|
||||
"domain": "医疗"
|
||||
},
|
||||
{
|
||||
"name": "医疗程序",
|
||||
"description": "为医疗诊断或治疗而执行的系统性操作流程",
|
||||
"examples": ["手术", "血液检查", "X光检查", "疫苗接种"],
|
||||
"parent_class": null,
|
||||
"entity_type": "过程",
|
||||
"domain": "医疗"
|
||||
},
|
||||
{
|
||||
"name": "诊断",
|
||||
"description": "基于症状和检查结果对疾病或状况的识别",
|
||||
"examples": ["糖尿病诊断", "癌症诊断", "流感诊断"],
|
||||
"parent_class": null,
|
||||
"entity_type": "概念",
|
||||
"domain": "医疗"
|
||||
},
|
||||
{
|
||||
"name": "医生",
|
||||
"description": "诊断和治疗患者的持证医疗专业人员",
|
||||
"examples": ["全科医生", "外科医生", "心脏病专家"],
|
||||
"parent_class": null,
|
||||
"entity_type": "角色",
|
||||
"domain": "医疗"
|
||||
},
|
||||
{
|
||||
"name": "治疗",
|
||||
"description": "为治愈或管理疾病状况而提供的医疗护理或疗法",
|
||||
"examples": ["药物治疗", "物理治疗", "化疗", "手术治疗"],
|
||||
"parent_class": null,
|
||||
"entity_type": "过程",
|
||||
"domain": "医疗"
|
||||
}
|
||||
],
|
||||
"domain": "医疗"
|
||||
}
|
||||
|
||||
**示例2(教育领域):**
|
||||
场景:"一所大学提供由教授教授的课程。学生注册项目,参加讲座,并完成作业以获得学位。"
|
||||
|
||||
输出:
|
||||
{
|
||||
"classes": [
|
||||
{
|
||||
"name": "学生",
|
||||
"description": "在教育机构注册学习的人",
|
||||
"examples": ["本科生", "研究生", "在职学生"],
|
||||
"parent_class": null,
|
||||
"entity_type": "角色",
|
||||
"domain": "教育"
|
||||
},
|
||||
{
|
||||
"name": "课程",
|
||||
"description": "涵盖特定学科或主题的结构化教育课程",
|
||||
"examples": ["计算机科学导论", "微积分I", "世界历史"],
|
||||
"parent_class": null,
|
||||
"entity_type": "概念",
|
||||
"domain": "教育"
|
||||
},
|
||||
{
|
||||
"name": "教授",
|
||||
"description": "教授课程并进行研究的学术教师",
|
||||
"examples": ["助理教授", "副教授", "正教授"],
|
||||
"parent_class": null,
|
||||
"entity_type": "角色",
|
||||
"domain": "教育"
|
||||
},
|
||||
{
|
||||
"name": "学术项目",
|
||||
"description": "通向学位或证书的结构化课程体系",
|
||||
"examples": ["理学学士", "文学硕士", "博士项目"],
|
||||
"parent_class": null,
|
||||
"entity_type": "概念",
|
||||
"domain": "教育"
|
||||
},
|
||||
{
|
||||
"name": "作业",
|
||||
"description": "分配给学生以评估学习成果的任务或项目",
|
||||
"examples": ["论文", "习题集", "研究报告", "实验报告"],
|
||||
"parent_class": null,
|
||||
"entity_type": "对象",
|
||||
"domain": "教育"
|
||||
}
|
||||
],
|
||||
"domain": "教育"
|
||||
}
|
||||
|
||||
{% else %}
|
||||
**Example 1 (Healthcare Domain):**
|
||||
Scenario: "A hospital manages patient records, schedules appointments, and coordinates medical procedures. Doctors diagnose conditions and prescribe treatments."
|
||||
|
||||
@@ -82,52 +242,46 @@ Output:
|
||||
"classes": [
|
||||
{
|
||||
"name": "Patient",
|
||||
"name_chinese": "患者",
|
||||
"description": "在医疗机构接受医疗护理或治疗的人",
|
||||
"examples": ["张三", "李四", "患有糖尿病的老年患者"],
|
||||
"description": "A person receiving medical care or treatment at a healthcare facility",
|
||||
"examples": ["John Smith", "Jane Doe", "Elderly patient with diabetes"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Person",
|
||||
"domain": "Healthcare"
|
||||
},
|
||||
{
|
||||
"name": "MedicalProcedure",
|
||||
"name_chinese": "医疗程序",
|
||||
"description": "为医疗诊断或治疗而执行的系统性操作流程",
|
||||
"examples": ["手术", "血液检查", "X光检查", "疫苗接种"],
|
||||
"description": "A systematic operation performed for medical diagnosis or treatment",
|
||||
"examples": ["Surgery", "Blood test", "X-ray examination", "Vaccination"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Process",
|
||||
"domain": "Healthcare"
|
||||
},
|
||||
{
|
||||
"name": "Diagnosis",
|
||||
"name_chinese": "诊断",
|
||||
"description": "基于症状和检查结果对疾病或状况的识别",
|
||||
"examples": ["糖尿病诊断", "癌症诊断", "流感诊断"],
|
||||
"description": "Identification of a disease or condition based on symptoms and examination results",
|
||||
"examples": ["Diabetes diagnosis", "Cancer diagnosis", "Flu diagnosis"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Concept",
|
||||
"domain": "Healthcare"
|
||||
},
|
||||
{
|
||||
"name": "Doctor",
|
||||
"name_chinese": "医生",
|
||||
"description": "诊断和治疗患者的持证医疗专业人员",
|
||||
"examples": ["全科医生", "外科医生", "心脏病专家"],
|
||||
"description": "A licensed medical professional who diagnoses and treats patients",
|
||||
"examples": ["General practitioner", "Surgeon", "Cardiologist"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Role",
|
||||
"domain": "Healthcare"
|
||||
},
|
||||
{
|
||||
"name": "Treatment",
|
||||
"name_chinese": "治疗",
|
||||
"description": "为治愈或管理疾病状况而提供的医疗护理或疗法",
|
||||
"examples": ["药物治疗", "物理治疗", "化疗", "手术治疗"],
|
||||
"description": "Medical care or therapy provided to cure or manage a disease condition",
|
||||
"examples": ["Medication therapy", "Physical therapy", "Chemotherapy", "Surgical treatment"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Process",
|
||||
"domain": "Healthcare"
|
||||
}
|
||||
],
|
||||
"domain": "Healthcare",
|
||||
"namespace": "http://example.org/healthcare#"
|
||||
"domain": "Healthcare"
|
||||
}
|
||||
|
||||
**Example 2 (Education Domain):**
|
||||
@@ -138,62 +292,48 @@ Output:
|
||||
"classes": [
|
||||
{
|
||||
"name": "Student",
|
||||
"name_chinese": "学生",
|
||||
"description": "在教育机构注册学习的人",
|
||||
"examples": ["本科生", "研究生", "在职学生"],
|
||||
"description": "A person enrolled in an educational institution for learning",
|
||||
"examples": ["Undergraduate student", "Graduate student", "Part-time student"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Role",
|
||||
"domain": "Education"
|
||||
},
|
||||
{
|
||||
"name": "Course",
|
||||
"name_chinese": "课程",
|
||||
"description": "涵盖特定学科或主题的结构化教育课程",
|
||||
"examples": ["计算机科学导论", "微积分I", "世界历史"],
|
||||
"description": "A structured educational program covering a specific subject or topic",
|
||||
"examples": ["Introduction to Computer Science", "Calculus I", "World History"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Concept",
|
||||
"domain": "Education"
|
||||
},
|
||||
{
|
||||
"name": "Professor",
|
||||
"name_chinese": "教授",
|
||||
"description": "教授课程并进行研究的学术教师",
|
||||
"examples": ["助理教授", "副教授", "正教授"],
|
||||
"description": "An academic teacher who teaches courses and conducts research",
|
||||
"examples": ["Assistant professor", "Associate professor", "Full professor"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Role",
|
||||
"domain": "Education"
|
||||
},
|
||||
{
|
||||
"name": "AcademicProgram",
|
||||
"name_chinese": "学术项目",
|
||||
"description": "通向学位或证书的结构化课程体系",
|
||||
"examples": ["理学学士", "文学硕士", "博士项目"],
|
||||
"description": "A structured curriculum leading to a degree or certificate",
|
||||
"examples": ["Bachelor of Science", "Master of Arts", "PhD program"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Concept",
|
||||
"domain": "Education"
|
||||
},
|
||||
{
|
||||
"name": "Assignment",
|
||||
"name_chinese": "作业",
|
||||
"description": "分配给学生以评估学习成果的任务或项目",
|
||||
"examples": ["论文", "习题集", "研究报告", "实验报告"],
|
||||
"description": "A task or project assigned to students to assess learning outcomes",
|
||||
"examples": ["Essay", "Problem set", "Research paper", "Lab report"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Object",
|
||||
"domain": "Education"
|
||||
},
|
||||
{
|
||||
"name": "Lecture",
|
||||
"name_chinese": "讲座",
|
||||
"description": "由教师进行的教育性演讲或讲座",
|
||||
"examples": ["入门讲座", "客座讲座", "在线讲座"],
|
||||
"parent_class": null,
|
||||
"entity_type": "Event",
|
||||
"domain": "Education"
|
||||
}
|
||||
],
|
||||
"domain": "Education",
|
||||
"namespace": "http://example.org/education#"
|
||||
"domain": "Education"
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
===Output Format===
|
||||
|
||||
@@ -203,8 +343,12 @@ Output:
|
||||
- Escape quotation marks in text with backslashes (\")
|
||||
- Ensure proper string closure and comma separation
|
||||
- No line breaks within JSON string values
|
||||
- All class names must be in PascalCase format
|
||||
- All class names must be unique (case-insensitive)
|
||||
- Extract between 5 and {{ max_classes }} classes
|
||||
{% if language == "zh" %}
|
||||
- 所有字段内容必须使用中文
|
||||
{% else %}
|
||||
- All field content must be in English
|
||||
{% endif %}
|
||||
|
||||
{{ json_schema }}
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
Extract entities and knowledge triplets from the given statement.
|
||||
|
||||
{% if language == "zh" %}
|
||||
**重要:请使用中文生成实体描述(description)和示例(example)。**
|
||||
**重要:请使用中文生成实体名称(name)、描述(description)和示例(example)。**
|
||||
{% else %}
|
||||
**Important: Please generate entity descriptions and examples in English.**
|
||||
**Important: Please generate entity names, descriptions and examples in English. If the original text is in Chinese, translate entity names to English.**
|
||||
{% endif %}
|
||||
|
||||
===Inputs===
|
||||
@@ -20,50 +20,48 @@ Extract entities and knowledge triplets from the given statement.
|
||||
**Entity Extraction:**
|
||||
- Extract entities with their types, context-independent descriptions, **concise examples**, aliases, and semantic memory classification
|
||||
{% if language == "zh" %}
|
||||
- **实体名称(name)必须使用中文**
|
||||
- **实体描述(description)必须使用中文**
|
||||
- **示例(example)必须使用中文**
|
||||
{% else %}
|
||||
- **Entity names must be in English** (translate if the original is in another language)
|
||||
- **Entity descriptions must be in English**
|
||||
- **Examples must be in English**
|
||||
{% endif %}
|
||||
- **Semantic Memory Classification (is_explicit_memory):**
|
||||
* Set to `true` if the entity represents **explicit/semantic memory**:
|
||||
- **Concepts:** "Machine Learning", "Photosynthesis", "Democracy", "人工智能", "光合作用", "民主"
|
||||
- **Knowledge:** "Python Programming Language", "Theory of Relativity", "Python编程语言", "相对论"
|
||||
- **Definitions:** "API (Application Programming Interface)", "REST API", "应用程序接口"
|
||||
- **Principles:** "SOLID Principles", "First Law of Thermodynamics", "SOLID原则", "热力学第一定律"
|
||||
- **Theories:** "Evolution Theory", "Quantum Mechanics", "进化论", "量子力学"
|
||||
- **Methods/Techniques:** "Agile Development", "Machine Learning Algorithm", "敏捷开发", "机器学习算法"
|
||||
- **Technical Terms:** "Neural Network", "Database", "神经网络", "数据库"
|
||||
- **Concepts:** "Machine Learning", "Photosynthesis", "Democracy"
|
||||
- **Knowledge:** "Python Programming Language", "Theory of Relativity"
|
||||
- **Definitions:** "API (Application Programming Interface)", "REST API"
|
||||
- **Principles:** "SOLID Principles", "First Law of Thermodynamics"
|
||||
- **Theories:** "Evolution Theory", "Quantum Mechanics"
|
||||
- **Methods/Techniques:** "Agile Development", "Machine Learning Algorithm"
|
||||
- **Technical Terms:** "Neural Network", "Database"
|
||||
* Set to `false` for:
|
||||
- **People:** "John Smith", "Dr. Wang", "张明", "王博士"
|
||||
- **Organizations:** "Microsoft", "Harvard University", "微软", "哈佛大学"
|
||||
- **Locations:** "Beijing", "Central Park", "北京", "中央公园"
|
||||
- **Events:** "2024 Conference", "Project Meeting", "2024会议", "项目会议"
|
||||
- **Specific objects:** "iPhone 15", "Building A", "iPhone 15", "A栋"
|
||||
- **People:** "John Smith", "Dr. Wang"
|
||||
- **Organizations:** "Microsoft", "Harvard University"
|
||||
- **Locations:** "Beijing", "Central Park"
|
||||
- **Events:** "2024 Conference", "Project Meeting"
|
||||
- **Specific objects:** "iPhone 15", "Building A"
|
||||
- **Example Generation (IMPORTANT for semantic memory entities):**
|
||||
* For entities where `is_explicit_memory=true`, generate a **concise example (around 20 characters)** to help understand the concept
|
||||
* The example should be:
|
||||
- **Specific and concrete**: Use real-world scenarios or applications
|
||||
- **Brief**: Around 20 characters (can be slightly longer if needed for clarity)
|
||||
- **In the same language as the entity name**
|
||||
* Examples:
|
||||
- Entity: "机器学习" → example: "如:用神经网络识别图片中的猫狗"
|
||||
- Entity: "SOLID Principles" → example: "e.g., Single Responsibility, Open-Closed"
|
||||
- Entity: "Photosynthesis" → example: "e.g., plants convert sunlight to energy"
|
||||
- Entity: "人工智能" → example: "如:智能客服、自动驾驶"
|
||||
{% if language == "zh" %}
|
||||
- **使用中文**
|
||||
{% else %}
|
||||
- **In English**
|
||||
{% endif %}
|
||||
* For non-semantic entities (`is_explicit_memory=false`), the example field can be empty
|
||||
- **Aliases Extraction (Important):**
|
||||
* **CRITICAL: Extract aliases ONLY in the SAME LANGUAGE as the input text**
|
||||
* **DO NOT translate or add aliases in different languages**
|
||||
* Include common alternative names in the same language (e.g., "北京" → aliases: ["北平", "京城"])
|
||||
* Include abbreviations and full names in the same language (e.g., "联合国" → aliases: ["联合国组织"])
|
||||
* Include nicknames and common variations in the same language (e.g., "纽约" → aliases: ["纽约市", "大苹果"])
|
||||
* If no aliases exist in the same language, use empty array: []
|
||||
* **Examples:**
|
||||
- Chinese input "北京" → aliases: ["北平", "京城"] (NOT ["Beijing", "Peking"])
|
||||
- English input "Beijing" → aliases: ["Peking"] (NOT ["北京", "北平"])
|
||||
- Chinese input "苹果公司" → aliases: ["苹果"] (NOT ["Apple Inc.", "Apple"])
|
||||
- **Aliases Extraction:**
|
||||
{% if language == "zh" %}
|
||||
* 别名使用中文
|
||||
{% else %}
|
||||
* Aliases should be in English
|
||||
{% endif %}
|
||||
* Include common alternative names, abbreviations and full names
|
||||
* If no aliases exist, use empty array: []
|
||||
- Exclude lengthy quotes, calendar dates, temporal ranges, and temporal expressions
|
||||
- For numeric values: extract as separate entities (instance_of: 'Numeric', name: units, numeric_value: value)
|
||||
Example: £30 → name: 'GBP', numeric_value: 30, instance_of: 'Numeric'
|
||||
@@ -73,6 +71,11 @@ Extract entities and knowledge triplets from the given statement.
|
||||
- Subject: main entity performing the action or being described
|
||||
- Predicate: relationship between entities (e.g., 'is', 'works at', 'believes')
|
||||
- Object: entity, value, or concept affected by the predicate
|
||||
{% if language == "zh" %}
|
||||
- subject_name 和 object_name 必须使用中文
|
||||
{% else %}
|
||||
- subject_name and object_name must be in English (translate if original is in another language)
|
||||
{% endif %}
|
||||
- Exclude all temporal expressions from every field
|
||||
- Use ONLY the predicates listed in "Predicate Instructions" (uppercase English tokens)
|
||||
- Do NOT translate predicate tokens
|
||||
@@ -81,7 +84,7 @@ Extract entities and knowledge triplets from the given statement.
|
||||
**When NOT to extract triplets:**
|
||||
- Non-propositional utterances (emotions, fillers, onomatopoeia)
|
||||
- No clear predicate from the given definitions applies
|
||||
- Standalone noun phrases or checklist items (e.g., "三脚架", "备用电池") → extract as entities only
|
||||
- Standalone noun phrases or checklist items → extract as entities only
|
||||
- Do NOT invent generic predicates (e.g., "IS_DOING", "FEELS", "MENTIONS")
|
||||
|
||||
**If no valid triplet exists:** Return triplets: [], extract entities if present, otherwise both arrays empty.
|
||||
@@ -96,248 +99,82 @@ Use ONLY these predicates. If none fits, set triplets to [].
|
||||
|
||||
|
||||
===Examples===
|
||||
|
||||
**Example 1 (English):** "I plan to travel to Paris next week and visit the Louvre."
|
||||
{% if language == "en" %}
|
||||
**Example 1 (English output):** "I plan to travel to Paris next week and visit the Louvre."
|
||||
Output:
|
||||
{
|
||||
"triplets": [
|
||||
{
|
||||
"subject_name": "I",
|
||||
"subject_id": 0,
|
||||
"predicate": "PLANS_TO_VISIT",
|
||||
"object_name": "Paris",
|
||||
"object_id": 1,
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"subject_name": "I",
|
||||
"subject_id": 0,
|
||||
"predicate": "PLANS_TO_VISIT",
|
||||
"object_name": "Louvre",
|
||||
"object_id": 2,
|
||||
"value": null
|
||||
}
|
||||
{"subject_name": "I", "subject_id": 0, "predicate": "PLANS_TO_VISIT", "object_name": "Paris", "object_id": 1, "value": null},
|
||||
{"subject_name": "I", "subject_id": 0, "predicate": "PLANS_TO_VISIT", "object_name": "Louvre", "object_id": 2, "value": null}
|
||||
],
|
||||
"entities": [
|
||||
{
|
||||
"entity_idx": 0,
|
||||
"name": "I",
|
||||
"type": "Person",
|
||||
"description": "The user",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 1,
|
||||
"name": "Paris",
|
||||
"type": "Location",
|
||||
"description": "Capital city of France",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 2,
|
||||
"name": "Louvre",
|
||||
"type": "Location",
|
||||
"description": "World-famous museum located in Paris",
|
||||
"example": "",
|
||||
"aliases": ["Louvre Museum"],
|
||||
"is_explicit_memory": false
|
||||
}
|
||||
{"entity_idx": 0, "name": "I", "type": "Person", "description": "The user", "example": "", "aliases": [], "is_explicit_memory": false},
|
||||
{"entity_idx": 1, "name": "Paris", "type": "Location", "description": "Capital city of France", "example": "", "aliases": [], "is_explicit_memory": false},
|
||||
{"entity_idx": 2, "name": "Louvre", "type": "Location", "description": "World-famous museum located in Paris", "example": "", "aliases": ["Louvre Museum"], "is_explicit_memory": false}
|
||||
]
|
||||
}
|
||||
|
||||
**Example 2 (English):** "John Smith works at Google and is responsible for AI product development."
|
||||
**Example 2 (Chinese input → English output - IMPORTANT: translate entity names):** "张明在腾讯工作,负责AI产品开发。"
|
||||
Output:
|
||||
{
|
||||
"triplets": [
|
||||
{
|
||||
"subject_name": "John Smith",
|
||||
"subject_id": 0,
|
||||
"predicate": "WORKS_AT",
|
||||
"object_name": "Google",
|
||||
"object_id": 1,
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"subject_name": "John Smith",
|
||||
"subject_id": 0,
|
||||
"predicate": "RESPONSIBLE_FOR",
|
||||
"object_name": "AI product development",
|
||||
"object_id": 2,
|
||||
"value": null
|
||||
}
|
||||
{"subject_name": "Zhang Ming", "subject_id": 0, "predicate": "WORKS_AT", "object_name": "Tencent", "object_id": 1, "value": null},
|
||||
{"subject_name": "Zhang Ming", "subject_id": 0, "predicate": "RESPONSIBLE_FOR", "object_name": "AI product development", "object_id": 2, "value": null}
|
||||
],
|
||||
"entities": [
|
||||
{
|
||||
"entity_idx": 0,
|
||||
"name": "John Smith",
|
||||
"type": "Person",
|
||||
"description": "Individual person name",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 1,
|
||||
"name": "Google",
|
||||
"type": "Organization",
|
||||
"description": "American technology company",
|
||||
"example": "",
|
||||
"aliases": ["Google LLC", "Alphabet Inc."],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 2,
|
||||
"name": "AI product development",
|
||||
"type": "Concept",
|
||||
"description": "Artificial intelligence product development work",
|
||||
"example": "e.g., developing chatbots, recommendation systems",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": true
|
||||
}
|
||||
{"entity_idx": 0, "name": "Zhang Ming", "type": "Person", "description": "Individual person name", "example": "", "aliases": [], "is_explicit_memory": false},
|
||||
{"entity_idx": 1, "name": "Tencent", "type": "Organization", "description": "Chinese technology company", "example": "", "aliases": ["Tencent Holdings"], "is_explicit_memory": false},
|
||||
{"entity_idx": 2, "name": "AI product development", "type": "Concept", "description": "Artificial intelligence product development work", "example": "e.g., developing chatbots", "aliases": [], "is_explicit_memory": true}
|
||||
]
|
||||
}
|
||||
|
||||
**Example 3 (Chinese):** "我计划下周去巴黎旅行,参观卢浮宫。"
|
||||
Output:
|
||||
{
|
||||
"triplets": [
|
||||
{
|
||||
"subject_name": "我",
|
||||
"subject_id": 0,
|
||||
"predicate": "PLANS_TO_VISIT",
|
||||
"object_name": "巴黎",
|
||||
"object_id": 1,
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"subject_name": "我",
|
||||
"subject_id": 0,
|
||||
"predicate": "PLANS_TO_VISIT",
|
||||
"object_name": "卢浮宫",
|
||||
"object_id": 2,
|
||||
"value": null
|
||||
}
|
||||
],
|
||||
"entities": [
|
||||
{
|
||||
"entity_idx": 0,
|
||||
"name": "我",
|
||||
"type": "Person",
|
||||
"description": "用户本人",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 1,
|
||||
"name": "巴黎",
|
||||
"type": "Location",
|
||||
"description": "法国首都城市",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 2,
|
||||
"name": "卢浮宫",
|
||||
"type": "Location",
|
||||
"description": "位于巴黎的世界著名博物馆",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
**Example 4 (Chinese):** "张明在腾讯工作,负责AI产品开发。"
|
||||
Output:
|
||||
{
|
||||
"triplets": [
|
||||
{
|
||||
"subject_name": "张明",
|
||||
"subject_id": 0,
|
||||
"predicate": "WORKS_AT",
|
||||
"object_name": "腾讯",
|
||||
"object_id": 1,
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"subject_name": "张明",
|
||||
"subject_id": 0,
|
||||
"predicate": "RESPONSIBLE_FOR",
|
||||
"object_name": "AI产品开发",
|
||||
"object_id": 2,
|
||||
"value": null
|
||||
}
|
||||
],
|
||||
"entities": [
|
||||
{
|
||||
"entity_idx": 0,
|
||||
"name": "张明",
|
||||
"type": "Person",
|
||||
"description": "个人姓名",
|
||||
"example": "",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 1,
|
||||
"name": "腾讯",
|
||||
"type": "Organization",
|
||||
"description": "中国科技公司",
|
||||
"example": "",
|
||||
"aliases": ["腾讯控股", "腾讯公司"],
|
||||
"is_explicit_memory": false
|
||||
},
|
||||
{
|
||||
"entity_idx": 2,
|
||||
"name": "AI产品开发",
|
||||
"type": "Concept",
|
||||
"description": "人工智能产品研发工作",
|
||||
"example": "如:开发智能客服机器人、推荐系统",
|
||||
"aliases": [],
|
||||
"is_explicit_memory": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
**Example 5 (Entity Only - English):** "Tripod"
|
||||
**Example 3 (Chinese input → English output):** "三脚架"
|
||||
Output:
|
||||
{
|
||||
"triplets": [],
|
||||
"entities": [
|
||||
{
|
||||
"entity_idx": 0,
|
||||
"name": "Tripod",
|
||||
"type": "Equipment",
|
||||
"description": "Photography equipment accessory",
|
||||
"example": "",
|
||||
"aliases": ["Camera Tripod"],
|
||||
"is_explicit_memory": false
|
||||
}
|
||||
{"entity_idx": 0, "name": "Tripod", "type": "Equipment", "description": "Photography equipment accessory", "example": "", "aliases": ["Camera Tripod"], "is_explicit_memory": false}
|
||||
]
|
||||
}
|
||||
{% else %}
|
||||
**Example 1 (English input → Chinese output):** "I plan to travel to Paris next week and visit the Louvre."
|
||||
Output:
|
||||
{
|
||||
"triplets": [
|
||||
{"subject_name": "我", "subject_id": 0, "predicate": "PLANS_TO_VISIT", "object_name": "巴黎", "object_id": 1, "value": null},
|
||||
{"subject_name": "我", "subject_id": 0, "predicate": "PLANS_TO_VISIT", "object_name": "卢浮宫", "object_id": 2, "value": null}
|
||||
],
|
||||
"entities": [
|
||||
{"entity_idx": 0, "name": "我", "type": "Person", "description": "用户本人", "example": "", "aliases": [], "is_explicit_memory": false},
|
||||
{"entity_idx": 1, "name": "巴黎", "type": "Location", "description": "法国首都城市", "example": "", "aliases": [], "is_explicit_memory": false},
|
||||
{"entity_idx": 2, "name": "卢浮宫", "type": "Location", "description": "位于巴黎的世界著名博物馆", "example": "", "aliases": [], "is_explicit_memory": false}
|
||||
]
|
||||
}
|
||||
|
||||
**Example 6 (Entity Only - Chinese):** "三脚架"
|
||||
**Example 2 (Chinese input → Chinese output):** "张明在腾讯工作,负责AI产品开发。"
|
||||
Output:
|
||||
{
|
||||
"triplets": [
|
||||
{"subject_name": "张明", "subject_id": 0, "predicate": "WORKS_AT", "object_name": "腾讯", "object_id": 1, "value": null},
|
||||
{"subject_name": "张明", "subject_id": 0, "predicate": "RESPONSIBLE_FOR", "object_name": "AI产品开发", "object_id": 2, "value": null}
|
||||
],
|
||||
"entities": [
|
||||
{"entity_idx": 0, "name": "张明", "type": "Person", "description": "个人姓名", "example": "", "aliases": [], "is_explicit_memory": false},
|
||||
{"entity_idx": 1, "name": "腾讯", "type": "Organization", "description": "中国科技公司", "example": "", "aliases": ["腾讯控股", "腾讯公司"], "is_explicit_memory": false},
|
||||
{"entity_idx": 2, "name": "AI产品开发", "type": "Concept", "description": "人工智能产品研发工作", "example": "如:开发智能客服机器人", "aliases": [], "is_explicit_memory": true}
|
||||
]
|
||||
}
|
||||
|
||||
**Example 3 (Entity Only - Chinese):** "三脚架"
|
||||
Output:
|
||||
{
|
||||
"triplets": [],
|
||||
"entities": [
|
||||
{
|
||||
"entity_idx": 0,
|
||||
"name": "三脚架",
|
||||
"type": "Equipment",
|
||||
"description": "摄影器材配件",
|
||||
"example": "",
|
||||
"aliases": ["相机三脚架"],
|
||||
"is_explicit_memory": false
|
||||
}
|
||||
{"entity_idx": 0, "name": "三脚架", "type": "Equipment", "description": "摄影器材配件", "example": "", "aliases": ["相机三脚架"], "is_explicit_memory": false}
|
||||
]
|
||||
}
|
||||
{% endif %}
|
||||
===End of Examples===
|
||||
|
||||
===Output Format===
|
||||
|
||||
@@ -348,10 +185,10 @@ Output:
|
||||
- Ensure proper string closure and comma separation
|
||||
- No line breaks within JSON string values
|
||||
{% if language == "zh" %}
|
||||
- **语言要求:实体描述(description)和示例(example)必须使用中文**
|
||||
- **语言要求:实体名称(name)、描述(description)、示例(example)、subject_name、object_name 必须使用中文**
|
||||
{% else %}
|
||||
- **Language Requirement: Entity descriptions and examples must be in English**
|
||||
- **Language Requirement: Entity names, descriptions, examples, subject_name, object_name must be in English**
|
||||
- **If the original text is in Chinese, translate all names to English**
|
||||
{% endif %}
|
||||
- Preserve the original language and do not translate
|
||||
|
||||
{{ json_schema }}
|
||||
{{ json_schema }}
|
||||
|
||||
@@ -1,3 +1,69 @@
|
||||
{% if language == "en" %}
|
||||
You are a professional mental health consultant. Based on the following user's emotional health data and personal information, generate 3-5 personalized emotional improvement suggestions.
|
||||
|
||||
## User Emotional Health Data
|
||||
|
||||
Health Score: {{ health_data.health_score }}/100
|
||||
Health Level: {{ health_data.level }}
|
||||
|
||||
Dimension Analysis:
|
||||
- Positivity Rate: {{ health_data.dimensions.positivity_rate.score }}/100
|
||||
- Positive Emotions: {{ health_data.dimensions.positivity_rate.positive_count }} times
|
||||
- Negative Emotions: {{ health_data.dimensions.positivity_rate.negative_count }} times
|
||||
- Neutral Emotions: {{ health_data.dimensions.positivity_rate.neutral_count }} times
|
||||
|
||||
- Stability: {{ health_data.dimensions.stability.score }}/100
|
||||
- Standard Deviation: {{ health_data.dimensions.stability.std_deviation }}
|
||||
|
||||
- Resilience: {{ health_data.dimensions.resilience.score }}/100
|
||||
- Recovery Rate: {{ health_data.dimensions.resilience.recovery_rate }}
|
||||
|
||||
Emotion Distribution:
|
||||
{{ emotion_distribution_json }}
|
||||
|
||||
## Emotion Pattern Analysis
|
||||
|
||||
Dominant Negative Emotion: {{ patterns.dominant_negative_emotion|default('None') }}
|
||||
Emotion Volatility: {{ patterns.emotion_volatility|default('Unknown') }}
|
||||
High Intensity Emotion Count: {{ patterns.high_intensity_emotions|default([])|length }}
|
||||
|
||||
## User Interests
|
||||
|
||||
{{ user_profile.interests|default(['Unknown'])|join(', ') }}
|
||||
|
||||
## Task Requirements
|
||||
|
||||
Please generate 3-5 personalized suggestions, each containing:
|
||||
1. type: Suggestion type (Emotion Balance/Activity Recommendation/Social Connection/Stress Management)
|
||||
2. title: Suggestion title (short and impactful)
|
||||
3. content: Suggestion content (detailed explanation, 50-100 words)
|
||||
4. priority: Priority level (High/Medium/Low)
|
||||
5. actionable_steps: 3 specific executable steps
|
||||
|
||||
Also provide a health_summary (no more than 50 words) summarizing the user's overall emotional state.
|
||||
|
||||
Please return in JSON format as follows:
|
||||
{
|
||||
"health_summary": "Your emotional health status...",
|
||||
"suggestions": [
|
||||
{
|
||||
"type": "Emotion Balance",
|
||||
"title": "Suggestion Title",
|
||||
"content": "Suggestion content...",
|
||||
"priority": "High",
|
||||
"actionable_steps": ["Step 1", "Step 2", "Step 3"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Notes:
|
||||
- Suggestions should be specific and actionable, avoid vague advice
|
||||
- Provide personalized suggestions based on user's interests and hobbies
|
||||
- Provide targeted suggestions for main issues (such as dominant negative emotions)
|
||||
- Allocate priorities reasonably (at least 1 high, 1-2 medium, rest low)
|
||||
- The 3 steps for each suggestion should be progressive and easy to implement
|
||||
- All output must be in English
|
||||
{% else %}
|
||||
你是一位专业的心理健康顾问。请根据以下用户的情绪健康数据和个人信息,生成3-5条个性化的情绪改善建议。
|
||||
|
||||
## 用户情绪健康数据
|
||||
@@ -33,10 +99,10 @@
|
||||
## 任务要求
|
||||
|
||||
请生成3-5条个性化建议,每条建议包含:
|
||||
1. type: 建议类型(emotion_balance/activity_recommendation/social_connection/stress_management)
|
||||
1. type: 建议类型(情绪平衡/活动建议/社交联系/压力管理)
|
||||
2. title: 建议标题(简短有力)
|
||||
3. content: 建议内容(详细说明,50-100字)
|
||||
4. priority: 优先级(high/medium/low)
|
||||
4. priority: 优先级(高/中/低)
|
||||
5. actionable_steps: 3个可执行的具体步骤
|
||||
|
||||
同时提供一个health_summary(不超过50字),概括用户的整体情绪状态。
|
||||
@@ -46,10 +112,10 @@
|
||||
"health_summary": "您的情绪健康状况...",
|
||||
"suggestions": [
|
||||
{
|
||||
"type": "emotion_balance",
|
||||
"type": "情绪平衡",
|
||||
"title": "建议标题",
|
||||
"content": "建议内容...",
|
||||
"priority": "high",
|
||||
"priority": "高",
|
||||
"actionable_steps": ["步骤1", "步骤2", "步骤3"]
|
||||
}
|
||||
]
|
||||
@@ -61,3 +127,4 @@
|
||||
- 针对主要问题(如主要负面情绪)提供针对性建议
|
||||
- 优先级要合理分配(至少1个high,1-2个medium,其余low)
|
||||
- 每个建议的3个步骤要循序渐进、易于实施
|
||||
{% endif %}
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
|
||||
Your task is to generate a comprehensive memory insight report based on the provided data analysis. The report should include four distinct sections that capture different aspects of the user's memory patterns and characteristics.
|
||||
|
||||
{% if language == "zh" %}
|
||||
**重要:请使用中文生成记忆洞察报告内容。**
|
||||
{% else %}
|
||||
**Important: Please generate the memory insight report content in English.**
|
||||
{% endif %}
|
||||
|
||||
|
||||
===Inputs===
|
||||
{% if domain_distribution %}
|
||||
@@ -31,56 +37,105 @@ Your task is to generate a comprehensive memory insight report based on the prov
|
||||
|
||||
**Section-Specific Requirements:**
|
||||
|
||||
1. **总体概述 (Overview)** (100-150 Chinese characters)
|
||||
- Focus on: Overall analysis of user profile based on interaction logs
|
||||
- Describe the user's main role, work network, and collaboration spirit
|
||||
- Use professional, data-driven language style
|
||||
- Example reference: "通过对156次交互日志的深度分析,系统发现三层一位主要用户档案和数据分析的产品经理。他的工作网络体现出鲜明的目标导向和团队协作精神。"
|
||||
{% if language == "zh" %}
|
||||
1. **总体概述** (100-150字)
|
||||
- 重点:基于交互日志对用户档案进行整体分析
|
||||
- 描述用户的主要角色、工作网络和协作精神
|
||||
- 使用专业、数据驱动的语言风格
|
||||
- 示例参考:"通过对156次交互日志的深度分析,系统发现张三是一位主要从事用户档案和数据分析的产品经理。他的工作网络体现出鲜明的目标导向和团队协作精神。"
|
||||
|
||||
2. **行为模式 (Behavior Pattern)** (80-120 Chinese characters)
|
||||
- Focus on: Work patterns, time regularity, and behavioral characteristics
|
||||
- Describe weekly work patterns and time preferences
|
||||
- Use objective, analytical language
|
||||
- Example reference: "张三的工作模式呈现出鲜明的周期性:周一通常用于规划和会议,周三周四专注于产品设计和用户研究,周五进行总结和复盘。他倾向于在上午进行头脑风暴,下午处理执行性工作。"
|
||||
2. **行为模式** (80-120字)
|
||||
- 重点:工作模式、时间规律和行为特征
|
||||
- 描述每周工作模式和时间偏好
|
||||
- 使用客观、分析性的语言
|
||||
- 示例参考:"张三的工作模式呈现出鲜明的周期性:周一通常用于规划和会议,周三周四专注于产品设计和用户研究,周五进行总结和复盘。他倾向于在上午进行头脑风暴,下午处理执行性工作。"
|
||||
|
||||
3. **关键发现 (Key Findings)** (3-4 bullet points, 30-50 characters each)
|
||||
- Focus on: Specific, insightful observations about user behavior and preferences
|
||||
- Use bullet points (•) format
|
||||
- Each finding should be concrete and data-supported
|
||||
- Example reference:
|
||||
3. **关键发现** (3-4个要点,每个30-50字)
|
||||
- 重点:关于用户行为和偏好的具体、有洞察力的观察
|
||||
- 使用项目符号(•)格式
|
||||
- 每个发现应具体且有数据支持
|
||||
- 示例参考:
|
||||
"• 在产品决策中,张三总是优先考虑用户反应,这在68%的决策记录中得到体现
|
||||
• 他善于使用数据可视化工具来支持论点,这种习惯在项目管理中发挥了重要作用
|
||||
• 团队成员对他的评价中,"思路清晰"和"思路敏捷"两个关键词出现频率最高
|
||||
• 他对AI机器学习领域保持持续关注,近3个月参加了7次相关培训"
|
||||
|
||||
4. **成长轨迹 (Growth Trajectory)** (100-150 Chinese characters)
|
||||
4. **成长轨迹** (100-150字)
|
||||
- 重点:用户的成长历程、关键里程碑和能力提升
|
||||
- 按时间顺序组织内容
|
||||
- 突出角色变化和成就
|
||||
- 使用积极、鼓励的语气
|
||||
- 示例参考:"从入职时的产品经理成长为高级产品经理,张三在产品规划、团队管理和技术理解三个方面都有显著提升。特别是在最近一年,他开始独立主导更复杂的项目,展现出更强的战略思维能力。他的成长轨迹显示出对新技术的持续学习和对产品思维的不断深化。"
|
||||
{% else %}
|
||||
1. **Overview** (100-150 words)
|
||||
- Focus on: Overall analysis of user profile based on interaction logs
|
||||
- Describe the user's main role, work network, and collaboration spirit
|
||||
- Use professional, data-driven language style
|
||||
- Example reference: "Through in-depth analysis of 156 interaction logs, the system identified Zhang San as a product manager primarily focused on user profiling and data analysis. His work network demonstrates a clear goal-oriented approach and team collaboration spirit."
|
||||
|
||||
2. **Behavior Pattern** (80-120 words)
|
||||
- Focus on: Work patterns, time regularity, and behavioral characteristics
|
||||
- Describe weekly work patterns and time preferences
|
||||
- Use objective, analytical language
|
||||
- Example reference: "Zhang San's work pattern shows distinct periodicity: Mondays are typically used for planning and meetings, Wednesdays and Thursdays focus on product design and user research, and Fridays are for summary and review. He tends to brainstorm in the morning and handle execution tasks in the afternoon."
|
||||
|
||||
3. **Key Findings** (3-4 bullet points, 30-50 words each)
|
||||
- Focus on: Specific, insightful observations about user behavior and preferences
|
||||
- Use bullet points (•) format
|
||||
- Each finding should be concrete and data-supported
|
||||
- Example reference:
|
||||
"• In product decisions, Zhang San always prioritizes user feedback, as evidenced in 68% of decision records
|
||||
• He excels at using data visualization tools to support arguments, a habit that plays an important role in project management
|
||||
• Among team member evaluations, 'clear thinking' and 'quick thinking' are the most frequently mentioned keywords
|
||||
• He maintains continuous attention to AI and machine learning, attending 7 related training sessions in the past 3 months"
|
||||
|
||||
4. **Growth Trajectory** (100-150 words)
|
||||
- Focus on: User's growth journey, key milestones, and capability improvements
|
||||
- Organize content chronologically
|
||||
- Highlight role changes and achievements
|
||||
- Use positive, encouraging tone
|
||||
- Example reference: "从入职时的产品经理成长为高级产品经理,张三在产品单独、团队管理和技术理解三个方面都有显著提升。特别是在最近一年,他开始独立主导更复杂的项目,展现出更强的战略思维能力。他的成长轨迹显示出对新技术的持续学习和对产品思维的不断深化。"
|
||||
- Example reference: "Growing from a product manager at entry to a senior product manager, Zhang San has shown significant improvement in product planning, team management, and technical understanding. Especially in the past year, he has begun to independently lead more complex projects, demonstrating stronger strategic thinking capabilities. His growth trajectory shows continuous learning of new technologies and deepening of product thinking."
|
||||
{% endif %}
|
||||
|
||||
|
||||
===Output Format (MUST STRICTLY FOLLOW)===
|
||||
|
||||
{% if language == "zh" %}
|
||||
【总体概述】
|
||||
[100-150 characters describing overall user profile and work network based on interaction analysis]
|
||||
[100-150字,基于交互分析描述用户整体档案和工作网络]
|
||||
|
||||
【行为模式】
|
||||
[80-120 characters describing work patterns, time regularity, and behavioral characteristics]
|
||||
[80-120字,描述工作模式、时间规律和行为特征]
|
||||
|
||||
【关键发现】
|
||||
• [First key finding with data support, 30-50 characters]
|
||||
• [Second key finding with data support, 30-50 characters]
|
||||
• [Third key finding with data support, 30-50 characters]
|
||||
• [Fourth key finding with data support, 30-50 characters]
|
||||
• [第一个关键发现,有数据支持,30-50字]
|
||||
• [第二个关键发现,有数据支持,30-50字]
|
||||
• [第三个关键发现,有数据支持,30-50字]
|
||||
• [第四个关键发现,有数据支持,30-50字]
|
||||
|
||||
【成长轨迹】
|
||||
[100-150 characters describing growth journey, milestones, and capability improvements]
|
||||
[100-150字,描述成长历程、关键里程碑和能力提升]
|
||||
{% else %}
|
||||
【Overview】
|
||||
[100-150 words describing overall user profile and work network based on interaction analysis]
|
||||
|
||||
【Behavior Pattern】
|
||||
[80-120 words describing work patterns, time regularity, and behavioral characteristics]
|
||||
|
||||
【Key Findings】
|
||||
• [First key finding with data support, 30-50 words]
|
||||
• [Second key finding with data support, 30-50 words]
|
||||
• [Third key finding with data support, 30-50 words]
|
||||
• [Fourth key finding with data support, 30-50 words]
|
||||
|
||||
【Growth Trajectory】
|
||||
[100-150 words describing growth journey, milestones, and capability improvements]
|
||||
{% endif %}
|
||||
|
||||
|
||||
===Example===
|
||||
|
||||
{% if language == "zh" %}
|
||||
Example Input:
|
||||
- 核心领域分布: 产品管理(38%), 数据分析(24%), 团队协作(21%)
|
||||
- 活跃时段: 用户在每年的 4 和 10 月最为活跃
|
||||
@@ -101,6 +156,28 @@ Example Output:
|
||||
|
||||
【成长轨迹】
|
||||
从入职时的产品经理成长为高级产品经理,张三在产品规划、团队管理和技术理解三个方面都有显著提升。特别是在最近一年,他开始独立主导更复杂的项目,展现出更强的战略思维能力。他与李明的47条共同记忆见证了他的成长历程。
|
||||
{% else %}
|
||||
Example Input:
|
||||
- Core Domain Distribution: Product Management (38%), Data Analysis (24%), Team Collaboration (21%)
|
||||
- Active Periods: User is most active in April and October each year
|
||||
- Social Connections: Has the most shared memories (47 entries) with user "Li Ming", primarily during 2020-2023
|
||||
|
||||
Example Output:
|
||||
【Overview】
|
||||
Through in-depth analysis of 156 interaction logs, the system identified Zhang San as a product manager primarily focused on user profiling and data analysis. His work network demonstrates a clear goal-oriented approach and team collaboration spirit, with deep practical experience in product management, data analysis, and team collaboration.
|
||||
|
||||
【Behavior Pattern】
|
||||
Zhang San's work pattern shows distinct periodicity: Mondays are typically used for planning and meetings, Wednesdays and Thursdays focus on product design and user research, and Fridays are for summary and review. He tends to brainstorm in the morning and handle execution tasks in the afternoon. April and October are his most active periods each year.
|
||||
|
||||
【Key Findings】
|
||||
• In product decisions, Zhang San always prioritizes user feedback, as evidenced in 68% of decision records
|
||||
• He excels at using data visualization tools to support arguments, a habit that plays an important role in project management
|
||||
• Among team member evaluations, "clear thinking" and "quick thinking" are the most frequently mentioned keywords
|
||||
• He maintains continuous attention to AI and machine learning, attending 7 related training sessions in the past 3 months
|
||||
|
||||
【Growth Trajectory】
|
||||
Growing from a product manager at entry to a senior product manager, Zhang San has shown significant improvement in product planning, team management, and technical understanding. Especially in the past year, he has begun to independently lead more complex projects, demonstrating stronger strategic thinking capabilities. His 47 shared memories with Li Ming bear witness to his growth journey.
|
||||
{% endif %}
|
||||
|
||||
===End of Example===
|
||||
|
||||
@@ -133,20 +210,40 @@ After generating the report, perform the following self-review steps:
|
||||
|
||||
===Output Requirements===
|
||||
|
||||
{% if language == "zh" %}
|
||||
**语言要求:**
|
||||
- 输出语言必须始终为简体中文
|
||||
- 所有章节内容必须使用中文
|
||||
- 章节标题必须使用指定的中文格式:【总体概述】【行为模式】【关键发现】【成长轨迹】
|
||||
|
||||
**格式要求:**
|
||||
- 每个章节必须以标题开头,标题独占一行
|
||||
- 内容紧跟标题之后
|
||||
- 章节之间用空行分隔
|
||||
- 关键发现章节必须使用项目符号(•)
|
||||
- 严格遵守每个章节的字数限制
|
||||
|
||||
**内容要求:**
|
||||
- 仅使用提供的数据点
|
||||
- 不得捏造或推测信息
|
||||
- 如果某个章节数据不足,请简要说明或跳过
|
||||
- 全文保持专业、分析性的语气
|
||||
{% else %}
|
||||
**LANGUAGE REQUIREMENT:**
|
||||
- The output language should ALWAYS be Chinese (Simplified)
|
||||
- All section content must be in Chinese
|
||||
- Section headers must use the specified Chinese format: 【总体概述】【行为模式】【关键发现】【成长轨迹】
|
||||
- The output language must ALWAYS be English
|
||||
- All section content must be in English
|
||||
- Section headers must use the specified English format: 【Overview】【Behavior Pattern】【Key Findings】【Growth Trajectory】
|
||||
|
||||
**FORMAT REQUIREMENT:**
|
||||
- Each section must start with its header on a new line
|
||||
- Content follows immediately after the header
|
||||
- Sections are separated by blank lines
|
||||
- Key Findings section must use bullet points (•)
|
||||
- Strictly adhere to character limits for each section
|
||||
- Strictly adhere to word limits for each section
|
||||
|
||||
**CONTENT REQUIREMENT:**
|
||||
- Only use provided data points
|
||||
- Do not fabricate or speculate information
|
||||
- If data is insufficient for a section, provide a brief note or skip
|
||||
- Maintain professional, analytical tone throughout
|
||||
{% endif %}
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
Your task is to generate a comprehensive user profile based on the provided entities and statements. The profile should include four distinct sections that capture different aspects of the user's identity and characteristics.
|
||||
|
||||
{% if language == "zh" %}
|
||||
**重要:请使用中文生成用户画像内容。**
|
||||
{% else %}
|
||||
**Important: Please generate the user profile content in English.**
|
||||
{% endif %}
|
||||
|
||||
===Inputs===
|
||||
{% if user_id %}
|
||||
@@ -30,40 +35,73 @@ Your task is to generate a comprehensive user profile based on the provided enti
|
||||
|
||||
**Section-Specific Requirements:**
|
||||
|
||||
1. **Basic Introduction** (4-5 sentences, max 150 Chinese characters)
|
||||
{% if language == "zh" %}
|
||||
1. **基本介绍** (4-5句话,最多150字)
|
||||
- 重点:身份、职业、地点及其他基本人口统计信息
|
||||
- 提供关于用户是谁的事实背景
|
||||
|
||||
2. **性格特点** (2-3句话,最多80字)
|
||||
- 重点:性格特征、行为习惯、沟通风格
|
||||
- 描述用户互动和行为中可观察到的模式
|
||||
|
||||
3. **核心价值观** (1-2句话,最多50字)
|
||||
- 重点:价值观、信念、目标和愿望
|
||||
- 捕捉对用户最重要的内容以及驱动其决策的因素
|
||||
|
||||
4. **一句话总结** (1句话,最多40字)
|
||||
- 提供对用户核心特质的高度浓缩描述
|
||||
- 类似于捕捉其本质的个人标语或座右铭
|
||||
{% else %}
|
||||
1. **Basic Introduction** (4-5 sentences, max 150 words)
|
||||
- Focus on: identity, occupation, location, and other basic demographic information
|
||||
- Provide factual background about who the user is
|
||||
|
||||
2. **Personality Traits** (2-3 sentences, max 80 Chinese characters)
|
||||
2. **Personality Traits** (2-3 sentences, max 80 words)
|
||||
- Focus on: personality characteristics, behavioral habits, communication style
|
||||
- Describe observable patterns in how the user interacts and behaves
|
||||
|
||||
3. **Core Values** (1-2 sentences, max 50 Chinese characters)
|
||||
3. **Core Values** (1-2 sentences, max 50 words)
|
||||
- Focus on: values, beliefs, goals, and aspirations
|
||||
- Capture what matters most to the user and what drives their decisions
|
||||
|
||||
4. **One-Sentence Summary** (1 sentence, max 40 Chinese characters)
|
||||
4. **One-Sentence Summary** (1 sentence, max 40 words)
|
||||
- Provide a highly condensed characterization of the user's core traits
|
||||
- Similar to a personal tagline or motto that captures their essence
|
||||
{% endif %}
|
||||
|
||||
|
||||
===Output Format (MUST STRICTLY FOLLOW)===
|
||||
|
||||
{% if language == "zh" %}
|
||||
【基本介绍】
|
||||
[4-5 sentences describing the user's basic identity, occupation, and location]
|
||||
[4-5句话描述用户的基本身份、职业和地点]
|
||||
|
||||
【性格特点】
|
||||
[2-3 sentences describing the user's personality traits, behavioral habits, and communication style]
|
||||
[2-3句话描述用户的性格特征、行为习惯和沟通风格]
|
||||
|
||||
【核心价值观】
|
||||
[1-2 sentences describing the user's values, beliefs, and goals]
|
||||
[1-2句话描述用户的价值观、信念和目标]
|
||||
|
||||
【一句话总结】
|
||||
[1句话提供对用户核心特质的高度浓缩总结]
|
||||
{% else %}
|
||||
【Basic Introduction】
|
||||
[4-5 sentences describing the user's basic identity, occupation, and location]
|
||||
|
||||
【Personality Traits】
|
||||
[2-3 sentences describing the user's personality traits, behavioral habits, and communication style]
|
||||
|
||||
【Core Values】
|
||||
[1-2 sentences describing the user's values, beliefs, and goals]
|
||||
|
||||
【One-Sentence Summary】
|
||||
[1 sentence providing a highly condensed summary of the user's core characteristics]
|
||||
{% endif %}
|
||||
|
||||
|
||||
===Example===
|
||||
|
||||
{% if language == "zh" %}
|
||||
Example Input:
|
||||
- User ID: user_12345
|
||||
- Core Entities & Frequency: 产品经理 (15), AI (12), 深圳 (10), 数据分析 (8), 团队协作 (7)
|
||||
@@ -81,6 +119,25 @@ Example Output:
|
||||
|
||||
【一句话总结】
|
||||
"让每一个产品决策都充满温度。"
|
||||
{% else %}
|
||||
Example Input:
|
||||
- User ID: user_12345
|
||||
- Core Entities & Frequency: Product Manager (15), AI (12), San Francisco (10), Data Analysis (8), Team Collaboration (7)
|
||||
- Representative Statement Samples: I have been working as a product manager in San Francisco for 5 years | I believe good products come from deep understanding of user needs | I enjoy playing a coordinating role in teams | Data-driven decision making is my work principle
|
||||
|
||||
Example Output:
|
||||
【Basic Introduction】
|
||||
This is a passionate senior product manager based in San Francisco. Over the past 5 years, they have focused on AI and data-driven product design, dedicated to creating products that truly improve users' lives. They believe good products stem from deep understanding of user needs and continuous exploration of technological possibilities.
|
||||
|
||||
【Personality Traits】
|
||||
Outgoing personality with excellent communication skills and attention to detail. Enjoys playing a coordinating role in teams, helping everyone reach consensus. Maintains optimism when facing challenges, believing every problem has a solution.
|
||||
|
||||
【Core Values】
|
||||
User-first, data-driven, continuous learning, team collaboration
|
||||
|
||||
【One-Sentence Summary】
|
||||
"Making every product decision with warmth and purpose."
|
||||
{% endif %}
|
||||
|
||||
===End of Example===
|
||||
|
||||
@@ -91,7 +148,7 @@ Before generating your final output, internally verify:
|
||||
1. All content is grounded in provided data (no fabrication)
|
||||
2. Format follows the specified structure with correct headers
|
||||
3. Tone is objective, third-person, and neutral
|
||||
4. All four sections are complete and within character limits
|
||||
4. All four sections are complete and within character/word limits
|
||||
|
||||
**IMPORTANT: These checks are for your internal use only. DO NOT include them in your output.**
|
||||
|
||||
@@ -101,14 +158,24 @@ Before generating your final output, internally verify:
|
||||
**CRITICAL: Your response must ONLY contain the four sections below. Do not include any reflection, self-review, or meta-commentary.**
|
||||
|
||||
**LANGUAGE REQUIREMENT:**
|
||||
- The output language should ALWAYS be Chinese (Simplified)
|
||||
- All section content must be in Chinese
|
||||
- Section headers must use the specified Chinese format: 【基本介绍】【性格特点】【核心价值观】【一句话总结】
|
||||
{% if language == "zh" %}
|
||||
- 输出语言必须为简体中文
|
||||
- 所有部分内容必须使用中文
|
||||
- 部分标题必须使用指定的中文格式:【基本介绍】【性格特点】【核心价值观】【一句话总结】
|
||||
{% else %}
|
||||
- The output language must be English
|
||||
- All section content must be in English
|
||||
- Section headers must use the specified format: 【Basic Introduction】【Personality Traits】【Core Values】【One-Sentence Summary】
|
||||
{% endif %}
|
||||
|
||||
**FORMAT REQUIREMENT:**
|
||||
- Each section must start with its header on a new line
|
||||
- Content follows immediately after the header
|
||||
- Sections are separated by blank lines
|
||||
- Strictly adhere to character limits for each section
|
||||
- **DO NOT include any text after the 【一句话总结】 section**
|
||||
{% if language == "zh" %}
|
||||
- 严格遵守每个部分的字数限制
|
||||
{% else %}
|
||||
- Strictly adhere to word limits for each section
|
||||
{% endif %}
|
||||
- **DO NOT include any text after the final section**
|
||||
- **DO NOT output reflection steps, self-review, or verification notes**
|
||||
|
||||
@@ -57,24 +57,57 @@ class EmotionAnalyticsService:
|
||||
self.emotion_repo = EmotionRepository(connector)
|
||||
logger.info("情绪分析服务初始化完成")
|
||||
|
||||
# 情绪类型的中英文映射
|
||||
EMOTION_TYPE_TRANSLATIONS = {
|
||||
'joy': {'zh': '喜悦', 'en': 'Joy'},
|
||||
'sadness': {'zh': '悲伤', 'en': 'Sadness'},
|
||||
'anger': {'zh': '愤怒', 'en': 'Anger'},
|
||||
'fear': {'zh': '恐惧', 'en': 'Fear'},
|
||||
'surprise': {'zh': '惊讶', 'en': 'Surprise'},
|
||||
'neutral': {'zh': '中性', 'en': 'Neutral'}
|
||||
}
|
||||
|
||||
def _translate_emotion_type(self, emotion_type: str, language: str = "zh") -> str:
|
||||
"""将情绪类型翻译成指定语言
|
||||
|
||||
Args:
|
||||
emotion_type: 情绪类型(英文key)
|
||||
language: 目标语言 ("zh" 或 "en")
|
||||
|
||||
Returns:
|
||||
翻译后的情绪类型名称
|
||||
"""
|
||||
if emotion_type in self.EMOTION_TYPE_TRANSLATIONS:
|
||||
return self.EMOTION_TYPE_TRANSLATIONS[emotion_type].get(language, emotion_type)
|
||||
return emotion_type
|
||||
|
||||
async def get_emotion_tags(
|
||||
self,
|
||||
end_user_id: str,
|
||||
emotion_type: Optional[str] = None,
|
||||
start_date: Optional[str] = None,
|
||||
end_date: Optional[str] = None,
|
||||
limit: int = 10
|
||||
limit: int = 10,
|
||||
language: str = "zh"
|
||||
) -> Dict[str, Any]:
|
||||
"""获取情绪标签统计
|
||||
|
||||
查询指定用户的情绪类型分布,包括计数、百分比和平均强度。
|
||||
确保返回所有6个情绪维度(joy、sadness、anger、fear、surprise、neutral),
|
||||
即使某些维度没有数据也会返回count=0的记录。
|
||||
|
||||
Args:
|
||||
end_user_id: 用户ID
|
||||
emotion_type: 情绪类型过滤
|
||||
start_date: 开始日期
|
||||
end_date: 结束日期
|
||||
limit: 返回数量限制
|
||||
language: 输出语言 ("zh" 中文, "en" 英文)
|
||||
|
||||
"""
|
||||
try:
|
||||
logger.info(f"获取情绪标签统计: user={end_user_id}, type={emotion_type}, "
|
||||
f"start={start_date}, end={end_date}, limit={limit}")
|
||||
f"start={start_date}, end={end_date}, limit={limit}, language={language}")
|
||||
|
||||
# 调用仓储层查询
|
||||
tags = await self.emotion_repo.get_emotion_tags(
|
||||
@@ -91,15 +124,17 @@ class EmotionAnalyticsService:
|
||||
# 将查询结果转换为字典,方便查找
|
||||
tags_dict = {tag["emotion_type"]: tag for tag in tags}
|
||||
|
||||
# 补全缺失的情绪维度
|
||||
# 补全缺失的情绪维度,并翻译 emotion_type
|
||||
complete_tags = []
|
||||
for emotion in all_emotion_types:
|
||||
if emotion in tags_dict:
|
||||
complete_tags.append(tags_dict[emotion])
|
||||
tag = tags_dict[emotion].copy()
|
||||
tag["emotion_type"] = self._translate_emotion_type(emotion, language)
|
||||
complete_tags.append(tag)
|
||||
else:
|
||||
# 如果该情绪类型不存在,添加默认值
|
||||
complete_tags.append({
|
||||
"emotion_type": emotion,
|
||||
"emotion_type": self._translate_emotion_type(emotion, language),
|
||||
"count": 0,
|
||||
"percentage": 0.0,
|
||||
"avg_intensity": 0.0
|
||||
@@ -475,6 +510,7 @@ class EmotionAnalyticsService:
|
||||
self,
|
||||
end_user_id: str,
|
||||
db: Session,
|
||||
language: str = "zh",
|
||||
) -> Dict[str, Any]:
|
||||
"""生成个性化情绪建议
|
||||
|
||||
@@ -483,6 +519,7 @@ class EmotionAnalyticsService:
|
||||
Args:
|
||||
end_user_id: 宿主ID(用户组ID)
|
||||
db: 数据库会话
|
||||
language: 输出语言 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
Dict: 包含个性化建议的响应:
|
||||
@@ -533,7 +570,7 @@ class EmotionAnalyticsService:
|
||||
user_profile = await self._get_simple_user_profile(end_user_id)
|
||||
|
||||
# 6. 构建LLM prompt
|
||||
prompt = await self._build_suggestion_prompt(health_data, patterns, user_profile)
|
||||
prompt = await self._build_suggestion_prompt(health_data, patterns, user_profile, language)
|
||||
|
||||
# 7. 调用LLM生成建议(使用配置中的LLM)
|
||||
if llm_client is None:
|
||||
@@ -554,12 +591,12 @@ class EmotionAnalyticsService:
|
||||
except Exception as e:
|
||||
logger.error(f"LLM 结构化输出失败: {str(e)}")
|
||||
# 返回默认建议
|
||||
suggestions_response = self._get_default_suggestions(health_data)
|
||||
suggestions_response = self._get_default_suggestions(health_data, language)
|
||||
|
||||
# 8. 验证建议数量(3-5条)
|
||||
if len(suggestions_response.suggestions) < 3:
|
||||
logger.warning(f"建议数量不足: {len(suggestions_response.suggestions)}")
|
||||
suggestions_response = self._get_default_suggestions(health_data)
|
||||
suggestions_response = self._get_default_suggestions(health_data, language)
|
||||
elif len(suggestions_response.suggestions) > 5:
|
||||
logger.warning(f"建议数量过多: {len(suggestions_response.suggestions)}")
|
||||
suggestions_response.suggestions = suggestions_response.suggestions[:5]
|
||||
@@ -624,7 +661,8 @@ class EmotionAnalyticsService:
|
||||
self,
|
||||
health_data: Dict[str, Any],
|
||||
patterns: Dict[str, Any],
|
||||
user_profile: Dict[str, Any]
|
||||
user_profile: Dict[str, Any],
|
||||
language: str = "zh"
|
||||
) -> str:
|
||||
"""构建情绪建议生成的prompt
|
||||
|
||||
@@ -632,6 +670,7 @@ class EmotionAnalyticsService:
|
||||
health_data: 情绪健康数据
|
||||
patterns: 情绪模式分析结果
|
||||
user_profile: 用户画像数据
|
||||
language: 输出语言 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
str: LLM prompt
|
||||
@@ -643,66 +682,114 @@ class EmotionAnalyticsService:
|
||||
prompt = await render_emotion_suggestions_prompt(
|
||||
health_data=health_data,
|
||||
patterns=patterns,
|
||||
user_profile=user_profile
|
||||
user_profile=user_profile,
|
||||
language=language
|
||||
)
|
||||
|
||||
return prompt
|
||||
|
||||
def _get_default_suggestions(self, health_data: Dict[str, Any]) -> EmotionSuggestionsResponse:
|
||||
def _get_default_suggestions(self, health_data: Dict[str, Any], language: str = "zh") -> EmotionSuggestionsResponse:
|
||||
"""获取默认建议(当LLM调用失败时使用)
|
||||
|
||||
Args:
|
||||
health_data: 情绪健康数据
|
||||
language: 输出语言 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
EmotionSuggestionsResponse: 默认建议
|
||||
"""
|
||||
health_score = health_data.get('health_score', 0)
|
||||
|
||||
if health_score >= 80:
|
||||
summary = "您的情绪健康状况优秀,请继续保持积极的生活态度。"
|
||||
elif health_score >= 60:
|
||||
summary = "您的情绪健康状况良好,可以通过一些调整进一步提升。"
|
||||
elif health_score >= 40:
|
||||
summary = "您的情绪健康需要关注,建议采取一些改善措施。"
|
||||
else:
|
||||
summary = "您的情绪健康需要重点关注,建议寻求专业帮助。"
|
||||
if language == "en":
|
||||
if health_score >= 80:
|
||||
summary = "Your emotional health is excellent. Keep up the positive attitude."
|
||||
elif health_score >= 60:
|
||||
summary = "Your emotional health is good. Some adjustments can further improve it."
|
||||
elif health_score >= 40:
|
||||
summary = "Your emotional health needs attention. Consider taking improvement measures."
|
||||
else:
|
||||
summary = "Your emotional health needs serious attention. Consider seeking professional help."
|
||||
|
||||
suggestions = [
|
||||
EmotionSuggestion(
|
||||
type="emotion_balance",
|
||||
title="保持情绪平衡",
|
||||
content="通过正念冥想和深呼吸练习,帮助您更好地管理情绪波动,提升情绪稳定性。",
|
||||
priority="high",
|
||||
actionable_steps=[
|
||||
"每天早晨进行5-10分钟的正念冥想",
|
||||
"感到情绪波动时,进行3次深呼吸",
|
||||
"记录每天的情绪变化,识别触发因素"
|
||||
]
|
||||
),
|
||||
EmotionSuggestion(
|
||||
type="activity_recommendation",
|
||||
title="增加户外活动",
|
||||
content="适度的户外运动可以有效改善情绪,增强身心健康。建议每周进行3-4次户外活动。",
|
||||
priority="medium",
|
||||
actionable_steps=[
|
||||
"每周安排2-3次30分钟的散步",
|
||||
"周末尝试户外运动如骑行或爬山",
|
||||
"在户外活动时关注周围环境,放松心情"
|
||||
]
|
||||
),
|
||||
EmotionSuggestion(
|
||||
type="social_connection",
|
||||
title="加强社交联系",
|
||||
content="与朋友和家人保持良好的社交联系,可以提供情感支持,改善情绪健康。",
|
||||
priority="medium",
|
||||
actionable_steps=[
|
||||
"每周至少与一位朋友或家人深入交流",
|
||||
"参加感兴趣的社交活动或兴趣小组",
|
||||
"主动分享自己的感受和想法"
|
||||
]
|
||||
)
|
||||
]
|
||||
suggestions = [
|
||||
EmotionSuggestion(
|
||||
type="Emotion Balance",
|
||||
title="Maintain Emotional Balance",
|
||||
content="Through mindfulness meditation and deep breathing exercises, help you better manage emotional fluctuations and improve emotional stability.",
|
||||
priority="High",
|
||||
actionable_steps=[
|
||||
"Practice 5-10 minutes of mindfulness meditation every morning",
|
||||
"Take 3 deep breaths when feeling emotional fluctuations",
|
||||
"Record daily emotional changes to identify triggers"
|
||||
]
|
||||
),
|
||||
EmotionSuggestion(
|
||||
type="Activity Recommendation",
|
||||
title="Increase Outdoor Activities",
|
||||
content="Moderate outdoor exercise can effectively improve mood and enhance physical and mental health. Recommend 3-4 outdoor activities per week.",
|
||||
priority="Medium",
|
||||
actionable_steps=[
|
||||
"Schedule 2-3 30-minute walks per week",
|
||||
"Try outdoor sports like cycling or hiking on weekends",
|
||||
"Focus on surroundings and relax during outdoor activities"
|
||||
]
|
||||
),
|
||||
EmotionSuggestion(
|
||||
type="Social Connection",
|
||||
title="Strengthen Social Connections",
|
||||
content="Maintaining good social connections with friends and family can provide emotional support and improve emotional health.",
|
||||
priority="Medium",
|
||||
actionable_steps=[
|
||||
"Have a deep conversation with at least one friend or family member weekly",
|
||||
"Join social activities or interest groups you enjoy",
|
||||
"Actively share your feelings and thoughts"
|
||||
]
|
||||
)
|
||||
]
|
||||
else:
|
||||
if health_score >= 80:
|
||||
summary = "您的情绪健康状况优秀,请继续保持积极的生活态度。"
|
||||
elif health_score >= 60:
|
||||
summary = "您的情绪健康状况良好,可以通过一些调整进一步提升。"
|
||||
elif health_score >= 40:
|
||||
summary = "您的情绪健康需要关注,建议采取一些改善措施。"
|
||||
else:
|
||||
summary = "您的情绪健康需要重点关注,建议寻求专业帮助。"
|
||||
|
||||
suggestions = [
|
||||
EmotionSuggestion(
|
||||
type="情绪平衡",
|
||||
title="保持情绪平衡",
|
||||
content="通过正念冥想和深呼吸练习,帮助您更好地管理情绪波动,提升情绪稳定性。",
|
||||
priority="高",
|
||||
actionable_steps=[
|
||||
"每天早晨进行5-10分钟的正念冥想",
|
||||
"感到情绪波动时,进行3次深呼吸",
|
||||
"记录每天的情绪变化,识别触发因素"
|
||||
]
|
||||
),
|
||||
EmotionSuggestion(
|
||||
type="活动建议",
|
||||
title="增加户外活动",
|
||||
content="适度的户外运动可以有效改善情绪,增强身心健康。建议每周进行3-4次户外活动。",
|
||||
priority="中",
|
||||
actionable_steps=[
|
||||
"每周安排2-3次30分钟的散步",
|
||||
"周末尝试户外运动如骑行或爬山",
|
||||
"在户外活动时关注周围环境,放松心情"
|
||||
]
|
||||
),
|
||||
EmotionSuggestion(
|
||||
type="社交联系",
|
||||
title="加强社交联系",
|
||||
content="与朋友和家人保持良好的社交联系,可以提供情感支持,改善情绪健康。",
|
||||
priority="中",
|
||||
actionable_steps=[
|
||||
"每周至少与一位朋友或家人深入交流",
|
||||
"参加感兴趣的社交活动或兴趣小组",
|
||||
"主动分享自己的感受和想法"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
return EmotionSuggestionsResponse(
|
||||
health_summary=summary,
|
||||
|
||||
@@ -32,7 +32,6 @@ from app.repositories.memory_short_repository import ShortTermMemoryRepository
|
||||
from app.repositories.neo4j.neo4j_connector import Neo4jConnector
|
||||
from app.schemas.memory_agent_schema import Write_UserInput
|
||||
from app.schemas.memory_config_schema import ConfigurationError
|
||||
from app.services.memory_base_service import Translation_English
|
||||
from app.services.memory_config_service import MemoryConfigService
|
||||
from app.services.memory_konwledges_server import (
|
||||
write_rag,
|
||||
@@ -265,7 +264,7 @@ class MemoryAgentService:
|
||||
logger.info("Log streaming completed, cleaning up resources")
|
||||
# LogStreamer uses context manager for file handling, so cleanup is automatic
|
||||
|
||||
async def write_memory(self, end_user_id: str, messages: list[dict], config_id: Optional[uuid.UUID]|int, db: Session, storage_type: str, user_rag_memory_id: str) -> str:
|
||||
async def write_memory(self, end_user_id: str, messages: list[dict], config_id: Optional[uuid.UUID]|int, db: Session, storage_type: str, user_rag_memory_id: str, language: str = "zh") -> str:
|
||||
"""
|
||||
Process write operation with config_id
|
||||
|
||||
@@ -276,6 +275,7 @@ class MemoryAgentService:
|
||||
db: SQLAlchemy database session
|
||||
storage_type: Storage type (neo4j or rag)
|
||||
user_rag_memory_id: User RAG memory ID
|
||||
language: 语言类型 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
Write operation result status
|
||||
@@ -341,7 +341,8 @@ class MemoryAgentService:
|
||||
initial_state = {
|
||||
"messages": langchain_messages,
|
||||
"end_user_id": end_user_id,
|
||||
"memory_config": memory_config
|
||||
"memory_config": memory_config,
|
||||
"language": language
|
||||
}
|
||||
|
||||
# 获取节点更新信息
|
||||
@@ -890,9 +891,7 @@ class MemoryAgentService:
|
||||
async def get_hot_memory_tags_by_user(
|
||||
self,
|
||||
end_user_id: Optional[str] = None,
|
||||
limit: int = 20,
|
||||
model_id: Optional[str] = None,
|
||||
language_type: Optional[str] = "zh"
|
||||
limit: int = 20
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
获取指定用户的热门记忆标签
|
||||
@@ -906,17 +905,15 @@ class MemoryAgentService:
|
||||
{"name": "标签名", "frequency": 频次},
|
||||
...
|
||||
]
|
||||
|
||||
注意:标签语言由写入时的 X-Language-Type 决定,查询时不进行翻译
|
||||
"""
|
||||
try:
|
||||
# by_user=False 表示按 end_user_id 查询(在Neo4j中,end_user_id就是用户维度)
|
||||
tags = await get_hot_memory_tags(end_user_id, limit=limit, by_user=False)
|
||||
payload=[]
|
||||
payload = []
|
||||
for tag, freq in tags:
|
||||
if language_type!="zh":
|
||||
tag=await Translation_English(model_id, tag)
|
||||
payload.append({"name": tag, "frequency": freq})
|
||||
else:
|
||||
payload.append({"name": tag, "frequency": freq})
|
||||
payload.append({"name": tag, "frequency": freq})
|
||||
return payload
|
||||
except Exception as e:
|
||||
logger.error(f"热门记忆标签查询失败: {e}")
|
||||
|
||||
@@ -16,7 +16,6 @@ import json
|
||||
from datetime import datetime
|
||||
|
||||
from app.schemas.memory_episodic_schema import EmotionType
|
||||
from app.services.memory_base_service import Translation_English
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -374,119 +373,6 @@ class MemoryEntityService:
|
||||
logger.warning(f"转换时间格式失败: {e}, 原始值: {dt}")
|
||||
return str(dt) if dt is not None else None
|
||||
|
||||
async def _translate_list(
|
||||
self,
|
||||
data_list: List[Dict[str, Any]],
|
||||
model_id: str,
|
||||
fields: List[str]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
翻译列表中每个字典的指定字段(并发有限度以降低整体延迟)
|
||||
|
||||
Args:
|
||||
data_list: 要翻译的字典列表
|
||||
model_id: 模型ID
|
||||
fields: 需要翻译的字段列表
|
||||
|
||||
Returns:
|
||||
翻译后的字典列表
|
||||
"""
|
||||
# 空列表或无字段时直接返回
|
||||
if not data_list or not fields:
|
||||
return data_list
|
||||
|
||||
import asyncio
|
||||
|
||||
# 并发限制,避免一次性发起过多请求
|
||||
# 可根据实际情况调整(建议 5-10)
|
||||
concurrency_limit = 5
|
||||
semaphore = asyncio.Semaphore(concurrency_limit)
|
||||
|
||||
async def translate_single_field(
|
||||
index: int,
|
||||
field: str,
|
||||
value: Any,
|
||||
) -> Optional[tuple]:
|
||||
"""
|
||||
翻译单个字段并返回 (索引, 字段名, 翻译结果)
|
||||
|
||||
Returns:
|
||||
(index, field, translated_value) 或 None(如果跳过)
|
||||
"""
|
||||
# 跳过空值
|
||||
if value is None or value == "":
|
||||
return None
|
||||
|
||||
# 统一转成字符串再翻译,防止非字符串类型导致错误
|
||||
text = str(value)
|
||||
|
||||
try:
|
||||
async with semaphore:
|
||||
# 调用 Translation_English 进行翻译
|
||||
# 注意:Translation_English 的参数顺序是 (model_id, text)
|
||||
translated = await Translation_English(model_id, text)
|
||||
|
||||
# 如果翻译结果为空,保留原值
|
||||
if translated is None or translated == "":
|
||||
return None
|
||||
|
||||
return index, field, translated
|
||||
except Exception as e:
|
||||
logger.warning(f"翻译字段 {field} (索引 {index}) 失败: {e}")
|
||||
return None
|
||||
|
||||
# 构造所有需要翻译的任务
|
||||
tasks = []
|
||||
for idx, item in enumerate(data_list):
|
||||
# 防御性检查:确保 item 是字典
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
for field in fields:
|
||||
if field not in item:
|
||||
continue
|
||||
|
||||
value = item.get(field)
|
||||
|
||||
# 对于 None 或空字符串的值,直接跳过,不创建任务
|
||||
if value is None or value == "":
|
||||
continue
|
||||
|
||||
tasks.append(
|
||||
asyncio.create_task(
|
||||
translate_single_field(idx, field, value)
|
||||
)
|
||||
)
|
||||
|
||||
# 如果没有需要翻译的任务,直接返回原列表
|
||||
if not tasks:
|
||||
return data_list
|
||||
|
||||
# 使用 gather 并发执行翻译任务(受 semaphore 限制)
|
||||
# return_exceptions=True 可以防止单个任务失败导致整体失败
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
# 创建深拷贝以避免修改原始数据
|
||||
translated_list = [item.copy() if isinstance(item, dict) else item for item in data_list]
|
||||
|
||||
# 将翻译结果回填到列表
|
||||
for result in results:
|
||||
# 跳过 None 结果和异常
|
||||
if result is None or isinstance(result, Exception):
|
||||
if isinstance(result, Exception):
|
||||
logger.warning(f"翻译任务异常: {result}")
|
||||
continue
|
||||
|
||||
idx, field, translated = result
|
||||
|
||||
# 防御性检查索引范围
|
||||
if 0 <= idx < len(translated_list) and isinstance(translated_list[idx], dict):
|
||||
translated_list[idx][field] = translated
|
||||
|
||||
return translated_list
|
||||
|
||||
|
||||
|
||||
|
||||
async def close(self):
|
||||
"""关闭数据库连接"""
|
||||
|
||||
@@ -236,12 +236,13 @@ class DataConfigService: # 数据配置服务类(PostgreSQL)
|
||||
return self._convert_timestamps_to_format(data_list)
|
||||
|
||||
|
||||
async def pilot_run_stream(self, payload: ConfigPilotRun) -> AsyncGenerator[str, None]:
|
||||
async def pilot_run_stream(self, payload: ConfigPilotRun, language: str = "zh") -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
流式执行试运行,产生 SSE 格式的进度事件
|
||||
|
||||
Args:
|
||||
payload: 试运行配置和对话文本
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Yields:
|
||||
SSE 格式的字符串,包含以下事件类型:
|
||||
@@ -315,6 +316,7 @@ class DataConfigService: # 数据配置服务类(PostgreSQL)
|
||||
dialogue_text=dialogue_text,
|
||||
db=self.db,
|
||||
progress_callback=progress_callback,
|
||||
language=language,
|
||||
)
|
||||
logger.info("[PILOT_RUN_STREAM] pipeline_main completed")
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ class OntologyService:
|
||||
scenario: str,
|
||||
domain: Optional[str] = None,
|
||||
scene_id: Optional[Any] = None,
|
||||
workspace_id: Optional[Any] = None
|
||||
workspace_id: Optional[Any] = None,
|
||||
language: str = "zh"
|
||||
) -> OntologyExtractionResponse:
|
||||
"""执行本体提取
|
||||
|
||||
@@ -91,6 +92,7 @@ class OntologyService:
|
||||
domain: 可选的领域提示
|
||||
scene_id: 可选的场景ID,用于权限验证(不再用于自动保存)
|
||||
workspace_id: 可选的工作空间ID,用于权限验证
|
||||
language: 输出语言 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
OntologyExtractionResponse: 提取结果
|
||||
@@ -155,6 +157,7 @@ class OntologyService:
|
||||
llm_max_tokens=self.DEFAULT_LLM_MAX_TOKENS,
|
||||
max_description_length=self.DEFAULT_MAX_DESCRIPTION_LENGTH,
|
||||
timeout=self.DEFAULT_LLM_TIMEOUT,
|
||||
language=language,
|
||||
)
|
||||
|
||||
extraction_duration = time.time() - extraction_start_time
|
||||
|
||||
@@ -36,6 +36,7 @@ async def run_pilot_extraction(
|
||||
dialogue_text: str,
|
||||
db: Session,
|
||||
progress_callback: Optional[Callable[[str, str, Optional[dict]], Awaitable[None]]] = None,
|
||||
language: str = "zh",
|
||||
) -> None:
|
||||
"""
|
||||
执行试运行模式的知识提取流水线。
|
||||
@@ -43,10 +44,12 @@ async def run_pilot_extraction(
|
||||
Args:
|
||||
memory_config: 从数据库加载的内存配置对象
|
||||
dialogue_text: 输入的对话文本
|
||||
db: 数据库会话
|
||||
progress_callback: 可选的进度回调函数
|
||||
- 参数1 (stage): 当前处理阶段标识符
|
||||
- 参数2 (message): 人类可读的进度消息
|
||||
- 参数3 (data): 可选的附加数据字典
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
"""
|
||||
log_file = "logs/time.log"
|
||||
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
||||
@@ -146,6 +149,7 @@ async def run_pilot_extraction(
|
||||
config=config,
|
||||
progress_callback=progress_callback,
|
||||
embedding_id=str(memory_config.embedding_model_id),
|
||||
language=language,
|
||||
)
|
||||
|
||||
log_time("Orchestrator Initialization", time.time() - step_start, log_file)
|
||||
@@ -191,6 +195,7 @@ async def run_pilot_extraction(
|
||||
chunked_dialogs,
|
||||
llm_client=llm_client,
|
||||
embedder_client=embedder_client,
|
||||
language=language,
|
||||
)
|
||||
|
||||
log_time("Memory Summary Generation", time.time() - step_start, log_file)
|
||||
|
||||
@@ -18,7 +18,7 @@ from app.repositories.end_user_repository import EndUserRepository
|
||||
from app.repositories.neo4j.neo4j_connector import Neo4jConnector
|
||||
from app.schemas.memory_episodic_schema import EmotionSubject, EmotionType, type_mapping
|
||||
from app.services.implicit_memory_service import ImplicitMemoryService
|
||||
from app.services.memory_base_service import MemoryBaseService, MemoryTransService, Translation_English
|
||||
from app.services.memory_base_service import MemoryBaseService, MemoryTransService
|
||||
from app.services.memory_config_service import MemoryConfigService
|
||||
from app.services.memory_perceptual_service import MemoryPerceptualService
|
||||
from app.services.memory_short_service import ShortService
|
||||
@@ -455,9 +455,7 @@ class UserMemoryService:
|
||||
async def get_cached_memory_insight(
|
||||
self,
|
||||
db: Session,
|
||||
end_user_id: str,
|
||||
model_id: str,
|
||||
language_type: str
|
||||
end_user_id: str
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
从数据库获取缓存的记忆洞察(四个维度)
|
||||
@@ -465,7 +463,7 @@ class UserMemoryService:
|
||||
Args:
|
||||
db: 数据库会话
|
||||
end_user_id: 终端用户ID (UUID)
|
||||
|
||||
|
||||
Returns:
|
||||
{
|
||||
"memory_insight": str, # 总体概述
|
||||
@@ -519,10 +517,6 @@ class UserMemoryService:
|
||||
memory_insight=end_user.memory_insight
|
||||
behavior_pattern=end_user.behavior_pattern
|
||||
growth_trajectory=end_user.growth_trajectory
|
||||
if language_type!='zh':
|
||||
memory_insight=await Translation_English(model_id,memory_insight)
|
||||
behavior_pattern=await Translation_English(model_id,behavior_pattern)
|
||||
growth_trajectory=await Translation_English(model_id,growth_trajectory)
|
||||
return {
|
||||
"memory_insight":memory_insight, # 总体概述存储在 memory_insight
|
||||
"behavior_pattern":behavior_pattern,
|
||||
@@ -571,6 +565,8 @@ class UserMemoryService:
|
||||
Args:
|
||||
db: 数据库会话
|
||||
end_user_id: 终端用户ID (UUID)
|
||||
model_id: 模型ID(用于翻译)
|
||||
language_type: 语言类型 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
{
|
||||
@@ -604,11 +600,10 @@ class UserMemoryService:
|
||||
personality_traits=end_user.personality_traits
|
||||
core_values=end_user.core_values
|
||||
one_sentence_summary=end_user.one_sentence_summary
|
||||
if language_type!='zh':
|
||||
user_summary=await Translation_English(model_id, user_summary)
|
||||
personality_traits = await Translation_English(model_id, personality_traits)
|
||||
core_values = await Translation_English(model_id, core_values)
|
||||
one_sentence_summary = await Translation_English(model_id, one_sentence_summary)
|
||||
|
||||
# 直接返回数据库中的数据,不进行二次翻译
|
||||
# 语言由生成时的 X-Language-Type 决定
|
||||
|
||||
has_cache = any([
|
||||
user_summary,
|
||||
personality_traits,
|
||||
@@ -658,7 +653,8 @@ class UserMemoryService:
|
||||
self,
|
||||
db: Session,
|
||||
end_user_id: str,
|
||||
workspace_id: Optional[uuid.UUID] = None
|
||||
workspace_id: Optional[uuid.UUID] = None,
|
||||
language: str = "zh"
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
生成并缓存记忆洞察
|
||||
@@ -667,6 +663,7 @@ class UserMemoryService:
|
||||
db: 数据库会话
|
||||
end_user_id: 终端用户ID (UUID)
|
||||
workspace_id: 工作空间ID (可选)
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Returns:
|
||||
{
|
||||
@@ -679,7 +676,7 @@ class UserMemoryService:
|
||||
}
|
||||
"""
|
||||
try:
|
||||
logger.info(f"开始为 end_user_id {end_user_id} 生成记忆洞察")
|
||||
logger.info(f"开始为 end_user_id {end_user_id} 生成记忆洞察, language={language}")
|
||||
|
||||
# 转换为UUID并查询用户
|
||||
user_uuid = uuid.UUID(end_user_id)
|
||||
@@ -700,7 +697,7 @@ class UserMemoryService:
|
||||
# 使用 end_user_id 调用分析函数
|
||||
try:
|
||||
logger.info(f"使用 end_user_id={end_user_id} 生成记忆洞察")
|
||||
result = await analytics_memory_insight_report(end_user_id)
|
||||
result = await analytics_memory_insight_report(end_user_id, language=language)
|
||||
|
||||
memory_insight = result.get("memory_insight", "")
|
||||
behavior_pattern = result.get("behavior_pattern", "")
|
||||
@@ -789,7 +786,8 @@ class UserMemoryService:
|
||||
self,
|
||||
db: Session,
|
||||
end_user_id: str,
|
||||
workspace_id: Optional[uuid.UUID] = None
|
||||
workspace_id: Optional[uuid.UUID] = None,
|
||||
language: str = "zh"
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
生成并缓存用户摘要(四个部分)
|
||||
@@ -798,6 +796,7 @@ class UserMemoryService:
|
||||
db: 数据库会话
|
||||
end_user_id: 终端用户ID (UUID)
|
||||
workspace_id: 工作空间ID (可选)
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Returns:
|
||||
{
|
||||
@@ -810,7 +809,7 @@ class UserMemoryService:
|
||||
}
|
||||
"""
|
||||
try:
|
||||
logger.info(f"开始为 end_user_id {end_user_id} 生成用户摘要")
|
||||
logger.info(f"开始为 end_user_id {end_user_id} 生成用户摘要, language={language}")
|
||||
|
||||
# 转换为UUID并查询用户
|
||||
user_uuid = uuid.UUID(end_user_id)
|
||||
@@ -831,7 +830,7 @@ class UserMemoryService:
|
||||
# 使用 end_user_id 调用分析函数
|
||||
try:
|
||||
logger.info(f"使用 end_user_id={end_user_id} 生成用户摘要")
|
||||
result = await analytics_user_summary(end_user_id)
|
||||
result = await analytics_user_summary(end_user_id, language=language)
|
||||
|
||||
user_summary = result.get("user_summary", "")
|
||||
personality = result.get("personality", "")
|
||||
@@ -915,7 +914,8 @@ class UserMemoryService:
|
||||
async def generate_cache_for_workspace(
|
||||
self,
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID
|
||||
workspace_id: uuid.UUID,
|
||||
language: str = "zh"
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
为整个工作空间生成缓存
|
||||
@@ -923,6 +923,7 @@ class UserMemoryService:
|
||||
Args:
|
||||
db: 数据库会话
|
||||
workspace_id: 工作空间ID
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Returns:
|
||||
{
|
||||
@@ -932,7 +933,7 @@ class UserMemoryService:
|
||||
"errors": List[Dict]
|
||||
}
|
||||
"""
|
||||
logger.info(f"开始为工作空间 {workspace_id} 批量生成缓存")
|
||||
logger.info(f"开始为工作空间 {workspace_id} 批量生成缓存, language={language}")
|
||||
|
||||
total_users = 0
|
||||
successful = 0
|
||||
@@ -953,10 +954,10 @@ class UserMemoryService:
|
||||
|
||||
try:
|
||||
# 生成记忆洞察
|
||||
insight_result = await self.generate_and_cache_insight(db, end_user_id)
|
||||
insight_result = await self.generate_and_cache_insight(db, end_user_id, language=language)
|
||||
|
||||
# 生成用户摘要
|
||||
summary_result = await self.generate_and_cache_summary(db, end_user_id)
|
||||
summary_result = await self.generate_and_cache_summary(db, end_user_id, language=language)
|
||||
|
||||
# 检查是否都成功
|
||||
if insight_result["success"] and summary_result["success"]:
|
||||
@@ -1007,7 +1008,7 @@ class UserMemoryService:
|
||||
|
||||
# 独立的分析函数
|
||||
|
||||
async def analytics_memory_insight_report(end_user_id: Optional[str] = None) -> Dict[str, Any]:
|
||||
async def analytics_memory_insight_report(end_user_id: Optional[str] = None, language: str = "zh") -> Dict[str, Any]:
|
||||
"""
|
||||
生成记忆洞察报告(四个维度)
|
||||
|
||||
@@ -1019,6 +1020,7 @@ async def analytics_memory_insight_report(end_user_id: Optional[str] = None) ->
|
||||
|
||||
Args:
|
||||
end_user_id: 可选的终端用户ID
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Returns:
|
||||
包含四个维度报告的字典: {
|
||||
@@ -1029,8 +1031,12 @@ async def analytics_memory_insight_report(end_user_id: Optional[str] = None) ->
|
||||
}
|
||||
"""
|
||||
from app.core.memory.utils.prompt.prompt_utils import render_memory_insight_prompt
|
||||
from app.core.language_utils import validate_language
|
||||
import re
|
||||
|
||||
# 验证语言参数
|
||||
language = validate_language(language)
|
||||
|
||||
insight = MemoryInsightHelper(end_user_id)
|
||||
|
||||
try:
|
||||
@@ -1070,7 +1076,8 @@ async def analytics_memory_insight_report(end_user_id: Optional[str] = None) ->
|
||||
user_prompt = await render_memory_insight_prompt(
|
||||
domain_distribution=domain_distribution_str,
|
||||
active_periods=active_periods_str,
|
||||
social_connections=social_connections_str
|
||||
social_connections=social_connections_str,
|
||||
language=language
|
||||
)
|
||||
|
||||
messages = [
|
||||
@@ -1097,11 +1104,11 @@ async def analytics_memory_insight_report(end_user_id: Optional[str] = None) ->
|
||||
full_response = str(content) if content is not None else ""
|
||||
|
||||
# 7. 解析四个部分
|
||||
# 使用正则表达式提取四个部分
|
||||
memory_insight_match = re.search(r'【总体概述】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
behavior_match = re.search(r'【行为模式】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
findings_match = re.search(r'【关键发现】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
trajectory_match = re.search(r'【成长轨迹】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
# 使用正则表达式提取四个部分(支持中英文双语标题)
|
||||
memory_insight_match = re.search(r'【(?:总体概述|Overview)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
behavior_match = re.search(r'【(?:行为模式|Behavior Pattern)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
findings_match = re.search(r'【(?:关键发现|Key Findings)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
trajectory_match = re.search(r'【(?:成长轨迹|Growth Trajectory)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
|
||||
memory_insight = memory_insight_match.group(1).strip() if memory_insight_match else ""
|
||||
behavior_pattern = behavior_match.group(1).strip() if behavior_match else ""
|
||||
@@ -1128,7 +1135,7 @@ async def analytics_memory_insight_report(end_user_id: Optional[str] = None) ->
|
||||
await insight.close()
|
||||
|
||||
|
||||
async def analytics_user_summary(end_user_id: Optional[str] = None) -> Dict[str, Any]:
|
||||
async def analytics_user_summary(end_user_id: Optional[str] = None, language: str = "zh") -> Dict[str, Any]:
|
||||
"""
|
||||
生成用户摘要(包含四个部分)
|
||||
|
||||
@@ -1139,6 +1146,7 @@ async def analytics_user_summary(end_user_id: Optional[str] = None) -> Dict[str,
|
||||
|
||||
Args:
|
||||
end_user_id: 可选的终端用户ID
|
||||
language: 语言类型 ("zh" 中文, "en" 英文),默认中文
|
||||
|
||||
Returns:
|
||||
包含四部分摘要的字典: {
|
||||
@@ -1149,8 +1157,12 @@ async def analytics_user_summary(end_user_id: Optional[str] = None) -> Dict[str,
|
||||
}
|
||||
"""
|
||||
from app.core.memory.utils.prompt.prompt_utils import render_user_summary_prompt
|
||||
from app.core.language_utils import validate_language
|
||||
import re
|
||||
|
||||
# 验证语言参数
|
||||
language = validate_language(language)
|
||||
|
||||
# 创建 UserSummaryHelper 实例
|
||||
user_summary_tool = UserSummaryHelper(end_user_id or os.getenv("SELECTED_end_user_id", "group_123"))
|
||||
|
||||
@@ -1165,8 +1177,9 @@ async def analytics_user_summary(end_user_id: Optional[str] = None) -> Dict[str,
|
||||
# 2) 使用 prompt_utils 渲染提示词
|
||||
user_prompt = await render_user_summary_prompt(
|
||||
user_id=user_summary_tool.user_id,
|
||||
entities=", ".join(entity_lines) if entity_lines else "(空)",
|
||||
statements=" | ".join(statement_samples) if statement_samples else "(空)"
|
||||
entities=", ".join(entity_lines) if entity_lines else "(空)" if language == "zh" else "(empty)",
|
||||
statements=" | ".join(statement_samples) if statement_samples else "(空)" if language == "zh" else "(empty)",
|
||||
language=language
|
||||
)
|
||||
|
||||
messages = [
|
||||
@@ -1193,11 +1206,11 @@ async def analytics_user_summary(end_user_id: Optional[str] = None) -> Dict[str,
|
||||
full_response = str(content) if content is not None else ""
|
||||
|
||||
# 5) 解析四个部分
|
||||
# 使用正则表达式提取四个部分
|
||||
user_summary_match = re.search(r'【基本介绍】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
personality_match = re.search(r'【性格特点】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
core_values_match = re.search(r'【核心价值观】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
one_sentence_match = re.search(r'【一句话总结】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
# 使用正则表达式提取四个部分(支持中英文标题)
|
||||
user_summary_match = re.search(r'【(?:基本介绍|Basic Introduction)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
personality_match = re.search(r'【(?:性格特点|Personality Traits)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
core_values_match = re.search(r'【(?:核心价值观|Core Values)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
one_sentence_match = re.search(r'【(?:一句话总结|One-Sentence Summary)】\s*\n(.*?)(?=\n【|$)', full_response, re.DOTALL)
|
||||
|
||||
user_summary = user_summary_match.group(1).strip() if user_summary_match else ""
|
||||
personality = personality_match.group(1).strip() if personality_match else ""
|
||||
|
||||
@@ -481,13 +481,16 @@ def read_message_task(self, end_user_id: str, message: str, history: List[Dict[s
|
||||
|
||||
|
||||
@celery_app.task(name="app.core.memory.agent.write_message", bind=True)
|
||||
def write_message_task(self, end_user_id: str, message: str, config_id: str, storage_type:str, user_rag_memory_id:str) -> Dict[str, Any]:
|
||||
def write_message_task(self, end_user_id: str, message: str, config_id: str, storage_type:str, user_rag_memory_id:str, language: str = "zh") -> Dict[str, Any]:
|
||||
"""Celery task to process a write message via MemoryAgentService.
|
||||
|
||||
Args:
|
||||
end_user_id: Group ID for the memory agent (also used as end_user_id)
|
||||
message: Message to write
|
||||
config_id: Configuration ID as string (will be converted to UUID)
|
||||
storage_type: Storage type (neo4j or rag)
|
||||
user_rag_memory_id: User RAG memory ID
|
||||
language: 语言类型 ("zh" 中文, "en" 英文)
|
||||
|
||||
Returns:
|
||||
Dict containing the result and metadata
|
||||
@@ -498,7 +501,7 @@ def write_message_task(self, end_user_id: str, message: str, config_id: str, sto
|
||||
from app.core.logging_config import get_logger
|
||||
logger = get_logger(__name__)
|
||||
|
||||
logger.info(f"[CELERY WRITE] Starting write task - end_user_id={end_user_id}, config_id={config_id}, storage_type={storage_type}")
|
||||
logger.info(f"[CELERY WRITE] Starting write task - end_user_id={end_user_id}, config_id={config_id}, storage_type={storage_type}, language={language}")
|
||||
start_time = time.time()
|
||||
|
||||
# Convert config_id string to UUID
|
||||
@@ -535,9 +538,9 @@ def write_message_task(self, end_user_id: str, message: str, config_id: str, sto
|
||||
async def _run() -> str:
|
||||
db = next(get_db())
|
||||
try:
|
||||
logger.info(f"[CELERY WRITE] Executing MemoryAgentService.write_memory with config_id={actual_config_id} (type: {type(actual_config_id).__name__})")
|
||||
logger.info(f"[CELERY WRITE] Executing MemoryAgentService.write_memory with config_id={actual_config_id} (type: {type(actual_config_id).__name__}), language={language}")
|
||||
service = MemoryAgentService()
|
||||
result = await service.write_memory(end_user_id, message, actual_config_id, db, storage_type, user_rag_memory_id)
|
||||
result = await service.write_memory(end_user_id, message, actual_config_id, db, storage_type, user_rag_memory_id, language)
|
||||
logger.info(f"[CELERY WRITE] Write completed successfully: {result}")
|
||||
return result
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
|
||||
# Language Configuration
|
||||
# Supported values: "zh" (Chinese), "en" (English)
|
||||
# This controls the language used for memory summary titles and other generated content
|
||||
DEFAULT_LANGUAGE=zh
|
||||
|
||||
# Neo4j Configuration (记忆系统数据库)
|
||||
NEO4J_URI=
|
||||
NEO4J_USERNAME=
|
||||
|
||||
Reference in New Issue
Block a user