Langchain-Chatchat 如何导出问答记录?审计日志功能实现
在企业级智能问答系统日益普及的今天,一个常被忽视但至关重要的问题浮出水面:我们能相信 AI 给出的答案吗?如果答案有误,又该如何追溯它的来源和决策过程?
尤其是在金融、医疗、法律等对合规性要求极高的行业,仅仅“答得快”已经远远不够。监管机构需要看到操作痕迹,管理层希望掌握知识使用情况,技术人员则要定位模型盲区——这一切的前提是:每一次问答都必须可记录、可回溯、可导出。
而基于 LangChain 与本地大模型构建的开源项目Langchain-Chatchat,正是为这一需求量身打造的解决方案。它不仅支持私有化部署、文档本地解析,更关键的是,其开放架构允许开发者深度定制审计日志功能,真正实现“智能”与“可控”的统一。
回调机制:藏在 LangChain 里的“监听器”
很多人以为日志记录必须侵入核心逻辑,其实不然。LangChain 框架早在设计之初就考虑到了监控与审计的需求,提供了一套强大的回调系统(Callbacks)——你可以把它理解为遍布整个执行链路的“探针”。
当你调用一个RetrievalQA链时,LangChain 允许你在每个步骤插入自定义行为:比如开始处理问题时打个标记,检索到文档时记下内容,生成答案后捕获输出。这些事件都可以被捕获并转发到日志系统中。
from langchain.callbacks import StdOutCallbackHandler from langchain.chains import RetrievalQA handler = StdOutCallbackHandler() qa_chain = RetrievalQA.from_chain_type( llm=your_llm, retriever=vectorstore.as_retriever(), callbacks=[handler] # 注册监听 ) qa_chain.run("公司差旅报销标准是多少?")这段代码不会改变任何业务逻辑,但它会在控制台输出完整的执行轨迹:从问题接收、文档检索到最终回答。如果你把StdOutCallbackHandler替换为自己写的处理器,就可以轻松将这些信息写入文件或数据库。
更重要的是,这种机制是非侵入式的。你不需要修改原有链的结构,只需通过配置注入监听逻辑,非常适合后期追加审计能力。
不过要注意一点:虽然名字叫get_openai_callback,这类工具也能用于本地模型,只是统计的可能是模拟 token 数或完全自定义字段。别被命名迷惑了——它的本质是一个通用事件收集器。
在 Chatchat 中动手埋点:从接口层面捕获完整上下文
Langchain-Chatchat 的一大优势在于,它的 API 层足够清晰且易于扩展。以api.py中的/chat接口为例,这里是所有用户提问进入系统的入口,自然也是最适合植入日志逻辑的位置。
设想这样一个场景:HR 部门想分析员工最常咨询的政策类问题。如果没有日志,他们只能靠猜测去优化知识库;但如果我们在每次问答后自动保存一条结构化记录呢?
import json import datetime from fastapi import APIRouter, Body router = APIRouter() def log_query(question: str, response: str, sources: list, user_id: str = "anonymous"): entry = { "timestamp": datetime.datetime.now().isoformat(), "user_id": user_id, "session_id": generate_session_id(), # 可选 "question": question.strip(), "answer": response.strip(), "sources": [src.metadata.get("source", "unknown") for src in sources], "model": "chatglm3-6b", "token_count": estimate_tokens(question + response) } # 使用 JSONL 格式追加写入,高效且易解析 with open("logs/audit.log", "a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n") @router.post("/chat") async def chat_endpoint(data: ChatRequest): try: answer_text, reference_docs = await generate_answer(data.question) # 关键一步:在返回前记录完整上下文 log_query(data.question, answer_text, reference_docs, user_id=data.user_id) return {"answer": answer_text, "references": reference_docs} except Exception as e: log_error(data.question, str(e), user_id=data.user_id) return {"error": "内部错误,请联系管理员"}这个简单的函数带来了巨大价值:
- 每条日志包含时间戳、用户标识、原始问题、AI 回答、引用来源;
- 来源字段保留了文档元数据(如 PDF 文件名、Word 文档路径),形成证据闭环;
- 采用
.jsonl(每行一个 JSON 对象)格式存储,便于后续批量导入 Elasticsearch、Pandas 或数据库进行分析。
而且你会发现,这并不影响主流程性能——除非你把写入做成同步阻塞操作。
向量检索不只是“找答案”,更是“留证据”
很多人把向量数据库当成单纯的检索工具,但在审计视角下,它是构建可信问答的关键一环。
传统 LLM 容易“幻觉”作答,因为它没有明确的知识边界。而 RAG 架构通过强制模型参考特定文档片段来生成答案,相当于给每句话加上了“引注”。这就让溯源成为可能。
举个例子:
retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) docs = retriever.invoke("年假可以跨年使用吗?") for doc in docs: print(f"【来源】{doc.metadata['source']} (页码: {doc.metadata.get('page', '?')})") print(f"【内容】{doc.page_content[:120]}...\n")输出可能是:
【来源】employee_handbook_v3.pdf (页码: 45) 【内容】年休假原则上应在当年休完,特殊情况经部门主管批准后可延至次年3月底前使用...这些信息不仅可以展示给用户增强信任感,更要存入审计日志。一旦未来出现争议(例如某员工声称从未被告知政策),你可以直接调出当时的问答记录,并指出:“答案依据来自《员工手册》第45页”。
这才是真正意义上的“可解释 AI”。
实际部署中的工程权衡:别让日志拖垮系统
理想很丰满,现实却需要权衡。我在多个客户现场见过因日志设计不当导致服务变慢甚至崩溃的情况。以下是几个实战建议:
✅ 推荐做法
| 场景 | 建议方案 |
|---|---|
| 小团队试用 | 直接写audit.log文件,每天轮转一次 |
| 中型企业 | 写入 PostgreSQL 表,建索引加速查询 |
| 多实例集群 | 使用 Redis Queue 异步投递日志,后端 Worker 消费入库 |
| 需要可视化 | 接入 ELK 或 Grafana + Loki,做实时看板 |
异步写入尤其重要。不要让你的日志逻辑卡住主响应流程。可以用 Python 的concurrent.futures或 Celery 轻松实现:
from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor(max_workers=3) def async_log(*args, **kwargs): executor.submit(log_query, *args, **kwargs) # 调用时不等待 async_log(question, answer, sources, user_id)这样即使磁盘 I/O 暂时拥堵,也不会影响用户体验。
⚠️ 易踩坑点
- 忘记挂载卷:Docker 容器里写的日志,默认重启就没了。务必用
-v ./logs:/app/logs挂载宿主机目录。 - 无脱敏直接导出:日志里可能含有身份证号、工号、薪资等敏感信息。导出前应启用正则替换或 NLP 实体识别做掩码处理。
- 无限增长:设置日志保留策略,比如只保留最近 180 天,超出自动归档或删除。
- 字段缺失:确保每条日志都有
timestamp,user_id,question,sources等关键字段,否则后期分析寸步难行。
日志不只是“备查”,更是“进化”的燃料
很多团队把审计日志当作应付检查的负担,但我更愿意把它看作系统的“记忆中枢”。
想象一下,如果你能定期跑一段脚本,自动分析过去一周的问答日志:
import pandas as pd df = pd.read_json("logs/audit.log", lines=True) top_questions = df["question"].value_counts().head(10) print("高频问题 Top 10:") print(top_questions)结果发现“公积金提取流程”被问了 87 次,远超其他问题。这意味着什么?说明这个政策宣传不到位,或者现有文档太难找。于是你可以:
- 主动推送相关指南给全体员工;
- 把该文档置顶或增加关键词索引;
- 在前端添加快捷入口,减少重复提问。
这不就是 AI 驱动的知识管理闭环吗?
再进一步,结合 NLP 技术对问题聚类,识别出“薪酬类”、“假期类”、“IT 支持类”等主题,还能生成知识热度图谱,指导企业持续优化知识资产。
结语:让每一次问答都留下数字足迹
Langchain-Chatchat 的魅力,不仅在于它能让本地模型读懂你的私有文档,更在于它赋予了开发者足够的自由度去掌控系统的每一个细节。
通过合理利用 LangChain 的回调机制,在 Chatchat 的 API 层插入轻量级日志逻辑,再借助向量检索提供的上下文溯源能力,我们可以轻松构建一套完整、可靠、可扩展的审计日志体系。
这套体系不只是为了满足合规要求,更是为了让 AI 的每一次输出都变得透明、可信、可持续改进。
未来的智能系统,不能只是“聪明”,更要“负责任”。而从今天起,就在你的问答系统里种下第一行日志代码吧——因为真正的智能化,始于可追溯。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考