智愿通
面向高考志愿咨询的智能决策 Agent 练习项目。把「录取事实查询」和「政策资料检索」放在同一条工作流里,由规则代码负责冲稳保分层与事实校验,大模型负责理解问题和组织语言。目前仍在学习中。
项目背景
高考志愿填报是个典型的高利害问答场景——用户问的「我这个分能不能上」必须给出有依据的答案,记错一个位次就可能误导一个真实的决策。同时,它的问题形态还很不统一:有人要查具体的录取数据,有人问政策概念,有人问某个专业怎么样。
这个项目想验证一件事:面对高风险事实型问答,怎么让大模型「有依据地说话」。核心思路是把职责拆开——
- PostgreSQL 负责录取事实(分数、位次、招生计划、一分一段)
- RAG 负责政策、院校和专业说明资料
- 规则代码 负责冲稳保分层和事实校验
- 大模型 只负责理解问题、组织语言,不决定事实
它也是我用来深入理解 Agent 编排、检索质量与评测体系的练习场。
技术栈
| 层次 | 技术选型 | 承担的角色 |
|---|---|---|
| 语言 | Python 3.13 | — |
| 工作流 | LangGraph | 主图 + 子图、条件分支、断点中断、PostgreSQL Checkpointer |
| 存储 | PostgreSQL 17 + pgvector | 结构化表 / 向量 / 全文索引 / 画像 / 会话状态同一个库 |
| 对话模型 | Qwen 系列 | 意图理解与最终回答组织 |
| 嵌入 | text-embedding-v4(1024 维) | 子块向量化 |
| 中文检索 | jieba 预分词 + 领域词典 | 解决 PG 原生全文检索不切中文词的问题 |
| 后端 | FastAPI + SSE | 接口服务与流式事件推送 |
| 前端 | React + Vite | 会话工作台 |
| 评测 | pytest + 自实现忠实度评测 | 检索消融、忠实度与引用准确率 |
架构:两条查询链路
用户自然语言
├─ 结构化链路:模型抽条件 → 代码校验 → 参数化 SQL → 冲稳保规则打标签
└─ 检索链路: pgvector 稠密 + tsvector 稀疏 → RRF 融合 → 回取父块
↓
统一证据上下文 → 最终模型组织回答 → 事实与来源校验 → SSE 返回
几个关键设计决定
不让模型写 SQL
模型只负责抽取结构化条件,SQL 由固定逻辑生成、参数化传值、条件受白名单约束。原因很直接:这是「够不够分」这类高利害问题,模型写错 SQL 不会报错,只会静默给出错误答案。
混合检索而不是纯向量
纯向量检索对高校名、专业名这类专名会漂移,纯关键词检索又扛不住口语改写。两路分数不是一个量纲,所以只融合排名——用 RRF。Recall@1 从 0.9231 提到 0.9487,提升集中在 Top-1,也就是最影响推荐结果的位置。
单库而不是多库
结构化表、向量、全文索引、画像、会话状态都放在一个 PostgreSQL 里。当前数据规模用 pgvector 完全够,单库能让这些数据共享事务与连接池,减少一致性问题。
先校验再展示
为避免「错误数字先展示、随后整段回退」这种比慢更伤体验的情况,采用的是先生成完整回答、校验通过、再分片发送。用户看到的是逐段呈现,但它不是模型首 token 实时流式——这是事实准确性与响应体验之间的取舍。
在幻觉治理上做的事
| 手段 | 说明 |
|---|---|
| 来源严格匹配 | 不取文件名后缀,走全路径匹配,避免错误归因 |
| 冲稳保确定性校验 | 标签由代码依据位次规则计算,模型只解释不改写 |
| 事实保护 | 结构化结果进入最终模型后,再做一遍事实一致性校验 |
| 忠实度评测 | 自实现,评判回答断言是否被本轮检索上下文支持 |
| 评测指纹 | 报告记录题集与被测代码哈希,不一致则强制全量重跑 |
当前状态
这是一个本地练习项目,仍在学习中。数据覆盖范围有限,功能在持续完善,没有对外提供服务,也不作为成熟产品对外运营。文中的技术选型与实测数据,来自我在练习过程中真实跑过的实验。
- 结构化查询、混合检索、评测与幻觉治理链路已跑通
- 检索消融与忠实度评测有可复现的报告产物
- 数据覆盖范围有限,暂未覆盖全国,也不宣称「全国数据库」
- 仍在持续:补充数据、扩充评测题集、优化响应时间
相关笔记:为什么纯向量检索不够用 · 38 个单测全绿,功能一次都没触发过 · 评测工具比业务代码更危险