
    뉰j                         d Z ddlZddlZddlZddlZddlZddlmZmZ ddlm	Z	 ddl
mZmZmZmZ ddlZ ej                   e      Z G d d      Zdd	 fd
d fdd fdd fdd fdd fdd fdd fdd fdd fdd fdd fd d! fd"d# fd$d% fd&d' fd(d) fd*d+ fd,d- fd.d/ fd0d1 fd2d3 fd4d5 fd6d7 fgZdBd8ed9ed:efd;Zd<ed:efd=Zd>ed<ed:ee   fd?Zd@ ZedAk(  r e        yy)Cu   人物档案管理器 - Profile Manager

功能：
1. 基础档案：姓名、头像、简介、偏好
2. 事实抽取：从对话中提取事实三元组
3. 关系图谱：维护人与人之间的关系
4. 事件时间线：重要事件的时序记录
    N)datetime	timedelta)Path)OptionalListDictAnyc            )          e Zd ZdZdOdefdZd Z	 	 	 	 	 	 	 dPdededed	ed
ededededededededededefdZ		 	 	 	 	 	 	 	 dQdededede
dededededededededededededededede
f(d Z	 	 	 	 	 	 	 dRd!ed"ed#ed$ed%ede
d&edededededededededef d'Z	 	 dSd!ed"ed(e
dee   fd)Z	 	 	 	 	 	 dPd*ed+ed,ed-ed.ede
dededededededededefd/Z	 	 dSd*ed+ed(e
dee   fd0Z	 	 	 	 	 	 dTd1ed2ee   d3ed4ed5ed6edededededededededefd7Zdedee   fd8Z	 	 	 dUdededed(e
d9edee   fd:ZdVded;edee   fd<Z	 	 dWd=ed3ed>e
d(e
dee   f
d?ZdXd@ed(e
dee   fdAZdYd@ed(e
dee   fdBZdZdedCe
defdDZdE Zd[dedCe
defdFZdedee   fdGZ	 dOdHedIedee   fdJZdHedIedee   fdKZ	 	 d\d!ed"edLe
dMedef
dNZ y)]ProfileManageru   
    人物档案管理器
    
    数据结构：
    - profiles: 基础档案（姓名、头像、简介）
    - facts: 事实三元组（主体-谓词-客体）
    - relationships: 关系图谱
    - events: 事件时间线
    Ndb_pathc                 v    |xs) t        t        j                         dz  dz  dz  dz        | _        d | _        y )Nz.hermes	workspacezlivekit-agentszprofiles.db)strr   homer   _conn)selfr   s     profile_manager.py__init__zProfileManager.__init__"   s6    o#diikI&=&KN^&^an&n"o
    c                    t        j                  | j                        | _        | j                  j	                         }|j                  d       |j                  d       |j                  d       |j                  d       |j                  d       |j                  d       |j                  d       |j                  d       |j                  d	       |j                  d
       |j                  d       |j                  d       |j                  d       |j                  d       | j                  j                          t        j                  d| j                          y)u   初始化数据库a@  
            CREATE TABLE IF NOT EXISTS profiles (
                identity TEXT PRIMARY KEY,
                name TEXT,
                avatar_hash TEXT,
                bio TEXT,
                preferences TEXT,
                created_at REAL NOT NULL,
                updated_at REAL NOT NULL
            )
        aC  
            CREATE TABLE IF NOT EXISTS facts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                subject_id TEXT NOT NULL,
                predicate TEXT NOT NULL,
                object TEXT NOT NULL,
                topic TEXT,
                time TEXT,
                source_dialogue_id INTEGER,
                confidence REAL DEFAULT 1.0,
                verified INTEGER DEFAULT 0,
                created_at REAL NOT NULL,
                expires_at REAL,
                FOREIGN KEY (subject_id) REFERENCES profiles(identity)
            )
        a  
            CREATE TABLE IF NOT EXISTS relationships (
                from_identity TEXT NOT NULL,
                to_identity TEXT NOT NULL,
                relation_type TEXT NOT NULL,
                strength REAL DEFAULT 0.5,
                note TEXT,
                source_dialogue_id INTEGER,
                created_at REAL NOT NULL,
                updated_at REAL NOT NULL,
                PRIMARY KEY (from_identity, to_identity)
            )
        a  
            CREATE TABLE IF NOT EXISTS relationship_history (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                from_identity TEXT NOT NULL,
                to_identity TEXT NOT NULL,
                relation_type TEXT,
                strength REAL,
                changed_at REAL NOT NULL,
                source_dialogue_id INTEGER,
                change_reason TEXT
            )
        z`CREATE INDEX IF NOT EXISTS idx_rel_hist_pair ON relationship_history(from_identity, to_identity)zPCREATE INDEX IF NOT EXISTS idx_rel_hist_time ON relationship_history(changed_at)u  
            CREATE TABLE IF NOT EXISTS change_log (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                entity_type TEXT NOT NULL,      -- profile/fact/relationship/event
                entity_id TEXT NOT NULL,
                field_name TEXT,                -- 变更字段

                -- 六要素
                who TEXT,                       -- 谁引起的变更
                what TEXT,                      -- 变更内容摘要
                time_desc TEXT,                 -- 时间描述（绝对+相对）
                location TEXT,                  -- 地点
                reason TEXT,                    -- 原因
                trigger_method TEXT,            -- 如何触发

                old_value TEXT,                 -- JSON
                new_value TEXT,                 -- JSON
                changed_at REAL NOT NULL,

                source_dialogue_id INTEGER,
                scene_context TEXT,             -- 场景描述（在做什么）
                trigger_dialogue TEXT,          -- 触发变更的原始对话
                metadata TEXT                   -- JSON: GPS/设备/参与者等辅助信息
            )
        zRCREATE INDEX IF NOT EXISTS idx_change_entity ON change_log(entity_type, entity_id)zDCREATE INDEX IF NOT EXISTS idx_change_time ON change_log(changed_at)a  
            CREATE TABLE IF NOT EXISTS events (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp REAL NOT NULL,
                participant_ids TEXT,
                event_type TEXT NOT NULL,
                description TEXT,
                scene_hash TEXT,
                importance REAL DEFAULT 0.5,
                metadata TEXT
            )
        zACREATE INDEX IF NOT EXISTS idx_facts_subject ON facts(subject_id)zBCREATE INDEX IF NOT EXISTS idx_facts_predicate ON facts(predicate)zDCREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp)z@CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type)u   📋 人物档案数据库: N)	sqlite3connectr   r   cursorexecutecommitloggerinfo)r   cs     r   
initializezProfileManager.initialize&   s0   __T\\2
JJ 	
		 
 
	 	
		  	$ 	
		  	 	
		  	 	
		tu			de 	
		  	2 	
		fg			XY 	
		  	 	
		UV			VW			XY			TU

24<<.ABr   identitynamebioavatar_hashpreferencesscene_contexttrigger_dialoguewhowhat	time_desclocationreasontrigger_methodmetadatareturnc                    | j                   s| j                          t        j                         }| j                   j                  d|f      j	                         }||||rt        j                  |      ndd}|r|d   |d   |d   |d   d}dD ]q  }||   	t        ||         t        ||         k7  s'| j                  d	||t        j                  ||         t        j                  ||         ||||	|
||||
       s |d   E|d   }t        |d         t        |      k7  r&| j                  d	|d|xs d|d   ||||	|
||||
       t        j                  d|        nw| j                  d	|ddt        j                  ||d      ||||	|
||||
       | j                   j                  d|||||d   ||f       t        j                  d| d| d       | j                   j                          y)uH   注册新人物档案或更新现有档案（自动记录变更历史）zUSELECT identity, name, bio, avatar_hash, preferences FROM profiles WHERE identity = ?N)r!   r"   r#   r$               )r!   r"   r#   profile)entity_type	entity_id
field_name	old_value	new_valuer%   r&   r'   r(   r)   r*   r+   r,   r-   r$   nullu   🔄 更新档案: creater!   r"   z
                INSERT INTO profiles (identity, name, bio, avatar_hash, preferences, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?)
            u   👤 注册新档案:  ()T)r   r   timer   fetchonejsondumpsr   _log_changer   r   r   )r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   nowexistingnew_dataold_datafield	old_prefss                        r   register_profilezProfileManager.register_profile   s@    zzOOiik ::%%cK
 (* 	 ![=H$**["9dT  (HQK&.qk(1+OH 8E?.3x3G3xX]K_3_$$$-e"&**Xe_"="&**Xe_"=&3FVdi(%nx %  8 &2$]3	x./3y>A$$$-m"+"5v"*="9&3FVdi(%nx %  KK-hZ89 %h**d3%?@+>Ndi(nx   JJ   D#{H]4KSRUVX KK0
"TF!DE

r   
subject_id	predicateobjectsource_dialogue_id
confidence
expires_attopic	fact_timesource_type	source_idc                    | j                   s| j                          t        j                         }d}|O| j                   j                  d||f      j	                         }|r!| j                   j                  d||d   f       | j                   j                  d||||t        |      |||||	|
f      }| j                   j                          |j                  }t        j                  d| d| d| d| d		       | j                  d
| d| d|rt        j                  |d         ndt        j                  |      ||||||||||       |S )uy  
        添加事实（追加模式：自动过期同predicate旧事实）

        Args:
            subject_id: 主体身份 ID
            predicate: 谓词（如 "favorite_color", "works_at"）
            object: 客体值
            source_dialogue_id: 来源对话 ID
            confidence: 可信度 0-1
            expires_at: 手动指定过期时间（可选，通常由系统自动设置）
            topic: 主题分类
            fact_time: 时间维度
            source_type: 数据来源类型（dialogue/photo/video/file）
            source_id: 来源唯一ID

        Returns:
            fact_id
        NzZSELECT id, object FROM facts WHERE subject_id = ? AND predicate = ? AND expires_at IS NULLz,UPDATE facts SET expires_at = ? WHERE id = ?r   z
            INSERT INTO facts (subject_id, predicate, object, topic, time, source_dialogue_id,
                             confidence, created_at, expires_at, source_type, source_id)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        u   📝 添加事实:  -> z (ID: r>   fact:rM   r0   r5   r6   r7   r8   r9   rN   r%   r&   r'   r(   r)   r*   r+   r,   r-   )r   r   r?   r   r@   normalize_timer   	lastrowidr   r   rC   rA   rB   )r   rK   rL   rM   rN   rO   rP   rQ   rR   rS   rT   r'   r(   r)   r*   r+   r,   r%   r&   r-   rD   old_factr   fact_ids                           r   add_factzProfileManager.add_fact   sr   6 zzOOiik zz))lY' hj  

""B(1+&
 ## % )VUN94MOa#z;	C	D 	

"")*T)DPVW^V__`ab 	#Ai[119djj!-tjj(1'-$)h.8 	 	
 r   from_idto_idrelation_typestrengthnotechange_reasonc                     | j                   s| j                          t        j                         }| j                   j                  d||f      j	                         }| j                   j                  d||||||||f       | j                   j                  d|||||||f       |r|d   |d   dnd}||d}|rL| j                  d| d	| dt        j                  |      t        j                  |      |||||	|
||||
       n8| j                  d| d	| ddt        j                  |      |||||	|
||||
       | j                   j                          |rd|d    d|d    dnd}t        j                  d| d| d| d| d| 
       y)u  
        更新关系并记录历史

        Args:
            from_id: 关系发起方
            to_id: 关系目标方
            relation_type: 关系类型 (colleague/friend/family/...)
            strength: 关系强度 0-1
            note: 备注
            source_dialogue_id: 来源对话ID
            change_reason: 变更原因（由LLM提取，用于历史记录）
        zYSELECT relation_type, strength FROM relationships WHERE from_identity=? AND to_identity=?a  
            INSERT INTO relationships (from_identity, to_identity, relation_type,
                                      strength, note, source_dialogue_id, created_at, updated_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ON CONFLICT(from_identity, to_identity) DO UPDATE SET
                relation_type = excluded.relation_type,
                strength = excluded.strength,
                note = COALESCE(excluded.note, relationships.note),
                updated_at = excluded.updated_at
        z
            INSERT INTO relationship_history (from_identity, to_identity, relation_type,
                                              strength, changed_at, source_dialogue_id, change_reason)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        r   r0   )ra   rb   Nrelationship   →rY   r=   z, r>    u   🔗 更新关系: z -[rX   ]z-> )r   r   r?   r   r@   rC   rA   rB   r   r   r   )r   r_   r`   ra   rb   rc   rN   rd   r'   r(   r)   r*   r+   r,   r%   r&   r-   rD   old_rowold_infonew_infos                        r   update_relationshipz"ProfileManager.update_relationship+  s   ( zzOOiik **$$ge
 (* 	 	

 	 umXt=O3
	 	

  umXs<NP]^		` MTWQZWQZHY]%2I*$IS0)**X.**X.#5+!1di(nx   *$IS0)**X.#5+!1di(nx   	

7>R
|2gaj\3B)'#m_AhZqQYPZZ]^c]defr   limitc                    | j                   s| j                          d}g }|r|dz  }|j                  |       |r|dz  }|j                  |       |dz  }|j                  |       | j                   j                  ||      j	                         }|D cg c]%  }|d   |d   |d   |d   |d	   |d
   |d   |d   d' c}S c c}w )u   查询关系变更历史z,SELECT * FROM relationship_history WHERE 1=1z AND from_identity = ?z AND to_identity = ?! ORDER BY changed_at DESC LIMIT ?r   r0   r1   r2   r3            )idfrom_identityto_identityra   rb   
changed_atrN   rd   r   r   appendr   fetchall)r   r_   r`   rn   queryparamsrowsrs           r   get_relationship_historyz'ProfileManager.get_relationship_history  s     zzOO>--EMM'"++EMM% 44ezz!!%099; 
 a	 A$1adqTqtA$adqT	

  	 s   *Cr5   r6   r7   r8   r9   c                     t        j                          }| j                  j                  d||||	|
|||||||||||rt        j                  |      ndf       t
        j                  d| d| d|        y)u0   记录变更日志（六要素完整上下文）a  
            INSERT INTO change_log (entity_type, entity_id, field_name,
                                   who, what, time_desc, location, reason, trigger_method,
                                   old_value, new_value, changed_at,
                                   source_dialogue_id, scene_context, trigger_dialogue, metadata)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        Nu   📝 变更记录: rX   .)r?   r   r   rA   rB   r   r   )r   r5   r6   r7   r8   r9   rN   r%   r&   r'   r(   r)   r*   r+   r,   r-   rD   s                    r   rC   zProfileManager._log_change  s     iik

  9j4HfnC -1A&.djj"D	:
	; 	)+a	{!J<PQr   c                    | j                   s| j                          d}g }|r|dz  }|j                  |       |r|dz  }|j                  |       |dz  }|j                  |       | j                   j                  ||      j	                         }|D cg c]j  }i d|d   d|d   d	|d
   d|d   d|d   d|d   d|d   d|d   d|d   d|d   d|d   d|d   d|d   d|d    d!|d"   d#|d$   d%|d&   l c}S c c}w )'u   查询变更历史z"SELECT * FROM change_log WHERE 1=1z AND entity_type = ?z AND entity_id = ?rp   rt   r   r5   r0   r6   r1   r7   r2   r'   r3   r(   rq   r)   rr   r*   rs   r+      r,   	   r8   
   r9      rw      rN      r%      r&      r-      rx   )r   r5   r6   rn   r{   r|   r}   r~   s           r   get_change_historyz!ProfileManager.get_change_history  s    zzOO4++EMM+&))EMM)$44ezz!!%099;  a
!A$
%qt
-8!A$
!A$
 1Q4
  1
 (3AaD
 ;Ead
 MUVWXYVZ
 ]mnopqnr
 2	
 !,QrU	

 !B%

 "6qu
 QrU
 %7"
 @J1R5
  	 s   A/D	timestampparticipant_ids
event_typedescription
scene_hash
importancec                    | j                   s| j                          | j                   j                  d|t        j                  |      |||||rt        j                  |      ndf      }| j                   j                          |j                  }t        j                  d| d|        | j                  dt        |      ddt        j                  |||d      ||||	|
|||       y)u   添加事件到时间线z
            INSERT INTO events (timestamp, participant_ids, event_type,
                               description, scene_hash, importance, metadata)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        Nu   📅 添加事件: z at event)r   r   participants)r5   r6   r7   r8   r9   r%   r&   r'   r(   r)   r*   r+   r,   )r   r   r   rA   rB   r   r[   r   r   rC   r   )r   r   r   r   r   r   r   r-   r'   r(   r)   r*   r+   r,   r%   r&   r   event_ids                     r   	add_eventzProfileManager.add_event  s     zzOO## % O4j:z84::h3GY]_	` 	

##)*T)EF 	(mjj
;1@"B C'-$)h. 	 	
r   c                    | j                   s| j                          | j                   j                  d|f      j                         }|r9|d   |d   |d   |d   |d   rt	        j
                  |d         ni |d   |d   d	S y
)u   获取人物档案z)SELECT * FROM profiles WHERE identity = ?r   r0   r1   r2   r3   rq   rr   )r    r!   r#   r"   r$   
created_at
updated_atN)r   r   r   r@   rA   loads)r   r    rows      r   get_profilezProfileManager.get_profile  s    zzOOjj  7(

(* 	 FA"1v1v58Vtzz#a&1!!f!!f  r   include_expiredc                    | j                   s| j                          d}g }|r|dz  }|j                  |       |r|dz  }|j                  d| d       |r|dz  }|j                  |       |s(|dz  }|j                  t        j                                |dz  }|j                  |       | j                   j	                  ||      j                         }|D 	cg c]%  }	|	d   |	d	   |	d
   |	d   |	d   |	d   |	d   |	d   d' c}	S c c}	w )u(  查询事实列表

        Args:
            subject_id: 过滤主体
            predicate: 模糊匹配谓词
            topic: 主题分类过滤
            limit: 最多返回条数
            include_expired: 是否包含已过期的事实（默认只返回当前有效事实）
        zjSELECT subject_id, predicate, object, topic, time, confidence, created_at, expires_at FROM facts WHERE 1=1z AND subject_id = ?z AND predicate LIKE ?%z AND topic = ?z+ AND (expires_at IS NULL OR expires_at > ?)z! ORDER BY created_at DESC LIMIT ?r   r0   r1   r2   r3   rq   rr   rs   )subjectrL   rM   rQ   r?   rO   r   rP   )r   r   ry   r?   r   rz   )
r   rK   rL   rQ   rn   r   r{   r|   r}   r~   s
             r   	get_factszProfileManager.get_facts  s0    zzOO|**EMM*%,,EMMAi[*+%%EMM%  BBEMM$))+&44ezz!!%099; CGHBFQ aDqtqtA$!AaD t1Q49BFH 	H Hs   *D	directionc                    | j                   s| j                          |dk(  r,| j                   j                  d|f      j                         }n]|dk(  r,| j                   j                  d|f      j                         }n,| j                   j                  d||f      j                         }|D cg c]!  }|d   |d   |d   |d	   |d
   |d   |d   d# c}S c c}w )u   
        查询关系
        
        direction: 'outgoing' (我发出的) | 'incoming' (我收到的) | 'both' (双向)
        outgoingz3SELECT * FROM relationships WHERE from_identity = ?incomingz1SELECT * FROM relationships WHERE to_identity = ?zFSELECT * FROM relationships WHERE from_identity = ? OR to_identity = ?r   r0   r1   r2   r3   rr   rs   )fromtotyperb   rc   r   r   r   r   r   rz   )r   r    r   r}   r~   s        r   get_relationshipsz ProfileManager.get_relationships4  s     zzOO
"::%%E{hj  *$::%%Ch[hj  ::%%X8$ hj   a aD!ad!aD!AaD
  	 s   /&Cparticipant_id
last_hoursc           
         | j                   s| j                          d}g }|0t        j                         |dz  z
  }|dz  }|j                  |       |r|dz  }|j                  d| d       |r|dz  }|j                  |       |dz  }|j                  |       | j                   j	                  ||      j                         }|D 	cg c]0  }	|	d	   |	d
   t        j                  |	d         |	d   |	d   |	d   d2 c}	S c c}	w )u   查询事件时间线zSELECT * FROM events WHERE 1=1i  z AND timestamp > ?z AND participant_ids LIKE ?z%"z"%z AND event_type = ?z  ORDER BY timestamp DESC LIMIT ?r   r0   r1   r2   r3   rr   )rt   r   r   r   r   r   )r   r   r?   ry   r   rz   rA   r   )
r   r   r   r   rn   r{   r|   cutoffr}   r~   s
             r   
get_eventszProfileManager.get_eventsP  s!    zzOO0!YY[J$56F))EMM&!22EMMB~.b12**EMM*%33ezz!!%099;  a A$QqT4::ad;KaD1QqT
  	 s   5Dr{   c                     | j                   s| j                          | j                   j                  dd| dd| dd| d|f      j                         }|D cg c]  }|d   |d   |d   d c}S c c}w )u   模糊搜索人物档案z
            SELECT identity, name, bio FROM profiles
            WHERE name LIKE ? OR bio LIKE ? OR identity LIKE ?
            LIMIT ?
        r   r   r0   r1   )r    r!   r"   r   r   r{   rn   r}   r~   s        r   search_profileszProfileManager.search_profilesn  s    zzOOzz!! # %lawaLAeWA,>	@ AI
	 	 HLLt!QqT1Q4!=tLLLs   A4c                     | j                   s| j                          | j                   j                  dd| dd| d|f      j                         }|D cg c]  }|d   |d   |d   |d   d c}S c c}w )u   搜索事实z
            SELECT subject_id, predicate, object, confidence
            FROM facts
            WHERE object LIKE ? OR predicate LIKE ?
            LIMIT ?
        r   r   r0   r1   r2   )r   rL   rM   rO   r   r   s        r   search_factszProfileManager.search_facts{  s    zzOOzz!! #
 %lawaL%02
 3;(* 	 /34.2 aDqtqt t%.24 	4 4s   A3hoursc                     | j                  |      }| j                  |      }| j                  |      }| j                  ||      }| j	                  |d      }|t        |      t        |      |dd |dd dS )u   获取人物完整画像rK   r   r      r6   rn   Nr   )r4   facts_countrelations_countrecent_eventsrecent_changes)r   r   r   r   r   len)r   r    r   r4   facts	relationseventshistorys           r   get_summaryzProfileManager.get_summary  s    ""8,(3**84	UK ))HB)G u:"9~#CR[%crl
 	
r   c                     | j                   r!| j                   j                          d| _         t        j                  d       y)u   关闭数据库Nu    📋 关闭人物档案数据库)r   closer   r   )r   s    r   r   zProfileManager.close  s-    ::JJDJ68r   c                    | j                   s| j                          | j                  |      }| j                  |      }| j	                  |      }|r| j                  ||      ng }| j                  |d      }g }|D ]  }	|j                  d|	d   |	d        |D ]  }
|j                  d|
d	   |
d        |j                  d
 d       |||||dd t        |      dS )uE   获取人物最近时间窗口内的完整上下文（聚合查询）r   r   2   r   r   r   )r   r?   contentchangerw   c                     | d   S )Nr?    xs    r   <lambda>z,ProfileManager.get_context.<locals>.<lambda>  s    AfIr   T)keyreverseNr   )r    r4   current_factscurrent_relationstimelinetotal_changes)
r   r   r   r   r   r   r   ry   sortr   )r   r    r   r4   r   r   r   r   r   ehs              r   get_contextzProfileManager.get_context  s   zzOO ""8,(3**84	 PUUKZ\ ))HB)G AOO+   AOO ,   	-t< !"!* " \
 	
r   c                    | j                   s| j                          | j                  |d      }i }|D ]$  }|d   }||vrg ||<   ||   j                  |       & g }|j	                         D ]  \  }}t               }|D ]  }|d   s	|j                  |d           t        |      dkD  s@|j                  |t        |      t        |      t        |d       |D cg c]
  }|d   s	| c}d	        |S c c}w )
u?   检测矛盾事实（同一 predicate 有多个不同 object）T)rK   r   rL   rM   r0   c                     | d   S )Nr   r   r   s    r   r   z1ProfileManager.detect_conflicts.<locals>.<lambda>  s    <r   )r   rP   )rL   conflicting_valuescountmost_recentexpired)
r   r   r   ry   itemssetaddr   listmax)	r   r    	all_factsgroupedfpred	conflicts
facts_listobjectss	            r   detect_conflictszProfileManager.detect_conflicts  s   zzOO NNhNM	 A[>D7" "DM  #	  	 'D*eGX;KK(,   7|a  !%*.w- _#&z7P#Q+5I:a<:I"  !0    Js   
C:)C:dialogue_text
speaker_idc                    	 ddl }ddl}ddlm}  |d       |j	                  d      }|j	                  dd      }|j	                  dd	      }	|r|d
k(  r't
        j                  d       | j                  ||      S |j                  ||      }
d| d| d}|
j                  j                  j                  |	d|dgd      }|j                  d   j                  j                  xs dj                         }|j!                  d      r;t#        j$                  |      }t
        j'                  dt)        |       d|        |S ddl} |j,                  d||j.                        }|rIt#        j$                  |j1                               }t
        j'                  dt)        |       d|        |S 	 | j                  ||      S # t2        $ r#}t
        j                  d| d       Y d}~9d}~ww xY w)uK  从对话文本中提取事实（对接 Hermes LLM API）

        Args:
            dialogue_text: 对话文本
            speaker_id: 说话人 ID
            llm_client: LLM 客户端（可选）

        Returns:
            提取的事实列表 [{'predicate': str, 'object': str, 'topic': str, 'confidence': float}]
        r   N)load_dotenvz4/Users/leo/.hermes/workspace/livekit-agents/.env.devOPENAI_API_KEYOPENAI_BASE_URLzhttps://api.agnes-ai.cn/v1	LLM_MODELzagnes-2.5-flashz
[REDACTED]z*LLM API Key not configured, using fallback)api_keybase_urlu@   你是一个信息抽取助手。从以下对话中提取关于"u   "的事实信息。
只返回 JSON 格式的数组，不要其他内容。

示例输出格式：
[{"predicate": "favorite_color", "object": "蓝色", "topic": "hobby", "confidence": 0.9}]

对话内容：u9   

只输出 JSON 数组，如果没有事实则返回 []
userroler   皙?)modelmessagestemperaturerh   [u   ✅ LLM 提取 u    条事实: z	\\[.*?\\]u   LLM 提取失败: u   ，使用 fallback 方法)osopenaidotenvr   getenvr   warning_extract_facts_fallbackOpenAIchatcompletionsr;   choicesmessager   strip
startswithrA   r   r   r   researchDOTALLgroup	Exception)r   r   r   
llm_clientr   r   r   r   r   r   clientpromptresponseresult_textr   r  
json_matchr   s                     r   extract_facts_from_dialoguez*ProfileManager.extract_facts_from_dialogue  s   3	N*NOii 01Gyy!24PQHIIk+<=Eg5KL33M:NN]]7X]FFYZdYe f  	F {{..55#)f=> 6 H $++A.66>>D"KKMK %%c*

;/oc%j\eWMN &RYY|["))L
 JJz'7'7'9:EKK/#e*\% QR L  ++M:FF	  	NNN/s2KLMM	Ns&   A8G ;CG A,G 	G0G++G0c           	          g }ddd|v rdndfddg}|D ]M  \  }}t        j                  ||      }|D ]-  }|j                  |t        |t              r|n|d   d	d
d       / O |S )u7   基于简单规则的提取（LLM 不可用时使用）)u   我叫(\w+)r!   u   我喜欢(\w+)u   颜色favorite_colorinterest)u   我在(.+?)(工作|公司)works_at)u*   我是(.+?)(程序员|工程师|设计师)
occupationr   other      ?)rL   rM   rQ   rO   )r  findallry   
isinstancer   )	r   r   r   r   patternspatternrL   matchesmatchs	            r   r  z&ProfileManager._extract_facts_fallback6  s     %H4M 0S]^7I	
 #+GYjj-8G !*'1%'=e58$"%	  ! #+ r   interaction_countinteraction_depthc                    | j                   s| j                          |d}| j                   j                  |d| dd| df      j                         }|r|d   nd}d}| j                   j                  |d| d| dd| df      j                         }||r|d   ndz  }ddl}dd|j                  |      z  |z  z   }	t        d	t        d|	            }
t        |
d
      S )uI  基于交互频率推断关系强度

        Args:
            from_id: 发起方 ID
            to_id: 目标方 ID
            interaction_count: 交互次数（可选，默认查数据库统计）
            interaction_depth: 交互深度权重（0-1）

        Returns:
            推断的关系强度（0-1）
        Nz
                SELECT COUNT(*) FROM events
                WHERE participant_ids LIKE ? AND participant_ids LIKE ?
            r   r   z
                SELECT COUNT(*) FROM change_log
                WHERE (entity_type = 'relationship' AND entity_id LIKE ?)
                   OR (entity_type = 'event' AND metadata LIKE ?)
            rg   333333?r   gffffff?r1   )	r   r   r   r@   mathlog1pminr   round)r   r_   r`   r%  r&  count_queryresultchange_queryr)  base_strengthinferred_strengths              r   infer_relationship_strengthz*ProfileManager.infer_relationship_strengthN  s    zzOO $K ZZ''+,WIQ1UG1(FHHP
 -3q	L
 ZZ''+,WISq)AQwiq>(RTT\T\T^ f!; 	cDJJ/@$AADUUUc#}&=>&**r   N)NNNNNNNNNNNNN)N      ?NNNNNNNNNNNNNN)r  NNNNNNNNNNNN)NNr   )NNr  NNNNNNNNN)NNNd   F)both)NNNr   )r   )r   )i  )   )Nr4  )!__name__
__module____qualname____doc__r   r   r   r   boolrJ   intfloatr^   rm   r   r   rC   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r2  r   r   r   r   r      s1    uCn ;?=A-1LPMQY]*.E EC E!E7:E&*E ),E GJE "	E 14	E HK	E
 $'E
 8;E
 TWE $(E 48EP FI8<;?"&EIQUDH"&E3 E3 E E%(E=BE"E25E  E 69E  	E
 E
 ),E
 @CE E 03E LOE !$E ?BE  E ,/EP ?C5904OS[_NR,0Vg3 Vgs Vg3 Vg%*Vg8;Vg/2Vg +.Vg !$	Vg 36	Vg JM	Vg
 &)Vg
 :=Vg
 VYVg +.Vg ILVg &*Vgp JN/1 3 ),6:4j6 OS;?HL<@LPTXRs Rs R R!R58R'*RBER &)R 7:R 	R 14	R GJ	R
 R
 8;R
 NRR* LP(*c S "%/3Dz< 7;>A#'FJRVEI"
5 "
49 "
!"
03"
!"
6;"
 !"
 	"
 *-	"
 AD	"

  "

 14"

 MP"
 "%"
 @C"
LC HTN * BF25*/'HC 'H3 'H'H,/'H#''H48J'HR# # 4PT: 8 HL8:  "25?CDz<MS M Md4j M4# 4c 44: 4
C 
 
d 
$9)
C )
 )
T )
V! !d !H 15CG CG# CG9=dCGJS c dSWj 2 ?C@C++3 ++s ++8;++8=++HM++r   r   u   (\d+)月(\d+)号?c           	          |j                    dt        | j                  d            ddt        | j                  d            dS N-r0   02dr1   yearr=  r  mrD   s     r   r   r     s=    SXXJaAGGAJ7LAcRSRYRYZ[R\o^aMb*cr   u   (\d+)月(\d+)日c           	          |j                    dt        | j                  d            ddt        | j                  d            dS r@  rC  rE  s     r   r   r     s=    CHH:Qs1771:s6K1SQRQXQXYZQ[_]`La)br   u   今年(\d+)月c                 V    |j                    dt        | j                  d            dS )NrA  r0   rB  rC  rE  s     r   r   r     s"    #((1S_S4I'Jr   u   (明年|去年)(\d+)月c                     |j                   | j                  d      dk(  rdndz    dt        | j                  d            dS )Nr0      明年rA  r1   rB  )rD  r  r=  rE  s     r   r   r     sD    CHHQWWQZS[E[ac4d2ffghklmlslstulvhwx{g|0}r   u	   上个月c                 ^    |j                  d      t        d      z
  j                  d      S )Nr0   daydays%Y-%mreplacer   strftimerE  s     r   r   r     s%    3;;1;#5	q8I#I"S"ST["\r   u	   下个月c                 ^    |j                  d      t        d      z   j                  d      S )N   rM  r3   rO  rQ  rR  rE  s     r   r   r     s%    3;;2;#69J#J"T"TU\"]r   u	   这个月c                 $    |j                  d      S NrQ  rT  rE  s     r   r   r     s    #,,w"7r   u   本月c                 $    |j                  d      S rX  rY  rE  s     r   r   r     s    s||G4r   u   (\d+)个月后c                 t    |t        t        | j                  d            dz        z   j                  d      S Nr0      rO  rQ  r   r=  r  rT  rE  s     r   r   r     +    iS_r=Q.R(R'\'\]d'er   u   (\d+)个月前c                 t    |t        t        | j                  d            dz        z
  j                  d      S r\  r^  rE  s     r   r   r     r_  r   u   上周c                 `    |t        |j                         dz         z
  j                  d      S Nrs   rO  %Y-%m-%dr   weekdayrT  rE  s     r   r   r     s%    iS[[]Q5F&G GQQR\]r   u   这周c                 $    |j                  d      S )Nrc  rY  rE  s     r   r   r     s    s||J7r   u   下周c                 `    |t        d|j                         z
        z   j                  d      S rb  rd  rE  s     r   r   r     s%    iQ5F&G GQQR\]r   u   (\d+)周前c                 n    |t        t        | j                  d                  z
  j                  d      S Nr0   )weeksrc  r^  rE  s     r   r   r     &    S93qwwqz?+K%K$U$UV`$ar   u   (\d+)周后c                 n    |t        t        | j                  d                  z   j                  d      S ri  r^  rE  s     r   r   r     rk  r   u   (\d+)天前c                 n    |t        t        | j                  d                  z
  j                  d      S Nr0   rO  rc  r^  rE  s     r   r   r     &    S9#aggaj/+J%J$T$TU_$`r   u   (\d+)天后c                 n    |t        t        | j                  d                  z   j                  d      S rn  r^  rE  s     r   r   r     ro  r   u5   上(周一|周二|周三|周四|周五|周六|周日)c           	          |t        |j                         g dj                  | j                  d            z
  dz   dz        z
  j	                  d      S )Nu   一u   二u   三u   四u   五u   六u   日r0   rs   rO  rc  r   re  indexr  rT  rE  s     r   r   r     s    sU^ehepeper  va  vg  vg  hi  ho  ho  pq  hr  vs  fs  vw  fw  {|  e|  V}  P}  OG  OG  HR  OSr   u5   这(周一|周二|周三|周四|周五|周六|周日)c                 `    |t        |j                         dz        z
  j                  d      S rb  rd  rE  s     r   r   r     s;    sU^dgdododqtuduUvOv  OA  OA  BL  OMr   u5   下(周一|周二|周三|周四|周五|周六|周日)c           	          |t        d|j                         z
  g dj                  | j                  d            z   dz        z   j	                  d      S )Nrs   rr  r0   rO  rc  rs  rE  s     r   r   r     s    sU^efilititivev  ze  zk  zk  lm  ls  ls  tu  lv  zw  fw  {|  e|  V}  P}  OG  OG  HR  OSr   u   去年c                 2    t        |j                  dz
        S Nr0   r   rD  rE  s     r   r   r         s388a<0r   u   今年c                 ,    t        |j                        S r3  ry  rE  s     r   r   r     s    s388}r   rJ  c                 2    t        |j                  dz         S rx  ry  rE  s     r   r   r     rz  r   u   (\d+)年前c                 b    t        |j                  t        | j                  d            z
        S rx  )r   rD  r=  r  rE  s     r   r   r     s    C3qwwqz?(B$Cr   time_strrD   r.   c                 *   | r| dv ry|xs t        j                         }| }dD ]  }	 t        j                  | |       | c S  t        D ])  \  }}t        j                  ||       }|s	  |||      c S  |S # t        $ r Y ^w xY w# t        $ r Y Hw xY w)u;   将相对时间转换为绝对日期 YYYY-MM-DD 或 YYYY-MM)r:   Nonerh   N)rc  rQ  z%Y)r   rD   strptime
ValueError_TIME_PATTERNSr  r  r  )r~  rD   originalfmtr"  funcr$  s          r   rZ   rZ     s    x#77

CH +	h,O + (		'8,E3''	 ( O  		  s#   A7)B7	BB	BBtextc                     d|  dddddddS )u0   构建事实提取提示词（含主题分类）uz   分析以下对话，提取用户的关键信息作为事实三元组，并为每条事实分类主题。

对话内容：u  

请提取以下类型的事实（如果存在）：
- works_at: 工作公司
- education: 教育背景
- favorite_color/hobby/food: 喜好
- location: 所在地
- family: 家庭关系
- role: 职位角色
- skill: 技能专长
- project: 正在进行的项目
- purchase: 购买/拥有物品
- reminder: 需要提醒的重要日期
- travel: 旅行经历、目的地、去过哪里

每条事实必须包含 topic 字段，从以下主题中选择：
profile（基础档案）、work（工作）、study（学习）、hobby（爱好）、
family（家庭）、location（地点）、travel（旅行）、event（事件）、
reminder（提醒）、skill（技能）、project（项目）、purchase（消费）、other（其他）

返回 JSON 格式（如无事实返回空数组 []）：
[rL   uQ    "works_at", "object": "谷歌", "topic": "work", "time": null, "confidence": 0.9z,
 ue    "wedding_date", "object": "下个月", "topic": "reminder", "time": "2024-10-15", "confidence": 0.95u#   ]

只输出 JSON，不要解释。r   )r  s    r   extract_fact_promptr    s;    v * _` ast u!1$ $r   r   c                   K   	 t        |      }| j                  d|dg       d{   }ddl} |j                  d||j                        }|rt        j                  |j                               }g }	|D ]  }
t        |
t              sd|
v sd|
v s|j                  ||
d   |
d   |
j                  dd	      |
j                  d
d             |	j                  |
       t        j                  d| d|
j                  dd	       d|
d    d|
d            |	S 	 g S 7 # t        $ r#}t        j!                  d|        Y d}~g S d}~ww xY ww)u  
    从对话文本中自动提取事实并存储（含主题分类）
    
    Args:
        llm_client: LLM 客户端
        profile_manager: ProfileManager 实例
        speaker_id: 说话人 ID
        text: 对话文本
        
    Returns:
        提取的事实列表
    r   r   Nr   z\[.*\]rL   rM   rQ   r  rO   皙?)rK   rL   rM   rQ   rO   u   📝 自动提取事实: z [z] -> rV   u   ⚠️ 事实提取失败: )r  r  r  r  r  rA   r   r  r   dictr^   getry   r   r   r  r  )r  profile_managerr   r  r  r  r  r  r   storedrW   r   s               r   _extract_and_store_factsr    s    :$T* $/*
  
 	RYYy(BII>
JJz//12E FdD)kT.AhRVFV#,,#-"&{"3#H~"hhw8#'88L##> -  MM$'KK";J<r$((SZ\cJdIeejkop{k|j}  ~B  CG  HP  CQ  BR  !S  T  M# * I92  :4QC899I:sX   E#D" D A"D" D" D" BD" EE D" "	E+E	E	EEc                     ddl } ddl}| j                  d      }t        |      }|j	                          t        d       |j                  ddd	       |j                  d
dd	       |j                  ddd	       t        d       |j                  dddd       |j                  dddd       |j                  dddd       |j                  d
ddd       |j                  d
ddd       t        d       |j                  dd
ddd !       |j                  dddd"d#!       |j                  d
dd$d%d&!       t        d'       |j                  t        j                         dd
gd(d)d*+       |j                  t        j                         dgd,d-d.+       t        d/       |j                  d      }t        d0       t        d1|d2   d3           t        d4|d2   d5           t        d6|d7           t        d8|d9           |j                  d:      }t        d;       |D ]  }t        d<|d=    d>|d?            |j                  d      }t        d@       |D ]0  }|dA   dk(  rdBndC}	t        dD|	 dE|dF    d>|dG    dH|dI   dJdK	       2 t        dL       |j                  dM      }
t        dN|
D cg c]  }|d3   	 c}        |j                          |j!                  |       t        dO       yc c}w )Pu   演示用法r   Nz.db)suffixu   === 1. 注册人物档案 ===zhangsanu   张三u$   产品经理，喜欢运动和科技r<   lisiu   李四u!   工程师，热爱编程和开源wangwuu   王五u   设计师，关注用户体验u   
=== 2. 添加事实 ===r  u   某科技公司r0   )rN   r  u   蓝色hatesu   早起开会r1   	expertiseu   后端开发r2   u   
=== 3. 建立关系 ===	colleaguer  u   项目组同事)rb   rc   g333333?u   产品&设计协作friendg?u	   好朋友u   
=== 4. 添加事件 ===meetingu   项目评审会议gffffff?)r   r   socialu   午餐聊天r(  u   
=== 5. 查询 ===u   张三档案:u     - 名字: r4   r!   u     - 简介: r"   u     - 事实数: r   u     - 关系数: r   r   u   
张三的事实:z  - rL   z: rM   u   
张三的关系:r   rg   u   ←z   r   r   u
    (强度: rb   z.1fr>   u   
=== 6. 搜索 ===u   科技u   搜索'科技': u   
✅ 演示完成)tempfiler   mktempr   r   printrJ   r^   rm   r   r?   r   r   r   r   r   unlink)r  r   r   pmsummaryr   r   r   r~   r   resultss              r   demor  	  s*   ooUo+G		 BMMO 

)*
7]^X3VWx5UV 

%&KK
J(9aKPKK
,h1KMKK
G^KJKK
$5!KLKK^KJ 

%&:v{SO`a:xsQfg68X+V 

%&LLz62I1c  CLLzlH+  = 

  nnZ(G	M	L+F34
56	L+E23
45	OGM23
45	OG$567
89 LLJL/E	 Q{^$Bq{m45  $$Z0I	 vY*4E%	9+Qqwir!F)Jq}S>QQRST 
 

   *G	9Aai9:
;<HHJIIg	
 :s   ?L
__main__r3  )r;  rA   r?   r   hashlibr  r   r   pathlibr   typingr   r   r   r	   logging	getLoggerr8  r   r   r  r   rZ   r  r  r  r   r   r   <module>r     s       	 (  , , 			8	$b+ b+R cdbcJK!}~\]]^7845efef]^78]^abab`a`a=  @S  T=  @M  N=  @S  T01,-01CD7<S x 3 8$c $c $>-C -WZ -_cdh_i -dE P zF r   