| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749 |
- """
- 通用的信息匹配分析模块
- 分析 <B> 在 <A> 中的字面语义匹配关系
- 适用于任何信息匹配场景
- 提供两个接口:
- 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
- from agents.tracing.create import custom_span
- from lib.client import get_model
- # ========== System Prompt ==========
- MATCH_SYSTEM_PROMPT = """
- # 任务
- 分析 <B> 在 <A> 中的字面语义匹配关系。
- ## 输入说明
- - **<B></B>**: 待匹配的内容(必选)
- - **<A></A>**: 上下文内容(必选)
- - **<B_Context></B_Context>**: B 的补充上下文(可选,帮助理解 B)
- - **<A_Context></A_Context>**: A 的补充上下文(可选,帮助理解 A)
- **重要**:匹配分析发生在 <B> 和 <A> 之间,Context 仅作为补充理解的辅助信息。
- ## 分析方法
- ### 核心原则:字面语义匹配
- 只关注 <B> 和 <A> 在**字面词语和概念**上的重叠度,不考虑抽象关系。
- ### 分析步骤
- 1. **提取关键词/概念**
- - 从 <B> 中提取:关键词语和核心概念
- - 从 <A> 中提取:关键词语和核心概念
- 2. **识别相同部分**
- - 完全相同的词语(字面一致)
- - 同义词或近义词
- 3. **识别增量部分**
- - <B> 中有,但 <A> 中没有的词语/概念
- - 这些是 <B> 相对于 <A> 的额外信息
- 4. **计算匹配分数**
- - 基于相同部分的覆盖度
- - 考虑词语/概念的重要性
- ---
- ## 评分标准(0-1分)
- **字面匹配度评分:**
- - **0.9-1.0**:<B> 和 <A> 几乎完全一致,词语高度重叠
- - **0.7-0.8**:大部分核心词语/概念匹配,少量增量
- - **0.5-0.6**:部分核心词语/概念匹配,有一定增量
- - **0.3-0.4**:少量词语/概念匹配,大部分不同
- - **0.1-0.2**:几乎无字面匹配,仅有概念联系
- - **0.0**:完全无关
- **重要原则:**
- - 如果 <A> 是抽象/元级别的描述,而 <B> 是具体内容,字面上无词语重叠,应给低分(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是<B>中的词语,value说明它与<A>中哪个词的关系(完全相同/同义);如果没有则填写空字典 {}
- 6. **增量部分**:字典格式,key是<B>中的词语,value说明为什么是增量(如"A中无此概念");如果没有增量部分,填写空字典 {}
- 7. **关键约束**:相同部分和增量部分的key必须只能是<B>中的词语,不能是<A>中的词语
- """.strip()
- # ========== System Prompt for Name+Definition Match ==========
- MATCH_WITH_DEFINITION_PROMPT = """
- # 任务
- 分析 <B> 与 <A> 的语义匹配关系。<A> 包含"名称"和"定义",定义作为辅助上下文帮助理解名称的含义。
- ## 输入说明
- - **<B></B>**: 待匹配的内容(灵感点)
- - **<A></A>**: 名称(必选)
- - **<B_Context></B_Context>**: B 的补充上下文(可选)
- - **<A_Context></A_Context>**: A 的补充上下文,包含定义等信息(可选)
- **重要**:A_Context 中的定义仅用于辅助理解名称 <A> 的含义,主要分析 <B> 与 <A> 的匹配关系。
- ## 分析方法
- ### 核心原则:语义概念匹配
- 先独立分析 <B> 和 <A> 的语义,再进行匹配,避免强行解释。
- ### 分析步骤
- **第一步:独立分析 <B> 的语义**
- 对 <B>(灵感点)进行语义分解,提取:
- **拆分步骤**:
- 1. 先识别出所有语义成分(主体、动作/状态、属性、修饰等)
- 2. 对每个成分做"去除测试":
- - 如果去掉这个成分后,剩余部分的核心语义**没有发生本质变化** → 该成分是**形式**
- - 如果去掉这个成分后,剩余部分的核心语义**发生了本质变化** → 该成分是**实质**
- 3. 将成分分类到实质或形式中,每个成分只能出现在一个类别
- - **实质语义**:核心概念、本质内容(字典格式)
- - 通常为1-2个核心概念
- - 不要过度拆分复合词,保持核心概念的完整性
- - key:语义片段(可以是词组)
- - value:**客观地说明这个片段在B的上下文中的具体含义**
- - **形式语义**:具体限定、修饰、程度等(字典格式)
- - 包括:主体限定词、属性修饰词、程度副词、时间/空间限定等
- - key:语义片段
- - value:**说明在B的上下文中修饰/限定什么,如何修饰/限定**
- **关键原则**:
- - 每个语义片段只能出现在一个类别中,不要重复
- - 表示主体、场景、对象的限定词通常属于形式,不要合并到实质中
- - 不要孤立地解释词语本身,要说明它**在当前B的上下文中**的具体含义
- - 不要过度推理,保持客观:描述B字面上说的是什么,而不是可能引发什么
- **注意**:纯粹分析 <B> 本身的语义,不要考虑与 <A> 的关系。
- **第二步:独立分析 <A> 的语义**
- 对 <A>(名称)进行语义分解,提取:
- - **实质语义**:核心概念、本质内容(字典格式)
- - **判断标准:单独拿出来看,语义跟原来相比没有发生本质变化**
- - key:语义片段(可以是词组,保持完整的核心概念)
- - value:**客观地说明这个片段在A的上下文中的具体含义和作用**
- - **形式语义**:具体限定、修饰等(字典格式)
- - **判断标准:去掉后,核心概念本身不会丢失,只是失去了限定、修饰等细节**
- - key:语义片段
- - value:**说明在A的上下文中修饰/限定什么,如何修饰/限定**
- **关键**:
- - 可以参考<A_Context>中的定义来辅助理解名称的含义,但只分析名称本身
- - 同样要说明语义片段**在当前A的上下文中**的具体含义,不要孤立解释
- - 不要过度拆分复合词,保持核心概念的完整性
- **注意**:纯粹分析 <A> 本身的语义,不要考虑与 <B> 的关系。
- **第三步:建立匹配关系**
- **只匹配实质语义**,形式语义作为补充信息不参与匹配。
- 遍历 <B> 的每个**实质语义**片段,找到与 <A> 中最接近的**实质语义**,判断关系:
- **重要**:
- - 在匹配关系中必须复述B语义说明和A语义说明,直接从前面的语义分析中复制过来
- - 形式语义(修饰词、限定词等)不单独匹配,因为它们脱离实质语义就失去意义
- - 形式语义的差异可以在score说明中提及,作为参考信息
- 1. **判断是否可比较**(基于抽象层级和本体范畴)
- - **参考B语义说明和A语义说明**,判断两个语义是否处于可比较的层级
- - **不可比较的情况**(直接标记为"无关"):
- - **元概念 vs 具体概念**:元概念(如"概念"、"内容"、"事物"、"要素"、"方面"、"维度"、"属性"等用于描述其他概念的抽象术语)不能与具体概念(如"期待"、"行为"、"猫咪"等实际指代特定事物的词)比较
- - **抽象层级相差过大**:相差2级以上(如"存在" vs "猫的行为")
- - **本体范畴完全不同**:不同的基本范畴(如数学概念 vs 生物实体、物理对象 vs 心理状态等,除非有明确的跨域映射关系)
- - **可比较的情况**:
- - 同级具体概念(如"期待" vs "焦虑")
- - 有明确上下位关系(如"情感" vs "期待")
- - 有交叉或关联关系(如"工作压力" vs "假期期待")
- - **输出要求**:
- - 如果不可比较:输出 "是否可比较": false, "不可比较原因": "说明为什么不可比较", "关系": "无关"
- - 如果可比较:输出 "是否可比较": true, 继续判断关系类型
- 2. **确定关系类型**(仅对可比较的语义)
- - **同义**:在各自上下文中的含义完全相同,可以互相替换
- - **近义**:在各自上下文中的含义非常接近,但有细微差异
- - **上位词**:B在其上下文中的含义 比 A在其上下文中的含义 更抽象、更上位(B ⊇ A)
- - **下位词**:B在其上下文中的含义 比 A在其上下文中的含义 更具体、更下位(B ⊆ A)
- - **无关**:虽然可比较,但没有明确的语义关系
- 3. **计算规则分数**
- - 关系得分:同义=1.0, 近义=0.9, 上位词=0.6, 下位词=0.4, 无关=0.0
- - 只基于实质语义匹配计算规则分数
- - 计算公式:
- ```
- 规则分数 = Σ(实质语义的关系得分) / 实质语义总数
- ```
- 4. **评估整体相关性**
- - 在完成上述匹配关系分析后,从宏观角度评估 <B> 和 <A> 的整体相关性
- - 考虑因素:
- - 语义距离:核心概念之间的距离
- - 上下文契合度:在各自上下文中的关联程度
- - 表达意图的一致性
- - **评分标准**(严格遵守):
- - **1.0**:<B> 和 <A> 完全同义,可以互相替换,表达完全相同的含义
- - **0.8-0.9**:高度相关,核心语义非常接近,只有细微差异
- - **0.6-0.7**:明显相关,有明确的语义重叠或上下位关系
- - **0.4-0.5**:一般相关,有一定的语义关联或主题相关性
- - **0.2-0.3**:弱相关,只有间接关联(如因果、触发等)
- - **0.0-0.1**:几乎无关或完全无关
- - 给出相关性分数(0-1之间的浮点数,保留2位小数)
- - 给出相关性说明
- 5. **计算最终分数**
- - 最终分数 = 0.8 × 规则分数 + 0.2 × 相关性分数
- - 在score说明中说明计算过程,可以提及形式语义的差异作为补充参考
- ---
- ## 评分标准(0-1分)
- **语义匹配度评分:**
- - **0.9-1.0**:<B> 的核心语义与名称/定义几乎完全对应
- - **0.7-0.8**:大部分核心语义匹配,少量增量
- - **0.5-0.6**:部分核心语义匹配,有一定增量
- - **0.3-0.4**:少量语义匹配,大部分概念不同
- - **0.1-0.2**:几乎无语义匹配,仅有弱关联
- - **0.0**:完全无关
- **重要原则:**
- - 关注有价值的语义片段,而非孤立的字面词语
- - 考虑概念之间的语义关系,不仅是字面匹配
- ---
- ## 输出格式(严格JSON)
- ```json
- {
- "B语义分析": {
- "实质": {
- "语义片段1": "说明"
- },
- "形式": {
- "语义片段2": "说明",
- "语义片段3": "说明"
- }
- },
- "A语义分析": {
- "实质": {
- "语义片段1": "说明",
- "语义片段2": "说明"
- },
- "形式": {
- "语义片段3": "说明"
- }
- },
- "匹配关系": [
- {
- "B语义": "语义片段1",
- "B语义说明": "从B实质语义分析中复述该片段的说明",
- "A语义": "语义片段1",
- "A语义说明": "从A实质语义分析中复述该片段的说明",
- "是否可比较": true,
- "关系": "下位词",
- "说明": "说明两者的关系"
- },
- {
- "B语义": "语义片段2",
- "B语义说明": "从B实质语义分析中复述该片段的说明",
- "A语义": "概念",
- "A语义说明": "从A实质语义分析中复述该片段的说明",
- "是否可比较": false,
- "不可比较原因": "A是元概念(用于描述其他概念的术语),B是具体概念,抽象层级完全不同",
- "关系": "无关",
- "说明": "元概念不能与具体概念比较"
- }
- ],
- "规则分数": 0.2,
- "规则分数说明": "B有2个实质语义,第1个是A的下位词(0.4),第2个无关(0.0),规则分数 = (0.4 + 0.0) / 2 = 0.2",
- "相关性分数": 0.3,
- "相关性说明": "虽然有部分语义重叠,但整体上下文契合度较低,因为B强调具体事物,A强调抽象概念",
- "score": 0.22,
- "score说明": "最终分数 = 0.8 × 0.2 + 0.2 × 0.3 = 0.22。形式语义差异:B有'修饰词x',A有'修饰词y',作为补充参考。"
- }
- ```
- **输出要求**:
- 1. 必须严格按照上述JSON格式输出
- 2. **B语义分析**:必须包含"实质"、"形式"两个字段,其中实质通常为1-2个核心概念
- 3. **A语义分析**:必须包含"实质"、"形式"两个字段
- 4. **实质和形式**:都是字典格式,key是语义片段,value是对该片段的说明
- 5. **匹配关系**:数组格式,**只包含实质语义的匹配**,字段顺序为:
- - B语义 → B语义说明 → A语义 → A语义说明 → 是否可比较
- - 如果是否可比较=false:添加"不可比较原因"字段
- - 关系 → 说明
- 6. **B语义说明和A语义说明**:必须从前面的实质语义分析中复述,保持一致
- 7. **是否可比较**:布尔值,true或false
- 8. **不可比较原因**:字符串,仅在是否可比较=false时提供,说明为什么不可比较
- 9. **关系**:必须是以下之一:"同义"、"近义"、"上位词"、"下位词"、"无关"
- 10. **规则分数**:0-1之间的浮点数,保留2位小数,基于匹配关系计算
- 11. **规则分数说明**:说明规则分数的计算依据
- 12. **相关性分数**:0-1之间的浮点数,保留2位小数,整体相关性评估
- 13. **相关性说明**:说明相关性分数的判断依据
- 14. **score**:0-1之间的浮点数,保留2位小数,= 0.8 × 规则分数 + 0.2 × 相关性分数
- 15. **score说明**:说明最终分数的计算过程,可以提及形式语义的差异作为补充
- """.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),
- tools=[],
- )
- return agent
- def calculate_score_by_code(match_result: dict) -> float:
- """根据匹配关系和相关性分数用代码计算最终score
- 计算公式:score = 0.8 × 规则分数 + 0.2 × 相关性分数
- 其中规则分数基于匹配关系计算,相关性分数由LLM给出
- Args:
- match_result: 匹配结果字典,包含 B语义分析、匹配关系、相关性分数
- Returns:
- 计算得出的最终score(0-1之间)
- Raises:
- ValueError: 当数据不完整时
- """
- # 关系得分映射
- RELATION_SCORES = {
- "同义": 1.0,
- "近义": 0.9,
- "上位词": 0.6,
- "下位词": 0.4,
- "无关": 0.0
- }
- b_semantics = match_result.get("B语义分析", {})
- match_relations = match_result.get("匹配关系", [])
- relevance_score = match_result.get("相关性分数")
- # 验证必需字段
- if not match_relations:
- raise ValueError("匹配关系为空,无法计算score_by_code")
- if not b_semantics:
- raise ValueError("B语义分析为空,无法计算score_by_code")
- if relevance_score is None:
- raise ValueError("相关性分数为空,无法计算score_by_code")
- # 提取B的实质语义片段
- b_essence = set(b_semantics.get("实质", {}).keys())
- if not b_essence:
- raise ValueError("B语义分析中实质为空,无法计算score_by_code")
- # 计算规则分数:基于实质语义的匹配关系
- essence_scores = []
- for relation in match_relations:
- b_semantic = relation.get("B语义", "")
- relation_type = relation.get("关系", "无关")
- score = RELATION_SCORES.get(relation_type, 0.0)
- # 验证该语义确实属于实质
- if b_semantic in b_essence:
- essence_scores.append(score)
- else:
- # 如果匹配关系中包含了非实质语义,报错
- raise ValueError(f"匹配关系中包含非实质语义: {b_semantic}")
- if not essence_scores:
- raise ValueError("匹配关系中没有实质语义,无法计算score_by_code")
- # 计算规则分数(基于匹配关系)
- rule_score = sum(essence_scores) / len(essence_scores)
- # 计算最终分数:0.8 × 规则分数 + 0.2 × 相关性分数
- final_score = 0.8 * rule_score + 0.2 * relevance_score
- return round(final_score, 2)
- 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对每个 <B> 输出一个匹配结果:"
- ).replace(
- "```json\n{",
- "```json\n[{"
- ).replace(
- "}\n```",
- "}]\n```"
- ) + "\n\n**额外要求**:数组长度必须等于 <B> 的数量,顺序对应"
- 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"<B>\n{b_content}\n</B>"
- if b_context:
- b_section += f"\n\n<B_Context>\n{b_context}\n</B_Context>"
- a_section = f"<A>\n{a_content}\n</A>"
- if a_context:
- a_section += f"\n\n<A_Context>\n{a_context}\n</A_Context>"
- task_description = f"""## 本次分析任务
- {b_section}
- {a_section}
- 请严格按照系统提示中的要求分析 <B> 在 <A> 中的字面语义匹配关系,{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]
- async def match_with_definition(
- b_content: str,
- element_name: str,
- element_definition: str,
- model_name: str,
- b_context: str = "",
- a_context: str = ""
- ) -> dict:
- """名称+定义匹配:分析 B 分别与名称、定义的语义匹配关系
- Args:
- b_content: B(待匹配)的内容
- element_name: 要素名称
- element_definition: 要素定义
- model_name: 使用的模型名称
- b_context: B 的补充上下文(可选,默认为空)
- a_context: A 的补充上下文(可选,默认为空)
- Returns:
- 匹配结果字典:{"名称匹配": {...}, "定义匹配": {...}}
- """
- try:
- # 创建使用新 prompt 的 Agent
- agent = Agent(
- name="Name+Definition Match Expert",
- instructions=MATCH_WITH_DEFINITION_PROMPT,
- model=get_model(model_name),
- tools=[],
- )
- # A 内容只包含名称,定义放在 A_Context 中
- a_content = element_name
- # 将定义添加到 A_Context(如果已有 a_context,则追加)
- if element_definition:
- definition_context = f"定义:{element_definition}"
- if a_context:
- full_a_context = f"{a_context}\n{definition_context}"
- else:
- full_a_context = definition_context
- else:
- full_a_context = a_context
- # 运行匹配
- output = await _run_match_agent(
- agent, b_content, a_content, "并输出 JSON 格式",
- b_context=b_context, a_context=full_a_context
- )
- # 解析响应
- parsed_result = parse_match_response(output)
- # 验证返回格式
- required_fields = [
- "B语义分析", "A语义分析", "匹配关系",
- "规则分数", "规则分数说明",
- "相关性分数", "相关性说明",
- "score", "score说明"
- ]
- missing_fields = [f for f in required_fields if f not in parsed_result]
- if missing_fields:
- return {
- "B语义分析": {
- "实质": {},
- "形式": {}
- },
- "A语义分析": {
- "实质": {},
- "形式": {}
- },
- "匹配关系": [],
- "规则分数": 0.0,
- "规则分数说明": f"解析失败:缺少字段 {missing_fields}",
- "相关性分数": 0.0,
- "相关性说明": f"解析失败:缺少字段 {missing_fields}",
- "score": 0.0,
- "score_by_code": 0.0,
- "score说明": f"解析失败:缺少字段 {missing_fields}"
- }
- # 计算程序score(如果数据不完整会抛出异常)
- score_by_code = calculate_score_by_code(parsed_result)
- parsed_result["score_by_code"] = score_by_code
- return parsed_result
- except Exception as e:
- return {
- "B语义分析": {
- "实质": {},
- "形式": {}
- },
- "A语义分析": {
- "实质": {},
- "形式": {}
- },
- "匹配关系": [],
- "规则分数": 0.0,
- "规则分数说明": f"匹配过程出错: {str(e)}",
- "相关性分数": 0.0,
- "相关性说明": f"匹配过程出错: {str(e)}",
- "score": 0.0,
- "score_by_code": 0.0,
- "score说明": f"匹配过程出错: {str(e)}"
- }
|