ccRAG:美国信用卡问答
覆盖 Chase、美国运通、美国银行共 54 张信用卡的中英双语问答系统。用中文或英文提问,得到一个简短的回答,回答里每个数字都标注出处:银行官网的产品页、持卡人协议、权益说明,或者带日期的博客文章。检索基于 LlamaIndex 和 Qdrant,回答由本地 9B 大模型生成,整个系统用家里的一块显卡对外提供服务。每个设计选择都由对比实验决定:比较规则在跑实验之前就定好,评测集逐题人工审核。
私有仓库。如想了解代码,欢迎联系。
亮点
- 检索是关键: 采用”小块检索、大块返回”(512 token 小块,2,048 token 大块),再加上按卡名过滤,测试集 Recall@5 从 0.62 提升到 0.93。卡名过滤靠一张人工审核的别名表,收录了中文俗称和 CSP、BCE 这类缩写。中文提问、但事实只在英文官网上的题目,Recall@5 从 0.34 提升到 0.97。
- 不检索,模型基本没法用: 不给任何资料直接问,本地 Qwen3.5-9B 在 61 道测试题里答错 57 道(评委打分 0.10 / 2)。有了检索后得分 1.69 / 2,和 Claude Sonnet 相差不到 0.2,而且每次查询零成本。
- 严谨的评测:
- 122 道评测题,每题都有标准答案和逐字摘录的证据原文,逐题人工审核。
- 所有选择都在开发集上做,测试集只在最后跑一次。
- 比较规则在跑实验之前就定好,每组比较都给出配对 bootstrap 95% 置信区间。
- 如实保留负面结果: 混合检索、交叉编码重排、官方资料优先排序、思考模式都试过,没有一个带来显著提升。重排让耗时增加 7–20 倍;思考模式下有 11/61 题回答为空。
- 大模型评委与人工评分校准: Claude Opus 按 0–2 分标准盲评,和我本人的盲评一致性为二次加权 κ 0.63,分歧从不超过一档。
- 真正对外运行: 经 Cloudflare Tunnel 对外提供服务,并做了加固:
- 请求排队,并显示排在第几位
- 按 IP 限流
- 提示词注入防护,在 122 道评测题上零误拒
- 访客和反馈日志,以及只有我能打开的统计页
工作原理
- 抓取与解析: 用无界面 Firefox 渲染银行产品页,包括隐藏的标签页和读屏文字;协议和权益说明 PDF 原样保存;再加上中英文博客文章。共 237 份文档,统一转为 Markdown。
- 建立索引: 按”小块检索、大块返回”切块,用多语言 BGE-M3 向量化后存入 Qdrant,共 2,961 个段落。
- 识别卡片: 别名表把”CSP”“蓝宝石”这类叫法对应到具体的卡,只在这些卡的资料里检索。名字有歧义时反问”你说的是哪一张”,不去猜。
- 带引用地作答: 最相关的 5 段资料连同标签(官方或博客、日期、语言)交给模型,提示词要求以官方数字为准。引用按出现顺序重新编号,只显示真正被引用的来源。
结果
测试集(61 题),所有选择在开发集上定好之后只跑一次
| 生成模型 | 评委打分(0–2) | 闭卷(不检索) | 每次查询成本 |
|---|---|---|---|
| Claude Sonnet 5.5(API) | 1.87 | 1.08 | 约 $0.03–0.04 |
| Claude Haiku 4.5(API) | 1.79 | — | 约 $0.01 |
| Qwen3.5-9B(本地,上线) | 1.69 | 0.10 | $0 |
| Gemma 4 12B(本地) | 1.67 | — | $0 |
| Granite 4.2 8B(本地) | 1.34 | — | $0 |
本地模型主要输在”该信哪条资料”。遇到博客宣传其他渠道更高优惠的陷阱题,Qwen 得 0.6 / 2,Sonnet 得 2.0 / 2。
截图
卡包:把卡拖进提问栏(或双击卡面),只检索这张卡的资料
对比题回答,引用按出现顺序编号,下方只显示被引用的来源
"蓝宝石"对应两张卡,系统会反问,不去猜
英文界面:鼠标悬停在引用上,可以看到原文段落,对应数字会高亮
对外运行
- 和开发版分开: 公开站只读发布出去的快照,包括网页、卡面和自己的一份索引副本。开发中的改动不会直接上线,也不会和实验抢向量库的锁。
- 设置固定,处处有上限:
- 只用一个模型,设置固定,浏览器传来的参数一律忽略
- 问题最多 500 字,回答最多 800 token
- 一次只回答一个问题,最多 6 人排队
- 每个 IP 每分钟最多 6 题、每小时 60 题
- 出错时浏览器只收到错误代码
- 防提示词注入:
- 公开站的提示词会拒绝和信用卡无关的请求,并忽略问题里夹带的指令。
- 9 种攻击(扮演角色、索要系统提示词、写长文、重复灌水、违规请求)中 8 种直接被拒。
- 第 9 种让模型在回答末尾加一个链接;现在服务器会删掉含有资料里没提到的网址的句子。
- 不用登录也能统计访客:
- 用浏览器匿名标识统计独立访客。
- 访问、提问、回答和 👍/👎 写进本地日志。
- 统计页只有我能打开。
- 开机自启: 用 systemd 服务开机启动网站、隧道和统计页。不用在路由器上开端口,家里 IP 也不会暴露。
技术概览
| 检索 | LlamaIndex、Qdrant、BGE-M3(稠密向量)、小块检索大块返回、基于别名表的按卡过滤 |
| 生成 | Qwen3.5-9B(Ollama 本地运行);对比了 Gemma 4、Granite 4.2、Claude |
| 评测 | 122 道人工审核题、配对 bootstrap、大模型盲评(κ 0.63) |
| 后端 | Python 3.12,服务器推送事件(SSE)流式输出 |
| 前端 | React 19、TypeScript、Vite、Tailwind、shadcn/ui、Motion、dnd-kit |
| 部署 | Cloudflare Tunnel、systemd、一块 RTX 5070 Ti(16 GB) |
| 语言 | 问题、界面和回答均支持中英文 |