保险/金融咨询大模型智能办理:基于工具调用与策略编排的完整方案及源代码
本文介绍一种面向保险/金融咨询场景的智能对话方案以及详细代码:通过**一次大模型调用完成槽位抽取**,再通过**策略编排与话术模板**完成总结与追问,从而避免「总结 + 追问」的二次 LLM 调用,在保证信息收集完整、流程可控的前提下,显著降低延迟与成本。
引导式对话系统(上):应用场景、业务模式与技术架构 与 引导式对话系统(下):槽位抽取、Function Calling 与流程编排怎么落地? 两篇文章详细介绍了引导式对话的技术架构以及各种实现方案。
本文介绍一种面向保险/金融咨询场景的智能对话方案:通过一次大模型调用完成槽位抽取,再通过策略编排与话术模板完成总结与追问,从而避免「总结 + 追问」的二次 LLM 调用,在保证信息收集完整、流程可控的前提下,显著降低延迟与成本。
代码以及文档参考:
- 源代码链接
- 引导式对话系统(上):应用场景、业务模式与技术架构
- 引导式对话系统(下):槽位抽取、Function Calling 与流程编排怎么落地?
- 保险/金融咨询大模型智能办理项目代码——运行说明与项目架构
关键要点
- 一次 LLM + 策略编排:每轮仅一次大模型调用做信息抽取,总结与追问全部由模板与策略函数完成,无二次 LLM。
- 六大主题有序收集:投保人基本信息、家庭结构、需求与偏好、已有保障、健康告知、补充说明,按固定顺序逐主题推进,用户可跨主题一次性回答。
- 缺失槽位递归查找:纯逻辑判断当前缺失槽位与下一主题,决定是「模板化反问」还是「总结 + 开启下一主题」或结束语。
- 槽位持久化:基于 SQLite 按
group_id持久化槽位,多轮对话可续写与修正,支持跨会话恢复。 - 流程可控、易扩展:话术与流程由配置与代码驱动,便于维护;可叠加提示词/正则等补漏机制提升抽取稳定性。
1. 项目背景与目标
1.1 项目背景
- 保险/金融产品咨询量大,机构需大量售前人员,人工成本高。
- 需先摸清客户情况(年龄、收入、家庭、需求、健康等)才能推荐产品,线上咨询成单率低但咨询量巨大。
1.2 项目目标
开发一款智能对话机器人,实现售前接待 + 信息引导收集,替代或前置人工客服,降低运营成本,收齐信息后做产品匹配或转人工。
1.3 产品交互要求
| 要求 | 实现方式 |
|---|---|
| 主动引导 | 通过缺失槽位查找函数与话术模板自动追问 |
| 按主题推进 | 一次询问一个主题的所有子问题,收集完总结并二次确认后开启下一主题 |
| 灵活对话 | 用户主动提及某主题时,优先追问该主题下的其他问题 |
2. 项目代码结构介绍
本节对项目代码结构与主流程做简要说明;更详细的文件说明、流程图与源码调用追踪见 README_运行与架构.md。
2.1 主要文件与职责
| 文件 | 说明 |
|---|---|
main.py |
Gradio 网页对话入口,在浏览器中与保险咨询助手聊天 |
server_api_prod.py |
FastAPI 服务入口,提供 POST /insurance_chatbot 流式接口 |
server_post_prod.py |
示例客户端,用于测试 API 服务 |
chat_process_client.py |
核心:对话主流程(首轮引导、LLM 调用、工具解析、槽位更新、模板反问/总结) |
function_utils.py |
槽位排序、缺失槽位查找 get_next_message_by_slots、总结模板类 FunctionCallSummaryConfig |
tools_definition.py |
工具定义(Function Calling 的 tools 描述、槽位与话术配置) |
config_parser.py |
API、数据库等配置 |
2.2 主流程概览
用户输入后,先读取历史槽位;若为首轮则直接发送固定引导语并流式输出;非首轮则进入 LLM 流式调用。若 LLM 返回普通回复(finish_reason=stop),则直接流式返回;若触发工具调用(tool_calls),则解析并执行工具、更新 all_filled_slots 并写库,再调用 get_next_message_by_slots:当前主题有缺失则模板化反问,无缺失且有下一主题则总结并开启下一主题,全部完成则发送结束语,无缺失且槽位未变则走 LLM 自由对话。详细文字流程与 Mermaid 图见 保险/金融咨询大模型智能办理项目代码——运行说明与项目架构。
3. 结果展示
当我们根据 保险/金融咨询大模型智能办理项目代码——运行说明与项目架构 在本地创建了虚拟环境,安装了依赖,添加了大模型的Base URL,API等,以及开始运行。我们通过 http://127.0.0.1:7860 在本地的浏览器打开前端,可以看到界面如下:

这个界面由三部分组成:
- chatbot对话框;
- 当前槽位信息展示;
- 槽位重置。
当我们在对话框进行对话,我们会发现,大模型会有意引导用户去填充槽位。而在 当前槽位信息展示 栏,我们也可以查看当前槽位的填充情况。
比如,如果一开始,用户问了一句 你好。自然的,不会有任何槽位被填充。我们看一下大模型的回复,以及槽位填充栏的结果:

我们可以看到,系统对于用户的 你好 回复了 欢迎咨询保险服务,我是您的智能顾问。请问您的年龄、职业和大概年收入区间?。
如果这个时候用户说 我今年40岁,这里有一个槽位会被填充:年龄。我们看一下大模型的回复,以及槽位填充栏更新后的结果:

我们看到,系统回复 收到反馈:年龄:40岁。还得辛苦您补充一下: 职业,年收入区间。相当于,系统会先和你确认一下用户刚才说的信息,然后再引导用户进一步填充其他槽位。
如果用户输入的回复包含了多个槽位信息,比如 我是一名教师,现在年收入在20万元左右。。这时,大模型会提取这两个槽位,然后再引导用户填充其他槽位。如下图:

这时候,用户突然想到,他之前输入的信息有误,比如,用户的年收入不是20万,而是10万。我们来看看大模型会怎么回复:

我们看到,对于用户的问题 哦不对,我搞错了,我现在的年收入只有10万元不到。,系统回答 已收到您的反馈:年龄:40岁 职业:教师 年收入区间:10万以下您是否是家里主要经济支柱?目前有几个孩子?。我们看到,槽位 年收入区间从 20万元左右跟新为了10万元以下。并且,大模型继续引导用户继续填充其他槽位。
如果用户这时候又输入了一个无关紧要的回答,比如 我也不清楚,我们来看看系统怎么回答:

槽位并没有任何改变,这也很合理,大模型进一步引导用户先填充其他槽位:
没关系,我来帮您梳理一下:
✅ 重疾险:覆盖大病治疗费用+收入损失补偿(适合家庭支柱)
✅ 寿险:保障家人生活、房贷/教育金等责任(尤其推荐主要养家者)
✅ 意外险:杠杆高、价格低,应对突发意外风险
✅ 年金险:为养老或子女教育做长期储蓄规划
您可以先告诉我:
🔹 最担心什么风险?(如生病花大钱、意外伤残、孩子教育费、退休后没钱花等)
🔹 或者有没有已经买过的保险?比如公司团险、之前买的重疾险等?
最后,当我们点击 重置槽位 按钮并确认后,系统将清空槽位所有信息,并且对话系统重置,如下图:

4. 方案核心逻辑
本方案的核心是**「一次大模型调用 + 策略编排驱动话术」**。
4.1 Agent 层(一次 LLM 调用)
- 仅通过一次大模型调用,理解用户本轮输入,通过 Function Calling 将用户自然语言中的信息结构化到预定义的业务槽位中。
- 关键优化:省略传统流程中「总结信息 + 追问下一个问题」的二次 LLM 调用,完全由话术模板和策略函数替代。
4.2 策略编排层(无 LLM 调用)
- 缺失槽位递归查找函数:遍历所有主题和槽位,识别当前缺失的信息,决定下一轮需要询问的内容。
- 话术模板生成:整合本轮收集到的信息与历史信息,通过固定模板生成自然语言回复,完全替代二次 LLM 调用。
5. 信息收集设计
5.1 六大信息主题与对应槽位
| 主题 | 函数名 | 核心槽位 |
|---|---|---|
| 1. 投保人基本信息 | get_policyholder_basic |
年龄 (age)、职业 (occupation)、年收入区间 (income_range) |
| 2. 家庭结构 | get_family_info |
是否主要养家 (is_breadwinner)、子女数 (children_count) |
| 3. 需求与偏好 | get_insurance_demand |
险种意向 (product_types)、保额预期 (coverage_expectation)、缴费预算 (premium_budget) |
| 4. 已有保障 | get_existing_insurance |
已有保险 (existing_insurance) |
| 5. 健康告知 | get_health_info |
健康状况简述 (health_summary) |
| 6. 补充说明 | get_other_info |
特殊需求及其他 (special_needs) |
5.2 槽位初始化结构
slot_dict = {
"get_policyholder_basic": {"age": "", "occupation": "", "income_range": ""},
"get_family_info": {"is_breadwinner": "", "children_count": ""},
"get_insurance_demand": {"product_types": "", "coverage_expectation": "", "premium_budget": ""},
"get_existing_insurance": {"existing_insurance": ""},
"get_health_info": {"health_summary": ""},
"get_other_info": {"special_needs": ""}
}
5.3 主题询问话术模板
main_question_schema = {
"get_policyholder_basic": "请问您的年龄、职业和大概年收入区间?",
"get_family_info": "您是否是家里主要经济支柱?目前有几个孩子?",
"get_insurance_demand": "您想了解哪类保险?重疾、寿险、年金还是意外?期望保额和年缴费预算大概多少?",
"get_existing_insurance": "您目前是否已有其他保险或保障?",
"get_health_info": "方便简单说一下您的健康状况吗?用于后续方案参考。",
"get_other_info": "还有什么需要补充说明的吗?例如海外就医等特殊需求。",
}
5.4 槽位中文映射(用于反问)
params_schema = {
"age": "年龄",
"occupation": "职业",
"income_range": "年收入区间",
"is_breadwinner": "是否主要养家",
"children_count": "子女数",
"product_types": "险种意向",
"coverage_expectation": "保额预期",
"premium_budget": "缴费预算",
"existing_insurance": "已有保险",
"health_summary": "健康状况",
"special_needs": "其他补充",
}
6. 核心技术实现
6.1 Function Calling 调整方案
- LLM 职责:仅负责信息抽取,通过 Function Calling 将用户输入结构化到槽位中。
- 关键优化:省略传统流程中「总结信息 + 追问下一个问题」的二次 LLM 调用,完全由话术模板和策略函数替代,大幅提升响应速度。
6.2 话术模板的实现
- 总结信息:当一个主题的所有槽位都收集完成后,调用对应的模板函数生成总结。
def get_policyholder_basic(age, occupation, income_range):
return f"年龄:{age} 职业:{occupation} 年收入区间:{income_range}"
- 追问缺失槽位:根据缺失槽位查找结果,用模板拼接出下一轮问题。
- 场景一(当前主题完成):开启下一个主题。
收到反馈:年龄:40岁,职业:教师,年收入区间:10万以下。
后续我还想了解一下:您是否是家里主要经济支柱?目前有几个孩子? - 场景二(当前主题缺失):继续追问当前主题缺失的槽位。
您已告知年龄 40 岁、职业是教师,
那么我还想知道:您的年收入区间大概是多少呢?
- 场景一(当前主题完成):开启下一个主题。
6.3 缺失槽位递归查找函数
def get_next_message_by_slots(filled_slots):
filled_slots = sort_nested_dict(input_dict=filled_slots)
next_collect_slot_list = []
for theme, slot_value_dict in filled_slots.items():
for slot, value_elem in slot_value_dict.items():
if value_elem == "":
next_collect_slot_list.append(slot)
if next_collect_slot_list:
if len(next_collect_slot_list) == len(slot_value_dict):
is_all_field_missing = True
else:
is_all_field_missing = False
return next_collect_slot_list, is_all_field_missing, theme
return [], False, ''
返回值含义:
next_collect_slot_list:下一轮要收集的槽位列表is_all_field_missing:当前主题是否所有槽位都缺失theme:当前要处理的主题
6.4 流式输出
- 模板话术通过
stream_string()按字符块流式输出 - LLM 自由回复走聊天补全的流式接口
6.5 历史槽位增补与修正
- 历史有值、本轮为空或「先空着」等:保留历史值
- 新旧值不同:更新槽位,实现增补和修正
7. 用户信息持久化实现方案
7.1 核心需求
不同用户多轮对话的槽位信息搜集进度需要被记录,防止信息混淆或丢失。
7.2 存储策略
- 持久化层:SQLite(aiosqlite),将会话槽位落盘到本地数据库文件(位于项目目录下,无需单独部署数据库服务)。
7.3 槽位填充表结构
CREATE TABLE IF NOT EXISTS slot_info_table (
group_id TEXT PRIMARY KEY,
slots_info_dict TEXT, -- JSON 序列化的槽位字典
created_time TEXT NOT NULL,
type TEXT -- 业务类型,如"保险咨询"
);
配置见 config_parser.py:db_path(如 insurance_consult.db 或 data/insurance_consult.db)、slot_table_name(默认 slot_info_table)。首次连接时若表不存在会自动创建。
7.4 工作流程
- 用户开始咨询,系统生成唯一
group_id,插入空slot_dict。 - 每轮对话后,调用
update_SlotInfo更新slots_info_dict。 - 用户再次进入时,通过
group_id调用Read_SlotInfo恢复会话。
8. 主流程串联
8.1 策略编排核心要点(必读)
理解主流程前,先把下面几件事说清楚。
系统会按顺序逐话题问吗?会。 系统通过 get_next_message_by_slots 遍历当前的槽位填充情况(filled_slots),从投保人基本信息、家庭结构、需求与偏好、已有保障、健康告知、补充说明,一路往下查。查到哪个主题有没填的槽位,就先问那个主题,然后停掉,不会再往后找。所以问话是有顺序的。
顺序是否固定?固定。 六个主题的先后顺序由 slot_dict 和 sort_nested_dict 决定,不会变。永远是 1 → 2 → 3 → 4 → 5 → 6 这样走。
用户必须按顺序回答吗?不用。 用户随便说,比如一次说「我 35 岁,年收入 50 万,想买重疾和寿险」,LLM 会通过 Function Calling 把多个主题的槽位都抽出来。下一轮系统再从头遍历,找到第一个还缺槽位的主题,继续追问。所以用户可以跳着答,系统会自己跟上。
槽位是否填满由谁判断? 由 get_next_message_by_slots 判断,纯逻辑判断,看 value_elem == "" 就知道哪个槽位是空的,不靠 LLM 再确认一遍。
回复是否固定? 在走 Function Calling 分支的情况下,是的。总结用 FunctionCallSummaryConfig,追问用 main_question_schema(该主题全空时)或 params_schema(该主题部分已填时)拼话术,都是模板生成,不会二次调 LLM 来写回复。只有用户纯闲聊、没触发工具调用时,才会走 LLM 自由生成。
顺便提一下 main_question_schema:它不是被遍历的对象,而是一个「主题 → 标准问法」的查找表。当某主题一个槽位都没填时,就查这个表,拿出对应的整句来问;如果已经填了一部分,就只把还缺的那几个槽位名用 params_schema 拼起来问。
8.2 文字版流程
用户输入 → 读取历史槽位 → 首轮固定引导语/后续进入LLM
→ LLM流式输出
├── 普通回复(finish_reason=stop):直接流式返回
└── 工具调用(finish_reason=tool_calls)
→ 解析并执行工具 → 更新all_filled_slots → 写库
→ 调用get_next_message_by_slots
├── 有缺失 → 模板化反问
├── 无缺失且有下一主题 → 总结+开启下一主题
├── 全部完成 → 发送final_message_template
└── 无缺失且槽位未变 → 调用LLM自由对话
8.3 简化为核心路径
9. 方案优势总结
- 极致高效:一轮对话仅一次 LLM 调用,回复速度快,成本低。
- 流程可控:话术和流程由模板和策略函数驱动,避免 LLM 自由发挥。
- 信息完整:缺失槽位递归查找,确保必要信息都被收集。
- 开发简单:核心是定义槽位、模板和查找函数,代码量小,维护成本低。
- 可靠稳定:槽位持久化,保证多轮对话连续性和一致性。
- 可扩展:可增加提示词抽取、正则词槽抽取等补漏机制,提高信息抽取稳定性。
10. 典型对话示例
10.1 示例一:首轮对话(固定引导语)
用户:你好
机器人:欢迎咨询保险服务,我是您的智能顾问。请问您的年龄、职业和大概年收入区间?
10.2 示例二:用户一次回答多主题(跨主题填槽)
用户:我35岁,年收入50万,想买重疾和寿险
机器人:收到反馈:年龄、职业、年收入区间、险种意向等已记录;并继续追问当前主题下缺失的槽位(如职业)或下一主题(家庭结构、保额预算等)。
10.3 示例三:信息全部搜集完成后的结束语
用户:没有了,就这些
机器人:我们已清楚您的需求:年龄:xx 职业:xx … 正在为您匹配方案,请稍候……
10.4 示例四:未触发 Function Calling(自由对话)
用户:重疾险和寿险有什么区别?
机器人:由 LLM 直接生成回答,不走槽位与模板。
更多推荐


所有评论(0)