""" 通用的信息匹配分析模块 分析 中的字面语义匹配关系 适用于任何信息匹配场景 提供两个接口: 1. match_single(b_content, a_content, model_name, b_context="", a_context="") - 单个匹配 2. match_batch(b_items, a_content, model_name, b_context="", a_context="") - 批量匹配 支持可选的 Context 参数: - b_context: B 的补充上下文(帮助理解 B) - a_context: A 的补充上下文(帮助理解 A) - Context 默认为空,不提供时不会出现在 prompt 中 """ import json from typing import List from agents import Agent, Runner, ModelSettings from agents.tracing.create import custom_span from lib.client import get_model # ========== System Prompt ========== MATCH_SYSTEM_PROMPT = """ # 任务 分析 中的字面语义匹配关系。 ## 输入说明 - ****: 待匹配的内容(必选) - ****: 上下文内容(必选) - ****: B 的补充上下文(可选,帮助理解 B) - ****: A 的补充上下文(可选,帮助理解 A) **重要**:匹配分析发生在 之间,Context 仅作为补充理解的辅助信息。 ## 分析方法 ### 核心原则:字面语义匹配 只关注 在**字面词语和概念**上的重叠度,不考虑抽象关系。 ### 分析步骤 1. **提取关键词/概念** - 从 中提取:关键词语和核心概念 - 从 中提取:关键词语和核心概念 2. **识别相同部分** - 完全相同的词语(字面一致) - 同义词或近义词 3. **识别增量部分** - 中有,但 中没有的词语/概念 - 这些是 相对于 的额外信息 4. **计算匹配分数** - 基于相同部分的覆盖度 - 考虑词语/概念的重要性 --- ## 评分标准(0-1分) **字面匹配度评分:** - **0.9-1.0**: 几乎完全一致,词语高度重叠 - **0.7-0.8**:大部分核心词语/概念匹配,少量增量 - **0.5-0.6**:部分核心词语/概念匹配,有一定增量 - **0.3-0.4**:少量词语/概念匹配,大部分不同 - **0.1-0.2**:几乎无字面匹配,仅有概念联系 - **0.0**:完全无关 **重要原则:** - 如果 是抽象/元级别的描述,而 是具体内容,字面上无词语重叠,应给低分(0.1-0.3) - 优先考虑具体词语的匹配,而非抽象概念的包含关系 --- ## 输出格式(严格JSON) ```json { "score": 0.75, "score说明": "简要说明分数是如何计算的,基于哪些词语/概念的匹配", "相同部分": { "B中的词1": "与A中的'某词'完全相同", "B中的词2": "与A中的'某词'同义" }, "增量部分": { "B中的词3": "A中无此概念" } } ``` **输出要求**: 1. 必须严格按照上述JSON格式输出(score 和 score说明在最前面) 2. 所有字段都必须填写 3. **score字段**:必须是0-1之间的浮点数,保留2位小数 4. **score说明**:必须简洁说明评分依据(基于相同部分的覆盖度) 5. **相同部分**:字典格式,key是中的词语,value说明它与中哪个词的关系(完全相同/同义);如果没有则填写空字典 {} 6. **增量部分**:字典格式,key是中的词语,value说明为什么是增量(如"A中无此概念");如果没有增量部分,填写空字典 {} 7. **关键约束**:相同部分和增量部分的key必须只能是中的词语,不能是中的词语 """.strip() def create_match_agent(model_name: str) -> Agent: """创建信息匹配分析的 Agent Args: model_name: 模型名称 Returns: Agent 实例 """ agent = Agent( name="Information Match Expert", instructions=MATCH_SYSTEM_PROMPT, model=get_model(model_name), model_settings=ModelSettings( temperature=0.0, max_tokens=65536, ), tools=[], ) return agent def parse_match_response(response_content: str) -> dict: """解析匹配响应 Args: response_content: Agent 返回的响应内容 Returns: 解析后的字典 """ try: # 如果响应包含在 markdown 代码块中,提取 JSON 部分 if "```json" in response_content: json_start = response_content.index("```json") + 7 json_end = response_content.index("```", json_start) json_text = response_content[json_start:json_end].strip() elif "```" in response_content: json_start = response_content.index("```") + 3 json_end = response_content.index("```", json_start) json_text = response_content[json_start:json_end].strip() else: json_text = response_content.strip() return json.loads(json_text) except Exception as e: print(f"解析响应失败: {e}") return { "相同部分": {}, "增量部分": {}, "score": 0.0, "score说明": f"解析失败: {str(e)}" } def _create_batch_agent(model_name: str) -> Agent: """创建批量匹配的 Agent Args: model_name: 模型名称 Returns: Agent 实例 """ # 批量匹配的 System Prompt(在单个匹配基础上修改输出格式) batch_prompt = MATCH_SYSTEM_PROMPT.replace( "## 输出格式(严格JSON)", "## 输出格式(JSON数组)\n对每个 输出一个匹配结果:" ).replace( "```json\n{", "```json\n[{" ).replace( "}\n```", "}]\n```" ) + "\n\n**额外要求**:数组长度必须等于 的数量,顺序对应" agent = Agent( name="Batch Information Match Expert", instructions=batch_prompt, model=get_model(model_name), tools=[], ) return agent async def _run_match_agent( agent: Agent, b_content: str, a_content: str, request_desc: str, b_context: str = "", a_context: str = "" ) -> str: """运行匹配 Agent 的公共逻辑 Args: agent: Agent 实例 b_content: B 的内容 a_content: A 的内容 request_desc: 请求描述(如"并输出 JSON 格式"或"并输出 JSON 数组格式") b_context: B 的上下文(可选) a_context: A 的上下文(可选) Returns: Agent 的原始输出 """ # 构建任务描述 b_section = f"\n{b_content}\n" if b_context: b_section += f"\n\n\n{b_context}\n" a_section = f"\n{a_content}\n" if a_context: a_section += f"\n\n\n{a_context}\n" task_description = f"""## 本次分析任务 {b_section} {a_section} 请严格按照系统提示中的要求分析 中的字面语义匹配关系,{request_desc}的结果。""" # 构造消息 messages = [{ "role": "user", "content": [ { "type": "input_text", "text": task_description } ] }] # 使用 custom_span 追踪匹配过程 # 截断显示内容,避免 span name 过长 b_short = (b_content[:40] + "...") if len(b_content) > 40 else b_content a_short = (a_content[:40] + "...") if len(a_content) > 40 else a_content with custom_span( name=f"匹配分析: {b_short} in {a_short}", data={ "B": b_content, "A": a_content, "B_Context": b_context if b_context else None, "A_Context": a_context if a_context else None, "模式": request_desc } ): # 运行 Agent result = await Runner.run(agent, input=messages) return result.final_output async def match_single( b_content: str, a_content: str, model_name: str, b_context: str = "", a_context: str = "" ) -> dict: """单个匹配:分析一个 B 在 A 中的匹配 Args: b_content: B(待匹配)的内容 a_content: A(上下文)的内容 model_name: 使用的模型名称 b_context: B 的补充上下文(可选,默认为空) a_context: A 的补充上下文(可选,默认为空) Returns: 匹配结果字典:{"相同部分": {}, "增量部分": {}, "score": 0.0, "score说明": ""} """ try: # 创建 Agent agent = create_match_agent(model_name) # 运行匹配 output = await _run_match_agent( agent, b_content, a_content, "并输出 JSON 格式", b_context=b_context, a_context=a_context ) # 解析响应 parsed_result = parse_match_response(output) return parsed_result except Exception as e: return { "相同部分": {}, "增量部分": {}, "score": 0.0, "score说明": f"匹配过程出错: {str(e)}" } async def match_batch( b_items: List[str], a_content: str, model_name: str, b_context: str = "", a_context: str = "" ) -> List[dict]: """批量匹配:分析多个 B 在 A 中的匹配(一次调用) Args: b_items: B列表(多个待匹配项) a_content: A(上下文)的内容 model_name: 使用的模型名称 b_context: B 的补充上下文(可选,默认为空) a_context: A 的补充上下文(可选,默认为空) Returns: 匹配结果列表:[{"相同部分": {}, "增量部分": {}, "score": 0.0, "score说明": ""}, ...] """ try: # 创建批量匹配 Agent agent = _create_batch_agent(model_name) # 构建 B 列表字符串 b_list_str = "\n".join([f"- {item}" for item in b_items]) # 运行匹配 output = await _run_match_agent( agent, b_list_str, a_content, "并输出 JSON 数组格式", b_context=b_context, a_context=a_context ) # 解析响应(期望是数组) parsed_result = parse_match_response(output) # 如果返回的是数组,直接返回;如果是单个对象,包装成数组 if isinstance(parsed_result, list): return parsed_result else: return [parsed_result] except Exception as e: # 返回错误信息(为每个 B 创建一个错误条目) return [{ "相同部分": {}, "增量部分": {}, "score": 0.0, "score说明": f"匹配过程出错: {str(e)}" } for _ in b_items]