export type ModuleId = 'trade' | 'career' | 'partner' export type EngineId = 'universal' export type OutcomeDistribution = { winWin: number neutral: number mismatch: number } export type SuccessPanel = { success: number neutral: number failure: number stages: { currentNegotiation: number afterConcession: number fulfillment: number } stageModels: { currentNegotiation: { model: 'bargaining-scorecard' factors: { label: string; value: number; coefficient: number; contribution: number }[] } afterConcession: { model: 'mechanism-counterfactual' concessionGain: number concessionCost: number paretoImprovement: number factors: { label: string; value: number; coefficient: number; contribution: number }[] } fulfillment: { model: 'bayesian-survival' baseSurvival: number hazard: number factors: { label: string; value: number; coefficient: number; contribution: number }[] } } grade: 'advantage' | 'balanced' | 'danger' formulaVersion: string drivers: { positive: { label: string; value: number; impact: number }[] negative: { label: string; value: number; impact: number }[] } } export type EngineFeature = { key: string label: string category: string source: 'base' | 'open' value: number confidence: number weight: number evidence: string[] missing: boolean } export type DynamicFactorInput = { key?: string label: string category: string value: number confidence: number weight: number userWeight?: number otherWeight?: number riskWeight?: number refWeight?: number evidence?: string[] question?: string } export type ContractAnalysis = { moduleId: EngineId moduleName: string verdict: string decision: string score: number formulas: { generalizedDistribution: string referenceContract: string bilateralMatching: string } features: EngineFeature[] factorCoverage: { total: number observed: number coverage: number missingHighWeightFactors: string[] openFactorCount: number } requiredFactors: { total: number observed: number missing: string[] blocking: boolean } informationSufficiency: { level: 'insufficient' | 'preliminary' | 'sufficient' score: number blocking: boolean reasons: string[] userOnlyMissing: string[] searchableMissing: string[] userQuestions: string[] searchQuestions: string[] } complianceReview: { status: 'pass' | 'warning' | 'blocking' score: number missing: string[] risks: string[] checklist: { item: string status: 'observed' | 'missing' | 'risk' evidence: string[] }[] } distribution: OutcomeDistribution successPanel: SuccessPanel generalizedDistribution: { expectedUtility: number variance: number downsideProbability: number cvarLoss: number stateUtilities: { state: string; probability: number; utility: number }[] } referenceContract: { referenceAlignment: number expectationGap: number holdUpRisk: number renegotiationRisk: number fairnessReference: number } controlRights: { controlAlignment: number residualControlRisk: number investmentIncentiveFit: number stateContingencyReadiness: number recommendedAllocation: string missingControlQuestions: string[] } bayesianReliability: { prior: number posterior: number evidenceCount: number positiveEvidence: string[] negativeEvidence: string[] reliabilityLevel: 'high' | 'medium' | 'low' interpretation: string } bilateralMatching: { userUtility: number counterpartyUtility: number outsideOptionPressure: number totalSurplus: number nashProduct: number matchingEquilibrium: number } missingQuestions: string[] modelPayload: { engineVersion: string theoryBasis: string[] constraints: string[] } } type FactorSpec = { key: string label: string category: string base: number weight: number userWeight: number otherWeight: number riskWeight: number refWeight: number positive: string[] negative: string[] question: string required?: boolean gapType?: 'user-only' | 'searchable' | 'hybrid' } const universalFactorSpecs: FactorSpec[] = [ { key: 'user_core_need', label: '用户最核心需求', category: '需求画像/必要条件', base: 0.46, weight: 1, userWeight: 1, otherWeight: 0.45, riskWeight: 0.35, refWeight: 1, positive: ['成长', '赚钱', '收入', '稳定', '安全', '自由', '自主权', '履历', '平台', '实战', '家庭压力', '长期', '短期', 'AI产品', '热爱', '想要', '最重要', 'growth', 'money', 'income', 'stability', 'autonomy', 'resume value', 'family pressure', 'long-term', 'short-term'], negative: ['不知道想要什么', '纠结', '都想要', '不确定', 'unclear', 'not sure'], question: '你这次决策最想优先满足什么:成长、赚钱、稳定、自由、履历、关系、时间,还是别的?请按重要性排个顺序。', required: true, gapType: 'user-only', }, { key: 'need_priority_order', label: '需求优先级/取舍顺序', category: '需求画像/必要条件', base: 0.45, weight: 1, userWeight: 1, otherWeight: 0.4, riskWeight: 0.45, refWeight: 1, positive: ['优先', '第一', '更重要', '取舍', '排序', '底线', '可以接受', '不能接受', '宁愿', '相比', 'priority', 'tradeoff', 'prefer', 'rather', 'bottom line'], negative: ['都可以', '没想好', '不知道怎么选', '无法取舍'], question: '如果不能同时满足所有目标,你愿意牺牲什么,绝不能牺牲什么?', required: true, gapType: 'user-only', }, { key: 'time_horizon', label: '短期/长期时间视角', category: '需求画像/必要条件', base: 0.48, weight: 0.94, userWeight: 1, otherWeight: 0.45, riskWeight: 0.5, refWeight: 0.8, positive: ['短期', '长期', '一年', '两年', '三年', '五年', '未来', '现在', '阶段', '职业初期', 'long-term', 'short-term', '1 year', '2 years', '3 years', 'future', 'horizon'], negative: ['只看眼前', '没想过以后'], question: '你更看重未来几个月的确定收益,还是未来一到三年的成长和选择权?', required: true, gapType: 'user-only', }, { key: 'capability_fit', label: '能力/兴趣/资源匹配', category: '需求画像/必要条件', base: 0.5, weight: 0.94, userWeight: 1, otherWeight: 0.55, riskWeight: 0.45, refWeight: 0.75, positive: ['技能', '能力', '擅长', '匹配', '兴趣', '专业', '经验', '资源', '适合', 'skill', 'fit', 'ability', 'interest', 'experience', 'major'], negative: ['不适合', '没经验', '不会', '吃力', '排斥'], question: '这件事和你的技能、兴趣、经验、资源是否匹配?哪些地方你有优势,哪些地方明显短板?', required: true, gapType: 'user-only', }, { key: 'constraint_floor', label: '现实约束/最低底线', category: '需求画像/必要条件', base: 0.47, weight: 0.92, userWeight: 1, otherWeight: 0.45, riskWeight: 0.9, refWeight: 0.75, positive: ['底线', '最低', '房租', '现金流', '家庭', '父母', '贷款', '存款', '不能承受', '最坏结果', 'red line', 'runway', 'cash flow', 'family', 'worst case'], negative: ['没有退路', '承受不了', '撑不住'], question: '你的现实底线是什么:最低收入、最大亏损、最晚时间、家庭压力、最坏结果能不能承受?', required: true, gapType: 'user-only', }, ] export const legacyScenarioFactorBank: Record = { trade: [ { key: 'price_gap', label: '价格/可接受区间', category: '必要条件', base: 0.52, weight: 1, userWeight: 1, otherWeight: 0.7, riskWeight: 0.5, refWeight: 1, positive: ['预算', '报价', '心理价', '底价', '区间', '万', '元'], negative: ['差太多', '超预算', '虚高'], question: '双方真实报价、预算、底价和可接受区间分别是多少?', required: true }, { key: 'liquidity_need', label: '交易时间/急迫性', category: '必要条件', base: 0.45, weight: 0.86, userWeight: 0.6, otherWeight: 1, riskWeight: 0.25, refWeight: 0.55, positive: ['急', '月底', '马上', '尽快', '时间', '周期', '交付', '换房', '换新', '周转', '现金流'], negative: ['不急', '慢慢卖'], question: '双方期望交易时间、最晚成交时间和真实急迫性是什么?', required: true }, { key: 'buyer_execution', label: '买方履约能力', category: '履约能力', base: 0.48, weight: 0.9, userWeight: 0.8, otherWeight: 1, riskWeight: 0.55, refWeight: 0.45, positive: ['全款', '贷款批了', '首付', '资金到位', '马上签', '可当天'], negative: ['贷款不确定', '资金紧张', '审批'], question: '你的付款方式、资金到位时间和贷款确定性如何?' }, { key: 'asset_quality', label: '标的质量', category: '信息质量', base: 0.5, weight: 1, userWeight: 1, otherWeight: 0.55, riskWeight: 1, refWeight: 0.9, positive: ['保养记录', '无事故', '房龄新', '设备齐全', '流水真实', '检测'], negative: ['车况', '事故', '水泡', '火烧', '房龄', '漏水', '设备老化', '流水一般'], question: '标的质量有哪些可验证材料或第三方检测?' }, { key: 'title_clearance', label: '权属清晰度', category: '权属/法律事实', base: 0.52, weight: 1, userWeight: 0.9, otherWeight: 0.75, riskWeight: 1, refWeight: 0.85, positive: ['无抵押', '产权清晰', '可过户', '房东同意', '证照齐全'], negative: ['抵押', '查封', '贷款未结清', '房东不同意', '产权不清'], question: '是否存在抵押、查封、未解押、房东不同意或证照不全?' }, { key: 'transfer_friction', label: '交付与验收安排', category: '履约流程', base: 0.5, weight: 0.82, userWeight: 0.75, otherWeight: 0.75, riskWeight: 0.8, refWeight: 0.6, positive: ['过户', '交付', '移交', '交接', '监管', '尾款', '验收', '节点'], negative: ['迁入', '排放', '审批', '拖延', '手续复杂', '交付不清', '移交不清'], question: '交付、验收、尾款和后续交接节点如何安排?' }, { key: 'risk_allocation', label: '风险分配工具', category: '契约设计', base: 0.42, weight: 1, userWeight: 0.9, otherWeight: 0.85, riskWeight: 1, refWeight: 0.75, positive: ['检测', '押金', '尾款', '违约', '监管', '对赌', '退款', '合同'], negative: ['口头', '没合同', '不能检测', '不退'], question: '风险能否用检测、尾款、监管、违约或退款条款分配?' }, { key: 'residual_control', label: '后续控制权/最终拍板权', category: '控制权配置', base: 0.43, weight: 1, userWeight: 0.9, otherWeight: 0.85, riskWeight: 1, refWeight: 0.9, positive: ['最终决定', '书面确认', '共同确认', '验收标准', '监管账户', '第三方', '审批权'], negative: ['口头', '先签', '后面再说', '单方决定', '拒绝写', '说了算'], question: '未来合同没写清或发生争议时,谁拥有最终决定权?哪些事项需要双方共同确认?' }, { key: 'state_control_transfer', label: '状态触发控制权转移', category: '控制权配置', base: 0.42, weight: 0.9, userWeight: 0.86, otherWeight: 0.82, riskWeight: 0.95, refWeight: 0.82, positive: ['触发', '逾期', '不达标', '验收不通过', '暂停', '解除', '转移', '托管'], negative: ['没有退出', '无法停止', '继续付款', '不能暂停'], question: '逾期、不达标、验收失败、付款异常等状态出现时,控制权是否会自动暂停、转移或共同决策?' }, { key: 'market_anchor', label: '当前市场情况', category: '必要条件', base: 0.48, weight: 0.78, userWeight: 0.8, otherWeight: 0.65, riskWeight: 0.35, refWeight: 1, positive: ['市场价', '公允价值', '同小区', '同款', '行情', '均价', '挂牌', '成交价', '租售比', '多个', '多家', '竞争', 'offer'], negative: ['不清楚行情', '没比较'], question: '请补充可查的市场参照:同类标的/岗位/服务的近期成交价、挂牌价、薪酬水平、报价区间、租售比、竞争强度或替代收益。', required: true, gapType: 'searchable' }, { key: 'outside_option', label: '双方替代选择/BATNA', category: '必要条件', base: 0.46, weight: 0.76, userWeight: 0.85, otherWeight: 0.85, riskWeight: 0.35, refWeight: 0.65, positive: ['还有别的', '备选', '可替代', '其他买家', '其他房源', '交易失败', '没有损失', 'BATNA'], negative: ['唯一', '没选择', '只能'], question: '请补充双方替代选择:你还有哪些备选对象/offer/买家/供应商,对方还有哪些替代对象;交易失败双方各自损失什么。', required: true, gapType: 'hybrid' }, { key: 'trust_signal', label: '可信信号', category: '信息质量', base: 0.48, weight: 0.72, userWeight: 0.7, otherWeight: 0.65, riskWeight: 0.75, refWeight: 0.6, positive: ['透明', '记录', '发票', '证明', '可查', '第三方'], negative: ['隐瞒', '含糊', '不透明', '拒绝提供'], question: '关键材料是否透明、可查、可第三方验证?' }, { key: 'post_deal_cost', label: '成交后隐性成本', category: '长期结果', base: 0.5, weight: 0.72, userWeight: 0.9, otherWeight: 0.45, riskWeight: 0.75, refWeight: 0.55, positive: ['保修', '质保', '维修基金', '已保养'], negative: ['维修', '税费', '装修', '年检', '续租', '涨租'], question: '成交后税费、维修、装修、续租或维护成本是多少?' }, { key: 'relationship_continuity', label: '后续协作需要', category: '关系/协作', base: 0.45, weight: 0.58, userWeight: 0.55, otherWeight: 0.55, riskWeight: 0.45, refWeight: 0.45, positive: ['后续配合', '售后', '交接', '协助'], negative: ['一次性', '不配合', '失联'], question: '成交后是否还需要对方协助交接、售后或配合?' }, ], career: [ { key: 'pay_return', label: '薪酬/绩效回报', category: '经济结果', base: 0.5, weight: 1, userWeight: 1, otherWeight: 0.65, riskWeight: 0.75, refWeight: 1, positive: ['涨薪', '奖金', '绩效', '兑现', '明确'], negative: ['没涨薪', '没有涨薪', '绩效没有', '只说以后'], question: '投入是否换来了明确薪酬、绩效或奖金兑现?' }, { key: 'growth_asset', label: '成长资产', category: '长期收益', base: 0.5, weight: 0.98, userWeight: 1, otherWeight: 0.7, riskWeight: 0.35, refWeight: 0.75, positive: ['成长', '核心', '项目', '能力', '学习', '行业资源'], negative: ['重复', '杂活', '学不到'], question: '岗位提供了哪些可迁移能力和行业资源?' }, { key: 'recognition', label: '组织认可', category: '参照点', base: 0.46, weight: 0.85, userWeight: 0.9, otherWeight: 0.7, riskWeight: 0.55, refWeight: 0.95, positive: ['认可', '晋升', '明确', '授权', '核心'], negative: ['口头', '画饼', '不明确', '忽视'], question: '领导认可是否对应到权限、评级、晋升或资源?' }, { key: 'resource_support', label: '资源支持', category: '履约能力', base: 0.48, weight: 0.8, userWeight: 0.8, otherWeight: 0.85, riskWeight: 0.6, refWeight: 0.6, positive: ['预算', '人手', '资源', '支持', '授权'], negative: ['资源少', '没人', '没预算', '孤军'], question: '组织给了多少预算、人手、授权和跨部门支持?' }, { key: 'residual_control', label: '后续控制权/最终拍板权', category: '控制权配置', base: 0.42, weight: 1, userWeight: 0.95, otherWeight: 0.75, riskWeight: 1, refWeight: 0.95, positive: ['最终决定权', '授权', '拍板', '否决权', '负责人', '汇报线', '决策权', '书面确认'], negative: ['只负责', '没权限', '无授权', '领导拍板', '背锅', '口头', '不明确'], question: '你承担责任后,预算、人事、技术路线、跨部门协调或结果验收的最终拍板权归谁?' }, { key: 'state_control_transfer', label: '状态触发控制权转移', category: '控制权配置', base: 0.42, weight: 0.9, userWeight: 0.92, otherWeight: 0.7, riskWeight: 0.95, refWeight: 0.85, positive: ['目标调整', '升级机制', '暂停', '复盘', '阶段目标', '不达标', '授权升级'], negative: ['目标不变', '资源不变', '责任全背', '无法升级', '不能调整'], question: '当资源不到位、目标变化、跨部门不配合时,责任和控制权是否能重新分配?' }, { key: 'workload_cost', label: '工作负荷/身心成本', category: '成本损耗', base: 0.58, weight: 0.95, userWeight: 1, otherWeight: 0.35, riskWeight: 1, refWeight: 0.75, positive: ['可控', '稳定', '弹性'], negative: ['加班', '消耗', '身体', '压力', '熬夜', '焦虑'], question: '损耗是短期波动,还是长期稳定常态?' }, { key: 'promotion_path', label: '晋升路径清晰度', category: '参照点', base: 0.45, weight: 0.85, userWeight: 0.9, otherWeight: 0.65, riskWeight: 0.55, refWeight: 1, positive: ['晋升时间表', '路径', '标准', '明确'], negative: ['不清晰', '以后', '再说'], question: '晋升标准、时间表和负责人是否明确?' }, { key: 'outside_market', label: '外部机会', category: '外部选择', base: 0.48, weight: 0.8, userWeight: 0.9, otherWeight: 0.45, riskWeight: 0.4, refWeight: 0.65, positive: ['跳槽', '面试', 'offer', '外部机会', '市场'], negative: ['没机会', '不好找'], question: '外部机会在薪酬、成长和确定性上是否更好?' }, { key: 'political_risk', label: '组织政治/背责风险', category: '风险', base: 0.56, weight: 0.78, userWeight: 0.85, otherWeight: 0.45, riskWeight: 0.95, refWeight: 0.65, positive: ['责任清晰', '流程清楚'], negative: ['背锅', '甩锅', '权责不清', '抢功', '内耗'], question: '功劳、责任、权限是否对等和清晰?' }, { key: 'manager_contract', label: '领导契约稳定性', category: '关系契约', base: 0.48, weight: 0.76, userWeight: 0.75, otherWeight: 0.7, riskWeight: 0.7, refWeight: 0.85, positive: ['稳定', '兑现', '信任', '支持'], negative: ['变卦', '画饼', '压榨', '反复'], question: '领导过去承诺的兑现率如何?' }, { key: 'role_fit', label: '角色/能力匹配度', category: '匹配', base: 0.5, weight: 0.75, userWeight: 0.8, otherWeight: 0.8, riskWeight: 0.35, refWeight: 0.5, positive: ['擅长', '匹配', '兴趣', '优势', '适配'], negative: ['不适合', '痛苦', '排斥', '不匹配'], question: '这份安排与你的能力、资源、兴趣和长期方向匹配吗?' }, ], partner: [ { key: 'resource_reciprocity', label: '资源互惠', category: '经济结果', base: 0.48, weight: 1, userWeight: 1, otherWeight: 0.85, riskWeight: 0.45, refWeight: 0.85, positive: ['客户', '资源', '帮助', '回馈', '互相'], negative: ['只索取', '不回馈', '占便宜'], question: '对方实际贡献了哪些可验证资源,频率是否稳定?' }, { key: 'profit_fairness', label: '收益分配公平', category: '参照点', base: 0.5, weight: 1, userWeight: 1, otherWeight: 0.85, riskWeight: 0.8, refWeight: 1, positive: ['分配透明', '规则', '按贡献', '结算'], negative: ['不透明', '少分', '拖款', '抢利益'], question: '收益分配规则是否提前说清并稳定执行?' }, { key: 'commitment_reliability', label: '承诺兑现率', category: '履约能力', base: 0.48, weight: 0.95, userWeight: 0.9, otherWeight: 0.85, riskWeight: 0.9, refWeight: 0.95, positive: ['兑现', '准时', '稳定', '说到做到'], negative: ['变更', '拖', '临时', '反复', '失约'], question: '承诺落空是偶发,还是已经成为合作模式?' }, { key: 'responsibility_sharing', label: '责任共担', category: '风险', base: 0.52, weight: 0.95, userWeight: 1, otherWeight: 0.75, riskWeight: 1, refWeight: 0.85, positive: ['一起承担', '共担', '对外一起', '负责'], negative: ['背锅', '推责任', '让我解释', '甩锅'], question: '风险和责任是否长期由你单方面承担?' }, { key: 'information_transparency', label: '信息透明', category: '信息质量', base: 0.48, weight: 0.85, userWeight: 0.9, otherWeight: 0.75, riskWeight: 0.85, refWeight: 0.8, positive: ['透明', '同步', '公开', '可查'], negative: ['隐瞒', '不说', '不透明', '信息差'], question: '关键客户、成本、收益、风险信息是否透明?' }, { key: 'control_dependency', label: '关键依赖风险', category: '外部选择', base: 0.52, weight: 0.75, userWeight: 0.85, otherWeight: 0.45, riskWeight: 0.9, refWeight: 0.55, positive: ['可替代', '备选', '可退出'], negative: ['离不开', '绑定', '唯一', '被卡'], question: '你是否过度依赖对方的客户、渠道、账户或关键资源?' }, { key: 'residual_control', label: '后续控制权/最终拍板权', category: '控制权配置', base: 0.4, weight: 1, userWeight: 1, otherWeight: 0.9, riskWeight: 1, refWeight: 1, positive: ['共同决策', '否决权', '账号归属', '客户归属', '数据归属', 'IP归属', '最终决定权', '董事会', '投票权'], negative: ['单方决定', '对方说了算', '账号被卡', '客户被抢', '无否决权', '控制在对方'], question: '合同未覆盖的新情况出现时,账号、客户、数据、IP、预算和方向的最终控制权归谁?' }, { key: 'state_control_transfer', label: '状态触发控制权转移', category: '控制权配置', base: 0.42, weight: 0.9, userWeight: 0.92, otherWeight: 0.88, riskWeight: 0.95, refWeight: 0.9, positive: ['退出机制', '回购', '清算', '不达标', '违约', '触发', '暂停合作', '转让'], negative: ['无法退出', '长期绑定', '没有退出', '不清算', '继续投入'], question: '一方不投入、违约、收益不达标或合作破裂时,控制权如何回收、转移或清算?' }, { key: 'long_term_stability', label: '长期稳定性', category: '长期结果', base: 0.48, weight: 0.82, userWeight: 0.82, otherWeight: 0.82, riskWeight: 0.65, refWeight: 0.7, positive: ['一年', '半年', '长期', '稳定'], negative: ['反复', '波动', '临时', '突然'], question: '这种合作模式持续了多久,稳定性如何?' }, { key: 'boundary_clarity', label: '边界清晰度', category: '契约设计', base: 0.44, weight: 0.78, userWeight: 0.85, otherWeight: 0.75, riskWeight: 0.8, refWeight: 0.75, positive: ['合同', '规则', '边界', '节点', '清楚'], negative: ['口头', '模糊', '没说清'], question: '贡献、责任、收益和退出边界是否说清?' }, { key: 'reputation_signal', label: '信誉信号', category: '信息质量', base: 0.48, weight: 0.68, userWeight: 0.7, otherWeight: 0.7, riskWeight: 0.55, refWeight: 0.45, positive: ['口碑', '信誉', '介绍', '记录'], negative: ['投诉', '失信', '骗', '黑历史'], question: '对方过往合作口碑和履约记录如何?' }, ], } const contractCoreFactorSpecs: FactorSpec[] = [ { key: 'price_gap', label: '价格/成本可接受区间', category: '必要条件', base: 0.52, weight: 1, userWeight: 1, otherWeight: 0.7, riskWeight: 0.5, refWeight: 1, positive: ['预算', '报价', '价格', '费用', '成本', '底价', '区间', '万', '元', '薪酬', '分成', '佣金'], negative: ['差太多', '超预算', '虚高', '过低', '压价'], question: '双方真实价格、成本、预算、底价和可接受区间分别是多少?', required: true }, { key: 'liquidity_need', label: '时间/期限/急迫性', category: '必要条件', base: 0.45, weight: 0.86, userWeight: 0.65, otherWeight: 1, riskWeight: 0.3, refWeight: 0.55, positive: ['急', '月底', '马上', '尽快', '时间', '周期', '期限', '交付', '上线', '截止', '现金流', '周转'], negative: ['不急', '慢慢', '可延期'], question: '双方期望时间、最晚期限、交付周期和真实急迫性是什么?', required: true }, { key: 'counterparty_execution', label: '对方履约能力', category: '履约能力', base: 0.48, weight: 0.92, userWeight: 0.82, otherWeight: 1, riskWeight: 0.62, refWeight: 0.55, positive: ['资金到位', '案例', '团队', '项目经理', '资源', '证据', '记录', '可验证', '正规', '合同'], negative: ['没案例', '不确定', '资金紧张', '人手不足', '口头', '拖延', '不透明'], question: '对方是否有可验证的资金、能力、团队、案例或履约记录?' }, { key: 'subject_quality', label: '标的/方案质量', category: '信息质量', base: 0.5, weight: 1, userWeight: 1, otherWeight: 0.55, riskWeight: 1, refWeight: 0.85, positive: ['质量', '成熟', '清单', '方案', '检测', '验收', '样品', 'demo', '案例', '标准'], negative: ['模糊', '不清楚', '质量风险', '缺陷', '返工', '不成熟'], question: '这份标的、方案、服务或安排的质量如何验证?' }, { key: 'ownership_clearance', label: '权属/归属清晰度', category: '权属/归属', base: 0.52, weight: 1, userWeight: 0.92, otherWeight: 0.75, riskWeight: 1, refWeight: 0.85, positive: ['归属', '所有权', '知识产权', 'IP', '账号', '数据', '代码', '客户', '产权清晰', '授权'], negative: ['归属不清', '无授权', '被卡', '抢客户', '代码失控', '数据失控', '产权不清'], question: '关键资产、账号、数据、客户、代码、知识产权或标的权属归谁?' }, { key: 'transfer_friction', label: '交付与验收安排', category: '履约流程', base: 0.5, weight: 0.82, userWeight: 0.75, otherWeight: 0.75, riskWeight: 0.8, refWeight: 0.6, positive: ['交付', '移交', '交接', '监管', '尾款', '验收', '节点', '里程碑', '过户'], negative: ['审批', '拖延', '手续复杂', '交付不清', '移交不清', '验收失败'], question: '交付、验收、尾款和后续交接节点如何安排?' }, { key: 'risk_allocation', label: '风险分配工具', category: '契约设计', base: 0.42, weight: 1, userWeight: 0.9, otherWeight: 0.85, riskWeight: 1, refWeight: 0.75, positive: ['检测', '押金', '尾款', '违约', '监管', '对赌', '退款', '合同', '验收', '暂停', '解除'], negative: ['口头', '没合同', '不退', '无违约', '无法暂停', '无法退出'], question: '风险能否用检测、尾款、监管、违约、退款、暂停或解除条款分配?' }, { key: 'residual_control', label: '后续控制权/最终拍板权', category: '控制权配置', base: 0.43, weight: 1, userWeight: 0.92, otherWeight: 0.85, riskWeight: 1, refWeight: 0.9, positive: ['最终决定', '书面确认', '共同确认', '验收标准', '审批权', '否决权', '拍板', '授权'], negative: ['口头', '先签', '后面再说', '单方决定', '拒绝写', '说了算', '没权限'], question: '未来合同没写清或发生争议时,谁拥有最终决定权?哪些事项需要双方共同确认?' }, { key: 'state_control_transfer', label: '状态触发控制权转移', category: '控制权配置', base: 0.42, weight: 0.9, userWeight: 0.88, otherWeight: 0.82, riskWeight: 0.95, refWeight: 0.82, positive: ['触发', '逾期', '不达标', '验收不通过', '暂停', '解除', '转移', '托管', '退出'], negative: ['没有退出', '无法停止', '继续付款', '不能暂停', '长期绑定'], question: '逾期、不达标、验收失败、付款异常或不投入时,控制权是否会暂停、转移或共同决策?' }, { key: 'market_anchor', label: '当前市场情况', category: '必要条件', base: 0.48, weight: 0.78, userWeight: 0.8, otherWeight: 0.65, riskWeight: 0.35, refWeight: 1, positive: ['市场价', '公允价值', '行情', '均价', '成交价', '报价区间', '多个', '多家', '竞争', 'offer', '同类'], negative: ['不清楚行情', '没比较'], question: '请补充可查的市场参照:同类标的、服务、岗位、资源或方案的近期价格、成交条件、竞争强度或替代收益。', required: true, gapType: 'searchable' }, { key: 'outside_option', label: '双方替代选择/BATNA', category: '必要条件', base: 0.46, weight: 0.76, userWeight: 0.85, otherWeight: 0.85, riskWeight: 0.35, refWeight: 0.65, positive: ['还有别的', '备选', '可替代', '替代', '交易失败', '合作失败', '没有损失', 'BATNA'], negative: ['唯一', '没选择', '只能'], question: '双方各自还有哪些替代对象、替代方案或退出选择?如果不成交/不合作,各自损失什么?', required: true, gapType: 'hybrid' }, { key: 'trust_signal', label: '可信信号', category: '信息质量', base: 0.48, weight: 0.72, userWeight: 0.7, otherWeight: 0.65, riskWeight: 0.75, refWeight: 0.6, positive: ['透明', '记录', '发票', '证明', '可查', '第三方', '合同', '案例', '介绍'], negative: ['隐瞒', '含糊', '不透明', '拒绝提供', '只口头'], question: '关键材料、承诺、履约记录是否透明、可查、可第三方验证?' }, { key: 'post_deal_cost', label: '签约/履约后隐性成本', category: '长期结果', base: 0.5, weight: 0.72, userWeight: 0.9, otherWeight: 0.45, riskWeight: 0.75, refWeight: 0.55, positive: ['保修', '质保', '维护', '售后', '续约', '长期服务'], negative: ['维修', '税费', '维护费', '续费', '涨价', '返工', '额外费用'], question: '签约或履约后还有哪些维护、返工、续费、税费、迁移或协作成本?' }, { key: 'relationship_continuity', label: '后续协作需要', category: '关系/协作', base: 0.45, weight: 0.58, userWeight: 0.55, otherWeight: 0.55, riskWeight: 0.45, refWeight: 0.45, positive: ['后续配合', '售后', '交接', '协助', '长期维护', '持续合作'], negative: ['一次性', '不配合', '失联', '被卡'], question: '成交或签约后是否还需要对方协助交接、售后、维护或长期配合?' }, ] const complianceChecklist: Record = { trade: [ { item: '合同主体与身份真实', positive: ['身份证', '主体', '本人', '授权', '委托书', '营业执照'], negative: ['代签', '不露面', '身份不明'], question: '交易双方主体、身份、授权文件是否真实完整?' }, { item: '权属/处分权清晰', positive: ['产权清晰', '无抵押', '无查封', '可过户', '房东同意', '证照齐全'], negative: ['抵押', '查封', '贷款未结清', '产权不清', '无权处分'], question: '标的是否存在抵押、查封、未解押、共有权人不同意或无权处分?' }, { item: '标的描述明确', positive: ['面积', '房号', '车架号', '里程', '设备清单', '附属设施'], negative: ['模糊', '口头', '说不清'], question: '标的物位置、编号、面积/型号、附属物和交付状态是否写清?' }, { item: '价款与付款节点明确', positive: ['定金', '首付', '尾款', '监管', '付款节点', '价格'], negative: ['口头付款', '私下转账', '不写'], question: '价款、定金、首付、尾款、资金监管和付款节点是否明确?' }, { item: '交付/过户/验收节点明确', positive: ['交付', '过户', '验收', '检测', '交接', '节点'], negative: ['拖延', '不配合', '无法过户'], question: '交付、过户、验收、检测和资料移交的时间节点是否明确?' }, { item: '违约责任与解除条件明确', positive: ['违约', '赔偿', '退款', '解除', '逾期', '定金罚则'], negative: ['没违约', '不退', '口头承诺'], question: '逾期、无法过户、质量问题、资金不到位时的违约责任和解除条件是什么?' }, { item: '强制性规定/限购限贷/税费合规', positive: ['限购', '贷款', '税费', '资质', '备案', '合规'], negative: ['规避', '阴阳合同', '逃税', '借名'], question: '是否涉及限购限贷、税费、备案、资质、阴阳合同或借名交易风险?' }, { item: '证据留存与争议解决', positive: ['书面', '聊天记录', '录音', '收据', '发票', '争议解决', '管辖'], negative: ['只口头', '无凭证'], question: '关键沟通、付款凭证、验收证据、争议解决和管辖条款是否留存?' }, ], career: [ { item: '劳动/合作主体清晰', positive: ['劳动合同', '公司', '主体', '岗位'], negative: ['外包不明', '主体不清'], question: '用工主体、岗位、汇报关系是否清晰?' }, { item: '薪酬绩效书面明确', positive: ['薪酬', '绩效', '奖金', '提成', '书面'], negative: ['口头', '画饼'], question: '薪酬、奖金、绩效、提成和晋升承诺是否有书面依据?' }, { item: '工时加班与休假合规', positive: ['加班费', '调休', '工时', '休假'], negative: ['长期加班', '无加班费'], question: '工时、加班、调休、休假是否符合劳动规则和公司制度?' }, { item: '权责边界与成果归属', positive: ['职责', '权限', '成果', '归属', '授权'], negative: ['权责不清', '背锅'], question: '职责、权限、成果归属和责任边界是否明确?' }, { item: '离职/竞业/保密风险', positive: ['保密', '竞业', '离职', '交接'], negative: ['限制不清', '违约金'], question: '是否涉及保密、竞业限制、违约金、离职交接或知识产权归属?' }, ], partner: [ { item: '合作主体与授权', positive: ['合同', '主体', '授权', '公司', '个人'], negative: ['代签', '主体不清'], question: '合作主体、授权和签约身份是否明确?' }, { item: '贡献/分工/交付物明确', positive: ['分工', '交付', '节点', '贡献', '责任'], negative: ['口头', '模糊'], question: '各方贡献、分工、交付物和验收节点是否明确?' }, { item: '收益分配与结算规则明确', positive: ['分配', '结算', '比例', '账期', '透明'], negative: ['不透明', '拖款'], question: '收益分配、成本承担、结算周期和账目透明规则是否明确?' }, { item: '风险承担和违约退出机制', positive: ['违约', '退出', '赔偿', '止损', '解除'], negative: ['背锅', '无法退出'], question: '违约、亏损、责任承担、退出和清算机制是否明确?' }, { item: '知识产权/客户资源/数据归属', positive: ['知识产权', '客户', '数据', '账号', '归属'], negative: ['抢客户', '账号被卡'], question: '客户资源、账号、数据、知识产权和后续使用权归谁?' }, { item: '证据留存与争议解决', positive: ['书面', '记录', '收据', '管辖', '仲裁'], negative: ['只口头', '无凭证'], question: '关键沟通、交付证据、账目和争议解决条款是否留存?' }, ], } function clamp(value: number, min = 0, max = 1) { return Math.min(max, Math.max(min, value)) } function round(value: number, digits = 3) { const base = 10 ** digits return Math.round(value * base) / base } function findEvidence(text: string, words: string[]) { return words.filter((word) => text.includes(word)) } function extractNumbers(text: string) { return [...text.matchAll(/(\d+(?:\.\d+)?)/g)].map((match) => Number(match[1])).filter(Number.isFinite) } function softmax(values: number[]) { const max = Math.max(...values) const exps = values.map((value) => Math.exp(value - max)) const sum = exps.reduce((total, value) => total + value, 0) return exps.map((value) => value / sum) } function weightedAverage(features: EngineFeature[], weightSelector: (factor: EngineFeature) => number) { const totalWeight = features.reduce((sum, item) => sum + weightSelector(item), 0) if (totalWeight === 0) return 0.5 return features.reduce((sum, item) => sum + item.value * weightSelector(item), 0) / totalWeight } function normalizeWeights>(weights: T): T { const total = Object.values(weights).reduce((sum, value) => sum + Math.max(value, 0.0001), 0) return Object.fromEntries(Object.entries(weights).map(([key, value]) => [key, round(Math.max(value, 0.0001) / total)])) as T } function bucketImportance(features: EngineFeature[], predicate: (item: EngineFeature) => boolean) { const bucket = features.filter(predicate) if (bucket.length === 0) return 0.5 return round(clamp(bucket.reduce((sum, item) => sum + item.weight * (0.55 + item.confidence * 0.45), 0) / bucket.length)) } function deriveContractFormulaWeights(features: EngineFeature[]) { const textOf = (item: EngineFeature) => featureText(item) const needImportance = bucketImportance(features, (item) => /需求|匹配|收益|benefit|matching|need|utility|价值/i.test(textOf(item))) const riskImportance = bucketImportance(features, (item) => /风险|损失|底线|下行|不确定|risk|loss|floor|uncertain|downside/i.test(textOf(item))) const controlImportance = bucketImportance(features, isControlFeature) const reliabilityImportance = bucketImportance(features, isReliabilityFeature) const referenceImportance = bucketImportance(features, (item) => /参照|公允|市场|预期|reference|market|anchor|expectation/i.test(textOf(item))) const surplusImportance = bucketImportance(features, (item) => /共同|剩余|互补|双赢|surplus|win|reciprocal|互惠/i.test(textOf(item))) return { matching: normalizeWeights({ nash: 0.32 * (0.72 + needImportance * 0.56), reference: 0.2 * (0.72 + referenceImportance * 0.56), surplus: 0.16 * (0.72 + surplusImportance * 0.56), outsideOption: 0.12, antiHoldUp: 0.1 * (0.72 + riskImportance * 0.56), control: 0.1 * (0.72 + controlImportance * 0.56), }), holdUp: normalizeWeights({ risk: 0.38 * (0.72 + riskImportance * 0.56), gap: 0.2 * (0.72 + referenceImportance * 0.56), missing: 0.16, control: 0.14 * (0.72 + controlImportance * 0.56), reliability: 0.12 * (0.72 + reliabilityImportance * 0.56), }), score: normalizeWeights({ winWin: 0.42 * (0.72 + surplusImportance * 0.56), matching: 0.24 * (0.72 + needImportance * 0.56), reference: 0.16 * (0.72 + referenceImportance * 0.56), coverage: 0.1, surplus: 0.08 * (0.72 + surplusImportance * 0.56), }), } } function scoreFactor(spec: FactorSpec, text: string, moduleId: ModuleId): EngineFeature { const positive = findEvidence(text, spec.positive) const negative = findEvidence(text, spec.negative) const observed = positive.length + negative.length let value = spec.base + positive.length * 0.105 - negative.length * 0.13 if (moduleId === 'trade' && spec.key === 'price_gap') { const numbers = extractNumbers(text) if (numbers.length >= 2) { const gap = Math.abs(numbers[0] - numbers[1]) / Math.max(numbers[0], numbers[1], 1) value = 1 - gap * 2.2 } } if (moduleId === 'trade' && spec.key === 'post_deal_cost' && negative.length === 0) { value -= 0.05 } if (moduleId === 'career' && spec.key === 'workload_cost') { value = spec.base + positive.length * 0.09 - negative.length * 0.16 } if (moduleId === 'partner' && spec.key === 'control_dependency') { value = spec.base + positive.length * 0.11 - negative.length * 0.15 } const confidence = clamp(0.28 + observed * 0.18 + Math.min(text.length / 360, 0.28)) return { key: spec.key, label: spec.label, category: spec.category, source: 'base', value: round(clamp(value)), confidence: round(confidence), weight: spec.weight, evidence: [...positive, ...negative].length > 0 ? [...positive, ...negative] : ['未显式出现,保留先验并计入信息缺口'], missing: observed === 0, } } function normalizeDynamicFactor(input: DynamicFactorInput, index: number): EngineFeature { const safeLabel = input.label || `开放参数 ${index + 1}` const safeCategory = input.category || '开放因素' return { key: input.key || `open_${index + 1}_${safeLabel.slice(0, 12)}`, label: safeLabel, category: safeCategory, source: 'open', value: round(clamp(Number(input.value))), confidence: round(clamp(Number(input.confidence || 0.62))), weight: round(clamp(Number(input.weight || 0.68), 0.1, 1)), evidence: input.evidence && input.evidence.length > 0 ? input.evidence : ['开放参数抽取器识别'], missing: false, } } function dynamicWeight(input: DynamicFactorInput | undefined, kind: 'user' | 'other' | 'risk' | 'ref') { if (!input) return 1 if (kind === 'user') return input.userWeight ?? 1 if (kind === 'other') return input.otherWeight ?? 1 if (kind === 'risk') return input.riskWeight ?? 0.65 return input.refWeight ?? 0.85 } function featureText(item: EngineFeature) { return `${item.key} ${item.label} ${item.category} ${item.evidence.join(' ')}` } function isControlFeature(item: EngineFeature) { return /控制权|理论2|拍板|决定权|否决权|验收权|退出权|暂停|解除|转移|托管|授权|权限|归属|账号|数据|代码|IP|知识产权|付款节点|尾款|交付|验收|职责边界|资源调度|最终决定|共同确认|control|rights|decision|veto|approval|ownership|authority|permission|handover|acceptance|payment|milestone|escrow|exit|terminate|pause/i.test(featureText(item)) } function isStateTriggerFeature(item: EngineFeature) { return /状态|触发|逾期|不达标|未达标|验收不通过|验收失败|验收不合格|付款异常|资源不到位|融资失败|融资|现金流|资金|无法转岗|无法转AI|不能保证|不保证|职责扩大|职责泛化|延期|暂停|解除|退出|退款|退货|尾款|里程碑|cliff|vesting|回购|清算|转移|共同决策|state|trigger|contingency|delay|overdue|fail|failure|refund|return|vesting|buyback|liquidation|fallback|walkaway|funding|financing|runway|cashflow|cannot guarantee|not guaranteed/i.test(featureText(item)) } function isReliabilityFeature(item: EngineFeature) { return /理论4|可信|履约|信任|信誉|证据|案例|书面|合同|透明|可查|第三方|发票|授权|质保|SLA|验收标准|项目经理|正规|历史|记录|资金|融资|现金流|口头|画饼|不确定|拒绝写|不透明|无案例|没案例|reliability|trust|credible|evidence|case|written|contract|transparent|verifiable|third|invoice|warranty|runway|funding|financing|verbal|uncertain|refuse/i.test(featureText(item)) } function buildControlRights(features: EngineFeature[], specs: FactorSpec[]) { const controlFeatures = features.filter(isControlFeature) const controlSignalFeatures = controlFeatures.filter((item) => item.source === 'open' || !item.missing) const controlBasis = controlSignalFeatures.length > 0 ? controlSignalFeatures : controlFeatures const controlAlignment = round(controlBasis.length > 0 ? weightedAverage(controlBasis, (item) => item.weight * (0.7 + item.confidence * 0.3)) : 0.42) const residualControlRisk = round(clamp(1 - controlAlignment)) const specificInvestment = round( weightedAverage( features.filter((item) => /专用|资产|投入|资源|客户|数据|账号|技术|装修|项目|成果|成长|品牌|股权|IP|知识产权/.test(`${item.label}${item.category}${item.evidence.join('')}`)), (item) => item.weight * (0.7 + item.confidence * 0.3), ), ) const investmentIncentiveFit = round(clamp(0.58 * controlAlignment + 0.42 * (1 - Math.abs(specificInvestment - controlAlignment)))) const stateControlFeatures = features.filter((item) => item.key === 'state_control_transfer' || isStateTriggerFeature(item)) const stateSignalFeatures = stateControlFeatures.filter((item) => item.source === 'open' || !item.missing) const stateBasis = stateSignalFeatures.length > 0 ? stateSignalFeatures : stateControlFeatures const stateContingencyReadiness = round( stateBasis.length > 0 ? weightedAverage(stateBasis, (item) => item.weight * (0.65 + item.confidence * 0.35)) : 0.35, ) const missingControlQuestions = specs .filter((spec) => spec.category.includes('控制权')) .filter((spec) => !features.some((item) => item.key === spec.key && !item.missing)) .map((spec) => spec.question) const uniqueMissingControlQuestions = [...new Set(missingControlQuestions)].slice(0, 3) const recommendedAllocation = residualControlRisk >= 0.58 ? '存在后续被锁定风险:凡涉及付款、验收、账号、客户、数据、IP、预算、方向、资源投入和责任承担的事项,都需要分项配置控制权,并设置逾期、不达标、违约或不投入时的暂停、转移、解除和退出机制。' : stateContingencyReadiness < 0.5 ? '基础控制权可谈,但状态变化机制不足:需要补充逾期、不达标、验收失败、资源不到位时的暂停、转移、解除或共同决策条款。' : '控制权配置相对可接受:继续把关键事项写成分项决策权、触发条件和证据留存。' return { controlAlignment, residualControlRisk, investmentIncentiveFit, stateContingencyReadiness, recommendedAllocation, missingControlQuestions: uniqueMissingControlQuestions, } } function buildBayesianReliability(text: string, features: EngineFeature[] = []) { const prior = 0.52 const positiveSignals = [ { word: '书面', likelihood: 1.45 }, { word: '透明', likelihood: 1.35 }, { word: '可查', likelihood: 1.28 }, { word: '第三方', likelihood: 1.28 }, { word: '资金到位', likelihood: 1.38 }, { word: '贷款已预批', likelihood: 1.32 }, { word: '定金', likelihood: 1.22 }, { word: '共同确认', likelihood: 1.3 }, { word: '明确', likelihood: 1.22 }, { word: '按时', likelihood: 1.3 }, { word: '兑现', likelihood: 1.36 }, { word: '后台数据', likelihood: 1.28 }, { word: '愿意写', likelihood: 1.42 }, { word: '验收标准', likelihood: 1.24 }, { word: '违约责任', likelihood: 1.2 }, { word: 'written', likelihood: 1.42 }, { word: 'transparent', likelihood: 1.32 }, { word: 'verifiable', likelihood: 1.28 }, { word: 'third party', likelihood: 1.26 }, { word: 'preapproved', likelihood: 1.32 }, { word: 'deposit', likelihood: 1.22 }, { word: 'clear terms', likelihood: 1.3 }, { word: 'acceptance criteria', likelihood: 1.24 }, { word: 'penalty clause', likelihood: 1.2 }, ] const negativeSignals = [ { word: '口头', likelihood: 0.66 }, { word: '画饼', likelihood: 0.58 }, { word: '不透明', likelihood: 0.52 }, { word: '拒绝写', likelihood: 0.42 }, { word: '后面再说', likelihood: 0.55 }, { word: '先签', likelihood: 0.62 }, { word: '拖延', likelihood: 0.65 }, { word: '变卦', likelihood: 0.48 }, { word: '反复', likelihood: 0.6 }, { word: '结算慢', likelihood: 0.58 }, { word: '无承诺', likelihood: 0.56 }, { word: '没有承诺', likelihood: 0.56 }, { word: '不愿提供', likelihood: 0.5 }, { word: '最终决策都由他', likelihood: 0.5 }, { word: '单方决定', likelihood: 0.48 }, { word: '账号归属没有写清', likelihood: 0.5 }, { word: 'verbal', likelihood: 0.62 }, { word: 'refuses to write', likelihood: 0.42 }, { word: 'refuse to write', likelihood: 0.42 }, { word: 'later', likelihood: 0.6 }, { word: 'not transparent', likelihood: 0.52 }, { word: 'delayed settlement', likelihood: 0.58 }, { word: 'unclear ownership', likelihood: 0.5 }, { word: 'single-sided decision', likelihood: 0.48 }, { word: 'no veto right', likelihood: 0.52 }, ] const hasPositiveSignal = (word: string) => { if (!text.includes(word)) return false if (word === '透明' && text.includes('不透明')) return false if (word === '书面' && /拒绝.{0,4}书面|不愿.{0,4}书面/.test(text)) return false if (text.includes(`not ${word}`) || text.includes(`no ${word}`) || text.includes(`without ${word}`)) return false return true } let odds = prior / (1 - prior) const positiveEvidence: string[] = [] const negativeEvidence: string[] = [] for (const signal of positiveSignals) { if (hasPositiveSignal(signal.word)) { odds *= signal.likelihood positiveEvidence.push(signal.word) } } for (const signal of negativeSignals) { if (text.includes(signal.word)) { odds *= signal.likelihood negativeEvidence.push(signal.word) } } for (const item of features.filter(isReliabilityFeature)) { const strength = item.weight * (0.55 + item.confidence * 0.45) const evidenceLabel = item.evidence[0] || item.label if (item.value >= 0.62) { odds *= 1 + (item.value - 0.5) * strength positiveEvidence.push(evidenceLabel) } else if (item.value <= 0.42) { odds *= Math.max(0.35, 1 - (0.5 - item.value) * strength * 1.45) negativeEvidence.push(evidenceLabel) } } const posterior = round(clamp(odds / (1 + odds))) const reliabilityLevel = posterior >= 0.68 ? 'high' : posterior >= 0.45 ? 'medium' : 'low' const interpretation = reliabilityLevel === 'high' ? '对方可信履约信号较强,可以继续推进,但仍需把关键承诺写成可验证条件。' : reliabilityLevel === 'medium' ? '对方可信履约能力尚未充分验证,适合继续谈,但必须用书面条件、验收标准和退出机制降低不确定性。' : '对方可信履约信号偏弱,不宜依赖口头承诺,应先要求书面确认、履约担保或降低投入。' return { prior, posterior, evidenceCount: positiveEvidence.length + negativeEvidence.length, positiveEvidence, negativeEvidence, reliabilityLevel, interpretation, } as const } function buildSuccessPanel(input: { matchingEquilibrium: number totalSurplus: number outsideOptionPressure: number coverage: number holdUpRisk: number expectationGap: number downsideProbability: number controlAlignment: number stateContingencyReadiness: number reliability: number }) { const weightedSum = (terms: { label: string; value: number; coefficient: number }[]) => terms.reduce((sum, term) => sum + term.value * term.coefficient, 0) const withContribution = (terms: { label: string; value: number; coefficient: number }[]) => terms.map((term) => ({ ...term, value: round(term.value), contribution: round(term.value * term.coefficient) })) const sigmoid = (value: number) => 1 / (1 + Math.exp(-value)) const terms = [ { label: '当前匹配质量', value: input.matchingEquilibrium, weight: 0.24, direction: 1 }, { label: '双方总剩余', value: input.totalSurplus, weight: 0.16, direction: 1 }, { label: '对方可信履约', value: input.reliability, weight: 0.18, direction: 1 }, { label: '控制权配置', value: input.controlAlignment, weight: 0.14, direction: 1 }, { label: '信息完整度', value: input.coverage, weight: 0.09, direction: 1 }, { label: '替代选择压力', value: input.outsideOptionPressure, weight: 0.07, direction: 1 }, { label: '状态触发机制', value: input.stateContingencyReadiness, weight: 0.08, direction: 1 }, { label: '被卡住风险', value: input.holdUpRisk, weight: 0.16, direction: -1 }, { label: '预期缺口', value: input.expectationGap, weight: 0.11, direction: -1 }, { label: '下行损失风险', value: input.downsideProbability, weight: 0.1, direction: -1 }, ] const positiveScore = terms .filter((term) => term.direction === 1) .reduce((sum, term) => sum + term.weight * term.value, 0) const negativeScore = terms .filter((term) => term.direction === -1) .reduce((sum, term) => sum + term.weight * term.value, 0) const successRaw = positiveScore - negativeScore const neutralRaw = 0.38 + 0.32 * input.coverage + 0.22 * (1 - Math.abs(0.5 - input.matchingEquilibrium)) - 0.18 * Math.abs(positiveScore - negativeScore) const failureRaw = negativeScore + 0.22 * (1 - input.reliability) + 0.16 * (1 - input.controlAlignment) - positiveScore * 0.45 const [success, neutral, failure] = softmax([successRaw * 4.2, neutralRaw * 2.4, failureRaw * 4.2]).map((value) => round(value)) const bargainingFactors = [ { label: '双方总剩余', value: input.totalSurplus, coefficient: 1.05 }, { label: '当前匹配质量', value: input.matchingEquilibrium, coefficient: 1 }, { label: '替代选择压力', value: input.outsideOptionPressure, coefficient: 0.62 }, { label: '对方可信履约', value: input.reliability, coefficient: 0.54 }, { label: '信息完整度', value: input.coverage, coefficient: 0.48 }, { label: '预期缺口', value: input.expectationGap, coefficient: -0.8 }, { label: '被卡住风险', value: input.holdUpRisk, coefficient: -0.62 }, ] const currentNegotiation = round(clamp(sigmoid(-1.05 + weightedSum(bargainingFactors)))) const concessionGainFactors = [ { label: '高价值低成本交换空间', value: input.outsideOptionPressure, coefficient: 0.38 }, { label: '预期缺口可修复度', value: 1 - input.expectationGap, coefficient: 0.32 }, { label: '状态触发机制可设计度', value: input.stateContingencyReadiness, coefficient: 0.24 }, { label: '控制权可锁定度', value: input.controlAlignment, coefficient: 0.22 }, { label: '对方可信履约', value: input.reliability, coefficient: 0.18 }, ] const concessionCostFactors = [ { label: '下行损失风险', value: input.downsideProbability, coefficient: 0.32 }, { label: '被卡住风险', value: input.holdUpRisk, coefficient: 0.28 }, { label: '控制权不足导致的让步损耗', value: 1 - input.controlAlignment, coefficient: 0.22 }, { label: '对方可信不足导致的让步损耗', value: 1 - input.reliability, coefficient: 0.18 }, ] const concessionGain = round(clamp(weightedSum(concessionGainFactors))) const concessionCost = round(clamp(weightedSum(concessionCostFactors))) const paretoImprovement = round(clamp(concessionGain - concessionCost + 0.5)) const afterConcession = round(clamp( currentNegotiation + 0.42 * paretoImprovement * (1 - currentNegotiation) - 0.16 * concessionCost, )) const survivalFactors = [ { label: '对方可信履约', value: input.reliability, coefficient: 0.34 }, { label: '控制权配置', value: input.controlAlignment, coefficient: 0.24 }, { label: '状态触发机制', value: input.stateContingencyReadiness, coefficient: 0.22 }, { label: '信息完整度', value: input.coverage, coefficient: 0.1 }, { label: '抗下行损失能力', value: 1 - input.downsideProbability, coefficient: 0.1 }, ] const hazardFactors = [ { label: '被卡住风险', value: input.holdUpRisk, coefficient: 0.34 }, { label: '预期缺口', value: input.expectationGap, coefficient: 0.24 }, { label: '下行损失风险', value: input.downsideProbability, coefficient: 0.22 }, { label: '可信履约不足', value: 1 - input.reliability, coefficient: 0.2 }, ] const baseSurvival = round(clamp(weightedSum(survivalFactors))) const hazard = round(clamp(weightedSum(hazardFactors))) const fulfillment = round(clamp(baseSurvival * Math.exp(-0.72 * hazard))) const grade = success >= 0.62 && failure <= 0.22 ? 'advantage' : failure >= 0.42 ? 'danger' : 'balanced' const positive = terms .filter((term) => term.direction === 1) .map((term) => ({ label: term.label, value: round(term.value), impact: round(term.weight * term.value) })) .sort((a, b) => b.impact - a.impact) .slice(0, 5) const negative = terms .filter((term) => term.direction === -1) .map((term) => ({ label: term.label, value: round(term.value), impact: round(term.weight * term.value) })) .sort((a, b) => b.impact - a.impact) .slice(0, 5) return { success, neutral, failure, stages: { currentNegotiation, afterConcession, fulfillment, }, stageModels: { currentNegotiation: { model: 'bargaining-scorecard', factors: withContribution(bargainingFactors), }, afterConcession: { model: 'mechanism-counterfactual', concessionGain, concessionCost, paretoImprovement, factors: [...withContribution(concessionGainFactors), ...withContribution(concessionCostFactors.map((term) => ({ ...term, coefficient: -term.coefficient })))], }, fulfillment: { model: 'bayesian-survival', baseSurvival, hazard, factors: [...withContribution(survivalFactors), ...withContribution(hazardFactors.map((term) => ({ ...term, coefficient: -term.coefficient })))], }, }, grade, formulaVersion: 'success-panel-deterministic-1.2', drivers: { positive, negative, }, } satisfies SuccessPanel } function buildQuestions(features: EngineFeature[], specs: FactorSpec[]) { const requiredMissingKeys = specs .filter((spec) => spec.required) .filter((spec) => !requiredIsSatisfied(spec, features)) .map((spec) => spec.key) const missingKeys = features .filter((item) => item.missing) .sort((a, b) => b.weight - a.weight) .map((item) => item.key) const questions = [...requiredMissingKeys, ...missingKeys.filter((key) => !requiredMissingKeys.includes(key))] .map((key) => specs.find((spec) => spec.key === key)?.question) .filter((question): question is string => Boolean(question)) return [...new Set(questions)].slice(0, 5) } function missingSpecQuestions(features: EngineFeature[], specs: FactorSpec[], gapType: FactorSpec['gapType']) { return specs .filter((spec) => spec.gapType === gapType || (gapType === 'user-only' && spec.gapType === 'hybrid')) .filter((spec) => !requiredIsSatisfied(spec, features)) .sort((a, b) => b.weight - a.weight) .map((spec) => spec.question) } function hasObservedFeature(features: EngineFeature[], key: string) { return features.some((item) => item.key === key && !item.missing) } function buildInformationSufficiency(text: string, features: EngineFeature[], specs: FactorSpec[], coverage: number) { const openFeatures = features.filter((item) => item.source === 'open' && !item.missing) const openText = openFeatures.flatMap((item) => [item.key, item.label, item.category, ...item.evidence]).join(' ') const combinedText = `${text} ${openText}` const hasOpenDecisionMaterial = openFeatures.length >= 3 const concreteArrangement = (text.length >= 30 || hasOpenDecisionMaterial) && /offer|报价|价格|年薪|薪酬|合作|合伙|买|卖|签|外包|入职|试用|房|车|项目|供应商|服务商|承包商|代理|渠道|平台|资源方|需求方|承诺方|交易对手|合同|岗位|公司|团队|客户|付款|交付|验收|股权|期权|分成|返点|佣金|授权|控制权|条件包|路径|安排|标的|候选|方案|A|B|C|D|对方|买家|卖家|candidate|supplier|vendor|contractor|partner|proposal|payment|delivery|acceptance|equity|option|budget|deadline|IP|data|code|control/i.test(combinedText) const exchangeTerms = /报价|价格|年薪|薪酬|付款|交付|验收|股权|期权|职责|岗位|负责|期限|现金流|融资|提成|分成|返点|佣金|预算|首付|贷款|让价|降价|万|元|资源|授权|控制权|退出|区域|账号|数据|代码|IP|客户归属|quote|price|salary|payment|delivery|acceptance|budget|deadline|runway|funding|financing|scope|responsibility|ownership|rights|exit/i.test(combinedText) const userNeedSignals = [ 'user_core_need', 'need_priority_order', 'time_horizon', 'capability_fit', 'constraint_floor', ].filter((key) => hasObservedFeature(features, key)).length const controlSignals = hasObservedFeature(features, 'residual_control') || hasObservedFeature(features, 'state_control_transfer') const marketSignals = hasObservedFeature(features, 'market_anchor') || /市场|行情|同类|竞品|同行|成交|挂牌|薪酬|报价|替代|备选|多家|多个|竞争|搜索补充|公开资料|公开信息|quote|price|market|alternative|competition|benchmark/i.test(combinedText) const reliabilitySignals = /书面|透明|可查|证明|案例|记录|口碑|兑现|拖延|不透明|融资|现金流|贷款|首付|履约|违约|written|contract|case|record|reliable|funding|runway|financing|breach/i.test(combinedText) const userQuestions = [...new Set(missingSpecQuestions(features, specs, 'user-only'))].slice(0, 3) const searchQuestions = [...new Set(missingSpecQuestions(features, specs, 'searchable'))].slice(0, 3) const hybridQuestions = [...new Set(missingSpecQuestions(features, specs, 'hybrid'))].slice(0, 2) const userOnlyMissing = specs .filter((spec) => spec.gapType === 'user-only' || spec.gapType === 'hybrid') .filter((spec) => !requiredIsSatisfied(spec, features, text)) .map((spec) => spec.label) const searchableMissing = specs .filter((spec) => spec.gapType === 'searchable' || spec.gapType === 'hybrid') .filter((spec) => !requiredIsSatisfied(spec, features, text)) .map((spec) => spec.label) let score = 0 if (concreteArrangement) score += 0.28 if (exchangeTerms) score += 0.16 score += Math.min(userNeedSignals, 3) * 0.12 if (marketSignals) score += 0.11 if (controlSignals) score += 0.1 if (reliabilitySignals) score += 0.08 score += Math.min(coverage, 1) * 0.15 score = round(clamp(score), 3) const reasons = [ concreteArrangement ? '已经有具体安排或候选对象' : '还没有具体安排或候选对象', exchangeTerms ? '已经有核心交换条件' : '缺少价格、薪酬、付款、交付、职责或期限等核心条件', userNeedSignals > 0 ? `已有${userNeedSignals}类用户需求/约束线索` : '缺少用户本人目标、取舍或底线', marketSignals ? '已有市场/替代选择线索,或可由 GPT 搜索补足' : '缺少市场情况或替代选择参照', controlSignals ? '已有控制权或状态变化线索' : '缺少关键事项谁说了算、出问题如何处理', ] const blocking = !hasOpenDecisionMaterial && (!concreteArrangement || (!exchangeTerms && userNeedSignals === 0)) const level = blocking ? 'insufficient' : score >= 0.72 ? 'sufficient' : 'preliminary' return { level, score, blocking, reasons, userOnlyMissing: [...new Set(userOnlyMissing)].slice(0, 5), searchableMissing: [...new Set(searchableMissing)].slice(0, 5), userQuestions: [...new Set([...userQuestions, ...hybridQuestions])].slice(0, 3), searchQuestions, } as const } function hasComputableDecisionSignal( dynamicFeatures: EngineFeature[], coverage: number, metrics: { winWin: number mismatch: number currentNegotiation: number fulfillment: number userUtility: number counterpartyUtility: number totalSurplus: number }, ) { const observedOpenFeatures = dynamicFeatures.filter((item) => !item.missing) if (observedOpenFeatures.length < 2) return false const dynamicText = observedOpenFeatures .flatMap((item) => [item.key, item.label, item.category, ...item.evidence]) .join(' ') const hasExchangeSignal = /报价|价格|年薪|薪酬|付款|交付|验收|职责|岗位|负责|期限|现金流|融资|股权|期权|分成|预算|资源|客户|代码|数据|IP|授权|控制权|退出|quote|price|salary|payment|delivery|acceptance|budget|deadline|funding|financing|scope|responsibility|ownership|rights|exit/i.test(dynamicText) const hasRiskOrValueSignal = /风险|收益|价值|能力|案例|履约|可信|控制权|不确定|成本|底线|替代|市场|匹配|risk|value|capability|case|reliable|control|uncertain|cost|floor|alternative|market|fit/i.test(dynamicText) const hasMetricSeparation = metrics.winWin >= 0.52 || metrics.mismatch >= 0.34 || metrics.currentNegotiation >= 0.6 || metrics.fulfillment <= 0.42 || metrics.userUtility >= 0.55 || metrics.counterpartyUtility >= 0.55 || metrics.totalSurplus >= 0.54 return (hasExchangeSignal && hasRiskOrValueSignal) || (coverage >= 0.45 && hasMetricSeparation) || observedOpenFeatures.length >= 4 } function requiredIsSatisfied(spec: FactorSpec, features: EngineFeature[], rawText = '') { if (features.some((item) => item.key === spec.key && !item.missing)) return true const featureText = features .filter((item) => item.source === 'open') .flatMap((item) => [item.label, item.category, ...item.evidence]) .join(' ') const text = `${rawText} ${featureText}` if (spec.key === 'user_core_need') return /成长|赚钱|收入|稳定|安全|自由|自主权|履历|平台|实战|家庭压力|长期|短期|AI产品|热爱|想要|最重要|growth|money|income|stability|autonomy|resume value|family pressure|long-term|short-term/i.test(text) if (spec.key === 'need_priority_order') return /优先|第一|更重要|取舍|排序|底线|可以接受|不能接受|宁愿|相比|priority|tradeoff|prefer|rather|bottom line/i.test(text) if (spec.key === 'time_horizon') return /短期|长期|一年|两年|三年|五年|未来|现在|阶段|职业初期|long-term|short-term|1 year|2 years|3 years|future|horizon/i.test(text) if (spec.key === 'capability_fit') return /技能|能力|擅长|匹配|兴趣|专业|经验|资源|适合|skill|fit|ability|interest|experience|major/i.test(text) if (spec.key === 'constraint_floor') return /底线|最低|房租|现金流|家庭|父母|贷款|存款|不能承受|最坏结果|red line|runway|cash flow|family|worst case/i.test(text) if (spec.key === 'price_gap') return /报价|价格|预算|底价|可接受|万|元/.test(text) if (spec.key === 'liquidity_need') return /时间|急|周期|截止|月底|交付|让步|时间优势/.test(text) if (spec.key === 'market_anchor') return /市场|公允|行情|成交价|挂牌|租售比|银行|性价比|同小区|片区/.test(text) if (spec.key === 'outside_option') return /替代|备选|BATNA|交易失败|没有损失|其他买家|其他房源|时间站在/.test(text) return false } function getUniversalSpecs() { return [ ...universalFactorSpecs.map((spec) => ({ sourceModule: 'trade' as ModuleId, spec })), ...contractCoreFactorSpecs.map((spec) => ({ sourceModule: 'trade' as ModuleId, spec: { ...spec, required: spec.required ?? false } })), ] } function buildComplianceReview(text: string) { const rules = Object.values(complianceChecklist).flat() const checklist = rules.map((rule) => { const positive = findEvidence(text, rule.positive) const negative = findEvidence(text, rule.negative) const status: 'observed' | 'missing' | 'risk' = negative.length > 0 ? 'risk' : positive.length > 0 ? 'observed' : 'missing' return { item: rule.item, status, evidence: [...positive, ...negative].length > 0 ? [...positive, ...negative] : [rule.question], } }) const missing = checklist.filter((item) => item.status === 'missing').map((item) => item.item) const risks = checklist.filter((item) => item.status === 'risk').map((item) => item.item) const observed = checklist.filter((item) => item.status === 'observed').length const score = Math.round(((observed + risks.length * 0.35) / checklist.length) * 100) const status = risks.length > 0 ? 'blocking' : missing.length > Math.ceil(checklist.length / 2) ? 'warning' : 'pass' return { status, score, missing, risks, checklist, } as const } export function analyzeContract(text: string, openFactors: DynamicFactorInput[] = []): ContractAnalysis { const moduleId: EngineId = 'universal' const universalSpecs = getUniversalSpecs() const specs = universalSpecs.map(({ spec }) => spec) const baseFeatures = universalSpecs.map(({ sourceModule, spec }) => scoreFactor(spec, text, sourceModule)) const dynamicFeatures = openFactors.map(normalizeDynamicFactor) const observedBaseFeatures = baseFeatures.filter((item) => !item.missing || item.category.includes('控制权') || item.category.includes('需求画像')) const calculationFeatures = [...observedBaseFeatures, ...dynamicFeatures] const features = calculationFeatures.length > 0 ? calculationFeatures : baseFeatures const specByIdentity = new Map(specs.map((spec) => [`${spec.key}::${spec.label}`, spec])) const specForFeature = (item: EngineFeature) => specByIdentity.get(`${item.key}::${item.label}`) const dynamicByKey = new Map(dynamicFeatures.map((feature, index) => [feature.key, openFactors[index]])) const observed = observedBaseFeatures.length + dynamicFeatures.length const totalAvailable = baseFeatures.length + dynamicFeatures.length const coverage = round(observed / totalAvailable) const missingHighWeightFactors = baseFeatures .filter((item) => item.missing && item.weight >= 0.85) .map((item) => item.label) const requiredSpecs = specs.filter((spec) => spec.required) const missingRequiredSpecs = requiredSpecs.filter((spec) => !requiredIsSatisfied(spec, features, text)) const informationSufficiency = buildInformationSufficiency(text, features, specs, coverage) const complianceReview = buildComplianceReview(text) const controlRights = buildControlRights(features, specs) const bayesianReliability = buildBayesianReliability(text, features) const formulaWeights = deriveContractFormulaWeights(features) const factorWeight = (item: EngineFeature) => item.weight * (0.7 + item.confidence * 0.3) const reliabilityAdjustment = 0.75 + bayesianReliability.posterior * 0.5 const userUtility = round(weightedAverage(features, (item) => factorWeight(item) * (specForFeature(item)?.userWeight ?? dynamicWeight(dynamicByKey.get(item.key), 'user')))) const counterpartyUtility = round(clamp(weightedAverage(features, (item) => factorWeight(item) * (specForFeature(item)?.otherWeight ?? dynamicWeight(dynamicByKey.get(item.key), 'other'))) * reliabilityAdjustment)) const riskExposure = round(clamp(1 - weightedAverage(features, (item) => factorWeight(item) * (specForFeature(item)?.riskWeight ?? dynamicWeight(dynamicByKey.get(item.key), 'risk'))) + (1 - bayesianReliability.posterior) * 0.12)) const referenceAlignment = round(weightedAverage(features, (item) => factorWeight(item) * (specForFeature(item)?.refWeight ?? dynamicWeight(dynamicByKey.get(item.key), 'ref')))) const informationPenalty = round((1 - Math.min(coverage + dynamicFeatures.length * 0.035, 1)) * 0.18) const expectationGap = round(clamp(1 - referenceAlignment + informationPenalty)) const fairnessReference = round(clamp(0.5 * referenceAlignment + 0.3 * userUtility + 0.2 * counterpartyUtility)) const holdUpRisk = round(clamp( formulaWeights.holdUp.risk * riskExposure + formulaWeights.holdUp.gap * expectationGap + formulaWeights.holdUp.missing * (1 - coverage) + formulaWeights.holdUp.control * controlRights.residualControlRisk + formulaWeights.holdUp.reliability * (1 - bayesianReliability.posterior), )) const renegotiationRisk = round(clamp(0.38 * expectationGap + 0.29 * riskExposure + 0.18 * (1 - fairnessReference) + 0.15 * (1 - controlRights.stateContingencyReadiness))) const outsideOptionPressure = round(clamp(1 - Math.abs(userUtility - counterpartyUtility))) const totalSurplus = round((userUtility + counterpartyUtility) / 2) const nashProduct = round(Math.sqrt(Math.max(userUtility, 0) * Math.max(counterpartyUtility, 0))) const matchingEquilibrium = round( clamp( formulaWeights.matching.nash * nashProduct + formulaWeights.matching.reference * referenceAlignment + formulaWeights.matching.surplus * totalSurplus + formulaWeights.matching.outsideOption * outsideOptionPressure + formulaWeights.matching.antiHoldUp * (1 - holdUpRisk) + formulaWeights.matching.control * controlRights.investmentIncentiveFit, ), ) const [winWin, neutral, mismatch] = softmax([ 2.9 * matchingEquilibrium + 1.0 * totalSurplus + 0.58 * coverage + 0.48 * controlRights.controlAlignment + 0.42 * bayesianReliability.posterior - 1.45 * holdUpRisk, 1.2 * coverage + 0.9 * referenceAlignment - 0.3 * Math.abs(userUtility - counterpartyUtility), 2.5 * holdUpRisk + 1.65 * renegotiationRisk + 1.15 * expectationGap - 1.25 * totalSurplus, ]).map((value) => round(value)) const stateUtilities = [ { state: '双赢匹配', probability: winWin, utility: round(1.2 * totalSurplus + 0.5 * matchingEquilibrium) }, { state: '中性波动', probability: neutral, utility: round(0.25 * totalSurplus - 0.1 * renegotiationRisk) }, { state: '错配损耗', probability: mismatch, utility: round(-1 * (0.7 + holdUpRisk + riskExposure)) }, ] const expectedUtility = round(stateUtilities.reduce((sum, item) => sum + item.probability * item.utility, 0)) const variance = round(stateUtilities.reduce((sum, item) => sum + item.probability * (item.utility - expectedUtility) ** 2, 0)) const cvarLoss = round(mismatch * Math.abs(stateUtilities[2].utility)) const successPanel = buildSuccessPanel({ matchingEquilibrium, totalSurplus, outsideOptionPressure, coverage, holdUpRisk, expectationGap, downsideProbability: mismatch, controlAlignment: controlRights.controlAlignment, stateContingencyReadiness: controlRights.stateContingencyReadiness, reliability: bayesianReliability.posterior, }) const rawScore = Math.round(clamp( formulaWeights.score.winWin * winWin + formulaWeights.score.matching * matchingEquilibrium + formulaWeights.score.reference * referenceAlignment + formulaWeights.score.coverage * coverage + formulaWeights.score.surplus * totalSurplus, ) * 100) const computableDecisionSignal = hasComputableDecisionSignal(dynamicFeatures, coverage, { winWin, mismatch, currentNegotiation: successPanel.stages.currentNegotiation, fulfillment: successPanel.stages.fulfillment, userUtility, counterpartyUtility, totalSurplus, }) const requiredBlocking = informationSufficiency.blocking && !computableDecisionSignal const adjustedInformationSufficiency = requiredBlocking ? informationSufficiency : { ...informationSufficiency, blocking: false, level: informationSufficiency.level === 'insufficient' ? 'preliminary' as const : informationSufficiency.level, reasons: informationSufficiency.blocking ? [...informationSufficiency.reasons, '开放参数已经足以形成当前初步判断,剩余信息用于校验、排序和优化条件'] : informationSufficiency.reasons, } const score = requiredBlocking ? Math.min(rawScore, 68) : rawScore const verdict = requiredBlocking ? '必要信息不足,不能下最终结论' : winWin >= 0.55 && mismatch < 0.25 ? '可优化双赢匹配' : mismatch >= 0.38 ? '高风险错配' : '信息不足或中性波动' const decision = requiredBlocking ? '先补齐价格、时间、当前市场情况、替代选择等必要条件,再做最终验算' : verdict === '可优化双赢匹配' ? '可以继续推进,但必须先把关键利益、责任、控制权和退出条件结构化' : verdict === '高风险错配' ? '暂停按原条件推进,除非关键风险能通过书面条件、分阶段验证或退出机制消除' : '优先补齐高权重缺失因素,再做最终决策' return { moduleId, moduleName: '全量因子决策引擎', verdict, decision, score, formulas: { generalizedDistribution: 'F(U)=Σ p_s·1(U_s<=u); E[U]=Σp_s·U_s; Var(U)=Σp_s·(U_s-E[U])²; CVaR_loss=P(loss)·|U_loss|', referenceContract: 'A_ref=Σ(w_ref_i·v_i·conf_i)/Σw_ref_i; Gap=1-A_ref+InfoPenalty; HoldUp=0.52·Risk+0.28·Gap+0.20·Missing', bilateralMatching: 'M=0.36·sqrt(U_user·U_other)+0.24·A_ref+0.18·Surplus+0.12·OutsideOption+0.10·(1-HoldUp)', }, features, factorCoverage: { total: totalAvailable, observed, coverage, missingHighWeightFactors, openFactorCount: dynamicFeatures.length, }, requiredFactors: { total: requiredSpecs.length, observed: requiredSpecs.length - missingRequiredSpecs.length, missing: missingRequiredSpecs.map((spec) => spec.label), blocking: requiredBlocking, }, informationSufficiency: adjustedInformationSufficiency, complianceReview, distribution: { winWin, neutral, mismatch }, successPanel, generalizedDistribution: { expectedUtility, variance, downsideProbability: mismatch, cvarLoss, stateUtilities, }, referenceContract: { referenceAlignment, expectationGap, holdUpRisk, renegotiationRisk, fairnessReference, }, controlRights, bayesianReliability, bilateralMatching: { userUtility, counterpartyUtility, outsideOptionPressure, totalSurplus, nashProduct, matchingEquilibrium, }, missingQuestions: buildQuestions(features, specs), modelPayload: { engineVersion: 'contract-engine-universal-factor-matrix-1.1', theoryBasis: ['广义分布函数模型', '不完全参照点契约', '双边匹配均衡', '贝叶斯后验更新', '开放式不定参数矩阵'], constraints: ['大模型只能解释和表达计算结果', '不得替代后台概率分布自行主观判断', '信息不足时必须优先追问高权重缺失因素'], }, } }