Canon-A受控英语:为多Agent通信建立可验证的语言规范
2026/9/17 2:05:49 网站建设 项目流程

很多团队的 Agent 系统已经跑通了原型,但一旦进入多人协作、多服务编排,就会遇到一个隐蔽却致命的问题:Agent 之间说起话来没有统一标准。同一个用户意图,一个 Agent 用“请帮我查询库存”表达,另一个 Agent 用“retrieve stock info”表达,第三个 Agent 用一长串上下文拼接的 prompt 表达。模型可能都懂,但外层系统不知道它们是否真的在说同一件事,也无法对消息做可验证的审核。Canon-A 这类方案的提出,正是为了给“模型提示”和“Agent 间通信”补上缺失的语言层规范。

本文会围绕 Canon-A 受控英语展开,先讲清楚它解决的真实问题,再给出可落地的规范设计、代码示例、验证方法和工程建议。读完你至少能独立设计一套小范围内的受控英语消息格式,并用它约束 Agent 的输入输出,让整个系统从“能跑”变成“可审计、可测试、可回滚”。如果你想在真实项目里做多 Agent 编排,这篇文章会比单纯刷模型评测更有实操参考价值。

1. 为什么需要“受控英语”而不是自然语言

自然语言是灵活的,但对系统来说,这种灵活性往往意味着不确定。一个任务用不同的句式表达,模型可以理解,但后续流程很难做规则校验。比如一个订单 Agent 说“帮我取消刚才那个订单”,另一个订单 Agent 说“请把订单 A10086 的状态改到 canceled”。上层系统如果想判断它们是否在表达同一件事,需要先做语义对齐,这在分布式环境里成本非常高。更麻烦的是,提示词只要换一个措辞,模型的输出就可能漂移,于是同一个 Agent 在不同版本、不同温度参数下,给出的结果差距很大。

受控英语(Controlled English)的概念并不新。航天、航空、工业维修等领域早就用简化技术英语来保证文档一致性和安全性,例如 ASD-STE100。它限制词汇表、限制句式,只允许使用既定语义范围内的表达,从而减少歧义。Canon-A 延续了这个思路,只是把使用对象从“人类技术文档”扩展到了“大语言模型的提示词”和“Agent 之间的消息”。也就是说,它可以被理解为一种面向 LLM 和智能体通信的简化英语规范。

如果没有这种规范,团队通常会临时约定一套 JSON 格式。JSON 能表达结构,但字段语义依然靠人和模型自觉。比如 status 字段可能被写成 completed、done、finished,三个词在 JSON 里都是合法字符串,系统却很难提前识别它们等价。Canon-A 不是要替代 JSON,而是在 JSON 之上增加一层受控英语约束,规定字段可以出现哪些词、句子模板长什么样、消息里必须携带哪些上下文。这样一来,模型输出的自由度被收敛到一个可测试的范围内。

从工程视角看,受控英语解决的核心问题有两个。其一是可验证性:每条 Agent 消息都能被规则引擎或校验器判断是否合规。其二是可解释性:无论是人类读日志,还是另一个 Agent 读上下文,都能快速理解这条消息在表达什么。这两点正好是当前提示词工程最缺失的部分。大多数人写 prompt 只关心能不能拿到期望输出,很少关心 prompt 本身是否具备稳定的结构化语义。Canon-A 则把注意力拉回到“语言的工程治理”上。

2. Canon-A 的核心概念与设计目标

从命名上看,Canon-A 可以拆解为 Canon(规范化)+ A(很可能是 Agent 或 Action 的首字母)。因此,它要解决的事情是:让 Agent 之间的通信和 Agent 调用的模型提示都遵循一套统一的规范语言。这套规范语言以英语作为载体,因为主流 LLM 的训练语料中英语占比最高,英语表达在模型理解和生成方面通常比小语种更稳定。

Canon-A 并不是一个独立的推理模型,也不需要替代现有 Agent 框架。它更像是一份“语言协议”,可以被嵌入到 LangChain、AutoGen、自研编排框架或普通 API 调用流程中。它的核心由四部分组成:

  1. 受控词汇表:规定合法单词集合,比如只允许 inventory、order、status、cancel、confirm 等业务词,禁止大量同义词随意替换。
  2. 限定语法模板:定义句子和消息的骨架,例如[ACTION] [ENTITY] [PARAMETERS],相当于一个“提示词填槽模型”。
  3. 语义约束:规定字段的取值范围和前置条件,比如 status 只能是 pending、paid、canceled 中的一种。
  4. 消息信封:定义 Agent 之间通信时的消息头,包括发送方、接收方、意图、关联 ID 等。

设计目标也很明确。首先是稳定,同一个意图无论由哪个 Agent 触发,最终生成的提示词或消息结构都应当一致。其次是可审计,每个动作都能精确记录为一条结构化消息,方便回放和追踪。再次是可组合,新 Agent 接入时不需要猜测对方的表达习惯,只需遵守同一套受控英语规范。最后是低迁移成本,Canon-A 不要求业务逻辑全重写,只要在消息入口和出口加一层翻译与校验即可。

要特别澄清一个误区:受控英语不等于“把英语翻译成一堆缩写”。它依然保持自然语句的形态,但句子结构是可枚举的。比如一个合法的 Canon-A 消息可以是Agent B, please confirm order 20250101 status is canceled.这种受限句,而不是B CANCEL 20250101这种需要额外编码表的口令。这样既能被规则解析,又能被模型快速理解,还不会让日志变成天书。

3. 适用场景与不适用场景

最容易从 Canon-A 获益的场景,是多 Agent 协作流程。例如一个订单履约系统里有订单 Agent、库存 Agent、物流 Agent、财务 Agent。用户取消一个订单时,订单 Agent 需要通知其他 Agent 释放库存、取消物流单、发起退款。如果每个 Agent 使用自己的自由文本描述,协调层很难判断它们是否指向同一个订单、同一套状态机。但如果所有 Agent 都使用一套受控英语消息,协调层就能通过统一的消息信封和状态字段来完成路由与校验。

另一个典型场景是企业合规审计。银行、保险、医疗等行业对操作记录有严格要求。自由文本日志很难直接作为合规依据,而受控英语消息天然带有结构化字段,可以像数据库记录一样被查询和追踪。比如request_order_cancellation这条消息会包含订单号、操作人 Agent、时间戳、原状态、目标状态,审计人员不需要理解大模型输出,也能从数据层面确认发生了什么。

此外,教科书式的提示词管理也很适合 Canon-A。如果你的团队维护着成百上千条 prompt,每次调整都怕影响其他业务,受控英语可以把 prompt 拆成“固定模板 + 受控参数 + 短句片段”。这样既能减少重复全文复制,也能让 prompt 变更以最小粒度被 review。

但 Canon-A 并不适合所有场景。简单的一问一答、开放式创意写作、需要模型自由发挥的场景,强行套受控英语反而会损失模型能力。比如让 Agent 写营销文案或生成故事,受控词汇表会显得生硬,用户也会觉得输出不自然。另一个不适合的场景是探索性对话,例如初期调研用户需求,意图边界还不清晰,过早定义词汇表只会增加维护负担。判断标准很简单:如果消息的目标是“执行一个明确动作”,就应该用 Canon-A;如果目标是“产生一段内容”,就不应该用。

4. 最小规范设计:词汇表、模板与消息信封

在开始写代码之前,我们需要先设计一个最小的 Canon-A 规范。这里以“订单取消”为例。这个场景足够简单,又能覆盖 Agent 间通信的主要要素。我们可以把规范文件放在独立目录canon_a/中,包括词汇表、模板和消息信封三部分。

首先是词汇表定义。用 YAML 的好处是易读性和兼容性好,人类可以直接评审,机器也可以解析。下面这个示例定义了动作词、实体词和状态词的合法取值。

# 文件路径:canon_a/vocabulary.yaml actions: cancel: synonyms: [cancel, canceling, canceled] description: "取消一个资源或流程" confirm: synonyms: [confirm] description: "确认一个操作或状态" entities: order: type: "order" id_field: "order_id" description: "用户订单" inventory: type: "inventory" id_field: "item_id" description: "库存条目" status: order: - pending - paid - canceled inventory: - available - held - released fields: order_id: pattern: "^[A-Z0-9]{6,}$" item_id: pattern: "^[A-Z0-9]+$"

这个文件展示了三个关键点。一是动作词只保留少量且必要的动词,并且把同义词统一映射到同一个规范动作。cancelcanceled在自然语言中含义相近,但在受控英语里必须归一为cancel。二是实体描述包含类型和 ID 字段,后续 Agent 可以通过type决定路由。三是字段值都有正则约束,这样在入口处就能拦截非法订单号,而不是等模型返回时才报错。

接下来是模板定义。模板规定了消息的主体句式,即[动作] [实体] [参数]。这里需要定义消息可能存在几种形态,以及每个组成部分的取值来源。

# 文件路径:canon_a/templates.yaml templates: - name: cancel_order pattern: "request {action} {entity} with id {order_id} because {reason}" required_fields: [order_id, reason] allowed_actions: [cancel] allowed_entities: [order] - name: confirm_cancel_order pattern: "confirm {action} {entity} with id {order_id}" required_fields: [order_id] allowed_actions: [confirm] allowed_entities: [order]

这里的模板并不要求使用非常严格的语法解析器,因为它最终会被交给 LLM 作为提示词,同时也会被我们的校验脚本做语义检查。只要模板能覆盖 Agent 之间 80% 的通信场景,就值得投入。实际项目里可能还需要status transition模板,例如update order status from pending to canceled,这里暂不展开。

然后是消息信封。Agent 间的通信不能只有业务内容,还需要路由和追踪信息。我们可以复用类似 HTTP Header 的思路,把发送方、接收方、消息 ID、关联链路 ID 放到外层。以下是一个符合 Canon-A 设计思想的 JSON 消息信封示例。

{ "canon_a_version": "1.0.0", "message_id": "msg_20250115_001", "trace_id": "trace_abcdef", "sender": "agent_order", "receiver": "agent_inventory", "intent": "release_inventory", "payload": { "template": "cancel_order", "action": "cancel", "entity": "order", "order_id": "ORDER123", "reason": "user changed mind" } }

为什么要把路径和路由从 payload 里拆出来?因为在实际系统中,并不是所有 Agent 都关心业务内容,但每个 Agent 都关心消息头。例如日志中心只希望读取message_idtrace_id来串联一整个请求生命周期,监控系统只需要统计intent的成功率。如果这些字段埋没在大段英文模板中,我们还需要额外解析一层文本,徒增复杂度。消息信封相当于给受控英语加上了可路由的结构化容器。

5. 代码实现:校验与生成受控英语提示

规范文件定义好之后,还需要程序能自动校验。这里我们用一个 Python 脚本演示核心思路:读取 YAML 规范,检查一个消息信封是否满足 Canon-A 约束,并且把 payload 渲染成一段可用于 LLM 提示的受控英语。

请注意,这个示例是设计思路演示,不是某个官方库的 API。真实项目中你可以把逻辑拆成校验器、渲染器、翻译器多个服务。

# 文件路径:canon_a_validator.py import re from typing import Any, Dict import yaml def load_yaml(path: str) -> Dict[str, Any]: with open(path, "r", encoding="utf-8") as f: return yaml.safe_load(f) class CanonAValidator: def __init__(self, vocabulary: Dict, templates: Dict) -> None: self.vocabulary = vocabulary self.templates = templates["templates"] def validate_message(self, message: Dict[str, Any]) -> None: payload = message["payload"] template_name = payload["template"] template = self._find_template(template_name) if template is None: raise ValueError(f"unknown template: {template_name}") action = payload.get("action") entity = payload.get("entity") if action not in template["allowed_actions"]: raise ValueError(f"action {action} not allowed") if entity not in template["allowed_entities"]: raise ValueError(f"entity {entity} not allowed") for field in template["required_fields"]: if field not in payload or not payload[field]: raise ValueError(f"missing required field: {field}") for field_pattern in self.vocabulary.get("fields", {}).items(): field_name, field_config = field_pattern if field_name in payload: if not re.search(field_config["pattern"], str(payload[field_name])): raise ValueError(f"field {field_name} invalid") def render_prompt(self, message: Dict[str, Any]) -> str: self.validate_message(message) payload = message["payload"] template = self._find_template(payload["template"]) pattern = template["pattern"] return pattern.format( action=payload["action"], entity=payload["entity"], order_id=payload.get("order_id", ""), reason=payload.get("reason", ""), ) def _find_template(self, name: str) -> Dict[str, Any] | None: for template in self.templates: if template["name"] == name: return template return None if __name__ == "__main__": vocabulary = load_yaml("canon_a/vocabulary.yaml") templates = load_yaml("canon_a/templates.yaml") validator = CanonAValidator(vocabulary, templates) test_message = { "canon_a_version": "1.0.0", "message_id": "msg_20250115_001", "trace_id": "trace_abcdef", "sender": "agent_order", "receiver": "agent_inventory", "intent": "release_inventory", "payload": { "template": "cancel_order", "action": "cancel", "entity": "order", "order_id": "ORDER123", "reason": "user changed mind" } } validator.validate_message(test_message) print("validation passed") prompt = validator.render_prompt(test_message) print("generated prompt:") print(prompt)

这个脚本的逻辑并不复杂,但它是整个 Canon-A 方案中的关键防线。validate_message先做模板匹配,再检查动作和实体是否在允许范围内,随后校验必填字段和字段格式。render_prompt在通过校验后,把结构化 payload 渲染成一段接近自然语言的受控英语提示词,例如request cancel order with id ORDER123 because user changed mind。这样的提示词一方面仍然保留人类可读性,另一方面又足够规整,可以被规则引擎识别。

在真实项目中,还需要考虑多语言、多 Agent 版本并存的场景。比如有些 Agent 运行在旧版本规范上,有些 Agent 已经升级到新规范。消息发给对方时,应该把canon_a_version放进消息头。协调框架可以根据版本号做消息转换,或者把不支持旧版本的消息直接拒绝并返回错误。这个细节在原型阶段可以忽略,但生产环境必须纳入设计。

6. 运行验证与预期效果

假设你已经把前面的示例代码保存为canon_a_validator.py,并把规范文件放在canon_a/目录下,可以先安装依赖PyYAML,然后运行:

pip install pyyaml python canon_a_validator.py

预期输出如下:

validation passed generated prompt: request cancel order with id ORDER123 because user changed mind

判断成功的标准有两个:第一,校验函数没有抛出异常,消息符合 Canon-A 规范;第二,渲染出的提示词结构稳定,order_idreason被正确填充。

如果失败了,优先级最高的排查点是 YAML 文件加载是否正确。例如templates.yamltemplates键对应的值必须是列表,否则_find_template在遍历时会直接报TypeError。可以先单独打印templates内容确认结构。其次要检查字段名是否完全一致,比如required_fields里的order_id是否与 payload 中的键一致。因为受控英语强调的是“强一致”,字段名一不一致就都会在不经意间破坏规则。

在真实 Agent 系统里,验证不会只发生在启动时,而是发生在每个消息入口。当一个外部 Agent 发送消息过来,系统先做 Canon-A 校验,再进入业务处理逻辑。如果一个 Agent 发来的是自由文本,无法校验,那么可以有两种处理策略:一种是直接拒绝,要求发送方改成 Canon-A 格式;另一种是加一层翻译 Agent,把自由文本转成受控英语。前者安全、简单,适合内部强制约束;后者灵活、复杂,适合对接外部系统。

另外,建议为校验器编写自动化测试。尤其要把这些用例覆盖进去:合法消息、缺少必填字段、非法 entity、非法状态值、消息版本不匹配。用自动化测试可以把规则的变更风险降到最低。毕竟 Canon-A 规范是团队共同维护的,未来一定会有人往里加新词、新模板,如果没有测试保护,很容易出现“语法上合法但业务语义被打破”的问题。

7. 常见问题与排查思路

以下汇总在实际引入受控英语规范时大概率会遇到的问题。

问题现象可能原因排查方式解决方案
校验器频繁误报,合法消息也被拒绝模板required_fields设置过严,字段名不一致查看日志中具体缺失字段调整模板,统一字段命名
Agent 返回自由文本,无法通过受控英语校验Agent 模型没有严格执行提示词中的格式要求查看模型原始输出在 prompt 中强约束句式,并增加输出校验与重试机制
词汇表越来越大,维护困难每个业务方都往共享词汇表里加私有词汇统计词汇使用频率,分析重复度按业务域拆分词汇表,并设置新增词汇的评审流程
多 Agent 版本并存时消息解析错误不同版本的消息信封字段不兼容检查canon_a_version和日志中的消息头增加版本转换插件,或强制所有 Agent 升级到兼容版本
受控英语提示词在复杂任务上输出变差模板约束过强,剥夺了模型推理空间对比自由文本提示与受控模板的输出将模板用于动作类消息,保留一小段自由推理字段
字段校验通过,但业务语义依然错误正则和词汇表只能保证格式,不能保证模型理解正确使用人工抽检样例构建评测集增加基于评测集的回归测试,定期验证意图识别准确率

这里需要说明一个容易被忽略的陷阱:受控英语校验本质上只能做“语法检查”,不能完全保证“语义正确”。比如模型生成的action: cancel完全合法,但它可能误解了用户意图,把“冻结订单”理解成了“取消订单”。这类问题不能靠 Canon-A 校验解决,只能通过意图识别评测、人工审核和试运行来兜底。因此在使用 Canon-A 时,要把它和企业原有的意图分类系统结合起来,而不是试图用词汇表替代真正的语义理解。

另一个常见问题是模板嵌套。初期大家会写简单的request cancel order,但业务一旦复杂,就会出现request cancel order with id x and then release inventory with id y。这种长句很难维护,也不利于校验。更推荐的做法是拆成多条短消息,或者使用子动作模板。在消息信封中,你可以设计payload.parent_id来关联多条子消息。保持模板短小,是 Canon-A 能持续演进的前提。

8. 最佳实践与工程建议

8.1 词汇治理

词汇表是 Canon-A 的核心资产,应该像代码一样做版本管理。建议把词汇表、模板、消息信封的定义都放在 Git 仓库中,通过 Merge Request 评审才能修改。每个词汇都应有明确的业务负责人。同义词映射表要精简化,不要把“用户体验”一词收录几十种说法,只保留最常用的一两个即可。过大的词汇表会让维护成本迅速上升,也会让模型在选择词项时无所适从。

8.2 模板设计

模板不应该追求覆盖所有自然语言表达,而应该追求覆盖高频动作。按“二八原则”管理:如果 80% 的消息只来自 20 个模板,就先把这 20 个模板打磨好,再考虑长尾。模板字段越少越好,必需的留,可选的尽量拆到子模板中。每次新增模板时,都要问自己:这个动作是不是真的必须用独立模板?能不能复用现有模板加一个参数?模板数量一旦超过几百个,即使用受控英语管理,也会变成新的“失控文档”。

8.3 版本兼容

面向 Agent 的协议,永远要假设存在多版本并存的时期。因此消息信封里必须携带canon_a_version,同时中心网关要维护一张版本兼容表。兼容策略可以很简单:只允许 N 和 N-1 版本互转,跨版本直接拒绝。不要试图让一个网关同时兼容十几个旧版本,那样等于又创造了一个没有文档的协议。在发布新版本规范时,应该先灰度一小类 Agent,观察日志中的校验错误率,再逐步扩大范围。

8.4 安全边界

受控英语消息最终会被渲染进 prompt。如果消息中的字段直接拼接进文本,可能带来提示注入风险。例如reason字段被注入一段“忽略之前指令”的文本,模型可能会偏离原有任务。因此所有业务字段在进入 prompt 前都应该做清洗和长度限制。不要无条件信任 Agent 之间传来的 payload,尤其是来自外部系统的消息。建议在渲染前做一次专门的“危险指令检测”,或者将自由文本字段与固定模板字段分开处理。

8.5 日志与审计

Canon-A 的一个优势是让日志结构化。记录日志时,不要把整个 JSON 信封直接塞进日志,而是提取关键字段:message_idtrace_idintenttemplatesenderreceiveraction。这样便于把同一个业务请求的所有 Agent 消息串联起来,做端到端的耗时分析。同时应该记录canon_a_version,一旦发生格式变动,可以通过日志回放找出是哪个 Agent 触发的问题。

8.6 团队协作

引入 Canon-A 不只是技术架构改造,更是开发习惯的改造。比较有效的方式是成立一个小的规范委员会,由核心 Agent 开发成员组成。任何人都可以提 RFP(规范改进提案),但合并前必须经过交叉评审和测试。新 Agent 接入时,考核标准不只是业务功能是否完成,还要包含消息是否全部通过 Canon-A 校验。这样的流程会让规范系统逐步长成组织内部的事实标准。

9. 总结与行动建议

本文从多 Agent 通信和提示词工程的实际痛点出发,解释了 Canon-A 受控英语的定位:它不是一个新的模型,也不是一个框架,而是一层位于自然语言与程序化协议之间的语言规范。它把 Agent 之间的消息约束为可控的词汇、模板和消息信封,让系统在保持一定自然语言表达能力的同时,获得可验证、可审计、可回滚的工程属性。

如果你的团队正在做多 Agent 编排、企业流程自动化或者提示词治理,可以考虑从最小业务场景入手。不要一开始就做全面的词汇表,先选一个高频动作,比如“取消订单”,按照本文示例定义词汇、模板、消息信封,然后写一个校验脚本。跑通之后,观察日志质量、调试效率和模型输出的稳定性,再决定是否向更多场景推广。你会发现,真正困难的地方从来不是模型能力,而是团队对“消息格式一致性”的坚持。只要开始把消息当接口设计,用受控英语做一层约束,整个 Agent 系统的复杂度就会下降一个量级。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询