## 目录
- [1. Agent 的记忆系统设计](#1. Agent 的记忆系统设计)
- [2. LangChain 记忆组件](#2. LangChain 记忆组件)
- [3. 向量数据库:ChromaDB 实战](#3. 向量数据库:ChromaDB 实战)
- [4. 向量数据库:Milvus 实战](#4. 向量数据库:Milvus 实战)
- [5. 图数据库:Neo4j 实战](#5. 图数据库:Neo4j 实战)
- [6. 结构化数据库:MySQL 实战](#6. 结构化数据库:MySQL 实战)
- [7. 为 Agent 集成数据库工具](#7. 为 Agent 集成数据库工具)
- [8. 小结与下一步](#8. 小结与下一步)
1. Agent 的记忆系统设计
1.1 为什么 Agent 需要数据库
在上一篇中,我们的 Agent 已经能够调用工具了。但它有一个致命缺陷:没有记忆。
Agent 用户 Agent 用户 第一轮对话 第二轮对话(新会话) ❌ 忘了! 我叫张三,在北京工作 你好张三! 我叫什么名字? 抱歉,我不知道您的名字。
数据库为 Agent 提供三种记忆能力:
| 记忆类型 | 存储内容 | 存储方案 | 持久性 |
|---|---|---|---|
| 对话记忆 | 历史对话记录 | 关系型数据库(MySQL) | 永久 |
| 语义记忆 | 知识、文档、经验 | 向量数据库(ChromaDB/Milvus) | 永久 |
| 关系记忆 | 实体之间的关系 | 图数据库(Neo4j) | 永久 |
1.2 三种数据库的定位
Agent 需要记住什么?
用户之前问过什么?
与这个问题相关的知识有哪些?
A 和 B 之间是什么关系?
MySQL
结构化记录
例如:查询订单历史、对话日志
向量数据库
语义搜索
例如:从知识库中检索相关文档
图数据库
关系查询
例如:张三的经理是谁?
1.3 安装依赖
bash
# LangChain 核心组件
pip install langchain langchain-core langchain-openai langchain-community
# 向量数据库
pip install chromadb # 轻量级,适合入门
# pip install pymilvus # 企业级(需要 Milvus 服务)
# 图数据库
pip install neo4j langchain-neo4j # Neo4j Python 驱动 + LangChain 集成
# 结构化数据库
pip install pymysql sqlalchemy langchain-community
# 嵌入模型(向量数据库需要)
pip install sentence-transformers # 用于生成文本向量
2. LangChain 记忆组件
2.1 LangChain 记忆架构
LangChain 提供了多种记忆组件,从简单的对话缓冲到复杂的摘要记忆:
LangChain 记忆组件
RunnableWithMessageHistory
ChatMessageHistory
VectorStoreMemory
基于 Runnable 的记忆管理
支持会话隔离
内存中的消息历史
可持久化到数据库
向量存储记忆
语义检索历史对话
2.2 使用 ChatMessageHistory
python
"""
memory/chat_history.py - LangChain 对话历史管理
"""
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain_community.chat_message_histories import ChatMessageHistory
from typing import Dict, List, Optional
import json
class InMemoryHistory(BaseChatMessageHistory):
"""
内存中的对话历史存储
适合:开发测试、单机应用
不适合:生产环境(重启丢失)
"""
def __init__(self):
self._messages: List[BaseMessage] = []
@property
def messages(self) -> List[BaseMessage]:
return self._messages
def add_message(self, message: BaseMessage) -> None:
self._messages.append(message)
def add_user_message(self, message: str) -> None:
self.add_message(HumanMessage(content=message))
def add_ai_message(self, message: str) -> None:
self.add_message(AIMessage(content=message))
def clear(self) -> None:
self._messages = []
# 会话存储(支持多用户)
class SessionHistoryStore:
"""
会话历史管理器
支持多用户、多会话的历史记录管理
"""
def __init__(self):
self._store: Dict[str, BaseChatMessageHistory] = {}
def get_session_history(self, session_id: str) -> BaseChatMessageHistory:
"""获取或创建会话历史"""
if session_id not in self._store:
self._store[session_id] = InMemoryHistory()
return self._store[session_id]
def list_sessions(self) -> List[str]:
"""列出所有会话"""
return list(self._store.keys())
def clear_session(self, session_id: str) -> None:
"""清除指定会话"""
if session_id in self._store:
self._store[session_id].clear()
# === 使用示例 ===
if __name__ == "__main__":
store = SessionHistoryStore()
# 获取用户会话
history = store.get_session_history("user_001")
# 添加对话
history.add_user_message("你好,我是张三")
history.add_ai_message("你好张三!很高兴认识你。")
history.add_user_message("我叫什么名字?")
history.add_ai_message("你叫张三。")
# 查看历史
print("对话历史:")
for msg in history.messages:
print(f" [{msg.type}]: {msg.content}")
2.3 使用 RunnableWithMessageHistory
python
"""
memory/runnable_with_history.py - 带记忆的 Chain
"""
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from typing import Dict, List
import os
# 会话存储
store: Dict[str, ChatMessageHistory] = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
"""获取会话历史的回调函数"""
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
def create_chat_with_memory():
"""
创建带记忆的对话 Chain
使用 LCEL 语法 + RunnableWithMessageHistory
"""
# 1. 创建 Prompt(包含历史消息占位符)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个友好的助手,能够记住用户的信息。"),
MessagesPlaceholder(variable_name="history"), # 历史消息占位符
("human", "{input}"),
])
# 2. 创建模型
model = ChatOpenAI(
model="deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0.7,
)
# 3. 创建基础 Chain
chain = prompt | model | StrOutputParser()
# 4. 包装为带记忆的 Chain
chain_with_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history",
)
return chain_with_history
# === 使用示例 ===
if __name__ == "__main__":
chain = create_chat_with_memory()
# 配置会话 ID
config = {"configurable": {"session_id": "user_zhangsan"}}
# 第一轮对话
print("=== 第一轮对话 ===")
response1 = chain.invoke({"input": "你好,我叫张三,在北京工作"}, config=config)
print(f"助手: {response1}")
# 第二轮对话(Agent 记住了!)
print("\n=== 第二轮对话 ===")
response2 = chain.invoke({"input": "我叫什么名字?在哪里工作?"}, config=config)
print(f"助手: {response2}")
# 切换用户(新会话)
print("\n=== 切换用户 ===")
config2 = {"configurable": {"session_id": "user_lisi"}}
response3 = chain.invoke({"input": "我叫什么名字?"}, config=config2)
print(f"助手: {response3}") # 不知道,因为是新会话
2.4 记忆流程图
记忆管理
用户输入
获取会话历史
SessionHistory
构建 Prompt
系统消息 + 历史消息 + 用户输入
LLM 处理
生成回复
保存到历史
返回结果
3. 向量数据库:ChromaDB 实战
3.1 什么是向量数据库
向量数据库存储的是文本的数学表示(向量/Embedding),而不是文本本身。它通过"语义相似度"来搜索,而不是关键词匹配。
向量搜索
查询: 手机退货
语义理解
向量相似度计算
匹配: 7天无理由
退换货规定 ✅
传统搜索
查询: 手机退货
关键词匹配
匹配: 手机、退货
可能漏掉
退换货政策
3.2 LangChain + ChromaDB 集成
python
"""
vector_store/chroma_langchain.py - LangChain ChromaDB 集成
安装: pip install chromadb langchain-community sentence-transformers
"""
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_core.documents import Document
from langchain_core.vectorstores import VectorStoreRetriever
from typing import List, Dict, Optional
import os
class ChromaVectorStore:
"""
基于 LangChain 的 ChromaDB 向量存储
特性:
- 使用 LangChain 标准接口
- 支持中文 Embedding 模型
- 支持 LCEL 集成
"""
def __init__(
self,
collection_name: str = "default",
persist_dir: str = "./chroma_db",
embedding_model: str = "BAAI/bge-small-zh-v1.5",
):
"""
初始化向量存储
Args:
collection_name: 集合名称
persist_dir: 持久化目录
embedding_model: Embedding 模型名称
"""
# 初始化 Embedding 模型
self.embeddings = HuggingFaceEmbeddings(
model_name=embedding_model,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True},
)
self.persist_dir = persist_dir
self.collection_name = collection_name
# 创建或加载向量存储
self.vectorstore = Chroma(
collection_name=collection_name,
embedding_function=self.embeddings,
persist_directory=persist_dir,
)
print(f"[ChromaDB] 集合 '{collection_name}' 已就绪")
# ========================
# CREATE: 添加文档
# ========================
def add_documents(
self,
texts: List[str],
metadatas: Optional[List[Dict]] = None,
ids: Optional[List[str]] = None,
):
"""
添加文档到向量库
Args:
texts: 文本列表
metadatas: 元数据列表
ids: 文档 ID 列表
"""
# 转换为 LangChain Document
documents = []
for i, text in enumerate(texts):
doc = Document(
page_content=text,
metadata=metadatas[i] if metadatas else {},
)
documents.append(doc)
# 添加到向量存储
self.vectorstore.add_documents(documents, ids=ids)
print(f"[ChromaDB] 添加了 {len(documents)} 个文档")
def add_texts(self, texts: List[str], metadatas: Optional[List[Dict]] = None):
"""添加文本(简化接口)"""
self.vectorstore.add_texts(texts, metadatas=metadatas)
# ========================
# READ: 查询文档
# ========================
def search(self, query: str, k: int = 5) -> List[Dict]:
"""
语义搜索
Args:
query: 查询文本
k: 返回数量
Returns:
[{"content": "...", "metadata": {...}, "score": 0.95}, ...]
"""
results = self.vectorstore.similarity_search_with_score(query, k=k)
formatted = []
for doc, score in results:
formatted.append({
"content": doc.page_content,
"metadata": doc.metadata,
"score": float(score),
})
return formatted
def search_with_threshold(
self,
query: str,
k: int = 5,
score_threshold: float = 0.8,
) -> List[Dict]:
"""带阈值过滤的搜索"""
results = self.search(query, k)
return [r for r in results if r["score"] >= score_threshold]
# ========================
# DELETE: 删除文档
# ========================
def delete_by_ids(self, ids: List[str]):
"""根据 ID 删除文档"""
self.vectorstore.delete(ids=ids)
print(f"[ChromaDB] 删除了 {len(ids)} 个文档")
# ========================
# RETRIEVER: 获取检索器
# ========================
def as_retriever(
self,
search_type: str = "similarity",
search_kwargs: Optional[Dict] = None,
) -> VectorStoreRetriever:
"""
获取 LangChain Retriever
Args:
search_type: 搜索类型
- "similarity": 相似度搜索
- "mmr": 最大边际相关性(多样性)
- "similarity_score_threshold": 带阈值过滤
search_kwargs: 搜索参数
- {"k": 5}
- {"k": 5, "fetch_k": 20, "lambda_mult": 0.5} # MMR
- {"k": 5, "score_threshold": 0.8} # 阈值
"""
kwargs = search_kwargs or {"k": 5}
return self.vectorstore.as_retriever(
search_type=search_type,
search_kwargs=kwargs,
)
# ========================
# UTILITY: 工具方法
# ========================
def count(self) -> int:
"""获取文档数量"""
return self.vectorstore._collection.count()
# === 使用示例 ===
if __name__ == "__main__":
store = ChromaVectorStore(
collection_name="demo",
persist_dir="./demo_chroma_db",
)
# 1. 添加文档
store.add_documents(
texts=[
"Python 是一种广泛使用的高级编程语言,以简洁易读著称。",
"Java 是一种面向对象的编程语言,广泛用于企业级开发。",
"机器学习是人工智能的一个分支,让计算机从数据中学习。",
"深度学习使用多层神经网络来学习数据的表示。",
"自然语言处理(NLP)让计算机理解和生成人类语言。",
],
metadatas=[
{"category": "编程", "language": "python"},
{"category": "编程", "language": "java"},
{"category": "AI", "topic": "ML"},
{"category": "AI", "topic": "DL"},
{"category": "AI", "topic": "NLP"},
],
ids=["doc1", "doc2", "doc3", "doc4", "doc5"],
)
# 2. 语义搜索
print("\n--- 搜索: '什么是深度学习' ---")
results = store.search("什么是深度学习", k=3)
for r in results:
print(f" [{r['score']:.3f}] {r['content'][:50]}...")
# 3. 获取 Retriever(用于 RAG)
retriever = store.as_retriever(search_kwargs={"k": 3})
docs = retriever.invoke("编程语言有哪些?")
print(f"\n--- Retriever 结果 ---")
for doc in docs:
print(f" {doc.page_content[:50]}...")
3.3 使用 LangChain Embeddings
python
"""
vector_store/embeddings.py - LangChain Embedding 模型配置
"""
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import OpenAIEmbeddings
from typing import List
import os
def get_embeddings(provider: str = "bge"):
"""
获取 Embedding 模型
Args:
provider: 模型提供商
- "bge": BGE 中文模型(推荐)
- "openai": OpenAI Embeddings
- "deepseek": DeepSeek Embeddings
"""
if provider == "bge":
# BGE 中文模型(本地运行,免费)
return HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True},
)
elif provider == "openai":
# OpenAI Embeddings(需要 API Key)
return OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key=os.getenv("OPENAI_API_KEY"),
)
elif provider == "deepseek":
# DeepSeek 兼容 OpenAI 接口
return OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key=os.getenv("DEEPSEEK_API_KEY"),
openai_api_base="https://api.deepseek.com/v1",
)
else:
raise ValueError(f"未知的 Embedding 提供商: {provider}")
# === 使用示例 ===
if __name__ == "__main__":
embeddings = get_embeddings("bge")
# 嵌入单个文本
vector = embeddings.embed_query("什么是机器学习?")
print(f"向量维度: {len(vector)}")
print(f"前 5 维: {vector[:5]}")
# 批量嵌入
texts = ["Python 是编程语言", "Java 是编程语言"]
vectors = embeddings.embed_documents(texts)
print(f"批量嵌入数量: {len(vectors)}")
4. 向量数据库:Milvus 实战
4.1 ChromaDB vs Milvus
| 维度 | ChromaDB | Milvus |
|---|---|---|
| 定位 | 轻量级,嵌入式 | 企业级,分布式 |
| 数据规模 | < 100 万向量 | 10 亿+ 向量 |
| 部署 | pip install,零配置 | 需要 Docker/K8s |
| 性能 | 适合开发和小规模 | GPU 加速,高性能 |
| 适合场景 | 原型开发、个人项目 | 生产环境、大规模应用 |
4.2 LangChain + Milvus 集成
python
"""
vector_store/milvus_langchain.py - LangChain Milvus 集成
安装: pip install pymilvus langchain-community
前置条件: 需要 Milvus 服务运行
"""
from langchain_community.vectorstores import Milvus
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_core.documents import Document
from typing import List, Dict, Optional
import os
class MilvusVectorStore:
"""
基于 LangChain 的 Milvus 向量存储
特性:
- 支持大规模数据(10亿+向量)
- 支持元数据过滤
- 支持分布式部署
"""
def __init__(
self,
collection_name: str = "default",
connection_args: Optional[Dict] = None,
embedding_model: str = "BAAI/bge-small-zh-v1.5",
):
"""
初始化 Milvus 向量存储
Args:
collection_name: 集合名称
connection_args: 连接参数
{"host": "localhost", "port": "19530"}
embedding_model: Embedding 模型
"""
self.connection_args = connection_args or {
"host": "localhost",
"port": "19530",
}
# 初始化 Embedding
self.embeddings = HuggingFaceEmbeddings(
model_name=embedding_model,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True},
)
self.collection_name = collection_name
self.vectorstore: Optional[Milvus] = None
def init_with_documents(self, documents: List[Document]):
"""
使用文档初始化向量存储(创建新集合)
Args:
documents: LangChain Document 列表
"""
self.vectorstore = Milvus.from_documents(
documents=documents,
embedding=self.embeddings,
collection_name=self.collection_name,
connection_args=self.connection_args,
)
print(f"[Milvus] 集合 '{self.collection_name}' 已创建")
def connect_existing(self):
"""连接到现有集合"""
self.vectorstore = Milvus(
embedding_function=self.embeddings,
collection_name=self.collection_name,
connection_args=self.connection_args,
)
print(f"[Milvus] 已连接到集合 '{self.collection_name}'")
# ========================
# CREATE: 添加文档
# ========================
def add_documents(self, documents: List[Document]):
"""添加文档"""
if not self.vectorstore:
raise RuntimeError("请先初始化向量存储")
self.vectorstore.add_documents(documents)
def add_texts(self, texts: List[str], metadatas: Optional[List[Dict]] = None):
"""添加文本"""
if not self.vectorstore:
raise RuntimeError("请先初始化向量存储")
self.vectorstore.add_texts(texts, metadatas=metadatas)
# ========================
# READ: 搜索
# ========================
def search(self, query: str, k: int = 5) -> List[Dict]:
"""语义搜索"""
if not self.vectorstore:
raise RuntimeError("请先初始化向量存储")
results = self.vectorstore.similarity_search_with_score(query, k=k)
return [
{
"content": doc.page_content,
"metadata": doc.metadata,
"score": float(score),
}
for doc, score in results
]
def search_with_filter(
self,
query: str,
filter_expr: str,
k: int = 5,
) -> List[Dict]:
"""
带元数据过滤的搜索
Args:
query: 查询文本
filter_expr: 过滤表达式
例如: 'category == "AI"'
"""
if not self.vectorstore:
raise RuntimeError("请先初始化向量存储")
results = self.vectorstore.similarity_search_with_score(
query,
k=k,
expr=filter_expr,
)
return [
{
"content": doc.page_content,
"metadata": doc.metadata,
"score": float(score),
}
for doc, score in results
]
# ========================
# RETRIEVER
# ========================
def as_retriever(self, search_kwargs: Optional[Dict] = None):
"""获取 Retriever"""
if not self.vectorstore:
raise RuntimeError("请先初始化向量存储")
return self.vectorstore.as_retriever(
search_kwargs=search_kwargs or {"k": 5}
)
# === 使用示例 ===
if __name__ == "__main__":
# 注意:需要先启动 Milvus 服务
# docker run -d --name milvus -p 19530:19530 milvusdb/milvus:latest
store = MilvusVectorStore(
collection_name="demo",
connection_args={"host": "localhost", "port": "19530"},
)
# 初始化并添加文档
documents = [
Document(page_content="人工智能是计算机科学的一个分支", metadata={"category": "AI"}),
Document(page_content="机器学习是实现人工智能的一种方法", metadata={"category": "ML"}),
Document(page_content="深度学习是机器学习的一个子领域", metadata={"category": "DL"}),
]
store.init_with_documents(documents)
# 搜索
results = store.search("AI 和机器学习有什么关系?", k=3)
for r in results:
print(f" [{r['score']:.3f}] {r['content']}")
5. 图数据库:Neo4j 实战
5.1 为什么 Agent 需要图数据库
图数据库擅长处理实体之间的关系:
图数据库 Neo4j
查询: 张三的经理的部门的所有项目
一次图遍历
毫秒级响应 ✅
关系型数据库 MySQL
查询: 张三的经理的部门的所有项目
需要 3 次 JOIN
性能较差
5.2 安装 Neo4j
bash
# 方式一:Docker(推荐)
docker run -d \
--name neo4j \
-p 7474:7474 \
-p 7687:7687 \
-e NEO4J_AUTH=neo4j/your-password \
neo4j:latest
# 方式二:Desktop
# 下载 Neo4j Desktop: https://neo4j.com/download/
5.3 LangChain + Neo4j 集成
python
"""
graph_store/neo4j_langchain.py - LangChain Neo4j 集成
安装: pip install neo4j langchain-community
"""
from langchain_community.graphs import Neo4jGraph
from langchain_community.chains.graph_qa.cypher import GraphCypherQAChain
from langchain_openai import ChatOpenAI
from typing import List, Dict, Optional
import os
class Neo4jGraphStore:
"""
基于 LangChain 的 Neo4j 图数据库操作
核心概念:
- Node(节点): 实体,如 Person、Department
- Relationship(关系): 节点之间的连接
- Property(属性): 节点或关系上的键值对
"""
def __init__(
self,
url: str = "bolt://localhost:7687",
username: str = "neo4j",
password: str = "your-password",
):
"""
初始化 Neo4j 连接
Args:
url: Neo4j 连接 URL
username: 用户名
password: 密码
"""
self.graph = Neo4jGraph(
url=url,
username=username,
password=password,
)
print(f"[Neo4j] 已连接到 {url}")
# ========================
# CREATE: 创建节点和关系
# ========================
def create_node(self, label: str, properties: Dict) -> None:
"""
创建节点
Args:
label: 节点标签(类型)
properties: 节点属性
"""
# 构建 Cypher 语句
props_str = ", ".join(f"{k}: ${k}" for k in properties.keys())
query = f"CREATE (n:{label} {{{props_str}}})"
self.graph.query(query, params=properties)
print(f"[Neo4j] 创建节点: {label} {properties}")
def create_relationship(
self,
from_label: str,
from_props: Dict,
to_label: str,
to_props: Dict,
rel_type: str,
rel_props: Optional[Dict] = None,
) -> None:
"""
创建关系
Args:
from_label: 起始节点标签
from_props: 起始节点匹配属性
to_label: 目标节点标签
to_props: 目标节点匹配属性
rel_type: 关系类型
rel_props: 关系属性(可选)
"""
from_match = " AND ".join(f"a.{k} = $from_{k}" for k in from_props)
to_match = " AND ".join(f"b.{k} = $to_{k}" for k in to_props)
rel_prop_str = ""
params = {}
if rel_props:
rel_prop_str = " {" + ", ".join(f"{k}: $rel_{k}" for k in rel_props) + "}"
for k, v in rel_props.items():
params[f"rel_{k}"] = v
for k, v in from_props.items():
params[f"from_{k}"] = v
for k, v in to_props.items():
params[f"to_{k}"] = v
query = f"""
MATCH (a:{from_label} {{{from_match}}})
MATCH (b:{to_label} {{{to_match}}})
CREATE (a)-[:{rel_type}{rel_prop_str}]->(b)
"""
self.graph.query(query, params=params)
print(f"[Neo4j] 创建关系: ({from_label})-[:{rel_type}]->({to_label})")
# ========================
# READ: 查询
# ========================
def query(self, cypher: str, params: Optional[Dict] = None) -> List[Dict]:
"""
执行 Cypher 查询
Args:
cypher: Cypher 查询语句
params: 查询参数
Returns:
查询结果列表
"""
return self.graph.query(cypher, params=params)
def find_nodes(self, label: str, properties: Optional[Dict] = None) -> List[Dict]:
"""查找节点"""
if properties:
where = " AND ".join(f"n.{k} = ${k}" for k in properties)
query = f"MATCH (n:{label}) WHERE {where} RETURN n"
return self.graph.query(query, params=properties)
else:
query = f"MATCH (n:{label}) RETURN n LIMIT 50"
return self.graph.query(query)
def get_neighbors(
self,
label: str,
name: str,
rel_type: Optional[str] = None,
) -> List[Dict]:
"""
获取节点的邻居
Args:
label: 节点标签
name: 节点名称
rel_type: 关系类型(可选)
"""
if rel_type:
query = f"""
MATCH (a:{label} {{name: $name}})-[:{rel_type}]-(b)
RETURN b
"""
else:
query = f"""
MATCH (a:{label} {{name: $name}})--(b)
RETURN b
"""
return self.graph.query(query, params={"name": name})
# ========================
# UPDATE: 更新
# ========================
def update_node(
self,
label: str,
match_props: Dict,
update_props: Dict,
) -> None:
"""更新节点属性"""
match_str = " AND ".join(f"n.{k} = $match_{k}" for k in match_props)
set_str = ", ".join(f"n.{k} = $update_{k}" for k in update_props)
params = {}
for k, v in match_props.items():
params[f"match_{k}"] = v
for k, v in update_props.items():
params[f"update_{k}"] = v
query = f"""
MATCH (n:{label} {{{match_str}}})
SET {set_str}
"""
self.graph.query(query, params=params)
print(f"[Neo4j] 更新节点: {label}")
# ========================
# DELETE: 删除
# ========================
def delete_node(self, label: str, properties: Dict) -> None:
"""删除节点(同时删除关联关系)"""
match_str = " AND ".join(f"n.{k} = ${k}" for k in properties)
query = f"MATCH (n:{label} {{{match_str}}}) DETACH DELETE n"
self.graph.query(query, params=properties)
print(f"[Neo4j] 删除节点: {label}")
def clear_all(self) -> None:
"""清空数据库(危险操作!)"""
self.graph.query("MATCH (n) DETACH DELETE n")
print("[Neo4j] 数据库已清空")
# ========================
# GRAPH QA: 自然语言查询
# ========================
def create_qa_chain(self, llm=None):
"""
创建图数据库问答 Chain
支持自然语言查询图数据库
"""
if llm is None:
llm = ChatOpenAI(
model="deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0,
)
# 刷新图结构信息
self.graph.refresh_schema()
# 创建 QA Chain
chain = GraphCypherQAChain.from_llm(
llm=llm,
graph=self.graph,
verbose=True,
return_intermediate_steps=True,
)
return chain
# === 使用示例 ===
if __name__ == "__main__":
store = Neo4jGraphStore(password="your-password")
# 清空旧数据
store.clear_all()
# 1. 创建节点
store.create_node("Person", {"name": "张三", "role": "工程师", "age": 30})
store.create_node("Person", {"name": "李四", "role": "经理", "age": 40})
store.create_node("Department", {"name": "技术部"})
store.create_node("Project", {"name": "AI助手", "status": "进行中"})
# 2. 创建关系
store.create_relationship(
"Person", {"name": "张三"},
"Department", {"name": "技术部"},
"WORKS_IN"
)
store.create_relationship(
"Person", {"name": "李四"},
"Department", {"name": "技术部"},
"MANAGES"
)
store.create_relationship(
"Person", {"name": "张三"},
"Project", {"name": "AI助手"},
"ASSIGNED_TO"
)
# 3. 查询
print("\n--- 查询: 技术部的所有员工 ---")
results = store.find_nodes("Department", {"name": "技术部"})
for r in results:
print(f" {r}")
print("\n--- 查询: 张三认识谁 ---")
neighbors = store.get_neighbors("Person", "张三")
for n in neighbors:
print(f" {n}")
# 4. 自然语言查询
print("\n--- 自然语言查询 ---")
qa_chain = store.create_qa_chain()
result = qa_chain.invoke({"query": "技术部有哪些人?"})
print(f"结果: {result['result']}")
5.4 图数据库查询流程
LangChain
Neo4j
自然语言查询
LLM 生成 Cypher
执行 Cypher 查询
获取查询结果
LLM 生成自然语言回答
返回结果
6. 结构化数据库:MySQL 实战
6.1 安装 MySQL
bash
# Docker 方式
docker run -d \
--name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=your-password \
-e MYSQL_DATABASE=agent_db \
mysql:8.0
6.2 LangChain SQL 集成
python
"""
db/mysql_langchain.py - LangChain SQL 数据库集成
安装: pip install pymysql sqlalchemy langchain-community
"""
from langchain_community.utilities import SQLDatabase
from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool
from langchain_openai import ChatOpenAI
from langchain.chains import create_sql_query_chain
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from typing import List, Dict, Optional
import os
class MySQLStore:
"""
基于 LangChain 的 MySQL 数据库操作
特性:
- 使用 LangChain SQLDatabase 统一接口
- 支持自然语言转 SQL
- 支持安全查询
"""
def __init__(
self,
host: str = "localhost",
port: int = 3306,
user: str = "root",
password: str = "your-password",
database: str = "agent_db",
):
"""
初始化 MySQL 连接
Args:
host: 主机地址
port: 端口
user: 用户名
password: 密码
database: 数据库名
"""
connection_string = f"mysql+pymysql://{user}:{password}@{host}:{port}/{database}?charset=utf8mb4"
self.db = SQLDatabase.from_uri(
connection_string,
include_tables=[], # 空列表表示包含所有表
sample_rows_in_table_info=3, # 在表信息中包含示例行
)
print(f"[MySQL] 已连接到 {host}:{port}/{database}")
# ========================
# SCHEMA: 获取表结构
# ========================
def get_table_info(self) -> str:
"""获取所有表的结构信息"""
return self.db.get_table_info()
def get_table_names(self) -> List[str]:
"""获取所有表名"""
return self.db.get_usable_table_names()
# ========================
# QUERY: 执行查询
# ========================
def run_query(self, sql: str) -> str:
"""
执行 SQL 查询(只读)
Args:
sql: SQL 查询语句
Returns:
查询结果(字符串格式)
"""
return self.db.run(sql)
def run_query_dict(self, sql: str) -> List[Dict]:
"""执行查询并返回字典列表"""
result = self.db.run(sql, include_columns=True)
# 解析结果为字典列表
return result
# ========================
# WRITE: 执行写入
# ========================
def execute_write(self, sql: str) -> None:
"""
执行写入操作(INSERT/UPDATE/DELETE)
注意:需要使用原生连接
"""
import pymysql
conn = pymysql.connect(
host="localhost",
user="root",
password="your-password",
database="agent_db",
charset="utf8mb4",
)
try:
with conn.cursor() as cursor:
cursor.execute(sql)
conn.commit()
finally:
conn.close()
# ========================
# NATURAL LANGUAGE: 自然语言查询
# ========================
def create_nl_query_chain(self, llm=None):
"""
创建自然语言转 SQL 的 Chain
用户可以用自然语言查询数据库
"""
if llm is None:
llm = ChatOpenAI(
model="deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0,
)
# 创建 SQL 查询生成 Chain
write_query = create_sql_query_chain(llm, self.db)
# 创建执行查询的工具
execute_query = QuerySQLDataBaseTool(db=self.db)
# 组合成完整 Chain
answer_prompt = PromptTemplate.from_template(
"""根据以下用户问题、SQL 查询和查询结果,给出自然语言回答:
用户问题: {question}
SQL 查询: {query}
查询结果: {result}
回答:"""
)
chain = (
RunnablePassthrough.assign(query=write_query).assign(
result=itemgetter("query") | execute_query
)
| answer_prompt
| llm
| StrOutputParser()
)
return chain
# 需要导入 itemgetter
from operator import itemgetter
# === 使用示例 ===
if __name__ == "__main__":
store = MySQLStore(password="your-password")
# 1. 查看表结构
print("=== 表结构 ===")
print(store.get_table_info())
# 2. 查看表名
print("\n=== 表名 ===")
print(store.get_table_names())
# 3. 执行查询
print("\n=== 查询结果 ===")
result = store.run_query("SELECT * FROM users LIMIT 5")
print(result)
# 4. 自然语言查询
print("\n=== 自然语言查询 ===")
chain = store.create_nl_query_chain()
answer = chain.invoke({"question": "有多少用户?"})
print(f"回答: {answer}")
6.3 使用 SQLAlchemy ORM
python
"""
db/sqlalchemy_models.py - SQLAlchemy ORM 模型
安装: pip install sqlalchemy pymysql
"""
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Text, Float
from sqlalchemy.orm import declarative_base, sessionmaker
from datetime import datetime
# 创建引擎
engine = create_engine(
"mysql+pymysql://root:your-password@localhost/agent_db?charset=utf8mb4"
)
Session = sessionmaker(bind=engine)
Base = declarative_base()
# === 定义模型 ===
class User(Base):
"""用户模型"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
email = Column(String(200), unique=True)
role = Column(String(50), default="user")
created_at = Column(DateTime, default=datetime.now)
class Conversation(Base):
"""对话记录模型"""
__tablename__ = "conversations"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, nullable=False)
session_id = Column(String(100))
role = Column(String(20), nullable=False) # user / assistant
content = Column(Text, nullable=False)
created_at = Column(DateTime, default=datetime.now)
class AgentLog(Base):
"""Agent 执行日志"""
__tablename__ = "agent_logs"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer)
session_id = Column(String(100))
action_type = Column(String(50)) # tool_call / response / error
tool_name = Column(String(100))
input_data = Column(Text)
output_data = Column(Text)
duration_ms = Column(Float)
created_at = Column(DateTime, default=datetime.now)
# 建表
Base.metadata.create_all(engine)
# === 使用示例 ===
if __name__ == "__main__":
session = Session()
# CREATE
user = User(name="张三", email="zhangsan@example.com", role="engineer")
session.add(user)
session.commit()
print(f"创建用户: ID={user.id}")
# READ
all_users = session.query(User).all()
engineers = session.query(User).filter(User.role == "engineer").all()
user = session.query(User).filter_by(name="张三").first()
# UPDATE
user.role = "senior_engineer"
session.commit()
# DELETE
session.delete(user)
session.commit()
session.close()
7. 为 Agent 集成数据库工具
7.1 统一数据库工具集
python
"""
tools/db_tools.py - Agent 数据库工具集
将数据库操作封装为 LangChain Tool
"""
from langchain_core.tools import tool
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from typing import Optional, List, Dict
import json
import os
class DatabaseToolkit:
"""
数据库工具集
为 Agent 提供统一的数据库操作工具
"""
def __init__(self):
# 初始化向量存储
self.embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={"device": "cpu"},
)
self.vectorstore = Chroma(
collection_name="knowledge",
embedding_function=self.embeddings,
persist_directory="./chroma_db",
)
def get_tools(self):
"""获取所有工具"""
return [
self.search_knowledge,
self.save_memory,
self.query_user_info,
]
@tool
def search_knowledge(self, query: str, category: Optional[str] = None) -> str:
"""
搜索知识库,查找与问题相关的文档和知识。
Args:
query: 搜索关键词或问题
category: 限定搜索类别(可选)
Returns:
相关文档内容
"""
# 构建过滤条件
filter_dict = None
if category:
filter_dict = {"category": category}
# 搜索
if filter_dict:
results = self.vectorstore.similarity_search_with_score(
query, k=5, filter=filter_dict
)
else:
results = self.vectorstore.similarity_search_with_score(query, k=5)
if not results:
return "未找到相关信息"
# 格式化结果
formatted = []
for doc, score in results:
formatted.append({
"content": doc.page_content,
"metadata": doc.metadata,
"score": round(float(score), 3),
})
return json.dumps(formatted, ensure_ascii=False, indent=2)
@tool
def save_memory(self, content: str, category: str = "general") -> str:
"""
保存重要信息到记忆中,用于后续对话。
Args:
content: 要记住的内容
category: 分类标签
Returns:
保存结果
"""
import uuid
doc_id = f"memory_{uuid.uuid4().hex[:8]}"
self.vectorstore.add_texts(
texts=[content],
metadatas=[{"type": "memory", "category": category}],
ids=[doc_id],
)
return f"已记住: {content[:50]}..."
@tool
def query_user_info(self, user_id: str) -> str:
"""
查询用户信息。
Args:
user_id: 用户 ID
Returns:
用户信息
"""
# 模拟查询(实际应连接 MySQL)
mock_data = {
"user_001": {"name": "张三", "role": "工程师", "department": "技术部"},
"user_002": {"name": "李四", "role": "经理", "department": "技术部"},
}
if user_id in mock_data:
return json.dumps(mock_data[user_id], ensure_ascii=False)
return f"未找到用户: {user_id}"
# === 使用示例 ===
if __name__ == "__main__":
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
# 初始化工具集
toolkit = DatabaseToolkit()
tools = toolkit.get_tools()
# 创建 Agent
llm = ChatOpenAI(
model="deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0,
)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个智能助手,可以使用工具来查询信息。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 测试
result = agent_executor.invoke({"input": "帮我记住:张三是技术部的工程师"})
print(f"\n结果: {result['output']}")
7.2 Agent 数据库工具架构
知识/文档
用户信息
保存记忆
用户请求
Agent
需要什么类型的数据?
search_knowledge
query_user_info
save_memory
向量数据库
ChromaDB
关系数据库
MySQL
返回结果
生成回复
8. 小结与下一步
8.1 本篇回顾
| 知识点 | 掌握程度 |
|---|---|
| LangChain 记忆组件(ChatMessageHistory, RunnableWithMessageHistory) | ✅ |
| LangChain + ChromaDB 向量数据库集成 | ✅ |
| LangChain + Milvus 企业级向量数据库集成 | ✅ |
| LangChain + Neo4j 图数据库集成 | ✅ |
| LangChain SQLDatabase MySQL 集成 | ✅ |
| 使用 @tool 装饰器封装数据库工具 | ✅ |
| 为 Agent 集成数据库工具 | ✅ |
8.2 数据库选型速查
| 需求 | 推荐 | 理由 |
|---|---|---|
| 快速原型 / 学习 | ChromaDB | 零配置,pip install 即用 |
| 生产级大规模向量 | Milvus | 10亿+向量,GPU 加速 |
| 实体关系查询 | Neo4j | 图遍历,毫秒级 |
| 业务数据存储 | MySQL + SQLAlchemy | 成熟稳定,ORM 方便 |
| 全都要 | 三者配合使用 | 各司其职 |
8.3 LangChain 数据库组件总览
LangChain 数据库组件
VectorStore
Graph
SQLDatabase
Memory
Chroma
Milvus
FAISS
Neo4jGraph
NebulaGraph
MySQL
PostgreSQL
SQLite
ChatMessageHistory
RunnableWithMessageHistory
8.4 下一篇预告
第四篇:RAG 检索增强生成 将深入讲解:
- LangChain RAG 完整流程(文档加载→分块→嵌入→检索→生成)
- LangChain Document Loaders 和 Text Splitters
- 使用 LangChain 构建 RAG Chain
- 混合搜索(向量 + BM25)
- 重排序(Reranking)
- 生产级 RAG 管道完整实现