"""公司知识库模块 — 公司匹配和证据排序。""" import re from typing import Optional def _normalize_loose(input_str: str) -> str: """模糊标准化:小写 + 去除括号/分隔符。""" s = input_str.lower() s = re.sub(r"[\s\-_·.()()\[\]{}]", "", s) s = s.replace("x", "") return s def _score_profile(input_str: str, aliases: list[str], tags: list[str]) -> int: """评分公司匹配度。""" normalized = _normalize_loose(input_str) score = 0 for alias in aliases: if _normalize_loose(alias) in normalized or normalized in _normalize_loose(alias): score += 4 for tag in tags: if _normalize_loose(tag) in normalized: score += 2 return score def match_company_knowledge( product_name: Optional[str] = None, company_hint: Optional[str] = None, forced_company_id: Optional[str] = None, companies: list[dict] = None, products: list[dict] = None, ) -> dict: """匹配公司知识库。 Args: product_name: 产品名称 company_hint: 公司名称提示 forced_company_id: 强制指定公司 ID companies: 公司配置列表 products: 产品配置列表 Returns: CompanyKnowledgeMatch dict """ companies = companies or [] products = products or [] # 1. 强制指定 if forced_company_id: company = next((c for c in companies if c.get("id") == forced_company_id), None) if company: return { "companyId": company["id"], "companyName": company.get("displayName", ""), "evidenceFiles": [], "confidence": 1.0, "matchedBy": "company_forced", } # 2. 产品目录匹配 if product_name: normalized = product_name.lower() for prod in products: aliases = prod.get("aliases", []) if any(_normalize_loose(a) in normalized for a in aliases): company_id = prod.get("companyId") company = next((c for c in companies if c.get("id") == company_id), None) if company: return { "companyId": company["id"], "companyName": company.get("displayName", ""), "evidenceFiles": [], "confidence": 1.0, "matchedBy": "product_catalog", } # 3. 别名/标签模糊匹配 best_match = None best_score = 0 search_text = f"{product_name or ''} {company_hint or ''}" for company in companies: aliases = company.get("aliases", []) tags = company.get("aliases", []) # 用别名作为标签 score = _score_profile(search_text, aliases, tags) if score > best_score: best_score = score best_match = company if best_match and best_score > 0: confidence = min(0.95, 0.45 + best_score / 20) return { "companyId": best_match["id"], "companyName": best_match.get("displayName", ""), "evidenceFiles": [], "confidence": confidence, "matchedBy": "company_alias", } return { "companyId": "", "companyName": "", "evidenceFiles": [], "confidence": 0, "matchedBy": "unknown", } def expected_company_id_for_product( product_name: Optional[str], products: list[dict] = None, ) -> Optional[str]: """通过产品名查找对应公司 ID。""" if not product_name: return None products = products or [] normalized = product_name.lower() for prod in products: aliases = prod.get("aliases", []) if any(_normalize_loose(a) in normalized for a in aliases): return prod.get("companyId") return None