从零到一手动搭建RAG系统

项目介绍

项目背景

随着美团业务的不断扩展,客服人员需要应对海量的用户咨询,包括订单问题、退款流程、配送异常、优惠政策等。传统的知识库客服系统依赖规则匹配,回答僵硬,难以及时覆盖最新的业务规则。

项目功能

为提升客户体验和客服效率,本项目基于 RAG(Retrieval-Augmented Generation,检索增强生成) 技术构建智能客服问答系统,将美团内部文档知识与大语言模型结合,实现更智能、更准确的自动化答复。

项目实现

文档收集

收集美团客服相关知识文档,例如:

  • 业务手册(退款规则、订单处理流程)
  • 常见问题 FAQ
  • 内部客服知识库
  • 实时更新的运营公告

以美团外卖常见问题为例,文档地址:https://waimai.meituan.com/help/faq,我们通过playwright 工具爬虫获取页面数据并写入本地 txt 文件中。

安装依赖包

# 安装浏览器插件库
pip install playwright chromium
playwright install
# 安装浏览器中文依赖
sudo apt update && sudo apt install fonts-wqy-zenhei fonts-wqy-microhei -y

代码如下:

from playwright.sync_api import sync_playwright

def collect_faq(url):
    """
    收集指定URL页面中的FAQ内容
    
    参数:
        url (str): 目标网页URL地址
        
    返回:
        str: 提取的FAQ文本内容
    """
    # 启动Playwright浏览器自动化工具
    with sync_playwright() as p:
        # 启动Chromium浏览器,设置为非无头模式并指定中文语言
        browser = p.chromium.launch(
            headless=False,
            args=['--lang=zh-CN']  # 浏览器语言
        )
        # 创建新页面,配置中文环境
        page = browser.new_page(
            locale='zh-CN',  # 页面 locale
            user_agent=(
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"
            ),
            extra_http_headers={
                "Accept-Language": "zh-CN,zh;q=0.9"
            }
        )
        # 访问目标URL并等待页面加载完成
        page.goto(url, timeout=30_000)
        page.wait_for_load_state("networkidle")

        # 提取FAQ列表区域的文本内容
        raw_text = page.locator("#faq-list").first.text_content()
        browser.close()
        return raw_text

def save_faq(cleaned_text:str, output_file:str):
    """
    将FAQ文本内容保存到指定文件
    
    参数:
        cleaned_text (str): 要保存的FAQ文本内容
        output_file (str): 输出文件路径
    """
    # 写入文件
    with open(output_file, "w", encoding="utf-8") as f:
        f.write(cleaned_text)

    print(f"FAQ 已保存到 {output_file}")

if __name__ == "__main__":
    cleaned_text = collect_faq(url="https://waimai.meituan.com/help/faq")
    output_file = "faq.txt"
    save_faq(cleaned_text, output_file)

执行结果如下:

# head -n 20 faq.txt           

      
        
          在线支付问题
          
        
        
          
            Q:在线支付取消订单后钱怎么返还?
            
              订单取消后,款项会在一个工作日内,直接返还到您的美团账户余额。
            
          
        
        
          
            Q:怎么查看退款是否成功?
            
              退款会在一个工作日之内到美团账户余额,可在“账号管理——我的账号”中查看是否到账

文档处理

我们已经爬取了 FAQ 文档,接下来就需要对收集到的文档进行统一处理,内容包括:

  • 文本清洗(去除 HTML 标签、无关字符)
  • 分段切分(按规则或语义将文档拆分成小片段,便于检索)
  • 元数据标注(来源、时间、业务类别等)。

代码如下:

import re
import json
from pathlib import Path
from datetime import datetime, timezone

def clean_text(text: str) -> str:
    """
    清洗文本内容,去除HTML标签和多余空格及无效字符。

    参数:
        text (str): 需要清洗的原始文本。

    返回:
        str: 清洗后的文本内容。
    """
    # 去掉 HTML 标签(如果有残留)
    text = re.sub(r"<.*?>", "", text)
    # 去掉多余空格并过滤空行
    lines = [line.strip() for line in text.splitlines() if line.strip()]
    return "\n".join(lines)

def split_faq(text: str):
    """
    根据 Q/A 规则将FAQ文本切分为问题和答案对。

    参数:
        text (str): 包含FAQ内容的文本字符串。

    返回:
        list[dict]: 每个元素是一个包含"question"和"answer"键的字典。
    """
    # 按 Q: 或 Q: 分割文本
    parts = re.split(r"(?:^|\n)Q[::]", text)
    qa_pairs = []
    for part in parts:
        part = part.strip()
        if not part:
            continue
        # 将第一行作为问题,其余部分作为答案
        lines = part.splitlines()
        question = lines[0]
        answer = "\n".join(lines[1:]) if len(lines) > 1 else ""
        qa_pairs.append({
            "question": question,
            "answer": answer
        })
    return qa_pairs

def process_faq(input_file: str, output_file: str, source_url: str, category="FAQ"):
    """
    处理FAQ文本文件,清洗、分割并添加元数据后保存为JSON格式。

    参数:
        input_file (str): 输入的原始FAQ文本文件路径。
        output_file (str): 输出处理后的JSON文件路径。
        source_url (str): 数据来源URL。
        category (str): FAQ分类,默认为"FAQ"。

    返回:
        None
    """
    raw_text = Path(input_file).read_text(encoding="utf-8")
    cleaned_text = clean_text(raw_text)
    qa_pairs = split_faq(cleaned_text)

    # 添加元数据信息
    now = datetime.now(timezone.utc).isoformat()
    processed = []
    for qa in qa_pairs:
        processed.append({
            "question": qa["question"],
            "answer": qa["answer"],
            "metadata": {
                "source": source_url,
                "category": category,
                "crawl_time": now
            }
        })

    Path(output_file).write_text(
        json.dumps(processed, ensure_ascii=False, indent=2),
        encoding="utf-8"
    )
    print(f"✅ 已处理 {len(processed)} 条 FAQ,结果保存到 {output_file}")

if __name__ == "__main__":
    process_faq(
        input_file="faq.txt",
        output_file="faq_processed.json",
        source_url="https://waimai.meituan.com/help/faq",
        category="支付问题"
    )

执行后的效果如下

# head -n 20 faq_processed.json
[
  {
    "question": "在线支付问题",
    "answer": "",
    "metadata": {
      "source": "https://waimai.meituan.com/help/faq",
      "category": "支付问题",
      "crawl_time": "2025-09-04T02:38:28.261319+00:00"
    }
  },
  {
    "question": "在线支付取消订单后钱怎么返还?",
    "answer": "订单取消后,款项会在一个工作日内,直接返还到您的美团账户余额。",
    "metadata": {
      "source": "https://waimai.meituan.com/help/faq",
      "category": "支付问题",
      "crawl_time": "2025-09-04T02:38:28.261319+00:00"
    }
  },
  {

文档数据向量化

我们将 FAQ 数据格式化成 json 数据后,接下来就要转成向量数据并存储到向量数据库中,此处以 redis 为例,操作内容包括:

  • 使用 向量化模型(Embedding Model,如 BGE、OpenAI Embedding) 将文档片段转换为向量表示。
  • 存储至向量数据库(如 Milvus、Weaviate、Redis Vector、Faiss),支持高效的相似度搜索。

代码如下

import os
import json
import dotenv
import dashscope
import redis
import numpy as np
from http import HTTPStatus
from redis.commands.search.field import TextField, VectorField
from redis.commands.search.index_definition import IndexDefinition

# ========== 配置 ==========
# 加载环境变量
dotenv.load_dotenv()
# 设置 DashScope API Key
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

# 定义索引名称、向量维度和距离度量方式
INDEX_NAME = "faq_index"
VECTOR_DIM = 1024
DISTANCE_METRIC = "COSINE"

# 初始化 Redis 客户端连接
redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password=None,
    decode_responses=False
)

# ========== 创建索引(只执行一次) ==========
def create_index():
    """
    创建 Redis 向量搜索索引。
    
    如果索引已存在,则跳过创建并提示信息;
    否则根据预定义的字段结构创建一个新的索引,用于支持 FAQ 的文本与向量混合检索。
    """
    try:
        redis_client.ft(INDEX_NAME).info()
        print("✅ 索引已存在")
    except Exception:
        redis_client.ft(INDEX_NAME).create_index(
            [
                TextField("question"),
                TextField("answer"),
                TextField("source"),
                TextField("category"),
                TextField("crawl_time"),
                VectorField(
                    "embedding",
                    "HNSW",
                    {"TYPE": "FLOAT32", "DIM": VECTOR_DIM, "DISTANCE_METRIC": DISTANCE_METRIC}
                )
            ],
            definition=IndexDefinition(prefix=["faq:"])
        )
        print("✅ 已创建向量索引")

# ========== 插入一条 FAQ ==========
def insert_faq(doc: dict):
    """
    将单条 FAQ 数据插入 Redis,并生成对应的文本嵌入向量。

    参数:
        doc (dict): 包含问题、答案及元数据的字典。
            - question (str): 问题内容
            - answer (str): 回答内容
            - metadata (dict): 元数据,包括 source, category, crawl_time 等字段

    返回值:
        无返回值。结果通过打印输出表示操作是否成功。
    """
    # 拼接问题和答案作为嵌入模型的输入文本
    text_for_embedding = doc["question"] + " " + doc["answer"]

    # 调用 DashScope 多模态嵌入模型获取向量表示
    resp = dashscope.MultiModalEmbedding.call(
        model="multimodal-embedding-v1",
        input=[{"text": text_for_embedding}]
    )

    if resp.status_code == HTTPStatus.OK:
        # 提取嵌入向量并转换为字节格式以便存储到 Redis 中
        embedding = resp.output["embeddings"][0]["embedding"]
        vector = np.array(embedding, dtype=np.float32).tobytes()

        # 构造 Redis 键名
        key = f"faq:{resp.request_id}"
        # 存储 FAQ 数据及其向量表示到 Redis Hash 结构中
        redis_client.hset(key, mapping={
            "question": doc["question"],
            "answer": doc["answer"],
            "source": doc["metadata"]["source"],
            "category": doc["metadata"]["category"],
            "crawl_time": doc["metadata"]["crawl_time"],
            "embedding": vector
        })
        print(f"✅ 已写入 Redis, key={key}")
    else:
        print(f"❌ Embedding 调用失败: {resp.code}, {resp.message}")

# ========== 批量处理 ==========
def insert_from_file(file_path="faq_processed.json"):
    """
    从指定 JSON 文件中读取 FAQ 数据并逐条插入 Redis。

    参数:
        file_path (str): JSON 格式的 FAQ 数据文件路径,默认为 "faq_processed.json"

    返回值:
        无返回值。每条数据插入后会打印状态信息。
    """
    with open(file_path, "r", encoding="utf-8") as f:
        docs = json.load(f)

    for doc in docs:
        insert_faq(doc)

if __name__ == "__main__":
    # 程序入口:先创建索引再批量插入数据
    create_index()
    insert_from_file("faq_processed.json")

查看 redis 数据内容

在这里插入图片描述

文档数据相似性检索

文档向量数据写入数据库后,接下来就是测试验证召回数据准确性,主要内容包括:

  • 用户提问后,将问题转换为向量,与向量数据库中的文档进行相似性匹配。
  • 召回与问题最相关的文档片段(如退款流程、配送延误规则),并返回给上层系统。

代码如下:

import os
import dotenv
import dashscope
import redis
import numpy as np
from http import HTTPStatus
from redis.commands.search.query import Query

# ========== 配置 ==========
# 加载环境变量
dotenv.load_dotenv()
# 设置 DashScope API 密钥
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

# Redis 向量索引名称
INDEX_NAME = "faq_index"
# 向量维度,用于模型 "multimodal-embedding-v1"
VECTOR_DIM = 1024
# 默认返回最相似的前 K 条结果
TOP_K = 3

# 初始化 Redis 客户端连接
redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password=None,
    decode_responses=False
)

# ========== 将问题转为向量 ==========
def embed_question(question: str):
    """
    使用 DashScope 的多模态嵌入模型将文本问题转换为向量表示。

    参数:
        question (str): 需要转换为向量的文本问题。

    返回:
        bytes: 问题对应的向量表示(以字节形式返回)。

    异常:
        RuntimeError: 如果调用嵌入服务失败,则抛出运行时错误。
    """
    resp = dashscope.MultiModalEmbedding.call(
        model="multimodal-embedding-v1",
        input=[{"text": question}]
    )
    if resp.status_code == HTTPStatus.OK:
        embedding = resp.output["embeddings"][0]["embedding"]
        return np.array(embedding, dtype=np.float32).tobytes()
    else:
        raise RuntimeError(f"❌ Embedding 调用失败: {resp.code}, {resp.message}")

# ========== 相似度搜索 ==========
def search_faq(question: str, top_k=TOP_K):
    """
    根据用户输入的问题,在 Redis 中进行向量相似度搜索,返回最相关的 FAQ 条目。

    参数:
        question (str): 用户提出的问题。
        top_k (int): 返回最相似的前 K 条结果,默认值为 TOP_K。
    """
    # 将问题转换为向量表示
    q_vector = embed_question(question)

    # 构造 RediSearch 的 KNN 查询语句
    query = (
        Query(f"*=>[KNN {top_k} @embedding $vec AS score]")
        .sort_by("score")
        .return_fields("question", "answer", "source", "category", "crawl_time", "score")
        .dialect(2)
    )

    # 执行查询并获取结果
    results = redis_client.ft(INDEX_NAME).search(query, query_params={"vec": q_vector})

    print(f"\n🔎 用户问题: {question}")
    print(f"📊 召回 {len(results.docs)} 条结果\n")

    # 打印每条匹配结果的详细信息
    for i, doc in enumerate(results.docs, start=1):
        print(f"--- Top {i} ---")
        print(f"相似度分数: {doc.score}")
        print(f"Q: {doc.question}")
        print(f"A: {doc.answer}")
        print(f"来源: {doc.source}")
        print(f"类别: {doc.category}")
        print(f"时间: {doc.crawl_time}")
        print()

# ========== 主函数 ==========
if __name__ == "__main__":
    # 测试用例:模拟用户提问
    test_question = "为什么会出现无法下单的情况?"
    search_faq(test_question, top_k=3)

执行结果如下

🔎 用户问题: 为什么会出现无法下单的情况?
📊 召回 3 条结果

--- Top 1 ---
相似度分数: 0.114289164543
Q: 为什么会出现无法下单的情况?
A: 无法下单有很多情况,可能是菜品售完、餐厅不在营业时间等,请查看无法下单时给的提示。
来源: https://waimai.meituan.com/help/faq
类别: 支付问题
时间: 2025-09-04T02:38:28.261319+00:00

--- Top 2 ---
相似度分数: 0.13062286377
Q: 刚下单发现信息填错了怎么办?
A: 如果商家尚未接单,您可以自主取消订单;如果商家已经接单,您可以电话联系商家后由对方取消订单。然后重新下一单。
来源: https://waimai.meituan.com/help/faq
类别: 支付问题
时间: 2025-09-04T02:38:28.261319+00:00

--- Top 3 ---
相似度分数: 0.138350009918
Q: 为什么提示下单次数过多,已无法下单?
A: 同一手机号在同一设备上一天最多可以成功提交7次订单(在线支付以完成支付为准,货到付款以提交订单为准)。
其他问题
来源: https://waimai.meituan.com/help/faq
类别: 支付问题
时间: 2025-09-04T02:38:28.261319+00:00

构建提示词

  • 用户问题 + 检索召回的上下文 拼接成一个高质量的 Prompt 送给大模型。
  • 提示词示例:
你是一个智能问答助手,请仅根据提供的文档片段回答用户问题。
如果文档片段中没有相关内容,请回答“未找到相关信息”。

用户问题:
取消订单后多久能收到退款?

可用文档片段:
【文档片段1】
Q: 在线支付取消订单后钱怎么返还?
A: 订单取消后,款项会在一个工作日内,直接返还到您的美团账户余额。

【文档片段2】
Q: 怎么查看退款是否成功?
A: 退款会在一个工作日之内到美团账户余额,可在“账号管理——我的账号”中查看是否到账。

请基于以上信息,生成简洁明了的回答:

提示词代码如下:

import os
import dotenv
import dashscope
import redis
import numpy as np
from http import HTTPStatus
from redis.commands.search.query import Query

# ========== 配置 ==========
# 加载环境变量
dotenv.load_dotenv()
# 设置 DashScope API Key
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

# Redis 向量索引名称
INDEX_NAME = "faq_index"
# 向量维度
VECTOR_DIM = 1024
# 相似度搜索返回的最相似结果数量
TOP_K = 3

# 初始化 Redis 客户端连接
redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password=None,
    decode_responses=False
)

# ========== 将问题转为向量 ==========
def embed_question(question: str):
    """
    使用 DashScope 的多模态嵌入模型将文本问题转换为向量表示。

    参数:
        question (str): 用户输入的问题文本。

    返回:
        bytes: 问题对应的向量表示(以字节形式存储)。

    异常:
        RuntimeError: 当调用嵌入服务失败时抛出异常。
    """
    resp = dashscope.MultiModalEmbedding.call(
        model="multimodal-embedding-v1",
        input=[{"text": question}]
    )
    if resp.status_code == HTTPStatus.OK:
        embedding = resp.output["embeddings"][0]["embedding"]
        return np.array(embedding, dtype=np.float32).tobytes()
    else:
        raise RuntimeError(f"❌ Embedding 调用失败: {resp.code}, {resp.message}")

# ========== 相似度搜索 ==========
def search_faq(question: str, top_k=TOP_K):
    """
    在 Redis 中基于向量相似度搜索与用户问题最相关的 FAQ 文档。

    参数:
        question (str): 用户提出的问题。
        top_k (int): 返回最相似的前 K 个文档,默认使用 TOP_K 常量。

    返回:
        list: 包含匹配文档对象的列表,每个对象包含字段如 question、answer、source 等。
    """
    q_vector = embed_question(question)

    # 构造 Redis 向量搜索查询语句
    query = (
        Query(f"*=>[KNN {top_k} @embedding $vec AS score]")
        .sort_by("score")
        .return_fields("question", "answer", "source", "category", "crawl_time", "score")
        .dialect(2)
    )

    # 执行搜索并返回结果
    results = redis_client.ft(INDEX_NAME).search(query, query_params={"vec": q_vector})
    return results.docs

# ========== 构建 Prompt ==========
def build_prompt(user_question: str, retrieved_docs, top_k=TOP_K) -> str:
    """
    根据用户问题和检索到的相关文档构建用于大模型推理的 Prompt。

    参数:
        user_question (str): 用户提出的问题。
        retrieved_docs (list): 检索到的相关文档列表。
        top_k (int): 使用的文档数量上限,默认为 TOP_K。

    返回:
        str: 构建完成的 Prompt 字符串。
    """
    context_parts = []
    for i, doc in enumerate(retrieved_docs[:top_k], start=1):
        context_parts.append(
            f"【文档片段{i}】\nQ: {doc.question}\nA: {doc.answer}"
        )
    context_text = "\n\n".join(context_parts)

    prompt = f"""
你是一个智能问答助手,请仅根据提供的文档片段回答用户问题。
如果文档片段中没有相关内容,请回答“未找到相关信息”。

用户问题:
{user_question}

可用文档片段:
{context_text}

请基于以上信息,生成简洁明了的回答:
"""
    return prompt.strip()

# ========== 主函数 ==========
if __name__ == "__main__":
    # 循环接收用户输入并进行问答处理
    while True:
        user_question = input("\n请输入问题(输入 exit 退出):")
        if user_question.lower() in ["exit", "quit"]:
            break

        docs = search_faq(user_question, top_k=TOP_K)
        if not docs:
            print("⚠️ 未检索到相关文档")
            continue

        prompt = build_prompt(user_question, docs)
        print("\n===== 构建的 Prompt =====\n")
        print(prompt)
        print("\n=========================\n")

执行结果如下

请输入问题(输入 exit 退出):为什么会出现无法下单的情况

===== 构建的 Prompt =====

你是一个智能问答助手,请仅根据提供的文档片段回答用户问题。
如果文档片段中没有相关内容,请回答“未找到相关信息”。

用户问题:
为什么会出现无法下单的情况

可用文档片段:
【文档片段1】
Q: 为什么会出现无法下单的情况?
A: 无法下单有很多情况,可能是菜品售完、餐厅不在营业时间等,请查看无法下单时给的提示。

【文档片段2】
Q: 刚下单发现信息填错了怎么办?
A: 如果商家尚未接单,您可以自主取消订单;如果商家已经接单,您可以电话联系商家后由对方取消订单。然后重新下一单。

【文档片段3】
Q: 为什么提示下单次数过多,已无法下单?
A: 同一手机号在同一设备上一天最多可以成功提交7次订单(在线支付以完成支付为准,货到付款以提交订单为准)。
其他问题

请基于以上信息,生成简洁明了的回答:

=========================


请输入问题(输入 exit 退出):exit

大语言模型生成结果

提示词构建完成验证无误后,接下来调用大模型对结果进行回答既可

  • 调用大语言模型(如 DeepSeek、ChatGPT、通义千问等)生成自然语言回答。

代码如下:

import os
import dotenv
import dashscope
import redis
import numpy as np
from http import HTTPStatus
from redis.commands.search.query import Query
from openai import OpenAI

# ========== 配置 ==========
dotenv.load_dotenv()
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

INDEX_NAME = "faq_index"
VECTOR_DIM = 1024
TOP_K = 3

redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password=None,
    decode_responses=False
)

# 初始化 OpenAI 客户端(兼容 DashScope)
client = OpenAI(
    api_key=os.getenv("BAILIAN_API_KEY"),
    base_url=os.getenv("BAILIAN_BASE_URL")
)

# ========== 将问题转为向量 ==========
def embed_question(question: str):
    resp = dashscope.MultiModalEmbedding.call(
        model="multimodal-embedding-v1",
        input=[{"text": question}]
    )
    if resp.status_code == HTTPStatus.OK:
        embedding = resp.output["embeddings"][0]["embedding"]
        return np.array(embedding, dtype=np.float32).tobytes()
    else:
        raise RuntimeError(f"❌ Embedding 调用失败: {resp.code}, {resp.message}")

# ========== 相似度搜索 ==========
def search_faq(question: str, top_k=TOP_K):
    q_vector = embed_question(question)

    query = (
        Query(f"*=>[KNN {top_k} @embedding $vec AS score]")
        .sort_by("score")
        .return_fields("question", "answer", "source", "category", "crawl_time", "score")
        .dialect(2)
    )

    results = redis_client.ft(INDEX_NAME).search(query, query_params={"vec": q_vector})
    return results.docs

# ========== 构建 Prompt ==========
def build_prompt(user_question: str, retrieved_docs, top_k=TOP_K) -> str:
    context_parts = []
    for i, doc in enumerate(retrieved_docs[:top_k], start=1):
        context_parts.append(
            f"【文档片段{i}】\nQ: {doc.question}\nA: {doc.answer}"
        )
    context_text = "\n\n".join(context_parts)

    prompt = f"""
    你是一个智能问答助手,请仅根据提供的文档片段回答用户问题。
    如果文档片段中没有相关内容,请回答“未找到相关信息”。
    
    用户问题:
    {user_question}
    
    可用文档片段:
    {context_text}
    
    请基于以上信息,生成简洁明了的回答:
    """
    return prompt.strip()

# ========== 调用大模型 ==========
def ask_llm(prompt: str) -> str:
    completion = client.chat.completions.create(
        model="deepseek-r1-distill-llama-70b",  # 也可以换成 qwen-turbo / qwen-plus 等
        messages=[{"role": "user", "content": prompt}]
    )

    # 输出最终答案
    return completion.choices[0].message.content

# ========== 主程序 ==========
if __name__ == "__main__":
    while True:
        user_question = input("\n请输入问题(输入 exit 退出):")
        if user_question.lower() in ["exit", "quit"]:
            break

        docs = search_faq(user_question, top_k=TOP_K)
        if not docs:
            print("⚠️ 未检索到相关文档")
            continue

        prompt = build_prompt(user_question, docs)
        # print("\n===== 构建的 Prompt =====\n")
        # print(prompt)
        # print("\n=========================\n")

        answer = ask_llm(prompt)
        print("💡 大模型回答:")
        print(answer)

执行结果如下

请输入问题(输入 exit 退出):为什么会出现无法下单的情况
💡 大模型回答:
无法下单的情况可能是由于菜品售完、餐厅不在营业时间等原因。请查看下单时的提示信息以获取具体原因。
en-turbo / qwen-plus 等
        messages=[{"role": "user", "content": prompt}]
    )

    # 输出最终答案
    return completion.choices[0].message.content

# ========== 主程序 ==========
if __name__ == "__main__":
    while True:
        user_question = input("\n请输入问题(输入 exit 退出):")
        if user_question.lower() in ["exit", "quit"]:
            break

        docs = search_faq(user_question, top_k=TOP_K)
        if not docs:
            print("⚠️ 未检索到相关文档")
            continue

        prompt = build_prompt(user_question, docs)
        # print("\n===== 构建的 Prompt =====\n")
        # print(prompt)
        # print("\n=========================\n")

        answer = ask_llm(prompt)
        print("💡 大模型回答:")
        print(answer)

执行结果如下

请输入问题(输入 exit 退出):为什么会出现无法下单的情况
💡 大模型回答:
无法下单的情况可能是由于菜品售完、餐厅不在营业时间等原因。请查看下单时的提示信息以获取具体原因。
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐