TT Lab
开始
学习 学习路径 课程

保险领域进阶

一笔理赔到底赔多少

在 TT Lab 中继续学习

目标

把条款中的保障条件转成代码,编写一个能输出单笔索赔的赔付金额及其依据的判定器。按固定顺序应用免赔额、共付比例、单笔限额和年度限额,按免责期和除外责任拒付,合并同一次事故的多笔索赔,然后对全部 141 笔索赔做判定,输出汇总和报告。

为什么重要

条款是用文字书写的,没有明确顺序。先扣免赔额还是先乘比例,免赔额按每笔索赔还是按每次事故计算,年度限额按事故日期还是受理日期计数,这些都会让同一笔索赔的赔付金额不同。而且哪一种都不会报错。 所以这类判定首先要解决的不是值对不对,而是能不能解释。把规则数值提取成表,并为每笔索赔记录哪条规则扣了多少,这份记录既是给客户的说明,也是回归测试的期望值。 本实验的条款数值(免赔额 1 万到 3 万韩元,共付 10% 到 20%,单笔限额 30 万到 100 万韩元,年度限额 100 万到 500 万韩元,免责期 30、90、180 天)并不是真实产品,而是为本实验虚构的合成表。 评分器不会相信你写的判定文字。它会在临时目录中搭建自己生成的条款、合同和索赔,每次用不同的数值实际运行你的判定器,并把赔付金额和判定依据与评分器另行计算的值进行核对。

步骤

  1. 创建并运行 /root/coverage/gen_claims.py,生成 /root/coverage/claims.db。包含 3 种条款、12 份合同、5 行除外责任、141 笔索赔。
  2. 比较两种应用顺序的合计,并在 /root/coverage/order_compare.json 中写入 claims、order_a_total、order_b_total、difference。
  3. 创建 /root/coverage/adjudicate.py,按免赔额 → 共付 → 单笔限额的顺序扣减,并把扣减的金额保留在 steps 中。
  4. 拒付保障期间之外、免责期之内以及属于除外责任项目的索赔。decision 为 denied,payable 为 0,reasons 中写入全部原因。
  5. 让免赔额每次事故只使用一次。同一次事故的第二笔索赔只扣减剩余的免赔额。
  6. 加入年度限额。按每份合同的事故年度累计,按受理日期顺序用尽,换年后重新恢复。
  7. 只从表中读取条款数值,并把共付计算的舍入固定为四舍五入(ROUND_HALF_UP)。
  8. 用 /root/coverage/make_payout.py 判定全部 141 笔索赔,生成 /root/coverage/payout.json 和 /root/coverage/coverage_report.md。

参考

创建条款表和索赔快照

创建并运行 /root/coverage/gen_claims.py,生成 /root/coverage/claims.db。包含 3 种条款、12 份合同、5 行除外责任、141 笔索赔(其中多笔索赔的事故 31 起)。

先创建 /root/coverage,然后在其中用 python3 创建 sqlite 数据库。表有 plan、policy、exclusion、claim 四张。索赔对应 100 起事故,其中一次事故对应两笔以上索赔的有 31 起。

应用顺序会使合计相差多少

在 /root/coverage/order_compare.json 中写入 claims、order_a_total、order_b_total、difference。A 是先扣免赔额再应用共付比例所得值的合计,B 是先应用共付比例再扣免赔额所得值的合计,两者都针对全部 141 笔索赔,负数按 0 处理。

这一步不是判定,而是比较。不要考虑限额、免责期和除外责任。A 是从 max(0, 索赔金额 - 免赔额) 中减去共付部分所得的值,B 是从索赔金额中减去共付部分,再减去免赔额,并在 0 处截断所得的值。共付部分按四舍五入取整到韩元的整数。

依次扣除免赔额、共付和单笔限额

创建 /root/coverage/adjudicate.py,按免赔额 → 共付 → 单笔限额的顺序,计算通过 --claim 传入的索赔的赔付金额,并把每条规则扣减的金额保留在 steps 中。

条款数值从 plan 表中读取。免赔额可能大于索赔金额,所以要用 min 截断;单笔限额只扣减超出的部分。steps 的每个元素有 rule、amount、after 三个键,最后一个 after 就是 payable。

按保障期间、免责期和除外项目拒付

拒付保障期间之外的事故、免责期之内的事故以及属于除外责任项目的索赔。decision 为 denied,payable 为 0,steps 为空列表,reasons 中放入 out_of_period、waiting_period、excluded 中所有适用的项。

免责期是事故日期与合同开始日期之间的间隔,而不是受理日期。日期请用 date.fromisoformat 和 timedelta 处理。保障期间之外的事故不必再判断免责期,但除外项目可以与之无关地同时记录。

免赔额每次事故只扣一次

让同一 accident_id 的索赔共用免赔额。第一笔索赔用完后,第二笔索赔的 deductible 步骤为 0;如果第一笔索赔小于免赔额,剩余的部分由下一笔索赔接着使用。

按事故记录已经用掉的免赔额,每次扣减剩余部分与索赔金额中较小的一个。索赔必须按受理日期顺序处理才能得到同样的答案——顺序一变,由哪笔索赔承担免赔额就会不同。

用尽年度限额

把年度限额作为第四条规则加入。按每份合同的事故年度累计,只扣减超出剩余额度的部分,换年后限额重新恢复。steps 的最后一项是 annual_cap。

年度限额取决于该合同之前的索赔已经用掉了多少。按受理日期顺序遍历判定,并按(合同、事故年度)累计已用金额。限额用尽后的索赔,赔付金额为 0,但并不是拒付——decision 仍是 paid,由 annual_cap 扣减全部金额。

数值放在表中,舍入作为策略

只从 plan 表中读取条款数值(不要把值写在代码里),并把共付计算的舍入固定为四舍五入(ROUND_HALF_UP)。

Python 的 round() 和 decimal 的默认值会把恰好处于一半的值向偶数方向取整。如果条款规定的是四舍五入,就必须在 Decimal 的 quantize 中明确指定 rounding。评分器会替换表中的数值,检查判定是否随之变化,以及恰好处于一半的位置是否会进位。

全部判定与赔付报告

用 /root/coverage/make_payout.py 判定全部 141 笔索赔,生成 /root/coverage/payout.json(claims、paid、denied、billed_total、payable_total、denied_by_reason、cut_by_rule)和 /root/coverage/coverage_report.md(## 무엇을 판정했나 ## 어떤 규칙이 얼마를 깎았나 ## 거절한 건과 이유 ## 남은 위험)。

不要重新实现判定器,而是汇总 --all 的输出。cut_by_rule 是按规则累加 steps 中 amount 得到的值,denied_by_reason 是按原因统计 reasons 的次数。有两个原因的索赔,在两个原因下各计一次。报告中请用数字写出赔付合计。