当前位置: 首页 > news >正文

20年老程序员×AI:2小时搭建社保智能客服系统实战

20年老程序员×AI:2小时搭建社保智能客服系统实战

一、背景

去年用 Python 现学现卖做了一个社保知识 RAG 问答系统——用 Milvus 向量库 + Ollama(BGE-M3) + DeepSeek,用户问政策,系统从知识库里找最像的问题喂给大模型回答。

跑了一段时间发现不对——用户不只是想"问问题",他们想办事:查养老金发了多少、转移社保关系、开参保证明、异地就医备案……这就需要从"单轮 QA"变成"有状态的多轮 Agent"。

下面是我和 AI 一起从头搭建这个 Agent 系统的完整过程。先说结论:一个人,2-3 小时,12 个业务类型,后端+前端+语音+HTTPS 全栈。传统开发哪个 10 人团队不得干两周?


二、核心对话实录

回合1:桩/真分层——开发效率的第一刀

:后端接口现在先全部用桩模块实现……LLM、Milvus、OCR 真实,人脸服务、业务 API 桩模拟。

这个需求背后的经验判断:如果全真,卡在联调上一个服务不通整体跑不起来;如果全桩,验证不了核心链路。真/假分层后,LLM 的 Function Calling、向量检索、OCR 身份证识别走真实服务,验证核心能力;人脸和业务 API 走桩返回模拟数据,保证流程能从头跑到尾。

AI 改了 2 个文件:face_handler.py去掉 requests 调外部接口,固定返回通过;api_caller.py根据 business_id 返回对应的模拟 JSON。

回合2:字段校验失败跳 QA

: 43103 输入后没有继续要身份证号,跳去回答问题了。

这是一个典型的边界条件 AI 自己想不到。用户办异地就医备案,我输入43103(不完整的身份证号),系统回答"您好,社会保障号码就是您的身份证号码……"——把残缺输入当成了提问。

原因是_handle_collecting里的逻辑:LLM 把43103识别为answer_question,校验失败后优先走了 QA 路径。修法很简单:校验失败一律返回纠错提示,不跳 QA

# 去掉这个分支:# if intent == "answer_question":# return self._handle_qa(...)# 验证失败始终:return {"type": "text", "content": validated_value, "next_action": "collecting"}

回合3:"办理"没有上下文

: 系统推荐了"💡 您可能需要办理【异地就医备案】,回复’办理’即可开始",用户回"办理",系统却又让重新说办什么。

用户说"办理"两个字,LLM 不知道办什么——它没看见上一轮的推荐。修法:给 IntentEngine 的 LLM 调用注入最少最近 6 条对话历史作为上下文。

def_build_messages(self,system_content,user_text,session):messages=[{"role":"system","content":system_content}]history=session.get("history",[])ifhistory:forhinhistory[-6:]:messages.append({"role":h.get("role"),"content":h.get("content","")[:300]})messages.append({"role":"user","content":user_text})returnmessages

加系统指令:“如果用户说办理、好的、可以等简短确认词,参考对话历史中最近一次推荐的业务来判定。”

效果:有历史→match_business,无历史→answer_question。

回合4:JSON 转卡片

:前端用户直接看到 JSON,太不友好了。

业务 API(桩)返回的 JSON 被原样展示为代码块。加了一个renderJsonBlocks()函数,解析<pre><code class="language-json">块,转成 key-value 表格卡片。配了 40+ 英译中字段映射表:

varlabelMap={'id_card':'身份证号','monthly_pension':'月养老金','application_id':'申请编号','direct_settlement':'直接结算',...};

嵌套 object 不再显示[object Object],转成 JSON 字符串展示。

回合5:聊天历史丢了

:前端聊天记录历史丢了。

原来是每条新消息直接覆盖#resultdiv。改成追加模式——用户消息蓝色气泡右对齐,AI 回复白色气泡左对齐 + 头像,chat-history容器自动滚底。

回合6-9:语音按钮——4 次迭代踩坑

这是与讯飞 IAT SDK 集成时踩的坑,也是整个开发过程里调最久的部分。

第一版需求:按住不放开始录音,松开提交文本,10 秒无语音自动放弃。

坑1:点了没反应。原因:btn.onclick = null之后才去读origClick,永远是 null → 先把原始点击保存了再清空。

坑2:一直"连接中…“,不到"录音中…”。原因:window.iatWS不存在——index.js里是let iatWS,不在 window 上 → 改typeof iatWS !== 'undefined'直接引用。

坑3:松开后文字为空。原因:松开→立即关 WebSocket,截断了正在回来的识别结果 → 改成松开只停录音,等 WebSocket 收到status=2自然关闭后延迟 300ms 再取文字。

坑4:同事反馈——“要考虑建立通道的时间,不能让客户白说” → 分两段:按住→"连接中…"→WebSocket 就绪→"录音中…"→现在可以说话了。

// 最终流程btn.innerText='连接中...';// 按下的瞬间// checkReady 轮询 iatWS.readyState === 1btn.innerText='录音中...';// 就绪后// 说话...// 松开 → '处理中...' → 等 close → 取文字 → 提交

坑5:语音识别结果自带句号。讯飞识别结果 “北京市。” 被^\d{17}[\dXx]$校验拦下 → 加rstrip('\u3002...')去掉中文标点。顺便加了中文日期智能解析:“026年3月5号” →2026-03-05

回合10:并发安全

:要支持并发,多个客户的上下文不能混在一起。

Redis key 天然按agent:session:{user_id}隔离。FlowController 单例加双重检查锁:

_flow_lock=threading.Lock()def_get_flow():globalflowifflowisNone:with_flow_lock:ifflowisNone:flow=FlowController(...)returnflow

SessionStore 加了 Redis 不可用时的内存回退,不依赖特定环境。

回合11:RAG 老说"根据上下文"

:回答问题里面出现了根据上下文。

Prompt 调了一轮:从"不要提示根据提供的上下文"改成"严禁说根据上下文、参考以上信息、根据提供的内容之类的话,要像自己本来就知道一样直接回答"。顺手修了一个 bug——for 循环里的q变量名覆盖了用户原始问题。

回合12:可配置化——业务的灵魂

:业务太少,还要有参保登记、个人信息修改、异地就医备案等——可配置化。

核心设计:业务规则不进代码,只改 YAML。字段定义、校验规则、API 映射全部配置化:

-id:"remote_medical"name:"异地就医备案"type:"handle"fields:-key:"medical_city"label:"就医城市"validate:"^[\\u4e00-\\u9fa5]{2,10}(?:市|区|县)?$"-key:"hospital"label:"就医医院"...require_confirm:true

config.yaml 里新增一个业务,api_caller.py加一段桩数据,前端bizNameMap补一行——不写任何业务逻辑代码。从 5 个业务扩到 12 个,纯加配置。

最终 12 个业务:养老金发放查询、缴费记录查询、养老金资格认证、社保关系转移、参保证明开具、参保登记、个人信息修改、异地就医备案、社保卡申领、医疗费用报销、失业保险金申领、生育保险申领。

回合13:身份证后置

:一个销售同事提的——身份证号是敏感信息,应该最后再要求客户输入,容易获取信赖。我考虑后觉得合理。

12 个业务类型,凡是含id_number字段的,全部调到最后一个。用户先回答城市、医院、日期这些低敏感信息,建立信任感之后,最后才要身份证号。config.yaml 里重新排序即可,代码不动。

回合14:手机端语音需要 HTTPS

:要有 HTTPS,要不手机端语音功能用不了——getUserMedia在 HTTP 下被禁止。

OpenSSL 生成自签名证书,启动代码加双模式:

# 默认 HTTPS 443(手机端可用语音)# $env:FLASK_HTTP=1 → HTTP 5000(本地调试)ifos.environ.get("FLASK_HTTP")=="1":app.run(host='0.0.0.0',port=5000,debug=True)else:app.run(host='0.0.0.0',port=443,ssl_context=(cert_path,key_path),debug=True)

三、关于 AI 写代码这件事

知道好的代码长什么样,让 AI 写。判断力比编码力更稀缺。

创造从"想要什么"开始,不是从"会什么"开始。AI 把"去找什么"的时间从 3 天压到 3 分钟而已。


四、技术栈

技术
LLMDeepSeek API (deepseek-chat)
向量化Ollama (bge-m3)
向量数据库Milvus
会话存储Redis (30min TTL, 内存回退)
意图识别Function Calling + 对话历史上下文 + 业务推荐
前端Flask + jQuery + AmazeUI + 讯飞 IAT/TTS + marked.js
HTTPSFlask SSL + OpenSSL 自签名证书

人脸服务和业务 API 当前用桩实现,联调时替换即可。


五、结语

有人说 AI coding 提升效率 50%,有人说 10%。看看这个项目的实际数据:传统开发 11-17 天,实际 2-3 小时——效率提升至少 80%。原因有两个:

  1. 项目类型恰好是 AI 最强项:样板代码、配置驱动、CRUD、前端适配
  2. 经验差决定了 AI 的放大倍数:初级程序员不知道该做什么、不该做什么、哪里会出问题。经验让 AI 从"能跑的代码生成器"变成"精准的工具"
http://www.jsqmd.com/news/781638/

相关文章:

  • 如何5分钟上手XUnity Auto Translator:Unity游戏实时翻译终极指南
  • 2026国内专业的环保pp管批发厂家排行 - 品牌排行榜
  • Sorcerer:AI应用开发的模块化工具箱,快速构建生产级智能系统
  • 深度学习图像数据集目录设计与Keras数据生成器实践
  • TMS320C645x DSP EMAC模块性能调优与实战解析
  • ts快速入门
  • 三维空间的刚体运动【小白学视觉SLAM(一)】
  • OpenClaw开源抓取框架应用实践:从模块化设计到工业自动化落地
  • Qwen3-4B-Thinking入门必看:Gemini 2.5 Flash蒸馏模型本地化部署详解
  • 程序合成技术与LLM结合的实践与优化
  • 别再只会用Base64了!手把手教你用Python魔改码表,打造专属加密工具
  • 张量基础与NumPy操作全解析
  • 第三章 集群的大脑 — Monitor
  • 基于Kotlin/JVM的轻量级负载均衡器nekot:动态服务发现与容器化部署实践
  • 哪种编程语言又快又省电?有人对比了27种语言
  • 数据科学能力模型:管理者视角与分析师成长路径
  • 亿坊·商城系统|多用户+多终端+多模式+多门店,源码交付!
  • Phi-3.5-mini-instruct惊艳效果:中文数学应用题解题思路生成,步骤清晰
  • TMS320F28P550SJ9实战解析:CPUTimer精准定时与中断服务设计
  • 随机森林在179个分类器中的大规模基准测试研究
  • LangChain框架解析:从RAG应用到智能体开发的完整指南
  • Momenta后端开发面试题精选:10道高频考题+答案解析(数据产线方向)
  • Gemma-4-26B-A4B-it-GGUF保姆级教程:webui.py路径修改+多量化版本切换实操
  • Qwen3.5-35B-A3B-AWQ-4bit参数详解:tensor-parallel-size/上下文长度/精度设置
  • OpenClaw Swarm:AI代理网关集群的统一监控与管理平台
  • 工业级嵌入式设计:MYC-JX8MX CPU模块解析与应用
  • ChatGPT自定义指令:从提示工程到高效AI协作的系统化方法
  • 如何快速配置XUnity.AutoTranslator:3个简单步骤完成游戏本地化
  • 好用的高温箱式马弗炉有哪些? - mypinpai
  • cv_unet_image-colorization GPU算力适配教程:Ampere架构显卡FP16加速推理实测