前言

在企业级知识库系统中,用户通常通过自然语言进行提问。但用户的表达千变万化——有时候是想知道有哪些可用的知识助手,有时候是指定某个助手回答问题,更多时候是直接提问期望系统自行判断。这就要求我们的系统具备智能理解用户意图的能力。

本文将围绕 MCP(Model Context Protocol) 思想,结合 RAGFlow 知识库平台和 LangChain 框架,详细介绍如何构建一套智能意图识别与分发系统,让用户以最自然的方式进行知识检索。


一、什么是 MCP?为什么需要它?

MCP(Model Context Protocol,模型上下文协议)是一种让 LLM 与外部工具进行标准化交互的协议。它的核心思想是:

  • LLM 不直接执行操作,而是根据用户意图选择合适的工具

  • 工具函数作为"能力单元" 被注册到系统中

  • 系统自动分析用户输入,判断意图并调度对应工具

在我们的场景中,RAGFlow 知识库本身只能根据用户的文字描述进行查询。用户输入是自由表达的自然语言,无法要求他们按照严格格式规范输入。因此,引入 MCP 架构的目的就是:

让系统自动理解用户到底想做什么,然后调用正确的工具去完成。


二、整体架构

我们将 MCP 工具模块拆分为三个层次:

层级 文件 职责
工具层 tool.py 定义可调用的工具函数
处理层 handler.py 意图分析 + 工具调度
封装层 chat.py 对外暴露极简调用接口

调用链路如下:

用户输入 → chat.py (RAGFlow_chat)
         → handler.py (analyze_user_intent → handle_user_query)
         → tool.py (list_assistant / choose_assistant / auto_assistant_answer)
         → RAGFlow API

三、工具层:tool.py

工具层负责定义所有可被调用的原子能力。本系统共提供 四个核心工具函数

3.1 列出所有助手 — list_assistant()

def list_assistant() -> str:
    """列出所有可用的助手"""
    return get_assistant_list()

直接调用 RAGFlow API 获取当前所有知识助手的列表,包含助手名称、功能介绍、关联知识库等信息。

3.2 指定助手问答 — choose_assistant(assistant_name, question)

def choose_assistant(assistant_name: str, question: str) -> str:
    """使用指定的助手回答问题"""
    answer = create_ask_delete(assistant_name, question)
    return answer

当用户明确说出"用XX助手回答XX问题"时,系统会精确路由到该助手。

3.3 智能匹配问答 — auto_assistant_answer(question)

def auto_assistant_answer(question: str) -> str:
    """
    自动选择合适的助手回答问题
    流程:获取助手列表 → LLM分析选择 → 调用选定助手
    """
    assistants_info = get_assistant_list()
    
    system_prompt = """你是一个助手选择器。请根据用户问题和助手列表,
    选择最适合回答该问题的助手。只返回助手名称,不要添加任何解释。"""
    
    selected_assistant = call_llm(question, system_prompt).strip()
    answer = create_ask_delete(selected_assistant, question)
    return answer

这是最智能的模式——用户随便问,系统自动判断该用哪个知识库。

3.4 LLM 调用封装 — call_llm(prompt, instruction)

def call_llm(prompt: str, instruction: str) -> str:
    """调用大模型进行分析"""
    prompt_template = ChatPromptTemplate.from_messages([
        ("system", "{instruction}"),
        ("human", "{prompt}")
    ])
    
    llm = ChatOpenAI(
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        base_url=os.getenv("DASHSCOPE_BASE_URL"),
        model=os.getenv("LLM_QWEN2.5")
    )
    
    llm_chain = prompt_template | llm
    response = llm_chain.invoke({"instruction": instruction, "prompt": prompt})
    return response.content

使用 LangChain 的 ChatPromptTemplate + ChatOpenAI 构建调用链,底层对接阿里百炼平台的通义千问模型。


四、处理层:handler.py —— 重点

处理层是整个 MCP 架构的核心调度中枢,负责"理解意图 → 匹配资源 → 调度执行"。

4.1 意图分析 — analyze_user_intent()

这是系统最关键的环节。它将用户输入分为三种意图:

def analyze_user_intent(user_query: str) -> Tuple[str, Optional[str], Optional[str]]:
    """
    返回: (意图类型, 助手名称, 问题内容)
    
    意图类型:
    - "list_assistant"        → 查询助手列表
    - "choose_assistant"      → 指定助手问答
    - "auto_assistant_answer" → 直接提问(自动匹配)
    """

具体实现上,通过向 LLM 发送精心设计的 system prompt 来完成分类:

system_prompt = """你是一个用户意图分析器。判断用户输入属于以下哪种类型:
1. 查询助手列表:用户想知道有哪些可用的助手
2. 指定助手回答问题:用户明确指定了某个助手来回答问题
3. 直接提问:用户直接提出了问题,没有指定助手
​
对于第2种情况,需要提取出用户指定的助手名称。
​
请以JSON格式返回分析结果,包含以下字段:
- intent_type: "list_assistant", "choose_assistant", "auto_assistant_answer"
- assistant_name: 如果指定了助手则返回名称,否则为null
​
只返回JSON格式的结果,不要包含任何其他解释或文本。"""

LLM 返回 JSON 后,代码会进行清理和解析,对于 LLM 可能返回的 markdown 代码块包裹(```json ... ```),也做了容错处理。

4.2 助手匹配 — find_best_matching_assistant()

用户说的助手名称可能和系统中的不完全一致(比如用户说"法律助手",系统里叫"刑法法律助手")。这个函数通过两级匹配来解决:

  • 第一级:子串模糊匹配 —— 检查用户输入的助手名和系统助手名是否互为子串

  • 第二级:LLM 语义匹配 —— 如果模糊匹配失败,交给 LLM 进行语义级别的理解匹配

  • 兜底策略 —— 都匹配不上时,回退到"通用知识助手"

# 精确匹配
for name in assistant_names:
    if assistant_name in name or name in assistant_name:
        return name
​
# LLM语义匹配
system_prompt = """你是一个助手匹配器。根据用户提供的助手名称,
从可用的助手列表中找出最匹配的那个。只返回最匹配的助手的完整名称。"""
matched_assistant = call_llm(user_prompt, system_prompt).strip()
​
# 兜底
return "通用知识助手"

4.3 请求处理入口 — handle_user_query()

将意图分析和工具调度串联起来的主流程:

def handle_user_query(user_query: str) -> str:
    # 1. 分析意图
    intent_type, assistant_name, question = analyze_user_intent(user_query)
    
    # 2. 按意图分发
    if intent_type == "list_assistant":
        result = list_assistant()
    elif intent_type == "choose_assistant" and assistant_name:
        matched = find_best_matching_assistant(assistant_name)
        result = choose_assistant(matched, user_query)
    else:
        result = auto_assistant_answer(user_query)
    
    return result

另外还做了错误降级处理:当指定助手回答失败时,自动回退到 auto_assistant_answer 走智能匹配。


五、封装层:chat.py

为了让外部调用方(如 LangChain 主流程、FastAPI 接口等)无需关心内部细节,我们通过 chat.py 做了极简封装:

from RAGFlow_mcp.handler import handle_user_query, list_assistant
​
def RAGFlow_chat(query: str) -> str:
    """处理用户查询,返回回答"""
    return handle_user_query(query)
​
def get_assistants() -> str:
    """获取所有可用的助手列表"""
    return list_assistant()

外部只需两行代码即可完成一次完整的 MCP 智能问答:

from RAGFlow_mcp.chat import RAGFlow_chat
​
answer = RAGFlow_chat("瓦尔登湖在哪里?")
print(answer)

六、运行效果

测试用例

test_queries = [
    "有哪些助手可以使用?",              # → list_assistant
    "用文学知识助手告诉我百年孤独的主角是谁?",  # → choose_assistant
    "空调的绝热工程怎么做?"              # → auto_assistant_answer
]
​
for query in test_queries:
    result = handle_user_query(query)
    print(result)

输出示例

  • 查询助手列表:返回所有助手的名称、功能介绍、关联知识库

  • 指定助手问答:精确使用"文学知识助手"返回《百年孤独》的主角信息

  • 自动匹配问答:系统自动将"空调绝热工程"路由到工程技术类知识库


七、核心设计理念总结

  1. 关注点分离:工具定义(tool)、意图调度(handler)、对外接口(chat)三层解耦

  2. LLM 作为路由器:利用 LLM 的理解能力来判断意图、匹配资源,而非硬编码 if-else 规则

  3. 优雅降级:每一层都有容错和兜底策略,指定助手失败 → 自动匹配 → 通用助手,保证用户体验

  4. 全程日志追踪:使用 Python logging 记录每一步的耗时和结果,便于排查线上问题


八、项目完整信息

  • 知识库平台:RAGFlow

  • LLM 框架:LangChain

  • 大模型:阿里百炼 · 通义千问 2.5

  • 记忆存储:MongoDB

  • 前端:Vue.js 3


总结

本文详细拆解了基于 RAGFlow 和 LangChain 的 MCP 工具模块实现。核心思路是将用户的自然语言输入通过 LLM 进行意图分析,再动态调度到最合适的知识库助手进行处理。这套架构不仅提升了用户的交互体验,也为后续扩展更多工具能力(如文档上传、知识库管理、多轮对话等)提供了灵活的扩展基础。

希望本文能为正在构建 RAG 知识库系统的同学提供一些参考和启发。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐