
    
j4              
          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ZddlmZ ddlm	Z	m
Z
 ddlZddlmZ ej                  j                  d      ZdZdZd	Zd
ZdZdZd
ZdZd	Zd
ZdZdZd
ZdZdede	e    fdZ!d'dede	e   de"de	e#   fdZ$d Z%d Z&dedefdZ'dedefdZ(dede	e    fdZ)d(dede"de*de	e#   fdZ+d)de"de	e#   fd Z,d!ede*fd"Z-de	e#   fd#Z.de	e#   de"fd$Z/d% Z0e1d&k(  r e0        yy)*u  
本地向量记忆系统
- Embedding: netease (bce-embedding-base_v1)
- 向量数据库: Chroma (本地)
- Reranker: bge-reranker-v2-m3

用法:
  python local_memory.py add "记忆内容"
  python local_memory.py search "查询内容" [--top_k 5] [--rerank]
  python local_memory.py list [--limit 20]
  python local_memory.py delete <memory_id>
  python local_memory.py export > memories.json
  python local_memory.py import < memories.json
    N)datetime)ListOptional)Settingsz~/.hermes/local_memory_dbmemories_4096memoriesz(https://api.siliconflow.cn/v1/embeddingsz3sk-llicoufjowafeksyzknhxebhxfyaawjrmiiefpswhofheirvzQwen/Qwen3-VL-Embedding-8Bz$https://api.siliconflow.cn/v1/rerankzQwen/Qwen3-VL-Reranker-8Bz$netease-youdao/bce-embedding-base_v1z#netease-youdao/bce-reranker-base_v1textreturnc                    	 dt          dd}t        | d}t        j                  t        ||d      }|j                          |j                         }|d   d   d	   S # t        $ r}t        d
| d       Y d}~nd}~ww xY wdt         dd}t        | d}t        j                  t        ||d      }|j                          |j                         }|d   d   d	   S )uL   获取文本的 embedding 向量（主：硅基流动，fallback：西农）Bearer application/jsonAuthorizationzContent-Typemodelinput   headersjsontimeoutdatar   	embeddingu    [Embedding] 硅基流动失败:    ，尝试西农...N)EMBEDDING_API_KEYEMBEDDING_MODELrequestspostEMBEDDING_API_URLraise_for_statusr   	ExceptionprintFALLBACK_EMBEDDING_API_KEYFALLBACK_EMBEDDING_MODELFALLBACK_EMBEDDING_API_URL)r	   r   r   respresultes         ,/home/ubuntu/.hermes/scripts/local_memory.pyget_embeddingr*   5   s   H&'8&9:.

 %
 }}.dTVWf~a -- H03EFGGH
 ##=">?*G
 *D ==3W4Y[\DYY[F&>![))s   AA 	B(A<<Bquery	documentstop_kc           	      :   |sg S 	 dt          dd}t        | |t        |t        |            d}t	        j
                  t        ||d      }|j                          |j                         }|j                  dg       S # t        $ r}t        d| d	       Y d
}~nd
}~ww xY wdt         dd}t        | |t        |t        |            d}t	        j
                  t        ||d      }|j                          |j                         }|j                  dg       S )uE   使用 Reranker 重排序（主：硅基流动，fallback：西农）r   r   r   )r   r+   r,   top_nr   r   resultsu   [Reranker] 硅基流动失败: r   N)RERANKER_API_KEYRERANKER_MODELminlenr   r   RERANKER_API_URLr    r   getr!   r"   FALLBACK_RERANKER_API_KEYFALLBACK_RERANKER_MODELFALLBACK_RERANKER_API_URL)r+   r,   r-   r   r   r&   r'   r(   s           r)   rerankr:   W   s&   	G&'7&89.

 $"I/	
 }}-wTSUVzz)R(( G/s2DEFFG
 ##<"=>*G
 )UC	N+	D ==2G$XZ[DYY[F::i$$s   A8A? ?	B!BB!c                  l    t        j                  t        d       t        j                  t              S )u   获取 Chroma 客户端T)exist_ok)path)osmakedirsCHROMA_PATHchromadbPersistentClient     r)   
get_clientrE      s!    KKd+$$+66rD   c                 4    | j                  t        ddi      S )u   获取或创建 collectiondescriptionu   本地向量记忆)namemetadata)get_or_create_collectionCOLLECTION_NAME)clients    r)   get_collectionrM      s&    **!56 +  rD   contentc                     t        j                         j                         }t        j                  |  | j                               j                         dd S )u   生成记忆IDN   )r   now	isoformathashlibmd5encode	hexdigest)rN   	timestamps     r)   generate_idrX      sG    ((*I;;'9+.5578BBDSbIIrD   c                     t               }t        |      }t        |       }t        |       }|j	                  |g|g| gdt        j                         j                         ig       |S )   添加记忆
created_atids
embeddingsr,   	metadatas)rE   rM   rX   r*   addr   rQ   rR   )rN   rL   
collection	memory_idr   s        r)   
add_memoryrc      si    \F'JG$Ig&INNK;) (,,.":":"<=>	   rD   c                     dt          dd}t        | d}t        j                  t        ||d      }|j                          |j                         }|d   d   d	   S )
u8   获取 768 维 embedding（用于搜索旧 collection）r   r   r   r   r   r   r   r   r   )r#   r$   r   r   r%   r    r   )r	   r   r   r&   r'   s        r)   get_fallback_embeddingre      sl     ##=">?*G
 *D ==3W4Y[\DYY[F&>![))rD   
use_rerankc           	      $   t               }g }	 |j                  t              }|j                         dkD  rt	        |       }|j                  |gt        |r|dz  n||j                                     }|d   d   r\t        |d   d         D ]H  \  }|j                  |d   d   |   |d   rd|d   d   |   z
  nd|d   r|d   d   |   ni d	d
       J 	 |j                  t              }
|
j                         dkD  rt        |       }|
j                  |gt        |r|dz  n||
j                                     }|d   d   rqt        |d   d         D ]]  \  }t        fd|D              r|j                  |d   d   |   |d   rd|d   d   |   z
  nd|d   r|d   d   |   ni dd
       _ |sg S |rlt        |      dkD  r^|D cg c]  }|d   	 }}t        | ||      }g }|D ]3  }|d   }||   j!                         }|d   |d<   |j                  |       5 |d| S |j#                  d d       |d| S # t        $ r}	t        d|	        Y d}	~	d}	~	ww xY w# t        $ r}	t        d|	        Y d}	~	d}	~	ww xY wc c}w )u5   搜索记忆（同时搜索新旧两个 collection）r      )query_embeddings	n_resultsr,   r]   	distances   r_   4096)idrN   scorerI   sourceu    [搜索] 新 collection 失败: Nc              3   .   K   | ]  }|d    k(    yw)rN   NrC   ).0mdocs     r)   	<genexpr>z search_memory.<locals>.<genexpr>   s     Eqq|s2Es   768u    [搜索] 旧 collection 失败: rN   indexrelevance_scorererank_scorec                     | d   S )Nro   rC   )xs    r)   <lambda>zsearch_memory.<locals>.<lambda>   s
    '
 rD   T)keyreverse)rE   rM   rK   countr*   r+   r3   	enumerateappendr!   r"   COLLECTION_NAME_FALLBACKre   anyr4   r:   copysort)r+   r-   rf   rL   r   new_colquery_embeddingr0   ir(   old_colfallback_embeddingrs   docsrerankedreranked_memoriesridxmemrt   s                      @r)   search_memoryr      s   \FH6''8==?Q+E2Omm"1!2:eai5'--/R $ G {#A&'(<Q(?@ FAsOO%enQ/2#&CJ;CWW[%9!%<Q%?!?]^BI+BVGK$8$;A$>\^"(% 6''(@A==?Q!7!>mm"4!5:eai5'--/R $ G {#A&'(<Q(?@ 	FAsEHEE ")%."3A"6'*GN{G[Q)=a)@)C%CabFMkFZ(<Q(?(B`b&+) 	 	 c(ma'&./)//%u-  	*AG*C3-$$&C"#$5"6C$$S)		* !%(( MM*DM9FU]  604556.  604556 0s>   C
I BI) 4AI) J	I&I!!I&)	J
2JJ
limitc                     t               }t        |      }|j                         dk(  rg S |j                  |       }g }t	        |d         D ]-  \  }}|j                  |d   |   ||d   r|d   |   ni d       / |S )u   列出所有记忆r   )r   r,   r]   r_   rn   rN   rI   )rE   rM   r   r6   r   r   )r   rL   ra   r0   r   r   rt   s          r)   list_memoriesr      s    \F'JQ	nn5n)GHGK01 3%.#3:;3G,Q/R
 	 OrD   rb   c                 t    t               }t        |      }	 |j                  | g       y# t        $ r Y yw xY w)   删除记忆)r]   TF)rE   rM   deleter!   )rb   rL   ra   s      r)   delete_memoryr     s@    \F'Jyk* s   + 	77c                      t               } t        |       }|j                         }g }t        |d         D ]-  \  }}|j	                  |d   |   ||d   r|d   |   ni d       / |S )u   导出所有记忆r,   r]   r_   r   )rE   rM   r6   r   r   )rL   ra   r0   r   r   rt   s         r)   export_memoriesr     s~    \F'JnnGHGK01 3%.#3:;3G,Q/R
 	 OrD   c           	         t               }t        |      }d}| D ]  }	 |j                  dd      }|s|j                  d      xs t        |      }t	        |      }|j                  di       }d|vr%t        j                         j                         |d<   |j                  |g|g|g|g       |dz  } |S # t        $ r>}	t        d	|j                  dd      d
d  d|	 t        j                         Y d
}	~	d
}	~	ww xY w)   导入记忆r   rN    rn   rI   r[   r\   rl   u   导入失败: N2   z... - )file)rE   rM   r6   rX   r*   r   rQ   rR   r`   r!   r"   sysstderr)
r   rL   ra   r   r   rN   rb   r   rI   r(   s
             r)   import_memoriesr   1  s   \F'JE \	\ggi,G=W)=I%g.Iwwz2.H8+)1)A)A)C&NNK%;")#*	   QJE%\, L  	\N3779b#9#2#>"?vaSIPSPZPZ[[	\s   B9BB99	D 4C;;D c                  8   t        j                  d      } | j                  dd      }|j                  dd      }|j	                  d	d
       |j                  dd      }|j	                  dd       |j	                  dt
        dd       |j	                  ddd       |j                  dd      }|j	                  dt
        dd       |j                  dd      }|j	                  dd       |j                  d d!       |j                  d"d#       |j                  d$d%       | j                         }|j                  dk(  r7t        |j                        }t        t        j                  d&|d'             y |j                  dk(  rOt        |j                  |j                  |j                         }t        t        j                  d(|id)d*+             y |j                  dk(  rCt#        |j$                        }	t        t        j                  |	t'        |	      d,d)d*+             y |j                  dk(  r6t)        |j*                        }
t        t        j                  d-|
i             y |j                  d k(  r,t-               }	t        t        j                  |	d)d*+             y |j                  d"k(  rut        j.                  t0        j2                        }t5        |t6              r|}	n|j9                  d.g       }	t;        |	      }t        t        j                  d&|d/             y |j                  d$k(  rDt=               }t?        |      }t        t        j                  d$|jA                         i             y | jC                          y )0Nu   本地向量记忆系统)rG   commandu   命令)desthelpr`   rZ   )r   rN   u   记忆内容searchu   搜索记忆r+   u   查询内容z--top_k   u   返回数量)typedefaultr   z--rerank
store_trueu   使用 reranker)actionr   listu   列出记忆z--limit   u   数量限制r   r   rb   u   记忆IDexportu   导出记忆importr   r   u   统计记忆数量T)successrn   r0   Frh   )ensure_asciiindent)r   r   r   r   )r   imported)"argparseArgumentParseradd_subparsers
add_parseradd_argumentint
parse_argsr   rc   rN   r"   r   dumpsr   r+   r-   r:   r   r   r4   r   rb   r   loadr   stdin
isinstancer   r6   r   rE   rM   r   
print_help)parser
subparsersr   search_parserlist_parserdelete_parserargsrb   r0   r   r   r   r   rL   ra   s                  r)   mainr   P  s   $$1KLF&&IH&EJ &&u>&BJIN; ))()HMw^<ysANSz,EVW ''^'DKYS">R ))()HM{< (8 (8 '(<=D||ut||,	djjT;<=		!

DJJDdjj)W-E!LM		 ,djjhXGV[defg		!/djj)W-./		!"$djja@A		!yy#dD!Hxx
B/H)djjTu=>?		 #F+
djj':#3#3#5678 	rD   __main__)r   )r   F)r   )2__doc__r>   r   r   r   rS   r   r   typingr   r   rA   chromadb.configr   r=   
expanduserr@   rK   r   r   r   r   r5   r1   r2   r%   r#   r$   r9   r7   r8   strfloatr*   r   dictr:   rE   rM   rX   rc   re   boolr   r   r   r   r   r   __name__rC   rD   r)   <module>r      s   
 
      !  $ gg  !<=!%  ? I .9 H , H R A B Q ? * *U *D&%# &%$s) &%C &%T
 &%R7J J J  $* *e * E ES E$ E4PT: EP d4j *	S 	T 	d $d4j S >DN zF rD   