| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- # -*- coding: utf-8 -*-
- """角色规则预测器。
- 按 ARCHITECTURE.md Phase 5 拆分建议,从 ``interface/predictor.py`` 迁出以下
- 角色/规则相关类与函数:
- - ``RoleRulePredictor`` — 角色规则预测(原 predictor.py 第 1613-2176 行)
- - ``RoleRuleFinalAdd`` — 正则补充最后一句实体角色(原 2178-2277 行)
- - ``TendereeRuleRecall`` — 招标人角色召回规则(原 2279-2557 行)
- - ``RoleGrade`` — 角色概率分级(原 2559-2952 行)
- - ``role_special_predictor`` — 站源特殊角色召回(原 9185-9198 行)
- 角色分类流程优化 Phase E 起,前三个召回类改为薄委托
- ``predictors/role_recall_engine.py::RoleRecallEngine``(规则外置 YAML +
- RoleContext 复用),类名与 ``predict`` 签名保持兼容。
- ``interface/predictor.py`` 仍 re-export 以上全部名称,老 import 不受影响。
- """
- from __future__ import absolute_import
- import re
- import json
- import threading
- from BiddingKG.dl.common.logging import log
- from BiddingKG.dl.common.context_utils import spanWindow, get_context
- from BiddingKG.dl.predictors._common import is_agency
- __all__ = [
- "RoleRulePredictor",
- "RoleRuleFinalAdd",
- "TendereeRuleRecall",
- "RoleGrade",
- "role_special_predictor",
- ]
- # ----------------------------------------------------------------------------
- # Phase E:三个召回类统一委托 RoleRecallEngine
- # ----------------------------------------------------------------------------
- # 原三个类各自内联正则、重复构建上下文的实现已合并到
- # ``predictors/role_recall_engine.py``:
- # - 规则外置:dl/rules/patterns/role_context.yaml(三组角色模式 + 金额召回)
- # 与 role_fallback.yaml(final_* 文末兜底、recall_* 招标人 7 级上下文链);
- # - 上下文复用:Phase A RoleContext(extract.py 入口构建一次,
- # prem / roleRule / tendereeRuleRecall 共享,contexts=None 时引擎自建)。
- # 此处保留旧类名与 predict 签名作为薄委托,保证 ``getPredictor`` 旧 key
- # (roleRule / roleRuleFinal / tendereeRuleRecall)与老 import 不变。
- # ----------------------------------------------------------------------------
- _ENGINE_LOCK = threading.Lock()
- _ENGINE = None
- def _get_recall_engine():
- """模块级引擎单例(惰性创建,线程安全;模式经 RuleLoader 编译缓存)。"""
- global _ENGINE
- if _ENGINE is None:
- with _ENGINE_LOCK:
- if _ENGINE is None:
- from BiddingKG.dl.predictors.role_recall_engine import RoleRecallEngine
- _ENGINE = RoleRecallEngine()
- return _ENGINE
- class RoleRulePredictor(object):
- """角色/金额上下文召回(原实现已合并至 RoleRecallEngine.predict_role_rule)。
- 兼容保留的旧属性/方法:``SET_NOT_TENDERER``、``_check_input``、
- ``ser_role``、``rule_predict``(ser_role/rule_predict 的 pattern_list
- 参数需传编译后的 Pattern,规则以 YAML 为准)。
- """
- def __init__(self):
- self._engine = _get_recall_engine()
- @property
- def SET_NOT_TENDERER(self):
- return self._engine.SET_NOT_TENDERER
- def _check_input(self, text, ignore=False):
- return self._engine._check_input(text, ignore)
- def ser_role(self, pattern_list, text, entity_text):
- return self._engine.ser_role(pattern_list, text, entity_text)
- def rule_predict(self, before, center, after, entity_text):
- return self._engine.rule_predict(before, center, after, entity_text)
- def predict(self, list_articles, list_sentences, list_entitys, list_codenames,
- channel_dic, title, on_value=0.5, all_winner=False, req_scope=[],
- deposit_project=False, contexts=None):
- """角色/金额上下文召回。
- :param contexts: build_role_contexts 预计算上下文(extract.py 入口已构建,
- 与 prem 共享);缺省时引擎内部自建(老调用方行为不变)。
- :return: all_tenderer 集合(所有召回的投标人实体文本)
- """
- return self._engine.predict_role_rule(
- list_articles, list_sentences, list_entitys, list_codenames,
- channel_dic, title, on_value=on_value, all_winner=all_winner,
- req_scope=req_scope, deposit_project=deposit_project,
- contexts=contexts)
- '''正则补充最后一句实体日期格式为招标或代理 2021/12/30'''
- class RoleRuleFinalAdd(object):
- """文末/全文格式兜底召回(原实现已合并至 RoleRecallEngine.predict_final_add,
- 规则外置到 role_fallback.yaml final_*)。"""
- def __init__(self):
- self._engine = _get_recall_engine()
- def predict(self, list_articles, list_sentences, list_entitys, list_codenames):
- return self._engine.predict_final_add(
- list_articles, list_sentences, list_entitys, list_codenames)
- # 招标人角色召回规则
- class TendereeRuleRecall(object):
- """招标人兜底召回(原实现已合并至 RoleRecallEngine.predict_recall,
- 规则外置到 role_fallback.yaml recall_*;原 entity_context_rule /
- subject_rule 对应引擎 _recall_entity_context / _recall_subject)。
- 兼容保留实例属性 ``get_tenderee``(最近一次 predict 是否召回招标人)。
- """
- def __init__(self):
- self._engine = _get_recall_engine()
- self.get_tenderee = False
- def predict(self, list_articles, list_sentences, list_entitys, list_codenames,
- contexts=None):
- """招标人兜底召回:7 级上下文链 + 公告主语规则。
- :param contexts: build_role_contexts 预计算上下文(可选,缺省自建)。
- """
- self.get_tenderee = self._engine.predict_recall(
- list_articles, list_sentences, list_entitys, list_codenames,
- contexts=contexts)
- return self.get_tenderee
- class RoleGrade():
- def __init__(self):
- self.tenderee_left_9 = "(?P<tenderee_left_9>(招标|采购|遴选|寻源|竞价|议价|比选|询比?价|比价|评选|谈判|邀标|邀请|洽谈|约谈|选取|抽取|抽选)(人|方|单位))"
- self.tenderee_center_8 = "(?P<tenderee_center_8>受.{5,20}委托)"
- self.tenderee_left_8 = "(?P<tenderee_left_8>(尊敬的供应商|项目法人|(需求|最终|发包|征集|甲|转让|出租|处置)(人|方|单位|组织|用户|业主|主体|部门|公司)))"
- self.tenderee_left_6 = "(?P<tenderee_left_6>(业主|建设|委托)(人|方|单位|组织|用户|业主|主体|部门|公司|企业)|业主|买方)"
- self.tenderee_left_5 = "(?P<tenderee_left_5>(发布)(人|方|单位|组织|用户|业主|主体|部门|公司|企业)|买方|发布机构|申报单位名称|委托人)"
- self.agency_left_9 = "(?P<agency_left_9>代理)"
- self.winTenderer_left_9 = "(?P<winTenderer_left_9>(中标|中选|中价|成交|竞得)|第[1一](名|候选)|排[名序]:1|名次:1)"
- self.winTenderer_left_8 = "(?P<winTenderer_left_8>(入选供应商|供货商|乙方|最[终后]选[择取]))" # 229435497 最后选择西平,县中原彩印有限公司,作为此项目中标供应商,
- self.winTenderer_left_6 = "(?P<winTenderer_left_6>(入围|承[接建包修做制担租销]))"
- self.winTenderer_right_9 = "(?P<winTenderer_right_9>^(为(中标|成交|中选)(人|单位|供应商|公司)|以\d+[\d.,]+万?元中标))"
- self.secondTenderer_left_9 = "(?P<secondTenderer_left_9>(第[二2](中标|中选|中价|成交)?候选(人|单位|供应商|公司)|第[二2](名|候选)|排[名序]:2|名次:2))"
- self.thirdTenderer_left_9 = "(?P<thirdTenderer_left_9>(第[三3](中标|中选|中价|成交)?候选(人|单位|供应商|公司)|第[三3](名|候选)|排[名序]:3|名次:3))"
- self.pattern_list = [self.tenderee_left_9,self.tenderee_center_8, self.tenderee_left_8,self.tenderee_left_6,self.tenderee_left_5,self.agency_left_9,
- self.winTenderer_left_9,self.winTenderer_left_8, self.winTenderer_right_9, self.winTenderer_left_6, self.secondTenderer_left_9, self.thirdTenderer_left_9] # 概率要由高到低 274941849
- self.ree_pattern = re.compile('^([((][一二三四五六七八九十\d]+[))]|[一二三四五六七八九十]+\s*[..、])凡?对本次(招标|采购|公告内容)提出询问,')
- self.win_pattern = re.compile(
- "^([((][一二三四五六七八九十\d]+[))]|[一二三四五六七八九十]+\s*[..、])[预拟]?((中标|中选|成交|(采购|招标|比选|比价|定标)结果)((成交))?(人|单位|供应商)?的?(基本|主要)?(信息|情况|概况|结果)(如下)?|[预拟]?(中标|中选|成交)((成交))?(人|供应商|单位)(名称)?(、地址)?([及和]\w{,2}(中标|投标)(价格|报价|金额))?(如下)?|中标公示单位|(采购|招标|比选|比价|定标)结果)[,:]?$")
- def _extract_outline_scope(self, outlines):
- """提取中标/招标大纲的范围"""
- bid_info = [] # 中标大纲范围
- ree_info = [] # 招标大纲范围
- for outline in outlines:
- text_, title_type, title_index, next_index, scope = outline
- if re.search(self.win_pattern, text_) and re.search('(未|没|是否)(中标|成交)|中标单位合同签订主体|业绩|提供', text_) == None:
- bid_info.append(scope)
- # log("提取的中标大纲:%s, docid:%s"%(text_, docid))
- elif re.search(self.ree_pattern, text_):
- ree_info.append(scope)
- # log("提取的招标大纲:%s, docid:%s"%(text_, docid))
- return bid_info, ree_info
- def _correct_low_prob_entities(self, agency_like_tenderee, org_tenderee, low_prob_agency,
- low_prob_tenderee, agency_l, low_prob_winner, all_tenderee_agency):
- """修正低概率实体的角色标签"""
- # 代理伪装招标人修正
- for entity in agency_like_tenderee:
- if not is_agency(entity.entity_text) or entity.entity_text in org_tenderee:
- entity.label = 0
- entity.values[entity.label] = 0.6
- else:
- entity.values[entity.label] = 0.5
- # 低概率代理→招标人(若在招标人列表)
- for entity in low_prob_agency:
- if entity.entity_text in org_tenderee:
- entity.label = 0
- entity.values[entity.label] = 0.6
- # 低概率招标人→代理(若在代理列表)
- for entity in low_prob_tenderee:
- if entity.entity_text in agency_l:
- entity.label = 1
- entity.values[entity.label] = 0.6
- # 低概率中标人→非角色(若在招标/代理列表)
- for entity in low_prob_winner:
- if entity.entity_text in all_tenderee_agency:
- entity.label = 5
- def _correct_winner_tenderee_conflict(self, org_winner, org_tenderee):
- if org_winner != []:
- flag = 0
- if org_tenderee != []:
- for ent in org_winner:
- if ent.entity_text in org_tenderee:
- # log('如果org中标人同时为招标人角色,降低中标概率:%s, %s' % (ent.entity_text, ent.label))
- ent.values[2] = 0.6
- flag = 1
- # if flag == 0 and company_winner != []: # 2024/04/18 注释掉 避免提取不到 273351465 供应商(乙方:湖南省第二测绘院
- # for ent in org_winner:
- # if ent.label == 2 and ent.values[2] > 0.6:
- # # log('如果同时包含org和company中标人,降低org中标人概率为0.6:%s, %s' % (ent.entity_text, ent.values[2]))
- # ent.values[2] = 0.6
- def _supplement_outline_entities(self,
- bid_info_company, ree_info_company, have_winner, have_ree,
- span, docid, sentences
- ):
- if have_winner == False and bid_info_company: # 优化 638075055 二、推荐中标候选人信息,邯郸市婷元紧固件制造有限公司第一名,
- for entity in bid_info_company:
- text = sentences[entity.sentence_index].sentence_text
- b = entity.wordOffset_begin
- e = entity.wordOffset_end
- if re.search('(单位(名称)?|投标(单位|人)(名称)?|公司名称):$', text[max(0, b-span-2):b]) and entity.label == 5:
- entity.label = 2
- entity.values[entity.label] = 0.55
- log('大纲规则补充中标人:%s, docid:%s'%(entity.entity_text, docid))
- elif entity.label == 0 and entity.values[entity.label]>0.5 and re.search('(采购|招标)(单位|人)', text[max(0, b-span-2):b])==None:
- entity.values[entity.label] = 0.5
- log('中标大纲下招标人概率降低:%s, docid:%s'%(entity.entity_text, docid))
- # elif len(bid_info_company) == 1 and entity.label != 2:
- # entity.label = 2
- # entity.values[entity.label] = 0.55
- # log('大纲规则补充中标人:%s, docid:%s' % (entity.entity_text, docid))
- if have_ree == False and ree_info_company: # 优化 638075055 二、推荐中标候选人信息,邯郸市婷元紧固件制造有限公司第一名,
- for entity in ree_info_company:
- text = sentences[entity.sentence_index].sentence_text
- b = entity.wordOffset_begin
- e = entity.wordOffset_end
- if re.search('(单位名称|([^\w]|^)名称):$', text[max(0, b-span-2):b]) and entity.label == 5:
- entity.label = 0
- entity.values[entity.label] = 0.55
- log('大纲规则补充招标人:%s, docid:%s'%(entity.entity_text, docid))
- break
- def _resolve_role_conflict(self, role_text_ent_dic,role_text_type_dic, docid):
- role_text_type_dic = {k: ' '.join(v) for k, v in role_text_type_dic.items() if len(v)>1 and '0' in v}
- if role_text_type_dic:
- log("同一个公司名称包含多个角色:%s, docid:%s"%(json.dumps(role_text_type_dic, ensure_ascii=False), docid))
- for k in role_text_type_dic: # 冲突角色取概率最大的类别
- if k in role_text_ent_dic:
- if max(role_text_ent_dic[k]['招标概率']) >= max(role_text_ent_dic[k]['中标概率']):
- log("角色冲突公司招标概率比中标概率大,去掉中标角色,公司名:%s, docid:%s" % (k, docid))
- for entity in role_text_ent_dic[k]['实体列表']:
- if entity.label in [2,3,4] and entity.values[entity.label] >= 0.5:
- entity.values[entity.label] = 0.4
- else:
- for entity in role_text_ent_dic[k]['实体列表']:
- if entity.label in [0] and entity.values[entity.label] >= 0.5:
- entity.values[entity.label] = 0.4
- log("角色冲突公司中标概率比招标概率大,去掉招标角色,公司名:%s, docid:%s" % (k, docid))
- def predict(self, list_sentences, list_entitys, docid, original_docchannel, title, span=15, min_prob=0.7, outlines=[]):
- '''
- 根据规则给角色分配不同等级概率;分三级:0.9-1,0.8-0.9,0.7-0.8;附件0.7-0.8,0.6-0.7,0.5-0.6
- 修改概率小于0.6的且在大数据代理集合里面的招标人为代理人
- :param list_articles:
- :param list_sentences:
- :param list_entitys:
- :param codeName:
- :return:
- '''
- role_text_type_dic = {}
- role_text_ent_dic = {}
- # 提取大纲范围
- bid_info, ree_info = self._extract_outline_scope(outlines)
- have_winner = False
- have_ree = False
- bid_info_company = []
- ree_info_company = []
- sentences = sorted(list_sentences[0], key=lambda x:x.sentence_index)
- role2id = {"tenderee": 0, "agency": 1, "winTenderer": 2, "secondTenderer": 3, "thirdTenderer": 4}
- org_winner = []
- company_winner = []
- org_tenderee = []
- agency_l = []
- agency_like_tenderee = [] # 类似招标人的代理人实体列表
- low_prob_agency = []
- low_prob_tenderee = []
- low_prob_winner = []
- all_tenderee_agency = []
- is_bid_record = True if re.search('开标记录', title[-8:]) else False
- is_result_change = True if re.search('(结果|中标|成交)[^\w]?(变更|更正)', title[-8:]) else False
- # 第一步:遍历实体,基础概率调整与分类
- for entity in list_entitys[0]:
- if entity.entity_type in ['org', 'company'] and entity.label == 1 and re.search('银行', entity.entity_text): # 20260528 银行不可能做代理 779393255
- entity.values[entity.label] = 0.49
- continue
- if entity.entity_type in ['org', 'company'] and entity.label in [0, 1, 2, 3, 4] and entity.values[entity.label]> min_prob:
- text = sentences[entity.sentence_index].sentence_text
- in_att = sentences[entity.sentence_index].in_attachment
- pre_prob = entity.values[entity.label] # 模型预测角色概率
- b = entity.wordOffset_begin
- e = entity.wordOffset_end
- not_found = 1
- if re.search('(乙方:甲方:|甲方((买方)?,|:)乙方((卖方)?)?:)$', text[max(0, b-span):b]):
- entity.label = 0 if entity.entity_type == 'org' else 5 # 修复 290777022 乙方:甲方: 重庆机场集团有限公司 错分为中标
- entity.values[entity.label] = 0.55
- continue
- elif re.search('(采购|招标)人(?或其?(采购|招标)?代理机构)?', text[max(0, b-span-2):b]): # 修复 275206588 招标人或其招标代理机构:(盖章)
- entity.label = 1 if is_agency(entity.entity_text) else 0
- entity.values[entity.label] = 0.7 if entity.values[entity.label] > 0.7 else 0.6
- if in_att:
- entity.values[entity.label] -= 0.05
- continue
- elif re.search('(采购|招标|询比?价|遴选|寻源|比选)机构(名称)?[是为:]+$', text[max(0, b-span):b]) and entity.label == 1:
- agency_like_tenderee.append(entity)
- for pattern in self.pattern_list:
- if 'left' in pattern:
- context = text[max(0, b-span):b]
- elif 'right' in pattern:
- context = text[e:e+span]
- elif 'center' in pattern:
- context = text[max(0, b-span):e+span]
- else:
- print('规则错误', pattern)
- ser = re.search(pattern, context)
- if ser:
- groupdict = pattern.split('>')[0].replace('(?P<', '')
- _role, _direct, _prob = groupdict.split('_')
- _label = role2id.get(_role)
- if _label != entity.label:
- continue
- _prob = int(_prob)*0.1
- # print('规则修改角色概率前:', entity.entity_text, entity.label, entity.values)
- if in_att:
- _prob = _prob - 0.1 # 0.2
- if pre_prob < _prob: # 如果模型预测概率小于关键词概率
- _prob = 0.65
- if len(entity.entity_text) < 6 and re.search('大学|医院', entity.entity_text)==None: # 如果实体名称小于6个字,概率再降0.05
- _prob -= 0.05
- if re.search('(地址|联系方式):$', context): # 地址结尾的概率 概率降低
- _prob -= 0.05
- if _label == 0 and is_agency(entity.entity_text): # 20250116 修复 584333688 同时有招标单位 : 安徽省招标集团股份有限公司,.采购人信息 名 称:安徽开放大学
- _prob -= 0.1
- if re.search('成交电商:$', text[:b]): # 609280615 万银政采平台 罗山县政采平台等成交电商都不是中标人
- _prob = 0.55
- entity.values[_label] = _prob + entity.values[_label] / 20
- not_found = 0
- if is_result_change and _label == 2 and (re.search('原(公告)?(中标|中选|中价|成交|竞得)(人|单位|供应商)|原?第[一1](中标|中选|中价|成交)?候选人', context)
- or re.search('更正前', text[max(0, b-20):b])):
- entity.values[_label] = 0.5
- log('结果变更公告去除原中标人:%s, docid:%s'%(entity.entity_text, docid))
- if is_result_change and _label == 3 and re.search('原(排名)?第二', text[max(0, b-20):b]):
- entity.label = 2
- entity.values[2] = 0.6
- log('结果变更公告第二名变中标人:%s, docid:%s'%(entity.entity_text, docid))
- # print('规则修改角色概率后:', entity.entity_text, entity.label, entity.values)
- break
- if original_docchannel == 117 and re.search('卖方(名称)?:$', text[max(0, b-span):b]):
- entity.label = 0
- if not_found and entity.values[entity.label]> min_prob:
- _prob = min_prob - 0.1 if in_att else min_prob
- entity.values[entity.label] = _prob + entity.values[entity.label] / 20
- # print('找不到规则修改角色概率:', entity.entity_text, entity.label, entity.values)
- if entity.label == 2 and entity.values[entity.label]> min_prob:
- if entity.entity_type == 'org':
- org_winner.append(entity)
- elif entity.entity_type == 'company':
- company_winner.append(entity) # 保存中标人实体
- if entity.label == 0 and entity.values[entity.label]> min_prob:
- org_tenderee.append(entity.entity_text) # 保存所有招标人名称
- elif entity.label == 1 and entity.values[entity.label]> min_prob:
- agency_l.append(entity.entity_text)
- if entity.entity_text not in role_text_type_dic:
- role_text_type_dic[entity.entity_text] = set()
- # if entity.entity_text not in role_text_ent_dic:
- # role_text_ent_dic[entity.entity_text] = {
- # "招标概率": [],
- # "中标概率": [],
- # "实体列表": []
- # }
- if entity.label in [0, 2, 3, 4]:
- role_text_type_dic[entity.entity_text].add(str(entity.label))
- # role_text_ent_dic[entity.entity_text]["实体列表"].append(entity)
- # if entity.label == 0:
- # role_text_ent_dic[entity.entity_text]["招标概率"].append(entity.values[entity.label])
- # else:
- # role_text_ent_dic[entity.entity_text]["中标概率"].append(entity.values[entity.label])
- # if entity.entity_type in ['org', 'company'] and entity.label == 0 and entity.entity_text in agency_set and entity.values[entity.label]<0.6: # 修改概率小于0.6的且在大数据代理集合里面的招标人为代理人
- # # log('修改概率小于0.6的且在大数据代理集合里面的招标人为代理人%s:'%entity.entity_text)
- # entity.label = 1
- # entity.values[entity.label] = 0.5
- elif entity.entity_type in ['org', 'company'] and entity.label in [1, 0] and 0.5<=entity.values[entity.label]<0.6:
- if entity.label == 1:
- low_prob_agency.append(entity)
- else:
- low_prob_tenderee.append(entity)
- elif entity.entity_type in ['org', 'company'] and entity.label == 2 and 0.5<=entity.values[entity.label]<0.6:
- low_prob_winner.append(entity)
- if entity.entity_type in ['org', 'company'] and entity.label in [1, 0] and 0.6<entity.values[entity.label]: # 由0.5调为0.6,避免367217504 同时为低概率招标、中标被改
- all_tenderee_agency.append(entity.entity_text)
- if bid_info and entity.entity_type in ['org', 'company']: # 中标大纲下实体
- for scope in bid_info:
- if scope[0][0] == scope[1][0]:
- if entity.sentence_index == scope[0][0] and scope[0][1] < entity.wordOffset_begin and entity.wordOffset_end <= scope[1][1]:
- bid_info_company.append(entity)
- else:
- if entity.sentence_index == scope[0][0] and entity.wordOffset_begin > scope[0][1]:
- bid_info_company.append(entity)
- elif scope[0][0] < entity.sentence_index < scope[1][0]:
- bid_info_company.append(entity)
- elif entity.sentence_index == scope[1][0] and entity.wordOffset_end <scope[1][1]:
- bid_info_company.append(entity)
- if entity.label == 2 and entity.values[entity.label] > 0.5:
- have_winner = True
- if ree_info and entity.entity_type in ['org', 'company']: # 招标大纲下实体
- for scope in ree_info:
- if scope[0][0] == scope[1][0]:
- if entity.sentence_index == scope[0][0] and scope[0][1] < entity.wordOffset_begin and entity.wordOffset_end <= scope[1][1]:
- ree_info_company.append(entity)
- else:
- if entity.sentence_index == scope[0][0] and entity.wordOffset_begin > scope[0][1]:
- ree_info_company.append(entity)
- elif scope[0][0] < entity.sentence_index < scope[1][0]:
- ree_info_company.append(entity)
- elif entity.sentence_index == scope[1][0] and entity.wordOffset_end <scope[1][1]:
- ree_info_company.append(entity)
- if entity.label == 0 and entity.values[entity.label] > 0.5:
- have_ree = True
- if is_bid_record and entity.entity_type in ['org', 'company'] and entity.label == 2 and 0.5<=entity.values[entity.label]<0.8:
- entity.values[entity.label] = 0.4
- log('开标记录去掉不确定中标人:%s,docid:%s'%(entity.entity_text, docid))
- # 第二步:低概率实体修正
- self._correct_low_prob_entities(
- agency_like_tenderee, org_tenderee, low_prob_agency,
- low_prob_tenderee, agency_l, low_prob_winner, all_tenderee_agency
- )
- # 第三步:中标人/招标人冲突修正
- self._correct_winner_tenderee_conflict(org_winner, org_tenderee)
- # 第四步:大纲范围内实体补充
- self._supplement_outline_entities(
- bid_info_company, ree_info_company, have_winner, have_ree,
- span, docid, sentences
- )
- # # 第五步:角色冲突最终修正
- # self._resolve_role_conflict(role_text_ent_dic, role_text_type_dic, docid)
- # for entity in agency_like_tenderee:
- # if not is_agency(entity.entity_text) or entity.entity_text in org_tenderee:
- # entity.label = 0
- # entity.values[entity.label] = 0.6
- # else:
- # entity.values[entity.label] = 0.5
- # for entity in low_prob_agency: # 如果低概率代理在招标人列表,改为招标人
- # if entity.entity_text in org_tenderee:
- # entity.label = 0
- # entity.values[entity.label] = 0.6
- # for entity in low_prob_tenderee:
- # if entity.entity_text in agency_l:
- # entity.label = 1
- # entity.values[entity.label] = 0.6
- # for entity in low_prob_winner: # 如果低概率中标人在招标或代理列表,改为非角色
- # if entity.entity_text in all_tenderee_agency:
- # entity.label = 5
- # # elif entity.in_attachment: # 附件低概率中标角色不要 避免:516109391 桂林银行崇左宁明支行,宁明县城中镇兴宁大道中70号,预测为中标 20241126 注释掉,558294326 附件单个候选人漏提取
- # # entity.label = 5
- # if org_winner != []:
- # flag = 0
- # if org_tenderee != []:
- # for ent in org_winner:
- # if ent.entity_text in org_tenderee:
- # # log('如果org中标人同时为招标人角色,降低中标概率:%s, %s' % (ent.entity_text, ent.label))
- # ent.values[2] = 0.6
- # flag = 1
- # # if flag == 0 and company_winner != []: # 2024/04/18 注释掉 避免提取不到 273351465 供应商(乙方:湖南省第二测绘院
- # # for ent in org_winner:
- # # if ent.label == 2 and ent.values[2] > 0.6:
- # # # log('如果同时包含org和company中标人,降低org中标人概率为0.6:%s, %s' % (ent.entity_text, ent.values[2]))
- # # ent.values[2] = 0.6
- # if have_winner == False and bid_info_company: # 优化 638075055 二、推荐中标候选人信息,邯郸市婷元紧固件制造有限公司第一名,
- # for entity in bid_info_company:
- # text = sentences[entity.sentence_index].sentence_text
- # b = entity.wordOffset_begin
- # e = entity.wordOffset_end
- # if re.search('(单位(名称)?|投标(单位|人)(名称)?|公司名称):$', text[max(0, b-span-2):b]) and entity.label == 5:
- # entity.label = 2
- # entity.values[entity.label] = 0.55
- # log('大纲规则补充中标人:%s, docid:%s'%(entity.entity_text, docid))
- # elif entity.label == 0 and entity.values[entity.label]>0.5 and re.search('(采购|招标)(单位|人)', text[max(0, b-span-2):b])==None:
- # entity.values[entity.label] = 0.5
- # log('中标大纲下招标人概率降低:%s, docid:%s'%(entity.entity_text, docid))
- # # elif len(bid_info_company) == 1 and entity.label != 2:
- # # entity.label = 2
- # # entity.values[entity.label] = 0.55
- # # log('大纲规则补充中标人:%s, docid:%s' % (entity.entity_text, docid))
- # if have_ree == False and ree_info_company: # 优化 638075055 二、推荐中标候选人信息,邯郸市婷元紧固件制造有限公司第一名,
- # for entity in ree_info_company:
- # text = sentences[entity.sentence_index].sentence_text
- # b = entity.wordOffset_begin
- # e = entity.wordOffset_end
- # if re.search('(单位名称|([^\w]|^)名称):$', text[max(0, b-span-2):b]) and entity.label == 5:
- # entity.label = 0
- # entity.values[entity.label] = 0.55
- # log('大纲规则补充招标人:%s, docid:%s'%(entity.entity_text, docid))
- # break
- # role_text_type_dic = {k: ' '.join(v) for k, v in role_text_type_dic.items() if len(v)>1 and '0' in v}
- # if role_text_type_dic:
- # log("同一个公司名称包含多个角色:%s, docid:%s"%(json.dumps(role_text_type_dic, ensure_ascii=False), docid))
- def role_special_predictor(web_source_name, content, nlp_enterprise):
- if web_source_name == '中国电子科技集团有限公司电子采购平台':
- ser = re.search(',(\w{5,30}),发布时间:\d+', content)
- if ser and ser.group(1) in nlp_enterprise:
- return ser.group(1)
- elif web_source_name == '高校仪器设备竞价网':
- ser = re.search('--(\w{5,30}),申购单主题', content)
- if ser and ser.group(1) in nlp_enterprise:
- return ser.group(1)
- elif web_source_name == '台泥阳光采购平台':
- ser = re.search(',(\w{5,30})招标公告,', content)
- if ser and ser.group(1) in nlp_enterprise:
- return ser.group(1)
|