93 lines
3.3 KiB
Python
93 lines
3.3 KiB
Python
"""会话目标自动持久化服务。
|
|
|
|
从用户消息自动持久化会话目标到 goals 表。
|
|
如果已有该 character 的活跃 session 级目标,判断内容是否显著不同:
|
|
- 内容相似 → 复用
|
|
- 内容不同 → 完成旧目标,创建新目标
|
|
"""
|
|
|
|
from app.models.governance import Goal
|
|
from app.repositories import governance as governance_repo
|
|
|
|
_TOPIC_SHIFT_MIN_LENGTH = 20
|
|
|
|
|
|
async def auto_persist_session_goal(
|
|
session,
|
|
character_id: str,
|
|
content: str,
|
|
) -> Goal | None:
|
|
"""自动持久化会话目标:复用活跃的 session 目标或新建。
|
|
|
|
内容过短(<10 字)不产生目标。
|
|
如果已有活跃 session 目标但内容显著不同,先完成旧目标再新建。
|
|
|
|
Returns:
|
|
持久化后的 Goal(可能为 None)。
|
|
"""
|
|
goal_text = (content or "").strip()
|
|
if not goal_text or len(goal_text) < 10:
|
|
return None
|
|
|
|
existing = await governance_repo.list_active_session_goal(session, character_id)
|
|
if existing is not None:
|
|
# 判断是否话题转换:内容不再相似时完成旧目标
|
|
if _is_topic_shift(existing.title, goal_text):
|
|
existing.status = "completed"
|
|
existing.progress = 1.0
|
|
# 创建新目标
|
|
goal = Goal(
|
|
character_id=character_id,
|
|
title=goal_text[:120],
|
|
description=f"从对话自动提取的会话目标:{goal_text[:500]}",
|
|
level="session",
|
|
type="mission",
|
|
status="active",
|
|
priority=50,
|
|
)
|
|
await governance_repo.add_goal(session, goal)
|
|
return goal
|
|
return existing
|
|
|
|
# 新建 session 目标
|
|
goal = Goal(
|
|
character_id=character_id,
|
|
title=goal_text[:120],
|
|
description=f"从对话自动提取的会话目标:{goal_text[:500]}",
|
|
level="session",
|
|
type="mission",
|
|
status="active",
|
|
priority=50,
|
|
)
|
|
await governance_repo.add_goal(session, goal)
|
|
return goal
|
|
|
|
|
|
def _is_topic_shift(existing_title: str, new_content: str) -> bool:
|
|
"""判断新内容与旧目标标题是否话题转换。
|
|
|
|
简单规则:如果新内容较长且与旧标题共享的关键词很少,视为话题转换。
|
|
这是启发式方法,后续可升级为 LLM 判断。
|
|
"""
|
|
if len(new_content) < _TOPIC_SHIFT_MIN_LENGTH:
|
|
return False
|
|
# 提取关键词集合(按空格/标点分割)
|
|
import re
|
|
old_words = set(re.split(r"[\s,。!?、;:,\.!?;:]+", existing_title.lower()))
|
|
new_words = set(re.split(r"[\s,。!?、;:,\.!?;:]+", new_content.lower()))
|
|
# 排除常见的助词
|
|
stop_words = {"的", "了", "是", "在", "有", "和", "就", "不", "人", "都", "一",
|
|
"一个", "上", "也", "很", "到", "说", "要", "去", "你", "我", "他",
|
|
"这", "那", "吗", "啊", "呢", "吧", "地", "得"}
|
|
old_keywords = old_words - stop_words
|
|
new_keywords = new_words - stop_words
|
|
|
|
if not old_keywords or not new_keywords:
|
|
return False
|
|
|
|
overlap = old_keywords & new_keywords
|
|
# 如果旧关键词的一个都没有出现在新内容中,或者重合率极低
|
|
if not overlap:
|
|
return True
|
|
return len(overlap) / max(len(old_keywords), len(new_keywords)) < 0.15
|