摘要
LangBot支持多种即时通信平台,包括QQ、微信、Discord、Telegram等,这得益于其灵活的平台适配器架构。本文将详细介绍LangBot多平台适配器的设计原理和开发方法,帮助开发者理解如何为新的消息平台开发适配器,或将LangBot集成到自定义的通信系统中。我们将通过实际示例,演示适配器的开发过程,并介绍相关的接口和最佳实践。
正文
1. 平台适配器概述
LangBot通过平台适配器(Platform Adapter)实现对不同即时通信平台的支持。适配器作为平台和LangBot核心之间的桥梁,负责:
- 消息接收:从平台接收用户消息并转换为LangBot内部格式
- 消息发送:将LangBot的回复转换为平台格式并发送
- 事件处理:处理平台特定的事件(如成员加入、离开等)
- 状态管理:管理与平台的连接状态
2. 系统架构
LangBot的平台适配器架构如下图所示:
3. 核心组件
3.1 平台管理器(PlatformManager)
平台管理器负责管理所有平台适配器的生命周期:
class PlatformManager:
"""平台管理器"""
def __init__(self, ap: app.Application):
self.ap = ap
self.adapters: dict[str, PlatformAdapter] = {}
self.bots: list[Bot] = []
async def initialize(self):
"""初始化平台管理器"""
# 加载平台配置
await self.load_platforms_from_config()
async def load_platforms_from_config(self):
"""从配置加载平台"""
# 实现细节...
async def run(self):
"""运行所有平台适配器"""
tasks = []
for adapter in self.adapters.values():
tasks.append(
self.ap.task_mgr.create_task(
adapter.run(),
name=f'platform-adapter-{adapter.platform_name}',
scopes=[core_entities.LifecycleControlScope.PLATFORM]
)
)
if tasks:
await asyncio.gather(*tasks)
3.2 平台适配器基类
所有平台适配器都继承自统一的基类:
class PlatformAdapter(metaclass=abc.ABCMeta):
"""平台适配器基类"""
ap: app.Application
platform_name: str
platform_slug: str
def __init__(self, ap: app.Application):
self.ap = ap
@abc.abstractmethod
async def run(self):
"""运行适配器"""
raise NotImplementedError
@abc.abstractmethod
async def reply_message(
self,
message_source: MessageEvent,
message: MessageChain,
quote_origin: bool = False,
):
"""回复消息"""
raise NotImplementedError
@abc.abstractmethod
async def send_message(
self,
target_type: str,
target_id: str,
message: MessageChain,
):
"""发送消息"""
raise NotImplementedError
@abc.abstractmethod
async def is_stream_output_supported(self) -> bool:
"""是否支持流式输出"""
raise NotImplementedError
@abc.abstractmethod
async def reply_message_chunk(
self,
message_source: MessageEvent,
bot_message: BotMessage,
message: MessageChain,
quote_origin: bool = False,
is_final: bool = False,
):
"""流式回复消息"""
raise NotImplementedError
4. 适配器开发实践
4.1 Discord适配器示例
让我们以Discord适配器为例,了解适配器的实现:
class DiscordAdapter(PlatformAdapter):
"""Discord平台适配器"""
def __init__(self, ap: app.Application):
super().__init__(ap)
self.platform_name = "Discord"
self.platform_slug = "discord"
self.client: discord.Client = None
self.bot: Bot = None
async def run(self):
"""运行Discord适配器"""
# 初始化Discord客户端
intents = discord.Intents.default()
intents.message_content = True
self.client = discord.Client(intents=intents)
# 注册事件处理器
@self.client.event
async def on_message(message: discord.Message):
await self._handle_message(message)
# 启动客户端
await self.client.start(self.bot.config.get("token"))
async def _handle_message(self, message: discord.Message):
"""处理Discord消息"""
# 忽略机器人自己的消息
if message.author == self.client.user:
return
# 构造LangBot消息事件
message_event = GroupMessage(
launcher_type=LauncherTypes.GROUP,
launcher_id=str(message.channel.id),
sender_id=str(message.author.id),
message_id=str(message.id),
message_chain=MessageChain([
Plain(text=message.content)
]),
sender_info={
"username": message.author.name,
"discriminator": message.author.discriminator,
"avatar": str(message.author.avatar.url) if message.author.avatar else None
}
)
# 创建查询对象
query = Query(
query_id=str(uuid.uuid4()),
launcher_type=LauncherTypes.GROUP,
launcher_id=str(message.channel.id),
sender_id=str(message.author.id),
message_event=message_event,
message_chain=message_event.message_chain,
adapter=self
)
# 将查询添加到队列
async with self.ap.query_pool:
self.ap.query_pool.put_query(query)
self.ap.query_pool.condition.notify_all()
async def reply_message(
self,
message_source: MessageEvent,
message: MessageChain,
quote_origin: bool = False,
):
"""回复消息"""
# 获取频道
channel = self.client.get_channel(int(message_source.launcher_id))
if not channel:
channel = await self.client.fetch_channel(int(message_source.launcher_id))
# 转换消息内容
content = ""
files = []
for component in message:
if isinstance(component, Plain):
content += component.text
# 处理其他消息组件...
# 发送消息
await channel.send(content=content, files=files)
async def send_message(
self,
target_type: str,
target_id: str,
message: MessageChain,
):
"""发送消息"""
if target_type == "group":
channel = self.client.get_channel(int(target_id))
if not channel:
channel = await self.client.fetch_channel(int(target_id))
# 转换并发送消息
content = "".join([
component.text for component in message
if isinstance(component, Plain)
])
await channel.send(content=content)
async def is_stream_output_supported(self) -> bool:
"""是否支持流式输出"""
return True
async def reply_message_chunk(
self,
message_source: MessageEvent,
bot_message: BotMessage,
message: MessageChain,
quote_origin: bool = False,
is_final: bool = False,
):
"""流式回复消息"""
# 实现流式消息发送逻辑
# 这里可以使用Discord的编辑消息功能实现流式输出
pass
4.2 消息组件转换
不同平台支持的消息组件类型不同,适配器需要处理这些差异:
class MessageConverter:
"""消息转换器"""
@staticmethod
def langbot_to_platform(message_chain: MessageChain, platform: str) -> dict:
"""
将LangBot消息链转换为平台特定格式
Args:
message_chain: LangBot消息链
platform: 目标平台
Returns:
平台特定格式的消息
"""
if platform == "discord":
return MessageConverter._to_discord_format(message_chain)
elif platform == "telegram":
return MessageConverter._to_telegram_format(message_chain)
elif platform == "qq":
return MessageConverter._to_qq_format(message_chain)
else:
# 默认文本格式
return {
"content": "".join([
component.text for component in message_chain
if isinstance(component, Plain)
])
}
@staticmethod
def _to_discord_format(message_chain: MessageChain) -> dict:
"""转换为Discord格式"""
content = ""
embeds = []
files = []
for component in message_chain:
if isinstance(component, Plain):
content += component.text
elif isinstance(component, Image):
# 处理图片
files.append(discord.File(component.url))
elif isinstance(component, Embed):
# 处理嵌入
embed = discord.Embed(
title=component.title,
description=component.description,
url=component.url
)
if component.image:
embed.set_image(url=component.image)
embeds.append(embed)
return {
"content": content,
"embeds": embeds,
"files": files
}
5. 开发自定义适配器
5.1 创建适配器类
要为新平台开发适配器,需要创建一个新的适配器类:
@adapter.platform_adapter_class("custom-platform")
class CustomPlatformAdapter(PlatformAdapter):
"""自定义平台适配器"""
def __init__(self, ap: app.Application):
super().__init__(ap)
self.platform_name = "自定义平台"
self.platform_slug = "custom"
# 初始化平台特定的客户端或连接
async def run(self):
"""运行适配器"""
# 实现平台连接和消息监听逻辑
while True:
# 监听平台消息
message = await self._listen_for_messages()
if message:
await self._handle_message(message)
async def _listen_for_messages(self):
"""监听平台消息"""
# 实现具体的消息监听逻辑
pass
async def _handle_message(self, message):
"""处理平台消息"""
# 将平台消息转换为LangBot格式
message_event = self._convert_to_langbot_format(message)
# 创建查询对象并加入队列
query = Query(
query_id=str(uuid.uuid4()),
launcher_type=message_event.launcher_type,
launcher_id=message_event.launcher_id,
sender_id=message_event.sender_id,
message_event=message_event,
message_chain=message_event.message_chain,
adapter=self
)
async with self.ap.query_pool:
self.ap.query_pool.put_query(query)
self.ap.query_pool.condition.notify_all()
async def reply_message(
self,
message_source: MessageEvent,
message: MessageChain,
quote_origin: bool = False,
):
"""回复消息"""
# 将LangBot消息转换为平台格式并发送
platform_message = self._convert_to_platform_format(message)
await self._send_message(message_source, platform_message)
async def send_message(
self,
target_type: str,
target_id: str,
message: MessageChain,
):
"""发送消息"""
# 实现消息发送逻辑
pass
async def is_stream_output_supported(self) -> bool:
"""是否支持流式输出"""
return False
async def reply_message_chunk(
self,
message_source: MessageEvent,
bot_message: BotMessage,
message: MessageChain,
quote_origin: bool = False,
is_final: bool = False,
):
"""流式回复消息"""
# 如果支持流式输出,实现相关逻辑
pass
def _convert_to_langbot_format(self, platform_message) -> MessageEvent:
"""将平台消息转换为LangBot格式"""
# 实现转换逻辑
pass
def _convert_to_platform_format(self, message_chain: MessageChain):
"""将LangBot消息转换为平台格式"""
# 实现转换逻辑
pass
async def _send_message(self, target, message):
"""发送消息到平台"""
# 实现具体的消息发送逻辑
pass
5.2 注册适配器
开发完成后,需要将适配器注册到系统中:
# 在适配器模块的初始化代码中注册
preregistered_adapters = {}
def platform_adapter_class(name: str):
"""平台适配器类装饰器"""
def decorator(cls):
preregistered_adapters[name] = cls
return cls
return decorator
6. 配置管理
适配器通常需要配置信息,如API密钥、访问令牌等:
# platform.yaml 配置示例
platforms:
- name: "自定义平台"
adapter: "custom-platform"
config:
api_key: "your-api-key"
base_url: "https://api.custom-platform.com"
timeout: 30
在适配器中读取配置:
async def initialize(self, platform_config: dict):
"""初始化适配器"""
self.api_key = platform_config.get("api_key")
self.base_url = platform_config.get("base_url", "https://api.custom-platform.com")
self.timeout = platform_config.get("timeout", 30)
# 使用配置初始化客户端
self.client = CustomPlatformClient(
api_key=self.api_key,
base_url=self.base_url,
timeout=self.timeout
)
7. 错误处理和重试机制
良好的适配器需要实现错误处理和重试机制:
class RobustPlatformAdapter(PlatformAdapter):
"""具备错误处理能力的平台适配器"""
async def _send_with_retry(self, message, max_retries=3):
"""带重试机制的消息发送"""
for attempt in range(max_retries + 1):
try:
await self._send_message(message)
return True
except NetworkError as e:
if attempt == max_retries:
self.ap.logger.error(f"发送消息失败,已重试{max_retries}次: {e}")
return False
# 指数退避
await asyncio.sleep(2 ** attempt)
except PlatformError as e:
# 平台特定错误,记录日志但不重试
self.ap.logger.error(f"平台错误: {e}")
return False
8. 性能优化
8.1 连接池
对于需要维护连接的平台,可以使用连接池:
class ConnectionPool:
"""连接池"""
def __init__(self, max_connections: int = 10):
self.max_connections = max_connections
self.connections = asyncio.Queue(maxsize=max_connections)
self.created_connections = 0
async def get_connection(self):
"""获取连接"""
try:
return self.connections.get_nowait()
except asyncio.QueueEmpty:
if self.created_connections < self.max_connections:
conn = await self._create_connection()
self.created_connections += 1
return conn
else:
return await self.connections.get()
async def release_connection(self, conn):
"""释放连接"""
try:
self.connections.put_nowait(conn)
except asyncio.QueueFull:
# 连接池已满,关闭连接
await self._close_connection(conn)
async def _create_connection(self):
"""创建新连接"""
# 实现连接创建逻辑
pass
async def _close_connection(self, conn):
"""关闭连接"""
# 实现连接关闭逻辑
pass
8.2 批量处理
对于支持批量操作的平台,可以实现批量处理:
async def send_messages_batch(self, messages: list):
"""批量发送消息"""
# 将消息分批处理
batch_size = 10
for i in range(0, len(messages), batch_size):
batch = messages[i:i+batch_size]
await self._send_batch(batch)
总结
LangBot的多平台适配器架构为开发者提供了灵活的扩展机制,通过遵循统一的接口规范,可以轻松地为新平台开发适配器。关键要点包括:
- 统一接口:所有适配器都实现相同的基类接口
- 事件驱动:采用事件驱动模型处理消息和事件
- 消息转换:处理不同平台间的消息格式转换
- 错误处理:实现健壮的错误处理和重试机制
- 性能优化:使用连接池、批量处理等技术优化性能
通过合理利用LangBot的平台适配器机制,开发者可以将LangBot集成到任何消息平台,构建跨平台的智能聊天机器人应用。

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



