邓立国Agent开发入门必读书《AI Agent智能体开发实践》全文试读-示例案例丰富且能正确运行_《ai agent 智能体开发实践》在线阅读-CSDN博客
目录
【示例15.1】多Agent客服系统的完整代码,文件名为rag_multi_agent_system.py。
本实战案例的依赖库说明如下。
- langchain:用于构建Agent和工具链的核心框架。
- langchain_community:LangChain的社区扩展,提供额外功能。
- dashscope:阿里云Qwen大模型的Python SDK。
- pandas:用于处理用户数据和库存数据的表格数据。
- pydantic:用于数据验证和模型定义。
- python-dotenv(可选):用于管理环境变量和API密钥。
【示例15.1】多Agent客服系统的完整代码,文件名为rag_multi_agent_system.py。
#pip install langchain langchain_community dashscope pandas pydantic python-dotenv
import os
import logging
from typing import List, Dict, Any, Optional
from langchain.agents import AgentType, Tool, initialize_agent, AgentExecutor
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.tools import StructuredTool, Tool
from langchain.memory import ConversationBufferMemory
from langchain.llms.base import LLM
from pydantic import BaseModel, Field
import pandas as pd
import dashscope
import json
# 配置Qwen API密钥(必须替换为你的真实密钥)
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY") # 👈 请替换为阿里云百炼获取的密钥
dashscope.api_key = DASHSCOPE_API_KEY # 👈 显式设置密钥
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 自定义Qwen LLM类,直接使用dashscope库
class QwenLLM(LLM):
model_name: str = "qwen-plus"
temperature: float = 0.0
max_tokens: int = 2048
@property
def _llm_type(self) -> str:
return "qwen"
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
try:
response = dashscope.Generation.call(
self.model_name,
prompt=prompt,
temperature=self.temperature,
max_tokens=self.max_tokens
)
if response.status_code == 200 and response.output:
return response.output["text"]
else:
logger.error(f"Qwen模型调用失败: {response.message}")
return f"模型调用失败: {response.message}"
except Exception as e:
logger.error(f"Qwen模型调用异常: {str(e)}")
return f"模型调用异常: {str(e)}"
class ToolAgent:
"""工具Agent基类,所有具体工具Agent继承此类"""
def __init__(self, name: str, description: str):
self.name = name
self.description = description
def get_tool(self) -> Tool:
"""将Agent转换为LangChain可用的工具"""
raise NotImplementedError("子类必须实现此方法")
class DocumentRetrievalAgent(ToolAgent):
"""文档检索Agent,使用预设文档内容而非向量数据库"""
def __init__(self, docs_dir: str = "docs"):
super().__init__(
name="DocumentRetrieval",
description="用于从产品手册、帮助文档等文本资料中检索信息。当需要产品细节、使用方法等信息时使用。输入应为查询的问题。"
)
# 创建docs目录(如果不存在)
if not os.path.exists(docs_dir):
os.makedirs(docs_dir)
logger.info(f"创建文档目录: {docs_dir}")
# 添加一个示例文档
with open(os.path.join(docs_dir, "example.txt"), "w", encoding="utf-8") as f:
f.write("智能手表ABC123使用方法:\n1. 长按电源键3s开机\n2. 通过蓝牙连接手机\n3. 打开配套App同步数据")
# 直接加载文档内容到内存
self.document_content = self._load_documents(docs_dir)
def _load_documents(self, docs_dir: str) -> str:
"""加载文档内容到内存"""
try:
content = []
for filename in os.listdir(docs_dir):
if filename.endswith(".txt"):
with open(os.path.join(docs_dir, filename), "r", encoding="utf-8") as f:
content.append(f.read())
return "\n\n".join(content) if content else "没有找到相关文档"
except Exception as e:
logger.warning(f"加载文档失败: {str(e)}")
return "文档加载失败"
def retrieve(self, query: str) -> str:
"""模拟检索:使用Qwen模型根据问题和文档内容生成回答"""
logger.info(f"文档检索Agent处理查询: {query}")
# 如果没有文档内容,直接返回提示
if self.document_content in ["没有找到相关文档", "文档加载失败"]:
return self.document_content
# 使用Qwen模型根据文档内容回答问题
prompt = f"""基于以下文档内容,回答问题:
文档内容:
{self.document_content}
问题:{query}
请根据文档内容回答,不要编造信息。如果文档中没有相关信息,请说明。
"""
llm = QwenLLM(model_name="qwen-plus", temperature=0)
return llm._call(prompt)
def get_tool(self) -> Tool:
return Tool(
name=self.name,
func=self.retrieve,
description=self.description
)
class UserDatabaseAgent(ToolAgent):
"""用户数据库Agent,负责查询用户信息和历史记录"""
def __init__(self, db_path: str = "user_data.csv"):
super().__init__(
name="UserDatabase",
description="用于查询用户信息或历史订单。输入格式应为'user_id:用户ID,type:查询类型',其中查询类型为'user_info'(用户信息)或'orders'(订单历史)。例如:'user_id:U12345,type:orders'"
)
self.db_path = db_path
self.data = self._load_data()
def _load_data(self) -> pd.DataFrame:
"""加载用户数据"""
try:
return pd.read_csv(self.db_path)
except FileNotFoundError:
logger.warning(f"用户数据库文件 {self.db_path} 未找到,创建示例数据")
# 创建示例数据
sample_data = {
"user_id": ["U12345"],
"name": ["张三"],
"email": ["zhangsan@example.com"],
"membership_level": ["VIP"],
"order_history": ["订单12345(已发货), 订单67890(处理中)"]
}
df = pd.DataFrame(sample_data)
df.to_csv(self.db_path, index=False, encoding="utf-8")
return df
except Exception as e:
logger.error(f"加载用户数据失败: {str(e)}")
return pd.DataFrame(columns=["user_id", "name", "email", "membership_level", "order_history"])
def query_user(self, user_id: str) -> str:
"""查询用户信息"""
logger.info(f"用户数据库Agent查询用户: {user_id}")
user_data = self.data[self.data["user_id"] == user_id]
if user_data.empty:
return f"未找到用户ID为 {user_id} 的信息"
user_info = user_data.iloc[0].to_dict()
return (f"用户信息:\n"
f"- 用户ID: {user_info.get('user_id')}\n"
f"- 姓名: {user_info.get('name')}\n"
f"- 邮箱: {user_info.get('email')}\n"
f"- 会员等级: {user_info.get('membership_level')}")
def query_orders(self, user_id: str) -> str:
"""查询用户订单历史"""
logger.info(f"用户数据库Agent查询用户 {user_id} 的订单")
user_data = self.data[self.data["user_id"] == user_id]
if user_data.empty:
return f"未找到用户ID为 {user_id} 的信息"
orders = user_data.iloc[0]["order_history"]
return f"用户 {user_id} 的订单历史: {orders}"
def _handle_query(self, input_str: str) -> str:
"""处理查询,使用单参数输入,格式为"user_id:xxx,type:xxx" """
try:
# 解析输入字符串
params = {}
parts = input_str.split(',')
for part in parts:
key_value = part.split(':')
if len(key_value) == 2:
params[key_value[0].strip()] = key_value[1].strip()
user_id = params.get('user_id')
query_type = params.get('type', 'user_info')
if not user_id:
return "缺少用户ID,请提供格式为'user_id:用户ID,type:查询类型'的输入"
if query_type == "user_info":
return self.query_user(user_id)
elif query_type == "orders":
return self.query_orders(user_id)
else:
return f"不支持的查询类型: {query_type},可选类型:user_info、orders"
except Exception as e:
return f"查询格式错误,请使用'user_id:用户ID,type:查询类型'的格式。错误: {str(e)}"
def get_tool(self) -> Tool:
# 使用普通Tool而非StructuredTool,避免多参数问题
return Tool(
name=self.name,
func=self._handle_query,
description=self.description
)
class InventoryAgent(ToolAgent):
"""库存查询Agent,负责查询产品库存状态"""
def __init__(self, inventory_path: str = "inventory.csv"):
super().__init__(
name="InventoryCheck",
description="用于查询产品库存状态和可用性。输入应为产品ID。当用户询问产品是否有货、库存数量时使用。"
)
self.inventory_path = inventory_path
self.inventory = self._load_inventory()
def _load_inventory(self) -> pd.DataFrame:
"""加载库存数据"""
try:
return pd.read_csv(self.inventory_path)
except FileNotFoundError:
logger.warning(f"库存文件 {self.inventory_path} 未找到,创建示例数据")
# 创建示例库存数据
sample_data = {
"product_id": ["ABC123", "DEF456"],
"name": ["智能手表", "蓝牙耳机"],
"quantity": [50, 0],
"location": ["仓库A", "仓库B"]
}
df = pd.DataFrame(sample_data)
df.to_csv(self.inventory_path, index=False, encoding="utf-8")
return df
except Exception as e:
logger.error(f"加载库存数据失败: {str(e)}")
return pd.DataFrame(columns=["product_id", "name", "quantity", "location"])
def check_inventory(self, product_id: str) -> str:
"""查询产品库存"""
logger.info(f"库存Agent查询产品 {product_id} 的库存")
product_data = self.inventory[self.inventory["product_id"] == product_id]
if product_data.empty:
return f"未找到产品ID为 {product_id} 的库存信息"
product_info = product_data.iloc[0].to_dict()
status = "有货" if int(product_info.get("quantity", 0)) > 0 else "缺货"
return (f"产品库存信息:\n"
f"- 产品ID: {product_info.get('product_id')}\n"
f"- 产品名称: {product_info.get('name')}\n"
f"- 库存数量: {product_info.get('quantity')}\n"
f"- 库存状态: {status}")
def get_tool(self) -> Tool:
return Tool(
name=self.name,
func=self.check_inventory,
description=self.description
)
class TopAgent:
"""顶层Agent,负责协调其他Agent和工具"""
def __init__(self):
# 初始化各个工具Agent
self.document_agent = DocumentRetrievalAgent()
self.user_db_agent = UserDatabaseAgent()
self.inventory_agent = InventoryAgent()
# 创建工具列表
self.tools = [
self.document_agent.get_tool(),
self.user_db_agent.get_tool(),
self.inventory_agent.get_tool()
]
# 初始化LLM为自定义的Qwen模型
self.llm = QwenLLM(model_name="qwen-plus", temperature=0)
# 初始化对话记忆
self.memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
# 创建顶层Agent
self.agent = self._create_agent()
def _create_agent(self) -> AgentExecutor:
"""创建顶层Agent"""
logger.info("初始化顶层Agent")
# 自定义提示模板
prompt_template = """你是一个智能客服顶层Agent,负责协调各种工具来回答客户问题。
你可以使用以下工具:
{tools}
使用工具的格式如下:
```
{{
"action": "{{工具名称}}",
"action_input": "{{工具所需的参数}}"
}}
```
根据客户的问题,决定使用哪个工具。如果需要多个工具的信息,逐步调用它们。
若缺少必要参数(如产品ID、用户ID),请先向客户询问。
客户问题: {input}
对话历史: {chat_history}
工具返回结果: {agent_scratchpad}
请思考是否需要调用工具,或者可以直接回答
"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["tools", "input", "chat_history", "agent_scratchpad"]
)
# 初始化Agent
return initialize_agent(
self.tools,
self.llm,
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
verbose=True,
memory=self.memory,
agent_kwargs={
"system_message": "你是专业的客户服务协调Agent,负责理解客户需求并调用合适的工具提供准确回答。",
"human_message_template": "{input}",
"prompt": prompt
},
handle_parsing_errors="工具调用格式错误,请检查并重新尝试"
)
def process_query(self, user_query: str, user_id: Optional[str] = None) -> str:
"""处理客户查询"""
logger.info(f"处理客户查询: {user_query}, 用户ID: {user_id}")
# 如果有用户ID,将其加入查询上下文
full_query = user_query
if user_id:
full_query += f"\n用户ID: {user_id}"
# 执行Agent
try:
result = self.agent.run(full_query)
return result
except Exception as e:
return f"处理请求时发生错误: {str(e)}"
# 系统初始化和运行示例
if __name__ == "__main__":
# 检查并安装必要的依赖库
required_packages = ["langchain", "langchain_community", "dashscope",
"pandas", "pydantic"]
missing_packages = []
for pkg in required_packages:
try:
__import__(pkg)
except ImportError:
missing_packages.append(pkg)
if missing_packages:
print(f"检测到缺失的依赖库,正在安装: {', '.join(missing_packages)}")
import subprocess
import sys
subprocess.check_call([sys.executable, "-m", "pip", "install", *missing_packages])
# 初始化系统
logger.info("初始化多Agent客户服务系统")
customer_service_system = TopAgent()
# 示例对话
print("欢迎使用智能客服系统!请问有什么可以帮助您的?")
# 示例1: 查询产品信息
query1 = "请告诉我产品ABC123的使用方法?"
print(f"\n用户: {query1}")
response1 = customer_service_system.process_query(query1, user_id="U12345")
print(f"客服: {response1}")
# 示例2: 查询库存
query2 = "产品ABC123现在有货吗?"
print(f"\n用户: {query2}")
response2 = customer_service_system.process_query(query2, user_id="U12345")
print(f"客服: {response2}")
# 示例3: 查询订单
query3 = "我的订单什么时候发货?"
print(f"\n用户: {query3}")
response3 = customer_service_system.process_query(query3, user_id="U12345")
print(f"客服: {response3}")
运行代码
输出如下:
C:\Users\xiayu\miniconda3\envs\langchain03\python.exe C:\Users\xiayu\PyCharmMiscProject\AI-Agent-Dev-Practices-Code\第15章代码\15.13-rag_multi_agent_system系统完整代码.py
* Running on local URL: http://127.0.0.1:7860
INFO:httpx:HTTP Request: GET http://127.0.0.1:7860/gradio_api/startup-events "HTTP/1.1 200 OK"
* To create a public link, set `share=True` in `launch()`.
INFO:httpx:HTTP Request: HEAD http://127.0.0.1:7860/ "HTTP/1.1 200 OK"
INFO:httpx:HTTP Request: GET https://api.gradio.app/pkg-version "HTTP/1.1 200 OK"
C:\Users\xiayu\PyCharmMiscProject\AI-Agent-Dev-Practices-Code\第15章代码\15.13-rag_multi_agent_system系统完整代码.py:295: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/
self.memory = ConversationBufferMemory(
INFO:__main__:初始化顶层Agent
C:\Users\xiayu\PyCharmMiscProject\AI-Agent-Dev-Practices-Code\第15章代码\15.13-rag_multi_agent_system系统完整代码.py:337: LangChainDeprecationWarning: LangChain agents will continue to be supported, but it is recommended for new use cases to be built with LangGraph. LangGraph offers a more flexible and full-featured framework for building agents, including support for tool-calling, persistence of state, and human-in-the-loop workflows. For details, refer to the `LangGraph documentation <https://langchain-ai.github.io/langgraph/>`_ as well as guides for `Migrating from AgentExecutor <https://python.langchain.com/docs/how_to/migrate_agent/>`_ and LangGraph's `Pre-built ReAct agent <https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/>`_.
return initialize_agent(
INFO:__main__:处理客户查询: 请告诉我产品ABC123的使用方法?, 用户ID: U12345
C:\Users\xiayu\PyCharmMiscProject\AI-Agent-Dev-Practices-Code\第15章代码\15.13-rag_multi_agent_system系统完整代码.py:362: LangChainDeprecationWarning: The method `Chain.run` was deprecated in langchain 0.1.0 and will be removed in 1.0. Use :meth:`~invoke` instead.
result = self.agent.run(full_query)
> Entering new AgentExecutor chain...
INFO:__main__:文档检索Agent处理查询: 产品ABC123的使用方法
```json
{
"action": "DocumentRetrieval",
"action_input": "产品ABC123的使用方法"
}
```
Observation: 产品ABC123的使用方法如下:
1. 长按电源键3秒开机;
2. 通过蓝牙连接手机;
3. 打开配套APP同步数据。
Thought:```json
{
"action": "Final Answer",
"action_input": "产品ABC123的使用方法如下:\n1. 长按电源键3秒开机;\n2. 通过蓝牙连接手机;\n3. 打开配套APP同步数据。"
}
```
> Finished chain.
INFO:__main__:初始化顶层Agent
INFO:__main__:处理客户查询: 产品ABC123现在有货吗?, 用户ID: U12345
> Entering new AgentExecutor chain...
INFO:__main__:库存Agent查询产品 ABC123 的库存
```json
{
"action": "InventoryCheck",
"action_input": "ABC123"
}
```
Observation: 产品库存信息:
- 产品ID: ABC123
- 产品名称: 智能手表
- 库存数量: 50
- 库存状态: 有货
Thought:```json
{
"action": "Final Answer",
"action_input": "产品ABC123(智能手表)目前有货,库存数量为50。"
}
```
> Finished chain.
INFO:__main__:初始化顶层Agent
INFO:__main__:处理客户查询: 我的订单什么时候发货?, 用户ID: U12345
> Entering new AgentExecutor chain...
INFO:__main__:用户数据库Agent查询用户 U12345 的订单
```json
{
"action": "UserDatabase",
"action_input": "user_id:U12345,type:orders"
}
```
Observation: 用户 U12345 的订单历史: 订单12345(已发货), 订单67890(处理中)
Thought:```json
{
"action": "Final Answer",
"action_input": "您的订单67890目前处于处理中状态,尚未发货;订单12345已发货。如需了解预计发货时间,请提供具体订单号以便进一步确认。"
}
```
> Finished chain.




427

被折叠的 条评论
为什么被折叠?



