食安智能问答助手(tcm-knowledge-graph)项目面试题。点击折叠区查看答案。
三、食安智能问答助手(tcm-knowledge-graph)
1:项目的 Neo4j 实体索引解决了什么问题?
查看答案
2:neo4j 的存续形式、怎么加速查询效率?
查看答案
3:为什么采用精确匹配加纯向量检索?
查看答案
- 关键词:为了精确匹配
- 向量:实现语义对齐
两级检索,如果关键词匹配到之后,就没必要消耗语义查询的性能了。
4:语义改写、监管意图识别和候选实体抽取,合并怎么实现的?
查看答案
5:为什么需要问题改写?
查看答案
6:意图识别错了怎么办?
查看答案
首先要控制不要错,识别错了,要做好校验,做好多层拦截;
- 结构化约束:JSON Schema 强制输出,格式错直接拦截,不会把非法意图放进流程
- 白名单校验:意图限定白名单,非法值走规则兜底(fallback 意图)
- 低置信兜底:识别不稳时触发澄清反问,让用户换种说法,不带错误意图去生成 Cypher
7:循环纠错详细讲讲。
查看答案
8:多轮对话的历史记忆机制是怎么设计的?
查看答案
MySQL全量持久化,Redis滑动窗口只缓存最近20条,过期就从MySQL重建;取5轮对话注入LLM做指代改写和生成上下文,Redis故障自动降级直连MySQL,不影响主流程。
取5轮同时也是解决上下文过长,如果想压缩也可以调用轻量LLM压缩
9:会话隔离怎么实现?
查看答案
每个会话用 user_id 加 session_id 拼成唯一键,Redis 里的对话记忆按键隔离,互不串扰;查历史时再校验会话归属,防止越权访问。
thread_id = f"{user_id}:{session_id}"(服务端生成 uuid)。
10:流式输出是怎么实现的?
查看答案
基于 SSE + asyncio.Queue 实现的。每个请求创建一个独立的异步队列,大模型侧开启流式推理,将 token 片段和进度信息逐条写入队列;FastAPI 端通过队列循环读取、逐条 yield 返回,最后(由 StreamingResponse )以 SSE 格式(text/event-stream)推送给前端。
扩展详解
我们基于 SSE 实现:FastAPI 接口返回 StreamingResponse,媒体类型为 text/event-stream。每个请求创建独立的 asyncio.Queue,后台任务执行 LangGraph 工作流,各节点通过 call_soon_threadsafe 把进度事件安全推入队列,接口逐条 yield 下发;前端以 stream=True 逐行读取并解析,实时渲染进度和最终答案。
支撑细节(追问时用):
-
事件类型分
msg/timing/reply/error/done,done通知前端结束 -
设置
Cache-Control: no-cache、X-Accel-Buffering: no防止代理缓冲导致"假流式" -
call_soon_threadsafe解决同步工作流节点跨线程写队列的线程安全问题
SSE 与 WebSocket 的区别:
-
SSE:单向推送,基于 HTTP,简单、自动重连,最适合 AI
-
WebSocket:双向通信,适合高频交互,但实现复杂(需自处理心跳/重连)
结论:仅需 AI 单向回复时,首选 SSE。
11:如何抑制幻觉,要求模型只依据查询结果回答?
查看答案
保证输入质量,提示词做好约束,还是不行需要进行对应的微调
- 源头:Cypher 只读校验 + EXPLAIN 验证,保证喂给模型的数据本身是对的
- 约束:Prompt 立死规矩——只准基于查询返回的数据组织答案,查无数据必须明确说"未查到",禁止编造
- 微调:靠领域微调强化格式遵从和拒答行为,只靠 Prompt 约束作用有限
我在提示词里强约束:只准基于检索到的内容回答,禁止用模型自有知识补充;查不到就答“信息不足”转人工,不硬编,从源头防幻觉。
12:如何防止提示词注入?
查看答案
NL2Cypher 场景天然要更严:
- 只读白名单:模型生成的 Cypher 先校验,拦截 INSERT/UPDATE/DELETE 等写操作
- 参数化查询:用户输入全部以参数注入,不拼接进语句
- 编译验证:Neo4j EXPLAIN 预检,非法语句不执行
- 权限服务端下发:辖区权限值由服务端注入,不信任前端传参
13:GraphRAG 怎么测评?
查看答案
这个测试不像普通 RAG 用 RAGAS 就可以全面评估,好多需要自己写脚本测:
- 比较 Cypher 语句语法对不对,意图准不准
- 需要写脚本通过 explain 执行测试
14:讲讲你的双 LoRA 微调方案?为什么是"双"LoRA?
查看答案
Qwen3-8B 基座训了两个独立 LoRA:意图理解(输出 JSON)和 NL2Cypher(4.9 万条,语法通过率 64%→94.6%)。分两个是因为任务输出空间差异大,合并训练互相干扰,双适配器可独立迭代。
vLLM 单服务挂载两个适配器,按请求 model 字段路由;对外 OpenAI 兼容 API,业务侧同一个 url 不同模型名称。
15:微调训练数据怎么构造的?
查看答案
写脚本用大模型批量生成 NL2Cypher 样本,然后语法检测(explain 通过才保留)→ 大模型语义检测 → 部分人工抽检,人工再提供部分真实数据混入。
合成数据 epoch 控制在 1.2,防过拟合。