使用 FastAPI + FastMCP 进行 MCP 引导的实用指南

本文将深入解析MCP协议中的诱导流程(Elicitation Process),通过基于FastAPI框架和FastMCP库构建的投资顾问机器人实例,详细讲解如何在实际项目中高效实现这一流程。

在当今的智能交互领域,用户与AI系统的对话质量直接决定了服务体验的优劣。Model Context Protocol(MCP,模型上下文协议)作为一种标准化的交互框架,为服务器与客户端之间的动态信息获取提供了可靠解决方案。本文将深入解析MCP协议中的诱导流程(Elicitation Process),通过基于FastAPI框架和FastMCP库构建的投资顾问机器人实例,详细讲解如何在实际项目中高效实现这一流程。无论你是AI应用开发者还是协议集成工程师,读完本文后都能清晰掌握MCP诱导流程的核心原理与落地方法。

什么是MCP诱导流程?

MCP诱导流程是Model Context Protocol的核心功能之一,它允许服务器在交互过程中通过客户端向用户请求额外信息,同时保持客户端对用户交互和数据共享的控制权。这种动态信息收集机制的关键在于:服务器通过JSON schema定义结构化数据请求,客户端负责呈现交互界面并返回验证后的用户响应,从而实现灵活且标准化的上下文管理。

举个生活化的例子:当你使用投资顾问AI时,它不会一次性抛出所有问题,而是先询问你的投资目标,再根据你的回答(如“为退休储蓄”)进一步询问退休时间、风险承受能力等相关信息。这种循序渐进的对话方式正是MCP诱导流程的典型应用——通过结构化的多轮交互,逐步构建完整的用户画像。

MCP协议的优势在于其标准化设计,它定义了服务器与客户端之间的通信规范,使得不同系统可以无缝集成。在传统的AI交互模式中,开发者往往需要为每类对话场景定制通信逻辑,而MCP通过统一的诱导流程接口,将对话管理与业务逻辑解耦,大幅提升了开发效率和系统兼容性。

MCP诱导流程的核心组件

要实现基于MCP的诱导流程,需要理解其核心组件及协作方式。在我们的投资顾问机器人案例中,系统架构分为两个关键部分:

MCP客户端(Client)

基于FastAPI框架构建,负责处理与用户的直接交互。客户端通过WebSocket建立实时通信通道,接收用户输入并展示AI消息。在代码实现中,client.py文件包含了WebSocket端点定义、会话管理逻辑和诱导回调函数。

客户端的核心职责包括:

  • 维护用户会话状态
  • 转发AI消息至用户界面
  • 收集并验证用户输入
  • 与MCP服务器建立安全通信

MCP服务器(Server)

基于FastMCP库实现,负责业务逻辑和对话流程管理。服务器定义了诱导步骤、AI交互逻辑和数据验证规则,通过工具接口(Tool)与客户端通信。mcp_server.py文件包含了步骤定义、AI代理链配置和工具实现。

服务器的核心功能包括:

  • 定义诱导流程的步骤序列
  • 生成AI对话内容
  • 解析和验证用户输入
  • 维护对话上下文状态

这两个组件通过MCP协议规范进行通信,客户端专注于用户交互体验,服务器专注于业务逻辑处理,两者通过预定义的消息格式交换数据,实现了清晰的职责分离。

实战:构建投资顾问机器人

接下来,我们将通过具体代码解析,详细讲解如何实现一个基于MCP诱导流程的投资顾问机器人。这个机器人能够通过多轮对话收集用户的投资目标、时间线、风险承受能力和初始投资金额,最终生成结构化的投资建议。

项目初始化与依赖配置

首先需要安装必要的依赖库:

复制

pip install fastapi uvicorn mcp fastmcp langchain-openai python-dotenv loguru

项目结构设计遵循模块化原则:

复制

mcp-investment-bot/
├── client.py        # MCP客户端实现
├── mcp_server.py    # MCP服务器实现
├── .env             # 环境变量配置
└── requirements.txt # 项目依赖

    在.env文件中配置OpenAI API密钥等敏感信息:

    复制

    OPENAI_API_KEY=your_api_key_here

    MCP客户端实现详解

    客户端代码(client.py)的核心是WebSocket端点和诱导回调函数。让我们逐步解析关键代码片段:

    WebSocket端点配置

    复制

    @app.websocket("/investment-conversation")
    async def investment_conversation(websocket: WebSocket):
        async with streamablehttp_client(url=MCP_SERVER_URL) as (read_stream, write_stream, get_session_id):
            async with ClientSession(
                read_stream=read_stream,
                write_stream=write_stream,
                elicitation_callback=smart_elicitation_callback,
            ) as session:
                await session.initialize()
                try:
                    await websocket.accept()
                    session_id = get_session_id()
                    WEBSOCKET_MANAGER[session_id] = websocket
                    result = await session.call_tool(
                        name="elicit-investment-conversation",
                        arguments={"session_id": session_id},
                    )
                    await websocket.send_json(result.structuredContent)
                except Exception as e:
                    logger.exception(e)
                finally:
                    await websocket.close()

      这段代码创建了一个WebSocket端点/investment-conversation,当用户连接时:

      1. 与MCP服务器建立流通信(streamable HTTP)
      2. 初始化客户端会话并注册诱导回调函数
      3. 接受WebSocket连接并生成会话ID
      4. 调用服务器端的elicit-investment-conversation工具启动诱导流程
      5. 接收最终结果并发送给用户
      诱导回调函数

      回调函数是客户端与服务器交互的关键桥梁:

      复制

      async def smart_elicitation_callback(
          context: RequestContext["ClientSession", Any],
          params: types.ElicitRequestParams,
      ):
          data = json.loads(params.message)
          # 向用户发送AI消息
          await WEBSOCKET_MANAGER[data["session_id"]].send_json(
              {"message": data["ai_message"], "step_name": data["step_name"]}
          )
          user_message = None
          # 如果需要用户输入,则等待并接收响应
          if data["retrieve_output"] is True:
              user_message = await WEBSOCKET_MANAGER[data["session_id"]].receive_json()
              user_message = user_message["message"]
          # 将用户输入返回给服务器
          return types.ElicitResult(
              actinotallow="accept",
              cnotallow={"user_message": user_message},
          )

        回调函数的工作流程:

        1. 解析服务器发送的消息参数
        2. 通过WebSocket将AI生成的问题发送给对应会话的用户
        3. 根据retrieve_output标志判断是否需要等待用户输入
        4. 收集用户响应并封装成ElicitResult返回给服务器

        这种设计确保了客户端能够灵活响应服务器的诱导请求,同时保持用户交互的实时性和流畅性。

        MCP服务器实现详解

        服务器代码(mcp_server.py)包含了诱导流程的核心逻辑,包括步骤定义、AI交互链和工具实现。

        诱导步骤定义

        投资顾问机器人的诱导流程分为四个关键步骤:

        复制

        STEPS = [
            (
                "purpose",
                "Step 1, The user is defining their investment purpose. Ask them why they are investing — for example, retirement, wealth growth, education, or a major purchase. If a purpose is provided, confirm.",
            ),
            (
                "timeline",
                "Step 2, The user is specifying their investment timeline. Ask how long they plan to keep the investment — short-term (1–3 years), medium-term (3–7 years), or long-term (7+ years). If a value is given, confirm and ask if they need liquidity before then.",
            ),
            (
                "risk_tolerance",
                "Step 3, The user is describing their risk tolerance. Ask how comfortable they are with potential losses in exchange for potential returns. If a level (low, moderate, high) is provided, confirm and ask for past experience or examples.",
            ),
            (
                "investment_entry",
                "Step 4, The user is entering how they plan to invest. Ask how much they plan to invest and whether it's a one-time or recurring investment. If they mention an amount or product (e.g., ETFs), confirm and ask about timing or strategy.",
            ),
        ]

          每个步骤包含两个元素:步骤名称(用于数据存储)和步骤提示(指导AI生成问题)。这种结构化定义使得流程易于扩展和修改——只需调整步骤列表,即可改变对话逻辑。

          AI代理链配置

          为了让AI能够生成自然且符合流程的问题,我们使用LangChain构建了AI代理链:

          复制

          async def retrieve_agent_chain():
              llm = ChatOpenAI(model="o3-2025-04-16", streaming=False)
              system_message = """
                  You are an intelligent and adaptive investment assistant designed to guide users through a step-by-step 
                  process to define their investment profile. Your role is to gather input for the following stages: 
                  purpose, timeline, risk tolerance, and investment entry. 
                  ...
                  Always respond in **this JSON structure**:
                  {
                    "ai_message": "string",  
                    "investment_data": "string or null"
                  }
              """
              prompt = ChatPromptTemplate.from_messages(
                  [
                      ("system", system_message),
                      MessagesPlaceholder(variable_name="chat_history"),
                      ("user", "{input}"),
                  ]
              )
              output_parser = OutputFixingParser.from_llm(
                  parser=PydanticOutputParser(pydantic_object=InvestmentStepOutput),
                  llm=llm,
                  max_retries=3,
              )
              agent_chain = prompt | llm | output_parser
              return agent_chain

            这个代理链的核心组件包括:

            • 大语言模型:使用OpenAI的o3-2025-04-16模型生成自然语言
            • 系统提示:定义AI的角色和行为规范,确保对话符合投资顾问的专业语境
            • 输出解析器:将AI响应解析为结构化的InvestmentStepOutput对象,包含ai_message(给用户的问题)和investment_data(提取的结构化信息)

            OutputFixingParser的使用确保了即使AI偶尔生成格式错误的响应,系统也能自动修正或重试,提高了流程的健壮性。

            诱导工具实现

            服务器通过工具(Tool)接口暴露诱导流程功能:

            复制

            @mcp_server.tool(name="elicit-investment-conversation")
            async def elicit_investment_conversation(
                ctx: Context, session_id: str
            ) -> InvestmentOutput:
                chat_history = []
                investment_output = InvestmentOutput()
                agent_chain = await retrieve_agent_chain()
            
                for step_name, step_prompt in STEPS:
                    user_message = ""
                    step_output = None
                    retrieve_output = True
                    while step_output is None:
                        # 调用AI生成当前步骤的消息
                        ai_output = await agent_chain.ainvoke(
                            input={
                                "input": user_message,
                                "step_prompt": step_prompt,
                                "chat_history": chat_history,
                            }
                        )
                        # 检查是否获取到足够信息
                        if ai_output.investment_data:
                            retrieve_output = False
                            step_output = ai_output.investment_data
                        # 向客户端发送诱导请求
                        response = await ctx.elicit(
                            message=json.dumps({
                                "session_id": session_id,
                                "retrieve_output": retrieve_output,
                                "ai_message": ai_output.ai_message,
                                "step_name": step_name,
                            }),
                            schema=ElicitationResponse,
                        )
                        # 处理客户端返回的响应
                        match response:
                            case AcceptedElicitation(data=data):
                                user_message = data.user_message
                                chat_history.append(("user", user_message))
                            case DeclinedElicitation():
                                break
                            case CancelledElicitation():
                                break
                    # 保存当前步骤的结果
                    setattr(investment_output, step_name, step_output)
                return investment_output

              这个工具函数实现了核心的诱导逻辑:

              1. 初始化对话历史和结果对象
              2. 遍历预定义的诱导步骤
              3. 对每个步骤:
              • 调用AI代理链生成问题
              • 通过ctx.elicit()向客户端发送诱导请求
              • 等待并处理用户响应
              • 验证是否获取到足够信息,否则重复当前步骤
              1. 收集所有步骤的结果并返回结构化数据

              这种循环结构确保了每个步骤都能获得足够的用户信息后再进入下一步,避免了因信息不足导致的建议质量问题。

              MCP诱导流程的工作原理

              理解MCP诱导流程的工作原理,需要跟踪从用户连接到最终结果生成的完整生命周期。

              流程启动阶段

              1. 用户通过WebSocket连接到客户端的/investment-conversation端点
              2. 客户端与MCP服务器建立流通信会话
              3. 客户端调用服务器的elicit-investment-conversation工具,传入会话ID
              4. 服务器初始化对话上下文,准备开始诱导流程

              多轮交互阶段

              以“投资目标”步骤为例,交互流程如下:

              1. 服务器调用AI代理链,基于步骤提示生成问题(如“您投资的主要目的是什么?”)
              2. 服务器通过ctx.elicit()发送诱导请求,包含AI消息和会话信息
              3. 客户端的smart_elicitation_callback被触发,将AI消息通过WebSocket发送给用户
              4. 用户输入响应(如“我在为退休储蓄”)并发送给客户端
              5. 客户端将用户响应封装成ElicitResult返回给服务器
              6. 服务器解析响应,AI判断信息是否足够
              7. 若信息足够,保存结果并进入下一步;否则重复当前步骤

              这一过程在每个步骤中重复,直到所有必要信息收集完毕。

              结果生成阶段

              1. 服务器完成所有诱导步骤后,将收集的信息整理成InvestmentOutput对象
              2. 服务器将结果返回给客户端
              3. 客户端通过WebSocket将最终结果展示给用户
              4. 客户端和服务器关闭会话连接

              整个流程中,MCP协议确保了服务器与客户端之间的通信标准化,而FastAPI和FastMCP则提供了高效的实现框架,使得开发者可以专注于业务逻辑而非通信细节。

              结构化输出与应用扩展

              MCP诱导流程的最终价值体现在其生成的结构化数据上。在我们的案例中,最终输出定义为:

              class InvestmentOutput(BaseModel):
                  purpose: Optional[str] = Field(default=None)
                  timeline: Optional[str] = Field(default=None)
                  risk_tolerance: Optional[str] = Field(default=None)
                  investment_entry: Optional[str] = Field(default=None)

              一个典型的输出示例如下:

              {
                "purpose": "retirement",
                "timeline": "20 years",
                "risk_tolerance": "moderate",
                "investment_entry": "$10,000 one-time"
              }

                这种结构化数据可以直接用于后续处理:

                • 传入投资策略生成模型,获取个性化建议
                • 存储到数据库,用于用户投资画像分析
                • 作为输入参数,进行投资回报模拟计算
                • 集成到财务规划系统,生成完整的退休计划

                在实际应用中,你可以根据业务需求扩展输出模型,例如增加“每月追加投资金额”“投资偏好行业”等字段,只需相应调整诱导步骤和AI提示即可。

                进阶优化与最佳实践

                要构建生产级别的MCP诱导系统,还需要考虑以下优化方向:

                错误处理与重试机制

                在网络不稳定或用户输入异常的情况下,系统需要具备容错能力。可以扩展客户端和服务器代码,增加:

                • 连接超时自动重连
                • 用户输入验证和错误提示
                • AI生成内容的质量检查
                • 关键步骤的日志记录和监控

                会话管理与上下文持久化

                对于复杂的诱导流程,可能需要支持会话中断后恢复。可以通过以下方式实现:

                • 将对话历史存储到数据库
                • 使用Redis等缓存服务保存会话状态
                • 实现会话ID与用户身份的关联
                • 支持会话过期策略配置

                用户体验优化

                为提升用户体验,可以在客户端实现:

                • 步骤进度指示器
                • 输入验证和即时反馈
                • 对话历史展示
                • 响应等待状态提示
                • 深色/浅色模式适配

                安全性增强

                生产环境中需要加强安全措施:

                • 对WebSocket通信进行加密(wss协议)
                • 实现用户身份认证和授权
                • 对服务器接口进行速率限制
                • 验证客户端请求的来源合法性
                • 敏感数据加密存储
                • AI大模型学习福利

                  作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

                  一、全套AGI大模型学习路线

                  AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

                  因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

                  二、640套AI大模型报告合集

                  这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

                  因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

                  三、AI大模型经典PDF籍

                  随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


                  因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

                  四、AI大模型商业化落地方案

                  因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

                  作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

                评论
                添加红包

                请填写红包祝福语或标题

                红包个数最小为10个

                红包金额最低5元

                当前余额3.43前往充值 >
                需支付:10.00
                成就一亿技术人!
                领取后你会自动成为博主和红包主的粉丝 规则
                hope_wisdom
                发出的红包
                实付
                使用余额支付
                点击重新获取
                扫码支付
                钱包余额 0

                抵扣说明:

                1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                余额充值