0%

使用 LangChain 环境搭建自己的智能客服

回顾

  之前我们学习了 RAG 的基本流程,分片、索引、召回、重排、生成。现在我们在 LangChain 环境下尝试一下搭建自己的智能客服。

分片

  我们让 chatgpt 生成一份简单的数据集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
智能温室系统的核心目标是自动监测环境参数,并根据作物生长需求调节设备状态。常见监测参数包括温度、湿度、光照强度和土壤湿度。

温度传感器用于采集温室内部空气温度。当温度低于 18 摄氏度时,系统可以启动加热设备;当温度高于 30 摄氏度时,系统可以启动风扇进行降温。

湿度传感器用于检测空气湿度。如果空气湿度低于 40%,可以开启加湿器;如果湿度高于 80%,可以适当通风,防止病虫害滋生。

光照传感器用于检测当前光照强度。在阴天或夜间,若光照不足,系统可以自动打开补光灯,以保证植物正常进行光合作用。

土壤湿度传感器用于检测土壤含水量。当土壤湿度低于设定阈值时,控制器会启动水泵进行自动灌溉;当湿度恢复正常后,关闭水泵。

智能温室系统通常由传感器、主控器、执行器和云平台组成。传感器负责采集数据,主控器负责逻辑判断,执行器负责动作控制,云平台负责远程监控与数据存储。

在嵌入式温室项目中,主控器可以使用 STM32、ESP32 或 Linux 开发板。对于需要图形界面、网络通信和多任务处理的场景,Linux 开发板更有优势。

自动灌溉系统的基本流程是:读取土壤湿度数据,判断是否低于阈值,如果低于阈值则启动继电器,继电器再控制水泵工作,从而完成灌溉。

远程监控平台可以显示实时温度、湿度和设备运行状态。用户可以通过网页或手机查看数据,并手动控制风扇、水泵和补光灯。

为了提高系统稳定性,智能温室软件通常采用状态机设计。这样可以将采集、判断、控制、报警等功能划分为不同状态,方便维护和扩展。

  接下来,我们写下如下代码读取 txt 文件并分片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain_text_splitters import RecursiveCharacterTextSplitter

with open("rag_data.txt", "r", encoding="utf-8") as f:
text = f.read()

text_splitter = RecursiveCharacterTextSplitter(
chunk_size=100,
chunk_overlap=20
)

chunks = text_splitter.split_text(text)

for i, chunk in enumerate(chunks):
print(f"\n--- 分片 {i+1} ---")
print(chunk)

  写下如上代码,可以从输出看到成功分片了!

索引

  现在我们已经有了分片后的 chunks,接下来要做的就是索引,我们先将每个文本块都转变成一串数字向量,然后存入向量库。向量库使用 chromadb 模块。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import chromadb
from sentence_transformers import SentenceTransformer

model = SentenceTransformer(r"F:\model\paraphrase-multilingual-MiniLM-L12-v2")
print("本地模型加载成功")
client = chromadb.Client()
collection = client.get_or_create_collection(name="rag_collection")

for i, chunk in enumerate(chunks):
embedding = model.encode(chunk).tolist()

collection.add(
ids=[f"chunk_{i}"],
documents=[chunk],
embeddings=[embedding],
metadatas=[{"chunk_id": i}]
)

print("索引建立完成")

  ChromaDB 是一个向量数据库,主要用来存放文本,存放文本对应的向量,并且可以按相似度检索最相关的内容。一条数据通常有四个部分,唯一标识(id)、原始文本(document)、文本对应的向量(embedding)、额外信息(metadata)。

  在这段代码中,client 是和 Chroma 交互的入口,collection 就是 chromadb 中的一个数据集合。

召回

  召回阶段,我们将问题也向量化,再使用 ChromaDB 的 query 函数来返回与其相关的 n 个片段。query 函数主要是对 collection 做一次 K 近邻向量检索,使用余弦相似度或欧氏距离来计算两个向量的距离,再返回 K 个有关向量。

1
2
3
4
5
6
7
query = "温度过高时系统会怎么处理?"
query_embedding = model.encode(query).tolist()

results = collection.query(
query_embeddings=[query_embedding],
n_results=3
)

重排

  在本次实践中,因为文本量小,所以不进行重排操作。

生成

  使用 ollama 在本地部署轻量化的 gamma3 模型,再通过 langchain 调用这个模型。

  具体代码如下,至此我们就完成了整个基础 RAG 的流程。

  

图 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
52
53
54
55
56
57
58
59
60
61
62
from langchain_ollama import ChatOllama

# 1. 初始化本地大模型
llm = ChatOllama(
model="gemma3",
temperature=0
)

# 2. 检索函数
def retrieve_docs(query, top_k=3):
query_embedding = model.encode(query).tolist()

results = collection.query(
query_embeddings=[query_embedding],
n_results=top_k
)

docs = results["documents"][0]
metadatas = results["metadatas"][0] if "metadatas" in results else None

return docs, metadatas, results


# 3. 生成上下文
def build_context(docs):
context_parts = []
for i, doc in enumerate(docs):
context_parts.append(f"参考资料{i+1}:\n{doc}")
return "\n\n".join(context_parts)


# 4. RAG问答函数
def rag_qa(query, top_k=3):
docs, metadatas, results = retrieve_docs(query, top_k=top_k)
context = build_context(docs)

prompt = f"""你是一个问答助手。
请严格根据下面提供的参考资料回答问题。

参考资料:
{context}

用户问题:
{query}
"""

response = llm.invoke(prompt)

return {
"query": query,
"docs": docs,
"metadatas": metadatas,
"context": context,
"answer": response.content
}


# 5. 测试
result = rag_qa("温度太高怎么样", top_k=3)

print("\n=== 大模型回答 ===")
print(result["answer"])