实习日志 / 中文 / 报案端前端 / 系统显示,模型说话:报案端从加功能到做收纳
系统显示,模型说话:报案端从加功能到做收纳
中文 | English
9 月 30 日,我在本地把匿名报案从头走了一遍。报案人的主线本来只有一条:用自己的话讲经过,Agent 追问缺的,随手核对记下的内容,最后提交。走下来,这条线断在几个地方:记下的信息客户看不到,进度又不准;一个"确认"按钮会把人直接推进收款;没登录就不能传照片;手机上对话被挤到了屏幕下半截。此外还有一串小毛病,比如 Enter 不发送、标题显示内部编号。
系统显示,模型说话
当时系统记下的信息放在右侧约 400px 宽的侧栏里,内容长,要上下滚动,客户在聊天时根本注意不到。客户刚说过的话在那里被标成 "Needs review",读起来像说错了什么。进度在标题下、侧栏顶部、概览卡里出现了三次,而且客户讲完经过和地点以后三处都还是 0,因为 AI 提议、尚未确认的事实不计入进度。为了让客户知道记下了什么,Agent 每一轮都把记下的值逐项复述一遍。复述占篇幅,也不可靠,模型转述的值和系统里存的值可能对不上。
改造方案定了一条原则:记下了什么、完成了多少,由界面按系统数据直接显示;模型只负责自然地聊天和追问。具体的形态是 Agent 回复下面的一张"刚记下的"卡片:
Agent: Glad no one's hurt and the car still drives. Did the other driver accept fault?
┌──────────────────────────────────────────┐
│ Just noted 5 of 7 noted · 2 to check │
│ · Location: Queen St, at the lights │
│ · Vehicle: drivable │
│ · Other vehicle: yes │
│ [ Looks right ] [ Edit ] │
└──────────────────────────────────────────┘
卡片只显示这一轮新增或变更的字段。右上角是界面上唯一的进度,客户说了但还没确认的事实也算进去,同时注明待核对的数量。"Looks right" 一次确认整张卡,复用现有的 POST /claims/{id}/form/confirmations;客户不理卡片、直接接着聊也可以,下一张卡出现后旧卡收成一行"3 项待核对"。客户自己说的内容,状态叫 "To check",不叫 "Needs review"。
Agent 那边的要求随之变成:两句以内,最多一个问题,不复述,目标 240 字符。硬上限仍然是 500,因为 OpenAI 的严格模式不支持 maxLength,设成 240 会让超长回复整轮失败。
同一个 PR(#1160)还把答题卡固定了下来。所有字段共用一种格子,每个注册表版本一份;本轮不允许的字段或形状不对的值,单独丢掉并记录,其余照常生效,不再整轮失败。按仓库的估算方法,首轮车险请求的答题卡从 4,508 token 降到 2,069 token。
用 10 个案例(追尾、停车场被撞、冰雹、水管爆裂、厨房起火、手机丢失等)各跑 2 轮做对比:
| 改前 | 第一次提交 | 最终版本 | |
|---|---|---|---|
| 失败轮数 | 0 / 20 | 0 / 20 | 0 / 20 |
| 回复长度中位数(字符) | 172 | 94 | 108 |
| 复述比例 | 0.32 | 0.07 | 0.14 |
| 客套话收尾 | 2 | 0 | 0 |
| 耗时中位数(秒) | 3.44 | 3.63 | 3.07 |
| 记下的字段总数 | 61 | 54 | 65 |
复述比例按"客户原话的三词短语在回复中出现的比例"算。第一次提交回复短了,字段却少了约 12%。原因是答题卡固定以后,v8 模型唯一能看到枚举可选值的地方没了:本以为 field_value_contracts 在上下文里,实际只在 v7 的路径上。模型猜错的枚举值,被新的"丢弃不合规字段"规则丢掉了。修法是在上下文里加 writable_fields,列出本轮可写的字段,按类型分组,枚举字段附上可选值,和校验用同一份字段合同,字段数回到 65。判断"模型看得到什么",要读实际发出去的请求,不能读相邻路径的代码。
点确认,就要银行账号
原来的流程里,客户在侧栏点一下"确认",字段一齐,前端就在对话里插入收款卡片,看上去就是刚确认完,AI 立刻开始要银行账号。收款这一步本身也多余。收款账户、联系方式、保单、车辆和房产,个人资料里都有(/account/profile、/account/policies、/account/payment-destinations、/account/assets),也有"报案时自动带入"的偏好,报案流程却要人在 Step 3 再选一遍。
提交路径改成:字段齐了,对话里出现一张"准备提交"卡片,点开是一页核对:事故摘要、材料清单、收款账户(默认取资料里的默认账户,显示成 "ANZ ••••1234 · Change")、联系方式、同意条款、提交按钮。条款的勾选框在客户打开条款之前不能勾,提示 "Open the terms to continue"。提交按钮依次调用现有的三个接口:保存收款与联系方式、同意条款、创建理赔。后端的步骤划分不变,前端不再把它们拆成单独的两步,Agent 也不再主动推进收款。
未登录的人点"查看并提交",先弹登录或注册,登录后用现有的 /claims/{id}/promote 把理赔单转到账号下,回到同一张核对页。
没登录也能拍照
匿名报案是仿照 ChatGPT 的做法设计的,给刚出事、着急的人用,不用先注册就能开始讲。可传照片被前后端各拦了一道:前端没有登录令牌就直接报 "Sign in before uploading a file",后端的 require_durable_claimant 也拒绝匿名会话上传内容。结果是客户最需要拍照的时候被要求登录。仓库自己的前端质量标准里其实写过,文件持久化需要认证,但匿名上传可以在当前 session 里处理,只要讲清楚它的生命周期。
所以匿名时的文件,我定的做法是放进一个和匿名 session 绑定的暂存区,session 不过期,文件就一直在。键就是浏览器本地保存的 X-Northwind-Anonymous-Session,同一个 session 下的理赔单都能引用暂存区里的文件,Agent 也能读这些照片。登录时 promote 一并把被引用的文件转成正式材料,没被引用的留在暂存区。
限额是单个文件 10MB,每个匿名 session 最多 20 个文件、共 100MB,防止被拿来当网盘。超出时提示 "This report already holds 20 files. Sign in to add more." 客户清掉浏览器数据或换设备,就拿不回这个 session,这和匿名对话本身的行为一致。后端要改的是把 require_durable_claimant 的适用范围收窄到账号级材料,新增以 session 为键的暂存存储,复用现有的对象存储和图片就绪流程。这一块涉及身份和隐私,单独做,失败路径和测试按高风险类别要求补齐。
模型选择器只说产品名
输入框里的下拉框原来显示 openrouter-gpt4o… 这类配置代码。选择器保留,但只显示产品名:
- Northwind Assist:主力云端模型。"Fast. Reads your photos and documents."
- Northwind Private:本地部署的 Qwen。"Text only. Your details stay on Northwind's own servers."
Private 必须写明只处理文字,因为 qwen-local 的配置是 image_input: false、document_input: false,不写清楚,客户会以为 AI 看过了照片。
两个塞满的仓库
这些改动陆续落地以后,到 10 月 2 日,界面又出现了另一种问题:功能是一项一项加出来的,没有统一的摆放规则。聊天区什么都放,回复下面有表格式的事实卡、彩色回执、服务同意大卡片、流程提示卡,中间还会插入整张收款表单、资料带入面板、6 种系统事件卡,一屏里同时有七八种视觉形态。右侧面板也什么都放:待办、带进度条的概览卡、分组清单、下一步、文件、第三方服务入口。进度又在标题栏、事实卡右上角、面板概览卡里各出现了一次。
这一轮的性质是收纳:不删功能,只做归位、合并和减重。先定 8 条规则,后面每一项的去处都由规则推出来:
- 聊天只放对话。一条 Agent 回复是文字加至多一行附件条,附件条由小标签组成(事实、回执、服务、文件判断),默认收起,点开才显示细节。
- 要填写、要核对、要读长内容的,都去面板的置顶区,聊天里只留一个入口标签。
- 状态只在标题栏出现一处。进度、阶段、人工协助状态合成一个状态区。
- 系统事件是对话流里的一条分隔线加一句话。
- 面板是档案。默认关闭,常驻三区依次是待办、已记下、文件;置顶区同一时刻只放一项。
- 只用一种强调色表示"需要你做",其余信息一律中性。
- 页头只放身份和出口:品牌、返回、一个求助入口。
- 工具页不挤进对话的中间区。
照这几条,"刚记下的"卡片从一张占半屏的表格,收成附件条里的一组事实标签:"Dominion Road · Today about 5
· No one hurt",有待核对的加一个小点,点开在原地展开成可改的精简列表。服务同意卡变成一个标签"Towing · needs your OK ›",点开在面板置顶显示完整的同意卡。条款引用做成可点的标签,点开在置顶区显示条款原文;回合记录里的cited_clauses 只保留这一轮实际检索到的条款。"外部服务"整页删掉,内容进了面板。
这一轮先做成一个直接引用 token 库的 HTML 原型,按场景分帧定下形态,再落到代码里(#1184)。视觉数值照旧全部取自 frontend/shared/design-tokens.css,这一轮新增了 --claimant-chip-*、--claimant-title-bar-height、--claimant-rail-width、--claimant-case-sheet-half-height 等 token。
手机上只剩对话
手机上原来是整页滚动:页头、History / Chat / Details 三个标签、换成两行的内部编号标题、进度条依次堆下来,占掉屏幕上方约 40%,第一条消息在那之后,输入框的工具栏常常在首屏之外。想看记下的信息,要切到 Details 标签,也就是离开对话。响应式布局只在早期做过,后来的改动都没跟上。
手机上没地方放侧栏,对话里的事实标签就是主要的核对方式。宽度小于 640 时,对话区单独滚动,输入框固定在底部,留出 env(safe-area-inset-bottom) 的安全区,键盘弹出时跟着上移;标题栏收成一行:返回菜单、短标题、状态;面板改成底部抽屉,停在半屏或全屏两个高度,下拉关闭,有置顶项时自动停在半屏。验收的宽度是 390、640 和 980 两侧,以及 1366。
员工不在收集期间进来
收纳的时候有一个问题绕不开:客户途中提出的、Agent 答不了的问题,放哪?原来的做法是发起人工交接,员工进到会话里来。客户一多,员工端就会被大量临时问题淹没。
10 月 3 日我定的规则是:员工不在案件收集期间进入会话。途中 Agent 答不了的问题、需要专业判断的点、客户的一般联系请求,都记进待答清单,每项附客户原话和 Agent 的建议答复及依据,客户会被告知"提交后由理赔团队处理"。紧急安全照旧直通交接;客户明确而强烈地要求人工,或一般请求被暂缓后再次坚持,也直通。这个判断交给 Agent,写在提示词里,Runtime 不写死规则。客户端的求助按钮发出的是一条普通消息,同样由 Agent 判断。
员工接手案件时,先弹出待答清单,每项可以"同意并发出""修改后发出""驳回"(写原因),联系请求标为"已联系"。答复以负责人的身份出现在客户的对话里。后端的结构是:
northwind_questions:
kind / question(客户口吻)/ claimant_wording / proposed_answer / basis / contact_preference
POST /workbench/claims/{id}/questions/{qid}/answer
decision: approve | edit | decline | contacted
客户端只看到 question 和状态。立案后负责人发过言,Agent 就让路;有待答事项或客户回复的已立案案件,回到 processing 队列。
后来回到那条主线
9 月 30 日那一轮修的是断点:看不到的记录、半路冒出来的收款、不能拍照、挤在下半截的手机对话。10 月 2 日那一轮修的是堆积:每个功能单独看都合理,放在一起就把对话淹没了。两轮用的是同一个判断。客户的注意力在对话上,系统知道的事由界面准确地显示一次,模型只负责说话;凡是需要客户做点什么的,放到客户正在读的那条消息旁边,或者面板最上面那一格。