feat(module12): update code and environment dependencies
This commit is contained in:
@@ -6,3 +6,6 @@ LANGCHAIN_API_KEY=your_langsmith_api_key
|
|||||||
|
|
||||||
# 高德地图相关配置 (用于MCP服务调试) —— 未到MCP模块无需修改
|
# 高德地图相关配置 (用于MCP服务调试) —— 未到MCP模块无需修改
|
||||||
AMAP_MAPS_API_KEY=your_gaode_api_key
|
AMAP_MAPS_API_KEY=your_gaode_api_key
|
||||||
|
|
||||||
|
# ChatGPT密钥(用于Agents SDK对接Smith追踪) —— 可选,非必须
|
||||||
|
CHATGPT_API_KEY=your_chatgpt_api_key
|
||||||
@@ -16,6 +16,7 @@ except ImportError:
|
|||||||
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
||||||
LANGCHAIN_API_KEY = os.getenv("LANGCHAIN_API_KEY")
|
LANGCHAIN_API_KEY = os.getenv("LANGCHAIN_API_KEY")
|
||||||
AMAP_MAPS_API_KEY = os.getenv("AMAP_MAPS_API_KEY")
|
AMAP_MAPS_API_KEY = os.getenv("AMAP_MAPS_API_KEY")
|
||||||
|
CHATGPT_API_KEY = os.getenv("CHATGPT_API_KEY")
|
||||||
|
|
||||||
if not OPENAI_API_KEY:
|
if not OPENAI_API_KEY:
|
||||||
raise ValueError("× 请在.env中设置OPENAI_API_KEY")
|
raise ValueError("× 请在.env中设置OPENAI_API_KEY")
|
||||||
@@ -23,3 +24,5 @@ if not LANGCHAIN_API_KEY:
|
|||||||
raise ValueError("× 请在.env中设置LANGCHAIN_API_KEY")
|
raise ValueError("× 请在.env中设置LANGCHAIN_API_KEY")
|
||||||
if not AMAP_MAPS_API_KEY:
|
if not AMAP_MAPS_API_KEY:
|
||||||
raise ValueError("× 请在.env中设置AMAP_MAPS_API_KEY")
|
raise ValueError("× 请在.env中设置AMAP_MAPS_API_KEY")
|
||||||
|
if not CHATGPT_API_KEY:
|
||||||
|
raise ValueError("× 请在.env中设置CHATGPT_API_KEY")
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# 🧩 模块说明:Agents SDK Swarm - 构建智能客服多智能体系统
|
||||||
|
|
||||||
|
> 📌 核心知识点:多智能体协作|智能体转接机制|工具调用|MCP Server 集成|流式响应输出
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1. 模块概述
|
||||||
|
|
||||||
|
本模块基于 Agents SDK Swarm 框架实现了一个航空公司智能客服系统,展示了多智能体协作、智能转接和工具调用的核心功能。
|
||||||
|
|
||||||
|
- ✅ 掌握点:
|
||||||
|
- 使用 Agents SDK 创建多个专业智能体(Agent)
|
||||||
|
- 实现智能体之间的转接网络(Handoff Network)
|
||||||
|
- 集成外部工具和 MCP Server(如高德地图服务)
|
||||||
|
- 实现流式响应和实时交互
|
||||||
|
- 配置动态提示词和上下文共享
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `tools.py` (业务工具与上下文)
|
||||||
|
|
||||||
|
定义了系统使用的业务工具和全局上下文变量。
|
||||||
|
|
||||||
|
- ✅ 掌握点:
|
||||||
|
- 使用 `@function_tool` 装饰器定义业务工具
|
||||||
|
- 实现工具函数的逻辑
|
||||||
|
- 配置全局上下文变量,实现智能体间信息共享
|
||||||
|
|
||||||
|
- 工具列表:
|
||||||
|
- `execute_refund()`:执行退款逻辑
|
||||||
|
- `check_seat()`:查询航班座位余量
|
||||||
|
- `context_variables`:全局上下文,包含用户信息和航班信息
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. `agent.py` (智能体定义与配置)
|
||||||
|
|
||||||
|
定义了系统中的所有智能体及其交互规则。
|
||||||
|
|
||||||
|
- ✅ 掌握点:
|
||||||
|
- 创建专业智能体(退票专员、改签专员、前台分诊员)
|
||||||
|
- 配置智能体的指令和工具集
|
||||||
|
- 集成高德地图 MCP Server
|
||||||
|
- 建立智能体之间的转接网络
|
||||||
|
- 使用动态提示词根据上下文生成指令
|
||||||
|
|
||||||
|
- 核心功能:
|
||||||
|
- `refund_agent`:退票专员,处理退票请求
|
||||||
|
- `change_agent`:改签专员,处理改签请求
|
||||||
|
- `triage_agent`:前台分诊员,负责用户意图识别和智能转接
|
||||||
|
- 建立了完整的智能体转接网络,支持灵活的任务流转
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. `main.py` (系统运行入口)
|
||||||
|
|
||||||
|
实现了智能客服系统的运行逻辑和用户交互界面。
|
||||||
|
|
||||||
|
- ✅ 掌握点:
|
||||||
|
- 配置系统 tracing 选项
|
||||||
|
- 连接 MCP Server
|
||||||
|
- 实现流式响应处理
|
||||||
|
- 管理对话历史和智能体状态
|
||||||
|
- 处理各类事件(文本流、智能体切换、工具调用等)
|
||||||
|
|
||||||
|
- 技术特点:
|
||||||
|
- 支持异步事件处理
|
||||||
|
- 实时展示智能体切换和工具调用过程
|
||||||
|
- 默认关闭 ChatGPT 的 Tracing 功能(可配置启用)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔔 配置与运行说明
|
||||||
|
|
||||||
|
1. **环境配置**:
|
||||||
|
- 在项目根目录的 `.env` 文件中配置高德地图 API Key(必填):
|
||||||
|
```
|
||||||
|
AMAP_MAPS_API_KEY=your_amap_api_key
|
||||||
|
```
|
||||||
|
- ChatGPT API Key 为选配(用于启用 Tracing):
|
||||||
|
```
|
||||||
|
OPENAI_API_KEY=your_openai_api_key
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **运行方式**:
|
||||||
|
```bash
|
||||||
|
python -m m12_agents_sdk_swarm.main
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **使用示例**:
|
||||||
|
```
|
||||||
|
✈️ 客服系统启动...
|
||||||
|
✅ MCP Server (amap) connected.
|
||||||
|
|
||||||
|
User: 我想退票
|
||||||
|
🤖 [TriageAgent] 我帮您转接到退票专员
|
||||||
|
🔀 [系统]: TriageAgent → RefundAgent
|
||||||
|
🔧 [工具]: execute_refund
|
||||||
|
✅ [结果]: ✅️ 退款申请已提交,预计3个工作日内到账。
|
||||||
|
🤖 [RefundAgent] 退款申请已提交,预计3个工作日内到账。您还需要其他帮助吗?
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 💡 扩展建议
|
||||||
|
|
||||||
|
- 尝试添加更多专业智能体,如值机专员、行李查询专员等
|
||||||
|
- 集成更多外部服务,如航班查询 API、支付系统等
|
||||||
|
- 优化智能体的转接逻辑,添加更复杂的意图识别规则
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📋 注意事项
|
||||||
|
|
||||||
|
- 确保已正确安装所有依赖(可通过项目根目录的 `requirements.txt` 安装)
|
||||||
|
- 高德地图 API Key 是系统正常运行的必要条件
|
||||||
|
- 如遇到连接问题,检查网络设置和 API Key 的有效性
|
||||||
|
- 系统默认关闭 Tracing 功能,如需启用请在 `main.py` 中取消注释相关代码并配置 API Key
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
from agents.mcp import MCPServerStdio, MCPServerStdioParams
|
||||||
|
from openai import AsyncOpenAI
|
||||||
|
from agents import Agent, OpenAIChatCompletionsModel
|
||||||
|
from config import OPENAI_API_KEY
|
||||||
|
from m12_agents_sdk_swarm.tools import execute_refund, check_seat
|
||||||
|
from config import AMAP_MAPS_API_KEY
|
||||||
|
|
||||||
|
client = AsyncOpenAI(api_key=OPENAI_API_KEY, base_url="https://api.deepseek.com")
|
||||||
|
model = OpenAIChatCompletionsModel(model="deepseek-chat", openai_client=client)
|
||||||
|
|
||||||
|
# 创建 Server,传入 params
|
||||||
|
amap_server = [
|
||||||
|
MCPServerStdio(
|
||||||
|
name="amap",
|
||||||
|
params=MCPServerStdioParams(
|
||||||
|
command="npx",
|
||||||
|
args=["-y", "@amap/amap-maps-mcp-server"],
|
||||||
|
env={"AMAP_MAPS_API_KEY": AMAP_MAPS_API_KEY}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# === 1. 创建所有Agent ===
|
||||||
|
|
||||||
|
# 退票专员
|
||||||
|
refund_agent = Agent(
|
||||||
|
name="RefundAgent",
|
||||||
|
instructions=lambda context, __: f"""你是退票专员。
|
||||||
|
|
||||||
|
【客户信息】
|
||||||
|
- 姓名: {context.context.get('user_name')}
|
||||||
|
- 航班号: {context.context.get('flight_no')}
|
||||||
|
|
||||||
|
【核心职责】
|
||||||
|
1. 当用户表达退票意图时,立即调用 execute_refund 工具完成退票
|
||||||
|
2. 不要问用户是否确认,直接执行
|
||||||
|
3. 执行完工具后,告知用户退款状态
|
||||||
|
|
||||||
|
【转接规则】
|
||||||
|
- 如果用户改变主意想改签,转接到 ChangeAgent
|
||||||
|
- 如果用户询问其他问题,转接到 TriageAgent
|
||||||
|
- 退票完成后,询问用户是否还需要其他帮助
|
||||||
|
|
||||||
|
【重要】你已经被转接过来,说明用户想退票,直接执行工具即可,不要犹豫!
|
||||||
|
""",
|
||||||
|
tools=[execute_refund],
|
||||||
|
model=model
|
||||||
|
)
|
||||||
|
|
||||||
|
# 改签专员
|
||||||
|
change_agent = Agent(
|
||||||
|
name="ChangeAgent",
|
||||||
|
instructions=lambda context, __: f"""你是改签专员。
|
||||||
|
|
||||||
|
【客户信息】
|
||||||
|
- 姓名: {context.context.get('user_name')}
|
||||||
|
- 航班号: {context.context.get('flight_no')}
|
||||||
|
|
||||||
|
【核心职责】
|
||||||
|
1. 当用户表达改签意图时,立即调用 check_seat 工具查询座位
|
||||||
|
2. 不要问用户是否确认,直接执行
|
||||||
|
3. 执行完工具后,告知用户结果
|
||||||
|
|
||||||
|
【转接规则】
|
||||||
|
- 如果用户改变主意想退票,转接到 RefundAgent
|
||||||
|
- 如果用户询问其他问题,转接到 TriageAgent
|
||||||
|
- 改签完成后,询问用户是否还需要其他帮助
|
||||||
|
|
||||||
|
【重要】你已经被转接过来,说明用户想改签,直接执行工具即可!
|
||||||
|
""",
|
||||||
|
tools=[check_seat],
|
||||||
|
model=model,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 前台分诊员(动态提示词)
|
||||||
|
triage_agent = Agent(
|
||||||
|
name="TriageAgent",
|
||||||
|
instructions=lambda context, __: f"""你是航空公司前台客服。
|
||||||
|
|
||||||
|
【客户信息】
|
||||||
|
- 姓名: {context.context.get('user_name')}
|
||||||
|
- 航班号: {context.context.get('flight_no')}
|
||||||
|
|
||||||
|
【转接规则】
|
||||||
|
1. 用户说"退票"、"退款"、"取消航班" → 转接到 RefundAgent
|
||||||
|
2. 用户说"改签"、"换航班"、"改时间" → 转接到 ChangeAgent
|
||||||
|
3. 用户想查询某个附近地方的酒店、景点信息时 → 调用 amap_server
|
||||||
|
4. 其他问题由你自己回答
|
||||||
|
|
||||||
|
【重要】
|
||||||
|
- 转接时要明确告知用户:"我帮您转接到XX专员"
|
||||||
|
- 不要重复询问用户意图,识别后立即转接
|
||||||
|
""",
|
||||||
|
mcp_servers=amap_server,
|
||||||
|
model=model,
|
||||||
|
)
|
||||||
|
|
||||||
|
# === 2. 建立 Handoff 网络 ===
|
||||||
|
|
||||||
|
# 前台可转给两位专员
|
||||||
|
triage_agent.handoffs = [refund_agent, change_agent]
|
||||||
|
|
||||||
|
# 专员之间也可互相转接,并能回退到前台
|
||||||
|
refund_agent.handoffs = [change_agent, triage_agent]
|
||||||
|
change_agent.handoffs = [refund_agent, triage_agent]
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# 问题
|
||||||
|
# 1. 命名不能中文
|
||||||
|
# 2. chatgpt默认启用smith,要关闭它
|
||||||
|
# 3. 如果不想关闭,需要配置一个api_key
|
||||||
|
# 4. 哪怕配置好,chatgpt与这边连接延迟较高,tracing追踪也不稳定,而且之前我们已经讲了一个langsmith,所以这里不用它其实更好。
|
||||||
|
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from openai.types.responses import ResponseTextDeltaEvent
|
||||||
|
from agents import Runner
|
||||||
|
from m12_agents_sdk_swarm.agent import triage_agent,amap_server
|
||||||
|
from m12_agents_sdk_swarm.tools import context_variables
|
||||||
|
|
||||||
|
# 不启用Chatgpt官方提供的Tracing(无需配置api_key)
|
||||||
|
from agents import set_tracing_disabled
|
||||||
|
set_tracing_disabled(True)
|
||||||
|
|
||||||
|
# 启用Chatgpt官方提供的Tracing(需要配置api_key)
|
||||||
|
# from config import CHATGPT_API_KEY
|
||||||
|
# from agents import set_tracing_export_api_key
|
||||||
|
# set_tracing_export_api_key(CHATGPT_API_KEY)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
print('✈️ 客服系统启动...\n')
|
||||||
|
messages = [] # 对话历史
|
||||||
|
cur_agent = triage_agent # 当前Agent
|
||||||
|
|
||||||
|
try:
|
||||||
|
await amap_server[0].connect()
|
||||||
|
print("✅ MCP Server (amap) connected.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Failed to connect MCP Server: {e}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# 获取用户输入
|
||||||
|
user_input = input("\nUser: ")
|
||||||
|
if user_input == "quit":
|
||||||
|
break
|
||||||
|
messages.append({"role": "user", "content": user_input})
|
||||||
|
|
||||||
|
# 启动流式响应
|
||||||
|
result = Runner.run_streamed(cur_agent, input=messages, context=context_variables)
|
||||||
|
|
||||||
|
# 状态变量
|
||||||
|
current_agent_name = None # 当前Agent名称
|
||||||
|
is_printing = False # 是否正在打印文本
|
||||||
|
|
||||||
|
# 处理事件流
|
||||||
|
async for event in result.stream_events():
|
||||||
|
|
||||||
|
# 事件1: 文本流(逐Token输出)
|
||||||
|
if event.type == "raw_response_event":
|
||||||
|
if isinstance(event.data, ResponseTextDeltaEvent):
|
||||||
|
# 第一次打印时显示Agent标签
|
||||||
|
if not is_printing:
|
||||||
|
agent_label = f"[{current_agent_name}]" if current_agent_name else ""
|
||||||
|
print(f"🤖 {agent_label} ", end="", flush=True)
|
||||||
|
is_printing = True
|
||||||
|
# 逐字输出
|
||||||
|
print(event.data.delta, end="", flush=True)
|
||||||
|
|
||||||
|
# 事件2: Agent切换
|
||||||
|
elif event.type == "agent_updated_stream_event":
|
||||||
|
new_agent = event.new_agent.name
|
||||||
|
if current_agent_name is None:
|
||||||
|
# 第一次设置Agent名称
|
||||||
|
current_agent_name = new_agent
|
||||||
|
else:
|
||||||
|
# Agent发生切换
|
||||||
|
if is_printing:
|
||||||
|
print() # 先换行
|
||||||
|
is_printing = False
|
||||||
|
print(f"🔀 [系统]: {current_agent_name} → {new_agent}")
|
||||||
|
current_agent_name = new_agent
|
||||||
|
|
||||||
|
# 事件3: 工具调用
|
||||||
|
elif event.type == "run_item_stream_event":
|
||||||
|
# 工具调用开始
|
||||||
|
if event.name == "tool_called":
|
||||||
|
if is_printing:
|
||||||
|
print() # 先换行
|
||||||
|
is_printing = False
|
||||||
|
tool_name = event.item.raw_item.name
|
||||||
|
tool_args = event.item.raw_item.arguments # 获取工具参数 调试需要时可加入
|
||||||
|
# 区分转接工具和业务工具
|
||||||
|
if tool_name.startswith("transfer_"):
|
||||||
|
print(f"📞 [转接]: {tool_name}")
|
||||||
|
else:
|
||||||
|
print(f"🔧 [工具]: {tool_name}")
|
||||||
|
|
||||||
|
# 工具输出结果
|
||||||
|
elif event.name == "tool_output":
|
||||||
|
print(f"✅ [结果]: {event.item.output}")
|
||||||
|
is_printing = False
|
||||||
|
|
||||||
|
# 结束本轮,确保换行
|
||||||
|
if is_printing:
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 更新状态
|
||||||
|
messages = result.to_input_list() # 获取完整对话历史
|
||||||
|
cur_agent = result.last_agent # 获取最后激活的Agent
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from agents import function_tool
|
||||||
|
|
||||||
|
# 全局上下文,所有 Agent 共享
|
||||||
|
context_variables = {
|
||||||
|
"user_name":"张三(白金会员)",
|
||||||
|
"flight_no":"CA1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 业务工具
|
||||||
|
@function_tool
|
||||||
|
def execute_refund():
|
||||||
|
"""执行退款逻辑"""
|
||||||
|
return "✅️ 退款申请已提交,预计3个工作日内到账。"
|
||||||
|
|
||||||
|
@function_tool
|
||||||
|
def check_seat():
|
||||||
|
"""查询座位余量"""
|
||||||
|
return "✅ 明日航班尚有余票。"
|
||||||
+11
-10
@@ -1,18 +1,19 @@
|
|||||||
|
agents==1.4.0
|
||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
huggingface_hub==1.2.1
|
huggingface_hub==1.2.1
|
||||||
langchain==1.1.3
|
langchain==1.2.0
|
||||||
langchain_chroma==1.0.0
|
langchain_chroma==1.1.0
|
||||||
langchain_classic==1.0.0
|
langchain_classic==1.0.0
|
||||||
langchain_community==0.4.1
|
langchain_community==0.4.1
|
||||||
langchain_core==1.1.3
|
langchain_core==1.2.2
|
||||||
langchain_huggingface==1.1.0
|
langchain_huggingface==1.2.0
|
||||||
langchain_mcp_adapters==0.2.1
|
langchain_mcp_adapters==0.2.1
|
||||||
langchain_openai==1.1.1
|
langchain_openai==1.1.5
|
||||||
langchain_text_splitters==1.0.0
|
langchain_text_splitters==1.1.0
|
||||||
langgraph==1.0.4
|
langgraph==1.0.5
|
||||||
langsmith==0.4.58
|
langsmith==0.5.0
|
||||||
mcp==1.23.3
|
mcp==1.24.0
|
||||||
openai==2.9.0
|
openai==2.13.0
|
||||||
pydantic==2.12.5
|
pydantic==2.12.5
|
||||||
python-dotenv==1.2.1
|
python-dotenv==1.2.1
|
||||||
Requests==2.32.5
|
Requests==2.32.5
|
||||||
|
|||||||
Reference in New Issue
Block a user