ai的奇幻漂流

写在前面

​ 在大学的时候,我一直对机器学习、人工智能之类的课程不怎么喜欢。一部分原因是授课老师讲的过于无聊,更重要的原因是ai存在明显的不确定性。当执行一段C语言代码时,再怎么样程序的功能都是可被理解的,你对于一行代码执行后会有什么样的表现拥有基本的预期,尽管存在多线程、内核抢占、编译优化、CPU乱序发射,底层硬件故障等一些不确定性,但你对它们的存在也是了解的,至少可以假装认为自己了解。但ai的执行结果就完全是个谜了,机器学习中的一系列参数的含义完全未知,为什么这个参数比较好,那个参数比较差,本身就很难彻底说明白,因此当时我们都叫搞ai的叫太白金星,整天炼丹调参。

​ 之前的程序都是从原理上,从逻辑上,从结构上仔细地拆分,解决问题;但ai看起来是直接从结果上来解决问题。从一个强大的模型、神经网络开始,尝试解决这个问题并计算结果的准确程度,然后一直往更加准确的方向前进。但这种方式的问题是:即使ai对一个问题的准确度一直是100%,也无法确保下一次同类型问题同样正确,虽然非ai方法也不一定真的能保证完全正确,但由于对“底层原理”的偏执, 我还是更加喜欢非ai的范式,猜字谜”的行为模式本身并不能说服我。

​ 但在2026年的这个时刻,不得不承认的事实就是:ai一定会改变世界!之前使用chatgpt、qwen等一些对话模型时,虽然能感觉到ai很强大,但没有那种惊叹的感觉(我好像非常喜欢用感觉这两个字)。直到我使用cursor生成前后端页面,我本来只是随便写一个简短的提示词试一试,后续再研究研究如何修改,毕竟在大学的时候我还是用过一段时间的vue,没想到它生成代码后就能执行运行,而且结果完全符合我的预期,这个时刻我就知道,我过几年就要被ai取代掉了,至少过几年后,我就完全不需要自己写代码了,是不是所有人到头来都是提示词工程师?最近的几个月, 我已经不怎么自己写代码了, 都是让ai生成后直接用(实际上没怎么review),这也带来很大的不确定性,但目前看起来没出什么问题,所以整个系统就是建立在不确定性之上的? 我从之前的如何写好一段代码变成了如何写好一段提示词,希望我后面能学会如何review一段ai代码,完全放飞也不太好(#^.^#)

​ 计算机领域好像只有ai可以称得上改变世界,如果说“SSD改变了世界!存储改变了世界!Linux改变了世界!”都有点违和,但ai

与一个伟大的技术挂钩

与改变世界的技术挂钩

黑客

伦理

与人类的区别

冯洛伊曼架构

绝对正确的无聊

ai味

推荐阅读/观看

莱姆狂想曲—泥人十四

时空奇旅

机器人之梦(封面)

ps:使用小写的ai感觉更加亲切,随和,大写的AI就更加刻板,严肃,官方。一个小发现~

初见

RAG、提示词工程、上下文工程、token、mcp、fuction call、skills、ai agent、agi、训练与推理、上下文压缩与遗忘、kv cache等一些花里胡哨的词汇

第一步:购买云服务器部署一个模型进行一次对话

购买 ecs.gn6v-c8g1.4xlarge实例,选择Alibaba Cloud Linux 3.2104 LTS 64位 预装NVIDIA GPU驱动和 CUDA,配置密钥对后通过vscode远程登录

安装依赖包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 升级python版本
yum install python3.11
sudo update-alternatives --config python


[root@iZbp1bzvlb6th4j2vwt9taZ ~]# python --version
Python 3.11.13

# 创建虚拟环境(推荐)
python -m venv qwen-env
source qwen-env/bin/activate

# 升级 pip 并安装依赖
pip install --upgrade pip
# 安装 PyTorch(使用阿里云 PyTorch 镜像)
pip install torch torchvision torchaudio \
--index-url https://mirrors.aliyun.com/pytorch-wheels/cu121/ \
--extra-index-url https://mirrors.aliyun.com/pypi/simple/ \
--trusted-host mirrors.aliyun.com

# 安装其他包(使用阿里云 PyPI 镜像)
pip install transformers accelerate bitsandbytes sentencepiece \
--index-url https://mirrors.aliyun.com/pypi/simple/ \
--trusted-host mirrors.aliyun.com

运行demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# -*- coding: utf-8 -*-
"""
Qwen2.5-7B-Instruct 4-bit 量化推理示例(完整注释版)
功能:加载通义千问 7B 指令微调模型,进行一次对话生成
适用环境:Linux + CUDA + 双 Tesla V100(16GB×2)
"""

# ==============================
# 第一部分:导入必要库
# ==============================

# 从 Hugging Face Transformers 库导入核心组件
from transformers import (
AutoTokenizer, # 自动加载与模型匹配的分词器
AutoModelForCausalLM, # 自动加载因果语言模型(用于文本生成)
BitsAndBytesConfig # 配置 bitsandbytes 4-bit/8-bit 量化参数
)

# 导入 PyTorch 深度学习框架(用于张量计算和 GPU 加速)
import torch


# ==============================
# 第二部分:模型配置
# ==============================

# 指定 Hugging Face Hub 上的模型 ID(使用 Qwen 官方发布的指令微调版本)
model_id = "Qwen/Qwen2.5-7B-Instruct"

# 配置 4-bit 量化参数(显著降低显存占用,适合单卡 V100 运行 7B 模型)
quantization_config = BitsAndBytesConfig(
load_in_4bit=True, # 启用 4-bit 量化(权重压缩至 4 位)
bnb_4bit_compute_dtype=torch.bfloat16 # 计算时临时提升为 bfloat16 精度(V100 支持),平衡速度与精度
# 注:若 GPU 不支持 bfloat16(如 10 系列),可改为 torch.float16
)


# ==============================
# 第三部分:加载分词器与模型
# ==============================

# 加载分词器(tokenizer)
# - trust_remote_code=True:Qwen 使用了自定义 tokenizer 逻辑,需信任远程代码
tokenizer = AutoTokenizer.from_pretrained(
model_id,
trust_remote_code=True
)

# 加载语言模型(LLM)
# - quantization_config:应用 4-bit 量化配置
# - device_map="auto":自动将模型分配到可用 GPU(双 V100 会智能切分或单卡加载)
# - trust_remote_code=True:Qwen 模型结构有自定义实现,必须启用
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=quantization_config,
device_map="auto",
trust_remote_code=True
)

# ✅ 此时模型已加载到 GPU,显存占用约 6–8 GB(4-bit 量化后)


# ==============================
# 第四部分:构造对话输入
# ==============================

# 定义对话历史(采用 ChatML 格式,Qwen 官方推荐)
messages = [
{"role": "system", "content": "You are a helpful assistant."}, # 系统角色设定
{"role": "user", "content": "你好!请介绍一下你自己。"} # 用户提问
]

# 使用 tokenizer 的聊天模板将 messages 转换为模型可识别的字符串
# - tokenize=False:先返回原始字符串(便于调试)
# - add_generation_prompt=True:在末尾添加 "assistant\n" 提示,引导模型开始回答
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
# 示例输出(简化):
# "<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n你好!...<|im_end|>\n<|im_start|>assistant\n"


# ==============================
# 第五部分:分词并移至 GPU
# ==============================

# 将文本转换为 token ID 张量(PyTorch 格式)
# - return_tensors="pt":返回 PyTorch 张量(而非 list 或 TensorFlow)
inputs = tokenizer(text, return_tensors="pt")

# 将输入数据移动到模型所在的设备(通常是 GPU)
# - 避免 CPU 到 GPU 的数据传输延迟
inputs = inputs.to(model.device)


# ==============================
# 第六部分:生成模型回复
# ==============================

# 调用模型生成文本
outputs = model.generate(
**inputs, # 解包输入张量(input_ids, attention_mask)
max_new_tokens=512, # 最多生成 512 个新 token(防止无限生成)
do_sample=True, # 启用随机采样(而非贪心搜索),使输出更自然
temperature=0.7, # 控制生成随机性:值越高越 creative,越低越 deterministic
top_p=0.9 # nucleus sampling:仅从累积概率 90% 的 token 中采样,兼顾多样性与质量
# 可选:add_special_tokens=False, repetition_penalty=1.1 等
)


# ==============================
# 第七部分:后处理与输出
# ==============================

# 从完整输出中截取“新生成的部分”(去掉原始输入)
# - outputs[0]:取第一个样本(batch_size=1)
# - inputs.input_ids.shape[-1]:输入序列长度
# - [inputs.input_ids.shape[-1]:]:切片获取生成内容
response_ids = outputs[0][inputs.input_ids.shape[-1]:]

# 将 token ID 解码为人类可读文本
# - skip_special_tokens=True:过滤掉 <|im_end|> 等特殊符号,只保留干净文本
response_text = tokenizer.decode(response_ids, skip_special_tokens=True)

# 打印最终回复
print(response_text)

第一次运行需要下载模型

1
2
3
4
5
6
7
8
# 1. 安装 hf-transfer
pip install hf-transfer

# 2. 设置环境变量
export HF_ENDPOINT=https://hf-mirror.com
export HF_HUB_ENABLE_HF_TRANSFER=1

python hello.py

模型下载太慢了,改成ModelScope

1
pip install modelscope[vision,nlp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# -*- coding: utf-8 -*-
"""
Qwen2.5-7B-Instruct 4-bit 量化推理示例 (ModelScope 魔搭版)
优势:下载速度快,兼容性好,无需担心远程代码安全警告
"""

# ==============================
# 第一部分:导入必要库
# ==============================
# 1. 导入 ModelScope 的核心组件(用于加载模型和分词器)
from modelscope import (
AutoTokenizer,
AutoModelForCausalLM,
)

# 2. 导入 PyTorch
import torch

# 3. 【关键修复】必须从 transformers 导入量化配置类
# ModelScope 不包含 BitsAndBytesConfig
from transformers import BitsAndBytesConfig

# ==============================
# 第二部分:模型配置
# ==============================
# 使用 ModelScope 上的模型 ID (短 ID)
# 你也可以使用全路径 "qwen/Qwen2.5-7B-Instruct",但短 ID 更快
model_id = "qwen/Qwen2.5-7B-Instruct"

# 4-bit 量化配置 (这部分与 transformers 完全一致)
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16
)

# ==============================
# 第三部分:加载分词器与模型
# ==============================

# 加载分词器
# ModelScope 内部已经集成了 Qwen 的 tokenizer,通常不需要 trust_remote_code
tokenizer = AutoTokenizer.from_pretrained(
model_id,
# trust_remote_code=True # 这一行在新版本 ModelScope 中通常不需要了
)

# 加载模型
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=quantization_config,
device_map="auto",
# trust_remote_code=True # 同上,通常不需要
)

# ==============================
# 第四至七部分:推理与生成 (完全不变)
# ==============================
# 从这部分开始,代码逻辑与 Hugging Face 完全一致,无需修改
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "你好!请介绍一下你自己。"}
]

# 使用 tokenizer 的聊天模板
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)

# 分词并移至 GPU
inputs = tokenizer(text, return_tensors="pt").to(model.device)

# 生成回复
outputs = model.generate(
**inputs,
max_new_tokens=512,
do_sample=True,
temperature=0.7,
top_p=0.9
)

# 后处理
response_ids = outputs[0][inputs.input_ids.shape[-1]:]
response_text = tokenizer.decode(response_ids, skip_special_tokens=True)

print(response_text)

第二步:运行一系列demo,了解一些相关概念

使用ai来学ai~

fastapi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import gc
from transformers import BitsAndBytesConfig

app = FastAPI(title="Qwen2.5 FastAPI 服务")

# ==============================
# 1. 全局变量:用于存储对话上下文
# ==============================
# 注意:生产环境请替换为 Redis
# 结构: {session_id: [{"role": "user", "content": "..."}, ...]}
CONVERSATION_HISTORY = {}

# ==============================
# 2. 模型加载配置
# ==============================
# --- 修改开始 ---
# 1. 先定义本地模型的实际路径
# 注意:路径必须指向包含 config.json, model.safetensors 等文件的文件夹
LOCAL_MODEL_PATH = "/root/.cache/modelscope/hub/models/qwen/Qwen2.5-7B-Instruct"

# 2. 强制使用本地路径,不再使用模型ID
MODEL_PATH = LOCAL_MODEL_PATH
# --- 修改结束 ---

print("正在加载分词器...")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_PATH,
trust_remote_code=True,
# 【关键】添加 local_files_only 防止回退到网络
local_files_only=True
)

print("正在加载模型...")
# 在加载模型前添加
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
)

model = AutoModelForCausalLM.from_pretrained(
MODEL_PATH,
quantization_config=bnb_config, # 👈 启用量化
device_map="auto",
trust_remote_code=True,
# torch_dtype=torch.float16 # 量化时通常不需要单独设这个
)
print("模型加载完成!")

# ==============================
# 3. 数据模型定义
# ==============================
class ChatRequest(BaseModel):
session_id: str
message: str

class ChatResponse(BaseModel):
session_id: str
response: str

# ==============================
# 4. 核心推理函数
# ==============================
def generate_response(session_id: str, user_message: str) -> str:
# 1. 获取或初始化会话历史
if session_id not in CONVERSATION_HISTORY:
CONVERSATION_HISTORY[session_id] = []

# 2. 添加用户新消息
CONVERSATION_HISTORY[session_id].append({"role": "user", "content": user_message})

# 3. 构建对话历史 (给模型看的上下文)
# 注意:Qwen 使用 chat template
messages = CONVERSATION_HISTORY[session_id]

# 4. 应用 chat template 并 tokenize
try:
# 使用 apply_chat_template 生成符合 Qwen 格式的输入
input_ids = tokenizer.apply_chat_template(
messages,
tokenize=True,
return_tensors="pt",
add_generation_prompt=True
).to(model.device)

# 5. 生成参数配置
terminators = [
tokenizer.eos_token_id,
tokenizer.convert_tokens_to_ids("<|eot_id|>")
]

# 1. 获取结束标记 ID
eos_token_id = tokenizer.eos_token_id
# 尝试获取 Qwen 特有的结束标记
eot_token_id = tokenizer.convert_tokens_to_ids("<|eot_id|>")

# 2. 过滤掉 None 值,只保留有效的整数 ID
valid_eos_tokens = [eos_token_id] # 基础的 eos 肯定存在
if eot_token_id is not None:
valid_eos_tokens.append(eot_token_id) # 只有存在时才添加

attention_mask = input_ids.ne(tokenizer.pad_token_id).long()

# === 修改模型生成调用 ===
outputs = model.generate(
input_ids=input_ids,
attention_mask=attention_mask, # 👈 显式传入 attention_mask
max_new_tokens=512,
do_sample=True,
temperature=0.7,
top_p=0.9,
pad_token_id=tokenizer.eos_token_id,
# eos_token_id 已经在前面处理过,这里按需保留
)

# 6. 解码输出 (只取新生成的部分)
# 注意:这里需要处理 Qwen 的特殊结束标记
generated_ids = outputs[0][input_ids.shape[-1]:]
response_text = tokenizer.decode(generated_ids, skip_special_tokens=True)

# 7. 将模型回复存入历史
CONVERSATION_HISTORY[session_id].append({"role": "assistant", "content": response_text})

return response_text

except Exception as e:
# 如果出错,尝试清理显存并重置会话
torch.cuda.empty_cache()
gc.collect()
raise HTTPException(status_code=500, detail=str(e))

# ==============================
# 5. 定义 API 路由
# ==============================
@app.post("/v1/chat/completions", response_model=ChatResponse)
async def chat_completions(request: ChatRequest):
try:
response_text = generate_response(request.session_id, request.message)
return ChatResponse(session_id=request.session_id, response=response_text)
except Exception as e:
# 🔥 关键:打印详细的错误信息和堆栈
import traceback
print("!!! 服务器内部错误详情:")
traceback.print_exc() # 这一行会打印出具体是哪一行代码错了
raise HTTPException(status_code=500, detail=str(e))

@app.get("/health")
async def health_check():
return {"status": "healthy", "model": "Qwen2.5-7B-Instruct"}

# ==============================
# 6. 启动命令 (终端执行)
# ==============================
# uvicorn qwen_server:app --host 0.0.0.0 --port 8000 --workers 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import requests
import json

# 配置
BASE_URL = "http://localhost:8000"
SESSION_ID = "user_123" # 你可以改成你的名字拼音

def chat():
print("欢迎使用 Qwen 聊天室!输入 'quit' 退出。")
print("-" * 50)

while True:
# 1. 获取用户输入
user_input = input("\n👤 你: ").strip()

# 2. 检查是否退出
if user_input.lower() in ['quit', 'exit', 'bye', '退出']:
print("👋 再见!")
break

# 3. 如果输入为空,跳过
if not user_input:
continue

# 4. 发送请求到服务器
try:
response = requests.post(
f"{BASE_URL}/v1/chat/completions",
json={
"session_id": SESSION_ID,
"message": user_input
}
)

# 5. 处理响应
if response.status_code == 200:
# 假设服务器返回的是 {"response": "内容"}
bot_reply = response.json().get("response", "(无响应)")
print(f"\n🤖 Qwen: {bot_reply}")
else:
print(f" 请求失败: {response.status_code}")
print(response.text)

except requests.exceptions.ConnectionError:
print(" 连接错误:请确保服务器 (uvicorn) 已经启动。")
break
except KeyboardInterrupt:
break

if __name__ == "__main__":
chat()

RAG

传统的问答模式是让大模型“凭记忆答题”,而 RAG 则是让大模型“开卷考试”。

  1. **先检索 (Retrieval)**:当用户提出一个问题时,系统首先在外部知识库(如你的文档、数据库)中搜索与问题最相关的信息片段。
  2. **再增强 (Augmented)**:将检索到的“参考资料”与用户的原始问题拼接在一起,形成一份包含“最新、特定外部知识”的增强提示(Prompt)。
  3. **后生成 (Generation)**:将这个增强后的提示输入给大语言模型(LLM)。LLM 基于这些具体的参考资料来生成最终答案,而不是依赖其内部过时的参数知识。

通过这种方式,RAG 成功解决了大模型的两个主要痛点:

  • 知识时效性差:无需重新训练模型,只需更新知识库即可让模型掌握最新信息。
  • 事实性错误(幻觉):让模型的回答有据可依,大幅降低其“一本正经胡说八道”的概率。

⚙️ RAG 的具体实现流程

实现一个 RAG 系统主要分为两个阶段:离线构建知识库(数据准备)和 在线问答(推理服务)。

第一阶段:离线构建知识库

这是 RAG 的“备考”阶段,目的是将你的文档资料处理成计算机可以高效检索的格式。

  1. 加载文档:从各种来源(如 TXT, PDF, Word, 数据库)读取原始文本数据。
  2. 文本分块:将长文档切分成小的文本片段(Chunk)。这是为了适应大模型的上下文窗口,并保证检索的精度。例如,将一本长篇小说按章节或固定长度切分成几百个段落。
  3. 向量化存储:使用嵌入模型(Embedding Model)将每个文本块转换成一串数字(向量),并存入向量数据库。这一步相当于将文字信息“翻译”成计算机可以理解的数学语言,并建立索引。

第二阶段:在线问答

这是 RAG 的“考试”阶段,当用户提问时,系统实时执行以下步骤。

  1. 检索:用户输入问题后,系统使用相同的嵌入模型将问题也转换成向量。然后在向量数据库中进行相似度搜索(如余弦相似度),找出与问题最相关的前 K 个文本块(例如最相关的 3 个段落)。
  2. 构建 Prompt:将检索到的最相关文本块作为上下文,与用户的原始问题拼接在一起,形成一个新的 Prompt。
  3. 生成答案:将这个包含上下文的 Prompt 发送给大语言模型(LLM)。模型阅读上下文后,生成最终的、准确的回答。
1
2
3
pip install langchain langchain_community langchain-core sentence-transformers faiss-cpu
pip install langchain-huggingface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import os
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
#from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
import requests
import json

# ==============================
# 1. 配置 (请根据实际情况修改)
# ==============================
DOC_PATH = "podcast.txt" # 你的知识文档路径
LOCAL_MODEL_PATH = "/root/.cache/modelscope/hub/models/AI-ModelScope/bge-small-en-v1___5"
# EMBEDDING_MODEL = "BAAI/bge-small-en-v1.5" # 开源通用的向量模型
QWEN_SERVER_URL = "http://localhost:8000/v1/chat/completions" # 你的Qwen服务地址

# ==============================
# 2. 构建 RAG 链
# ==============================

class RAGChain:
def __init__(self):
# 1. 加载文档
print("正在加载文档...")
loader = TextLoader(DOC_PATH, encoding='utf-8')
documents = loader.load()

# 2. 切分文本
print("正在切分文本...")
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
splits = text_splitter.split_documents(documents)

# 3. 创建向量库 (Embedding)
print("正在生成向量库...")
embedding_model = HuggingFaceEmbeddings(model_name=LOCAL_MODEL_PATH)
self.vectorstore = FAISS.from_documents(splits, embedding_model)

# 4. 创建检索器
self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": 2})

print("RAG 系统初始化完成!")

def call_qwen_server(self, prompt):
"""调用本地 Qwen 服务"""
try:
response = requests.post(
QWEN_SERVER_URL,
json={
"session_id": "rag_user",
"message": prompt
}
)
if response.status_code == 200:
return response.json()["response"]
else:
return f"API Error: {response.status_code}"
except Exception as e:
return f"Connection Error: {e}"

def invoke(self, question: str):
# --- RAG 核心逻辑 ---

# 1. 检索:根据问题在知识库中查找相关片段
retrieved_docs = self.retriever.invoke(question)
context = "\n".join([doc.page_content for doc in retrieved_docs])

# 2. 提示词工程:将上下文和问题拼接
final_prompt = f"""你是一个问答助手。请根据以下检索到的信息回答问题。

检索到的信息:
{context}

问题:{question}

请用中文回答:"""

# print(f"\n🔍 检索到的上下文:\n{context}\n")

# 3. 生成:调用 Qwen 生成最终答案
result = self.call_qwen_server(final_prompt)
return result

# ==============================
# 4. 运行交互式 Demo
# ==============================

if __name__ == "__main__":
# 初始化 RAG 系统
rag = RAGChain()

print("\n" + "="*50)
print("RAG Demo 已启动!输入 'quit' 退出。")
print("="*50)

while True:
query = input("\n❓ 你的问题: ").strip()
if query.lower() in ['quit', 'exit']:
break
if not query:
continue

answer = rag.invoke(query)
print(f"\n✅ 最终答案:\n{answer}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 播客名称:跟宇宙结婚
# 主播:刀夫(刀老师)、小伙子(冯广健)、青年(谢丹青)
# 出品方:日谈公园 / 荔枝FM
# 上线时间:2015年
# 核心风格:巨型文化知识类聊天节目,妄图传播无用/冷门/过时但有趣的知识。气氛诙谐调皮,被听众称为“快乐的源泉”。
# 主要内容:涵盖网购、电影、民俗、城市历史、文学、动画评析等。节目以“零基础入门”系列(如零基础聊《牛虻》)和“跟饮料/城市结婚”等专题著称。
# 代表系列:《零基础文学入门》、《跟饮料结婚》、《我们少年时代写下的文字》。

# 播客名称:文化有限
# 主播:大壹、星光、超哥
# 首播时间:2019年
# 核心风格:泛文化播客,主打“百无一用”的闲聊。风格轻松、真诚,像三个老友的读书会,注重分享最打动他们的文学和影视作品。
# 主要内容:深度解读经典名著(如《呼啸山庄》、《傲慢与偏见》)和当代热门书籍(如《红鱼之姻》)。节目常有对拉美文学、社会现象的探讨。
# 特色:每周二更新,片头曲固定,被称为“精神避难所”。

# 播客名称:壮游者
# 主持人:Yang(前媒体人)
# 获奖记录:苹果播客2022编辑精选、单向街书店文学奖提名
# 核心风格:人文旅行声音游记。继承了18世纪欧洲“壮游”的精神,注重深度的参与式观察。
# 主要内容:分享全球各地的旅行见闻,涵盖人文、地理、历史。节目质量极高,常做系列专题(如中东系列、非洲系列),不仅讲风景,更讲当地的社会结构和文化冲突。
# 代表作:《阿曼|入沙漠记》、《约旦贝都因人》。

# 播客名称:Woo话可说
# 主播:马里奥、沙拉包、小宋
# 核心风格:轻松幽默的闲聊,融合了街头文化与生活观察。节目常邀请嘉宾进行深夜对谈,话题发散性强。
# 主要内容:讨论生活方式、社会热点、旅行攻略(如去非洲穿羽绒服)、饮食记忆等。也涉及一些亚文化话题(如“入赘”、“老鼠人”概念)。
# 特色:有独立的周边设计(如卫衣),常有线下市集活动。

# 播客名称:日谈公园
# 创始人:李叔(李晨)、小伙子
# 核心风格:日光派对旗下的头部播客,被称为“中文播客的黄埔军校”。内容包罗万象,从电影音乐到生活琐事。
# 主要内容:旗下拥有众多子节目(如《跟宇宙结婚》、《日知录》)。主节目常邀请文化名人对谈,风格轻松、不端着,强调“生活方式”的分享。

# 播客名称:无聊斋
# 出品方:单立人喜剧
# 主持人:刘旸(教主)、六兽
# 核心风格:脱口秀播客,融合了单口喜剧的形式。轻松幽默,注重独立策划与生活化表达。
# 主要内容:每期邀请嘉宾聊奇人异事、职场经验、旅行见闻或社会热点。常有“好物安利”、“Gap人生”等有趣话题,是喜马拉雅平台的高播放量节目。

# 播客名称:蜜獾吃书
# 主播:晴总、北冥(李八斗)
# 荣誉:2025春风悦读榜“年度阅读推广人”
# 核心风格:小型杂食性读书节目。两位主播一唱一和(主讲与捧哏),热衷于阅读闲杂书籍和科幻小说。
# 主要内容:不仅限于文学,还讨论心理学(如阿德勒)、历史(如猫咪秘史)、十二生肖等冷门话题。风格治愈,旨在“多讲故事,少讲道理”。
# 代表选题:《闲聊十二生肖龙》、《用阿德勒心理学解读自卑》。