
    i=S                    B   d Z ddlmZ ddlmZ ddlmZmZ ddlm	Z	m
Z
mZ ddlmZmZmZmZmZ dZdZdNdZdOdZdPdZdOdZdQdZdddRd ZdSd#ZdTd&ZdUd)ZdUd*ZdVdWd/Zd0d1d2dXd5ZdYd7Z d0d0d8dZd;Z!d[d?Z"d\d@Z#d]dAZ$d^dDZ%d_dEZ&d`dFZ'dadGZ(dbdIZ)dcdKZ*dddMZ+d0S )eu  研究引擎格式化与标准化工具函数。

提供 ES 命中聚合、文档合并去重、上下文构建、LLM 消息组装、
研究计划/报告/章节重跑结果的标准化等纯函数，供 ResearchEngine 和
ResearchRetriever 使用。所有函数无状态依赖。
    )annotations)Any)ResearchPlanResearchTask)QA_FOLLOWUP_TEMPLATEQA_SYSTEM_PROMPTQA_USER_TEMPLATE)DEEP_RESEARCH_REPORT_SYSTEMDEEP_RESEARCH_REPORT_USERRESEARCH_FOLLOWUP_TEMPLATERESEARCH_SYSTEM_PROMPTRESEARCH_USER_TEMPLATE   iX  rawdict[str, Any]top_nintreturnlist[dict[str, Any]]c                   |                      di                                dg           }i }|D ]t}|                     di           }|                     d          pg }|r|d         n|                     dd          }|sS|                     dd          }|                     d	i                                d
g           }	||vr||                     dd          |                     d          |                     d          |                     d          |                     d          |                     d          |g d	||<   ||         }
|r||
d         pdk    r||
d<   |	D ];}t          |
d                   t          k     r|
d                             |           <vt	          |                                d d          d|         S )u   将 ES chunk 级命中按 doc_id 聚合为文档级结果，保留最高分和高亮片段。

    Aggregate ES chunk hits to document-level results.hits_sourcedoc_idsr   doc_id _scoreg        	highlightcontenttitle
doc_numberissuing_orgdoc_typepublish_datesigner)	r   r   r    r!   r"   r#   r$   scorepassagesr%   r&   c                0    |                      d          pdS )Nr%   r   get)ds    5D:\work\zm-rag\backend\app\core\research_formatter.py<lambda>z$_aggregate_es_hits.<locals>.<lambda>H   s    aeeGnn)     T)keyreverseN)r)   len_MAX_PASSAGES_PER_DOCappendsortedvalues)r   r   r   doc_maphitsrcr   r   r%   
hl_contentdochls               r+   _aggregate_es_hitsr;       s    7762""62..D)+G + +ggi$$'')$$*&ACGGHb,A,A 	#&&WW["--11)R@@
   "--!ggl33"ww}55GGJ// # 7 7''(++
 
GFO fo 	!Uc'l/a00 CL 	+ 	+B3z?##&;;;J&&r***	+ ))   uf	 r-   groupsc                     g }t                      }| D ]M}|D ]H}|                    d          }|r||v r|                    |           |                    |           IN|S )u\   将多组文档按 doc_id 去重合并，保持优先级顺序（先出现的组优先）。r   )setr)   addr2   )r<   mergedseengroupr9   r   s         r+   _merge_unique_docsrC   M   s    #%FUUD   	 	CWWX&&F Vt^^HHVMM#	 Mr-   r9   
str | Nonec                    |                      d          }|sdS t          |                      d          p|                      d          pd          }|dk    r|                      d          }d|p| S d	| S )
u]   为引用条目生成稳定身份键，允许 Guide 与原文档在同一 doc_id 下共存。r   N_source_typesource_groupsearchguide
profile_idzguide:zdoc:)r)   str)r9   r   rG   rJ   s       r+   _reference_identityrL   [   s    WWXF tsww~..U#''.2I2IUXVVLwWW\**
.
,f...&??r-   c                     g }t                      }| D ]G}|D ]B}t          |          }|r||v r|                    |           |                    |           CH|S )u_   按引用身份去重，普通文档按 doc_id 合并，Guide 额外纳入 profile_id 维度。)r>   rL   r?   r2   )r<   r@   rA   rB   r9   identitys         r+   _merge_unique_reference_docsrO   h   s    #%FUUD   	 	C*3//H x4//HHXMM#	 Mr-   docsrK   c                   g }t          | d          D ]\  }}|                    d          pd}|                    d          pd}|                    d          pd}|                    d          pd}|                    d          pg }d	| d
| d}	|r|	d| z  }	|r|	d| z  }	|r|	d| z  }	|r%d                    d |D                       }
d|
 }nd}|                    |	|z              d                    |          S )u   将检索到的文档格式化为编号上下文文本，作为 LLM 的参考材料输入。

    Format retrieved documents into numbered context passages.   r   u   （无标题）r    r   r!   r#   r&   [u   ] 标题：《u   》u   
     文号：u   
     发文机关：u   
     时间：
c              3  r   K   | ]2}d |                     dd                               dd           dV  3dS )u        ……z<em>r   z</em>u   ……N)replace).0ps     r+   	<genexpr>z&_build_context_text.<locals>.<genexpr>   s`       % % Qaii33;;GRHHPPP% % % % % %r-   u   
     相关内容摘录：


)	enumerater)   joinr2   )rP   partsidxr9   r   r    r!   r#   r&   headerpassage_textbodys               r+   _build_context_textrb   v   sw    EdA&& $ $S  5$5WW\**0b
ggm,,2ww~..4"77:&&,"2S22222 	65555F 	=<{<<<F 	87777F 	99 % %!% % %  L CLBBDDDVd]####;;ur-   research)modequestioncontexthistoryrd   list[dict[str, str]]c                  |dk    rt           nt          }|dk    rt          nt          }|dk    rt          nt
          }d|dg}|D ]R}|                    d|                    |d                   d           |                    d|d         d           S|                    d|                    | |	          d           |S )
u   构建 LLM 对话消息列表：系统提示词 + 历史轮次 + 当前问题与上下文。

    Construct the messages list for the LLM.rc   systemroler   userre   re   	assistantanswer)re   rf   )r   r   r   r   r   r	   r2   format)	re   rf   rg   rd   system_promptfollowup_templateuser_templatemessagesturns	            r+   _build_messagesrw      s    /3j.@.@**FVM6:j6H6H22Nb.2j.@.@**FVM m44&H
  	J 	J,33!*- 4   	
 	
 	
 	hHHIIII OO$++!7 ,  	
 	
   Or-   taskr   c                   d| j          d| j         d| j         d| j         d| j         g}| j        r|                    d| j                    | j        r|                    d| j                    | j        r|                    d| j                    | j	        r|                    d	| j	                    | j
        r8|                    d
d                    | j
        dd                               | j        r8|                    dd                    | j        dd                               d                    |          S )u>   将研究任务格式化为文本摘要，用于 LLM 输入。u   - 模式：u   - 主题：u   - 核心问题：u   - 输出模板：u   - 研究深度：   - 研究目标：u   - 时间范围：u   - 地域范围：u   - 部门范围：u   - 必须纳入文档：z, N   u   - 关注事项：rT   )rd   topicre   output_templatedepth_levelgoalr2   
time_rangeregion_scope	org_scoperequired_doc_idsr\   required_matter_ids)rx   r]   s     r+   _build_task_briefr      s    	"di!!"dj""+DM++2D022.D,..E y 6444555 <:::;;; ><):<<===~ ;999::: WUtyy9NrPQr9R/S/SUUVVV TR43KBQB3O)P)PRRSSS99Ur-   planr   c                h   d| j          g}| j        r0|                    dd                    | j                  z              | j        r0|                    dd                    | j                  z              | j        r0|                    dd                    | j                  z              | j        r0|                    dd                    | j                  z              | j        r0|                    dd                    | j                  z              d                    |          S )	u>   将研究计划格式化为文本摘要，用于 LLM 输入。u   - 计划摘要：rz      ；u   - 子问题：u   - 检索重点：u   - 章节结构：u   - 风险提示：rT   )summary
objectivesr2   r\   sub_questionsretrieval_focussection_outlinenotes)r   r]   s     r+   _build_plan_briefr      s!   ///0E H(5::do+F+FFGGG H%

43E(F(FFGGG M(5::d6J+K+KKLLL M(5::d6J+K+KKLLLz C(5::dj+A+AABBB99Ur-   template	list[str]c                ^    g dg dg dg dg dd}|                     | |d                   S )u3   根据输出模板类型返回默认章节大纲。)u   结论摘要u   政策依据与演进u   当前执行要求   风险与建议)u   阶段划分u   关键节点u   政策演进u   阶段性判断)u   比较维度u	   共同点u	   差异点u   结论建议)u   任务目标u   执行链条u   落地难点u   推进建议)u   研究结论u   政策依据u   执行要求r   )policy_brieftimeline
comparisonimplementationcomprehensiver   r(   )r   mappings     r+   _default_sections_for_templater      sY     kjjWWWPPPZZZ\\\ G ;;x!9:::r-   c                2    | dk    rg dS | dk    rg dS g dS )u6   根据输出模板类型返回默认交付物列表。r   )u   时间线梳理u   阶段判断u   关键依据列表r   )u   比较矩阵u   差异点归纳u   决策建议)u   执行摘要u   章节化研究报告u   引用附录 )r   s    r+   _default_deliverablesr      s<    :HHHH<BBBBDDDDr-   r   valuer   fallbackc                X    t          | t                    r|                                 S |S )u<   安全地将任意值转为字符串，去除首尾空白。
isinstancerK   strip)r   r   s     r+   _normalize_textr      s'    % {{}}Or-   Nr{   )r   limitlist[str] | Noner   c                  g }t          | t                    rU| D ]R}t          |t                    r;|                                r'|                    |                                           S|s|rd |D             }|d|         S )uK   安全地将任意值转为字符串列表，过滤空值并限制长度。c                    g | ]}||S r   r   )rW   items     r+   
<listcomp>z#_normalize_list.<locals>.<listcomp>  s    333$d3333r-   N)r   listrK   r   r2   )r   r   r   itemsr   s        r+   _normalize_listr     s    E% + 	+ 	+D$$$ + +TZZ\\*** 4X 433(333%=r-   r4   c                ^    d t                               d | D                       D             S )u*   对字符串列表去重并保持顺序。c                    g | ]}|S r   r   )rW   r   s     r+   r   z#_unique_strings.<locals>.<listcomp>  s    llleElllr-   c              3     K   | ]A}t          |t                    |                                +|                                V  Bd S Nr   )rW   vs     r+   rY   z"_unique_strings.<locals>.<genexpr>  sM      ,k,k1*UVX[J\J\,kabahahajaj,kQWWYY,k,k,k,k,k,kr-   )dictfromkeys)r4   s    r+   _unique_stringsr     s1    llt}},k,k,k,k,kkkllllr-   )seed_doc_idsextra_doc_idsr   r   c                   t          |j        pg           }t          | j                  t          |pg           z   }|p|}t          t          |pg           |z             S )u=   从任务和计划中合并显式纳入的文档 ID 列表。)r   included_doc_idsr   r   )rx   r   r   r   plan_doc_idsfallback_doc_idsscoped_doc_idss          r+   _resolve_explicit_doc_idsr     sg     -344LD122T,:L"5M5MM!5%5N4 344~EFFFr-   resultkeywordsplanner_planc               >   t          |                    d          |p| j        g          }t          |dd          r|                    d|j                    t          |dd          r|                    d|j                    t          |dd          r|                    d	|j                    t          |                    d
          g           }|r&|                    dt          |           d           | j	        r|                    d           t          t          |                    d          d| j         d          t          |                    d          | j        p| j        ddg          t          |                    d          | j        ddg          t          |          dd         t          |                    d          t          | j                            t          |                    d          t#          | j                            t          |          dd         t          t%          | j                  t%          |pg           z             t          t%          | j	                            	  	        S )ut   标准化 LLM 返回的研究计划：填充缺失字段、合并图谱规划信息、添加安全回退默认值。r   r   doc_codeNu	   文号：entity_nameu	   实体：matter_queryu	   事项：r   u   已显式纳入 u+    份资料，应优先核对这些材料。uH   已指定事项范围，需结合事项办理依据核查研究结论。r   u	   围绕“uH   ”拆解研究问题、检索依据链并生成结构化研究报告。r   u   识别关键依据链条u   归纳执行要求与风险r   u'   现行政策依据链条如何构成？u'   执行环节有哪些重点和风险？r{   r   expected_deliverables   )	r   r   r   r   r   r   r   r   included_matter_ids)r   r)   r|   getattrr2   r   r   r   r0   r   r   r   r   re   r   r   r}   r   r   r   )rx   r   r   r   r   r   r   s          r+   _normalize_research_planr   %  s    &fjj1B&C&ChNf[_[eZfgggO|Z.. DB<+@BBCCC|]D11 GE<+CEEFFF|^T22 HF<+DFFGGGFJJw//"===E hfL(9(9fffggg a_```JJy!!{DJ{{{
 
 
 #JJ|$$i04=2LNkl
 
 
 &JJ''99
 
 
 (88!<'JJ())3D4HII
 
 
 .JJ.//*4+?@@
 
 
 e$$RaR((d.C)D)DtLL^\^G_G_)_``+D1I,J,JKK9   r-   c                   | j         | j        g}| j        r|                    | j                   | j        r|                    d| j                    | j        r|                    d| j                    | j        r|                    d| j                    |j        r8|                    dd                    |j        dd                   z              d                    d	 |D                       S )
u6   将研究任务和计划合成为检索查询文本。u   时间范围：u   地域范围：u   部门范围：u   检索重点：r   N   rT   c              3     K   | ]}||V  	d S r   r   rW   parts     r+   rY   z$_build_task_query.<locals>.<genexpr>j  s'      44dt4T444444r-   )	r|   re   r   r2   r   r   r   r   r\   )rx   r   r]   s      r+   _build_task_queryr   ]  s   Z'Ey  TY :8t88999 <:t'8::;;;~ 97t~77888 O&D4H!4L)M)MMNNN9944e444444r-   c           
        dt           dg}|D ]m}|                    dt          j        |                    dd                    d           |                    d|                    dd          d           n|                    dt          j        t          |           t          |          |	          d           |S )
u5   构建深度研究报告生成的 LLM 消息列表。rj   rk   rm   re   r   rn   ro   rp   )
task_brief
plan_briefrf   )r
   r2   r   rq   r)   r   r   r   )rx   r   rf   rg   ru   rv   s         r+   _build_deep_research_messagesr   m  s     &ABB&H  R R5<dhhz[]F^F^___ 	
 	
 	
 	(B9O9OPPQQQQOO07,T22,T22  	
 	
	 	 	 Or-   section_titlesection_summaryc                l    d|  g}|r|                     d|            d                    |          S )u$   构建章节重跑的提示文本。u   目标章节：u   当前摘要：rT   )r2   r\   )r   r   r]   s      r+   _build_section_hintr     sG    .}../E :88899999Ur-   c                
   t          | t                    sg S g }| D ]Y}t          |t                    sd|cxk    rt          |          k    r'n 5|                    ||dz
           d                    Zt          |          S )u=   将 LLM 返回的文档序号列表映射为 doc_id 列表。rR   r   )r   r   r   r0   r2   r   )r   rP   r   r   s       r+   _normalize_source_indicesr     s    eT"" 	G 5 5$$$ 	!!!!D		!!!!!NN4q>(34447###r-   c                p   g }|                      dg           dd         D ]}t          |t                    st          |                     d                    }|s=|                    t          |                     d          d          |t          |                     d          d	          pd	t          |                     d
          |          d           g }|                      dg           dd         D ]}t          |t                    st          |                     d                    }|s=|                    t          |                     d          d          |t          |                     d          d	          pd	t          |                     d
          |          d           g }|                      dg           dd         D ]v}t          |t                    st          |                     d                    }t          |                     d                    }	|r|                    ||	d           wg }
|                      dg           dd         D ]}t          |t                    st          |                     d                    }|s=|
                    t          |                     d          d          t          |                     d                    |t          |                     d
          |          d           |
sd |j        dd         D             }
t          |                      d                    ||||
t          |                      d                    t          |                      d          g           dS )ux   标准化 LLM 返回的研究报告：解析发现、冲突、待解问题和章节，将文档序号映射为 doc_id。findingsNr{   r   r   u   关键发现r   strengthmediumsource_indices)r   r   r   source_doc_ids	conflictsr   u   证据冲突或缺口severity)r   r   r   r   open_questionsre   reason)re   r   sections   研究章节r   r   r   r   r   c                    g | ]	}|d dg d
S )r   ux   当前轮研究已完成检索，但结构化章节生成不足，请结合执行摘要与证据工作区继续补充。r   r   )rW   r   s     r+   r   z._normalize_research_report.<locals>.<listcomp>  s@     
 
 
   V"$	 
 
 
r-   r   executive_summaryone_page_summaryrecommended_next_steps)r   r   r   r   r   r   r   )r)   r   r   r   r2   r   r   r   )r   rP   r   r   r   r   r   r   re   r   r   s              r+   _normalize_research_reportr     s    &(H

:r**2A2. 
 
$%% 	!$((9"5"566 	('):):^TTT"+DHHZ,@,@8TTT`X`";DHHEU<V<VX\"]"]	 	
 	
 	
 	
 ')I

;++BQB/ 
 
$%% 	!$((9"5"566 	('):):E\]]]"+DHHZ,@,@8TTT`X`";DHHEU<V<VX\"]"]	 	
 	
 	
 	
 ,.N

+R00!4 L L$%% 	"488J#7#788 (!3!344 	L!!x6"J"JKKK%'H

:r**2A2. 
 
$%% 	!$((9"5"566 	('):):^TTT*488I+>+>??"";DHHEU<V<VX\"]"]	 	
 	
 	
 	
  	

 
 -bqb1
 
 
 -VZZ8K-L-LMM(+FJJ7I,J,JKK"1&**=U2V2Vac"d"d"d  r-   c                   t          | t                    r|                     d          ni }t          |t                    si }t          |                    d          d          }t          |                    d          |          t          |                    d                    |t	          |                    d          |          dt          |                     d	          g           d
S )u,   标准化章节重跑的 LLM 返回结果。sectionr   uT   当前轮局部重跑未生成有效章节内容，请补充证据范围后重试。r   r   r   r   r   r   )r   r   )r   r   r)   r   r   r   )r   rP   r   raw_sectionr   s        r+   _normalize_section_rerun_resultr     s     ,6fd+C+CK&**Y'''Kk4(( 	""g  G %[__W%=%=VVV&{y'A'ABB7HX8Y8Y[_``	
 
 !G!4!4rBBB  r-   rG   c                <    ddddd}|                     | d          S )u-   将源分组标识符映射为中文标签。u   显式纳入资料u   混合检索命中u   图谱补充材料u   办事指南结构化证据)seedrH   graphrI   r(   )rG   r   s     r+   _source_group_labelr     s3     %&%.	 G ;;|%9:::r-   reportc                   g }|                      d          r|                    | d                    |                      dg           D ]K}|                     d          pd}|                     d          pd}|                    d| d|            L|                      d	          r|                    d
| d	         z              d                    d |D                       S )uB   将结构化研究报告拼接为纯文本，用于会话存储。r   r   r   r   r   r   ## rZ   r   u   ## 一页式摘要

c              3     K   | ]}||V  	d S r   r   r   s     r+   rY   z$_join_report_text.<locals>.<genexpr>(  s'      666t666666r-   )r)   r2   r\   )r   r]   r   r   r   s        r+   _join_report_textr     s    Ezz%&& 2V/0111::j"-- 1 1G$$6++i((.B/5//g//0000zz$%% L-7I0JJKKK;;66666666r-   r   c                    |                      d          pd}|                      d          pd}d| d|                                 S )u9   将单个章节拼接为纯文本，用于会话存储。r   r   r   r   r   rZ   )r)   r   )r   r   r   s      r+   _join_section_textr   +  sP    KK  2NEkk)$$*G%%%G%%++---r-   )r   r   r   r   r   r   )r<   r   r   r   )r9   r   r   rD   )rP   r   r   rK   )
re   rK   rf   rK   rg   r   rd   rK   r   rh   )rx   r   r   rK   )r   r   r   rK   )r   rK   r   r   )r   )r   r   r   rK   r   rK   )r   r   r   r   r   r   r   r   )r4   r   r   r   )
rx   r   r   r   r   r   r   r   r   r   )rx   r   r   r   r   r   r   r   r   r   r   r   )rx   r   r   r   r   rK   )
rx   r   r   r   rf   rK   rg   r   r   rh   )r   rK   r   rD   r   rK   )r   r   rP   r   r   r   )r   r   rP   r   r   r   r   r   )r   r   rP   r   r   rK   r   r   )rG   rK   r   rK   )r   r   r   rK   )r   r   r   rK   ),__doc__
__future__r   typingr   app.api.schemas.researchr   r   app.prompts.qa_promptsr   r   r	   app.prompts.research_promptsr
   r   r   r   r   r1   _MAX_PASSAGE_CHARSr;   rC   rL   rO   rb   rw   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r-   r+   <module>r     s    # " " " " "       ? ? ? ? ? ? ? ?         
                * * * *Z   
 
 
 
      N ' ' ' ' ' 'T   0    	; 	; 	; 	;E E E E     AEST 	 	 	 	 	 	m m m m &*&*G G G G G G5 5 5 5p5 5 5 5    B   
$ 
$ 
$ 
$R R R Rj   4; ; ; ;7 7 7 7. . . . . .r-   