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 聊天应用代码。

相关推荐
李大腾腾39 分钟前
5、n8n 中调用 API
openai·workflow
阿里云大数据AI技术1 小时前
【跨国数仓迁移最佳实践6】MaxCompute SQL语法及函数功能增强,10万条SQL转写顺利迁移
python·sql
杜子不疼.1 小时前
《Python学习之文件操作:从入门到精通》
数据库·python·学习
微小的xx1 小时前
java + html 图片点击文字验证码
java·python·html
金色旭光1 小时前
uv 现代化的虚拟环境管理工具
python·python进阶
赞哥哥s2 小时前
Python脚本开发-统计Rte中未连接的Port
python·autosar·rte
Franklin2 小时前
Python界面设计【QT-creator基础编程 - 01】如何让不同分辨率图像自动匹配graphicsView的窗口大小
开发语言·python·qt
onejason2 小时前
《利用 Python 爬虫获取 Amazon 商品详情实战指南》
前端·后端·python
苏婳6663 小时前
【最新版】怎么下载mysqlclient并成功安装?
数据库·python·mysql