A2A:对话与对话服务器
在上一章(01_mesop_based_ui_.md)中,大家探讨了 Mesop 如何帮助大家为 A2A 项目构建友好的用户界面。现在,让大家把注意力转向当你实际与人工智能代理聊天时幕后发生的事情。大家将讨论两个关键部分:
- “对话”,它代表用户与人工智能之间的一系列消息。
- “对话服务器”,这是一个专门的迷你服务器,用于管理这些对话,并处理诸如发送消息、流式传输更新以及检索过往交互等任务。
为什么大家需要对话?
设想你正在构建一个聊天页面,用户可以在上面向人工智能代理提出一系列问题。你希望保留所有这些问答的连续记录,这样如果用户重新加载页面或稍后返回,他们可以看到之前问了什么以及得到了怎样的回答。“对话”就是这些消息的一个容器。
然而,对话可能会变得很复杂:
- 你可能会有多个聊天会话(每个“对话”一个)。
- 一些消息可能会触发后台任务(比如获取地图或总结文本)。
- 实时更新或流式传输的标记需要立即显示在对话中。
协调所有这些可能会很混乱。这就是“对话服务器”发挥作用的地方。它跟踪每个对话的消息,并管理与这些消息相关的任务/事件。
一个简单的用例
假设你想让用户与人工智能开始一个新的对话:
- 用户点击“开始新对话”。
- 用户界面调用大家的对话服务器,告诉它创建一个新的聊天会话(有自己的 ID)。
- 用户输入一条消息,服务器将其保存在新对话的记录中。
- 之后,大家可以重新获取对话以显示到目前为止的所有消息。
关键概念
1. 对话
对话本质上是:
- 一个 ID,比如“abc123”。
- 一个用户/代理消息列表,每条消息可能包含文本、图像或其他数据。
对话让你能够从上次中断的地方继续。服务器将它们存储在内存中(或者如果你愿意,也可以存储在数据库中),这样你可以在任何时候重新访问它们。
2. 对话服务器
可以把对话服务器想象成你所有对话的“交通管制员”。它:
- 收到请求时创建新的对话。
- 将消息存储在相应的对话中,以便你稍后可以检索它们。
- 在后台处理传入的任务(比如“总结这段文本”)。
- 将事件(例如,部分流式传输响应)发送回用户界面,这样用户可以实时看到更新。
如何使用对话服务器
假设大家有一个 Mesop 页面,允许你开始一个新的对话。当用户点击按钮时,大家的代码可能会调用一个服务器端点,比如“/conversation/create”:
def start_new_conversation(_event):
import requests
response = requests.post("http://localhost:8000/conversation/create")
data = response.json()
conversation_id = data["result"]["conversation_id"]
# 现在将 'conversation_id' 存储在 Mesop 状态或局部变量中
解释:
- 大家向“/conversation/create”发送一个简单的 POST 请求。
- 对话服务器响应一个新创建的对话 ID。
- 大家保存这个 ID,以便大家可以将未来的消息附加到正确的对话中。
接下来,要向大家的代理发送一条消息:
def send_message(conversation_id, user_text):
msg_payload = {"params": {
"text": user_text,
"metadata": {"conversation_id": conversation_id}
}}
requests.post("http://localhost:8000/message/send", json=msg_payload)
解释:
- 大家在消息的元数据中包含对话 ID,这样服务器就知道将其存储在哪里。
- 服务器处理该消息,可能会生成任务或发送部分响应,大家稍后可以获取这些内容。
最后,要列出一个对话中的所有消息:
def list_messages(conversation_id):
payload = {"params": conversation_id}
resp = requests.post("http://localhost:8000/message/list", json=payload)
return resp.json()["result"]
解释:
- 大家再次传递对话 ID,这样大家只会获取相关的消息。
- 服务器返回一个消息列表,你可以在用户界面中显示这些消息(例如,聊天气泡)。
幕后(序列概述)
下面是一个当用户发送消息时发生情况的小例子:
- 用户界面将消息发送到对话服务器。
- 对话服务器与一个内部管理器(比如 ADKHostManager)进行通信,该管理器处理人工智能任务。
- 服务器返回一个响应,表示消息已被接收。
- 与此同时,任务或流式传输事件在幕后继续更新对话。
窥探真实实现
下面,你将看到“demo/ui/service/server/server.py”中的一个简化代码片段,它定义了大家的对话服务器:
from fastapi import APIRouter, Request
from service.types import CreateConversationResponse
class ConversationServer:
def __init__(self, router: APIRouter):
# 管理器决定如何处理所有事情(代理、消息、任务)
self.manager = ...
router.add_api_route("/conversation/create",
self._create_conversation,
methods=["POST"])
# 对于 /message/send、/message/list 等类似处理
def _create_conversation(self):
conversation = self.manager.create_conversation()
return CreateConversationResponse(result=conversation)
解释:
ConversationServer
构造函数连接了各种 POST 路由(比如/conversation/create
)。- 当一个请求到达
/conversation/create
时,会调用_create_conversation()
函数。 - 该函数委托给
manager.create_conversation()
,然后将结果包装在一个响应对象中。
下面是 ApplicationManager
(在“demo/ui/service/server/application_manager.py”中)的一部分:
class ApplicationManager(ABC):
@abstractmethod
def create_conversation(self) -> Conversation:
pass
@abstractmethod
async def process_message(self, message: Message):
pass
# ...
解释:
ApplicationManager
是一个抽象基类。不同的实现决定如何存储、处理和检索消息(例如,使用实际的人工智能或模拟)。create_conversation()
方法由对话服务器调用,以创建一个新的对话对象。process_message()
方法用于处理传入的用户消息(这可能会运行任务、发送部分回复等)。
整合所有内容
当你的用户界面调用“/conversation/create”时,对话服务器会调用 manager.create_conversation()
。该管理器返回一个带有 ID 的对话对象。用户界面使用这个 ID 将后续消息发送到“/message/send”。每当你想要对话的历史记录时,你使用相同的 ID 调用“/message/list”。在幕后,服务器组织你的消息和任务,确保一切清晰一致。
结论和下一步
在本章中,你了解到:
- “对话”如何对相关消息进行分组。
- 为什么“对话服务器”就像一个用于存储消息和编排任务的迷你聊天服务器。
- 如何创建新的对话、发送消息以及列出它们。
- 管理器在幕后做了什么来处理你的请求。
接下来,大家将看看 A2A 项目如何使用 主机代理编排器 来编排代理。在那里,你将看到如何管理多个代理以及它们如何与大家的对话流程相联系!
本文由博客一文多发平台 OpenWrite 发布!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。