智能体间通讯协议 (ACP) 的语义自描述演进:超越 JSON 的新范式
在分布式系统和人工智能领域,智能体(Agent)作为自主、反应、主动、社会化的实体,其间的有效通信是构建复杂智能系统的基石。传统的通信协议,如基于 HTTP 和 JSON 的 RESTful API,在数据交换方面表现出色,但随着智能体系统复杂性的提升,它们在语义理解和互操作性方面的局限性日益凸显。本次讲座将深入探讨现有协议的不足,并提出一种超越 JSON 的、具备语义自描述能力的智能体间通讯协议(Agent Communication Protocols, ACP)设计,旨在实现真正的语义互操作性。
1. 现有智能体通讯协议的挑战:JSON 的局限性
当前,JSON (JavaScript Object Notation) 因其轻量、易读、易解析的特性,已成为智能体乃至绝大多数分布式系统间数据交换的首选格式。结合 HTTP 等传输协议,它构成了事实上的标准。然而,当我们将视角从简单的数据交换转向复杂的智能体交互时,JSON 的局限性便浮现出来:
- 缺乏固有语义(Lack of Intrinsic Semantics):JSON 本质上是一种数据结构描述语言,它描述的是数据的语法结构,而非其意义。例如,
{"temperature": 25, "unit": "C"}只有在开发者双方预先约定了temperature指的是“温度”且unit指的是“单位”时才有效。这种语义是隐含在代码和文档中的,而非协议自身的一部分。 - 互操作性瓶颈(Interoperability Bottleneck):由于缺乏共享的、机器可理解的语义,不同智能体或系统在交互时需要大量的预先约定和数据映射。当系统规模增大、参与方增多时,维护这些映射将变得极其复杂且易错。一个智能体可能将
temp理解为“临时文件”,而另一个将其理解为“温度”。 - 脆弱的演进能力(Fragile Evolution):当数据模型发生变化时,例如增加一个字段或修改一个字段的含义,所有依赖该模型的智能体都需要同步更新。这种紧耦合的设计使得系统演进变得困难且风险高。
- 上下文缺失(Missing Context):JSON 消息通常是独立的,缺乏对整个对话或交互过程的上下文感知。智能体需要自行维护对话状态,并将其与接收到的独立消息关联起来,增加了逻辑复杂性。
- 难以进行自动化推理(Difficulty in Automated Reasoning):由于缺乏形式化的语义描述,智能体难以对接收到的信息进行自动化的语义理解、验证和推理。例如,无法自动判断
25摄氏度是否在某个设备的有效工作温度范围内,除非硬编码了这些规则。
这些挑战在智能体领域尤为突出,因为智能体被设计为自主且能够进行复杂协作。它们需要能够理解彼此的意图、请求、承诺,并根据这些语义信息做出决策。FIPA ACL (Foundation for Intelligent Physical Agents Agent Communication Language) 等协议虽然提供了更丰富的通讯原语(performatives),但其内容语言(content language)通常仍需依赖于某种数据序列化格式(如字符串、XML或JSON),而这些格式本身并不提供深层语义。
为了克服这些局限性,我们需要一种全新的智能体通讯协议,它能够将语义信息内嵌到协议本身,实现真正的“语义自描述”。
2. 语义自描述通讯协议的核心原则与设计目标
语义自描述的ACP旨在让智能体在接收消息时,不仅能解析其语法结构,还能理解其背后的含义,无需依赖外部文档或人工解释。这通过以下核心原则实现:
- 本体驱动(Ontology-Driven):使用形式化的本体论(Ontology)来定义智能体领域内的概念、关系和规则。本体是共享的、明确的、机器可读的知识表示。
- 语义内容(Semantic Content):消息的实际内容不再是简单的键值对,而是以本体定义的概念和关系构成的知识图谱片段。
- 协议语义化(Protocol Semantification):通讯原语(performatives)及其参数的含义也被本体明确定义,使得智能体能理解一个“请求”具体是请求什么,以及如何回应。
- 上下文感知(Context-Awareness):协议应支持显式地传递和引用对话上下文,帮助智能体理解消息在整个交互序列中的作用。
基于这些原则,我们设定以下设计目标:
- 高表达性(High Expressiveness):能够精确、无歧义地表达复杂的概念、意图和状态。
- 语义互操作性(Semantic Interoperability):不同智能体,即使由不同开发者开发,也能基于共享本体理解彼此消息。
- 可演进性(Evolvability):支持本体和协议的灵活演进,最小化对现有系统的影响。
- 可验证性(Verifiability):能够自动验证消息的语义一致性和有效性。
- 效率与可伸缩性(Efficiency & Scalability):在保证语义丰富性的前提下,优化消息大小和处理速度,并支持大规模智能体系统的部署。
- 安全性(Security):提供消息的认证、授权、完整性和机密性保障。
- 人类可读性(Human Readability):在调试和开发阶段,协议消息应尽可能地对人类友好,便于理解。
3. 新型ACP的核心组件与架构设计
为了实现上述目标,我们提出的新型ACP将是一个分层、模块化的设计,其核心是基于语义网技术(如RDF、OWL)来构建消息内容和协议元数据。
3.1 协议分层架构
我们将协议栈分为以下几层:
| 层级名称 | 描述 | 主要职责 |
|---|---|---|
| 应用层 | 智能体应用程序逻辑,执行具体任务。 | 基于语义消息进行决策、规划、执行业务逻辑。 |
| 语义层 | 负责消息的语义解析、构建和验证。 | 使用本体解析和构建消息内容(知识图谱片段),验证语义一致性,映射到应用层概念。 |
| 消息层 | 定义消息的结构、通讯原语(Performatives)和会话管理。 | 封装语义内容,管理消息头、元数据、会话ID,处理消息的生命周期(请求、回应、拒绝等)。 |
| 序列化层 | 将消息层构建的逻辑结构转换为字节流,并进行反序列化。 | 选择高效、支持语义的序列化格式(如CBOR-LD, Turtle)。 |
| 传输层 | 负责消息的物理传输,提供可靠性、连接管理等。 | 使用HTTP/2, WebSockets, gRPC, MQTT等,或更低层的TCP/UDP。可选择支持加密传输(TLS)。 |
3.2 核心数据模型:RDF/OWL
语义自描述的核心在于使用形式化的知识表示语言。资源描述框架(Resource Description Framework, RDF)和网络本体语言(Web Ontology Language, OWL)是W3C推荐的标准,非常适合此目的。
- RDF:用于描述万维网上资源的陈述(statements),以主语-谓语-宾语(Subject-Predicate-Object)三元组(triples)的形式表示。例如:
<product:LaptopX> <product:hasPrice> "1200 USD". - OWL:基于RDF,提供更丰富的本体建模能力,包括类、属性、个体、等价、子类、属性特征(如传递性、对称性)等,用于定义概念及其关系。
我们将使用OWL来定义领域本体和协议本体,并使用RDF来表示实际的消息内容。
3.3 协议定义语言 (PDL)
为了形式化地定义通讯原语(performatives)及其语义,我们需要一个协议定义语言。该语言将基于OWL,允许我们定义:
- Performative:作为OWL类,例如
acp:RequestPrice,acp:InformTemperature。 - Parameters:作为OWL属性,关联到Performative类,定义其预期的参数类型和语义。例如,
acp:RequestPrice可能有一个acp:requestsProduct属性,其值应为product:Product类的实例。 - Pre-conditions & Post-conditions:使用逻辑规则(如SWRL或SPARQL CONSTRUCT/ASK)来定义执行某个performative的前提条件和执行后的状态变化。
示例:定义一个简单的产品本体
我们将使用Turtle格式来展示本体定义,因为它具有较好的可读性。
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix product: <http://example.com/ontology/product#> .
@prefix agent: <http://example.com/ontology/agent#> .
product:Product a owl:Class ;
rdfs:label "产品"@zh ;
rdfs:comment "泛指可销售或服务的商品或服务"@zh .
product:hasIdentifier a owl:DatatypeProperty ;
rdfs:domain product:Product ;
rdfs:range xsd:string ;
rdfs:label "具有标识符"@zh ;
rdfs:comment "产品的唯一标识符"@zh .
product:hasName a owl:DatatypeProperty ;
rdfs:domain product:Product ;
rdfs:range xsd:string ;
rdfs:label "具有名称"@zh ;
rdfs:comment "产品的名称"@zh .
product:hasPrice a owl:DatatypeProperty ;
rdfs:domain product:Product ;
rdfs:range xsd:decimal ; # 价格可以是小数
rdfs:label "具有价格"@zh ;
rdfs:comment "产品的价格"@zh .
product:hasCurrency a owl:DatatypeProperty ;
rdfs:domain product:Product ;
rdfs:range xsd:string ; # 货币单位,如"USD", "CNY"
rdfs:label "具有货币单位"@zh ;
rdfs:comment "产品价格的货币单位"@zh .
# 定义一个Agent类
agent:Agent a owl:Class ;
rdfs:label "智能体"@zh ;
rdfs:comment "系统中的一个智能实体"@zh .
agent:hasAgentID a owl:DatatypeProperty ;
rdfs:domain agent:Agent ;
rdfs:range xsd:string ;
rdfs:label "具有智能体ID"@zh ;
rdfs:comment "智能体的唯一标识符"@zh .
这个本体定义了 Product 和 Agent 两个核心类,以及它们的一些属性。这些定义将作为智能体之间沟通的基础词汇表。
4. 详细设计:语义自描述消息结构
我们的消息结构将分为三个主要部分:消息头(Header)、消息体(Payload)和元数据(Metadata)。所有字段,包括消息头字段,都将通过本体进行语义定义。
4.1 消息头 (Header)
消息头包含协议级别的控制信息,用于路由、会话管理和基本的语义上下文。每个字段都将引用一个预定义的URI,指向其在协议本体中的定义。
| 字段名称 | URI引用示例 | 类型(OWL类/属性) | 描述 URL:acp:sender
| acp:hasSender | acp:AgentIdentity | 发送方智能体的逻辑标识符。 URI Reference:acp:senderAgentIdentifier
| acp:hasRecipient | acp:AgentIdentity | 接收方智能体的逻辑标识符。
| acp:hasReplyTo | xsd:string | 当前消息回应的哪条消息的唯一标识符。 |
| acp:hasConversationId | xsd:string | 用于关联一系列相关消息的唯一标识符,维持对话的连贯性。 “`
@prefix acp: http://example.com/acp# .
@prefix xsd: http://www.w3.org/2001/XMLSchema# .
@prefix rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# .
@prefix owl: http://www.w3.org/2002/07/owl# .
@prefix rdfs: http://www.w3.org/2000/01/rdf-schema# .
协议本体的命名空间
http://example.com/acp# rdf:type owl:Ontology ;
rdfs:label "Agent Communication Protocol Ontology" ;
rdfs:comment "Defines the core concepts and performatives for the semantic ACP." .
智能体身份
acp:AgentIdentity a owl:Class ;
rdfs:label "智能体身份" ;
rdfs:comment "代表一个智能体的唯一标识符,可以是DID,URI等。" .
acp:hasAgentIdentifier a owl:DatatypeProperty ;
rdfs:domain acp:AgentIdentity ;
rdfs:range xsd:string ;
rdfs:label "具有智能体标识符" .
消息类
acp:Message a owl:Class ;
rdfs:label "ACP消息" ;
rdfs:comment "ACP协议中的基本通信单元。" .
消息头字段的属性定义
acp:hasSender a owl:ObjectProperty ;
rdfs:domain acp:Message ;
rdfs:range acp:AgentIdentity ;
rdfs:label "发送方" ;
rdfs:comment "消息的发送方智能体身份。" .
acp:hasRecipient a owl:ObjectProperty ;
rdfs:domain acp:Message ;
rdfs:range acp:AgentIdentity ;
rdfs:label "接收方" ;
rdfs:comment "消息的接收方智能体身份。" .
acp:hasConversationId a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:string ;
rdfs:label "会话ID" ;
rdfs:comment "用于关联一系列相关消息的唯一标识符,维持对话的连贯性。" .
acp:hasMessageId a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:string ;
rdfs:label "消息ID" ;
rdfs:comment "消息的唯一标识符。" .
acp:hasPerformative a owl:ObjectProperty ;
rdfs:domain acp:Message ;
rdfs:range acp:Performative ; # Performative类将在下面定义
rdfs:label "通讯原语" ;
rdfs:comment "消息的意图或类型(如请求、告知)。" .
acp:hasOntology a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:anyURI ;
rdfs:label "使用本体" ;
rdfs:comment "消息内容所依赖的本体URI。" .
acp:hasProtocol a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:anyURI ;
rdfs:label "协议版本" ;
rdfs:comment "当前消息遵循的ACP协议版本URI。" .
acp:hasEncoding a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:string ;
rdfs:label "编码方式" ;
rdfs:comment "消息内容的编码方式(如UTF-8)。" .
acp:hasLanguage a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:string ;
rdfs:label "内容语言" ;
rdfs:comment "消息内容的自然语言(如zh-CN, en-US)。" .
acp:hasReplyWith a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:string ;
rdfs:label "回复标识" ;
rdfs:comment "发送方期望接收方在回复时引用的标识符。" .
acp:hasInReplyTo a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:string ;
rdfs:label "回复目标" ;
rdfs:comment "当前消息是对哪条消息的回复,引用对方消息的acp:hasReplyWith值。" .
acp:hasSentAt a owl:DatatypeProperty ;
rdfs:domain acp:Message ;
rdfs:range xsd:dateTime ;
rdfs:label "发送时间" ;
rdfs:comment "消息发送的时间戳。" .
通讯原语(Performatives)
acp:Performative a owl:Class ;
rdfs:label "通讯原语" ;
rdfs:comment "智能体意图的抽象表示。" .
具体的通讯原语示例:RequestPrice
acp:RequestPrice a owl:Class, acp:Performative ;
rdfs:label "请求价格" ;
rdfs:comment "智能体请求某个产品或服务的价格。" .
acp:requestsProduct a owl:ObjectProperty ;
rdfs:domain acp:RequestPrice ;
rdfs:range product:Product ; # 引用产品本体中的Product类
rdfs:label "请求的产品" ;
rdfs:comment "此请求原语所针对的产品实例。" .
具体的通讯原语示例:InformPrice
acp:InformPrice a owl:Class, acp:Performative ;
rdfs:label "告知价格" ;
rdfs:comment "智能体告知某个产品或服务的价格。" .
acp:informsProduct a owl:ObjectProperty ;
rdfs:domain acp:InformPrice ;
rdfs:range product:Product ; # 引用产品本体中的Product类
rdfs:label "告知的产品" ;
rdfs:comment "此告知原语所针对的产品实例。" .
定义一个错误原语
acp:Refuse a owl:Class, acp:Performative ;
rdfs:label "拒绝" ;
rdfs:comment "智能体拒绝执行请求或提供信息。" .
acp:hasReason a owl:DatatypeProperty ;
rdfs:domain acp:Refuse ;
rdfs:range xsd:string ;
rdfs:label "拒绝原因" ;
rdfs:comment "拒绝的具体原因描述。" .
acp:hasRefusedPerformative a owl:ObjectProperty ;
rdfs:domain acp:Refuse ;
rdfs:range acp:Performative ;
rdfs:label "拒绝的原语" ;
rdfs:comment "被拒绝的原始请求或原语。" .
通过这种方式,`acp:hasPerformative` 字段的值不再是简单的字符串(如 "request-price"),而是一个URI,指向 `acp:RequestPrice` 这个OWL类。智能体可以通过解析这个URI来获取关于 `RequestPrice` 的所有形式化定义,包括它预期的参数(例如通过 `acp:requestsProduct` 属性),从而理解其完整语义。
#### 4.2 消息体 (Payload)
消息体是消息的核心,承载了智能体之间交换的实际信息。它将以一个RDF图片段的形式表示,这个图片段是基于 `acp:hasOntology` 中引用的领域本体来构建的。
**示例:请求一个特定产品的价格**
假设我们有一个产品实例 `<product:LaptopModelA>`。请求其价格的消息体可能包含以下RDF三元组:
```turtle
@prefix product: <http://example.com/ontology/product#> .
@prefix acp: <http://example.com/acp#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
# 这是一个虚拟的URI,代表本次请求所关注的产品实例
<http://example.com/agent/request/req123/productRef> a product:Product ;
product:hasIdentifier "LaptopModelA" ;
product:hasName "Super Laptop Pro" .
这里,我们并没有直接在Payload中包含Performative,Performative是在Header中定义的。Payload中包含的是Performative所操作的“对象”的语义描述。
4.3 元数据 (Metadata)
元数据提供额外的、非核心业务逻辑的信息,例如安全凭证、质量服务(QoS)要求、时间限制等。这部分也可以用RDF图的形式表示,引用专门的元数据本体。
示例:消息签名元数据
@prefix acp: <http://example.com/acp#> .
@prefix sec: <http://example.com/ontology/security#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<acp:Message/msg456> sec:hasSignatureValue "AABBCCDD..."^^xsd:base64Binary ;
sec:hasSignatureAlgorithm "SHA256withRSA" ;
sec:hasSigningCertificate "..."^^xsd:base64Binary .
4.4 消息的完整结构与序列化
整合上述部分,一个完整的语义自描述ACP消息在逻辑上可以表示为一个复杂的RDF图。为了在网络上传输,这个RDF图需要被序列化为字节流。
我们有多种序列化选项:
- Turtle/JSON-LD:人类可读性好,但可能相对冗长。JSON-LD 尤其适合与现有基于 JSON 的基础设施集成,同时提供了完整的语义层。
- Binary RDF Formats (e.g., HDT, RDF/XML Binary):高度压缩,但通常用于存储而非实时传输,且不是W3C标准。
- CBOR-LD (CBOR-Linked Data):基于 Concise Binary Object Representation (CBOR) 和 JSON-LD 上下文,提供了一种紧凑的二进制表示,同时保留了Linked Data的语义能力。这可能是生产环境中兼顾效率和语义的最佳选择。
为了演示方便,我们将使用一个类JSON-LD的结构来表示消息,因为它能很好地将RDF图的概念映射到JSON结构,并且可以引入 @context 来解析URI。
一个完整的语义消息实例(逻辑表示,接近JSON-LD):
{
"@context": {
"acp": "http://example.com/acp#",
"product": "http://example.com/ontology/product#",
"agent": "http://example.com/ontology/agent#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"sec": "http://example.com/ontology/security#"
},
"@id": "acp:Message/msg456",
"acp:hasSender": {
"@id": "agent:PriceRequesterAgent1",
"agent:hasAgentIdentifier": "uuid:abc-123"
},
"acp:hasRecipient": {
"@id": "agent:ProductServiceAgent2",
"agent:hasAgentIdentifier": "uuid:def-456"
},
"acp:hasConversationId": "conv-001",
"acp:hasMessageId": "msg456",
"acp:hasPerformative": {
"@id": "acp:RequestPrice",
"acp:requestsProduct": {
"@id": "product:LaptopModelA",
"product:hasIdentifier": "LaptopModelA",
"product:hasName": "Super Laptop Pro"
}
},
"acp:hasOntology": "http://example.com/ontology/product#",
"acp:hasProtocol": "http://example.com/acp#",
"acp:hasEncoding": "UTF-8",
"acp:hasLanguage": "en-US",
"acp:hasReplyWith": "reply-to-msg456",
"acp:hasSentAt": {
"@value": "2023-10-27T10:00:00Z",
"@type": "xsd:dateTime"
},
"acp:hasMetadata": [
{
"@id": "acp:Message/msg456/signature",
"sec:hasSignatureValue": {
"@value": "AABBCCDD...",
"@type": "xsd:base64Binary"
},
"sec:hasSignatureAlgorithm": "SHA256withRSA"
}
]
}
在这个JSON-LD结构中:
@context定义了URI前缀映射,使得我们可以使用短语(如acp:,product:)代替完整的URI。- 每个字段的键都直接是本体中的属性URI(或其短语形式),如
acp:hasSender。 - 值可以是字面量,也可以是嵌套的JSON对象,表示另一个RDF资源(OWL类的实例),例如
acp:hasSender的值就是一个agent:AgentIdentity实例。 @id字段为资源提供了唯一的URI标识符。@value和@type组合用于表示带类型的字面量,如xsd:dateTime或xsd:base64Binary。
这种格式将消息的所有部分,从头部到内容再到元数据,都统一为RDF图的表示,并通过 acp:hasOntology 和 acp:hasProtocol 字段明确引用了用于解释这些图的本体URI。接收智能体可以通过这些URI获取本体定义,然后利用RDF解析器和OWL推理机来理解消息的深层语义。
5. 智能体交互流程与代码示例
为了更好地理解语义自描述ACP的工作方式,我们通过一个简单的“请求产品价格”的交互场景来演示。
场景描述:
- 价格查询智能体 (PriceRequesterAgent):需要查询某个产品的价格。
- 产品服务智能体 (ProductServiceAgent):提供产品信息和价格查询服务。
交互步骤:
- PriceRequesterAgent 构建一个
acp:RequestPrice消息,其acp:requestsProduct字段指向它要查询的产品实例。 - ProductServiceAgent 接收消息,解析其语义。识别出
acp:RequestPrice意图和被请求的产品。 - ProductServiceAgent 查询其内部知识库,获取产品价格。
- ProductServiceAgent 构建一个
acp:InformPrice消息作为回复,其acp:informsProduct字段包含产品的完整信息(包括价格)。 - PriceRequesterAgent 接收回复,解析语义,获取价格信息。
5.1 智能体本体和协议本体的引用与加载
在智能体启动时,它需要加载其预期的领域本体和协议本体。这通常通过一个本体库或注册中心来管理。
# 假设我们有一个简单的本体加载器
from rdflib import Graph, Literal, URIRef, BNode, Namespace
from datetime import datetime
class OntologyManager:
def __init__(self):
self.ontologies = {}
def load_ontology(self, uri, file_path):
g = Graph()
g.parse(file_path, format="turtle")
self.ontologies[uri] = g
print(f"Loaded ontology: {uri} from {file_path}")
def get_ontology(self, uri):
return self.ontologies.get(uri)
# 实例化本体管理器
ontology_manager = OntologyManager()
# 假设本体文件已经存在于本地
ontology_manager.load_ontology("http://example.com/ontology/product#", "product_ontology.ttl")
ontology_manager.load_ontology("http://example.com/acp#", "acp_ontology.ttl")
# 定义命名空间
ACP = Namespace("http://example.com/acp#")
PRODUCT = Namespace("http://example.com/ontology/product#")
AGENT = Namespace("http://example.com/ontology/agent#")
XSD = Namespace("http://www.w3.org/2001/XMLSchema#")
product_ontology.ttl 和 acp_ontology.ttl 就是前面定义的Turtle格式的本体文件。
5.2 智能体消息构建与发送(PriceRequesterAgent)
PriceRequesterAgent 构建一个请求消息。
# 假设智能体自身的ID
requester_agent_id = AGENT.PriceRequesterAgent1
requester_agent_identifier_value = "uuid:abc-123"
# 目标智能体ID
recipient_agent_id = AGENT.ProductServiceAgent2
recipient_agent_identifier_value = "uuid:def-456"
# 请求的产品
requested_product_uri = PRODUCT.LaptopModelA # 假设此URI标识一个特定的产品实例
requested_product_identifier = "LaptopModelA"
requested_product_name = "Super Laptop Pro"
# 构建消息的RDF图
msg_graph = Graph()
msg_id = f"msg-{datetime.now().timestamp()}"
conv_id = "conv-001" # 首次请求,创建新的会话ID
reply_with_id = f"reply-to-{msg_id}"
# 消息的主体URI
message_uri = URIRef(f"acp:Message/{msg_id}")