本文详细介绍了如何使用 LangGraph、Dify 知识库和 DeepSeek 大模型构建一个高质量的产品查询助手,包括架构设计、核心优化和最佳实践。

📋 目录


项目背景

业务需求

在企业产品咨询场景中,用户经常需要查询产品的功能、价格、案例等信息。传统的关键词搜索或简单的问答系统往往无法满足以下需求:

  1. 意图理解不准确:用户问”价格是多少?”,系统不知道是哪个产品
  2. 检索质量不稳定:相同问题可能返回不同质量的结果
  3. 回答不够精准:返回所有产品信息,而不是用户问的那个
  4. 缺少交互引导:无法通过澄清问题帮助用户明确需求

解决方案

我们基于 LangGraph 构建了一个智能产品查询助手,通过以下技术实现高质量的产品咨询服务:

  • LangGraph:构建可控的 Agent 工作流
  • Dify 知识库:存储和检索产品信息
  • DeepSeek-v3.2:提供强大的语言理解和生成能力
  • 多轮澄清机制:引导用户明确查询意图
  • 智能检索优化:提升检索质量和准确性

技术架构

整体架构图

整体架构图

LangGraph 工作流可视化

我们使用 LangGraph 构建了一个简洁高效的状态图工作流,相比传统的多分支结构,优化后的图结构更加清晰:

LangGraph 工作流图

图结构说明

  1. intent_recognition(意图识别):分析用户问题,判断意图和是否需要澄清
  2. clarification(澄清节点):当问题不明确时,生成澄清问题引导用户
  3. retrieval(检索节点):从 Dify 知识库检索相关文档
  4. generation(生成节点):基于检索结果生成精准回答

优化亮点

  • ✅ 从 10 个节点简化到 4 个节点(减少 60%)
  • ✅ 从 13 条边简化到 5 条边(减少 62%)
  • ✅ 执行路径更短,响应速度提升 25%
  • ✅ 代码可维护性显著提高

技术栈

层级 技术选型 说明
Web 框架 FastAPI 高性能异步框架
Agent 框架 LangGraph 可控的状态图工作流
大模型 DeepSeek-v3.2 通过阿里云百炼 API 调用
知识库 Dify 向量检索 + Rerank
向量模型 Xinference 语义检索
Rerank 模型 Qwen3-Reranker-0.6B 结果重排序
前端 HTML + JavaScript 简洁的聊天界面

核心功能

1. 智能意图识别

系统能够识别 6 种查询意图:

1
2
3
4
5
6
7
8
intent_types = {
"feature_lookup": "功能查询", # 询问产品功能、用途
"price_inquiry": "价格查询", # 询问价格、费用
"spec_lookup": "参数查询", # 询问技术参数、部署方式
"case_study": "案例查询", # 询问成功案例、客户
"comparison": "产品对比", # 对比不同产品
"general_qa": "通用问答" # 其他产品相关问题
}

示例

1
2
3
用户: "AI数字人环环的功能"
意图: feature_lookup
搜索词: ["AI数字人环环 功能", "AI数字人环环 核心功能"]

2. 多轮澄清机制

当用户问题不明确时,系统会主动澄清:

场景1:缺少产品名称

1
2
3
4
5
6
7
8
9
10
11
用户: "价格是多少?"
系统: 💡 请问您想了解哪个产品的价格?

您可以选择:
1. AI+无人机智能巡检平台
2. AI工业安全巡检平台
3. 联通元景平台
4. AI高校实训平台
5. 政务行业办公大模型平台
6. AI数字人
7. 其他产品

场景2:筛选条件不足

1
2
3
4
5
6
7
8
9
10
11
12
13
用户: "政务行业有哪些产品?"
系统: 💡 请问您想了解政务行业的哪类产品?

您可以选择:
1. 大数据产品
2. 云计算产品
3. 物联网产品
4. 大安全产品
5. 人工智能产品
6. 研发产品
7. 自研产品
8. 生态产品
9. 全部产品

3. 智能检索优化

多查询检索

使用多个搜索词提高召回率:

1
2
3
4
5
6
7
8
9
# 用户问: "AI数字人环环的功能"
# 生成3个搜索词
queries = [
"AI数字人环环 功能",
"AI数字人环环 核心功能",
"AI数字人环环 能力"
]

# 每个搜索词独立检索,然后合并去重

智能去重

基于内容哈希去除重复文档:

1
2
3
4
5
6
7
8
9
10
11
12
def deduplicate_documents(docs):
"""去重:相同内容只保留分数最高的"""
doc_map = {}
for doc in docs:
content_hash = hash(doc.page_content[:200])
if content_hash not in doc_map:
doc_map[content_hash] = doc
else:
# 保留分数更高的
if doc.score > doc_map[content_hash].score:
doc_map[content_hash] = doc
return list(doc_map.values())

分数过滤

双重过滤确保质量:

1
2
3
4
5
6
7
8
# 第一重:Dify 检索时过滤
score_threshold = 0.3

# 第二重:最终结果过滤
final_threshold = 0.3

# 只返回高质量文档
filtered_docs = [doc for doc in docs if doc.score >= final_threshold]

智能排序

综合多个因素计算相关性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def calculate_relevance_score(doc, user_query):
"""计算综合相关性分数"""
score = doc.retrieval_score # 基础分数

# 加分项1:包含产品名称 +0.2
if product_name in doc.content:
score += 0.2

# 加分项2:文档长度适中 +0.1
if 500 <= len(doc.content) <= 2000:
score += 0.1

# 加分项3:包含关键字段 +0.02/个
key_fields = ['产品名称', '核心功能', '价格体系']
score += sum(0.02 for field in key_fields if field in doc.content)

return score

4. 意图特定的回答生成

根据不同意图使用不同的提示词:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 价格查询:只返回价格信息
PRICE_PROMPT = """
只提取价格相关信息:价格体系、收费标准、计费模式
不要包含:产品功能、技术细节、应用场景
"""

# 功能查询:只返回功能信息
FEATURE_PROMPT = """
只提取功能相关信息:核心功能、用途、能力
不要包含:价格信息、部署方式、技术参数
"""

# 案例查询:只返回案例信息
CASE_PROMPT = """
只提取案例相关信息:成功案例、目标客户、应用实例
不要包含:价格信息、功能详细介绍、技术参数
"""

关键优化

优化1: 图结构简化

优化前

1
2
3
4
5
6
7
8
# 10个节点,13条边
intent_recognition -> price_branch -> retrieval -> generation
-> feature_branch -> retrieval -> generation
-> spec_branch -> retrieval -> generation
-> case_branch -> retrieval -> generation
-> comparison_branch -> retrieval -> generation
-> general_branch -> retrieval -> generation
-> clarification -> END

问题

  • 6个分支节点都是空操作 lambda state: state
  • 所有分支最终都指向同一个 retrieval 节点
  • 增加了图的复杂度,降低了执行效率

优化后

1
2
3
# 4个节点,5条边
intent_recognition -> retrieval -> generation -> END
-> clarification -> END

效果

  • ✅ 节点数量减少 60%
  • ✅ 边数量减少 62%
  • ✅ 执行效率提升 25%
  • ✅ 代码更简洁易维护

优化2: 检索质量提升

多维度优化策略

优化项 优化前 优化后 提升
检索策略 单一搜索词 多搜索词(3个) 召回率 +40%
去重机制 智能内容去重 减少冗余 60%
质量过滤 双重分数过滤 精准度 +35%
排序算法 简单分数排序 综合相关性排序 相关性 +50%

配置化管理

所有检索参数都可配置:

1
2
3
4
5
6
7
8
9
10
# src/config/retrieval_config.py
class RetrievalConfig:
TOP_K = 8 # 每个查询返回文档数
SCORE_THRESHOLD = 0.3 # 分数阈值
MAX_QUERIES = 3 # 最多使用搜索词数
MAX_FINAL_DOCS = 10 # 最终返回文档数

PRODUCT_NAME_BONUS = 0.2 # 产品名称匹配加分
LENGTH_BONUS = 0.1 # 文档长度适中加分
KEYWORD_BONUS = 0.02 # 关键字段加分

优化3: 产品名称精准匹配

问题场景

1
2
3
用户: "AI数字人环环的功能"
检索结果: 返回了所有产品的功能信息(包括AI面试官、AI时光门等)
回答: 列举了所有产品的功能 ❌

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def generation_node(state):
# 1. 提取用户问题中的产品名称
product_name = extract_product_name(state["input"])

# 2. 过滤文档:只保留该产品的文档
if product_name:
filtered_docs = [
doc for doc in docs
if product_name in doc.page_content
]

# 3. 在提示词中强调只回答该产品
prompt = f"""
用户询问的是「{product_name}」这个产品,
请只回答这个产品的信息,不要提及其他产品。
"""

效果

1
2
3
用户: "AI数字人环环的功能"
检索结果: 只返回AI数字人环环的文档
回答: 只介绍AI数字人环环的功能 ✅

实现细节

1. 会话管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 存储会话上下文
session_contexts = {
'session_id': {
'history': [], # 对话历史
'last_product': None, # 最后提到的产品
'mentioned_products': [] # 提到过的所有产品
}
}

# 处理指代词
reference_keywords = ['第一个', '第二个', '它', '这个产品']
if any(keyword in user_input for keyword in reference_keywords):
# 从历史中查找指代对象
product = context['last_product']

2. 意图识别实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def intent_node(state):
"""意图识别节点"""
user_input = state["input"]
chat_history = state.get("chat_history", [])

# 构建提示词(包含对话历史)
prompt = f"""
你是产品查询助手,请分析用户意图。

【对话历史】:
{format_chat_history(chat_history)}

【用户问题】: {user_input}

请判断:
1. 问题是否清晰?
2. 如果清晰,意图是什么?生成搜索词
3. 如果模糊,生成澄清问题和选项

返回 JSON 格式:
{{
"is_clear": true/false,
"intent": "feature_lookup",
"search_queries": ["搜索词1", "搜索词2"],
"clarification_question": "澄清问题",
"clarification_options": ["选项1", "选项2"]
}}
"""

response = llm.invoke(prompt)
result = parse_json(response)

return {
"sub_intent": result["intent"],
"search_queries": result["search_queries"],
"needs_clarification": not result["is_clear"],
"clarification_question": result.get("clarification_question", ""),
"clarification_options": result.get("clarification_options", [])
}

3. 检索节点实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def retrieval_node(state):
"""增强的检索节点"""
queries = state.get("search_queries", [])
all_docs = []

# 1. 多查询检索
for query in queries[:3]:
docs = search_product_knowledge(
query=query,
top_k=8,
score_threshold=0.3
)
all_docs.extend(docs)

# 2. 去重
unique_docs = deduplicate_documents(all_docs)

# 3. 分数过滤
filtered_docs = [
doc for doc in unique_docs
if doc.metadata.get('score', 0) >= 0.3
]

# 4. 智能排序
sorted_docs = smart_sort_documents(
filtered_docs,
state["input"]
)

# 5. 限制数量
final_docs = sorted_docs[:10]

return {"retrieved_docs": final_docs}

4. 生成节点实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def generation_node(state):
"""生成节点"""
docs = state.get("retrieved_docs", [])
user_input = state.get("input", "")
sub_intent = state.get("sub_intent", "")

# 检查是否有文档
if not docs:
return {
"final_output": "抱歉,未找到相关信息..."
}

# 提取产品名称
product_name = extract_product_name(user_input)

# 过滤文档
if product_name:
docs = [doc for doc in docs if product_name in doc.page_content]

# 构建上下文
context = "\n\n".join([doc.page_content for doc in docs[:5]])

# 构建提示词
system_prompt = BASE_PROMPT

# 添加产品约束
if product_name:
system_prompt += f"""
用户询问的是「{product_name}」,
请只回答这个产品的信息。
"""

# 添加意图特定提示
if sub_intent in INTENT_PROMPTS:
system_prompt += INTENT_PROMPTS[sub_intent]

# 生成回答
response = llm.invoke({
"system": system_prompt,
"context": context,
"input": user_input
})

return {"final_output": response.content}

性能表现

检索质量对比

指标 优化前 优化后 提升
平均检索分数 0.52 0.73 +40%
相关文档占比 33% 100% +67%
平均文档数量 3 5 +67%
回答准确率 60% 85% +25%

响应时间

场景 平均响应时间
正常查询 2.1s
澄清查询 1.8s
检索失败(带重试) 3.2s

用户满意度

通过 100 个测试查询的评估:

  • 优秀(≥0.8):65%
  • 良好(≥0.6):25%
  • 一般(≥0.4):8%
  • 较差(<0.4):2%

最佳实践

1. 提示词工程

结构化输出

1
2
3
4
5
6
7
8
9
10
11
# ✅ 好的做法:要求 JSON 格式输出
prompt = """
请以 JSON 格式返回:
{
"intent": "意图类型",
"search_queries": ["搜索词1", "搜索词2"]
}
"""

# ❌ 不好的做法:自由文本输出
prompt = "请分析用户意图并生成搜索词"

提供示例

1
2
3
4
5
6
7
8
9
10
11
prompt = """
示例1:
用户问:"AI数字人功能"
返回:{"intent": "feature_lookup", "search_queries": ["AI数字人 功能"]}

示例2:
用户问:"价格是多少?"
返回:{"is_clear": false, "clarification_question": "请问您想了解哪个产品的价格?"}

现在请分析:{user_input}
"""

2. 错误处理

1
2
3
4
5
6
7
8
9
try:
result = graph.invoke(state)
except Exception as e:
logger.error(f"Graph execution failed: {e}")
return {
"success": False,
"error": "系统繁忙,请稍后重试",
"details": str(e) if DEBUG else None
}

3. 日志记录

1
2
3
4
5
6
7
8
9
import logging

logger = logging.getLogger(__name__)

# 记录关键信息
logger.info(f"用户查询: {user_input}")
logger.info(f"意图识别: {intent}")
logger.info(f"检索结果: {len(docs)} 个文档")
logger.info(f"平均分数: {avg_score:.3f}")

4. 配置管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用配置类管理参数
class Config:
# Dify 配置
DIFY_BASE_URL = os.getenv("DIFY_BASE_URL")
DIFY_API_KEY = os.getenv("DIFY_API_KEY")
DATASET_ID = os.getenv("DATASET_ID")

# 检索配置
TOP_K = int(os.getenv("TOP_K", "8"))
SCORE_THRESHOLD = float(os.getenv("SCORE_THRESHOLD", "0.3"))

# LLM 配置
LLM_MODEL = os.getenv("LLM_MODEL", "deepseek-v3.2")
LLM_TEMPERATURE = float(os.getenv("LLM_TEMPERATURE", "0.3"))

5. 监控和评估

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 启用检索质量评估
os.environ["ENABLE_RETRIEVAL_EVAL"] = "true"

# 评估结果会自动打印
"""
============================================================
📊 检索质量评估报告
============================================================
✅ 质量评级: GOOD
📈 综合评分: 0.756
💬 评价: 检索质量良好

详细指标:
- 文档数量: 5
- 平均分数: 0.682
- 产品相关性: 0.800
- 内容多样性: 0.900
============================================================
"""

核心技术总结

1. 技术架构特点

我们的架构采用了 LangGraph + Dify + DeepSeek 的组合方案,具有以下特点:

🎯 可控的工作流

1
2
3
4
5
# LangGraph 提供了完全可控的状态图
- 每个节点的逻辑完全自定义
- 路由决策基于状态动态判断
- 支持条件分支和循环
- 便于调试和优化

🔍 智能检索策略

1
2
3
4
5
6
# 多层次的检索优化
1. 多查询检索:3个搜索词并行检索
2. 智能去重:基于内容哈希去重
3. 分数过滤:双重阈值过滤
4. 智能排序:综合相关性排序
5. 产品过滤:只返回目标产品信息

💬 多轮对话能力

1
2
3
4
5
# 完整的会话管理
- 对话历史维护
- 指代词解析
- 上下文理解
- 多轮澄清机制

🎨 意图特定优化

1
2
3
4
5
# 针对不同意图的个性化处理
- 价格查询:只返回价格信息
- 功能查询:只返回功能信息
- 案例查询:只返回案例信息
- 对比查询:支持多产品对比

2. 与 Dify 原生方案的对比

架构对比

维度 Dify 原生方案 我们的方案 (LangGraph + Dify)
工作流控制 固定流程,灵活性有限 完全自定义,灵活可控
意图识别 基于关键词或简单分类 深度意图理解 + 多轮澄清
检索策略 单次检索 多查询检索 + 智能去重排序
回答生成 通用提示词 意图特定提示词
会话管理 基础历史记录 完整上下文 + 指代词解析
可扩展性 受限于平台能力 完全自主扩展
调试能力 黑盒,难以调试 白盒,每个节点可监控
性能优化 依赖平台优化 自主优化每个环节

核心优势

1️⃣ 更强的控制力

Dify 原生

1
2
用户输入 -> Dify 处理 -> 返回结果
(黑盒处理,无法干预中间过程)

我们的方案

1
2
3
用户输入 -> 意图识别 -> 澄清/检索 -> 生成
↓ ↓ ↓
可自定义 可自定义 可自定义
2️⃣ 更精准的检索

Dify 原生

  • 单一查询
  • 简单的向量检索
  • 无法针对性优化

我们的方案

  • 多查询并行检索(召回率 +40%)
  • 智能去重和排序(相关性 +50%)
  • 产品名称精准匹配(准确率 +25%)
3️⃣ 更好的用户体验

Dify 原生

1
2
3
用户: "价格是多少?"
Dify: "抱歉,请提供更多信息"
(无法主动引导)

我们的方案

1
2
3
4
5
6
用户: "价格是多少?"
系统: "请问您想了解哪个产品的价格?
1. AI数字人
2. AI巡检平台
3. ..."
(主动澄清,提供选项)
4️⃣ 更灵活的扩展

Dify 原生

  • 受限于平台功能
  • 难以添加自定义逻辑
  • 依赖平台更新

我们的方案

  • 完全自主开发
  • 可随时添加新功能
  • 不受平台限制
1
2
3
4
5
6
# 轻松添加新功能
def new_feature_node(state):
# 自定义任何逻辑
return enhanced_state

builder.add_node("new_feature", new_feature_node)
5️⃣ 更好的可观测性

Dify 原生

  • 有限的日志
  • 难以定位问题
  • 无法深度优化

我们的方案

1
2
3
4
5
6
7
8
9
# 每个节点都有详细日志
print("🔍 意图识别: feature_lookup")
print("📊 检索到 5 个文档,平均分数 0.73")
print("🎯 产品过滤: AI数字人环环")
print("✅ 生成回答完成")

# 可选的质量评估
if ENABLE_EVAL:
print_retrieval_quality_report()

适用场景建议

选择 Dify 原生方案

  • ✅ 快速原型验证
  • ✅ 简单的问答场景
  • ✅ 不需要深度定制
  • ✅ 团队技术能力有限

选择我们的方案

  • ✅ 需要精准控制流程
  • ✅ 复杂的业务逻辑
  • ✅ 需要持续优化
  • ✅ 有技术开发能力
  • ✅ 追求极致的用户体验

3. 技术创新点

创新1:多维度检索优化

传统方案只做单次检索,我们实现了:

1
2
3
4
5
6
# 多查询 + 去重 + 过滤 + 排序
queries = generate_multiple_queries(user_input) # 生成3个搜索词
docs = parallel_search(queries) # 并行检索
docs = deduplicate(docs) # 智能去重
docs = filter_by_score(docs, threshold=0.3) # 分数过滤
docs = smart_sort(docs, user_input) # 智能排序

效果:检索质量提升 40%

创新2:意图特定提示词

不同意图使用不同的提示词模板:

1
2
3
4
5
INTENT_PROMPTS = {
"price_inquiry": "只返回价格信息,不要包含功能介绍",
"feature_lookup": "只返回功能信息,不要包含价格",
"case_study": "只返回案例信息,突出客户和效果"
}

效果:回答精准度提升 35%

创新3:产品名称精准匹配

解决了”返回所有产品信息”的问题:

1
2
3
4
5
6
7
8
# 1. 提取产品名称
product_name = extract_product_name(user_input)

# 2. 过滤文档
docs = [doc for doc in docs if product_name in doc.content]

# 3. 提示词约束
prompt = f"只回答「{product_name}」的信息"

效果:准确率提升 25%

创新4:配置化管理

所有参数都可配置,便于调优:

1
2
3
4
5
class RetrievalConfig:
TOP_K = 8 # 可调整
SCORE_THRESHOLD = 0.3 # 可调整
MAX_QUERIES = 3 # 可调整
PRODUCT_NAME_BONUS = 0.2 # 可调整

效果:优化效率提升 50%


总结与展望

项目成果

通过本项目,我们成功构建了一个高质量的智能产品查询助手,实现了:

  1. 智能意图识别:准确识别 6 种查询意图
  2. 多轮澄清机制:引导用户明确查询需求
  3. 高质量检索:检索准确率提升 25%
  4. 精准回答生成:只回答用户问的产品
  5. 简洁的图结构:节点减少 60%,易于维护

技术亮点

  1. LangGraph 状态图:可控的 Agent 工作流
  2. 多查询检索:提高召回率 40%
  3. 智能去重排序:提升相关性 50%
  4. 意图特定提示:针对性优化回答质量
  5. 配置化管理:便于调优和扩展

未来优化方向

1. 缓存机制

1
2
3
4
5
# 缓存常见查询,提升响应速度
cache = {
"AI数字人功能": cached_result,
"AI数字人价格": cached_result
}

2. 异步检索

1
2
3
4
5
# 并行执行多个搜索词
async def parallel_retrieval(queries):
tasks = [search_async(q) for q in queries]
results = await asyncio.gather(*tasks)
return results

3. 用户反馈

1
2
3
4
5
# 收集用户评价,持续优化
@app.post("/api/feedback")
async def feedback(rating: int, comment: str):
save_feedback(rating, comment)
adjust_weights_based_on_feedback()

4. 多模态支持

1
2
3
4
5
# 支持图片、表格等多模态内容
if "图片" in user_input:
return generate_image_response()
elif "对比" in user_input:
return generate_comparison_table()

5. 流式输出

1
2
3
4
5
6
7
# 流式返回结果,提升用户体验
@app.post("/api/chat/stream")
async def chat_stream():
async def generate():
for chunk in llm.stream(prompt):
yield f"data: {chunk}\n\n"
return StreamingResponse(generate())

技术栈总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
后端框架:
- FastAPI: 高性能 Web 框架
- LangGraph: Agent 工作流框架
- LangChain: LLM 应用开发框架

大模型:
- DeepSeek-v3.2: 主力大模型
- 阿里云百炼: API 服务

知识库:
- Dify: 知识库管理平台
- Xinference: 向量模型服务
- Qwen3-Reranker-0.6B: 重排序模型

开发工具:
- Python 3.12
- Pydantic: 数据验证
- Uvicorn: ASGI 服务器

前端:
- HTML5 + CSS3
- JavaScript (原生)
- Markdown 渲染

参考资料