| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- """
- 批量匹配分析模块
- 分析单个特征与多个特征之间的语义匹配度(批量版本)
- 提供接口:
- analyze_batch_match(phrase_a, phrase_b_list, model_name) - 批量分析匹配度
- 返回格式:
- [
- {
- "特征": "...",
- "分数": 0.85,
- "说明": "..."
- },
- ...
- ]
- """
- from typing import List
- from agents import Agent, Runner, ModelSettings
- from agents.tracing.create import custom_span
- from lib.client import get_model
- from lib.utils import parse_json_from_text
- # ========== System Prompt ==========
- BATCH_MATCH_SYSTEM_PROMPT = """
- # 任务
- 分析单个特征 <A> 与多个特征 <B_List> 之间的语义匹配度。
- ## 输入说明
- - **<A></A>**: 待分析的特征(必选)
- - **<B_List></B_List>**: 多个特征列表(必选)
- **重要**:
- 1. 必须在同一个评分标准下对所有 B 进行评分,确保分数可比
- 2. **优先识别并给出高分**给与 <A> 相似度最高的特征
- 3. 严格区分高相似度和低相似度,避免分数过于集中
- ---
- ## 评分标准(0-1分)
- **核心原则**:从 <B_List> 中找出与 <A> 最相似的特征,给予最高分,其他按相似度递减。
- - **0.9-1.0**:几乎完全相同(同义词、可互换)
- - **0.7-0.9**:非常接近、高度相关(强关联、核心相关)
- - **0.5-0.7**:有一定关联(中等相关、间接关联)
- - **0.3-0.5**:关系较弱(弱相关、边缘关联)
- - **0.0-0.3**:几乎无关或完全无关
- **评分策略**:
- - 优先识别与 <A> 最相似的特征,给 0.7+ 高分
- - 对明显无关的特征,果断给 0.0-0.3 低分
- - 合理使用中间分数段,避免过度集中
- - 确保分数有梯度,体现明确的相似度差异
- ---
- ## 输出格式(严格JSON数组)
- ```json
- [
- {
- "特征": "第一个B的特征",
- "分数": 0.85,
- "说明": "简要说明评分依据"
- },
- {
- "特征": "第二个B的特征",
- "分数": 0.45,
- "说明": "简要说明评分依据"
- }
- ]
- ```
- **输出要求**:
- 1. 数组长度必须等于 <B_List> 的长度,顺序一一对应
- 2. 分数必须是0-1之间的浮点数,保留2位小数
- 3. 所有评分必须使用相同的标准,分数之间可比
- 4. **必须有明显的分数梯度**,最相似的给高分,不相关的给低分
- """.strip()
- def create_batch_match_agent(model_name: str) -> Agent:
- """创建批量匹配分析的 Agent
- Args:
- model_name: 模型名称
- Returns:
- Agent 实例
- """
- agent = Agent(
- name="Batch Match Expert",
- instructions=BATCH_MATCH_SYSTEM_PROMPT,
- model=get_model(model_name),
- model_settings=ModelSettings(
- temperature=0.0,
- max_tokens=65536,
- ),
- tools=[],
- )
- return agent
- def clean_json_text(text: str) -> str:
- """清理JSON文本中的常见错误
- Args:
- text: 原始JSON文本
- Returns:
- 清理后的JSON文本
- """
- import re
- # 1. 移除数组元素之间的异常字符(如 trib{)
- # 匹配模式:逗号后面跟着任意非空白字符,直到遇到正常的对象开始 {
- text = re.sub(r',\s*[a-zA-Z]+\s*\{', r',\n {', text)
- # 2. 移除对象之间的异常字符
- text = re.sub(r'\}\s*[a-zA-Z]+\s*\{', r'},\n {', text)
- return text
- def parse_batch_match_response(response_content: str) -> List[dict]:
- """解析批量匹配响应
- Args:
- response_content: Agent 返回的响应内容
- Returns:
- 解析后的字典列表
- """
- try:
- # 使用 parse_json_from_text 函数进行健壮的 JSON 解析
- result = parse_json_from_text(response_content)
- # 如果解析失败(返回空字典),尝试清理后再解析
- if not result:
- print(f"首次解析失败,尝试清理JSON文本后重新解析...")
- cleaned_text = clean_json_text(response_content)
- result = parse_json_from_text(cleaned_text)
- # 如果清理后仍然失败
- if not result:
- print(f"清理后仍解析失败: 无法从响应中提取有效JSON")
- return [{
- "特征": "",
- "分数": 0.0,
- "说明": "解析失败: 无法从响应中提取有效JSON"
- }]
- # 确保返回的是列表
- if not isinstance(result, list):
- return [result]
- return result
- except Exception as e:
- print(f"解析响应失败: {e}")
- return [{
- "特征": "",
- "分数": 0.0,
- "说明": f"解析失败: {str(e)}"
- }]
- async def analyze_batch_match(
- phrase_a: str,
- phrase_b_list: List[str],
- model_name: str = None
- ) -> List[dict]:
- """批量分析匹配度
- Args:
- phrase_a: 待分析的特征
- phrase_b_list: 多个特征列表
- model_name: 使用的模型名称(可选,默认使用 client.py 中的 MODEL_NAME)
- Returns:
- 匹配结果列表:[{"特征": "...", "分数": 0.85, "说明": "..."}, ...]
- """
- try:
- # 如果未指定模型,使用默认模型
- if model_name is None:
- from lib.client import MODEL_NAME
- model_name = MODEL_NAME
- # 创建 Agent
- agent = create_batch_match_agent(model_name)
- # 构建 B 列表字符串
- b_list_str = "\n".join([f"- {b}" for b in phrase_b_list])
- # 构建任务描述
- task_description = f"""## 本次分析任务
- <A>
- {phrase_a}
- </A>
- <B_List>
- {b_list_str}
- </B_List>
- 请分析 <A> 与 <B_List> 中每个特征的匹配度,输出 JSON 数组格式的结果。
- 重要:必须使用一致的评分标准!"""
- # 构造消息
- messages = [{
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": task_description
- }
- ]
- }]
- # 使用 custom_span 追踪分析过程
- # 截断显示内容,避免 span name 过长
- a_short = (phrase_a[:30] + "...") if len(phrase_a) > 30 else phrase_a
- with custom_span(
- name=f"批量匹配分析: {a_short} vs {len(phrase_b_list)}个特征",
- data={
- "phrase_a": phrase_a,
- "phrase_b_list": phrase_b_list,
- "b_count": len(phrase_b_list)
- }
- ):
- # 运行 Agent
- result = await Runner.run(agent, input=messages)
- # 解析响应
- parsed_result = parse_batch_match_response(result.final_output)
- # 验证返回的结果数量
- if len(parsed_result) != len(phrase_b_list):
- print(f"警告: 返回结果数量 ({len(parsed_result)}) 与输入数量 ({len(phrase_b_list)}) 不匹配")
- # 补齐或截断
- while len(parsed_result) < len(phrase_b_list):
- parsed_result.append({
- "特征": phrase_b_list[len(parsed_result)],
- "分数": 0.0,
- "说明": "结果数量不匹配,自动补齐"
- })
- parsed_result = parsed_result[:len(phrase_b_list)]
- return parsed_result
- except Exception as e:
- # 返回错误信息(为每个 B 创建一个错误条目)
- return [{
- "特征": b,
- "分数": 0.0,
- "说明": f"分析过程出错: {str(e)}"
- } for b in phrase_b_list]
|