DLAI-智能体通信协议笔记-全-
DLAI 智能体通信协议笔记(全)
001:1. 课程介绍
在本节课中,我们将要学习代理通信协议(Agent Communication Protocol, ACP)。这是一个由IBM Research的BAI项目合作构建的开源协议,旨在标准化不同代理(Agent)之间的通信与协作。通过本课程,你将学会如何构建和运行通过ACP进行通信的代理,并配置顺序和分层的工作流。
课程概述
欢迎来到ACP课程。本课程由Sandy Besson(AI研究工程师兼生态系统负责人)和Nicholas Rent(IBM AI开发者倡导负责人)共同讲授。我们将一起构建一个通过ACP进行通信和协作的代理团队。

ACP 的核心架构
上一节我们介绍了课程目标,本节中我们来看看ACP的基本工作原理。ACP采用客户端-服务器架构来标准化代理间的交互。
一个代理可以托管在ACP服务器上。该服务器接收来自ACP客户端的REST请求,并将这些请求转发给代理执行。
[用户/进程] -> [ACP客户端] -> [ACP服务器] -> [代理]
ACP客户端本身可以是一个代理或任何其他进程。它通过发现ACP服务器的端点来定位代理,并发起请求。这种统一的客户端-服务器接口旨在标准化跨团队代理之间的通信。
ACP 的实际应用示例
为了理解ACP如何工作,让我们看一个客户服务的多代理系统示例。
以下是该系统可能包含的代理类型:
- 物流代理:由一个团队构建,负责回答与订单状态相关的问题。
- 产品问答代理:由另一个团队构建,使用RAG技术回答与产品相关的通用问题。
- 路由代理:作为ACP客户端运行,处理客户咨询并将其路由到专门的代理。
每个团队都可以通过一个ACP服务器使其代理可用。这样,即使代理使用完全不同的框架构建,甚至后续切换框架,其他团队也无需修改自己的代码,因为所有通信都通过标准化的ACP协议进行。

ACP 的优势与生态
这种标准化也使得将任何现有代理集成到多代理工作流中变得非常容易。你可以将一个现有代理包装在ACP服务器中,从而使ACP客户端能够发现并集成它。这些代理还可以在注册表或代理目录中可见,以提供集中化的列表并简化搜索,尤其适用于大规模部署或企业环境。
ACP是一个开源且开放治理的协议,这意味着没有单一公司控制它。其发展由社区共同塑造。
值得注意的是,ACP可以与模型上下文协议(MCP) 互补使用。一个ACP代理可以使用MCP来访问工具,同时使用ACP与其他代理交互。
本课程实践内容
在接下来的课程实践中,你将逐步构建复杂的多代理工作流。
以下是本课程的主要实践步骤:
- 将一个使用Q AI框架构建的RAG代理包装到ACP服务器中,并通过ACP客户端与其交互,创建一个顺序工作流。
- 将另一个使用Small Agents框架构建的代理包装到第二个ACP服务器中。
- 在ACP客户端中,学习如何发现这两个代理并将它们按顺序链接起来。
- 构建一个分层工作流,使用在ACP客户端中编码的第三个路由代理(使用Small Agents框架),将输入查询路由到指定的专业代理,而非简单顺序执行。
- 扩展Small Agent ACP服务器,使其使用MCP来获取工具访问权限。
- 学习如何发现并将一个现有的ACP代理导入BAI平台(一个用于代理发现的开源注册表)。
总结与展望
本节课中我们一起学习了ACP的基本概念、架构、优势以及本课程的大纲。ACP标准化了代理间的通信,这些代理可能由同一开发者、同一组织的不同团队、甚至不同组织开发。它不仅能用于本地开发环境,还能连接分布式代理。


在下一课中,Sandy将带领我们开始动手实践,深入探索如何构建你的第一个ACP代理。让我们进入下一个视频开始学习吧。
002:为什么需要代理通信协议 🧩
在本节课中,我们将学习开放标准的重要性,了解ACP如何填补不断发展的代理生态中的空白,并探讨它支持的各种用例。

开放标准的重要性与历史背景
上一节我们介绍了课程目标,本节中我们来看看开放标准的历史背景及其价值。
1989年,蒂姆·伯纳斯·李在欧洲核子研究中心工作期间提出了万维网,旨在实现原始数据在万维网上的传输。他开发了第一个版本的HTTP,这是一种用于传输原始数据的传输协议。
在HTTP出现之前,存在一系列如FTP、Telnet和Gopher等协议,它们各有特定的用例。但HTTP因其简单性和开放治理的特性,迅速普及开来。它是推动网络爆炸性增长的催化剂之一。
ACP:连接异构代理的桥梁

正如HTTP连接了不同的托管网页,代理通信协议(ACP)利用HTTP来连接多样化的独立代理生态系统。
它提供了一种方式,让代理能够轻松地相互通信和协作,无论它们基于何种框架构建或利用何种技术。
当前代理生态的挑战与ACP的解决方案
构建复杂的多代理系统,例如执行搜索、写作、编辑或处理工作流的代理,需要与其他代理协作以实现共同目标。虽然有许多框架针对代理开发,但实现不同框架间代理的即插即用能力仍是一个未被满足的需求。
目前,大多数代理缺乏相互通信的共享协议。连接使用不同技术构建的代理,需要开发者构建脆弱且不可重复的集成,这些集成也容易受到框架不断更新的影响。
ACP为代理提供了一个统一的通信接口,无论其使用何种框架。
以下是ACP的核心价值之一:
- 开放治理与社区协作:这意味着它不仅仅是开源(开发者可以查看代码并请求更改,但决策权由单一或一组公司掌握)。ACP的开放治理邀请社区共同规划其发展路线图。因此,ACP成为一个由社区塑造、为社区服务的协议,这与HTTP非常相似。
ACP不仅使开发者无需不断重写集成逻辑而受益,也使最终用户能够获得更好的结果,因为开发者有更多选择来为任务挑选最佳的代理。

ACP支持的关键用例模式
现在,你将了解一些ACP使之成为可能的激动人心的模式。
动态更新代理
鉴于该领域创新速度极快,上个月表现最佳的代理往往不是本月的最佳选择。正如我们刚才讨论的,使代理兼容ACP意味着你可以在系统中更换代理,即使新代理是使用不同技术构建的。这使得你的系统真正具有互操作性,并且对于测试哪些代理在你的系统中能提供最佳性能非常有用。

专业化代理团队协作
专业化代理可以作为一个团队工作。因此,与其构建一个处理所有事情的巨型代理,ACP允许专业化的代理团队动态协作。
以下是其工作方式的示例:
- 如果你有一个研究代理,它与一个能创建精美可视化的代理,以及一个专门为金融建模配置的代理协作。
- 当你请求一份报告时,这些代理可以像团队协作时那样,将任务相互传递。

跨公司工作流
公司依赖许多平台,例如客户关系管理平台、企业资源规划系统、人力资源系统、项目管理工具等等。
以下是其应用场景:
- 例如,如果一家零售公司使用客户支持平台和ERP系统来管理其库存和物流。
- 每个系统都可以有一个专用代理来管理任务。
- 因此,如果客户提交关于延迟发货的投诉,客户支持代理可以识别出它需要库存代理的协助,并使用ACP发送请求。
每个代理都专注于其领域,但通过ACP进行协作。

跨组织协作
ACP也为组织间协作开辟了机会。你将在后续课程中与尼克一起构建一个来自不同组织的代理进行通信的示例。简而言之,ACP可以使来自不同公司的代理安全地协作,即使它们由完全独立的公司托管。随着我们从仅在内部使用代理转向使我们的代理可供外部提供商使用,这开启了一个全新的可能性世界,使公司能够协同工作。
ACP还支持人在回路的协作,以确保你的代理系统能够负责任地按照你的期望运行。
总结与下节预告
本节课中,我们一起学习了开放标准的重要性,探讨了ACP如何通过提供统一接口解决当前代理生态中互操作性差的问题,并介绍了它支持的动态更新、专业化团队协作、跨公司工作流以及跨组织协作等关键用例。

在下一课中,我们将更深入地学习ACP的架构组件和核心原则。
003:核心原则 🧠
在本节课中,我们将要学习代理通信协议(ACP)的核心原则。我们将了解ACP在技术栈中的位置、其整体架构、代理的生命周期,并与其他协议(如MCP和A-to-A)进行比较。
概述
上一节我们介绍了ACP的价值和应用场景。本节中,我们来看看ACP的技术架构和核心工作原理。理解这些概念将帮助你构建符合ACP规范的代理。
ACP在技术栈中的位置
要真正掌握ACP的工作原理,理解它在智能体系统中所处的位置很有帮助。一个智能体系统通常包含基础模型、存储、代理编排、应用层等组件。
下图展示了ACP位于应用层之下,有时被称为操作层。它使用HTTP和REST架构风格来促进独立系统之间的通信。

ACP的架构与交互
ACP基于客户端-服务器架构,使用简单的REST接口。这意味着客户端负责发起通信,服务器则响应该请求。在ACP中,客户端和服务器都可以是AI代理、人类或微服务。客户端总是发起请求,服务器总是响应请求。
以下是一个基本的ACP交互示例,其中服务器主机是一个AI代理:
- ACP客户端发现可用的服务器。
- 客户端向ACP服务器发送一个REST请求。
- 服务器(封装了一个AI代理)处理请求。
- 服务器通过REST将响应返回给客户端。
随着系统变得复杂,我们可以看到服务于不同目的的多层协议如何协同工作。
在下图中,我们看到一个客户端通过REST向一个封装了代理的ACP服务器发出请求。该代理确定需要调用一个工具,因此它向一个可用的MCP服务器发送请求以执行工具调用并返回结果。一旦ACP代理完成其运行,它将输出返回给ACP客户端。

核心概念详解
在下一课深入构建你的第一个符合ACP规范的代理之前,我们先了解一些核心概念,这将帮助你更深入地理解协议的工作原理。
代理详情
代理详情是你定义代理基本身份和能力的地方。你可以指定代理名称、提供描述,并添加可选的元数据以获取额外信息。这些基本信息使你的代理在ACP生态系统中可被发现和使用。
代理详情支持在线和离线发现:
- 在线发现:发生在ACP服务器已经在运行,并且可以通过其API端点访问时。
- 离线发现:发生在更高级别,例如在代理目录或注册表中。代理详情被嵌入到代理包中,允许用户或系统在无需代理首先运行的情况下发现它们。这使得可以创建代理目录,在那里可以浏览、选择代理,并在需要时启动。
激活与执行
要激活你的代理并启用其在线发现,你需要部署它。你可以使用SDK内置服务器(这是更容易上手的方式)或使用外部服务器。启动ACP服务器使代理可供ACP客户端运行。内置服务器将是你在本课程中主要使用的。
一旦代理被激活,它就准备好执行,意味着它可以主动处理请求并生成响应。ACP提供三种执行模式:同步、异步和流式。
以下是每种模式的说明:
- 同步模式:客户端等待代理完成其运行,然后返回最终结果。
- 异步模式:客户端不等待代理响应,它可以在后台继续执行其他任务。
- 流式模式:服务器建立一个SSE(服务器发送事件)连接,在代理生成结果时提供实时更新。
无论采用哪种执行模式,每次运行都会经历诸如“进行中”、“等待中”等状态,并最终达到终止状态,例如“已完成”或“已失败”。
代理通信协议在设计时考虑了生产级环境,因此它优先考虑安全性、可扩展性和可观测性,以确保在大规模下的可靠性能。
ACP、MCP与A-to-A协议对比
一个常见的问题是:使用ACP、MCP或A-to-A有什么区别?简而言之,MCP旨在通过工具、资源和提示来丰富单个模型的上下文。但MCP和ACP是兼容的,甚至是互补的。
一个代理可能在需要进行工具调用时使用MCP,该调用会向代理返回更多上下文,然后该代理可能决定需要与另一个代理通信,这时它通过ACP进行。然而,MCP与ACP是解耦的,因为它并非主要为代理间通信而设计。例如,截至目前,MCP协议的设计并未轻易支持一个代理从另一个代理接管任务或进行点对点协作。
关于共享内存,MCP支持会话管理,意味着服务器可以是有状态的,并在请求之间维护有关客户端会话的信息。然而,MCP本身不处理状态。ACP SDK支持用于运行和会话的集中式存储,这意味着多个ACP服务器可以跨这些运行或会话持久化信息。
关于消息结构,MCP对消息格式没有特定要求。由于代理需要支持自然语言以及其他模态,ACP消息遵循多模态结构来交换内容。
谷歌的A-to-A协议在ACP之后不久推出,也旨在标准化代理之间的通信。两种协议都有实现多代理系统的共同目标,但在理念和治理上存在分歧。
以下是一些显著的差异:
- 治理:A-to-A和ACP都是开源的,但ACP由Linux基金会公开治理,确保其开放、中立并以社区参与为中心。
- 服务器模型:在ACP中,你可以在同一服务器上运行多个代理,减少了整体设置和管理工作。而在A-to-A中,每个代理必须使用单独的服务器运行。
- 架构风格:ACP遵循基于REST的架构风格,支持标准的Web基础设施和模式,增强了可扩展性和互操作性。A-to-A使用JSON-RPC风格,这可能引入更多复杂性。
- 输出与历史:在A-to-A中,代理输出和消息历史是分开的。这使得在不实施额外持久化信息方法的情况下,很难确定多代理轮次中的事件顺序,而这对于透明度和可观测性非常重要。
- 执行模式:ACP支持多种代理类型,并允许开发者在同步、异步和流式模式之间明确选择。这使得响应处理可预测,并简化了通过SSE保证流式传输的客户端逻辑。A-to-A也支持无状态和有状态代理,但交互模式由代理动态决定。因此,客户端必须构建得足够灵活,以根据代理能力处理同步、异步或流式响应。
尽管ACP和A-to-A存在一些差异,但重要的是强调它们有很多共同点。你在本课程中学到的关于ACP的知识在很大程度上也适用于A-to-A。
总结
本节课中,我们一起学习了ACP的核心原则。你现在应该能够根据你的目标和系统配置,判断何时使用ACP、MCP或A-to-A。重要的是要记住,这些协议都是活跃且快速发展的项目,在本课程录制时的情况可能很快会过时。因此,请务必进行自己的研究,并通过实验验证任何假设。


恭喜你完成了理论部分的学习,现在你已经准备好进入课程的实践环节了。
004:使用CrewAI构建RAG代理 🛠️
在本节课中,我们将学习如何使用CrewAI框架构建一个检索增强生成(RAG)代理。这个代理将专注于处理保险覆盖范围相关的查询,并为我们后续将其转换为符合ACP协议的代理打下基础。



概述
在深入探讨代理通信协议(ACP)之前,我们首先需要一个代理。因此,本节课程将指导您逐步构建一个基于CrewAI的RAG代理。我们将从导入必要的依赖项开始,配置语言模型和嵌入模型,创建RAG工具,定义代理角色和任务,最终运行代理并获得查询结果。
导入依赖项
首先,我们需要导入构建代理所需的核心组件。以下是构建RAG代理所需的关键依赖项。
from crewai import Crew, Agent, Task, LLM
from crewai_tools import RagTool
import warnings
warnings.filterwarnings('ignore')
- Crew: 构成多代理系统架构的基础。我们将从单个代理开始,但使用Crew来启动整个流程。
- Task: 用于定义任务,我们将在此处传递提示词和期望的输出。
- Agent: 代表我们的代理,包含LLM(大语言模型)和工具。
- LLM: 语言模型提供者,用于创建代理。
- RagTool: CrewAI自带的工具,用于简化构建RAG系统的过程。
- warnings: 用于忽略运行过程中可能产生的不必要警告信息。
配置语言模型(LLM)
检索增强生成的核心在于拥有一个向量数据库,查询该数据库,获取上下文信息,并将其传递给语言模型以生成输出。因此,我们需要配置一个LLM。
llm = LLM(
model="openai/gpt-4",
max_tokens=1024
)
- model: 指定使用的模型。本例中使用OpenAI的GPT-4,但您也可以使用其他提供商(如Anthropic的Claude等)。
- max_tokens: 设置LLM生成内容的最大令牌数。您可以根据需要调整此数值以控制生成内容的长度。
配置RAG工具
接下来,我们需要为RAG工具定义配置,包括用于生成输出的LLM和用于嵌入文档块的嵌入模型。
config = {
"llm": {
"provider": "openai",
"config": {
"model": "gpt-4"
}
},
"embedding_model": {
"provider": "openai",
"config": {
"model": "text-embedding-ada-002"
}
}
}
- llm: 指定生成答案所使用的语言模型及其配置。
- embedding_model: 指定将文档块转换为向量以存入数据库并进行搜索的嵌入模型。



创建RAG工具并加载文档
配置完成后,我们可以实例化RAG工具,并将包含保险信息的文档加载到向量数据库中。
rag_tool = RagTool(
config=config,
chunk_size=1200
)
rag_tool.add(
file_path="./data/gold_hospital_and_premium_extras.pdf",
data_type="pdf_file"
)
- chunk_size: 将文档分割成块的大小(以令牌计)。设置为1200,并包含一定的重叠,以确保不会在不理想的位置切断文本块。
- add方法: 用于将特定文档(本例中是一个关于保险包含项的PDF文件)添加到向量数据库中。您需要根据实际文件路径和名称进行修改。
定义代理
现在,我们开始定义代理本身。在CrewAI中,代理的定义非常详细,需要指定角色、目标、背景故事等属性。
insurance_agent = Agent(
role="高级保险覆盖范围助理",
goal="确定某项内容是否在保险覆盖范围内",
backstory="您是一位专业的保险代理,旨在协助处理保险覆盖范围查询。",
verbose=True,
allow_delegation=False,
llm=llm,
tools=[rag_tool],
max_retry_limit=5
)
- role: 代理扮演的角色,本例中为“高级保险覆盖范围助理”。
- goal: 代理的核心目标,即判断保险是否覆盖某项内容或相关等待期。
- backstory: 代理的背景故事,有助于塑造其行为和响应方式。
- verbose: 设置为True,以便在代理运行时查看进度信息。
- allow_delegation: 设置为False,表示此代理不会将任务委托给其他代理。
- tools: 代理可使用的工具列表,这里包含了我们之前创建的
rag_tool。 - max_retry_limit: 设置代理尝试获取答案的最大重试次数,作为一种保护机制,防止过多的API调用。
定义任务
任务封装了我们要向代理提出的具体问题(提示词),并定义了期望的输出格式。
task1 = Task(
description="康复治疗的等待期是多久?",
expected_output="对用户问题的全面回答。",
agent=insurance_agent
)
- description: 传递给代理的具体提示词或问题。您可以将其更改为文档中存在的其他问题。
- expected_output: 描述期望的回答风格,例如全面的回答、摘要或要点列表。
- agent: 指定执行此任务的代理。
创建Crew并执行任务
最后,我们将代理和任务封装到一个Crew中,并启动执行流程。
crew = Crew(
agents=[insurance_agent],
tasks=[task1],
verbose=True
)
task_output = crew.kickoff()
print(task_output)
- Crew: 将代理和任务组织在一起。虽然目前只有一个代理和一个任务,但该架构支持多代理多任务。
- kickoff(): 启动Crew执行任务,类似于在聊天界面中按下回车键。
- 打印输出: 显示任务的最终执行结果。
运行上述代码后,您将看到代理开始工作,从RAG系统中检索相关文档块,并生成最终答案。例如,对于“康复治疗的等待期是多久?”这个问题,代理可能会返回:“根据此保险政策,康复治疗的等待期为两个月。但是,如果康复治疗需求是由于既存状况引起的,则等待期延长至12个月。”
总结

在本节课中,我们一起学习了如何使用CrewAI框架构建一个完整的检索增强生成(RAG)代理。我们从导入依赖项开始,逐步配置了语言模型和嵌入模型,创建了RAG工具并加载了保险文档,然后定义了具有特定角色和目标的代理,设置了具体的查询任务,最后通过Crew组织并执行了整个流程。这个RAG代理能够基于提供的文档知识库,智能地回答关于保险覆盖范围和等待期的具体问题,为我们后续将其集成到更复杂的代理通信协议(ACP)系统中奠定了坚实的基础。您可以通过修改提示词、代理配置或加载不同的文档来定制此代理以满足特定需求。
005:将RAG智能体封装为ACP服务器 🚀
在本节课中,我们将学习如何将之前构建的RAG智能体封装成一个符合ACP标准的服务器。这将使我们的智能体能够通过统一的协议与其他组织的智能体进行通信。
概述







上一节我们完成了RAG智能体的构建。本节中,我们将把这个智能体转换为一个ACP服务器。通过这种方式,我们可以让不同组织的智能体相互通信,这正是ACP协议的优势所在。
导出现有代码
首先,我们需要将现有的智能体代码导出为一个独立的Python文件。以下是具体步骤:
以下是代码导出步骤:
- 将第3课中编写的保险RAG智能体代码保存到一个文件中。
- 我们将使用一个特定的函数,将当前单元格的内容复制到一个Python脚本中。
- 导出的文件将命名为
crew_agent_server.py。
导入ACP依赖项
要将我们的智能体转换为ACP服务器,需要引入一些关键的依赖库。
以下是需要导入的核心模块:
AsyncGenerator来自collections.abc:用于定义服务器的输出类型。Message和MessagePart来自acp库:用于格式化智能体的输出结果。run_yield、run_yield_resume和Server来自acp.server库:run_yield和run_yield_resume构成异步生成器的一部分,展示了ACP服务器的输出类型;Server类则是我们ACP服务器的基础。
导入代码如下:
from collections.abc import AsyncGenerator
from acp import Message, MessagePart
from acp.server import run_yield, run_yield_resume, Server
创建ACP服务器并包装智能体
现在,我们可以开始创建ACP服务器并包装现有的智能体功能。
首先,创建一个服务器实例:
server = Server()
接下来,使用装饰器将我们的智能体函数声明为服务器上的可用智能体。智能体在服务器中的名称基于我们为函数起的名字,同时可以通过文档字符串提供元数据。
我们将函数命名为 policy_agent,并使其成为异步函数。它接收一个消息列表作为输入,并返回一个异步生成器。
智能体的用途描述通过文档字符串提供,当其他用户查询此智能体的元数据时,ACP服务器会返回这些信息。
核心包装代码如下:
@server.agent
async def policy_agent(input: list[Message]) -> AsyncGenerator[run_yield | run_yield_resume, None]:
"""
这是一个用于处理保单覆盖范围相关问题的智能体。
它使用RAG模式,基于保单文档寻找答案。
你可以用它来帮助回答关于保险覆盖范围和等待期的问题。
"""
# 动态获取用户提示,而非使用固定提示
user_prompt = input[0].parts[0].content
# 原有的任务设置,现在使用动态提示
task = AgentTask(
description=user_prompt, # 替换原有的固定字符串
agent=researcher_agent,
expected_output="A detailed answer based on the policy documents."
)
# 异步执行智能体任务
result = await crew.kickoff()
# 以消息形式返回结果
yield Message(parts=[MessagePart(content=str(result))])
运行为独立服务器
代码封装完成后,我们需要将其运行为一个独立的服务器。

我们使用常见的Python结构来启动服务器,并指定运行端口(例如8001),以便后续可以启动其他服务器进行串联调用。
服务器启动代码如下:
if __name__ == "__main__":
server.run(port=8001)


运行导出后的 crew_agent_server.py 文件,如果成功,终端将显示服务器正在本地主机的指定端口上运行。
请注意:在本学习环境中,服务器启动后会运行120分钟。如果你离开后返回本节课,可能需要重新启动它。如果在本地运行,服务器将持续运行直到你停止Python脚本。
总结



本节课中,我们一起学习了将RAG智能体封装为ACP服务器的完整流程。我们导出了现有代码,引入了必要的ACP库依赖,使用装饰器包装了智能体函数使其符合ACP标准,并最终将其启动为一个独立的网络服务。现在,我们的保险策略智能体已经可以通过ACP协议被访问,为下一课中通过ACP客户端调用它并与医院智能体通信做好了准备。
006: 使用客户端调用ACP代理 🚀
在本节课中,我们将学习如何使用ACP客户端来调用我们已经搭建好的ACP服务器。我们将创建一个标准化的客户端,并通过它向服务器上的代理发送请求。



确认服务器状态 🔍
上一节我们介绍了如何搭建ACP服务器。在开始调用之前,首先需要确认服务器是否仍在运行。
以下是确认服务器状态的步骤:


- 打开终端。
- 运行与启动服务器时相同的命令。
- 检查终端输出,确认服务器进程是否活跃。


如果服务器运行在Deep Learning Dojo AI环境中,它会持续运行120分钟。如果在本地运行,只要Python脚本未停止,服务器就会一直运行。
构建ACP客户端 🛠️
确认服务器正常运行后,接下来我们开始构建ACP客户端。客户端的作用是与ACP服务器的端点进行标准化通信。
首先,我们需要导入必要的库。为了能在Jupyter Notebook环境中运行异步调用,我们需要导入asyncio。同时,我们还会从ACP SDK中导入客户端模块,并使用colorama库来美化终端输出,使结果更清晰易读。



以下是构建客户端所需的导入语句和初始化步骤:
import asyncio
from acp import Client
from colorama import Fore, Style
创建异步调用函数 ⚡
我们将创建一个异步函数来执行对服务器的调用。这个函数会连接到我们的ACP服务器,并向指定的代理发送请求。



以下是创建异步函数example的步骤:
- 定义异步函数
async def example():。 - 在函数内部,设置服务器的基本URL(例如
http://localhost:8001)。 - 使用这个URL初始化一个
Client实例。 - 使用
await关键字,通过客户端异步调用服务器上名为policy_agent的代理。 - 调用时,需要传入一个提示词作为输入参数,例如:
"what is the waiting period for rehabilitation"。 - 最后,打印出代理返回的结果。我们使用
colorama将输出文本设置为黄色,以便于识别。
完整的函数代码如下:
async def example():
base_url = "http://localhost:8001"
client = Client(base_url=base_url)
# 调用名为 ‘policy_agent‘ 的代理
response = await client.run(agent="policy_agent", prompt="what is the waiting period for rehabilitation")
# 打印返回结果
print(Fore.YELLOW + response[0].content + Style.RESET_ALL)



执行客户端调用 ▶️

函数定义完成后,我们需要执行它来触发对服务器的调用。
使用 asyncio.run() 方法来运行我们定义的 example 函数:
asyncio.run(example())
当执行这段代码时,你可以同时观察两个地方:
- Jupyter Notebook 单元格输出:这里将以黄色文本打印出代理的回复。
- 运行ACP服务器的终端窗口:你会看到新的请求日志出现,显示提示词已被接收并正在处理。
如果一切正常,客户端将成功收到服务器的响应,例如:“The waiting period for rehabilitation in the insurance policy is two months.” 这证明我们已成功使用ACP客户端与服务器完成了通信。

总结 📝




本节课中我们一起学习了如何使用ACP客户端。我们首先确认了服务器的运行状态,然后构建了一个标准化的客户端,并创建了一个异步函数来调用服务器上的特定代理。通过传递提示词并接收响应,我们完成了从客户端到服务器的完整调用流程。这为后续连接多个代理和服务器奠定了基础。你可以尝试修改提示词或上传不同的文档,来测试代理的不同功能。
007:将Smolagents代理封装为ACP服务器 🏥
在本节课中,我们将学习如何创建第二个ACP服务器,并使用不同的代理框架(Smolagents)来构建一个专注于医疗健康领域的代理。我们将看到如何让多个ACP服务器协同工作,模拟不同组织(如医院和保险公司)之间的交互。
概述
上一节我们介绍了如何创建保险公司的ACP服务器。本节中,我们将创建第二个服务器——医院ACP服务器,并开始学习如何顺序调用这些代理,以实现跨组织的互操作性。



创建医院ACP服务器
首先,我们需要创建第二个服务器。这个服务器将专注于医院场景,与我们的保险服务器进行交互。这种模式在实际中很常见,不同组织或团队可能使用不同的ACP服务器进行通信。
以下是创建医院ACP服务器的步骤:
1. 导入依赖项
首先,我们需要导入必要的依赖项。从ACP的角度看,这些依赖与保险服务器类似,但代理部分会有所不同。
from collections.abc import AsyncGenerator
from acp.models import Message, MessagePart
from acp_sdk import run_yield, run_yield_resume, Server
from smolagents import CodeAgent, DuckDuckGoSearchTool, LiteLLMModel, VisitWebpageTool
AsyncGenerator用于定义代理函数的返回类型。Message和MessagePart用于构建ACP服务器的标准化输出。run_yield,run_yield_resume,Server是ACP SDK的核心组件,用于创建服务器和处理异步生成器。- 从
smolagents框架导入CodeAgent(代码代理)、DuckDuckGoSearchTool(搜索工具)、LiteLLMModel(轻量级LLM模型)和VisitWebpageTool(网页访问工具)。这展示了ACP兼容不同代理框架的能力。
2. 初始化服务器和LLM
接下来,我们初始化ACP服务器实例和语言模型。
server = Server()
model = LiteLLMModel(model_id='openai/gpt-4')
max_tokens = 2048
- 创建
Server实例。 - 使用
LiteLLMModel初始化模型,这里指定为openai/gpt-4。你也可以替换为其他模型,如ollama或本地模型。 - 设置代理的最大令牌数为2048。
3. 定义健康代理
现在,我们使用装饰器定义代理函数。这与定义保险代理的过程类似。
@server.agent
async def health_agent(input: list[Message]) -> AsyncGenerator[Message, None]:
"""
这是一个代码代理,旨在帮助医院处理基于健康的问题。
当前或潜在的患者可以使用它来获取关于健康和医院治疗的财务答案。
"""
- 使用
@server.agent装饰器将函数注册为服务器上的一个代理。 - 函数命名为
health_agent(之前是policy_agent)。客户端将通过此名称调用代理。 - 输入参数
input是一个Message列表,这是ACP的标准输入格式。 - 返回类型为
AsyncGenerator[Message, None],用于流式输出。 - 文档字符串(Docstring)至关重要,它提供了代理的元数据,描述其功能。
4. 构建代理逻辑
在函数内部,我们构建代理的核心逻辑。
agent = CodeAgent(
tools=[DuckDuckGoSearchTool(), VisitWebpageTool()],
model=model
)
prompt = input[0].parts[0].content
response = await agent.run(prompt)
yield Message(parts=[MessagePart(content=str(response))])
- 创建
CodeAgent实例,它能够编写Python函数来调用各种工具。 - 将
DuckDuckGoSearchTool和VisitWebpageTool作为工具传递给代理,使其能够访问互联网信息。 - 将之前定义的
model赋值给代理。 - 从ACP标准输入
input中解包出用户提示(prompt)。input[0].parts[0].content提取了第一个消息的第一个部分的内容。 - 使用
agent.run(prompt)运行代理,并等待结果。 - 最后,使用
yield返回一个ACP标准格式的Message作为输出。MessagePart的content被设置为代理的响应。
5. 运行服务器
最后,添加运行服务器的代码。
if __name__ == "__main__":
server.run(port=8000)
- 当脚本直接运行时,启动服务器。
- 指定运行在端口
8000上(保险服务器运行在8001端口,以避免冲突)。
启动医院服务器
创建好服务器文件(例如 hospital_server.py)后,需要在终端中启动它。
如果你在本地运行,只需打开一个新的终端窗口(在Mac上是新的bash终端,在Windows上是新的PowerShell窗口),导航到项目目录,然后运行:

uv run hospital_server.py


如果一切正常,你将看到医院ACP服务器成功运行在 http://localhost:8000 上。
总结


本节课中,我们一起学习了如何创建第二个ACP服务器。我们使用 Smolagents框架 构建了一个医院健康代理,它能够处理健康相关的查询并利用网络搜索工具。通过将服务器运行在不同的端口(8000),我们实现了与保险服务器(8001)的并存。现在,我们已经拥有了两个可以独立响应请求的ACP服务器,为后续学习代理间的链式调用和互操作性打下了基础。
008:串联代理调用 🔗
概述
在本节课中,我们将学习如何将运行在不同框架上的多个ACP服务器连接起来,并实现它们之间的顺序调用。我们将从一个医院服务器获取信息,然后将该信息作为上下文传递给一个保险服务器,从而构建一个连贯的工作流程。




检查服务器状态
上一节我们介绍了如何分别运行保险和医院ACP服务器。本节中,我们来看看如何让它们协同工作。首先,我们需要确保两个服务器都在正常运行。



以下是检查步骤:
- 检查保险服务器:保险服务器使用CrewAI框架,是一个检索增强生成(RAG)代理,运行在终端1的端口8001上。确认其处于运行状态。
- 检查医院服务器:医院服务器使用SmalAgents框架,是一个代码代理,运行在终端2的端口8000上。确认其同样处于运行状态。
两个服务器使用不同的框架,这展示了ACP能够兼容多种后端技术。


构建串联调用
现在,核心部分来了:如何让两个服务器对话,并将一个调用的输出作为另一个调用的输入。
我们将使用异步编程来实现顺序调用。首先,我们会调用医院服务器,询问“肩部手术后是否需要康复治疗”。然后,我们将获取其回答,并将其作为上下文传递给保险服务器的策略代理,询问“康复治疗的等待期是多久”。

以下是实现串联调用的关键代码步骤:
import asyncio
from acp_sdk import Client
from colorama import Fore, Style
async def hospital_workflow():
# 1. 创建连接到两个服务器的客户端
insurer_client = await Client.connect("localhost:8001")
hospital_client = await Client.connect("localhost:8000")
# 2. 第一个调用:询问医院服务器
hospital_run = await hospital_client.run(
agent_id="health_agent",
input="do I need rehabilitation after a shoulder reconstruction"
)
# 提取回答内容
hospital_answer = hospital_run.output[0].parts[0].content
print(Fore.LIGHTMAGENTA_EX + hospital_answer + Style.RESET_ALL)
# 3. 第二个调用:将医院回答作为上下文传递给保险服务器
insurance_prompt = f"Context: {hospital_answer}\n\nQuestion: What is the waiting period for rehabilitation?"
insurance_run = await insurer_client.run(
agent_id="policy_agent",
input=insurance_prompt
)
# 提取保险回答内容
insurance_answer = insurance_run.output[0].parts[0].content
print(Fore.YELLOW + insurance_answer + Style.RESET_ALL)
# 运行工作流
asyncio.run(hospital_workflow())
代码解释:
Client.connect():创建与指定地址和端口的ACP服务器的连接。client.run():向指定的代理发送请求并执行任务。run.output[0].parts[0].content:从ACP响应中提取文本内容的标准方式。- 我们使用
colorama库为不同服务器的输出添加颜色,以便在终端中清晰区分。
运行与结果
当我们执行上述代码时,工作流程如下:
- 首先触发对医院服务器的调用。我们看到它进行了网络搜索并返回结果:“是的,肩部重建手术后需要康复治疗。”(以亮粉色显示)。
- 接着,这个答案被自动作为上下文,触发对保险服务器的调用。保险代理处理信息后返回:“根据金牌保险计划,肩部重建手术的典型等待期是两个月。”(以黄色显示)。



通过这个例子,我们成功地串联了两次ACP调用。这展示了将不同流程、甚至不同组织内的专用代理连接起来的可能性。您可以根据需要,以类似方式构建更复杂的多步骤工作流。




总结
本节课中我们一起学习了ACP的核心优势之一:跨框架、跨服务器的代理协同。我们掌握了如何:
- 同时连接多个独立的ACP服务器。
- 使用异步编程顺序调用不同的代理。
- 将一个代理的输出作为上下文,无缝传递给下一个代理。


这种方法为构建模块化、可扩展的复杂AI应用系统提供了强大的基础。
009:使用路由代理进行分层链式代理调用 🚀


概述
在本节课中,我们将学习如何构建一个更智能的代理调用系统。之前我们已经通过顺序调用来连接不同的ACP服务器。现在,我们将引入一个“路由代理”(Router Agent),让它能够自动分析用户的问题,并智能地决定应该调用哪个服务器上的哪个代理来获取最佳答案。这种方法被称为“分层链式调用”。

准备工作:确保服务器运行


上一节我们介绍了顺序调用,本节中我们来看看如何让代理自动路由请求。首先,我们需要确保所有必要的ACP服务器都在正常运行。

以下是需要检查的服务器:
- 保险服务器:运行在本地端口8001上。
- 医院服务器:运行在本地端口8000上。
请打开终端,分别确认这两个服务器进程是否处于活动状态。如果通过本课程环境运行,请注意有120分钟的时间限制;若在本地运行,则时间取决于Python脚本的执行。
构建分层工作流
准备工作完成后,我们现在开始构建核心的分层调用工作流。主要思路是创建一个“路由代理”,它会接收一个复合问题,自动将其分解,并决定调用哪个后端代理。
首先,我们需要导入必要的依赖库。
import asyncio
import nest_asyncio
from acp_sdk import Client
from small_agents import LiteLLM
from fast_acp import AgentCollection, ACPCallingAgent
from colorama import Fore, init
代码解释:
asyncio和nest_asyncio:用于处理异步调用。acp_sdk.Client:用于连接ACP服务器。small_agents.LiteLLM:我们将使用它提供的模型类(例如GPT-4)。你也可以直接使用LiteLLM的补全功能。fast_acp:这是一个示例模块,包含了我们需要的AgentCollection(用于收集和管理代理)和ACPCallingAgent(即我们的路由代理)。colorama:用于在终端输出彩色文本,提升可读性。
ACPCallingAgent 是这个工作流的核心。它的作用类似于Small Agents中的工具调用代理,但它的“工具”是其他ACP代理。它接收一个代理列表和一个语言模型,能够自动规划如何调用这些代理来回答复杂问题。
实现医院工作流
接下来,我们定义一个异步函数来实现完整的分层调用流程。
async def run_hospital_workflow():
# 1. 连接到两个ACP服务器
insurer_client = Client(“http://localhost:8001”)
hospital_client = Client(“http://localhost:8000”)
# 2. 发现所有可用的代理
collection = await AgentCollection.from_acp_call([insurer_client, hospital_client])
# 3. 整理代理信息,便于路由代理调用
acp_agents = {}
for client, agent in collection.agents:
acp_agents[agent.name] = {“agent”: agent, “client”: client}
# 4. 定义路由代理使用的语言模型
model = LiteLLM(“gpt-4”) # 可根据需要更换模型
# 5. 创建路由代理实例
router_agent = ACPCallingAgent(agents=acp_agents, model=model)
# 6. 向路由代理提出复合问题
final_result = await router_agent.acp_agent_call(
“do I need rehabilitation after a shoulder reconstruction and what is the waiting period from my insurance?”
)
# 7. 打印最终结果
print(f”{Fore.YELLOW}Final Result: {final_result}{Fore.RESET}”)
关键步骤分析:
- 连接客户端:创建两个客户端对象,分别指向保险和医院服务器。
- 代理发现:
AgentCollection.from_acp_call方法会自动联系所有服务器,获取其上所有已注册代理的详细信息(包括名称、描述等)。 - 代理整理:我们将代理和其对应的客户端绑定在一起。这样,当路由代理决定调用“policy_agent”时,它能知道应该使用
insurer_client去调用。 - & 5. 创建路由代理:使用发现的代理列表和选定的LLM(如GPT-4)实例化路由代理。
- 执行调用:向路由代理提出一个复合问题:“肩关节重建后是否需要康复治疗,以及我的保险等待期是多久?”。路由代理会分析这个问题,将其拆解,并自动调用
health_agent(医院服务器)来回答康复必要性,调用policy_agent(保险服务器)来回答等待期问题。 - 输出结果:将整合后的答案打印出来。
运行这个工作流,你将看到路由代理自动在两个服务器间导航,并返回一个统一的答案,例如:“是的,肩关节重建后通常需要康复治疗...关于保险,康复治疗的等待期是两个月...”。



总结




本节课中我们一起学习了ACP的分层链式调用。我们通过构建一个路由代理(ACPCallingAgent),实现了智能的代理调用路由。与手动顺序调用相比,这种方法能够:
- 自动问题分解:将复杂的复合问题拆解成子问题。
- 智能路由:根据代理的描述和能力,自动选择最合适的代理来回答每个子问题。
- 结果整合:将来自不同服务器的答案整合成一个连贯的最终回复。

这使得基于ACP构建的代理系统更加灵活和强大,能够处理更复杂的多步骤查询任务。
010:将MCP集成到医院服务器 🏥
在本节课中,我们将学习如何将模型上下文协议(MCP)与代理通信协议(ACP)结合使用。我们将通过改造一个现有的医院ACP服务器,使其能够调用一个专门用于查找医生的MCP服务器,来演示这两种协议如何协同工作。


概述
之前我们已经介绍了顺序调用和分层调用。现在,我们将探讨如何让ACP和MCP协同工作。ACP定义了代理之间的通信协议,而MCP则主要定义了工具(Tools)的通信协议。通过将它们结合,我们可以构建更强大、功能更丰富的代理系统。
创建MCP服务器 🛠️
首先,我们需要创建一个MCP服务器。这个服务器将提供一个工具,允许用户根据所在州(美国)查找附近的医生。
我们将使用Jupyter notebook的魔法函数将MCP服务器代码写入项目目录,以便后续ACP服务器能通过标准输入输出(STDIO)与其通信。



以下是创建MCP服务器的步骤:
-
导入必要的库:我们需要
colorama用于终端格式化,fastmcp用于构建MCP服务器,json用于处理数据,requests用于从GitHub获取医生数据。import colorama from mcp import fastmcp import json import requests -
初始化MCP服务器:创建一个名为
doctor_server的MCP服务器实例。mcp = fastmcp.FastMCP("doctor_server") -
定义工具函数:使用
@mcp.tool()装饰器创建一个名为list_doctors的工具。该工具接收一个州名缩写(字符串),并返回一个字符串格式的医生列表。@mcp.tool() def list_doctors(state: str) -> str: """ 此工具返回您附近可能存在的医生。 它接收一个您居住地的两字母州代码。 返回一个您附近可能存在的医生列表。 """ # 从GitHub获取医生数据 url = "https://raw.githubusercontent.com/Nicknoack/ACP-walkthrough/refs/heads/main/doctors.json" response = requests.get(url) doctors = json.loads(response.text) # 根据州筛选医生 matches = [] for doctor in doctors.values(): if doctor["address"]["state"] == state: matches.append(doctor) # 将匹配的医生列表转换为字符串返回 return str(matches) -
运行服务器:确保服务器在
__name__ == "__main__"条件下以STDIO传输方式运行。if __name__ == "__main__": mcp.run(transport="stdio")
运行此单元格后,MCP服务器代码将被写入my_acp_project目录,供后续ACP服务器调用。
更新ACP服务器以集成MCP 🤝
上一节我们创建了MCP服务器,本节我们将更新现有的医院ACP服务器,添加一个能利用该MCP服务器的第二个代理。
首先,我们需要在ACP服务器代码中引入新的依赖。
# 原有的Small Agent导入
from smallagents import CodeAgent, GoogleSearchTool, VisitWebPageTool
# 新增导入,用于支持MCP
from smallagents import ToolCallingAgent, ToolCollection
from mcp import StdioServerParameters
接下来,我们需要配置如何连接到刚刚创建的MCP服务器。
# 定义连接到MCP服务器的参数
server_params = StdioServerParameters(
command="uv", # 使用uv工具
args=["run", "mcp_server.py"], # 运行MCP服务器的命令参数
env=None # 无特殊环境变量
)
现在,我们可以在ACP服务器中定义第二个代理。第一个代理health_agent处理一般的医院查询,第二个代理doctor_agent将专门用于查找医生。
以下是定义doctor_agent的步骤:
- 使用代理装饰器:使用
@server_agent装饰器定义一个新的异步函数doctor_agent。 - 创建工具集合:使用
with语句和ToolCollection.from_mcp方法连接到MCP服务器,并获取其工具。 - 初始化工具调用代理:使用
ToolCallingAgent,并将从MCP服务器获取的工具传递给它。 - 处理请求并返回响应:从输入中提取用户提示(prompt),交给代理运行,并将结果返回给用户。
@server_agent
async def doctor_agent(input: List[Message]) -> AsyncGenerator[Message, None]:
"""
这是一个医生代理,帮助用户查找附近的医生。
"""
# 连接到MCP服务器并获取工具集合
async with ToolCollection.from_mcp(server_params, trust_remote_code=True) as tool_collection:
# 创建工具调用代理,传入MCP工具和语言模型
agent = ToolCallingAgent(
tools=[*tool_collection.tools.values()], # 解包所有工具
model=model # 使用配置的模型,如GPT-4
)
# 从输入中提取用户提示
prompt = input[0].parts[0].content
# 运行代理获取响应
response = await agent.run(prompt)
# 将响应返回给用户
yield Message(parts=[MessagePart(content=response)])
更新并保存ACP服务器代码后,需要重启服务器以使更改生效。在终端中运行:
uv run smallagents_server.py
服务器将在端口8000上重新启动。



调用集成MCP的ACP代理 📞
服务器更新并运行后,我们现在可以编写客户端代码来调用新的doctor_agent。


首先,导入必要的客户端依赖。
import asyncio
import nest_asyncio
from acp_sdk import Client
from colorama import Fore, Style
nest_asyncio.apply() # 允许在Jupyter等环境中嵌套事件循环


然后,定义一个异步函数来执行工作流。关键点在于指定要调用的代理为doctor_agent,并传递一个包含地点和需求的提示。
async def run_doctor_workflow() -> None:
async with Client(base_url="http://localhost:8000") as hospital_client:
# 同步调用doctor_agent
run1 = await hospital_client.run_sync(
agent="doctor_agent", # 指定调用新创建的医生代理
prompt="I'm based in Atlanta, Georgia. Are there any cardiologists near me?" # 用户查询
)
# 提取并打印响应内容
content = run1.output.messages[0].parts[0].content
print(Fore.LIGHTMAGENTA_EX + content + Style.RESET_ALL)
# 运行工作流
asyncio.run(run_doctor_workflow())
执行此代码后,doctor_agent会通过MCP服务器查询数据,并返回在佐治亚州亚特兰大附近的医生(包括心脏科医生)信息。
总结

在本节课中,我们一起学习了如何将MCP与ACP结合使用。

- 创建MCP服务器:我们构建了一个独立的MCP服务器,它提供了一个
list_doctors工具,能够根据州名筛选并返回医生数据。 - 更新ACP服务器:我们在现有的医院ACP服务器中添加了第二个代理
doctor_agent。该代理利用ToolCallingAgent和从MCP服务器动态获取的工具,扩展了服务器的功能。 - 进行调用测试:我们通过ACP SDK客户端,指定
doctor_agent并发送查询,成功获取了基于位置的医生信息,验证了ACP与MCP的协同工作。



通过这个实例,你掌握了如何让专注于代理间通信的ACP与专注于工具标准化的MCP相互配合,从而构建出既能进行复杂对话又能调用专用工具功能的强大智能代理系统。你可以尝试修改提示词,查询其他州或特定类型的医生,以进一步探索该系统的能力。
011:管理符合ACP规范的代理
概述
在本节课中,我们将学习如何将我们构建的符合ACP规范的代理集成到B平台中,实现集中管理和通过用户界面运行。我们将了解注册表的概念,并实践将代理添加到B平台注册表,以及通过命令行和图形界面调用这些代理。
什么是注册表?
上一节我们介绍了如何通过Python客户端发现ACP服务器上的代理。但还有其他方法可以实现这一点,其中之一就是通过注册表。
可以将注册表视为一个中心化的仓库,你可以访问其中具有不同用例、工具和能力的各种代理。注册表还允许你集中管理、部署和搜索代理。在即将展示的注册表案例中,你还可以执行离线发现。这意味着你能够在没有网络连接的情况下搜索代理。


你将学习的注册表由B平台提供。它拥有内置的注册表,同时也提供了一个用户界面来运行和管理代理,并提供了使代理符合ACP规范的能力,以便让它们按顺序和分层方式执行任务。
安装B平台
根据你运行的操作系统,有几种不同的安装方式。
如果你运行在MacOS或Linux上,可以按照这里的说明进行安装。如果你使用Windows,则可以按照这组说明进行安装。

现在,要启动B平台,你可以运行 b_platform start 并按照设置说明进行操作。我将使用X AI,特别是平台上的Llama。你可以使用任何你喜欢的模型。
探索B平台内置代理
安装完成后,你会立即获得一些流行的内置代理,例如用于研究的G研究员、用于编程的Ada,以及用于创建播客的播客创建代理。
如果我们选择其中一个,比如Ada,我们可以传递提示并运行它,然后你就得到了你的代理运行结果。
集成自定义ACP代理
问题是,你已经付出了所有努力来构建自己的符合ACP规范的代理。那么,你能将它们添加到B平台内部的注册表中吗?当然可以,让我们开始操作。
我们将把我们的ACP代理引入B平台。为此,首先我在本地机器上复制了DeepLearning.AI环境,并更新了它以使用一些不同的语言模型。所以现在,我在我的小型代理服务器中使用了WatsonX的Llama实例,在我的CrewAI代理服务器中也做了同样的事情。
现在,我想向你展示B平台的可能性。我们要做的第一件事,或者我要带你运行的第一个命令是,如果你想在本地运行,如何安装它。
为此,你可以运行 b_install b_ai 并继续执行,它应该会在你的机器上运行。然后,你可以通过运行 b_ai platform start 来启动它,这将启动B AI服务器。之后,你将能够运行 b_ai 及其所有衍生命令。
如果我们先运行 b_ai,你可以看到我有许多不同的选项和命令。我们将在本课中重点关注代理命令。
在这里,如果我运行 b_ai list,我能够列出所有可用的不同代理。让我们试试看。运行 b_ai list 后,你可以看到B AI中预装了许多代理,例如代理文档创建器、Ada聊天、G研究员等等。
现在,我们可以通过运行 b_ai run 加上你想运行的代理名称来实际运行这些代理。但我想重点使用你在这些课程中已经构建的代理,而不是使用这些内置的。
使代理符合B平台规范
在我们运行之前,让我们先让我们已经创建的ACP代理符合B平台的规范。
所以,在我们的健康代理服务器内部,我将导入元数据能力。这将允许我们定义与该特定代理关联的文档以及如何运行它。
导入元数据能力后,我们将滚动到我们的健康代理。现在,在代理装饰器内部,我将设置 metadata 关键字参数,并将其等于我们的元数据类。然后我们将设置UI值。UI类型将是一个“hands-off”代理。还有其他类型的代理,例如聊天代理。用户问候语将是“Ask your health question”。
我也将为我们的小型代理服务器中创建的医生代理设置相同或稍类似的元数据。我将设置元数据,再次将UI设置为“hands-off”代理,然后我们将为此设置用户问候语:“Find a doctor. Past your query and state here”。
现在,让我们快速为我们的CrewAI代理做同样的事情。我们将再次在这里导入元数据,然后在我们的代理装饰器内部设置相同的参数。我们将设置 metadata 关键字参数,提供元数据类,然后将UI类型设置为“hands-off”,问候语会略有不同:“Got a question about your policy? Ask here”。
启动代理服务器并注册
在设置好我们的代理之后,关于这一点的一个美妙之处是,你可能已经看到,当我们构建最后一个代理并为其更新MCP时,我们实际上并没有注册代理。这是因为B平台当时没有在DeepLearning.AI平台上运行,但我现在在本地运行它。那么这实际上意味着什么呢?
如果我启动我们的两个代理服务器。让我们启动我们的小型代理服务器。运行 uvicorn small_agent_server:app,我们应该会启动并运行一个服务器。看,我们正在8000端口上运行,但我们得到了所有这些额外信息。我们稍后会回来看这个。
让我们也启动我们的CrewAI代理服务器。运行 uvicorn crew_agent_server:app。我们收到一点警告,但没关系。看起来我们现在正在运行。
现在,关于这一点很酷的是,如果我们运行 b_ai,让我们实际上将 b_ai 移到它自己单独的终端中。如果我们现在运行 b_ai list,看,我们现在有了我们的医生代理、健康代理和政策代理,现在它们被描述或现在在BAI中可用。
通过B平台运行代理
所以,如果我们想运行其中一个代理,例如,让我们运行我们的医生代理,我们可以运行 b_ai run doctor_agent。看,我们有一个问候语:“Find a doctor. Past your query and state”。让我们尝试使用我们实际构建服务器时使用的相同提示。所以输入:“I'm based in Atlanta, Georgia, are there any cardiologists near me?”。
如果我们看一下我们的服务器,看,它实际上已经启动了。看起来我们得到了一个响应。如果我们跳回来,看起来我们得到了一个响应:“Yes, there are several cardiologists near you in Atlanta, Georgia. One of them is Dr. Sarah Mitche, who's board certified and has 15 years of experience.”。
所以你可以看到,我们现在能够将我们的代理添加到注册表,并且也能够运行它们。如果我们想运行我们的政策代理,例如,我们可以这样做。运行 b_ai run policy_agent。你可以看到我们那里有问候语:“Got a question about your policy? Ask here”。所以我们可能会问:“What is the waiting period on physiotherapy?”。
如果我们跳回我们的服务器,你可以看到它现在正在运行。看起来它正在搜索我们的知识库。实际上我不确定我们的向量数据库中是否有这个信息。但让我们看看是否能得到回复。看,我们的代理正在运行。我们得到最终回复了吗?看:“The waiting period on physiotherapy is two months. Group Physiotherapy is covered with a limit of $35 per visit.”。
通过用户界面运行代理
我们现在已经使用B平台呈现了一个响应,但是我们也能在UI中使用这个吗?实际上,如果我们运行 b_ai ui,这将打开一个单独的UI,你可以在这里使用服务器。如果我们看一下,我们所有的代理都会在这里呈现。
所以我们有我们的医生代理、健康代理,也应该有我们的政策代理。如果我们在这里运行它,我们可以问:“What is the waiting period on...”,我们想说什么来着?“What is the waiting period on...”,我不知道,“dental”?实际上,我们还是坚持用“rehabilitation”吧。
如果我们运行这个,这将启动它。如果我们看一下我们的服务器,看,它们正在运行,但我们现在是在UI内部运行它。我们得到了最终结果:“The waiting period for rehabilitation, which falls under the hospital substitution programs is generally two months of continuous cover unless it's preexisting condition in which case it is 12 months.”。
总结
本节课中,我们一起学习了如何将自定义构建的ACP代理集成到B平台中。我们了解了注册表作为中心化管理工具的作用,实践了通过添加元数据使代理符合B平台规范,并成功通过命令行和图形用户界面调用和运行了这些代理。你现在已经掌握了将代理纳入注册表并在一个设计良好的用户界面中运行它们的能力。

注意:一定要去查看课程末尾的资源部分。我会确保链接到文档、ACP协议、B平台以及我的ACP GitHub仓库,还有一些其他有用的资源。
012: 总结与后续步骤 🎯
在本节课中,我们将对《代理通信协议》课程进行总结,并了解如何参与到ACP的社区生态中,继续你的学习和构建之旅。
课程回顾 📚
上一节我们完成了对ACP的实践探索。本节中,我们一起来回顾整个课程的核心收获。

我们希望,通过动手构建ACP代理,能够激发你的创造力,并让你对代理之间有效协作所能实现的可能性感到兴奋。在整个课程中,你探索了代理通信协议的价值所在,学习了一些其核心原则,并且实际构建并运行了你自己的ACP代理。
加入ACP社区 🚀
如果你已准备好迈出下一步,加入正在塑造ACP未来的、不断壮大的社区,以下是你参与的方式。
以下是你可以参与的几种途径:
- 保持关注:通过查看Github代码仓库来跟进最新动态。
- 加入讨论:加入Discord频道,并参与Github上的讨论。
- 展示成果:如果你构建了很酷的东西,请在“展示与讲述”环节与我们分享。
- 直接贡献:查看标记为“需要帮助”的公开问题并参与解决。
- 发起讨论:如果你有自己的想法想要探索,可以发起一个新的讨论。

提供反馈与展望未来 💡
最后,请提供反馈,我们积极鼓励这样做。如果你遇到错误或问题,请在Github上提交一个问题,我们将尽力快速解决。
ACP是开放治理、社区驱动的。它由社区构建,服务于社区。因此,无论你是构建者、贡献者,还是仅仅感到好奇,在这个生态系统中都有你的一席之地。
感谢你与我们一同学习。我们迫不及待想看到你接下来会构建出什么。


本节课总结:本节课中我们一起回顾了ACP课程的核心内容,学习了如何通过Github、Discord等渠道加入ACP社区并进行贡献、讨论与反馈。ACP作为一个由社区驱动的开放协议,欢迎所有人的参与。

浙公网安备 33010602011771号