FastAPI开发AI应用三:添加深度思考功能

本文将深入讲解如何在 FastAPI AI 聊天应用中接入 DeepkSeek 等有深度思考功能的模型时,如何让 AI 能够展示其推理过程, 提升用户对 AI 回答的理解和信任度。通过本教程,你将学会如何处理 AI 模型的 reasoning_content (思考内容)输出, 并在前端优雅地展示思考过程。

📖 项目地址:github.com/wayn111/fas...

温馨提示:本文全文约七千字,看完约需 10 分钟。

本章概述

深度思考功能的核心在于正确处理两种不同类型的数据流。本项目将使用 openai python sdk 提供的通用方式来检测 reasoning_content (思考内容)和 content (普通内容)字段来区分不同类型的内容,前端则根据 type 字段进行差异化渲染。

核心功能

  • 思考过程可视化:实时展示 AI 的推理步骤和思考逻辑
  • 双重内容流:后端代码如何区分思考内容(reasoning_content)和最终回答(content)
  • 差异化展示:前端展示时,思考内容和最终回答采用不同的视觉样式

技术栈

  • 后端框架:FastAPI(高性能异步 Web 框架)
  • AI 集成:OpenAI SDK(支持 reasoning_content 的模型,比如 DeepSeek)
  • 流式协议:Server-Sent Events(SSE)
  • 前端渲染:Marked.js + Highlight.js(Markdown 和代码高亮)
  • 样式设计:HTML5 + CSS3(差异化视觉效果)

支持深度思考的模型

  • OpenAI:o4-mini
  • DeepSeek:deepseek-reasoner
  • Qwen:qwen-plus

核心设计

设计理念

深度思考功能的实现基于三个核心设计原则:

1. 内容分离原则思考内容(reasoning_content)和最终回答(content)是两个独立的数据流,需要分别处理和展示。这样可以让用户清楚地区分 AI 的思考过程和最终结论。

2. 实时展示原则思考过程应该实时展示,让用户能够跟随 AI 的思维轨迹。这不仅提升了用户体验,还增加了 AI 回答的透明度和可信度。

3. 视觉区分原则思考内容和最终回答需要采用不同的视觉样式,让用户能够一眼区分两种不同类型的内容。

代码分层

深度思考功能的架构分为三个清晰的层次:

1. 通用处理层(OpenAICompatibleProvider(BaseAIProvider))

这一层负责处理 AI 模型的原始输出,识别和分离 reasoning_content 和 content:

python 复制代码
# class OpenAICompatibleProvider(BaseAIProvider):
asyncdef generate_streaming_response(
    self,
    messages: List[AIMessage],
    **kwargs
) -> AsyncGenerator[str, None]:
    """
    生成流式AI响应,支持深度思考内容

    Args:
        messages: 对话历史消息列表
        **kwargs: 其他参数

    Yields:
        str: 流式响应内容片段,包含类型标识
    """
    try:
        # 格式化消息并构建请求参数
        system_prompt = kwargs.get('system_prompt')
        formatted_messages = self.format_messages(messages, system_prompt)
        request_params = self._build_request_params(formatted_messages, stream=True, **kwargs)

        logger.info(f"调用{self.get_provider_display_name()}流式API - 模型: {request_params['model']}")

        # 调用流式API
        response = self.client.chat.completions.create(**request_params)

        chunk_count = 0
        import json

        for chunk in response:
            # 处理深度思考内容
            if hasattr(chunk.choices[0].delta, 'reasoning_content') and chunk.choices[0].delta.reasoning_content:
                content = chunk.choices[0].delta.reasoning_content
                chunk_count += 1
                # 返回带类型标识的思考内容
                yieldf"data: {json.dumps({'type': 'reasoning', 'content': content})}\n\n"

            # 处理普通回答内容
            elif hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
                content = chunk.choices[0].delta.content
                chunk_count += 1
                # 返回带类型标识的回答内容
                yieldf"data: {json.dumps({'type': 'content', 'content': content})}\n\n"

        logger.info(f"{self.get_provider_display_name()}流式响应完成 - 块数: {chunk_count}")

    except Exception as e:
        logger.error(f"{self.get_provider_display_name()}流式响应失败: {e}")
        yieldf"抱歉,{self.get_provider_display_name()}流式服务暂时不可用:{str(e)}"

核心特点:

  • 双重检测:在返回的 response 中同时检测 reasoning_content 和 content 字段
  • 类型标识:为每个数据块添加 type 字段,便于前端区分处理 reasoning_content 和 content
  • JSON 格式:使用结构化的 JSON 格式传输数据
  • 错误处理:完善的异常处理机制

2. 接口传输层(FastAPI)

main.py 的流式响应处理中,系统会解析每个数据块并只保存 type: 'content' 的内容到 Redis, 在大模型的多轮对话记忆中,无需添加 reasoning_content 字段。

ini 复制代码
    full_response = ""
    content_only_response = ""# 只保存 type: 'content' 的内容
    chunk_count = 0
    asyncfor chunk in ai_manager.generate_streaming_response(
        messages=ai_messages,
        provider=provider,
        model=model,
        system_prompt=system_prompt
    ):
            if chunk:
                full_response += chunk
                chunk_count += 1
            # 解析chunk数据,只保留 type: 'content' 的内容到Redis
            try:
                if chunk.startswith("data: "):
                    json_str = chunk[6:].strip()  # 移除 "data: " 前缀
                    if json_str:
                        chunk_data = json.loads(json_str)
                        # 只累积 type 为 'content' 的内容用于保存到Redis
                        if chunk_data.get('type') == 'content'and'content'in chunk_data:
                            content_only_response += chunk_data['content']
            except (json.JSONDecodeError, KeyError) as e:
                # 如果解析失败,按原来的方式处理(向后兼容)
                logger.debug(f"解析chunk数据失败,使用原始内容: {e}")
            yield chunk

    logger.info(f"流式响应完成 - 用户: {user_id}, 会话: {session_id[:8]}..., 块数: {chunk_count}, 总长度: {len(full_response)}, 内容长度: {len(content_only_response)}")

    # 保存AI响应(只保存 type: 'content' 的内容)
    ai_msg = ChatMessage(
        role="assistant",
        content=content_only_response,  # 使用过滤后的内容
        timestamp=time.time()
    )
    await save_message_to_redis(user_id, session_id, ai_msg)

3. 前端展示层(JavaScript)

这一层负责接收数据并进行差异化展示:

ini 复制代码
// 项目中的实际前端处理逻辑
if (data.type === 'chunk' || data.type === 'content' || data.type === 'reasoning') {
    if (data.type === 'reasoning') {
        // 深度思考内容
        if (!reasoningElement) {
            reasoningElement = document.createElement('div');
            reasoningElement.className = 'message-content reasoning-content';

            // 创建reasoning-body容器
            const reasoningBody = document.createElement('div');
            reasoningBody.className = 'reasoning-body';
            reasoningElement.appendChild(reasoningBody);

            // 将reasoning元素添加到消息容器的content wrapper中
            const contentWrapper = messageContainer.querySelector('.message-content-wrapper');
            contentWrapper.appendChild(reasoningElement);
        }
        reasoningMessage += data.content;
        ...
        scrollToBottom();

    } else {
        // 普通内容
        if (!contentElement) {
            contentElement = document.createElement('div');
            contentElement.className = 'message-content';

            // 将content元素添加到消息容器的content wrapper中
            const contentWrapper = messageContainer.querySelector('.message-content-wrapper');
            contentWrapper.appendChild(contentElement);
        }
        contentMessage += data.content;
        ...
        scrollToBottom();
    }

    scrollToBottom();
}

前端在接受 SSE 响应上,要区分 data 返回的 type 类型是 reason 还是 content,以此作不同展示。

启动一下

复制代码
python start_server.py

打开浏览器访问:http://localhost:8000

测试一下深度思考功能~

选择 deepseek-reasoner,进行模型问答,

  1. 数学问题求解用户:证明勾股定理

2. 编程问题分析

复制代码
用户:设计一个高效的排序算法

FastAPI开发AI应用系列文章

📚 总结

本文详细介绍了在 FastAPI AI 聊天应用中集成深度思考功能的完整实现方案。通过分层设计(通用处理层、接口传输层、前端展示层),成功实现了 AI 推理过程的可视化展示。

最后觉得本文写的不错的话,可以关注我,我会继续更新 FastAPI 框架开发 AI 聊天应用代码。

相关推荐
程序员小远2 小时前
软件测试之单元测试详解
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
用户47949283569152 小时前
Bun 卖身 Anthropic!尤雨溪神吐槽:OpenAI 你需要工具链吗?
前端·openai·bun
心无旁骛~2 小时前
python多进程和多线程问题
开发语言·python
星云数灵2 小时前
使用Anaconda管理Python环境:安装与验证Pandas、NumPy、Matplotlib
开发语言·python·数据分析·pandas·教程·环境配置·anaconda
计算机毕设匠心工作室3 小时前
【python大数据毕设实战】青少年抑郁症风险数据分析可视化系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习
后端·python
计算机毕设小月哥3 小时前
【Hadoop+Spark+python毕设】智能制造生产效能分析与可视化系统、计算机毕业设计、包括数据爬取、Spark、数据分析、数据可视化、Hadoop
后端·python·mysql
计算机毕设小月哥5 小时前
【Hadoop+Spark+python毕设】中风患者数据可视化分析系统、计算机毕业设计、包括数据爬取、Spark、数据分析、数据可视化、Hadoop
后端·python·mysql
Keep_Trying_Go5 小时前
基于Zero-Shot的目标计数算法详解(Open-world Text-specified Object Counting)
人工智能·pytorch·python·算法·多模态·目标统计
计算机毕设匠心工作室5 小时前
【python大数据毕设实战】强迫症特征与影响因素数据分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
后端·python·mysql