Langchain-Chatchat如何实现知识热度分析?用户查询行为洞察

在企业内部知识系统日益复杂的今天,一个常见的场景是:HR部门反复收到“年假怎么申请”的提问,IT支持群中不断出现“打印机连不上”的求助,而新员工入职一周后仍对报销流程一头雾水。这些问题的背后,并非员工不够主动,而是知识的供给与需求之间存在明显的错配。

传统的文档管理系统往往止步于“存”和“搜”,却无法回答更深层的问题:哪些知识被频繁查阅?哪些问题始终得不到解答?不同岗位的用户关注点有何差异?正是这些缺失的洞察,让企业的知识资产停留在静态档案阶段,难以真正赋能组织。

而随着大模型技术的发展,以 Langchain-Chatchat 为代表的本地化知识库系统,正逐步打破这一僵局。它不仅能让私有文档“开口说话”,更通过可扩展的日志机制,为知识热度分析与用户行为洞察打开了通路——让系统不仅能答,还能“思考”:大家在关心什么?哪里还存在盲区?


Langchain-Chatchat 的核心价值,在于它构建了一个安全、可控且语义智能的问答闭环。所有文档处理均在本地完成,支持 PDF、Word 等多种格式输入,利用中文优化的嵌入模型(如 BGE)将文本转化为向量,存储至 FAISS 或 Chroma 等向量数据库。当用户提问时,系统先进行语义检索,找出最相关的文档片段,再交由本地部署的 LLM(如 ChatGLM、Qwen)生成自然语言回答。

这个过程看似只是“一问一答”,实则每一步都留下了可供挖掘的数据痕迹。例如:

from langchain_community.document_loaders import UnstructuredFileLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS

# 加载并切分文档
loader = UnstructuredFileLoader("knowledge_base/hr_policy.docx")
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
split_docs = text_splitter.split_documents(docs)

# 向量化入库
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = FAISS.from_documents(split_docs, embedding=embeddings)

# 检索示例
query = "产假是多久?"
retrieved = vectorstore.similarity_search(query, k=3)
for r in retrieved:
    print(r.page_content)

这段代码不仅是知识入库的标准流程,更是后续行为分析的数据基石。每一次 similarity_search 调用,都会产生一条包含原始问题、匹配结果、时间戳等信息的日志记录。正是这些日志,构成了知识热度分析的原材料。

但原始日志本身并不直接揭示规律。比如,“怎么请病假?”“生病了能休几天?”“医疗期规定是什么?”这三个问题在字面上完全不同,但在语义上高度相关。如果仅按字符串统计频次,会严重低估这类主题的真实热度。

这就引出了关键一步:语义聚类。我们不再依赖关键词匹配,而是将问题转化为向量空间中的点,使用聚类算法自动发现潜在的主题簇。

import pandas as pd
from sentence_transformers import SentenceTransformer
from sklearn.cluster import DBSCAN

# 加载查询日志
logs = pd.read_csv("query_logs.csv")  # 包含 timestamp, user_id, query, doc_id, score

# 使用多语言 MiniLM 模型编码
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
embeddings = model.encode(logs['query'].tolist())

# DBSCAN 聚类(无需预设簇数量)
clustering = DBSCAN(eps=0.5, min_samples=2, metric='cosine').fit(embeddings)
logs['cluster_id'] = clustering.labels_

# 统计各簇热度
cluster_stats = logs.groupby('cluster_id').agg(
    count=('query', 'size'),
    success_rate=('doc_id', lambda x: (x.notna()).mean()),
    last_seen=('timestamp', 'max')
).reset_index()

# 排除噪音点(-1),输出热门主题
hot_topics = cluster_stats[cluster_stats['cluster_id'] != -1]
hot_topics = hot_topics.sort_values(by='count', ascending=False).head(10)

print("Top 10 Hot Knowledge Topics:")
for idx, row in hot_topics.iterrows():
    sample_query = logs[logs['cluster_id'] == row['cluster_id']].iloc[0]['query']
    print(f"#{idx+1} '{sample_query}' [Count: {row['count']}, Success: {row['success_rate']:.2f}]")

这套方法的优势在于“抗表述多样性”。无论用户用口语、书面语还是缩写提问,只要语义相近,就能归入同一类别。更重要的是,那些“未命中”的问题也会被纳入统计——这恰恰是识别知识盲区的关键信号。例如,某个关于“远程办公审批流程”的问题反复出现但从未返回有效答案,系统就会标记该主题为“高需求低覆盖”,提示管理员补充相关内容。

然而,仅仅知道“什么问题热”还不够。我们还需要理解“谁在问”“怎么问”以及“问后如何”。这就进入了用户查询行为洞察的范畴。

设想一位新入职的销售代表,在第一天密集查询了“客户签约流程”“合同模板下载”“提成计算方式”等问题;而另一位财务人员则在月底集中搜索“发票报销截止时间”“差旅标准调整”等条目。这些模式背后,隐藏着角色特征与业务周期的强关联。

为了捕捉这种动态,我们需要对用户行为进行会话级建模。即将分散的查询按时间窗口聚合为“会话”,分析其持续时间、提问密度、失败率等指标,判断用户是否遇到障碍。

from datetime import datetime, timedelta

def analyze_user_session(logs, user_id, session_window_minutes=30):
    user_logs = logs[logs['user_id'] == user_id].sort_values('timestamp')
    user_logs['timestamp'] = pd.to_datetime(user_logs['timestamp'])

    sessions = []
    current_session = []

    for _, row in user_logs.iterrows():
        if not current_session:
            current_session.append(row)
        else:
            last_time = current_session[-1]['timestamp']
            current_time = row['timestamp']
            if (current_time - last_time) <= timedelta(minutes=session_window_minutes):
                current_session.append(row)
            else:
                sessions.append(current_session)
                current_session = [row]

    if current_session:
        sessions.append(current_session)

    insights = []
    for sess in sessions:
        queries = [q['query'] for q in sess]
        no_answer_count = sum(1 for q in sess if pd.isna(q['doc_id']))
        insight = {
            'start': sess[0]['timestamp'],
            'end': sess[-1]['timestamp'],
            'duration': (sess[-1]['timestamp'] - sess[0]['timestamp']).seconds,
            'query_count': len(queries),
            'no_answer_rate': no_answer_count / len(queries),
            'topics': list(set(extract_topic(q) for q in queries))
        }
        insights.append(insight)

    return insights

def extract_topic(query):
    keywords = ['报销', '请假', '合同', '绩效', '电脑']
    for kw in keywords:
        if kw in query:
            return kw
    return '其他'

在这个函数中,我们将超过30分钟无交互的查询视为新会话的开始。通过分析每次会话的“无答案率”和持续时间,我们可以识别出两类典型场景:一是用户快速获得解答(短时、低失败率),二是长时间反复尝试(长时、高失败率)。后者往往是知识缺口或界面引导不足的表现,值得重点关注。

结合角色标签后,这种分析还能进一步深化。例如:

  • 对比研发与市场团队的知识偏好;
  • 观察新员工在入职第1周 vs 第4周的查询变化;
  • 监测政策发布后相关关键词的搜索量波动。

这些洞察可以直接驱动运营动作:为新人生成《首月必读清单》,在季度初推送绩效考核指南,甚至在某类问题激增时自动触发告警,通知责任人更新文档。

整个系统的架构也因此呈现出清晰的分层结构:

+------------------+     +---------------------+
|   用户终端       |<--->|   Web 前端界面       |
| (PC/移动端)      |     | (React/Vue)         |
+------------------+     +----------+----------+
                                    |
                                    v
                           +--------+---------+
                           |   后端服务层       |
                           | (FastAPI/Flask)    |
                           +--------+---------+
                                    |
        +---------------------------v----------------------------+
        |                        核心引擎                        |
        |--------------------------------------------------------|
        | • 文档解析模块 → Text Splitter                         |
        | • 向量嵌入模块 → HuggingFace Embeddings                |
        | • 向量数据库 → FAISS / Chroma                          |
        | • LLM 推理模块 → Local LLM (e.g., Qwen, ChatGLM)       |
        | • 日志记录模块 → Query Logging & Behavior Tracking     |
        +--------------------------------------------------------+
                                    |
                                    v
                     +--------------+---------------+
                     |   数据持久化层               |
                     | • 知识文档目录               |
                     | • 向量索引文件               |
                     | • 查询日志数据库(SQLite/MySQL)|
                     +------------------------------+

其中,知识热度分析模块通常以外挂形式存在,通过定时任务拉取日志数据,执行聚类与统计,最终输出可视化报表或 API 接口供管理后台调用。这种方式既避免了实时计算对主线程的压力,又保证了分析结果的时效性。

在实际落地过程中,有几个工程细节尤为关键:

  • 日志粒度要平衡:太粗无法支撑深度分析,太细则影响性能。建议至少记录用户ID(匿名化处理)、时间、问题文本、命中文档ID、检索得分、回答状态。
  • 隐私保护不可忽视:用户身份应做哈希脱敏,敏感字段如部门、职级需权限控制。
  • 资源开销需控制:语义聚类计算成本较高,建议异步执行,配合缓存机制提升效率。
  • 结果需可解释:热度榜单应附带代表性问题样本,便于人工验证聚类准确性。
  • 形成闭环迭代:将分析结果反哺到知识库更新流程中,实现“发现问题→补充内容→验证效果”的持续优化。

从技术角度看,Langchain-Chatchat 的真正突破,不在于它能调用大模型生成流畅回答,而在于其模块化设计为上层数据分析提供了可能性。它没有把功能封闭在“问答”之内,而是通过开放接口和标准化日志,允许企业根据自身需求定制洞察能力。

这也意味着,它的价值边界远不止于 HR 政策查询或 IT 故障排查。在科研机构,它可以追踪研究人员对特定技术文献的关注趋势;在制造企业,它能识别一线工人对操作规程的理解难点;在教育领域,它甚至可用于评估学生对知识点的掌握情况。

当知识系统不仅能回答问题,还能主动感知需求、预警风险、推荐内容时,它就不再是一个被动工具,而成为组织智慧的一部分。这种从“响应式”到“预见式”的转变,正是智能化演进的核心方向。

Langchain-Chatchat 所提供的,正是一条切实可行的技术路径:从一次简单的问答出发,积累数据,提炼洞察,最终让知识库具备自我进化的能力。而这,或许才是企业数字化转型中最值得关注的“软基建”。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐