
    zjs                       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Zej	        
                    ej	        
                    ej	                            e                              Zej	                            ed          Zej	                            d          Zej	                            ed          Zej	                            ed          Zej	                            ed          Zej	                            ed          Zd	Zej	                            ed
          Zej	                            ed          ZdZddddZd Zd Zd Zd Zdd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+i Z,d# Z-d$ Z.d% Z/h d&Z0d' Z1d( Z2d)g d*iZ3i d+d+d,gd-g d.d/d/d0gd1d1d2gd3g d4d5d5d6gd7g d8d9g d:d;d<d=gd>g d?d@d@dAgdBdBgdCdCdDgdEdEgdFdFgdGdGgdHdHdIgdJdKgdLgdMZ4g dNZ5g dOdPgdQZ6i Z7ddSZ8ddTZ9dU Z:dVZ;dWZ<ddYZ=dZ Z>da?d[ Z@d\ ZAd] ZBdd^ZCd_ ZDd` ZEda ZFej	                            edb          ZGg dcZHdd ZIde ZJdf ZKdg ZLddhZMdi ZNdj ZOdkdldmdndodpdqiZPdr ZQds ZRdt ZSduZTdvZUdw ZVdx ZWdy ZXej	                            edz          ZYd{ ZZd| Z[d} Z\d~ Z]ddZ^ddZ_ddZ`ddZaddZbddZcd Zdd Zed Zfd Zgd Zhd Zid Zjd Zkd Zld Zmd Znd Zod ZpdddddZqd Zrd Zsd Ztd Zud ZvdS )u  
宝锐销售工作台 · AI 智能问答模块

被 销售工作台服务.py 的 /api/chat 端点调用。
职责：
  1. 只答工作问题（system prompt 铁律）
  2. 客户问题权限隔离（按角色过滤可见客户，只注入权限范围内的客户摘要）
  3. 行业/市场/竞品问题自由回答（不注入客户数据）

仅依赖 Python 标准库 + yaml（读取 Hermes config）。
    Nu   数据u:   ~/Library/Application Support/宝锐工作台/生成文件filesz
index.jsonzchat_history.jsonzai_token_usage.json   zai_deepseek_quota.jsonzuser_permissions.json<   u   诊断原料   生命科学u   诊断原料+生命科学)dalsbothc                  $   	 t           j                            t                    rYt	          t          d          5 } t          j        |           }ddd           n# 1 swxY w Y   t          |t                    r|S n# t          $ r Y nw xY wi S )u.   读取历史文件，返回 {name: [messages]}utf-8encodingN)
ospathexistsHISTORY_FILEopenjsonload
isinstancedict	Exceptionfds     B   /Users/liuxinyuan/Desktop/Hermes输出-工作类/脚本/ai_chat.py_load_historyr   &   s    7>>,'' 	lW555 !IaLL! ! ! ! ! ! ! ! ! ! ! ! ! ! !!T""    I4   :B  AB  A!!B  $A!%B   
BBc                 ,   	 t          j        t          d           t          dz   }t	          |dd          5 }t          j        | |dd	           d
d
d
           n# 1 swxY w Y   t          j        |t                     dS # t          $ r Y dS w xY w)u!   写回历史文件（原子写）Texist_ok.tmpwr   r   F   ensure_asciiindentN)	r   makedirsDATA_DIRr   r   r   dumpreplacer   datatmpr   s      r   _save_historyr.   3   s    
Ht,,,,V##sW--- 	=IdAE!<<<<	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=

3%%%t   uu4   7B AB A""B %A"&B 
BBc                 F    t                                          | g           S )u0   返回某用户的历史（可能为空列表）)r   getnames    r   _get_historyr4   @   s    ??tR(((    c                     t                      }|r!t          |          t           d         || <   n|                    | d           t	          |           dS )u$   设置某用户的历史（截断）N)r   listMAX_HISTORYpopr.   )r3   messageshs      r   _set_historyr<   E   sW    A x..+/$	dD!r5    c           
      ^   | r|sdS 	 ddl m } i }t          j                            t                    rYt          t          d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                    r|}t          |                                                                          |                                                    d          |pdt          |                    d          pd          t          |                    d	          pd          t          |                    d
          pd          |pddd         d}|                    | g                               |           ||          dd         || <   t          j        t$          d           t          dz   }	t          |	dd          5 }t          j        ||dd           ddd           n# 1 swxY w Y   t          j        |	t                     dS # t*          $ r Y dS w xY w)uR   记录一次 AI 调用的 token 消耗（按 name 分桶，保留最近 500 条）Nr   datetimer   r   %Y-%m-%dr=   prompt_tokenscompletion_tokenstotal_tokensP   )tsdatemodelprompt
completiontotalqiTr   r!   r"   Fr#   r$   )r@   r   r   r   TOKEN_USAGE_FILEr   r   r   r   r   intnow	timestampstrftimer1   
setdefaultappendr'   r(   r)   r*   r   )
r3   usagequestionrH   r@   r,   r   _drecr-   s
             r   _record_token_usagerX   O   s    u %%%%%%7>>*++ 	&999 "QYq\\" " " " " " " " " " " " " " ""d## hllnn..0011LLNN++J77[b%))O449::eii(;<<ABB>227a88.b#2#&
 
 	b!!((---$Z&T

Ht,,,,'#sW--- 	=IdAE!<<<<	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=

3()))))   s[   AH 
A+H +A//H 2A/3EH G7+H 7G;;H >G;?H 
H,+H,c                  $   	 t           j                            t                    rYt	          t          d          5 } t          j        |           }ddd           n# 1 swxY w Y   t          |t                    r|S n# t          $ r Y nw xY wi S )u8   读取 DeepSeek 每日限流计数 {name: {date: count}}r   r   N)
r   r   r   DEEPSEEK_QUOTA_FILEr   r   r   r   r   r   r   s     r   _load_deepseek_quotar[   o   s    7>>-.. 	)G<<< !IaLL! ! ! ! ! ! ! ! ! ! ! ! ! ! !!T""    Ir   c                 ,   	 t          j        t          d           t          dz   }t	          |dd          5 }t          j        | |dd	           d
d
d
           n# 1 swxY w Y   t          j        |t                     d
S # t          $ r Y d
S w xY w)u"   原子写回 DeepSeek 限流计数Tr   r!   r"   r   r   Fr#   r$   N)	r   r'   r(   rZ   r   r   r)   r*   r   r+   s      r   _save_deepseek_quotar]   |   s    
Ht,,,,!F*#sW--- 	=IdAE!<<<<	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=

3+,,,,,   r/   c                    ddl m } |                                                    d          }t                                          | i           }t          |                    d          |k    r|                    d          pd          S )uF   返回某用户今天已用的 DeepSeek 次数（跨天自动归零）r   r?   rA   rG   count)r@   rO   rQ   r[   r1   rN   )r3   r@   todaybuckets       r   _deepseek_used_todayrb      s    !!!!!!LLNN##J//E!##''b11Fvzz&!!U*Bvzz'/B/BGaHHHr5   c                 V    t          |           }|dk     rdS t          |           |k     S )uU   DeepSeek 是否还有今日额度（limit=-1 不限；否则按 per-user limit 计）r   T)_get_ds_limitrb   )r3   limits     r   _deepseek_quota_availablerf      s0    $Eqyyt%%--r5   c                 l   ddl m } |                                                    d          }t                      }|                    | i           }|                    d          |k    r|dd}t          |                    d          pd          dz   |d<   ||| <   t          |           |d         S )uF   消耗一次 DeepSeek 今日额度（返回消耗后的已用次数）r   r?   rA   rG   )rG   r_   r_   r#   )r@   rO   rQ   r[   r1   rN   r]   )r3   r@   r`   r,   ra   s        r   _consume_deepseek_quotarh      s    !!!!!!LLNN##J//E!!DXXdBFzz&U""!,,&**W--233a7F7ODJ'?r5   c                  $   	 t           j                            t                    rYt	          t          d          5 } t          j        |           }ddd           n# 1 swxY w Y   t          |t                    r|S n# t          $ r Y nw xY wi S )uU   读取按人权限覆盖 {name: {ds_limit, tabs, extra_customers, blocked_customers}}r   r   N)
r   r   r   PERMISSIONS_FILEr   r   r   r   r   r   r   s     r   _load_user_permissionsrk      s    7>>*++ 	&999 !QIaLL! ! ! ! ! ! ! ! ! ! ! ! ! ! !!T""    Ir   c                 ,   	 t          j        t          d           t          dz   }t	          |dd          5 }t          j        | |dd	           d
d
d
           n# 1 swxY w Y   t          j        |t                     dS # t          $ r Y dS w xY w)u   原子写回权限覆盖文件Tr   r!   r"   r   r   Fr#   r$   N)	r   r'   r(   rj   r   r   r)   r*   r   r+   s      r   _save_user_permissionsrm      s    
Ht,,,,'#sW--- 	=IdAE!<<<<	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=

3()))t   uur/   c                 F    t                                          | i           S )u8   返回某用户的权限覆盖 dict（可能为空 {}）)rk   r1   r2   s    r   _get_user_permro      s    !##''b111r5   c                     t          |           }|                    d          }|t          S 	 t          |          }n# t          $ r
 t          cY S w xY w|dk     rdS |S )uk   返回某用户 DeepSeek 每日限流次数。-1=不限；<=0 或缺省用全局默认 DEEPSEEK_DAILY_LIMITds_limitNr   )ro   r1   DEEPSEEK_DAILY_LIMITrN   r   )r3   pvs      r   rd   rd      sw    tA	jAy##$FF $ $ $####$1uurHs   ? AAc                     	 ddl } |                     t          t          j                            d          d                    }|                    di                               di           }|                    dd	          |                    d
          pd                    d          |                    d          pddS # t          $ r}dt          |          icY d}~S d}~ww xY w)u0   从 ~/.hermes/config.yaml 读取 DeepSeek 凭证r   N~/.hermes/config.yamlr   r   	providersdeepseekapi_keyr=   base_urlzhttps://api.deepseek.com/default_modelzdeepseek-v4-prorz   r{   rH   error
yaml	safe_loadr   r   r   
expanduserr1   rstripr   str)r   cfgdses       r   _load_deepseek_cfgr      s    
!nnT"'"4"45L"M"MX_```aaWW["%%))*b99vvi,,
++I/IQQRUVVVVO,,A0A
 
 	

  ! ! !Q       !   CC 
C-C("C-(C-c                     	 ddl } |                     t          t          j                            d          d                    }|                    di                               di           }|                    dd	          |                    d
          pd                    d          |                    d          pddS # t          $ r}dt          |          icY d}~S d}~ww xY w)uF   从 ~/.hermes/config.yaml 读取智谱 GLM 凭证（providers.zhipu）r   Nrw   r   r   rx   zhipurz   r=   r{   z$https://open.bigmodel.cn/api/paas/v4r|   r}   zglm-5.3-flashr~   r   r   )r   r   zr   s       r   _load_glm_cfgr      s    
!nnT"'"4"45L"M"MX_```aaGGK$$(("55uuY++z**T.T\\]`aaUU?++>
 
 	

  ! ! !Q       !r   c                     g } 	 t           j                            t          dd          }t           j                            |          r=t          |d          5 }t          j        |          } ddd           n# 1 swxY w Y   n# t          $ r g } Y nw xY wg }	 t           j                            t          d          }t           j                            |          rt          |d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                    r|                    dg           }nt          |t                    r|}n# t          $ r g }Y nw xY w| |fS )uE   加载客户数据：诊断原料重点客户 + 生命科学课题组pricingz_key_customers.jsonr   r   Nzls_groups.jsongroups)r   r   joinr(   r   r   r   r   r   r   r   r1   r7   )custsrt   r   r   gs        r   _load_customer_datar      s   EGLL9.CDD7>>! 	%a'*** %a	!% % % % % % % % % % % % % % %   F
GLL#3447>>! 	a'*** !aIaLL! ! ! ! ! ! ! ! ! ! ! ! ! ! !!T"" x,,At$$    &=sn   AB A;/B ;A??B A?B BBAE! 1DE! DE! DAE! !E0/E0c            
         dt           v rt           d         S 	 t          j                            t          d          } t          | d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                    r|
                    dg           n|}g }|D ]}|
                    d          }t          |t                    r|
                    dd	          n|rt          |          nd	}|
                    d
          }t          |t                    r|
                    dd	          n|rt          |          nd	}	|                    |
                    d          pd	|
                    d          pd	||	|
                    d          pd	d           |t           d<   n# t          $ r g t           d<   Y nw xY wt           d         S )uO   加载飞书活动数据（daily_activities_mcp.json），返回规范化列表
activitiesdaily_activities_mcp.jsonr   r   Nitemsfield_5f20fclabelr=   ownername_cnr3   field_b99055
start_time)r3   desccustomerr   rG   )	_BG_CACHEr   r   r   r(   r   r   r   r   r   r1   r   rS   r   )
rt   r   r   r   actsitfccustowr   s
             r   _load_activitiesr     s   y  &&%GLL#>??!g&&& 	!	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	&0D&9&9@gr"""q 	 	B''B*4R*>*>[266'2&&&PREZSWWWXZDB-7D-A-A^BFF9b)))SUH]B[]EKKv,"~..4" |,,2      #'	, % % %"$	,%\""s6   6F; A/#F; /A33F; 6A37EF; ;GGc                     dt           v rt           d         S 	 t          j                            t          dd          } t          | d          5 }t          j        |          }ddd           n# 1 swxY w Y   i }t          |t                    r|ng D ]A}|
                    d          pd}|
                    d	          pd}|r|d
k    r	||vr|||<   B|t           d<   n# t          $ r i t           d<   Y nw xY wt           d         S )uD   加载产品库（_all_products.json），构建 sku→名称 映射productsr   z_all_products.jsonr   r   Nskur=   r3   nan)r   r   r   r   r(   r   r   r   r   r7   r1   r   )rt   r   r   pmapprodr   r3   s          r   _load_productsr   +  s]   Y$$#GLL9.BCC!g&&& 	!	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	$Q--5QQ2 	! 	!D((5//'RC88F##)rD !se||4 S	 $	* # # # "	*#Z  s6   7C# A0$C# 0A44C# 7A48A*C# #C:9C:c                 T    ddl }t          |                    d| pd                    S )u1   提取货号（M2181、M2331、FM2181-4-NA 等）r   Nz/(?:[A-Z]{0,3})M\d{3,4}(?:-\d+)*(?:-[A-Z]{1,3})?r=   )resetfindall)textr   s     r   _extract_skusr   ?  s-    IIIrzzLdjVXYYZZZr5   >-      一样   中心   产业   健康   内部   出来   厂家   反馈   发展   哪里   国际   实业   实验   客户   应该   怎么   性能   控股   推动   无法   有限   检验   生命   研究   确认   科学   继续   股份   药业   资源   问题   项目   公司   制药   医学   医疗   医药   工程   技术   检测   测试   生物   科技   诊断   集团c                     t          t          |           dz
            D ] }| ||dz            }|t          v r||v r dS !dS )uD   a 是否含某个非通用 2 字片段，且该片段出现在 b 中r#      TF)rangelen_COMMON_BIGRAM)abisubs       r   _has_common_bigramr   L  s\    3q66A:  !a%j.  !8844 5r5   c                    | r|sdS t          |           t          |          c}fdt          |dz             D             }d\  }}t          d|dz             D ]r}t          ddz             D ]\}| |dz
           ||dz
           k    rB||dz
           |dz
           dz   ||         |<   ||         |         |k    r||         |         }|}]s| ||z
  |         S )uZ   返回 a 与 b 的最长公共连续子串（动态规划）。用于精确简称匹配。r=   c                 "    g | ]}d gdz   z  S )r   r#    ).0_lbs     r   
<listcomp>z-_longest_common_substring.<locals>.<listcomp>\  s#    	0	0	0Q1#a.	0	0	0r5   r#   )r   r   )r   r   )	r   r   ladpmax_lenend_ar   jr   s	           @r   _longest_common_substringr   W  s    A rVVSVVFB	0	0	0	0%Q--	0	0	0BNGU1b1f  q"q&!! 	 	AQx1QU8##a!e9QU+a/1aa58g%% eAhGE	 UW_U"##r5      动物疫病)u   非瘟u   非洲猪瘟u   猪瘟u	   口蹄疫u	   禽流感u	   新城疫u   蓝耳u   圆环u	   伪狂犬u	   猪腹泻u   流行性腹泻u   细小u   犬瘟u   猫瘟u	   马立克u	   法氏囊u   鸭瘟u   动保r   u   兽医u   兽用u   兽药u   畜牧u   养殖u   水产u   宠物	   诺唯赞Vazyme   翌圣)r   u   羿圣Yeasen	   全式金TransGenu   生工SangonTakara)r  u	   宝日医u	   宝生物u   天根TIANGEN   康为世纪)u   康为r  CWBIO   近岸蛋白)u   近岸r  Novoproteinu   义翘神州u   义翘SinoBiological   爱博泰克)r	  u   艾博泰克Abclonalu   罗氏Rocheu   菲鹏u	   迈迪安Meridianu   华峰u	   白垩纪u   东盛u	   碧云天Beyotime	   索莱宝Solarbio   中科唐碳)r  r  )   竞品u   竞对u   对手u   品牌u   货号u   友商u   竞争)P526QV110Q811zR333-c1Q711AS131)r   r   Fc           
           sdS t           fdt          D                       sdS d}t                                          D ]$\  }}t           fd|D                       r|} n%|sdS t          j                            t          d          }	 t          j                            |          }n# t          $ r d}Y nw xY wt                              |          }	|	r!|	                    d          |k    r|	d         S |r6|	r4|	                    d          |k    rt                              d	d           t          |         }i d}
t                      D ]}|d
         pddz   |d         pdz   t          fd|D                       s7|
dz  }
                                t                                           D ]!\  }}t          fd|D                       s"                    |dt%                      t%                      t%                      d          }|dxx         dz  cc<   |d         r:|st'          |d         ||          r!|d                             |d                    |d         r)|d                             |d         dd                    t*                              |g           D ]!}|v r|d                             |           "#sd||
fz  }nd}                                D ]'}|d         rt/          |d                   }||k    r|}(d||
|fz  g}t1          fd          D ]}|         }|d         r(d                    t1          |d                             nd}d                    t1          |d                   dd                   }|                    d||d         ||fz             d                     |          }||d!t          |<   |S )"u   主题竞品扫描：问题命中「业务主题 + 竞品意图」时，全量扫描活动提取竞品品牌+货号（非超管过滤客户名）r=   c              3       K   | ]}|v V  	d S Nr   r   krU   s     r   	<genexpr>z,_search_topic_competitors.<locals>.<genexpr>  s'      99qH}999999r5   Nc              3       K   | ]}|v V  	d S r  r   r  s     r   r  z,_search_topic_competitors.<locals>.<genexpr>  s'      **qH}******r5   r   r   mtimesummaryr   r3    r   c              3       K   | ]}|v V  	d S r  r   )r   r  r   s     r   r  z,_search_topic_competitors.<locals>.<genexpr>  s'      **19******r5   r#   c              3   D   K   | ]}|                                 v V  d S r  lower)r   altls     r   r  z,_search_topic_competitors.<locals>.<genexpr>  s0      ::BrxxzzR'::::::r5   )r_   	customersdatesskusr_   r   r'  rG   r(  
   r)  uT   【%s主题竞品扫描】相关活动 %d 条，未发现明确竞品品牌提及。u?   【%s主题竞品扫描（相关活动 %d 条，最新 %s）】c                 "    |          d          S )Nr_   r   )x
brand_stats    r   <lambda>z+_search_topic_competitors.<locals>.<lambda>  s    :a=3I2I r5   key   、u   未记录货号   u0   - %s：提及 %d 次；货号：%s；客户：%s
)r  r  )any_COMPETITOR_INTENT_TOPIC_KEYWORDSr   r   r   r   r(   getmtimer   _TOPIC_COMPETITOR_CACHEr1   r   r9   r   r$  _COMPETITOR_BRANDSrR   r   _name_visibleadd_COMPETITOR_SKUSvaluesmaxsortedrS   )rU   vcustvgroupsis_admintopictkwsrt   r  c
topic_actsr   bmainaliasesstr   r  maxdater   linesr   r)  r   r-  r   r&  s   `                      @@@r   _search_topic_competitorsrM    s    r9999&899999 rE!''))  3****c***** 	EE	  r
X:;;A  ##   ##E**A QUU7^^u$$| * *quuW~~..lD)))
%
 CJJ ( (&	R3&!F)/r:****c***** 	a
ZZ\\06688 	( 	(NE7::::'::::: &&uX[X]X]gjglgl.m.mnnBwKKK1KKK} 3( 3mAjM5RY.Z.Z 3;##AjM222y 07&	#2#///'++E266 ( ($;;vJNN3'''(	(  #hlqs}k~~##%% 	  	 B'{  7$$w;;GRV[]gipUqqr
(I(I(I(IJJJ 	m 	mAAB57ZV5::fRZ00111EVDJJvbo66rr:;;ELLKqRTU\R]_cejNkkllll))E""/4%I%IE"Ns   B5 5CCc                    #  sdS t                     }g }t                      }g }|D ]}|s |d         rt          |d         ||          s$|d         dz   |d         z   dz   |d         z   #d}	|d         rt          |d                    rd}	n@|rt	          #fd|D                       rd	}	n |d         rt          |d                    rd
}	|	r|                    |	|f           |rAt                      }
g }|D ]`\  }	}|d         |d         z   |d         z   |d         dd         z   }||
vr,|
                    |           |                    |	|f           a|                    d d           |                    d d           d |D             dd         }dt          |          z  g}|D ]L}|                    d|d         d|d         d|d         d|d         d|d         dd         
           M|                    d
                    |                     |rt                      }g }|D ]}|                    d          d         }|                                D ]Y\  }}||k    s2|                    |dz             s|r4|                    |dz             r|                    |d|            nZt          t                               |                    dd         }|r+|                    d d
                    |          z              	 g }t$          j        
                    t(          d!          }t$          j                            |          rt-          |d"#          5 }t/          j        |          }ddd           n# 1 swxY w Y   |                    d$g           pg D ]b}|                    d%g           pg }|                    d&d          pddz   |                    d'd          pdz   dz   d
                    |          z   dz   d
                    |                    d(g           pg           z   }t	           fd)|D                       }|st          |           r|                    d*i           pi }|                    d+|                    d&d          d,|                    d-d          d.|                    d/d          d0d1
                    |                    d(g           pg                      dt$          j        
                    t(          d2          }t$          j                            |          rt-          |d"#          5 }t/          j        |          }ddd           n# 1 swxY w Y   |                    d3g           pg D ]}|                    d4d          pd} | rt          |            rqd5 |                    d6g           pg D             }!|!rM|                    d7| d8|                    d9d          d:d;
                    |!dd<                              |r3|                    d=d
                    |dd                   z              n# t4          $ r Y nw xY w	 t7           |||          }"|"r|                    |"           n# t4          $ r Y nw xY w|rd>
                    |          ndS )?uu   按问题检索相关背景（飞书活动 + 产品），返回背景文本。非超管按可见名单过滤活动。r=   r   r   r3   r   r   r   c              3       K   | ]}|v V  	d S r  r   )r   shays     r   r  z%_search_background.<locals>.<genexpr>  s'      11q!s(111111r5   r   r#   rG   N   c                 J    | d         d         pd                     dd          S )Nr#   rG   r=   -)r*   r,  s    r   r.  z$_search_background.<locals>.<lambda>  s#    1f!3 < <S" E E r5   Tr0  reversec                     | d         S )Nr   r   rU  s    r   r.  z$_search_background.<locals>.<lambda>  s
    ! r5   c                     g | ]\  }}|S r   r   )r   r   r   s      r   r   z&_search_background.<locals>.<listcomp>  s    ###da###r5      u+   【飞书活动记录（最近 %d 条）】- [] u   ｜客户：u   ｜跟进人：r      ｜   r3  rT     ：r*  u   【相关产品】
zproduct_selling_points.jsonr   r   r   r)  product_linecategorycompetitorsc              3       K   | ]}|v V  	d S r  r   )r   rP  rU   s     r   r  z%_search_background.<locals>.<genexpr>  s'      ::qAM::::::r5   fabeu   【产品卖点】u   ｜定位：positionu   ｜优势：	advantageu   ｜竞品：r1  zpcr_lamp_selection.jsonr   projectc                 d    g | ]-}|                     d           |                     d d          .S )r   r=   r1   )r   rs     r   r   z&_search_background.<locals>.<listcomp>  s;    hhh[\[`[`af[g[ghAEE%,,hhhr5   recommendedu   【选型推荐】   （methodu   ）→ z /    u   【产品卖点/选型】


)r   r   r:  r   r4  rS   r   r;  sortr   r   r   splitr   
startswithr7   r   fromkeysr   r   r(   r   r   r   r   r1   r   rM  )$rU   r@  rA  rB  r)  partsr   matchedr   prioseenuniqr0  rL  r   plinesrP  baser  ru   sp_linessp_file_f_spdrt   _skus_hay_hit_fbsel_file_seldr   _proj_rec_tcrQ  s$   `                                  @r   _search_backgroundr    s    r""DEDG & & 	AjM 	-*uV]2^2^ 	
mc!AfI-3ai?Z= 	/*xHH 	DD 	c1111D11111 	DDvY 	-aiBB 	D 	&NND!9%%% 'uu 	' 	'GD!F)a
m+ai7!F)CRC.HC$T1I&&&		EEt	TTT		nnd	333##d###BQB'>TJK 	S 	SALLL&			1V999a
mmmQwZZZ64C4R S S S STYYu%%&&& E 	 	A773<<?D

  166Q\\!c'226t6TTWZ@X@X6MMqqq!!"4555EdmmF++,,SbS1 	ELL/$))F2C2CCDDD',,x)FGG7>>'"" 
	Qg000 %By}}% % % % % % % % % % % % % % %XXj"--3 Q Qfb))/Rnb117R3>!%%
TVBWBWB][]^addgjgogopugvgvvy||  @C  @H  @H  IJ  IN  IN  O\  ^`  Ia  Ia  Ig  eg  @h  @h  h::::E::::: Q-dH== Q%%++1rCOOOnb1111155R3H3H3H3H#''R]_aJbJbJbJbdidndnopotot  vC  EG  pH  pH  pN  LN  eO  eO  eO%P Q Q Q7<<*CDD7>>(## 	Dh111 &R	"& & & & & & & & & & & & & & &ii,,2 D Dy"--3 D/x@@ DhhrvvmR7P7P7VTVhhhD D PUPUPUWYW]W]^fhjWkWkWkWkmrmwmwx|}~}  yA  nB  nB  nB  )C  D  D  D 	RLL68BQB<9P9PPQQQ   '%(KK 	LL   !&.6;;uB.so   ,AZ N%Z %N))Z ,N)-GZ V"Z "V&&Z )V&*C.Z 
Z&%Z&*)[ 
[! [!c                     	 t           j                            d          pQt          t           j                            d          d                                                                          } t           j                            d          pQt          t           j                            d          d                                                                          }| |fS # t          $ r Y dS w xY w)u*   读取 IMA 凭证（client_id + api_key）IMA_OPENAPI_CLIENTIDz~/.config/ima/client_idr   r   IMA_OPENAPI_APIKEYz~/.config/ima/api_key)r=   r=   )	r   environr1   r   r   r   readstripr   )cidr0  s     r   _load_ima_cfgr  -  s   jnn344  MRW=O=OPi=j=ju|8}8}8}  9C  9C  9E  9E  9K  9K  9M  9Mjnn122  Id27;M;MNe;f;fqx6y6y6y6~6~  7A  7A  7G  7G  7I  7ICx   vvs   C%C( (
C65C6z,fwEO41E0Ef0CfulmtQHYgYQy_jBsbl6tXE5x6VYLIoA=z,lofJ7cHGhLIvU_ISjQ0tmdYkgzvfjhvZrfVaoo4s7Jc=r   c           	      	   ddl }ddl}| sdS t          d          }| }|D ]}|                    |d          }d |                    d|          D             }g }t                      }	|t          t          |                     z   D ]O}
|
                                }
|
r7|
t          vr.|
|	vr*|		                    |
           |
                    |
           P|sdS g }t                      \  }}|r`|r]|dk    rt          nt          }	 t          j                            d	t#          j        ||d         dd
                              d          ||ddd          }t          j                            |d          5 }t#          j        |                                                    d                    }ddd           n# 1 swxY w Y   |                    d          pi                     d          pg D ]B}|                    d          pd                                }|r|
                    |           Cn# t2          $ r Y nw xY wt4          j                            t:          d          }g }	 g }|dd         D ]o}|                    t4          j                            |dd|z   dz             d          D ]0}||vr*|                    d          s|
                    |           1p|dd         D ]}	 |                    d          r>t?          |d          5 }|                                }ddd           n# 1 swxY w Y   nO|                    d          r9ddl }|                    |          }d                     d! |D                       }n|
                    d"t4          j        !                    |          d#|dd$                    # t2          $ r Y w xY wn# t2          $ r Y nw xY wg }|r=|
                    d%d                     d& |dd         D                       z              |r+|
                    d'd(                    |          z              |rd(                    |          ndS ))ud   检索 IMA 知识库（索引层标题）+ 本地知识库内容（内容层），返回背景文本r   Nr=   ur   的了和与跟是有在把被让向从对这将那什么怎么哪比更最很都也还再又或并及等而之其r   c                 8    g | ]}t          |          d k    |S )r   )r   )r   r"   s     r   r   z*_search_knowledge_base.<locals>.<listcomp>F  s%    YYY1SQRVVWX[[Q[[[r5   z[^\u4e00-\u9fffA-Za-z0-9]+r   z3https://ima.qq.com/openapi/wiki/v1/search_knowledge)knowledge_base_idquerycursorr   application/json)zima-openapi-clientidzima-openapi-apikeyContent-TypePOSTr,   headersrm  rZ  timeoutr,   	info_listtitleu   知识库-copilotr2  z***T)	recursivez	.DS_Storern  .mdr   .pdfr3  c              3   >   K   | ]}|                                 V  d S r  )get_textr   rt   s     r   r  z)_search_knowledge_base.<locals>.<genexpr>t  s*      #>#>QAJJLL#>#>#>#>#>#>r5   u   【u   】
i  u    【IMA 知识库相关材料】
c              3   0   K   | ]}d |dd         z   V  dS )- NrE   r   r   rD  s     r   r  z)_search_knowledge_base.<locals>.<genexpr>~  s/      DgDgWXTAcrcF]DgDgDgDgDgDgr5   u   【本地知识库内容】
ro  )"r   globr   r*   rq  r7   r   r  r   r;  rS   r  	IMA_KB_LSIMA_KB_DIAGurllibrequestRequestr   dumpsencodeurlopenloadsr  decoder1   r   r   r   r   ROOTendswithr   pymupdfbasename)r  line_re_glob_STOPcleanedrF  cands
core_wordsseen_cwr"   titlesr  r0  kbreqrj  r   r   rD  kb_dircontent_partsru  kwfpr   txtr  docrt  s                                 r   _search_knowledge_baser  ;  s    r  E  F  FEG * *//!S))YY		"?IIYYYEJeeGT-../// ! !GGII 	!.((Qg-=-=KKNNNa    rFHC
 s $,,YYK	.((EZb:a=\^ _ _``gghopp14Castt	 )  C
 ''Q'77 91Jqvvxxw77889 9 9 9 9 9 9 9 9 9 9 9 9 9 9uuV}}*//<<B % %VVG__*1133 %MM!$$$%  	 	 	D	 W\\$ 344FMRaR. 	' 	'BjjfdC"HsN!K!KW[j\\ ' 'W$$R[[-E-E$NN2&&&' "1"+ 	 	B;;u%% b7333 'qffhh' ' ' ' ' ' ' ' ' ' ' ' ' ' '[[(( "NNN!,,r**C))#>#>##>#>#>>>CC$$$rw7G7G7K7K7K7KSQVRVQVZZ%XYYYY   	    E i8499DgDg\bcedece\fDgDgDg;g;gghhh S4v{{=7Q7QQRRR!&.6;;uB.s   
A7I :G;I GI GA2I 
II:BO7 &O&)M
>O&
M	O&M	AO&$O7 %A O&%O7 &
O30O7 2O33O7 7
PPc                 b   | pd                                 }|sdS d|v rWd|v rS|                    d          }|                    d|          }||k    r|d|         ||dz   d         z   }nnd|v rd|v Sd|v rWd|v rS|                    d          }|                    d|          }||k    r|d|         ||dz   d         z   }nnd|v rd|v SdD ]R}|                    |          r;t          |          t          |          z
  d	k    r|dt          |                    }Sd
}|rd}dD ]~}|                    |          rgt          |          t          |          z
  d	k    rD|dk    r$t          |          t          |          z
  dk     rd|dt          |                    }d
}||S )uY   提取客户核心词：去括号 + 去公司/科技/生物等后缀，保留至少 2 字r=   rl     ）Nr#   ())u   股份有限公司u   有限责任公司u   有限公司r   r   r   TF)
r   r   r   r   r   r   r   r   r   r   r   r   )r  findr  r   )r3   rP  r   r   sufchangeds         r   
_cust_corer    s   	A r
1**!FF5MMqvveQ//1q55"1"!a%&&	!AA 1**! ((saxxFF3KKQVVC^^q55"1"!a%&&	!AA ((saxx `  ::c?? 	s1vvC0A55*CHH9*A G
 w 	 	Czz# 3q66CHH#4#9#9(??s1vvC'81'<'<jCyjM   Hr5   c                  ^   t           t           S g } 	 t          t          j                            t
          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                    r|
                    dg           n|}|D ]x}t          |t                    r|
                    d          nt          |          }|r;t          |          }|r*t          |          dk    r|                     ||f           yn# t          $ r Y nw xY w| a | S )uC   加载 mcp_customers.json 客户名 → 核心词映射（缓存）Nmcp_customers.jsonr   r   r'  r3   r   )_MCP_CUST_CORESr   r   r   r   r(   r   r   r   r   r1   r   r  r   rS   r   )coresr   r   r   rF  r3   cores          r   _load_mcp_customer_coresr    so    "E"',,x)=>>QQQ 	UV	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	*4Q*=*=Dk2&&&1 	/ 	/A$.q$$7$7C155===SVVD /!$'' /CIINNLL$...	/    OLs6   4D A'D 'A++D .A+/B+D 
D('D(c                    | sdS t                      }d\  }}|D ]/\  }}|r(|| v r$t          |          |k    r|t          |          }}0|r|S |D ]O\  }}|st          ||           }t          |          dk    r$t          |          |k    r|t          |          }}P|r|S |D ];\  }}|r4t          ||           r$t          |          |k    r|t          |          }}<|S )uc   从问题中识别客户名，返回客户全名或空串（最长核心词优先，支持简称）r=   )r=   r   r   )r  r   r   r   )rU   r  bestbest_lenr3   r  lcss          r   _identify_customerr    s0    r$&&END( - -
d 	-DH$$TX)=)=!3t99(D  , ,
d 	'h77s88q==SXX00!3s88(D  - -
d 	-&tX66 	-3t99x;O;O!3t99(DKr5   c                    | sdS t          |           }|pg D ]R}t          |t                    r|                    d          pdnt	          |          }|r|| k    s
|r||v s||v r dS S|pg D ]R}t          |t                    r|                    d          pdnt	          |          }|r|| k    s
|r||v s||v r dS SdS )u~   判断客户名是否在可见名单内（vcust: 客户, vgroups: 课题组）。用于非超管背景检索的权限过滤。Fr3   r=   T)r  r   r   r1   r   )r3   r@  rA  r  rF  cnr   gns           r   r:  r:    s     udDkr  &0D&9&9EaeeFmm!rs1vv 	2::$:DBJJ"**44m  &0D&9&9EaeeFmm!rs1vv 	2::$:DBJJ"**445r5   c                    t          |           }|sdS t          |          sdS |st          |||          sdS ddlm} g }	 t          t          j                            t          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   fd|                    d	          pg D             }	|	rt          d
 |	D                       }
 |d |	D                       }dt          |	          |
dz  fz  g}|                    dd                    d |                    d          D                       z              d |	D             }|r|                    dt          |          z             t#          |d d          dd         D ]}|                    d          pi }|                    d|                    dd          d|                    dd          d|                    d          pddd         d |                    d!d          d"|                    d#d          d|                    d$d%                     |                    d&                    |                     n# t$          $ r Y nw xY w	 t          t          j                            t          d'          d          5 }t          j        |          }ddd           n# 1 swxY w Y   fd(|                    d)          pg D             }|rd*t          |          z  g}|dd         D ]}|                    d+|                    d,d          d-|                    d.d          d/|                    d0d          d1|                    d2d          d|                    d3          pddd4         
           |                    d&                    |                     n# t$          $ r Y nw xY w	 t          t          j                            t          d5          d          5 }t          j        |          }ddd           n# 1 swxY w Y   fd6|                    d7          pg D             }|rd8t          |          z  g}|dd         D ]}|                    d9|                    d.d          |                    d3d          |                    d:          pddz  |                    d;          pddz  |                    d<          pddz  |                    d=          pddz  |                    d>          pddz  fz             |                    d&                    |                     n# t$          $ r Y nw xY wfd?t'                      D             }|rt#          |d@ d          }dAt          |          z  g}|dd         D ]e}|                    d+|                    dBd          d-|                    d.d          d|                    dC          pdddD                    f|                    d&                    |                     	 t)          |dE          }|r|                    |           n# t$          $ r Y nw xY w	 t          t          j                            t          dF          d          5 }t          j        |          }ddd           n# 1 swxY w Y   fdG|                                D             }|rdHdIdJdKdL}dMg}|dd         D ]X}|                    d|                    |                    d3          dN          dO|                    d.d                     Y|                    d&                    |                     n# t$          $ r Y nw xY w|sdS dP|z  dQ                    |          z   S )Ru   识别问题中的客户名，拉取该客户 6 维全景（订单/客诉/商机/活动/知识库/材料）。每维失败降级。非超管按可见名单隔离。r=   r   )Counterzshipment_status.jsonr   r   Nc                 F    g | ]}|                     d           pdv |S r   r=   ri  r   or  s     r   r   z%_load_customer360.<locals>.<listcomp>  s5    ZZZzARARAXVX8Y8Y!8Y8Y8Yr5   ordersc              3   D   K   | ]}|                     d           pdV  dS )taxTotalr   Nri  r   r  s     r   r  z$_load_customer360.<locals>.<genexpr>   s3      ??1quuZ((-A??????r5   c              3   n   K   | ]0}|                     d           pi                      d          pdV  1dS )shipmentstatusLabel	   无物流Nri  r  s     r   r  z$_load_customer360.<locals>.<genexpr>  sG      eeYZ!%%
++1r66}EETeeeeeer5   u1   【订单/物流】%d 笔，价税合计 %.1f 万'  u	   状态：r1  c              3   *   K   | ]\  }}d ||fz  V  dS )z%s %dNr   )r   r  ru   s      r   r  z$_load_customer360.<locals>.<genexpr>  s/      1a1atq!'QF2B1a1a1a1a1a1ar5      c                     g | ]l}d |                     d          pdv sOd|                     d          pdv s6|                     d          pddk    O|                     d          pddk    j|mS )r   
contractNor=   u   样品amountr   r  ri  r  s     r   r   z%_load_customer360.<locals>.<listcomp>  s     _ _ _aeeL>Q>Q>WUW1X1X\dijinino{i|i|  jC  AC  ]D  ]D uuX3!99quuZ?P?P?UTUZ[>[>[ >[>[>[r5   u"   【测试/样品订单】%d 笔：c                 0    |                      d          pdS )N
createDater=   ri  rU  s    r   r.  z#_load_customer360.<locals>.<lambda>	  s    AEE,<O<O<USU r5   TrV  rZ  r  r  r  r]  orderIdu
   ｜合同 r  rR  u
   ｜单号 
trackingNou
   ｜收件 	recipientr  r  r3  mcp_complaints.jsonc                 F    g | ]}|                     d           pdv |S r  ri  )r   r   r  s     r   r   z%_load_customer360.<locals>.<listcomp>  s5    XXXqdquuZ?P?P?VTV6W6W6W6W6Wr5   r   u   【客诉】%d 条r[  createdr\  r3   u   ｜紧急度 urgencyu
   ｜状态 statustype(   zopportunity_sandbox.jsonc                 F    g | ]}|                     d           pdv |S r  ri  r  s     r   r   z%_load_customer360.<locals>.<listcomp>$  s5    ___aTaeeJFWFWF][]=^=^=^=^=^r5   opportunitiesu   【商机】%d 个u7   - %s｜%s｜目标 %.1f 万｜Q1-Q4 %.1f/%.1f/%.1f/%.1ftargetq1q2q3q4c                 F    g | ]}|                     d           pdv |S r  ri  )r   r   r  s     r   r   z%_load_customer360.<locals>.<listcomp>0  s4    OOO!TaeeJ6G6G6M2-N-NA-N-N-Nr5   c                 0    |                      d          pdS )NrG   r=   ri  )r   s    r   r.  z#_load_customer360.<locals>.<lambda>2  s    155==+>B r5   u   【近期活动】%d 条rG   r      r   zfile_manifest.jsonc                 F    g | ]}|                     d           pdv |S r3   r=   ri  )r   ru   r  s     r   r   z%_load_customer360.<locals>.<listcomp>B  s2    HHHq$155==3FB*G*G*G*G*Gr5   	   拜访卡   市场调研卡u   来访接待u   调研报告)visitmarketvisitorreportu   【已有材料】u   文件r_  u   【客户 360 全景：%s】

ro  )r  r  r:  collectionsr  r   r   r   r   r(   r   r   r1   sumr   rS   most_commonr?  r   r   r  r=  )rU   r@  rA  rB  r   r  rt  r   r   r  taxrJ  rL  test_ordersr  shipr   r   oppsr   r   kb_bgr   labelsr  s                           @r   _load_customer360r    s	   h''D rdD r M$w?? r######E"',,x)?@@7SSS 	WX	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	ZZZZaeeHoo3ZZZ 	+???????Cee^deeeeeBHCPVKKY\_dYdKeefELLuzz1a1ar~~^_O`O`1a1a1a'a'aabbb_ _f _ _ _K WACDTDTTUUU1V1V`deeefhghfhi W WA55,,2DLLLlB////y"1E1E1E1E|,,2CRC888!%%b:Q:Q:Q:Qk2....0T0T0T"V W W W W LL5))***   "',,x)>??'RRR 	VW	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	XXXXQUU7^^1rXXX 	+)CJJ67E2A2Y F FEE)R((((!%%*;*;*;*;QUU9b=Q=Q=Q=QEE(B''''!%%--*=2ss)C)CE F F F F LL5))***   "',,x)CDDwWWW 	[\	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	____AEE/228b___ 	+)CII56E"1"X i iVEE&"%%quuVR'8'8UU8__)U2QUU4[[5EA4NUU4[[%A.t1AU0JQUUSW[[M]\]afLfZh h i i i i LL5))***    POOO'))OOOD 'd ? ?NNN+c$ii78bqb 	q 	qALLLQUU62->->->->fb@Q@Q@Q@QTUTYTYZ`TaTaTgegimjmimSnSnoppppTYYu%%&&&&tT22 	 LL   "',,x)=>>QQQ 	UV	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	HHHHAHHJJHHH 	+*6GTbn|}}F)*E2A2Y e eFJJquuV}}h,O,O,O,OQRQVQVW]_aQbQbQbcddddLL5))***    r-4v{{57I7IIIs   4J :BJ BJ "B#G0J 
J! J!%4P K:.P :K>>P K>DP 
PP4V# 	Q*V# *Q..V# 1Q.2D0V# #
V0/V0'Z6 6
[[4_ ;\_ \  _ #\ $B:_ 
_,+_,c                 2    | pd}d|v rdS d|v rdS d|v rdS |S )u   部门归一化r=   u	   大客户u   大客户部u   拓展u   销售拓展部r   u   生命科学销售部r   )deptr   s     r   
_norm_deptr  O  s@    
Aa~1}}  &&Hr5   c                    t                      }	 t          j                            t          dd          }t          |d          5 }t          j        |                              dg           }ddd           n# 1 swxY w Y   |D ]^}t          |                    dd                    | k    r5|                    d	d
          dk    r|
                    |d                    _n# t          $ r Y nw xY w|S )u!   返回某部门在职人员名单r   z_personnel.jsonr   r   	personnelNr  r=   r  u   在职u   离职r3   )r   r   r   r   r(   r   r   r   r1   r  r;  r   )my_deptnamesrt   r   plr,  s         r   _dept_personnelr$  [  s6   EEEGLL9.?@@!g&&& 	3!1!!+r22B	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	% 	%A!%%++,,77AEE(H<U<UYa<a<a		!F)$$$	%    Ls6   7C) )A<0C) <B  C) B A$C) )
C65C6c                 t  	
 t                      \  }}|                     d          pd                                	|                     d          pd                                }|                     d          pd                                }|dv r||fS g g c|dk    rXd|v sd	|v sd
|v r!d	|v rd	nd
|v rd
nd

r
fd|D             n|nGt          t	          |                    fd|D             n	fd|D             	fd|D             t          	          }t          |                    d          pg           t          |                    d          pg           r$fd|D             z   fd|D             z   rfdD             fdD             fS )ui   按角色过滤可见客户/课题组（支持按人 extra_customers 追加 / blocked_customers 屏蔽）r3   r=   rolesalesr  admingmmanagerr   u   浙江u   广东c                 F    g | ]}|                     d           pdv |S )regionr=   ri  )r   r   	region_kws     r   r   z%_visible_for_user.<locals>.<listcomp>y  s2    QQQQI!%%//:OR,P,Pq,P,P,Pr5   c                 l    g | ]0}|                     d           p|                     d          v .|1S salespersonr'  ri  )r   rF  
dept_namess     r   r   z%_visible_for_user.<locals>.<listcomp>}  s?    ```1!%%*>*>*P!%%..U_)_)_Q)_)_)_r5   c                 p    g | ]2}|                     d           p|                     d          k    0|3S r0  ri  )r   rF  r3   s     r   r   z%_visible_for_user.<locals>.<listcomp>  s?    VVVqaeeM&:&:&LaeeGnnQU%U%U%U%U%Ur5   c                 J    g | ]}|                     d           pdk    | S )r'  r=   ri  )r   r   r3   s     r   r   z%_visible_for_user.<locals>.<listcomp>  s2    GGGw)=2$(F(F1(F(F(Fr5   extra_customersblocked_customersc                 N    g | ]!}|                     d           pdv |v|"S r  ri  )r   rF  extrar@  s     r   r   z%_visible_for_user.<locals>.<listcomp>  s>    ___qaeeFmm.Are-K-KPQY^P^P^P^P^P^r5   c                 N    g | ]!}|                     d           pdv |v|"S r  ri  )r   r   r8  rA  s     r   r   z%_visible_for_user.<locals>.<listcomp>  s>    fff1155==3FB52P2PUV^eUeUeQUeUeUer5   c                 F    g | ]}|                     d           pdv|S r  ri  )r   rF  blockeds     r   r   z%_visible_for_user.<locals>.<listcomp>  s2    JJJqaeeFmm&9r'%I%I%I%I%Ir5   c                 F    g | ]}|                     d           pdv|S r  ri  )r   r   r;  s     r   r   z%_visible_for_user.<locals>.<listcomp>  s2    NNN!%%--*=2g)M)M1)M)M)Mr5   )r   r1   r  r$  r  ro   r   )userr   r   r&  r  permr;  r2  r8  r3   r.  r@  rA  s         @@@@@@@r   _visible_for_userr?  j  sX   '))ME6HHV"))++DHHV'..00DHHV"))++Df}NE7yd""D(8(8h$>N>N$,$4$4xSWGWGW88]_IU^jQQQQ&QQQQdjGG )D)9)9::J```````EE WVVVEVVVGGGGfGGG $D*++1r22E$((.//5266G g_____E____fffffffff OJJJJEJJJNNNNgNNN'>r5   z"customer_special_requirements.json)u   搭配规则u   配方/体系u   包装/标签u   交付/物流u   质检/文件u   价格/账期   其他c                     	 t          t          d          5 } t          j        |           }ddd           n# 1 swxY w Y   n'# t          $ r g t          t                    dcY S w xY wt          |t
                    r|t          t                    dS t          |                    d          t
                    sg |d<   |                    d          st          t                    |d<   |S )u3   加载全部特殊要求（不做权限过滤）。r   r   N)r   
categoriesr   rB  )	r   SPECIAL_REQ_FILEr   r   r   r7   SPECIAL_REQ_CATEGORIESr   r1   r   s     r   _load_special_reqsrE    s2   I"W555 		!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 I I I40F+G+GHHHHHI!T H$/E*F*FGGGaeeGnnd++ '
55 7566,Hs.   A 9A =A  =A !A)(A)c                 2   ddl m } |                                                    d          | d<   t          dz   }t	          |dd          5 }t          j        | |d	d
           ddd           n# 1 swxY w Y   t          j        |t                     | S )uL   原子写回特殊要求文件（先写 tmp 再 replace，防止写坏）。r   r?   %Y-%m-%dT%H:%M:%S
updated_atr!   r"   r   r   Fr   r$   N)	r@   rO   rQ   rC  r   r   r)   r   r*   )r,   r@   r-   r   s       r   _save_special_reqsrI    s    !!!!!!!001DEED
V
#C	c3	)	)	) 9Q	$a88889 9 9 9 9 9 9 9 9 9 9 9 9 9 9Js$%%%Ks   A22A69A6c                 X    t          |           \  }}t          d |D                       S )uE   当前用户可见客户名集合（与客户档案同一口径）。c              3   n   K   | ]0}t          |t                    |                    d           pdV  1dS )r3   r=   Nr   r   r1   r   rF  s     r   r  z*_visible_customer_names.<locals>.<genexpr>  s@      KKz!T7J7JKf#KKKKKKr5   )r?  r   )r=  r@  _vgs      r   _visible_customer_namesrO    s0    "4((JE3KKeKKKKKKr5   c                    t          | t                    rd | D             S t          | t                    r|                     dd                              dd                              dd                              dd          }g }|                    d          D ]D}|                                D ]-}|                                }|r|                    |           .E|S g S )uL   货号字段归一成 list（兼容字符串逗号/顿号/分号分隔）。c                     g | ]D}t          |                                          #t          |                                          ES r   r   r  r   r,  s     r   r   z!_fmt_sku_list.<locals>.<listcomp>  s9    <<<1SVV\\^^<A<<<r5   u   ，,r1     ；;)r   r7   r   r*   rq  r  rS   )ru   rP  outparttoks        r   _fmt_sku_listrZ    s    !T =<<<<<<!S IIeS!!))%55==eSIIQQRUWZ[[GGCLL 	$ 	$Dzz|| $ $iikk $JJsOOO$ 
Ir5   c                    t                      }|                    d          pg }t          |           }|                     d          pd                                }g }|D ]R}t	          |t
                    s|                    d          pd}||vr4|r||k    r=|                    |           Si }	|D ]B}|                    d          pd}|	                    |g                               |           Ci }
|D ]5}|                    d          pd}|
                    |d	          d
z   |
|<   6|t          |          t          |	          t          |	
                                          |
|                    d          pt          t                    t          |          ||dv |                    dd          d
S )uN   返回当前用户可见的特殊要求 + 统计信息（权限过滤后）。r   r&  r'  r   r=   u   未知客户ra  r@  r   r#   rB  )r)  r*  r+  rH  )
r   rK   customer_countcustomers_with_reqby_categoryrB  visible_customersr&  can_manage_allrH  )rE  r1   rO  r  r   r   rS   rR   r   r?  keysr7   rD  )r=  r   r,   r   visr&  rW  r   r  by_cust	cat_countrF  s               r   special_requirements_forre    s   DHHW#E
!$
'
'CHHV'..00D
C  "d## 	VVJ%2S== 	h

2G . .VVJ1>2r""))"----I / /FF:*( }}Q**Q.	!Sg,,$W\\^^44 hh|,,L5K0L0L#C[["<<hh|R00  r5   c                    ddl m } t          |                    d          t                    r|                    d          n|}t          |t                    sdS |                    d          pd                                }|                    d          pd                                }|                    d          pd                                }|sd	S |sd
S t          |           }||vrdS t                      }|                    d          pg }	|                    d          pt          t                    }
|	                                
                    d          }|                    d          pd                                pd}||
vr|
                    |           |
|d<   |r&d}|	D ]4}t          |t                    r|                    d          |k    r|} n5|sdS |                    d          pd|vrdS |                    |||                    d          pd                                |t          |                    d                    |                    d          pd                                |                    d          p|                    d          pd||                     dd          d	           d}nd|	                                
                    d          t          d |	D                       dz   fz  }||||                    d          pd                                |t          |                    d                    |                    d          pd                                d|                     dd          ||d d!}|	                    |           d"}|	|d<   t          |           d#||fS )$u;   新增/更新一条特殊要求。返回 (ok, msg, item)。r   r?   item)Fu   参数错误Nr   r=   contentid)Fu   请选择客户N)Fu   请填写特殊要求内容N)Fu3   无权操作：该客户不在你的客户范围内Nr   rB  rG  ra  r@  N)Fu'   记录不存在（可能已被删除）N)Fu   无权修改该记录Nr  r)  noter  activer3   )	r   ra  r  rh  r)  rj  r  rH  
updated_byu	   已更新z
sr_%s_%03dz%Y%m%d%H%M%Sc                 <    g | ]}t          |t                    |S r   )r   r   rS  s     r   r   z,save_special_requirement.<locals>.<listcomp>  s'    $M$M$M1At9L9L$MQ$M$M$Mr5   r#   u   工作台录入)ri  r   ra  r  rh  r)  rj  r  
created_by
created_atrH  sourceu	   已新增T)r@   r   r1   r   r  rO  rE  r7   rD  rO   rQ   rS   updaterZ  r   rI  )r=  payloadr@   rg  r  rh  ridrb  r,   r   catsrO   catr  r   actionnewids                    r   save_special_requirementrx    s   !!!!!!",W[[-@-@$"G"GT7;;vWDdD!! +**
((:


$"	+	+	-	-Bxx	""(b//11G88D>>R
&
&
(
(C .-- :99
!$
'
'C	}}QQDHHW#E88L!!AT*@%A%AD
,,..
!
!"5
6
6C88J+8
2
2
4
4
@C
$C!\
 ( 	 	B"d## t(;(; 	JIIJJz""(b4477hhw''-24466!$((6"2"233XXf%%+2244hhx((LFJJx,@,@LH((62..

 

 
	 
	 
	  7 7 G G #$M$M$M$M$M N NQR R T T hhw''-24466!$((6"2"233XXf%%+2244((62..'
 
 	VDMtr5   c                    t          |           }t                      }|                    d          pg }t          |          D ]{\  }}t	          |t
                    ra|                    d          |k    rH|                    d          pd|vr dS |                    |           ||d<   t          |            dS |dS )u@   删除一条特殊要求（权限校验）。返回 (ok, msg)。r   ri  r   r=   )Fu$   无权删除该客户的特殊要求)Tu	   已删除)Fu   记录不存在)rO  rE  r1   	enumerater   r   r9   rI  )r=  rs  rb  r,   r   r   r   s          r   delete_special_requirementr{  5  s    
!$
'
'CDHHW#E5!! % %2b$ 	%BFF4LLC$7$7z""(b44DDDIIaLLL!DMt$$$$$$##r5   u	   刘子研rZ  i@] ihS i؛r i`S )
join_monthfull_year_targetpre_join_targetpost_join_targetpre_join_actualc                    |                      dd          }|                      dd          }|                      dd          }|                      dd          }t                               ||          }g }|                    d           |                    d	|d
|d|d|d	           |                    d           |                    d           |                    d           |                    d           |                    d           |                    d           |dv r	 t          t	                                }	n# t
          $ r d}	Y nw xY w|                    d|z             |                    dt          |          |	fz             |                    d           n|                    d           |r|D ]}
|
                     d          p|
                     d          pd}|
                     d          pd}	 t          t          |          dz  d          }n# t
          $ r d}Y nw xY w|                    d|
                     dd          d|d|d           |rw|D ]t}|                    d |                     dd          d|                     dd          d!|                     d"d          d#|                     d$d          d	           u|s|s|                    d%           |                    d&           |                    d'           |                    d           |                    d(           |                    d)           t                               |          }|r|                    d           |                    d*           |                    d+||d,         |d-         dz  fz             |                    d.|d/         dz  |d0         dz  ||d1         dz  fz             |                    d           |                    d2           |                    d3           |                    d4           |                    d5           |                    d6           |                    d7           |                    d8           |                    d9           |                    d:           |                    d;           |                    d<           |                    d           |                    d=           |                    d>           |                    d?           |                    d@           |                    dA           |                    d           |                    dB           |                    dC           |                    dD           |                    dE           |                    d           |                    dF           |                    dG           |                    dH           |                    dI           |                    dJ           |                    dK           |                    dL           |                    dM           |                    dN           dO	                    |          S )Pu;   构建 system prompt（三条铁律 + 可见客户名单）r3   r=   r&  r'  r  r  r   uF   你是宝锐生物（BIORI BIOTECH）销售工作台的智能助手。u   当前用户：u   （角色：u   ，部门：u   ，业务线：r  u$   【铁律一：只答工作问题】u   你只回答销售工作相关问题（客户、订单、KPI业绩、产品、行业动态、市场竞争、竞品分析、销售方法论、工作流程等）。u   严禁回答家庭、个人生活、私人事务、娱乐八卦、政治、宗教信仰等与工作无关的问题。遇到此类问题，统一回复："抱歉，我只能协助销售工作相关的问题～"u*   【铁律二：客户数据权限隔离】r(  r   ue   当前用户是超管（%s），拥有全公司客户数据访问权限，不受客户名单限制。u   可查询对象包括：ERP 重点客户（%d 家）、CRM 客户库（%d 家），以及飞书活动、订单物流、价格档案、客诉、商机、测试反馈等数据源中出现的所有客户。u   对于任何客户，只要系统检索到其真实数据（见下方背景）即可引用并回答；检索不到则明确说明"暂无该客户的相关数据"，严禁编造客户数据。uj   你只能谈论当前用户权限范围内的客户/课题组。以下是该用户可见的客户名单：r1  
sales_2026r  r#   r  u   （归属：u   ，2026销售额：u   万）u   - [课题组] u   ，状态：r  u   ，区域：r-  u   （无可见客户记录）u}   名单之外的客户，一律回复"该客户不在您的权限范围内，无法提供信息"。不得编造客户数据。u   用户上传的文件内容同样受此权限约束：文件中若出现名单之外的客户/人员，不得引用其数据。u2   【铁律三：行业/市场/竞品自由回答】u{   行业趋势、市场规模、竞品动态、产品技术等非客户专属问题，可自由回答，不受权限限制。u;   【当前员工的 KPI 口径（入职拆分，重要）】uD   %s 于 2026 年 %d 月入职。全年（1-12月）目标 %.1f 万。r|  r}  u   其中：入职前（1-7月）目标 %.1f 万，已达成 %.1f 万（这是前任业绩，不计入 %s 本人的达成）；入职后（8-12月）目标 %.1f 万，由本人承担。r~  r  r  u3   【场景路由与回答框架（务必遵守）】uB   根据用户问题类型，采用对应数据源和回答框架：u   1. 产品选型（问"XX检测/项目推什么体系/货号"）→ 检索 数据/pcr_lamp_selection.json 和 数据/product_selling_points.json，命中具体货号 + 一线反馈，不要泛泛而谈。u   2. 产品竞争力（问"XX产品优势/有竞争力吗"）→ 用 FABE 四段式（特征→优势→客户利益→证据）回答，引用 数据/product_selling_points.json 的定量证据；无数据明说"需补测"，严禁编造参数。u   3. 竞品对比（问"和XX比/国产替代"）→ 引用 数据/market_intel/ 最新竞品情报 + selling_points 的 competitors/evidence，给出差异化打法和胜率判断。uo   4. 客户咨询（问"XX客户怎么样"）→ 聚合订单+客诉+商机+活动+竞品五维，一次答全。uw   5. 市场/行业（问"市场规模/趋势"）→ 引用 selling_points 的 market_intel 字段 + 行业报告数据。u   6. 数据查询（问"订单/回款/KPI"）→ 引用 shipment_status / kpi_progress / kpi_dashboard 的真实数字，注明口径。u~   7. 销售打法（问"怎么谈/话术/异议"）→ 引用话术库 + LTC 方法论，给可落地话术和下一步动作。u   通用思考顺序：①识别场景 → ②提取实体（产品/货号/客户/竞品）→ ③权限过滤 → ④检索对应数据源 → ⑤结构化输出。up   涉及产品/竞品问题时，可用工具读取上述 JSON 文件获取卖点、选型、竞品情报数据。u   【回答要求】u*   1. 用中文，简洁专业，条理清晰u    2. 金额统一用"万元"单位u<   3. 涉及客户数据时，只引用上面名单中的信息u0   4. 不确定的数据明确说明，不要编造u$   【高效原则（务必遵守）】u`   优先基于上面的客户名单和背景数据直接回答，不要反复调用工具检索。ub   数据查不到就明确说"暂无相关数据"，严禁为了凑答案反复尝试多个工具。u9   整个回答尽量在 3 步以内完成，避免拖长。u<   【文件输出规则（用户要下载文件时遵守）】um   当用户明确要求生成文件、文档、HTML页面、报告、知识卡、表格等可下载内容时：u   1. 【重要】不要调用 write_file 等工具把文件写到磁盘，也不要提及任何本地文件路径——系统会自动把你输出的内容保存为文件。uo   2. 把完整文件内容（如完整 HTML 代码，不要省略、不要截断）用以下标记包裹输出：u      <<<FILE:文件名.html>>>u      （完整的文件内容；HTML 用纯文字+样式即可，【不要】内嵌 base64 图片——配图位置用文字占位说明，图片由系统/人工后续补充）z   <<<END_FILE>>>uD   3. 标记之外，用 1-2 句简短文字说明生成结果即可。uN   4. 文件名用有意义的中文名，扩展名用 .html/.md/.txt/.csv 等。r3  )
r1   
LINE_LABELrS   r   r  r   roundfloat
JOIN_SPLITr   )r=  r@  rA  r3   r&  r  r  
line_labelLcrm_nrF  sps26s26wr   _jss                   r   _build_system_promptr  K  s   88FBD88FG$$D88FBD88FD!!Dd++J
AHHUVVVHHHTXTXTXZ^Z^Z^`d`d`dfpfpfpqrrrHHRLLLHH3444HH  r  s  s  sHH  a  b  b  bHHRLLLHH9:::	02233EE 	 	 	EEE		x{  	A  	A  	A	  _  cf  gl  cm  cm  ot  bu  u  	v  	v  	v	  S  	T  	T  	T  	T	}~~~ 	j j jUU=))AQUU7^^AreeL)).Q se!3Q77DD    DDDAEERXZ\L]L]L]L]_a_a_acgcgcghiiii 	n n n\]\a\abhjl\m\m\m\mopototu|  A  pB  pB  pB  pB  DE  DI  DI  JR  TV  DW  DW  DW  DW  YZ  Y^  Y^  _g  ik  Yl  Yl  Yl  Yl  m  n  n  n  n 	4W 	4HH2333	  Q  	R  	R  	R	  S  	T  	T  	THHRLLLHHABBBHH  K  L  L  L
..

C
 u		NOOO	W[_adeqartw  yK  uL  OT  uT  [U  U  	V  	V  	V	  J  NQ  Rc  Nd  gl  Nl  nq  rC  nD  GL  nL  NR  TW  Xj  Tk  ns  Ts  Mt  t  	u  	u  	uHHRLLLHHBCCCHHQRRRHH  [  \  \  \HH  E  F  F  FHH  E  F  F  FHH~HH  G  H  H  HHH  V  W  W  WHH  N  O  O  OHH  s  t  t  tHH  A  A  AHHRLLLHH!"""HH9:::HH/000HHKLLLHH?@@@HHRLLLHH3444HHopppHHqrrrHHHIIIHHRLLLHHKLLLHH|}}}HH  {  |  |  |HH~HH+,,,HH  ~      HH !!!HHSTTTHH]^^^99Q<<s$   0E EE H44IIc                  (   	 t           j                            t                    rYt	          t          d          5 } t          j        |           }ddd           n# 1 swxY w Y   t          |t                    r|S n# t          $ r Y nw xY wdg iS )u/   读生成文件索引，返回 {'files': [...]}r   r   Nr   )
r   r   r   
FILE_INDEXr   r   r   r   r   r   r   s     r   _load_file_indexr    s    7>>*%% 	j7333 !qIaLL! ! ! ! ! ! ! ! ! ! ! ! ! ! !!T""    R=r   c                 ,   	 t          j        t          d           t          dz   }t	          |dd          5 }t          j        | |dd	           d d d            n# 1 swxY w Y   t          j        |t                     d S # t          $ r Y d S w xY w)
NTr   r!   r"   r   r   Fr#   r$   )	r   r'   PRIVATE_BASEr  r   r   r)   r*   r   )idxr-   r   s      r   _save_file_indexr    s    
L400006!#sW--- 	<Ic15;;;;	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	<

3
#####   r/   ).htmlz.htmr  .txtz.csvz.jsonr  )u   拜访u   访前r  u	   准备见u   去见u   要见c                 n      sdS t           fdt          D                       sdS t                     S )u2   检测"拜访"意图，返回客户名或空串。r=   c              3       K   | ]}|v V  	d S r  r   )r   r"   rU   s     r   r  z'_detect_visit_intent.<locals>.<genexpr>  s'      33qH}333333r5   )r4  _VISIT_WORDSr  )rU   s   `r   _detect_visit_intentr    sH     r3333l33333 rh'''r5   c                     | pd}dD ]S}|                     |          r<t          |          t          |          z
  dk    r|t          |          d         } nT|p| S )u[   客户简称：去掉常见地名前缀，保留品牌词（如"上海伯杰"→"伯杰"）r=   )u   上海u   北京u   广州u   深圳u   珠海u   杭州u   苏州u   成都u   武汉u   南京u   天津u   重庆u   长沙u   西安u   郑州u   青岛u   厦门u   宁波u   无锡u   佛山r   N)rr  r   )r  rP  prefs      r   _cust_shortr    so    
Au  << 	#a&&3t99"4"9"9#d))**AE9r5   c                 J   ddl }t          j                            t          dd          }t          j                            |          sdS 	 t          |d          5 }|                                }ddd           n# 1 swxY w Y   n# t          $ r Y dS w xY wi }	 t          t          j                            t          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   n# t          $ r Y nw xY w|                    | i           pi }i }	 t          t          j                            t          dd	          d          5 }t          j        |          }ddd           n# 1 swxY w Y   n# t          $ r Y nw xY wt          |t                    r|                    | i           ni }	d}
g }t          |	t                    re|	                                D ]P\  }}t          |t                     st#          d
 |D                       }|
|z  }
|                    ||f           Q|                    d            g }t          j                            t          d          t          j                            t          dd          fD ]}	 t          |d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                     r|n)|                    d|                    dg                     }t)          |           }t+          |          }|D ]d}t          |t                    r#t-          |                    dd                    nt-          |          }|r||v r|                    |           e n# t          $ r Y w xY wt-          |                    dd          pd                                          }|                    dd          pd}|                    dd          p|                    dd          }|                    d          }t)          |           }t+          |          p	| dd         }d                    d |dd         D                       pd}i d|d| d |d!|d"|d#dd$dd%|d&d'|pd(z   d)z   d*|                    d+d          d,|                    d-d          d.d/t5          |          z  d0d1d2d3t5          |          z  d4d5d6d3t          |	t                    rt5          |	          ndz  d7d8|
rd9|
d:z  z  ndd;d<d=d>dddddddddd?d@}|}|                                D ]\  }}|                    ||          }g } |rp|                     dAdB|d         d                             dC          d                                         ddD         z   dE|d         dF         d:z  z  dGdGdHdI           |rA|                     dAdJdK                    dL |dd         D                       dGdMdNdI           |                     dOdPddMddQdI           g }!|r|!                    dRdSdTdU           |!                    dVdSdWdU           dXdYdZd[d\|
rd]|
d:z  z  nd^dZd[d_d`dZd[da|rdbndcdZd[g}"g }#	 t          t          j                            t          dd          d          5 }t          j        |          }$ddd           n# 1 swxY w Y   |$                    | g           }%t9          |%ddG                   D ]\  }&}'t          |'t                    s|#                    de|&dFz   z  |'                    ddf          |'                    dgd          |'                    dgdh          didhddjdkdldmdndkdldmgdo           n# t          $ r Y nw xY w|#sd|rb|#                    dXdp|d         d                             dC          d                                         ddD         dqdidqdqdjdrdsdmgdo           dt|#fdu| fdv|!fdw|"ffD ]9\  }(})|                    dx|(z  |(dyt          j        |)dZz          d{dF          }:|S )|u   生成基础版拜访卡 HTML（后端自动收集本地数据：分级/决策链链接/价格档案/客诉）。
    返回 html 字符串，失败返回 None。r   Nu   工具u   拜访卡模板.htmlr   r   decision_chain_links.json_archivezprice_archive.jsonc              3   l   K   | ]/}t          |t                    |                    d d          V  0dS amtr   NrL  rS  s     r   r  z$_build_visit_card.<locals>.<genexpr>  s=      HH!Jq$4G4GHaeeE1ooHHHHHHr5   c                     | d          S Nr#   r   rU  s    r   r.  z#_build_visit_card.<locals>.<lambda>      AaD5 r5   r/  r  _backup_erp_20260821_111646r   r,   r3   r=   level   类   级   重点客户r   rA   r   r1  c                     g | ]=}|d                               d          d                                          dd         >S r   |N   rq  r  r  s     r   r   z%_build_visit_card.<locals>.<listcomp>  @    HHH1qtzz#q)//11#2#6HHHr5   r   	   待补充u   {{客户简称}}u   {{客户全称}}u   {{客户级别}}u   {{销售员}}u
   {{日期}}u   {{CRM活动数}}u   {{CRM范围}}u   {{今日拜访人}}z{{storage_key}}bp_r   	_visit_v1u   {{决策链URL}}decision_chain_urlu   {{客户链接}}customer_linku   {{KPI1值}}u   %d条u   {{KPI1说明}}u   历史客诉记录u   {{KPI2值}}u   %d个u   {{KPI2说明}}u   在采/在测产品u   {{KPI3值}}u   {{KPI3说明}}u   价格档案SKUu   %.1f万r  u   累计采购额(档案)u   待补充(研发决策)u   待补充(技术对接)u   待补充(采购)uZ   待补充（建议：确认在测项目进展 + 新项目立项时间点 + 客诉闭环）)u   {{KPI4值}}u   {{KPI4说明}}u   {{KR角色名}}u   {{SR1角色名}}u   {{SR2角色名}}u   {{项目1}}u   {{项目2}}u   {{项目3}}u   {{技术员1}}u   {{线1}}u   {{技术员2}}u   {{线2}}u   {{技术员3}}u   {{线3}}u   {{拜访目标占位}}p0u   主采产品：r  r  u@   累计采购 %.1f 万，确认在测/在采状态与后续用量r#   r  u0   确认当前用量、性能反馈、后续需求)priorityrC  contextimpactfeasibilitystrategyu   客诉闭环rU  c                 "    g | ]}|d d         S )iNr   rM  s     r   r   z%_build_visit_card.<locals>.<listcomp>9  s     -N-N-N!ag-N-N-Nr5   rn  u'   主动汇报处理方案，重建信任p1u   新项目立项u!   摸清产品管线，提前卡位u   客诉影响信任u   中u   主动闭环历史客诉)r  probcounteru   测试周期拉长u$   设定里程碑，每周同步进度c1u   带最新测试/采购数据F)ri  r   donec2u    带价格档案(累计 %.1f 万)u   带价格档案c3u   确认决策链人物名单c4u   准备客诉闭环方案u   准备新项目推荐方案opps_by_customer.jsonzc%d   商机r  	   待确认testingu   宝锐2   z50%)r3   pctbaru   竞品(待确认))ri  r3   r   phasephaseClsr  r  	suppliersu   主采产品u   在采d   z100%rb  topicsrisks
prepChecksz%s: [],z: )r%   rT  )timer   r   r   r  r   r   r  r   r(   r   r   r1   r   r   r   r7   r  rS   rp  r  r  r   r  r*   rQ   r   rq  rz  r  )*	cust_namer=  r  tpl_pathr   tpldcllinkpaprices	total_amttopr  ru   r  
complaintscpathcompcitems_core_shortr,  nm
_raw_levellevel_shortr   r`   r  shorttop3phhtmlr  r  r  rb  	_opp_data_olist_i_or3   r,   s*                                             r   _build_visit_cardr    s    KKKw||D(,BCCH7>>(## t(W--- 	&&((C	 	 	 	 	 	 	 	 	 	 	 	 	 	 	   tt C"',,x)DEEPWXXX 	\])A,,C	 	 	 	 	 	 	 	 	 	 	 	 	 	 	   779b!!'RD	B"',,x5IJJU\]]] 	ab1B	 	 	 	 	 	 	 	 	 	 	 	 	 	 	   &0T&:&:BRVVIr"""FI
C&$ !LLNN 	! 	!DAqa&& HHqHHHHHCIJJ3x    HHH!!!J',,x)>??',,x)FH]^^`  	eg... $!y||$ $ $ $ $ $ $ $ $ $ $ $ $ $ $'d33`TT'488TZ\^K_K_9`9`Fy))E ''F * */9!T/B/BNSvr**+++A *fll%%b)))E 	 	 	H	 TXXgr**0b117799J$$UE22DnKHHWb!!9TXXfb%9%9EMM*%%Ei  D.2A2E::HHBQBHHHIIX[D
E
I
 	K
 		

 	e
 	K
 	
 	u
 	5EOV4{B
 	DHH%92>>
 	DHH_b99
 	wZ0
 3CDX
 	wS)
 ,<=R
 	wFD1I1I"P#f+++qQ
 Tdev
 =FV	Y%677;  kD45/";{%;%;%;"~/
 
 
B2 D

 " "1||Aq!!F
 w42Cc!fQiooVYFZFZ[\F]FcFcFeFefigifiFj2j"dhklmhnophqtyhy"z!"1Btv v 	w 	w 	w  n4."'**-N-Nz"1"~-N-N-N"O"O!"1Bkm m 	n 	n 	n MMt.?Kq>ac c d d dE l3UOijjkkk	LL/Kqrrsss;UKKYby?9uCTUUhy  DI  	J  	J:EJJ:h77Khrwxx	J K"',,x)@AAGTTT 	%XY	!I	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	%y"--rr
++ 	 	FBb$'' rAv&vx00vvh+++66%%*'/5IITgpr  |A  LB  LB  C	  	  	 	 	 		     3 s1vays7K7KA7N7T7T7V7VWZXZWZ7[9Ya#+CGGH
 
 	 	 	
 &{3h5G'SXIY\hjt[uv k k
d||I,444D_dAeAeAeAeAe.fhijjKs  B A?3B ?BB BB 
BB4D  C4(D  4C88D  ;C8<D   
DD+5F  F5F FF F	F 
FF0N5K"N5"K&	&N5)K&	*C	N55
OO4_% \&_% &\**_% -\*.B6_% %
_21_2competitor_intel.jsonc                     | pd                                 }|sdS t                                          D ]8\  }}|D ]0}|                                |                                v r|c c S 19|S )uJ   竞对品牌归一化：别名 → 标准名。未知品牌原样返回。r=   )r  r9  r   r$  )brandr   mainrI  r   s        r   _normalize_competitor_brandr  l  s    	"A r+1133  g 	 	AwwyyAGGII%% &	 Hr5   c                      	 t          t          d          5 } t          j        |           }d d d            n# 1 swxY w Y   t	          |t
                    r|S n# t          $ r Y nw xY wdg iS )Nr   r   entries)r   COMPETITOR_INTEL_FILEr   r   r   r   r   r   s     r   _load_competitor_intelr  x  s    ''::: 	a	!A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	a 	H	   r?s.   A 9A =A  =A 
A)(A)c                 .   ddl }t          |                     d          pd          }|                     d          pd                                }|                     d          pd                                }|                     d          pd                                }|                     d          pd                                }|r|s|s|sd	S t	                      }|                    d
g           }	|||f}
|	D ]G}|                    d          |                    d          |                    d          f|
k    r dS H|	                    ||||||                    dd          |                    d          d           |	|d
<   	 t          t          dd          5 }t          j
        ||dd           ddd           n# 1 swxY w Y   ddt          |	          z  fS # t          $ r}dt          |          fcY d}~S d}~ww xY w)uU   写入一条竞对情报（品牌归一化 + 去重 + 落库）。返回 (ok, msg)。r   Nr  r=   r   performancefeedbackr   )FuG   竞对信息不完整：至少要有品牌 + 货号/性能/评价之一r  )Tu$   已存在相同竞对情报，跳过r3   %Y-%m-%d %H:%M:%S)r  r   r  r  r   r   r  r"   r   r   Fr#   r$   Tu!   竞对情报已沉淀（%d 条）)r  r  r1   r  r  rS   rQ   r   r  r   r)   r   r   r   )rr  r=  r  r  r   r  r  r   r  r  dup_keyr   r   s                r   _add_competitor_intelr    s   KKK'G(<(<(BCCE;;u#
*
*
,
,C;;}--3::<<KJ''-24466HJ''-24466H ` `[ ` `__
 
"
"Cggi$$Gc8$G @ @EE'NNAEE%LL!%%
*;*;<GG??? HNNs;Htxx';';T]]SfEgEg     C	N'w??? 	<1Ic15;;;;	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	<83w<<GGG   c!ff}sB   G/ +GG/ GG/ GG/ /
H9H	HHc                    t          |           \  }}|                     d          pddv }|r	 t          t          j                            t          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   d |                    d	g           D             }n# t          $ r g }Y nw xY wd
 |D             }d |D             }i }	 t          t          j                            t          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   |
                                D ]-\  }	}
|	|v r$t          |
t                    rd |
D             ||	<   .n# t          $ r Y nw xY wi }	 t          t          j                            t          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   |                    di           }t          |t                    rD|
                                D ]/\  }	}|	|v r&t          |t                    rd |D             ng ||	<   0n# t          $ r Y nw xY wg }	 t          t          j                            t          d          d          5 }t          j        |          }ddd           n# 1 swxY w Y   n# t          $ r Y nw xY w||||dS )uN   活动录入下拉选项（权限过滤）：客户/商机/联系人/货号。r&  r'  r(  r  r   r   Nc                 d    g | ]-}|                     d           |                     d d          .S r  ri  rM  s     r   r   z%_activity_options.<locals>.<listcomp>  s;    aaa1STSXSXY_S`S`aQUU62..aaar5   r'  c                 d    g | ]-}|                     d           |                     d d          .S r  ri  rM  s     r   r   z%_activity_options.<locals>.<listcomp>  s5    KKKqQUU6]]Kvr**KKKr5   c                     g | ]}||S r   r   )r   ns     r   r   z%_activity_options.<locals>.<listcomp>  s    33313Q333r5   r  c                     g | ]Y}t          |t                    |                    d d          |                    dd          |                    dd          dZS )r3   r=   r  url)r3   r  r  rL  r  s     r   r   z%_activity_options.<locals>.<listcomp>  sw     C C C !jD.A.ACQUU62%6%6!%%RTBUBU^_^c^cdikm^n^noo C C Cr5   zcustomer_contacts.jsoncontactsc                     g | ]D}t          |                                          #t          |                                          ES r   rR  r  s     r   r   z%_activity_options.<locals>.<listcomp>  s9    #T#T#TqSVV\\^^#TCFFLLNN#T#T#Tr5   zsku_list.json)r'  r  r  r)  )r?  r1   r   r   r   r   r(   r   r   r   r   r   r7   r   )r=  r@  rA  rB  r   mcvisible_namesr  opp_datar  olistr  ccclistplistr)  s                   r   _activity_optionsr    s9   &t,,NE7  +G?H L	bgll8-ABBWUUU "YZYq\\" " " " " " " " " " " " " " "aa{B8O8OaaaMM 	 	 	MMM	 LKEKKK33333MD"',,x)@AAGTTT 	$XYy||H	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$!)) 	C 	CIB]""z%'>'>"C C%*C C CR	C     H	"',,x)ABBWUUU 	YZ1B	 	 	 	 	 	 	 	 	 	 	 	 	 	 	z2&&eT"" 	x"[[]] x x	E&&XbchjnXoXo#w#T#TE#T#T#T#TuwHRL    D"',,x99GLLL 	 PQ9Q<<D	  	  	  	  	  	  	  	  	  	  	  	  	  	  	    &(TXYYYs   4B0 #B8B0 BB0 B#B0 0B?>B?4F  D2&F  2D66F  9D6:AF   
FF4I# G(I# (G,,I# /G,0A2I# #
I0/I064K *K?K KK KK 
K$#K$c                     ddl ddlddlg fd}                    d|| j                  }                    dd|                                          }|fS )u   从 answer 里提取 <<<FILE:名>>>...<<<END_FILE>>> 标记，保存到私有目录并写索引。

    返回 (clean_text, files)，files = [{'fid': ..., 'name': ...}]
    r   Nc           
         |                      d          pd                                }|                      d          pd                                }|r|sdS                     dd|                              d          d d         }|sdS t          j                            |          d                                         }|t          vrd}	 t          j        t          d	
           
                    d          dz                                   j        d d         z   }t          j                            t          ||z             }t          |dd          5 }|                    |           d d d            n# 1 swxY w Y   t          j                            |          }t#                      }|                    dg                               |||pd
                    d          |dd           t)          |           	 ddlm}	  |	||           n# t.          $ r Y nw xY w
                    ||d           n# t.          $ r Y nw xY wdS )Nr#   r=   r   z[\\/:*?"<>|\r\n]+r   z._rE   r  Tr   z%Y%m%d-%H%M%SrT  r2  r"   r   r   r   u   未知r  F)fidr3   extr   r  size	is_publicr   )archive_one)r  r3   )groupr  r   r   r   splitextr$  _ALLOWED_EXTr'   FILE_DIRrQ   uuid4hexr   r   writegetsizer  rR   rS   r     文书归档员r  r   )mr3   rh  r  r  r  r   r  r  r  r   r   r   r  uuids             r   _savez_extract_files.<locals>._save  s   

 b''))771::#**,, 	7 	2vv*C66<<TBB3B3G 	2gt$$Q'--//l""C	K40000--00369I"1"9MMChc	22Bb#000 !A   ! ! ! ! ! ! ! ! ! ! ! ! ! ! !7??2&&D"$$CNN7B''..D*(DMMBU4V4V50 0   
 S!!!777777Bt,,,,   LLd334444 	 	 	D	rs\   BI "F8I FI FBI H& %I &
H30I 2H33I 
IIz+<<<FILE:([^>]+)>>>\s*(.*?)\s*<<<END_FILE>>>)flagsz\n{3,}ro  )r   r  r"  r   DOTALLr  )answerr   r#  cleanr   r   r  r"  s    `  @@@@r   _extract_filesr(    s    
 IIIKKKKKKE# # # # # # # # #J FFA5&XZXaFbbEFF9fe,,2244E%<r5   r'  c                    t                      }|dv }g }|                    dg           D ]}|s|                    d          | k    r|                    |                    d          |                    d          |                    d          |                    d          |                    d          t          |                    d                    d	           |                    d
 d           |S )u9   按权限列出文件：admin/gm 全见，否则仅自己r(  r   r   r  r3   r  r  r  )r  r3   r   r  r  r  c                 .    |                      dd          S )Nr  r=   ri  rU  s    r   r.  z_list_files.<locals>.<lambda>  s    155,, r5   TrV  )r  r1   rS   boolrp  )viewervroler  rB  rW  r   s         r   _list_filesr.    s    


C'H
CWWWb!!   	AEE'NNf44

55<<vwEE&MM155==tAEER]L^L^G_G_
 
 	 	 	 	 HH,,dH;;;Jr5   c           	         t                      }|dv }|                    dg           D ]}|                    d          | k    r|s|                    d          |k    r dS t          j                            t
          | |                    dd          z             }t          j                            |          s dS ||                    d	d
          fc S dS )u5   鉴权取文件：返回 (path, name) 或 (None, err)r(  r   r  r   )Nu   无权访问该文件r  r  )Nu   文件不存在r3   file)r  r1   r   r   r   r  r   )r  r,  r-  r  rB  r   r  s          r   _get_file_pathr1    s    


C'HWWWb!! ) )55<<3 	1AEE'NNf44000W\\(C!%%w*?*?$?@@w~~b!! 	+***155((((((""r5   Tc                     t                      }|                    dg           D ]?}|                    d          | k    r$t          |          |d<   t          |            dS @dS )u4   标记/取消公共资源（权限在上层校验）r   r  r  TF)r  r1   r+  r  )r  r  r  r   s       r   _mark_publicr3  #  so    


CWWWb!!  55<<3!)__AkNS!!!44  5r5      c           	      J   ddl }t                      }|                                 | dz  z
  }g }d}|                    dg           D ]F}	 |                    |                    |                    dd          d                    }n$# t
          $ r |                                 }Y nw xY w|                    d          s||k    r|                    |           	 t          j        	                    t          |                    d	d          |                    d
d          z             }t          j                            |          rt          j        |           |dz  }"# t
          $ r |                    |           Y Dw xY w||d<   t          |           |S )uH   清理超过 days 天、且未标记公共的文件，返回删除数量r   NiQ r   r  r=   r  r  r  r  r  r#   )r  r  r1   mktimestrptimer   rS   r   r   r   r  r   remover  )	days_tr  cutoffkeepremovedr   rF   r  s	            r   _cleanup_expiredr>  .  s   


CWWYY%FDGWWWb!!  	2;;quuVR'8'8:MNNOOBB 	 	 	BBB	55 	fKKNNN	heR(8(8155;P;P(PQQBw~~b!! 	"qLGG 	 	 	KKNNNNN	CLSNs%   =B		B*)B*BE))FF   c                    t          |           }|sdS g }t          |          |k    r|d|          ng }|dd         D ]}t          |t                    s|                    d          dk    rdnd}|                    d          pd                                                    d	d
          }|su|                    |d|dd                    |sdS dd	                    |          z   S )u   读该用户历史对话，返回压缩的『记忆』文本（取超出当前上下文的部分，供跨会话连贯）。

    exclude_recent：排除最近 N 条（这些已在当前 messages 里），注入更早的历史。
    r=   Nr&  r=  u   用户u   助手rh  r3  r   r_  r^  u   【该用户更早的对话记忆（帮助保持上下文连贯，回答时自然引用关键信息，勿逐字复述历史原文）】
)	r4   r   r   r   r1   r  r*   rS   r   )r3   exclude_recenthistrL  poolr!  r&  rh  s           r   _load_memoryrE  I  sA   
 D rE%(YY%?%?4 . !!RD#$$Z 8 8!T"" 	55==F22xx55##)r0022::4EE 	$$$67777 r W  Z^  Zc  Zc  di  Zj  Zj  j  jr5   c                 $   t                      }d|v rdd|d         z   fS |                    d          sdS |d         d| dg|z   d	d
did}t          j                            d|d         z  t          j        |                              d          dd|d         z  dd          }t          j                            |d          5 }t          j	        |
                                          }ddd           n# 1 swxY w Y   |                    di g          d                             di           }|                    d          pd                                }|s)|                    d          pd                                }|dfS )uU   调用 DeepSeek API（thinking 必须 disabled，否则答案在 reasoning_content）r   Nu   DeepSeek 凭证读取失败：rz   )Nu   DeepSeek 凭证未配置rH   systemr&  rh  i  r  disabled)rH   r:   
max_tokensthinking%s/chat/completionsr{   r   r  	Bearer %sr  Authorizationr  r  r	  r  choicesr   messagerh  r=   reasoning_content)r   r1   r  r  r  r   r  r  r  r  r  r  	system_promptr:   r   bodyr  rj  r   msgrh  s	            r   _call_deepseekrW  `  s   


C#~~5GDDD779 0//W&=AABXMZ(	 D .
 
 J/Z$$W--!3kTWXaTbFbcc	 !  C 
		S		1	1 !QJqvvxx  ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
%%	B4
 
 
#
'
'	2
6
6Cwwy!!'R..00G ?77.//52<<>>D=s   'C>>DDc                    t           j                            d          pd}dd| dg|z   dd}t          j                            dt          j        |                              d	          d
d|z  dd          }	 t          j        	                    |d          5 }t          j
        |                                          }ddd           n# 1 swxY w Y   |                    d          pi }|                    di g          d                             di           }|                    d          pd                                }	|	s)|                    d          pd                                }	|	sdd|fS |	d|fS # t          $ rS}
t          |
          }d|                                v sd|                                v rY d}
~
dS dd|
z  dfcY d}
~
S d}
~
ww xY w)uL   调用 Hermes API Server（OpenAI 兼容，带持久记忆 + 只读工具）API_SERVER_KEYnas-file-share-token-2026hermes-agentrG  rH  >  )rH   r:   rJ  )http://127.0.0.1:8642/v1/chat/completionsr   r  rM  rN  r  r     r  NrT   rP  r   rQ  rh  r=   rR  u   Hermes 返回空回答z	timed outr  )u   （该问题处理超时了。请换成更具体的问题，比如具体客户名、订单编号，或「XX客户最近的订单/回款情况」，我能更快回答。）NNu   Hermes 调用失败：%s)r   r  r1   r  r  r  r   r  r  r  r  r  r  r   r   r$  )rT  r:   tokenrU  r  rj  r   rT   rV  rh  r   _ms               r   _call_hermesra  |  s^   JNN+,,K0KE&=AABXM D
 .
 
 3Z$$W--!3kTYFYZZ	 !  C:^##C#55 	%
16688$$A	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	%g$"eeIt$$Q'++Ir::779%%+2244 	Cww2339r@@BBG 	91588e## : : :VV"((**$$	RXXZZ(?(? M  M  M  M  M  M/!3T9999999	:sO   >!F 'CF CF CB%F  F 
G";GGG"G"c              #     K   t           j                            d          pd}dd| dg|z   ddd}t          j                            d	t          j        |                              d
          dd|z  dd          }t          j        	                    |d          5 }|D ]7}|
                    d
d                                          }|                    d          sA|dd                                         }	|	dk    r n	 t          j        |	          }
n# t          $ r Y w xY w|
                    d          pg }|r|d                             di           ni }t          |t                     r|                    d          pdnd}|r|V  |
                    d          }|r*t          |t                     r|                    |           9ddd           dS # 1 swxY w Y   dS )ua   流式调用 Hermes API Server（SSE），逐段 yield delta 文本；usage 写入 usage_out dictrY  rZ  r[  rG  rH  r\  T)rH   r:   rJ  streamr]  r   r  rM  rN  r  r  ,  r  ignoredata:r  N[DONE]rP  r   deltarh  r=   rT   )r   r  r1   r  r  r  r   r  r  r  r  r  rr  r  r   r   r   rq  )rT  r:   	usage_outr_  rU  r  rj  rawr  r,   r   rP  chrh  us                  r   _call_hermes_streamrm    se     JNN+,,K0KE&=AABXM	 D .
 
 3Z$$W--!3kTYFYZZ	 !  C 
		S		1	1 $Q 	$ 	$C::gx006688D??7++ 8>>##DxJt$$   eeI&&,"G07?,,,RB3=b$3G3GOrvvi((.BRG gA $Z	400 $  ###'$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $s8   !A(G*
DG*
D,)G*+D,,B1G**G.1G.c                     t                      }d|v rdd|d         z   fS |                    d          sdS |d         d| dg|z   d	d
d}t          j                            d|d         z  t          j        |                              d          dd|d         z  dd          }t          j                            |d          5 }t          j	        |
                                          }ddd           n# 1 swxY w Y   |                    di g          d                             di           }|                    d          pd                                }|s)|                    d          pd                                }|dfS )u   调用智谱 GLM 5.3 Flash（直连 open.bigmodel.cn）。

    GLM 5.3 Flash 是 thinking 模型：真实答案在 content，思考过程在 reasoning_content。
    只取 content（reasoning 是英文思考痕迹，不进正文）。
    r   N   GLM 凭证读取失败：rz   )N   GLM 凭证未配置rH   rG  rH  r\  333333?)rH   r:   rJ  temperaturerL  r{   r   r  rM  rN  r  r  r	  r  rP  r   rQ  rh  r=   rR  )r   r1   r  r  r  r   r  r  r  r  r  r  rS  s	            r   	_call_glmrs    s    //C#~~03w<???779 +**W&=AABXM	 D .
 
 J/Z$$W--!3kTWXaTbFbcc	 !  C 
		S		1	1 !QJqvvxx  ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
%%	B4
 
 
#
'
'	2
6
6Cwwy!!'R..00G ?77.//52<<>>D=s   	'C<<D D c              #   t  K   t                      }d|v rt          d|d         z             |                    d          st          d          |d         d| dg|z   dd	d
d}t          j                            d|d         z  t          j        |                              d          dd|d         z  dd          }t          j        	                    |d          5 }|D ]7}|
                    dd                                          }|                    d          sA|dd                                         }	|	dk    r n	 t          j        |	          }
n# t          $ r Y w xY w|
                    d          pg }|r|d                             di           ni }t          |t                     r|                    d          pdnd}|r|V  |
                    d           }|r*t          |t                     r|                    |           9ddd           dS # 1 swxY w Y   dS )!u  流式调用智谱 GLM 5.3 Flash（SSE）。

    只 yield delta.content（真实答案），跳过 delta.reasoning_content（思考 token，不进正文、不占前端显示）。
    配置缺失时抛异常，由 handle_stream 捕获转 error 事件。
    r   ro  rz   rp  rH   rG  rH  r\  rq  T)rH   r:   rJ  rr  rc  rL  r{   r   r  rM  rN  r  r  rd  r  re  rf  r  Nrg  rP  r   rh  rh  r=   rT   )r   RuntimeErrorr1   r  r  r  r   r  r  r  r  r  rr  r  r   r   r   rq  )rT  r:   ri  r   rU  r  rj  rj  r  r,   r   rP  rk  rh  rl  s                  r   _call_glm_streamrv    s      //C#~~6WEFFF779 20111W&=AABXM D .
 
 J/Z$$W--!3kTWXaTbFbcc	 !  C 
		S		1	1 $Q 	$ 	$C::gx006688D??7++ 8>>##DxJt$$   eeI&&,"G07?,,,RB3=b$3G3GOrvvi((.BRG gA $Z	400 $  ###'$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $s8   $A(H-E"!H-"
E/,H-.E//B1H--H14H1c                      t           j                            dd                                                                          dvS )uV   模型路由总开关：AI_MODEL_ROUTING=off/0 时退回全 DeepSeek（一键回退）AI_MODEL_ROUTINGon)0offfalseno)r   r  r1   r  r$  r   r5   r   _routing_enabledr~    s7    :>>,d3399;;AACCKfffr5   c                     |rdS |rdS | pd                                 sdS d}t          fd|D                       rdS dS )uh   复杂度判断：文件上传 / 四步闭环模式 / 深度分析意图词 → 复杂（走 DeepSeek）Tr=   F)u   分析u   对比u   报告u   生成u   策略u   方案u   复盘r  360u   全景u   趋势u   预测u   订单u   回款kpiu   达成u   业绩u   金额u   账期u   应收c              3   D   K   | ]}|                                 v V  d S r  r#  )r   r"   rL   s     r   r  z_is_complex.<locals>.<genexpr>  s/      
.
.a1		>
.
.
.
.
.
.r5   )r  r4  )rU   r   step
deep_wordsrL   s       @r   _is_complexr    sv     t t	R  A u_J

.
.
.
.:
.
.
... t5r5   c                     t                      sdS |pddv }|dk    rdS |dk    r'|st          |          rdS ddt          |          z  fS t          | ||          r|st          |          rdS dS dS )u+  决定本次用哪个模型，返回 (target_model, fallback_notice)。

    target_model ∈ {'glm', 'deepseek'}；fallback_notice 非空表示发生了降级（DeepSeek 额度用尽）。
    规则：
      - pref='glm'       → 直接 GLM（无限流）
      - pref='deepseek'  → DeepSeek（admin/gm 豁免限流；其余每人每天 DEEPSEEK_DAILY_LIMIT 次，超限降 GLM）
      - pref='auto'      → 复杂走 DeepSeek（同额度约束），简单走 GLM
      - 路由开关 off      → 一律 DeepSeek（一键回退，不限额）
    )ry   r=   r'  r(  glm)r  r=   ry   u]   DeepSeek 今日额度已用完（每人每天 %d 次），已自动改用 GLM 快速模型。)r~  rf   rd   r  )rU   r   r  
model_prefr3   r&  rB  s          r   _route_modelr  $  s      ~O3HUyZ 	"066 	"!>u  yF  GK  yL  yL  L  L  	L8UD))  	"066 	"!>y9r5   c           	          ddl }|                    t          j        |           dd          }g }|j        D ]}g }|                    d          D ]Z}d |D             }t          |          r(|                    d                    |                     t          |          dk    r n[|r3|                    d	|j
        d
d                    |                     d                    |          dd         S )u2   xlsx/xlsm → 文本（前 200 行，tab 分隔）r   NT)	read_only	data_only)values_onlyc                 4    g | ]}|dnt          |          S )Nr=   )r   rM  s     r   r   z _parse_excel.<locals>.<listcomp>G  s&    >>>Q19RR#a&&>>>r5   	r^  z[Sheet: z]
r3  ro  p  )openpyxlload_workbookioBytesIO
worksheets	iter_rowsr4  rS   r   r   r  )rj  r  wbrt  wsrowsrowcellss           r   _parse_excelr  ?  s   OOO			
344		P	PBEm 	J 	J<<D<11 	 	C>>#>>>E5zz .DIIe,,---4yyC   	JLLLbhhh		$HIIIKK&&r5   c                 "   ddl }|                    | d          }g }t          |          D ]\  }}|dk    r n|                                pd                                }t          |          dk     rg|dk     ra	 |                    d	
          }t          |                    d          d          pd                                }n# t          $ r d}Y nw xY w|r|
                    |           d                    |          dd         S )uM   PDF → 文本（前 20 页；扫描版无文字层时前 3 页 OCR 回退）r   Npdf)rc  filetyperR  r=   r4  r   x   )dpipngr3  r  )r  r   rz  r  r  r   
get_pixmap
_parse_ocrtobytesr   rS   r   )rj  r  r  rt  r   pager  pixs           r   
_parse_pdfr  Q  s!   NNN
,,cE,
2
2CES>>  477E}}$"++--s88b==QUUoo#o..!#++e"4"4e<<BIIKK    	LL99UETE""s   <AC

CCc                    t           j                            |          d         pd}t          j        |          \  }}	 t          j        |d          5 }|                    |            ddd           n# 1 swxY w Y   t          j        dddd	|gd
d          }|j	        pd
                    dd          dd         	 t          j        |           S # t          $ r Y S w xY w# t          $ r* Y 	 t          j        |           dS # t          $ r Y dS w xY ww xY w# 	 t          j        |           w # t          $ r Y w w xY wxY w)u8   docx/doc/rtf/txt → 文本（textutil，macOS 自带）r#   r  suffixr  Ntextutilz-convertr  z-stdoutTr4  capture_outputr  r5   r   re  errorsr  r=   )r   r   r  tempfilemkstempfdopenr  
subprocessrunstdoutr  unlinkr   )rj  r3   r  fdr-   r   rj  s          r   _parse_via_textutilr  f  s   Wd##A&0&Ff---GBYr4   	AGGCLLL	 	 	 	 	 	 	 	 	 	 	 	 	 	 	NJ
E9cJ*.< < <C'''AA%4%H	IcNNNN 	 	 	D	    	IcNNNNN 	 	 	DD		IcNNNN 	 	 	D	s   C* A8,C* 8A<<C* ?A< AC* C
C'&C'*
D4D! 6D 
DDDD! !E#D87E8
EEEEc                 N   t          j        d|z             \  }}	 t          j        |d          5 }|                    |            ddd           n# 1 swxY w Y   t          j        d|dddgd	d
          }|j        pd                    dd          dd         	 t          j	        |           S # t          $ r Y S w xY w# t          $ r* Y 	 t          j	        |           dS # t          $ r Y dS w xY ww xY w# 	 t          j	        |           w # t          $ r Y w w xY wxY w)u.   图片 → 文本（tesseract，chi_sim+eng）.r  r  N	tesseractr  z-lzchi_sim+engTr   r  r5   r   re  r  r  r=   )r  r  r   r  r  r  r  r  r  r  r   )rj  r  r  r-   r   rj  s         r   r  r  y  s   cCi000GBYr4   	AGGCLLL	 	 	 	 	 	 	 	 	 	 	 	 	 	 	NKhmL*.< < <C'''AA%4%H	IcNNNN 	 	 	D	    	IcNNNNN 	 	 	DD		IcNNNN 	 	 	D	s   C AC AC AAC !B66
CC
C:C= C( (
C65C69C::C= =D$?DD$
D!D$ D!!D$c                 r   | pd} d| v r.|                      dd          d                                         nd}d}t          |t                    r0	 t	          j        |          }nE# t          $ r |dd         cY S w xY wt          |t          t          f          rt          |          }|dS 	 |dv rt          |          S |dk    r|
                    d	d
          dd         S |dv rt          ||           S |dk    rt          |          S |dv rt          ||          S n# t          $ r Y dS w xY wdS )uK   按扩展名分派解析上传文件，返回文本（失败返回空串）r=   r  r#   rr   Nr  )xlsxxlsmcsvr   re  r  )docxr  rtfr  r  )r  jpgjpeggifwebpbmp)rsplitr$  r   r   base64	b64decoder   bytes	bytearrayr  r  r  r  r  )r3   r,   r  rj  s       r   _parse_uploadr    s   :2D-0D[[$++c1

b
!
'
'
)
)
)bC
C$ 	"4((CC 	 	 	;		D5),	-	- Dkk
{r"""$$$%<<::gh:77>>///&sD111%<<c??">>>c3''' ?   rr2s<   A& &A=<A=0D& $D& (D& <D& D& &
D43D4u   

【当前步骤：🔍 分析】你现在的任务是帮销售做"访前分析"：主动给出客户画像、痛点、竞品、机会，并列出"该问客户什么"。u   

【当前步骤：📢 传递】你现在的任务是帮销售"说对话"：给出场景话术、价值主张、异议处理、竞品对比数据。u   

【当前步骤：✅ 跟进】你现在的任务是帮销售"推落地"：把结论拆解成行动项（负责人/截止日），给出协同推进建议。u   

【当前步骤：📖 沉淀】你现在的任务是帮销售"沉淀经验"：提炼成败原因、关键动作、可复用打法，引导回写知识库。)analyzedeliverfollowreviewc                    |                      d          pd                                }|                      d          pi }|                      d          pg }|                      d          pg }|s|s	|sdddd	fS |                     d
          sdddd	fS 	 t          |          \  }}t          |||          }|                      d          pd                                }|t          v r|t          |         z  }|}	|	st          |          D ]}
t          |
t                    rm|
                     d          dk    rT|
                     d          pd                                r+|
                     d          pd                                }	 nt          |	          }|rt          ||          }|rddl
}t          t          |                    p	|dd         d|                    d          d}d|d|d}t          ||                     d
d                    \  }}dd||t          |          t          |          g dgdfS t!          |	|||                     d          pddv           }t#          |	|                     dd                     }t%          |          }|r|r|d!z   |z   n|}t'          |	|||                     d          pddv           }|r|r|d!z   |z   n|}|r|d!|z   d"z   z  }t)          |                     d
d                    }|r|d!|z   z  }d}g }t          |t*                    r|ng D ]v}t          |t                    s|                     d
          pd}t-          ||                     d#          pd          }|r |                    |           |d$|d%|z  }wg }|d&d         D ]}
t          |
t                    r|
                     d          nd}t          |
t                    r)|
                     d          pd                                nd}|d'v r|r|                    ||d(           |r?d)|z   }|r|                    d|d!z   |z   d(           n3|                    d|d(           n|r|                    d|d(           |                      d*          pd+                                                                } t3          |	||| |                     d
d          |                     dd                    \  }!}"|!d,k    r8t5          |                     d
d                     t7          ||          \  }#}$}%nt9          ||          \  }#}$d}%|$rd-d|$d	fS t          |#|                     d
d                    \  }&}'t;          |                     d
d          |%||!           g }(|D ]})|(                    d.|)z              |r,|r|(                    d/           |(                    d0           |r|(                    d1           |r%|(                    d2t          |          z             |r%|(                    d3t          |          z             |(s|(                    d4           dd|&|'t          |          t          |          ||(dfS # t<          $ r}*d-dt?          |*          d	fcY d}*~*S d}*~*ww xY w)5u4   处理 /api/chat 请求，返回 (status_code, dict)rU   r=   r=  r:   r   i  Fquestion required)okr   r3   i     未登录，无法回答r  r&  rh  r   Nr      _拜访卡_%Y%m%dr  <<<FILE:>>>
   
<<<END_FILE>>>

✅ 已生成拜访卡（含分级、决策链链接、价格档案、客诉）。决策链人物、竞品份额等深度信息标了「待补充」，可继续问我补充。r^  T   拜访卡自动生成)r  r&  r   r_  visible_groupsparsed_filessourcesr'  r(  r  r   ro     

（以上是系统检索到的飞书活动、产品、知识库背景，请结合这些信息回答用户问题，不要回复"无法访问飞书信息"。）r,      

=== 文件： ===
r=  	assistantrH  o   我上传了文件，请基于文件内容回答问题（文件中涉及客户数据时遵守权限约束）。rH   autory   i     上传文件：   客户360全景   飞书活动+产品背景   IMA知识库   客户数据（%d 条可见）!   课题组数据（%d 条可见）   AI 通用知识) r1   r  r?  r  STEP_PROMPTSreversedr   r   r  r  r  r  r  rQ   r(  r   r  r  r+  r  rE  r7   r  rS   r$  r  rh   ra  rs  rX   r   r   )+rr  rU   r=  r:   r   r@  rA  
sys_promptr  _q_for_searchr!  
visit_cust_visit_htmlr:  _fname_full_clean_files
backgroundr  kb_hitcust360_mem
upload_ctxupload_namesr   fnameparsedhistoryr&  rh  up_mp_tm_fnr&  errrT   clean_answer	files_outr  r  r   s+                                              r   handler    sO   J''-24466H;;v$"D{{:&&,"HKK  &BE @H @U @5+>????88F G5+EFFFFc3*400w)$w??
F##)r0022<,t,,J  	h''  a&& 155==F+B+BiHXHXH^\^GeGeGgGg+B%&UU9%5%5%;$B$B$D$DME)-88
 		W+J==K W!!!!3>z*?U?U3V3V3hZdegfgegZh3h3hjljujuv~jjjj  A sy  sy  sy  {F  {F  {F  G!/txx7K7K!L!L46F25e**PST[P\P\-/=T<UW W W W (ugQWHXHXHc\chwGwxx
&}dhhvt6L6LMMe 	P:DO*v-55%J#M5'DHHVDTDTD_X_dsCstt 	T<FS*v-77GJ 	U&:-  1U  U  UJDHHVR0011 	(&4-'J
%eT22:%% 	N 	NAa&& EE&MM'RE"5!%%--*=2>>F N##E***
uuuffMM
#$$ 	C 	CA$.q$$7$7?155===RD:DQ:M:MUquuY''-244666SUG,,,,AABBB 	B C  FP  PB @8f;Lr;QRRSSSS2>>???? 	BNNFx@@AAA{{7##-v4466<<>>udC&RTAUAUW[W_W_`fhjWkWkllS*#DHHVR$8$8999!-j'!B!BFC#J88KFCE 	4us3333"0&"9M9M"N"NiDHHVR00%3GGG 	2 	2ANN,q01111 	8 20111NN6777 	+NN>*** 	JNN;c%jjHIII 	ONN>WMNNN 	.NN,---4<)*-e**G%1gG G G 	G  3 3 353q662222222223s-   F9[ L8[ D;[ 
[0[+%[0+[0c              #   ^  K   |                      d          pd                                }|                      d          pi }|                      d          pg }|                      d          pg }|s|s|s	dddV  d	S |                     d
          s	dddV  d	S dddV  	 t          |          \  }}t          |||          }|                      d          pd                                }|t          v r|t          |         z  }|}	|	st          |          D ]}
t          |
t                    rm|
                     d          dk    rT|
                     d          pd                                r+|
                     d          pd                                }	 nt          |	          }|rt          ||          }|rdd	l
}t          t          |                    p	|d	d         d|                    d          d}d|d|d}t          ||                     d
d                    \  }}ddg|ddt          |          t          |          g dV  d	S t!          |	|||                     d          pddv           }t#          |	|                     dd                     }t%          |          }|r|r|d!z   |z   n|}t'          |	|||                     d          pddv           }|r|r|d!z   |z   n|}|r|d!|z   d"z   z  }t)          |                     d
d                    }|r|d!|z   z  }d}g }t          |t*                    r|ng D ]v}t          |t                    s|                     d
          pd}t-          ||                     d#          pd          }|r |                    |           |d$|d%|z  }wg }|d&d	         D ]}
t          |
t                    r|
                     d          nd}t          |
t                    r)|
                     d          pd                                nd}|d'v r|r|                    ||d(           |r?d)|z   }|r|                    d|d!z   |z   d(           n3|                    d|d(           n|r|                    d|d(           |                      d*          pd+                                                                } t3          |	||| |                     d
d          |                     dd                    \  }!}"|!d,k    r#t5          |                     d
d                     d-d.d/                     |!|!          }#dd0|#z  dV  i }$d}%|!d1k    rt6          nt8          }& |&|||$          D ]}'|'r|%|'z  }%d2|'dV  t          |%|                     d
d                    \  }(})t;          |                     d
d          |$||!           g }*|D ]}+|*                    d3|+z              |r,|r|*                    d4           |*                    d5           |r|*                    d6           |r%|*                    d7t          |          z             |r%|*                    d8t          |          z             |*s|*                    d9           d|*|)|!|"pdt          |          t          |          |dV  d	S # t<          $ r},dt?          |,          dV  Y d	},~,d	S d	},~,ww xY w):u$  流式处理 /api/chat：yield 事件 dict，type ∈ {status, delta, done, error}

    与 handle() 逻辑一致（检索背景 → 调 Hermes），区别是：检索前先发 status、
    LLM 回答改走 SSE 逐段 yield delta，避免前端长时间无响应被 240s 超时切断。
    rU   r=   r=  r:   r   r   r  )r  r   Nr3   r  r  u   正在检索业务背景…r  r&  rh  r   r   r  r  r  r  r  r  r  r  z
visit-card)r  r  r   rH   noticer_  r  r  r'  r(  r  r   ro  r  r,   r  r  r  r  rH  r  rH   r  ry   u
   GLM 快速u   DeepSeek 深度)r  ry   u1   已检索到背景，正在用 %s 思考回答…r  rh  r  r  r  r  r  r  r  ) r1   r  r?  r  r  r  r   r   r  r  r  r  r  rQ   r(  r   r  r  r+  r  rE  r7   r  rS   r$  r  rh   rv  rm  rX   r   r   )-rr  rU   r=  r:   r   r@  rA  r  r  r  r!  r  r  r:  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r   r&  rh  r  r  target_modelfallback_notice_model_labelrT   	full_text
_stream_fnchunk
clean_textr  r  r  r   s-                                                r   handle_streamr  "  s      J''-24466H;;v$"D{{:&&,"HKK  &BE H U (;<<<<<88F (BCCCCC%B
C
CCCCm0*400w)$w??
F##)r0022<,t,,J  	h''  a&& 155==F+B+BiHXHXH^\^GeGeGgGg+B%&UU9%5%5%;$B$B$D$DME)-88
 	+J==K !!!!3>z*?U?U3V3V3hZdegfgegZh3h3hjljujuv~jjjj  A sy  sy  sy  {F  {F  {F  G!/txx7K7K!L!L%#:"; & ,!#,/JJ),W')+ + + + + 'ugQWHXHXHc\chwGwxx
&}dhhvt6L6LMMe 	P:DO*v-55%J#M5'DHHVDTDTD_X_dsCstt 	T<FS*v-77GJ 	U&:-  1U  U  UJDHHVR0011 	(&4-'J
%eT22:%% 	N 	NAa&& EE&MM'RE"5!%%--*=2>>F N##E***
uuuffMM
#$$ 	C 	CA$.q$$7$7?155===RD:DQ:M:MUquuY''-244666SUG,,,,AABBB 	B C  FP  PB @8f;Lr;QRRSSSS2>>???? 	BNNFx@@AAAkk'**4f;;==CCEE
(45$
DHHVR4H4H$((SY[]J^J^)` )`%o:%%#DHHVR$8$8999+9JKKOOP\^jkk)\_k)klllll	)5)>)>%%DW
Z
GU;; 	7 	7E 7U"	&66666 .y$((62:N:N O O
IDHHVR00%<PPP 	2 	2ANN,q01111 	8 20111NN6777 	+NN>*** 	JNN;c%jjHIII 	ONN>WMNNN 	.NN,---!!$(.B$'JJ!$W+- - 	- 	- 	- 	- 	-  0 0 0A///////////0s    &F;\ #R\ 
\,\''\,c                    ddl m } g }t          | t                    r| ng D ]}t          |t                    r|                    d          nd}t          |t                    r)|                    d          pd                                nd}|dv r|r|                    ||d           |sdS d	                    d
 |dd         D                       }g d}	}|D ]8}|d         dk    r	|d         }	|	|                    |	|d         f           d}	9d                    d t          |d          D                       }
d|z   }d}t          |d|dg          \  }}|rd|fS |sdS |                                
                    d	          }d}g }t          |          D ]q\  }}|                                }|dk    r=|                    d          r(|                    d                                          }\|                    |           r|s*d|                                                    d          z   }d	                    |                                          }|}|
r|d|
z   z  }||fS )u   把一段多轮问答链（含补充追问）提炼成结构化经验条目。

    返回 (title, content) 或 (None, error)。
    content = LLM 提炼的结构化正文 + 附带的原始问答链（可追溯）。
    r   r?   r&  r=   rh  r  rH  )Nu   没有可归档的对话内容r3  c              3   H   K   | ]}|d          dk    rdnd|d         z   V  dS )r&  r=  u	   用户：u	   助手：rh  Nr   )r   r!  s     r   r  z%archive_experience.<locals>.<genexpr>  sQ         &	V++)L     r5   rA  Nr=  ro  c              3   4   K   | ]\  }\  }}d ||||fz  V  dS )u   **Q%d**：%s

**A%d**：%sNr   )r   r   qqaas       r   r  z%archive_experience.<locals>.<genexpr>  sK        AxB 	'!RB7     r5   r#   u  以下是宝锐销售工作台 AI 助手与用户的一段多轮对话（含补充追问）。
请把这段对话提炼成一条可复用的销售经验，用 markdown 输出，格式严格如下：

# <一句话标题，概括这条经验的核心结论>

**问题背景**：<初始问题、客户或场景>

**涉及客户**：<对话中提到的客户名，没有就写"无">

**涉及货号/产品**：<对话中提到的货号或产品，没有就写"无">

**关键结论**：<多轮追问后沉淀的核心结论，分点列出>

**建议动作**：<下一步建议或可复用的打法>

要求：简洁、专业、可复用；不得编造对话中没有的信息；保留具体数字与货号。

对话内容：
uf   你是销售知识管理助手，负责把销售对话提炼成结构化、可复用的经验条目。)Nu$   经验提炼失败，请稍后重试#u   销售经验_z
%Y%m%d%H%Mu   

---

## 原始问答链

)r@   r   r7   r   r1   r  rS   r   rz  rW  rq  rr  lstriprO   rQ   )r:   r=  r@   chainr!  r&  rh  dialogqarL   	raw_chainextract_promptextract_sys	extractedr  rL  r  
body_linesr   lnrP  rU  fulls                          r   archive_experiencer$    s    "!!!!!E$Xt44<hh" = = *1d 3 3;quuV}}}6@D6I6IQ155##)r00222r(((W(LL$7;;<<< 655 YY  stt    F B  V9)AA]IIq!I,'(((A  $R++    I		 %		%  {K#K6n2]2]1^__NIs
 Sy <;;OO##D))EEJ5!! " "2HHJJ66all3''6HHSMM''))EEb!!!! H(,,.."9"9,"G"GG99Z  &&((DD @3i??$;r5   c                    ddl }d } |d          pi }|                    | i           pi } |d          pi }t          |t                    r|                    | g           ng } |d          pi }t          |t                    r|n@t          |t                    r*|                    d|                    dg                     ng }	t          |           }
t          |
          p	| dd	         }g }|	pg D ]\}t          |t                    st          |                    d
d                    }|r||v s|
r|
|v r|                    |           ] |d          pi }t          |t                    r|                    | i           ni }d}g }t          |t                    rd|	                                D ]O\  }}t          |t                    r5t          d |D                       }||z  }|                    ||f           P|                    d            d                    d |dd         D                       pd}t          |                    dd          pd                                          }|                    dd          pd}|                    dd          p|                    dd          }|                    d          }d}t!          |pg dd                   D ]T\  }}t          |t                    r:|                    dd          }|                    dd          }|d|d z   ||fz  z  }U|sd!}d} |dd         D ]g}|                    d"|                    d#d                    }!|                    d$d          }"|                    d%d          }#| d&|!d'|"d'|#d(z  } h| sd)} |                    d*d          }$|                    d+d          }%d}&|$r|&d,|$z  z  }&|%r|&d-|%z  z  }&|&sd.}&d/|||||| ||&t#          |pg           |d0z  t#          |          ||| ||d0z  fz  }'|'S )1u   生成基础版市场调研卡 HTML（本地数据：画像/商机/决策链/竞对情报/价格档案）。
    深度竞品格局需 Hermes 主 Agent Web 搜索，此处标"待补充"。返回 html 或 None。r   Nc                     	 t          t          j                            t          |           d          5 }t          j        |          cd d d            S # 1 swxY w Y   d S # t          $ r Y d S w xY w)Nr   r   )r   r   r   r   r(   r   r   r   )r   r   s     r   _ljz_build_market_card.<locals>._lj  s    	bgll8T22WEEE $y||$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 	 	 	44	s4   4A$ A
A$ AA$ AA$ $
A21A2r  r  r  r   r,   r   r   r=   z_archive/price_archive.jsonc              3   l   K   | ]/}t          |t                    |                    d d          V  0dS r  rL  rS  s     r   r  z%_build_market_card.<locals>.<genexpr>	  s=      LLa
1d8K8KL!%%q//LLLLLLr5   c                     | d          S r  r   rU  s    r   r.  z$_build_market_card.<locals>.<lambda>	  r  r5   r/  r1  c                     g | ]=}|d                               d          d                                          dd         >S r  r  r  s     r   r   z&_build_market_card.<locals>.<listcomp>	  r  r5   r   r  r  r  r  r  r   r3   rA   rZ  r  r  r  zE<tr><td>%d</td><td>%s</td><td><span class="tag b">%s</span></td></tr>r#   uF   <tr><td colspan="3" style="color:#9aa0a6">暂无商机记录</td></tr>r  
competitorproductrj  z<tr><td>z	</td><td>z
</td></tr>uc   <tr><td colspan="3" style="color:#9aa0a6">暂无竞对情报沉淀，需 Web 搜索补充</td></tr>r  r  uG   <a class="btn" href="%s" target="_blank">🔗 打开飞书决策链</a>uJ   <a class="btn" href="%s" target="_blank">📋 打开飞书客户档案</a>u>   <span style="color:#9aa0a6">暂无飞书决策链链接</span>u.  <!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>%s · 市场调研卡（基础版）</title>
<style>
:root{--pri:#1a73e8;--pri-d:#0d47a1;--bg:#f5f7fa;--card:#fff;--ink:#202124;--sub:#5f6368;--line:#e8eaed;--ok:#10ac84;--warn:#f2994a;--bad:#eb5757}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--bg);color:var(--ink);padding:16px;line-height:1.65}
.wrap{max-width:980px;margin:0 auto}
.banner{background:linear-gradient(135deg,#0d47a1,#1565c0,#1a73e8);border-radius:14px;padding:24px 28px;color:#fff;box-shadow:0 4px 16px rgba(13,71,161,.25)}
.banner h1{font-size:21px}.banner .sub{font-size:13px;opacity:.9;margin-top:4px}
.badge{display:inline-block;background:rgba(255,255,255,.22);border-radius:20px;padding:2px 12px;font-size:12px;margin-left:10px;vertical-align:middle}
section{background:var(--card);border-radius:12px;padding:20px 22px;margin-top:16px;box-shadow:0 1px 3px rgba(0,0,0,.05)}
h2{font-size:16px;color:var(--pri-d);margin-bottom:14px;padding-left:10px;border-left:4px solid var(--pri)}
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px}
.kpi{background:var(--bg);border-radius:10px;padding:14px 16px;border:1px solid var(--line)}
.kpi .v{font-size:20px;font-weight:800;color:var(--pri)}.kpi .l{font-size:12px;color:var(--sub);margin-top:2px}
table{width:100%%;border-collapse:collapse;font-size:13px}
th{background:#f1f3f4;color:#5f6368;text-align:left;padding:9px 10px;font-weight:600;border-bottom:2px solid var(--line)}
td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}
tr:last-child td{border-bottom:none}
.tag{display:inline-block;font-size:11px;border-radius:4px;padding:1px 7px;background:#e8f0fe;color:var(--pri)}
.btn{display:inline-block;background:var(--pri);color:#fff;text-decoration:none;border-radius:8px;padding:8px 16px;font-size:13px;margin:4px 6px 4px 0}
.note{background:#eef4fd;border:1px solid #c9ddf6;border-radius:10px;padding:14px 16px;font-size:13px;margin-top:12px}
</style></head><body><div class="wrap">
<div class="banner"><h1>%s <span class="badge">市场调研卡 · 基础版</span></h1><div class="sub">级别：%s ｜ 对接销售：%s ｜ 编制：%s</div></div>
<section><h2>一、赛道定位</h2><table>
<tr><th style="width:120px">维度</th><th>结论</th></tr>
<tr><td>客户名称</td><td>%s</td></tr>
<tr><td>客户分级</td><td><span class="tag">%s</span></td></tr>
<tr><td>决策链</td><td>%s</td></tr>
<tr><td>终端赛道</td><td><span style="color:var(--warn)">待确认</span>（需 Web 搜索判断：人用IVD / 宠物诊断 / 科研服务 / 食品安全检测）</td></tr>
</table></section>
<section><h2>二、关键数据</h2><div class="kpis">
<div class="kpi"><div class="v">%d</div><div class="l">在跟商机数</div></div>
<div class="kpi"><div class="v">%.1f 万</div><div class="l">累计采购额</div></div>
<div class="kpi"><div class="v">%d</div><div class="l">竞对情报条数</div></div>
<div class="kpi"><div class="v">%s</div><div class="l">主采产品 TOP</div></div>
</div></section>
<section><h2>三、在跟商机</h2><table><tr><th>#</th><th>商机名称</th><th>状态</th></tr>%s</table></section>
<section><h2>四、竞对情报（本地沉淀）</h2><table><tr><th>竞品品牌</th><th>产品/项目</th><th>备注</th></tr>%s</table>
<div class="note">⚠️ 深度竞品格局（终端赛道竞争格局、市场规模、竞品对比）需 Hermes 主 Agent 执行 Web 搜索补全。本基础版仅含本地沉淀数据。</div>
</section>
<section><h2>五、宝锐协同角色</h2><table>
<tr><th style="width:140px">主采产品</th><td>%s</td></tr>
<tr><th>累计采购额</th><td>%.1f 万</td></tr>
</table></section>
</div></body></html>g     @)r  r1   r   r   r7   r  r  r   rS   r   r  rp  r   r  r*   rQ   rz  r   )(r  r=  r  r'  r  r  oppr  cici_itemsr  r  comp_matchesr,  cnamer  r  r  r  r  ru   r  r  r  r  r   r`   opp_rowsr   r  r  rJ  	comp_rowsr  r   rj  durlclink	link_btnsr  s(                                           r   _build_market_cardr7    s    KKK   #)
*
*
0bC779b!!'RD
#%
&
&
,"C&0d&;&;CCGGIr"""E	$	%	%	+BD))trrWabdfjWkWk0swvr@R@R0S0S0SqsHi  D.2A2ELn" # #!T"" 	AEE*b))** 	#eunn$n45=="""	*	+	+	1rB&0T&:&:BRVVIr"""FI
C&$ %LLNN 	% 	%DAq!T"" %LL1LLLLLS 	

As8$$$HHH!!!::HHBQBHHHIIX[DTXXgr**0b117799J$$UE22DnKHHWb!!9TXXfb%9%9EMM*%%EH5;B+,, r r1a 	rvx((Bx--B_cdghchjlnpbqqqH \[I"1" X Xgquu\;??@@uuY##uuVR  	UUUDDDRVRVRVWW		 zy	88("--DHH_b))EI f^aeee	 jadiii	 UT	.\ !%eU{Ii')3|+<+<diy724]14Dd Kr5   c                    |                      d          pd                                }|sg dfS |                      d          pg }|sdg}t          |          }t          |          p	|dd         }t          j                            t          dd          }t          j                            t          d          }g }g }	d|v r	 t          ||          }
|
rt	          j	        |d	
           t          j                            |d|z            }t          |dd          5 }|                    |
           ddd           n# 1 swxY w Y   |                    dt          j                            |t                    |d           n|	                    d           n/# t          $ r"}|	                    d|z             Y d}~nd}~ww xY wd|v r	 t          ||          }
|
rt	          j	        |d	
           t          j                            |d|z            }t          |dd          5 }|                    |
           ddd           n# 1 swxY w Y   |                    dt          j                            |t                    |d           n|	                    d           n/# t          $ r"}|	                    d|z             Y d}~nd}~ww xY w||	rd                    |	          ndfS )u_   处理一个拜访请求：按 types 生成拜访卡 + 市场调研卡。返回 (files, err)。r   r=   zcustomer requiredtypesr  Nr   u   客户管理Tr   u   %s_拜访卡.htmlr"   r   r   )r  r   r   u   拜访卡生成失败u   拜访卡异常: %sr  u   %s_市场调研卡.htmlu   市场调研卡生成失败u   市场调研卡异常: %sz; )r1   r  r  r  r   r   r   r  r  r'   r   r  rS   relpathr   r7  )r  r=  r   r9  r  r  card_dirmkt_dirr   errsr  rW  r   r   s                 r   _process_visit_requestr>  i	  s   
##)r0022H '&&&GGG"E hD-"1"Ew||D.+>>Hgll400GEDe	3$Xt44D 5Ht4444gll8-@5-HII#sW555 "GGDMMM" " " " " " " " " " " " " " "k27??3PT;U;Uckllmmmm3444 	3 	3 	3KK-122222222	3E!!	9%h55D ;Gd3333gll7,E,MNN#sW555 "GGDMMM" " " " " " " " " " " " " " "&7QTVZA[A[iqrrssss9::: 	9 	9 	9KK3a788888888	9T1$))D///r11s|   	AF& &E<F& EF& EAF& &
G0GGAJ8 8IJ8 IJ8 !I"AJ8 8
K$KK$)r=   )NNF)NF)r   r  )r=   r'  )T)r4  )r?  )w__doc__r  r  r   r   r  r  urllib.requestr  r   dirnameabspath__file__r  r   r(   r   r  r  r  r   rM   rs   rZ   rj   r8   r  r   r.   r4   r<   rX   r[   r]   rb   rf   rh   rk   rm   ro   rd   r   r   r   r   r   r   r   r   r   r   r6  r9  r5  r<  r8  rM  r  r  r  r  r  r  r  r  r  r:  r  r  r$  r?  rC  rD  rE  rI  rO  rZ  re  rx  r{  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r(  r.  r1  r3  r>  rE  rW  ra  rm  rs  rv  r~  r  r  r  r  r  r  r  r  r  r  r$  r7  r>  r   r5   r   <module>rD     s  
 
  				  				         	wrwrwx'@'@AABB7<<h''w!!"^__7<<g..W\\,55
w||H&9::7<<*?@@  gll8-EFF 7<<*ABB ".B]^^

 
 

 
 
) ) )
     @
 
 
	 	 	I I I. . .  
 
 

 
 
2 2 2
  ! ! !! ! !  4 	# # #8! ! !([ [ [l l l  $ $ $&  z z z+x(,,, +z* x"	
 222 x# 777 === X/0 @@@ x! xj +z* xj +  xj!" +z*#$ z*#$'   , \[[  >==  
  ? ? ? ?DW/ W/ W/ W/t   =:	F/ F/ F/ F/X  D   ,  8   _J _J _J _JB	 	 	  # # #T 7<<*NOO  I  I  I      L L L   # # # #LC  C  C L$ $ $$ xT[qx  NU  V  V

U U Up
 
 
   I R( ( (  O O Of X/FGG 	 	 	    :,Z ,Z ,Z^1 1 1 1h    # # # #       6j j j j.  8: : :@"$ "$ "$J  @+$ +$ +$\g g g
     6' ' '$# # #*  &  $  > ~ j r r	 m3 m3 m3`0 0 0DF F FRy y yx(2 (2 (2 (2 (2r5   