LangGraph+deepseek-r1+FastAPI+Gradio(升级1)

3 阅读35分钟

流量套餐客服大模型系统 逐文件逐行超详细解析

本次解析将按文件维度,从代码逐行解读 + 参数含义 + 逻辑设计 + 技术原理 + 业务落地五个层面,把每个文件的核心代码、隐藏逻辑、设计初衷讲透,同时关联各文件间的调用关系,让你彻底理解这套带长 / 短期记忆流式 / 非流式响应多模型适配的大模型客服系统实现细节。

所有文件的核心调用链路:webUI.py/apiTest.py → 调用 main.py 的 8012 接口 → main.py 调用 llms.py 初始化模型 → 基于 demoWithMemory.py 的记忆逻辑处理对话 → 加载 prompt_template_*.txt 业务规则 → 完成响应并返回

一、llms.py - 全系统模型入口(底层封装,核心解耦)

核心定位:统一封装所有大模型 / 嵌入模型的初始化逻辑,为上层main.py/demoWithMemory.py提供标准化的模型实例,实现模型与业务逻辑完全解耦,切换模型仅需修改llm_type参数。

1. 导入依赖 & 日志配置

python

运行

import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from typing import Optional
import logging
# 设置日志模版
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
  • os:用于设置环境变量(适配 Ollama 本地模型);
  • ChatOpenAI:LangChain 封装的 OpenAI 风格聊天模型类,兼容所有实现 OpenAI 接口的大模型(本地 Ollama、OneAPI、通义千问等);
  • OpenAIEmbeddings:向量嵌入模型类,将文本转为向量,为长期记忆的向量检索提供能力;
  • typing.Optional:类型注解,标识参数可为空,提升代码可读性和规范性;
  • logging:Python 内置日志库,配置时间 - 日志名 - 级别 - 信息的格式,方便调试和问题排查。

2. 模型核心配置字典

python

运行

MODEL_CONFIGS = {
    "openai": {
        "base_url": "https://yunwu.ai/v1",  # 云雾AI的OpenAI兼容接口
        "api_key": "sk-3QztQa9rwwKc9MjkgjtnK0GjTdtnVjDkEswHcX0uEm5uQB1E",  # 平台API密钥
        "chat_model": "gpt-4o-mini",  # 聊天模型名
        "embedding_model": "text-embedding-3-small"  # 嵌入模型名
    },
    "oneapi": {
        "base_url": "http://139.224.72.218:3000/v1",  # 自建OneAPI服务地址
        "api_key": "sk-EDjbeeCYkD1OnI9E48018a018d2d4f44958798A261137591",
        "chat_model": "qwen-max",  # 通义千问大模型
        "embedding_model": "text-embedding-v1"
    },
    "qwen": {
        "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",  # 阿里通义千问官方兼容接口
        "api_key": "sk-80a72f794bc4488d85798d590e96db43",
        "chat_model": "qwen-max",
        "embedding_model": "text-embedding-v1"
    },
    "ollama": {
        "base_url": "http://localhost:11434/v1",  # 本地Ollama的OpenAI兼容接口
        "api_key": "ollama",  # Ollama无需真实密钥,任意值即可
        "chat_model": "deepseek-r1:14b",  # 本地部署的deepseek-r1 14B模型
        "embedding_model": "nomic-embed-text:latest"  # 本地嵌入模型
    }
}

设计初衷:将所有模型的配置集中管理,避免硬编码在业务逻辑中,后续新增 / 修改模型仅需在该字典中添加配置,无需改动其他代码关键特性:所有模型均实现OpenAI 接口规范,因此可通过同一个ChatOpenAI类初始化,这是多模型适配的核心基础。

3. 全局默认配置

python

运行

DEFAULT_LLM_TYPE = "openai"  # 默认使用openai类型模型(云雾AI)
DEFAULT_TEMPERATURE = 0.7    # 模型温度系数:0=确定性回答,1=创造性回答,0.7兼顾稳定和灵活
  • temperature:大模型核心参数,控制输出的随机性,客服场景设为 0.7,既保证回答符合套餐规则,又有一定口语化灵活性。

4. 自定义异常类

python

运行

class LLMInitializationError(Exception):
    """自定义异常类用于LLM初始化错误"""
    pass

设计初衷:为模型初始化错误创建专属异常,与其他通用异常(如 IOError、JSONDecodeError)区分,方便上层代码精准捕获并处理模型初始化问题,提升错误排查效率。

5. 核心初始化函数:initialize_llm

python

运行

def initialize_llm(llm_type: str = DEFAULT_LLM_TYPE) -> tuple[ChatOpenAI, OpenAIEmbeddings]:
    """
    初始化LLM实例
    Args:
        llm_type (str): LLM类型,可选值为 'openai', 'oneapi', 'qwen', 'ollama'
    Returns:
        tuple: (ChatOpenAI实例, OpenAIEmbeddings实例)
    Raises:
        LLMInitializationError: 当LLM初始化失败时抛出
    """
    try:
        # 1. 校验模型类型是否合法
        if llm_type not in MODEL_CONFIGS:
            raise ValueError(f"不支持的LLM类型: {llm_type}. 可用的类型: {list(MODEL_CONFIGS.keys())}")
        config = MODEL_CONFIGS[llm_type]  # 获取对应模型配置
        
        # 2. 特殊处理Ollama本地模型:Ollama无需真实OpenAI API密钥,强制设置为NA
        if llm_type == "ollama":
            os.environ["OPENAI_API_KEY"] = "NA"
        
        # 3. 初始化聊天模型
        llm = ChatOpenAI(
            base_url=config["base_url"],  # 模型接口地址
            api_key=config["api_key"],    # 接口密钥
            model=config["chat_model"],   # 模型名
            temperature=DEFAULT_TEMPERATURE,  # 温度系数
            timeout=30,  # 超时时间:30秒,避免模型响应过慢导致服务阻塞
            max_retries=2  # 重试次数:失败后重试2次,提升服务鲁棒性
        )
        
        # 4. 初始化嵌入模型(为长期记忆的向量检索服务)
        embedding = OpenAIEmbeddings(
            base_url=config["base_url"],
            api_key=config["api_key"],
            model=config["embedding_model"],
            deployment=config["embedding_model"]  # 部署名,与模型名一致即可
        )
        
        logger.info(f"成功初始化 {llm_type} LLM")
        return llm, embedding  # 返回聊天模型+嵌入模型元组
    
    # 捕获配置错误(如非法llm_type)
    except ValueError as ve:
        logger.error(f"LLM配置错误: {str(ve)}")
        raise LLMInitializationError(f"LLM配置错误: {str(ve)}")
    # 捕获所有其他初始化错误(如网络错误、接口无效等)
    except Exception as e:
        logger.error(f"初始化LLM失败: {str(e)}")
        raise LLMInitializationError(f"初始化LLM失败: {str(e)}")

核心作用:接收模型类型,返回可直接调用的聊天模型 + 嵌入模型实例,所有异常均封装为LLMInitializationError,上层统一捕获。关键参数timeoutmax_retries是生产级服务的必备配置,避免因模型接口问题导致整个服务挂死。

6. 封装函数:get_llm

python

运行

def get_llm(llm_type: str = DEFAULT_LLM_TYPE) -> ChatOpenAI:
    """
    获取LLM实例的封装函数,提供默认值和错误重试逻辑
    Args:
        llm_type (str): LLM类型
    Returns:
        tuple: (ChatOpenAI实例, OpenAIEmbeddings实例)
    """
    try:
        return initialize_llm(llm_type)  # 尝试初始化指定模型
    except LLMInitializationError as e:
        logger.warning(f"使用默认配置重试: {str(e)}")
        # 若指定模型初始化失败,且不是默认模型,则重试默认的openai模型
        if llm_type != DEFAULT_LLM_TYPE:
            return initialize_llm(DEFAULT_LLM_TYPE)
        raise  # 若默认模型也失败,直接抛出异常,终止程序

设计初衷:为模型初始化添加失败重试机制,提升服务的容错性。例如:若指定ollama模型但本地未启动,自动重试openai模型,保证服务能正常运行。

7. 示例测试代码

python

运行

if __name__ == "__main__":
    try:
        # 测试不同类型的LLM初始化
        llm_openai = get_llm("openai")
        llm_qwen = get_llm("qwen")
        # 测试无效类型
        llm_invalid = get_llm("invalid_type")
    except LLMInitializationError as e:
        logger.error(f"程序终止: {str(e)}")

作用:单独运行llms.py时,可测试模型初始化逻辑是否正常,快速排查模型配置 / 网络问题。

二、demoWithMemory.py - 记忆逻辑核心 Demo(基础验证)

核心定位:实现 LangGraph 的长 / 短期记忆核心逻辑,是main.py业务化记忆逻辑的基础原型,专门用于验证记忆功能的有效性(用户隔离、会话隔离、记忆存储 / 检索),无需启动后端服务,直接运行即可测试。

1. 导入依赖

python

运行

import os
import uuid  # 生成唯一标识,为长期记忆的每条记录分配唯一ID
from langgraph.store.base import BaseStore  # LangGraph存储基类,用于长期记忆
from langchain_core.runnables import RunnableConfig  # LangGraph配置类,传递user_id/thread_id
from langgraph.graph import StateGraph, START, END, MessagesState  # LangGraph核心组件
from llms import get_llm  # 导入自定义的模型初始化函数
import sys  # 用于程序异常退出
from langgraph.checkpoint.memory import MemorySaver  # 短期记忆存储:保存对话上下文
from langgraph.store.memory import InMemoryStore  # 长期记忆存储:基于内存的向量存储

关键组件说明

表格

组件核心作用
StateGraphLangGraph 状态图,定义业务执行流程(START→节点→END),是对话逻辑的载体
MessagesStateLangGraph 内置状态类,核心存储messages列表(对话消息),状态图的核心数据
MemorySaver短期记忆组件,基于thread_id隔离会话,保存对话的上下文状态
InMemoryStore长期记忆组件,基于向量检索,保存用户专属信息,基于user_id隔离用户
RunnableConfig配置类,传递configurable参数(user_id/thread_id),实现用户 / 会话隔离

2. 全局配置

python

运行

llm_type = "openai"  # 测试使用的模型类型

3. 核心函数:create_graph(构建 LangGraph 状态图)

python

运行

def create_graph(llm_type: str) -> StateGraph:
    try:
        # 1. 初始化模型:调用llms.py的get_llm,获取聊天模型+嵌入模型
        llm, embedding = get_llm(llm_type)
        
        # 2. 初始化长期记忆存储:InMemoryStore(内存向量存储)
        in_memory_store = InMemoryStore(
            index={
                "embed": embedding,  # 嵌入模型:将文本转为向量
                "dims": 1536,        # 向量维度:text-embedding-3-small的维度为1536,需与嵌入模型匹配
            }
        )
        
        # 3. 初始化状态图:以MessagesState为状态载体
        graph_builder = StateGraph(MessagesState)
        
        # 4. 定义短期记忆过滤函数:控制上下文长度,避免模型输入过长
        def filter_messages(messages: list):
            if len(messages) <= 3:
                return messages
            return messages[-3:]  # 只保留最后3条消息,作为短期上下文(核心:限制上下文长度)
        
        # 5. 定义状态图核心节点:chatbot(处理所有对话逻辑,含长/短期记忆)
        def chatbot(state: MessagesState, config: RunnableConfig, *, store: BaseStore):
            # ---------------------- 长期记忆逻辑:存储/检索/使用 ----------------------
            # 5.1 定义长期记忆的命名空间:(固定标识, user_id),基于user_id隔离不同用户的记忆
            namespace = ("memories", config["configurable"]["user_id"])
            # 5.2 检索长期记忆:将用户最新问题作为查询,从当前用户的命名空间中检索相关记忆
            memories = store.search(namespace, query=str(state["messages"][-1].content))
            # 5.3 拼接检索到的记忆:将多条记忆拼接为字符串,用于拼接到系统提示词
            info = "\n".join([d.value["data"] for d in memories])
            # 5.4 构建带记忆的系统提示词:将用户记忆传入,让模型“记住”用户专属信息
            system_msg = f"You are a helpful assistant talking to the user. User info: {info}"
            
            # 5.5 存储新的长期记忆:当用户消息包含“记住”关键词时,将指定信息存入向量库
            last_message = state["messages"][-1]  # 获取用户最新消息
            if "记住" in last_message.content.lower():  # 触发条件:消息含“记住”
                memory = "我的频道是南哥AGI研习社。"  # 要存储的记忆内容(Demo固定值)
                # 存储记忆:(命名空间, 唯一ID, 记忆数据),uuid保证每条记忆ID唯一
                store.put(namespace, str(uuid.uuid4()), {"data": memory})
            
            # ---------------------- 短期记忆逻辑:过滤上下文 ----------------------
            messages = filter_messages(state["messages"])  # 只保留最后3条消息
            
            # ---------------------- 调用大模型生成回复 ----------------------
            response = llm.invoke(
                [{"role": "system", "content": system_msg}] + messages  # 系统提示词+过滤后的上下文
            )
            return {"messages": [response]}  # 将模型回复存入状态,更新MessagesState
        
        # 6. 配置状态图:添加节点+定义边
        graph_builder.add_node("chatbot", chatbot)  # 添加chatbot节点
        graph_builder.add_edge(START, "chatbot")    # 起始点→chatbot节点
        graph_builder.add_edge("chatbot", END)      # chatbot节点→结束点
        
        # 7. 初始化短期记忆组件:MemorySaver
        memory = MemorySaver()
        
        # 8. 编译状态图:整合长/短期记忆,生成可调用的graph实例
        # checkpointer=memory:短期记忆由MemorySaver管理
        # store=in_memory_store:长期记忆由InMemoryStore管理
        return graph_builder.compile(checkpointer=memory, store=in_memory_store)
    
    except Exception as e:
        raise RuntimeError(f"Failed to create graph: {str(e)}")

长记忆核心逻辑(3 步)

  1. 检索:用户提问→将问题向量化→从当前用户的向量库中检索相关记忆;
  2. 使用:将检索到的记忆拼接到系统提示词→传入大模型;
  3. 存储:用户消息含 “记住”→将指定内容向量化→存入当前用户的向量库,分配唯一 UUID。短记忆核心逻辑:仅保留最后 3 条对话,平衡上下文连续性和模型推理效率,避免上下文过长导致模型卡顿 / 收费过高。

4. 辅助函数:save_graph_visualization(状态图可视化)

python

运行

def save_graph_visualization(graph: StateGraph, filename: str = "graph.png") -> None:
    try:
        with open(filename, "wb") as f:
            # 将状态图转为Mermaid格式的PNG图片,直观查看流程
            f.write(graph.get_graph().draw_mermaid_png())
        print(f"Graph visualization saved as {filename}")
    except IOError as e:
        print(f"Warning: Failed to save graph visualization: {str(e)}")

设计初衷:LangGraph 状态图的流程可通过可视化直观展示,方便开发阶段调试,快速定位节点 / 边的配置问题。

5. 辅助函数:stream_response(处理流式响应)

python

运行

def stream_response(graph: StateGraph, user_input: str, config) -> None:
    try:
        # 调用graph.stream,传入用户输入和配置,获取流式响应
        events = graph.stream({"messages": [{"role": "user", "content": user_input}]}, config)
        # 遍历流式响应,打印模型回复
        for event in events:
            for value in event.values():
                print("Assistant:", value["messages"][-1].content)
    except Exception as e:
        print(f"Error processing response: {str(e)}")

6. 主函数:main(核心测试逻辑)

python

运行

def main():
    try:
        # 1. 构建状态图+可视化
        graph = create_graph(llm_type)
        save_graph_visualization(graph)
    except RuntimeError as e:
        print(f"Error: {str(e)}")
        sys.exit(1)  # 状态图构建失败,程序退出
    
    # 测试1:存储记忆 → user_id=1,thread_id=1
    config = {"configurable": {"thread_id": "1", "user_id": "1"}}
    input_message = {"role": "user", "content": "记住:我的频道是南哥AGI研习社"}
    # graph.stream:非异步流式调用,stream_mode="values":只返回状态值
    for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
        chunk["messages"][-1].pretty_print()  # 格式化打印模型回复
    
    # 测试2:检索记忆 → 同一用户(user_id=1),不同会话(thread_id=2),验证跨会话记忆
    config = {"configurable": {"thread_id": "2", "user_id": "1"}}
    input_message = {"role": "user", "content": "我的频道是什么?"}
    for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
        chunk["messages"][-1].pretty_print()
    
    # 测试3:用户隔离 → 不同用户(user_id=2),提问相同问题,验证无法检索到其他用户的记忆
    config = {"configurable": {"thread_id": "3", "user_id": "2"}}
    input_message = {"role": "user", "content": "我的频道是什么?"}
    for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
        chunk["messages"][-1].pretty_print()
    
    # 测试4:交互式聊天 → user_id=4,thread_id=4,支持手动输入问题测试
    print("Chatbot ready! Type 'quit', 'exit', or 'q' to end the conversation.")
    config = {"configurable": {"thread_id": "4", "user_id": "4"}}
    while True:
        try:
            user_input = input("User: ").strip()
            # 退出条件
            if user_input.lower() in {"quit", "exit", "q"}:
                print("Goodbye!")
                break
            # 空输入处理
            if not user_input:
                print("Please enter something to chat about!")
                continue
            # 处理用户输入并返回回复
            stream_response(graph, user_input, config)
        # 捕获用户手动中断(Ctrl+C)
        except KeyboardInterrupt:
            print("\nInterrupted by user. Goodbye!")
            break
        # 捕获其他异常,触发兜底逻辑
        except Exception as e:
            print(f"Unexpected error: {str(e)}")
            fallback_input = "What do you know about LangGraph?"
            print(f"User (fallback): {fallback_input}")
            stream_response(graph, fallback_input)
            break

if __name__ == "__main__":
    main()

4 组测试的设计目的

  1. 测试 1:验证记忆存储功能,含 “记住” 关键词时能正常存入向量库;
  2. 测试 2:验证跨会话记忆检索,同一用户不同会话能获取到之前存储的记忆;
  3. 测试 3:验证用户隔离,不同用户无法获取到其他用户的记忆(核心:多用户场景的必备特性);
  4. 测试 4:提供交互式测试,方便手动输入任意问题验证记忆逻辑。

三、main.py - 后端核心服务(FastAPI + 业务化改造,生产级入口)

核心定位:将demoWithMemory.py的记忆逻辑业务化改造(适配流量套餐客服场景),基于 FastAPI 实现标准 OpenAI 格式/v1/chat/completions接口,提供流式 / 非流式响应,是前端webUI.py和测试脚本apiTest.py核心调用目标(端口 8012)。与 demoWithMemory.py 的核心区别

  1. 适配流量套餐客服业务,修改系统提示词和记忆存储内容;
  2. 基于 FastAPI 实现异步 Web 接口,支持高并发;
  3. 实现标准化的请求 / 响应模型(兼容 OpenAI);
  4. 加载本地提示词文件prompt_template_*.txt),实现业务规则可配置;
  5. 增加全局异常处理日志精细化记录,适配生产级服务。

1. 导入依赖(在 demo 基础上新增 FastAPI 相关组件)

python

运行

import os
import re
import uuid
import time
import json
import logging
from contextlib import asynccontextmanager  # FastAPI生命周期管理
from pydantic import BaseModel, Field     # 数据校验与模型定义
from typing import List, Optional
from langchain_core.prompts import PromptTemplate  # 提示词模板
from langgraph.store.base import BaseStore
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph, START, END, MessagesState
from llms import get_llm
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
from fastapi import FastAPI, HTTPException  # FastAPI核心组件
from fastapi.responses import JSONResponse, StreamingResponse  # FastAPI响应类
import uvicorn  # 运行FastAPI的ASGI服务器

新增关键组件说明

表格

组件核心作用
asynccontextmanager管理 FastAPI 生命周期,实现启动初始化关闭清理
pydantic.BaseModel定义请求 / 响应的数据模型,实现自动数据校验结构化输出
FastAPI高性能异步 Web 框架,构建 RESTful API,支持异步接口和高并发
HTTPExceptionFastAPI 异常类,返回标准化的 HTTP 错误响应(如 500/400)
JSONResponse/StreamingResponse分别返回 JSON 格式响应和流式响应(SSE)
uvicornASGI 服务器,用于运行 FastAPI 异步应用,替代 Python 内置的 wsgiref
PromptTemplateLangChain 提示词模板类,加载本地 txt 文件中的提示词,实现业务规则可配置

2. 全局配置 & 环境变量

python

运行

# 设置LangSmith环境变量:开启LangChain应用跟踪,实时查看模型调用/状态图执行流程
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "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"

# 日志配置(与llms.py一致)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 提示词文件路径:业务规则写在txt文件中,无需改代码即可调整
PROMPT_TEMPLATE_TXT_SYS = "prompt_template_system.txt"  # 系统提示词(套餐规则+回复风格)
PROMPT_TEMPLATE_TXT_USER = "prompt_template_user.txt"    # 用户提示词模板(接收用户问题)

# 模型类型配置
llm_type = "openai"

# API服务端口
PORT = 8012

# 全局变量:存储编译后的LangGraph状态图,供所有接口调用,避免重复初始化
graph = None

LangSmith 作用:LangChain 官方的应用跟踪平台,可实时查看模型调用记录、提示词内容、状态图执行步骤、记忆检索结果,是开发和调试 LangChain/LangGraph 应用的必备工具。

3. Pydantic 数据模型(兼容 OpenAI 接口规范)

严格遵循OpenAI 的 Chat Completions 接口规范定义请求 / 响应模型,保证接口的通用性(可直接对接所有兼容 OpenAI 的前端 / 工具)。

python

运行

# 单条消息模型:对应OpenAI的Message对象
class Message(BaseModel):
    role: str  # 角色:user/assistant/system
    content: str  # 消息内容

# 接口请求模型:对应OpenAI的ChatCompletionRequest
class ChatCompletionRequest(BaseModel):
    messages: List[Message]  # 消息列表,至少包含1条用户消息
    stream: Optional[bool] = False  # 是否流式响应,默认非流式
    userId: Optional[str] = None    # 用户ID,用于长期记忆隔离
    conversationId: Optional[str] = None  # 会话ID,用于短期记忆隔离

# 响应中的选择项模型:对应OpenAI的ChatCompletionResponseChoice
class ChatCompletionResponseChoice(BaseModel):
    index: int  # 选择项索引,固定为0(单模型)
    message: Message  # 模型回复的消息
    finish_reason: Optional[str] = None  # 结束原因:stop=正常结束

# 接口响应模型:对应OpenAI的ChatCompletionResponse
class ChatCompletionResponse(BaseModel):
    # 响应ID:自动生成,格式为chatcmpl-+UUID十六进制
    id: str = Field(default_factory=lambda: f"chatcmpl-{uuid.uuid4().hex}")
    object: str = "chat.completion"  # 固定值,标识响应类型
    created: int = Field(default_factory=lambda: int(time.time()))  # 响应时间戳(秒)
    choices: List[ChatCompletionResponseChoice]  # 选择项列表
    system_fingerprint: Optional[str] = None  # 系统指纹,暂不使用

Field(default_factory=...) :Pydantic 特性,每次创建实例时自动生成值,避免所有响应使用相同的 ID / 时间戳。

4. 核心函数:create_graph(业务化改造的状态图构建)

demoWithMemory.py的基础上,针对流量套餐客服场景做了 3 处核心修改,其余逻辑一致:

python

运行

def create_graph(llm, in_memory_store) -> StateGraph:
    try:
        graph_builder = StateGraph(MessagesState)
        
        # 短期记忆过滤函数(与demo一致)
        def filter_messages(messages: list):
            if len(messages) <= 3:
                return messages
            return messages[-3:]
        
        # 核心chatbot节点(业务化改造核心)
        def chatbot(state: MessagesState, config: RunnableConfig, *, store: BaseStore):
            # 长期记忆命名空间(与demo一致)
            namespace = ("memories", config["configurable"]["user_id"])
            memories = store.search(namespace, query=str(state["messages"][-1].content))
            info = "\n".join([d.value["data"] for d in memories])
            
            # 改造1:系统提示词适配流量套餐客服场景,将记忆用于“指定客服名字”
            system_msg = f"你是一个推荐手机流量套餐的客服代表,你的名字由用户指定。用户指定你的名称为: {info}"
            
            # 改造2:记忆存储内容适配业务,用户说“记住”时,存储“你的名字是南哥”(客服名字)
            last_message = state["messages"][-1]
            if "记住" in last_message.content.lower():
                memory = "你的名字是南哥。"  # 业务化的记忆内容,替代demo的频道信息
                store.put(namespace, str(uuid.uuid4()), {"data": memory})
            
            # 短期记忆(与demo一致)
            messages = filter_messages(state["messages"])
            
            # 调用模型(与demo一致)
            response = llm.invoke(
                [{"role": "system", "content": system_msg}] + messages
            )
            return {"messages": [response]}
        
        # 状态图配置(与demo一致)
        graph_builder.add_node("chatbot", chatbot)
        graph_builder.add_edge(START, "chatbot")
        graph_builder.add_edge("chatbot", END)
        memory = MemorySaver()
        
        # 编译状态图(与demo一致)
        return graph_builder.compile(checkpointer=memory, store=in_memory_store)
    
    except Exception as e:
        raise RuntimeError(f"Failed to create graph: {str(e)}")

业务化改造的核心目的:将通用的记忆 Demo,改造为专用于流量套餐推荐的客服系统,记忆功能仅用于存储 / 检索用户指定的客服名字,贴合实际业务场景。

5. 辅助函数:save_graph_visualization(与 demo 一致,略)

6. 辅助函数:format_response(响应格式化,提升可读性)

核心作用:对大模型的原始输出进行格式化处理,解决模型输出排版混乱、代码块无标记的问题,让前端展示的内容更易读。

python

运行

def format_response(response):
    # 1. 按两个及以上换行符分割段落,解决段落不分隔的问题
    paragraphs = re.split(r'\n{2,}', response)
    formatted_paragraphs = []
    
    # 2. 遍历每个段落进行处理
    for para in paragraphs:
        # 2.1 处理代码块:为代码块添加标准的```包裹,并格式化换行
        if '```' in para:
            parts = para.split('```')  # 按```分割代码块和普通文本
            for i, part in enumerate(parts):
                if i % 2 == 1:  # 奇数索引为代码块内容
                    parts[i] = f"\n```\n{part.strip()}\n```\n"  # 格式化代码块
            para = ''.join(parts)  # 重新拼接
        # 2.2 处理普通文本:句点后换行,解决句子连在一起的问题
        else:
            para = para.replace('. ', '.\n')
        # 2.3 去除段落首尾空白字符,添加到格式化列表
        formatted_paragraphs.append(para.strip())
    
    # 3. 用两个换行符连接所有格式化后的段落,返回最终结果
    return '\n\n'.join(formatted_paragraphs)

处理效果

  • 原始输出:`经济套餐 50 元 10G。畅游套餐 180 元 100G。```python print (123) ````

  • 格式化后:

    plaintext

    经济套餐50元10G。
    畅游套餐180元100G。
    

    print(123)

    生成代码

7. FastAPI 生命周期管理:lifespan

核心作用:实现 FastAPI 的启动初始化关闭清理,启动时完成模型初始化、内存存储初始化、状态图构建、可视化,避免每次接口调用都重复初始化,提升性能。

python

运行

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时执行的初始化逻辑(核心:初始化全局graph变量)
    global graph
    try:
        logger.info("正在初始化模型、定义Graph...")
        # 1. 初始化模型:调用llms.py的get_llm
        llm, embedding = get_llm(llm_type)
        # 2. 初始化长期记忆存储:InMemoryStore
        in_memory_store = InMemoryStore(
            index={
                "embed": embedding,
                "dims": 1536,
            }
        )
        # 3. 构建业务化的状态图
        graph = create_graph(llm, in_memory_store)
        # 4. 状态图可视化
        save_graph_visualization(graph)
        logger.info("初始化完成")
    except Exception as e:
        logger.error(f"初始化过程中出错: {str(e)}")
        raise  # 初始化失败,终止服务启动
    
    yield  # 交出控制权,FastAPI开始运行,处理接口请求
    
    # 关闭时执行的清理逻辑(本系统无特殊清理需求,仅打印日志)
    logger.info("正在关闭...")

@asynccontextmanager:异步上下文管理器,yield之前是启动逻辑,之后是关闭逻辑,是 FastAPI 推荐的生命周期管理方式(替代旧的on_startup/on_shutdown)。

8. 初始化 FastAPI 应用

python

运行

# 将lifespan传入FastAPI,启动时自动执行初始化
app = FastAPI(lifespan=lifespan)

9. 核心接口:/v1/chat/completions(POST)

全局唯一的业务接口,处理所有聊天请求,支持流式 / 非流式响应,是前端 / 测试脚本的核心调用目标,逐行解析核心逻辑

python

运行

@app.post("/v1/chat/completions")
async def chat_completions(request: ChatCompletionRequest):
    # 1. 校验服务是否初始化完成:全局graph为None则返回500错误
    if not graph:
        logger.error("服务未初始化")
        raise HTTPException(status_code=500, detail="服务未初始化")
    
    try:
        logger.info(f"收到聊天完成请求: {request}")
        # 2. 获取用户最新问题:取messages列表的最后一条(用户消息)
        query_prompt = request.messages[-1].content
        logger.info(f"用户问题是: {query_prompt}")
        
        # 3. 构建LangGraph配置:实现用户/会话隔离
        # thread_id:userId@@conversationId,基于用户+会话隔离短期记忆
        # user_id:基于用户隔离长期记忆
        config = {"configurable": {"thread_id": request.userId+"@@"+request.conversationId, "user_id": request.userId}}
        logger.info(f"用户当前会话信息: {config}")
        
        # 4. 加载本地提示词文件,构建最终的prompt
        # 4.1 加载系统提示词(套餐规则+回复风格)
        prompt_template_system = PromptTemplate.from_file(PROMPT_TEMPLATE_TXT_SYS)
        # 4.2 加载用户提示词模板(接收用户问题)
        prompt_template_user = PromptTemplate.from_file(PROMPT_TEMPLATE_USER)
        # 4.3 拼接最终prompt:系统提示词 + 格式化后的用户问题
        prompt = [
            {"role": "system", "content": prompt_template_system.template},
            {"role": "user", "content": prompt_template_user.template.format(query=query_prompt)}
        ]
        
        # ---------------------- 流式响应处理(stream=True) ----------------------
        if request.stream:
            # 定义异步生成器:逐块返回流式响应(SSE格式)
            async def generate_stream():
                # 生成流式响应的唯一ID
                chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
                # 调用graph.astream:异步流式调用,stream_mode="messages":只返回消息块
                async for message_chunk, metadata in graph.astream({"messages": prompt}, config, stream_mode="messages"):
                    chunk = message_chunk.content  # 获取单块内容
                    logger.info(f"chunk: {chunk}")
                    # 按SSE(Server-Sent Events)格式返回:data: + JSON字符串 + \n\n
                    yield f"data: {json.dumps({
                        'id': chunk_id,
                        'object': 'chat.completion.chunk',  # 固定值,标识流式块
                        'created': int(time.time()),
                        'choices': [{
                            'index': 0,
                            'delta': {'content': chunk},  # delta:增量内容
                            'finish_reason': None
                        }]
                    })}\n\n"
                # 流结束:返回最后一个块,delta为空,finish_reason=stop,告知前端流结束
                yield f"data: {json.dumps({
                    'id': chunk_id,
                    'object': 'chat.completion.chunk',
                    'created': int(time.time()),
                    'choices': [{
                        'index': 0,
                        'delta': {},  # 空delta标识流结束
                        'finish_reason': 'stop'
                    }]
                })}\n\n"
            # 返回StreamingResponse:媒体类型为text/event-stream(SSE标准)
            return StreamingResponse(generate_stream(), media_type="text/event-stream")
        
        # ---------------------- 非流式响应处理(stream=False,默认) ----------------------
        else:
            try:
                # 调用graph.stream:同步调用,获取完整响应
                events = graph.stream({"messages": prompt}, config)
                # 遍历响应,获取模型最终回复
                for event in events:
                    for value in event.values():
                        result = value["messages"][-1].content
            except Exception as e:
                logger.info(f"Error processing response: {str(e)}")
            
            # 格式化模型回复,提升可读性
            formatted_response = str(format_response(result))
            logger.info(f"格式化的搜索结果: {formatted_response}")
            
            # 构建标准化的响应对象(基于Pydantic模型)
            response = ChatCompletionResponse(
                choices=[
                    ChatCompletionResponseChoice(
                        index=0,
                        message=Message(role="assistant", content=formatted_response),
                        finish_reason="stop"
                    )
                ]
            )
            logger.info(f"发送响应内容: \n{response}")
            
            # 返回JSON格式响应:将Pydantic模型转为字典(model_dump())
            return JSONResponse(content=response.model_dump())
    
    # 捕获所有业务异常,返回500错误并记录日志
    except Exception as e:
        logger.error(f"处理聊天完成时出错:\n\n {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

流式响应核心原理:采用SSE(Server-Sent Events) 协议,前端通过一次 HTTP 请求建立长连接,后端逐块返回数据,格式为data: {JSON}\n\n,前端监听message事件即可实时渲染,比 WebSocket 更轻量,适合大模型的流式输出场景。非流式响应核心:一次性返回完整的 JSON 数据,适合对实时性要求不高的场景,处理逻辑更简单。

10. 启动服务

python

运行

if __name__ == "__main__":
    logger.info(f"在端口 {PORT} 上启动服务器")
    # 启动uvicorn服务器:host=0.0.0.0(允许外部访问),port=8012
    uvicorn.run(app, host="0.0.0.0", port=PORT)
  • host=0.0.0.0:表示服务器监听所有网络接口,不仅限于本地 127.0.0.1,外部设备(如手机、另一台电脑)可通过 IP 访问该接口。

四、prompt_template_system.txt & prompt_template_user.txt - 业务规则配置(无代码,纯配置)

核心定位:将流量套餐的业务规则模型的回复风格从代码中剥离,写在纯文本文件中,实现业务规则可配置,后续新增 / 修改套餐、调整回复风格,无需改动任何代码,仅需修改 txt 文件,极大提升运维效率。

1. prompt_template_system.txt(系统提示词,核心业务规则)

txt

你可以帮助用户选择最合适的流量套餐产品。可以选择的套餐包括:
经济套餐,月费50元,10G流量;
畅游套餐,月费180元,100G流量;
无限套餐,月费300元,1000G流量;
校园套餐,月费150元,200G流量,仅限在校生。
记住:回复时要求你使用口语,亲切一些。不用说“抱歉”。直接给出回答。NO COMMENTS. NO ACKNOWLEDGEMENTS.

核心规则拆解

  • 业务规则:明确 4 类套餐的月费 + 流量 + 限制条件(校园套餐仅限在校生);
  • 回复风格:口语、亲切,避免生硬;
  • 禁忌规则:不说 “抱歉”、直接回答、无多余评论、无致谢,贴合客服高效回复的场景。

2. prompt_template_user.txt(用户提示词模板,接收问题)

txt

用户问:
{query}

核心作用:为用户问题提供标准化的格式,通过{query}占位符接收用户的实际问题,与PromptTemplate.from_file配合使用,实现用户问题的结构化传入。

五、webUI.py - Gradio 可视化前端(快速使用,无需前端代码)

核心定位:基于 Gradio 快速构建轻量级可视化聊天前端,无需编写 HTML/CSS/JS 代码,一行命令启动,直接调用main.py的 8012 接口,适合开发测试快速演示,非生产级前端(生产级可替换为 Vue/React)。

1. 导入依赖

python

运行

import gradio as gr  # 快速构建可视化前端的核心库
import requests      # 同步HTTP请求库,调用后端8012接口
import json
import logging
import re            # 正则表达式,用于格式化模型输出

2. 全局配置

python

运行

# 日志配置(与其他文件一致)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 后端接口地址(main.py的8012接口)
url = "http://localhost:8012/v1/chat/completions"
# 请求头:固定为application/json,标识请求体为JSON格式
headers = {"Content-Type": "application/json"}

# 默认流式输出:False=非流式,可手动改为True开启流式
stream_flag = False

3. 核心函数:send_message(处理用户输入 + 调用后端 + 更新前端)

Gradio 的核心事件处理函数,用户点击 “发送” 或按回车时触发,生成器函数(yield) 用于实时更新聊天界面,逐行解析: ,逐行解析:

python

运行

def send_message(user_message, history):
    # 1. 封装后端接口的请求参数(与main.py的ChatCompletionRequest模型一致)
    data = {
        "messages": [{"role": "user", "content": user_message}],  # 用户消息
        "stream": stream_flag,  # 是否流式
        "userId": "123",  # 固定用户ID,测试用
        "conversationId": "123"  # 固定会话ID,测试用
    }
    
    # 2. 初始化聊天状态:添加用户消息+“正在生成回复...”的加载状态,实时更新前端
    history = history + [["user", user_message], ["assistant", "正在生成回复..."]]
    yield history  # 向Gradio返回更新后的聊天记录,前端实时渲染
    
    # 3. 定义模型输出格式化函数:适配deepseek-r1模型的输出格式(含思考过程标记)
    def format_response(full_text):
        # 精确替换模型的思考过程标记:        formatted_text = full_text
        formatted_text = re.sub(r'        formatted_text = re.sub(r'', '**思考过程**:\n', formatted_text)
        formatted_text = re.sub(r'', '\n\n**最终回复**:\n', formatted_text)
        return formatted_text.strip()  # 去除首尾空白
    
    # ---------------------- 流式输出处理(stream_flag=True) ----------------------
    if stream_flag:
        assistant_response = ""  # 存储拼接后的模型回复
        try:
            # 发送流式POST请求:stream=True
            with requests.post(url, headers=headers, data=json.dumps(data), stream=True) as response:
                # 逐行遍历流式响应
                for line in response.iter_lines():
                    if line:
                        # 解码并去除SSE的data: 前缀
                        json_str = line.decode('utf-8').strip("data: ")
                        if not json_str:
                            logger.info(f"收到空字符串,跳过...")
                            continue
                        # 校验JSON格式是否合法
                        if json_str.startswith('{') and json_str.endswith('}'):
                            try:
                                response_data = json.loads(json_str)
                                # 获取流式增量内容
                                if 'delta' in response_data['choices'][0]:
                                    content = response_data['choices'][0]['delta'].get('content', '')
                                    # 格式化增量内容
                                    formatted_content = format_response(content)
                                    logger.info(f"接收数据:{formatted_content}")
                                    # 拼接增量内容
                                    assistant_response += formatted_content
                                    # 更新聊天记录:替换加载状态为拼接后的回复
                                    updated_history = history[:-1] + [["assistant", assistant_response]]
                                    yield updated_history  # 实时返回给前端
                                # 流结束:检测到finish_reason=stop,退出循环
                                if response_data.get('choices', [{}])[0].get('finish_reason') == "stop":
                                    logger.info(f"接收JSON数据结束")
                                    break
                            # JSON解析错误处理
                            except json.JSONDecodeError as e:
                                logger.error(f"JSON解析错误: {e}")
                                yield history[:-1] + [["assistant", "解析响应时出错,请稍后再试。"]]
                                break
                        else:
                            logger.info(f"无效JSON格式: {json_str}")
                    else:
                        logger.info(f"收到空行")
                # 循环正常结束但未检测到stop,说明流不完整
                else:
                    logger.info("流式响应结束但未明确结束")
                    yield history[:-1] + [["assistant", "未收到完整响应。"]]
        # 请求异常处理(如网络错误、后端服务未启动)
        except requests.RequestException as e:
            logger.error(f"请求失败: {e}")
            yield history[:-1] + [["assistant", "请求失败,请稍后再试。"]]
    
    # ---------------------- 非流式输出处理(stream_flag=False,默认) ----------------------
    else:
        # 发送非流式POST请求
        response = requests.post(url, headers=headers, data=json.dumps(data))
        # 解析JSON响应
        response_json = response.json()
        # 获取模型完整回复
        assistant_content = response_json['choices'][0]['message']['content']
        # 格式化回复
        formatted_content = format_response(assistant_content)
        logger.info(f"非流式输出,格式化后的内容是: {formatted_content}")
        # 更新聊天记录:替换加载状态为格式化后的回复
        updated_history = history[:-1] + [["assistant", formatted_content]]
        yield updated_history

核心设计

  1. 加载状态:用户发送消息后,立即显示 “正在生成回复...”,提升用户体验,避免前端无响应;
  2. 生成器函数:通过yield实时返回更新后的聊天记录,Gradio 会自动渲染,实现实时更新
  3. 格式化函数:专门适配 deepseek-r1 模型的输出格式(含思考过程标记),将标记替换为更易读的格式;
  4. 异常全覆盖:处理 JSON 解析错误、网络错误、流不完整等异常,保证前端不会崩溃,返回友好的错误提示。

4. 构建 Gradio 前端界面

python

运行

# 初始化Gradio的Blocks布局(比默认布局更灵活)
with gr.Blocks() as demo:
    # 聊天记录组件:Chatbot,标签为“聊天对话”
    chatbot = gr.Chatbot(label="聊天对话")
    # 行布局:包含输入框和发送按钮
    with gr.Row():
        # 列布局:占比8,输入框
        with gr.Column(scale=8):
            message = gr.Textbox(label="请输入消息", placeholder="在此输入您的消息")
        # 列布局:占比2,发送按钮
        with gr.Column(scale=2):
            send = gr.Button("发送")
    
    # 绑定事件:发送按钮点击 → 调用send_message函数
    send.click(send_message, [message, chatbot], chatbot)
    # 绑定事件:输入框按回车 → 调用send_message函数
    message.submit(send_message, [message, chatbot], chatbot)
    # 绑定事件:发送按钮点击 → 清空输入框
    send.click(lambda: "", None, message)
    # 绑定事件:输入框按回车 → 清空输入框
    message.submit(lambda: "", None, message)

界面设计逻辑

  • 采用Blocks 布局:Gradio 的高级布局方式,支持行 / 列嵌套,比默认的gr.Interface更灵活;
  • 行 + 列布局:输入框占 8 份,发送按钮占 2 份,符合视觉习惯;
  • 事件绑定:支持点击发送回车发送,发送后自动清空输入框,提升操作体验。

5. 启动前端

python

运行

if __name__ == "__main__":
    # 启动Gradio服务:监听127.0.0.1,端口7860
    demo.launch(server_name="127.0.0.1", server_port=7860)
  • 启动后访问http://127.0.0.1:7860即可打开聊天界面,直接使用。

六、apiTest.py - 后端接口测试脚本(轻量级调试)

核心定位:无需启动 Gradio 前端,直接通过命令行测试main.py的 8012 接口,适合开发阶段的快速调试,排查接口调用、流式 / 非流式响应、记忆功能等问题,逻辑与webUI.py高度相似,更轻量化。

1. 核心逻辑(与 webUI.py 一致)

  1. 配置后端接口地址、请求头、流式开关;
  2. 定义测试输入文本(可直接修改,测试不同问题);
  3. 封装请求参数(固定 userId=123456,conversationId=123456);
  4. 分别处理流式 / 非流式响应,打印日志和响应内容;
  5. 捕获异常,打印错误信息。

2. 关键区别

  1. 无前端界面,所有结果通过日志打印到命令行;
  2. 无聊天记录管理,仅测试单次接口调用;
  3. 无输出格式化,直接打印模型原始输出;
  4. 测试文本可直接注释 / 取消注释,快速切换测试用例。

3. 测试用例设计

脚本中预设了 6 个测试用例,覆盖核心业务场景:

  1. 记住你的名字是南哥。 → 测试长期记忆存储
  2. 200元以下,流量大的套餐有啥? → 测试套餐推荐核心业务
  3. 你叫什么名字? → 测试长期记忆检索
  4. 就刚刚提到的这个套餐,是多少钱? → 测试短期记忆(上下文理解)
  5. 有没有豪华套餐? → 测试未知套餐处理
  6. 重复上述用例 → 测试记忆的持久性

七、各文件核心关联与完整运行流程

1. 文件核心关联

表格

文件名依赖文件核心输出 / 提供能力
llms.py标准化的聊天模型 + 嵌入模型实例
demoWithMemory.pyllms.py记忆逻辑 Demo,验证长 / 短期记忆有效性
prompt_template_*.txt业务规则配置,提供提示词模板
main.pyllms.py、prompt_*.txtFastAPI 后端服务,8012 接口,业务化记忆
webUI.pymain.pyGradio 可视化前端,调用 8012 接口
apiTest.pymain.py命令行测试脚本,调试 8012 接口

2. 完整运行流程(生产 / 演示场景)

  1. 启动后端:运行main.py → 初始化模型 → 构建状态图 → 启动 FastAPI 服务(8012 端口);

  2. 启动前端:运行webUI.py → 启动 Gradio 服务(7860 端口);

  3. 用户交互:打开http://127.0.0.1:7860 → 输入问题(如 “200 元以下流量大的套餐有啥?”);

  4. 前端调用后端:webUI.py 封装请求 → 调用http://localhost:8012/v1/chat/completions

  5. 后端处理

    • 加载提示词模板,拼接系统 + 用户提示词;
    • 检索用户长期记忆(如有),拼接到系统提示词;
    • 过滤短期记忆(保留最后 3 条);
    • 调用大模型生成回复;
    • 格式化回复,按流式 / 非流式返回;
  6. 前端渲染:接收后端响应 → 实时更新聊天界面 → 展示模型回复。

3. 调试流程(开发阶段)

  1. 运行llms.py → 测试模型初始化是否正常;
  2. 运行demoWithMemory.py → 测试长 / 短期记忆逻辑是否正常;
  3. 运行main.py → 启动后端服务;
  4. 运行apiTest.py → 命令行测试后端接口是否正常;
  5. 运行webUI.py → 可视化测试整体流程。

八、系统核心设计亮点与生产级优化建议

1. 核心设计亮点

  • 极致解耦:模型、业务逻辑、前端、测试完全分离,单一职责,便于维护和扩展;
  • 接口标准化:完全兼容 OpenAI 的 Chat Completions 接口,可直接对接所有兼容 OpenAI 的前端 / 工具;
  • 记忆分层:长期记忆(用户专属信息)+ 短期记忆(对话上下文),贴合人类交流习惯,支持用户 / 会话隔离;
  • 可配置化:模型配置、业务规则、服务端口均通过配置 / 文件定义,无需改代码即可调整;
  • 异常全覆盖:关键步骤均做异常捕获和处理,返回友好的错误提示,提升服务鲁棒性;
  • 可视化:LangGraph 状态图可视化、Gradio 前端可视化,降低开发和调试成本;
  • 异步高并发:FastAPI + 异步接口,支持高并发请求,适合生产级部署。

2. 生产级优化建议

(1)记忆持久化
  • 长期记忆:将InMemoryStore替换为Pinecone/Chroma/Milvus/FAISS等向量数据库,服务重启后记忆不丢失;
  • 短期记忆:将MemorySaver替换为Redis/PostgreSQL等持久化存储,支持会话记录回溯;
  • 对话记录:将用户对话记录存入MySQL/MongoDB,支持聊天记录查询、导出、分析。
(2)服务增强
  • 接口鉴权:添加 API Key/Token/JWT 鉴权,防止接口被恶意调用;
  • 请求限流:添加接口限流(如 Redis + 令牌桶),防止高并发压垮服务;
  • 日志持久化:将日志写入ELK/ Loki,支持日志检索、分析、告警;
  • 配置中心:将模型配置、服务端口、提示词路径等放入Nacos/Apollo配置中心,无需重启服务即可修改配置;
  • 容器化部署:将服务打包为Docker 镜像,配合 K8s 实现容器化部署、扩缩容、故障自愈。
(3)功能扩展
  • 多轮上下文优化:将短期记忆的消息过滤逻辑改为基于 token 数的过滤(而非固定 3 条),更灵活;
  • 套餐动态配置:将套餐信息从提示词移到数据库 / 配置中心,支持动态增删改查套餐;
  • 用户管理:添加用户登录、注册、权限管理,支持多用户独立使用;
  • 会话管理:前端添加会话列表、会话重命名、会话删除,支持多会话切换;
  • 前端美化:将 Gradio 前端替换为Vue/React/Uniapp开发的专业前端,提升用户体验。
(4)性能优化
  • 模型缓存:对高频问题的回复进行缓存(如 Redis),避免重复调用模型,提升响应速度;
  • 嵌入模型优化:使用本地嵌入模型(如 BGE)替代云端模型,降低调用成本,提升检索速度;
  • 异步优化:将所有同步操作(如文件读取、数据库操作)改为异步,提升服务并发能力;
  • 模型量化:若使用本地模型,对模型进行4/8bit 量化,降低显存占用,提升推理速度。

这套代码是大模型落地实际业务(客服) 的优秀原型,整合了当前主流的大模型开发技术(LangGraph/LangChain、FastAPI、向量检索),结构清晰、解耦性好,经过上述优化后,可直接用于生产级部署。