从自由文本到受控英语:Canon-A如何规范提示词与Agent通信
在项目里同时接入多个大模型之后你大概率会遇到这样一个问题同一个指令用自然语言写给 GPT 用得好好的换到另一个模型上就完全失灵团队里两个 Agent 协作时一个把“你看着办”理解成自主决策另一个把它理解成什么也不做任务直接卡死。表面看是模型能力差异本质问题是我们一直在用“自然语言”这种高度依赖上下文、充满歧义和省略的介质去要求模型和 Agent 完成精确任务。Canon-A 这个方向正是冲着这个问题来的。它的核心思路很清晰用受控英语Controlled English作为模型提示和 Agent 间通信的规范语言把“怎么写提示词”和“Agent 之间怎么对话”变成一套有边界、可校验、可复用的规则。这篇文章会拆解 Canon-A 的设计逻辑分析它到底解决了什么问题也会给出一个你在现有模型上可以先落地的受控英语实践方案。1. 这篇文章真正要解决的问题先下一个判断提示词工程正在从“写得好”走向“写得规范”Agent 通信也正在从“任意字符串”走向“协议约定”而 Canon-A 属于后一个阶段的探索。它要解决的痛点是当前大模型落地时普遍存在的三件麻烦事。第一件事是提示词不可复现。同一段自然语言提示词企业内部不同人写出来的风格差异很大同样的业务逻辑放在不同模型上表现也不稳定出了问题很难定位是模型问题还是提示词问题。第二件事是 Agent 之间没有约定。两个 Agent 协作时如果没有预先定义好消息格式、字段含义、回复约束就会出现职责模糊、取数失败、循环对话、结果串台一类问题。第三件事是安全边界模糊。自由文本提示词里可以夹带任何内容Agent 的消息里也可以出现权限描述、外部指令、恶意引导缺少一层“合法输入”的过滤边界。如果你正在做以下任一类工作这篇文章值得读下去需要为团队建立统一提示词规范做 Agent 多智能体协作但消息格式经常对不齐在 To B 或金融、医疗等合规要求较高的场景里使用大模型需要对输入输出做可审计约束或者你只是想知道除了“追提示词技巧”和“追框架更新”之外还有什么相对稳定的方法论。Canon-A 不是一个能一键安装的普通工具它更像一个尚在形成中的规范思路。这篇文章会先讲清楚它背后的受控英语原理再给出一套能在现有 LLM 上落地的受控英语提示模板与 Agent 通信协议示例。你会发现核心并不是某个具体格式而是“限定表达范围、固定句法结构、明确字段语义”这一套思想。2. 受控英语Controlled English的核心概念与为什么它对大模型有效受控英语不是新概念。它指的是对自然语言进行词汇、语法、句式约束后形成的子集。最著名的先例是航空航天领域的简化技术英语Simplified Technical English它规定了大约一千个核心词汇、每个词汇只能使用特定词性和含义、句子长度和句法结构都有限制目的是让非英语母语的工程师也能准确理解维护手册避免歧义。把这个思路迁移到大模型场景价值非常明确。自然语言提示词本质上是一个自由编码通道自由意味着灵活但也意味着模型需要在每一个 token 上做意图推断。受控英语则提前把“意图空间”压缩到一个可预期的范围内句子用什么动词开头参数用什么标签结果用什么格式返回都有一致约定。模型不需要猜测“用户到底想要我做什么”只需要在限定规则里执行映射。传统自然语言提示词和受控英语提示词的区别可以看下面这个维度对比。对比维度自然语言提示词受控英语/受控提示词汇范围开放任意表达限定领域词汇表句法结构自由可省略可倒装模板化动宾结构为主歧义容忍度高依赖上下文推断低规则先行可校验性弱无法程序化检查强可做语法与字段校验跨模型稳定性不稳定模型差异明显相对稳定语义约束收敛审计与安全困难自由文本难过滤容易非法输入可拦截学习成本低会说话就能写中需要学习和维护规范需要强调一点受控英语不是要取代自然语言。自然语言的优势在开放创意、复杂语义理解、情感表达这些场景下强行套受控模板是得不偿失的。它适合的是任务边界清晰、需要稳定复现、需要审计和约束的场景比如日志分析、SQL 生成、数据抽取、Agent 分工协作。理解了这一点就不会把“受控英语”误认为“把自然语言变笨”而是理解为“在需要确定性的地方建立规则”。3. Canon-A 的设计思想拆解提示规范与智能体通信从名称和已有材料看Canon-A 的核心可以拆成两个方向一个方向是面向模型提示model prompting的受控英语规范另一个方向是面向智能体之间通信agent-to-agent communication的消息规范。二者共同点是都使用“受限英语表达”来降低歧义但服务的对象和约束的层次不同。先看模型提示方向。它的思路是把提示词从自由文本变成“结构化任务描述”。比如不再写“看一下这个日志文件如果有很多 error帮我总结一下原因”而是写成一张任务卡包含任务类型、输入源、处理策略、输出格式、约束条件。每个字段值是受控词汇表中的一个选项。这样模型接收到的不再是一段需要猜的文本而是一份明确的任务订单。再看智能体通信方向。Agent 之间的消息如果只是纯文本“我完成了”“请继续”那么接收方判断下一步的依据就会非常模糊。受控英语的思路是规定消息必须包含意图字段、状态字段、请求参数、响应承诺等结构化信息并且意图字段只能使用预设的动词集合比如 REQUEST、INFORM、OFFER、ACCEPT、REJECT、STATUS。相当于给 Agent 通信定义了一层轻量级协议让双方不必用语义猜测来协作。Canon-A 中“Canon”这个名字本身也有含义。Canonical 表示规范化、标准形式它隐含的设计目标是无论底层是哪个模型、哪个 Agent 框架交互时都先转换为统一的规范表达。这类似于 API 网关把后端各异协议统一成对外标准协议的做法。需要注意目前公开资料有限具体版本号、官方文档和工具链细节还不能确定本文的分析以设计思想推导为主更稳妥的判断是它属于“受控英语 Agent 协议”方向的代表性探索读者应关注其设计理念而非某个固定实现。传统 Agent 协作方式与规范通信方式的差异可以这样归纳传统方式中两个 Agent 直接交换自然语言消息语义依赖模型理解出现问题只能通过加提示词补丁规范通信方式则在 Agent 之外增加了一层消息协议校验消息必须符合既定的意图集合和字段结构非法消息直接拒绝。前者是“让模型猜”后者是“让协议约束”这就是 Canon-A 试图改变的架构层变化。这个设计背后还有一个工程层面的收益可观测性。当 Agent 消息被规范化之后日志中记录的不再是不可检索的文本而是带有明确字段标签的结构化事件。运营人员可以方便地统计每个意图出现的次数、请求成功率、消息平均耗时也能够快速定位是哪一步的 Agent 对话出了问题。4. 受控英语提示词 vs 自由提示词用一个场景对比看差异概念上讲完用一个实战场景对比可能更有体感。假设你有一个日志分析任务希望模型读取一条错误日志后给出执行建议。用自由提示词你可能会写“帮我看下这段日志看看是什么问题应该怎么处理。”用受控英语你写的是TASK: LOG_ANALYSIS INPUT: log_text ANALYZE: ERROR_PATTERN CONSTRAINT: NO_GUESSING OUTPUT: SUMMARY注意这两者不是同一个量级的“写得好不好”而是决策路径的差异。自由提示词下模型需要自行判断任务类型、分析策略、输出格式任何一步判断偏差都会导致结果不稳定。受控英语下任务类型是 LOG_ANALYSIS分析策略是 ERROR_PATTERN输出是 SUMMARY模型不需要做大范围意图匹配只需要在限定范围内执行。即使换一个模型只要它理解这些受控词的含义输出结构也会保持稳定。从工程上看受控英语提示词的另一个优势是可以做程序化校验。自由提示词写错字、语义含糊程序无法自动发现。受控英语却有明确的可检查规则任务类型是否在预设集合中字段是否齐全约束值是否合法。这就是为什么在自动化流水线里受控英语明显更友好——它把“提示词质量”从人的主观判断变成了一批可执行的校验规则。当然自由提示词也有不可替代的场景。当任务本身边界模糊需要模型发挥创造力比如“帮我构思一个产品功能方案”强行套受控模板反而画蛇添足。实际项目中更推荐的做法是分级使用开放任务走自然语言提示确定任务走受控英语模板系统对接类任务全部走协议化消息。5. 环境准备与前置条件如果你打算在现有项目中试验受控英语和 Canon-A 式通信不需要等待某个特定框架发布可以直接用通用语言和 LLM API 实现一套最小方案。这里给出一个基础环境建议。操作系统Windows / macOS / Linux 均可建议使用 Linux 或 macOS 开发调试与生产环境更接近。编程语言Python 3.10 及以上类型提示和 Pydantic 支持比较完善。Python 环境管理建议使用 venv 或 conda 创建独立环境。LLM 接口OpenAI 兼容的 Chat Completions 接口或其他支持 function calling 的模型服务。关键依赖pydantic 用于消息结构校验openai 或 requests 用于调用模型接口pyyaml 用于维护受控词汇表。版本说明以下代码依赖的具体包版本请以实际环境为准本文的重点是演示通用实现思路。安装基础依赖使用以下命令python -m venv .venv source .venv/bin/activate pip install pydantic openai pyyaml准备工作区的目录结构可以直接在项目里创建一个 experiments 目录后续示例都放在这里。experiments/ ├── vocabulary.yaml ├── model_client.py ├── messages.py ├── prompt_builder.py ├── agent_worker.py └── demo_run.py其中 vocabulary.yaml 维护受控词汇表messages.py 定义 Agent 间消息模型prompt_builder.py 负责把结构化任务卡转换为模型提示词agent_worker.py 模拟一个能收发规范化消息的 Agentdemo_run.py 是整个示例的入口。6. 核心流程拆解从受控词汇表到 Agent 消息整个实践流程可以分为四步每一步都在回应一个问题怎么让“规范”真正落到程序和模型交互中。第一步是设计受控词汇表。词汇表是整个规范的地基它定义了模型和 Agent 能使用的意图动词、任务类型、状态值、约束项。设计时应该从业务场景出发先盘点高频任务给每个任务起一个语义清晰的动词或类型名而不是追求通用完备。一个简单的词汇表可以这样组织。# 文件路径experiments/vocabulary.yaml task_types: - LOG_ANALYSIS - CODE_EXPLAIN - SQL_GENERATION - DATA_SUMMARY intents: - REQUEST - INFORM - ACCEPT - REJECT - STATUS status_values: - PENDING - RUNNING - SUCCESS - FAILED constraints: - NO_GUESSING - STRICT_FORMAT - MAX_TOKENS output_formats: - SUMMARY - TABLE - JSON - TEXT第二步是构建 Agent 消息模型。两个 Agent 通信时消息必须具备协议化的最小结构。用 Pydantic 定义消息不是为了让这段代码看起来很正规而是为了在消息进入模型或进入下一个 Agent 之前完成一次强制校验让非法消息直接暴露在早期。第三步是构建提示词生成器。它的作用是把结构化任务卡转换为模型能理解的自然语言模板但模板的句式是固定的字段名也是从词汇表里映射出来的。这一步决定了受控英语最终如何变成模型提示。第四步是模拟 Agent 通信流程。一个编排 Agent 发出 REQUEST一个工作 Agent 返回 STATUS/INFORM 消息整个过程可以被记录和验证。这四个步骤顺序上不能颠倒。词汇表是源头消息模型是契约提示词生成器是转换层Agent 流程是最终应用。如果先写 Agent 流程再补词汇表很容易出现消息字段和实际业务对不齐的情况。7. 完整示例代码实现下面是一个完整的最小示例覆盖从提示词构造到 Agent 消息校验的各个关键环节。不需要额外框架跑通这个示例就能直观体会受控英语消息和自由文本消息的差别。先定义消息模型。文件路径为 experiments/messages.py# 文件路径experiments/messages.py from enum import Enum from typing import Optional from pydantic import BaseModel, Field, ValidationError class Intent(str, Enum): REQUEST REQUEST INFORM INFORM ACCEPT ACCEPT REJECT REJECT STATUS STATUS class TaskType(str, Enum): LOG_ANALYSIS LOG_ANALYSIS CODE_EXPLAIN CODE_EXPLAIN SQL_GENERATION SQL_GENERATION DATA_SUMMARY DATA_SUMMARY class Status(str, Enum): PENDING PENDING RUNNING RUNNING SUCCESS SUCCESS FAILED FAILED class AgentMessage(BaseModel): intent: Intent task_type: Optional[TaskType] Field(defaultNone, description任务类型必须属于受控词汇表) status: Optional[Status] Field(defaultNone, description当前状态必须属于受控词汇表) request_id: str Field(description全局唯一请求ID用于追踪) payload: str Field(description携带的原始数据或结果描述) max_tokens: int Field(default500, ge1, le4000)这段代码的关键在于把所有通信字段都限定在枚举类型中。意图、任务类型、状态都只能是预设值request_id 保证消息可以追踪payload 承载具体的业务数据。相比自由文本消息这种结构让 Agent 之间有了明确的协议边界。接下来实现提示词生成器。文件路径为 experiments/prompt_builder.py# 文件路径experiments/prompt_builder.py import yaml from pathlib import Path from messages import AgentMessage class PromptBuilder: def __init__(self, vocabulary_path: str): self.vocab self._load_vocabulary(vocabulary_path) def _load_vocabulary(self, path: str) - dict: with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def build_task_prompt(self, message: AgentMessage) - str: task_type message.task_type.value if message.task_type else UNKNOWN_TASK prompt ( fYou are an AI assistant specialized in {task_type}.\n fFollow the instructions strictly.\n fTask: {task_type}\n fInput: {message.payload}\n fConstraint: NO_GUESSING\n fOutput format: JSON\n fDo not include extra explanations outside the required JSON.\n ) return promptPromptBuilder 的职责很纯粹把 AgentMessage 转换为一个限制严格的提示词模板。模板中固定出现 Task、Input、Constraint、Output format 四个字段不留给模型自行发挥的余地。所有能复用的变量都来自词汇表或消息字段这样整个提示词始终在受控范围内。然后实现一个模拟的 Agent 工作节点它负责接收规范消息、解析消息内容、调用模型或直接返回结果。为了便于运行演示这里使用消息自带的 payload 模拟处理结果。文件路径为 experiments/agent_worker.py# 文件路径experiments/agent_worker.py from messages import AgentMessage, Intent, Status class AgentWorker: def process(self, message: AgentMessage) - AgentMessage: if message.intent ! Intent.REQUEST: return AgentMessage( intentIntent.REJECT, request_idmessage.request_id, payloadintent is not REQUEST, cannot process, statusStatus.FAILED, ) # 模拟执行任务 result fhandled task {message.task_type.value} with payload: {message.payload[:20]} return AgentMessage( intentIntent.INFORM, task_typemessage.task_type, statusStatus.SUCCESS, request_idmessage.request_id, payloadresult, )这里的 AgentWorker 是一个极简实现但它演示了协议的重要性它不是靠读自然语言猜对方意图而是先校验消息的 intent 字段。如果消息意图不是 REQUEST就直接返回 REJECT。在真实项目里这一步可以换成更复杂的权限校验、模型调用、任务分派逻辑结构不变。最后写一个入口文件串起消息构造、提示词生成、Agent 处理和输出展示。文件路径为 experiments/demo_run.py# 文件路径experiments/demo_run.py from messages import AgentMessage, Intent, TaskType from prompt_builder import PromptBuilder from agent_worker import AgentWorker def main(): builder PromptBuilder(vocabulary.yaml) worker AgentWorker() request AgentMessage( intentIntent.REQUEST, task_typeTaskType.LOG_ANALYSIS, request_idreq-20250301-001, payload2025-03-01 10:00:00 ERROR Connection timeout, ) prompt builder.build_task_prompt(request) print( generated prompt ) print(prompt) response worker.process(request) print( agent response ) print(response.model_dump_json(indent2, ensure_asciiFalse)) if __name__ __main__: main()运行命令cd experiments python demo_run.py预期会输出一份结构化提示词和一份 Agent 响应消息响应中包含 REQUEST 对应的 INFORM 意图、SUCCESS 状态和统一 request_id。这说明整个流程已经跑通受控消息可以转换为固定模板提示词Agent 之间也能通过规范消息完成一次简单协作。如果运行失败第一步检查依赖是否安装完整第二步检查 vocabulary.yaml 路径是否正确第三步看是否有 pydantic 校验异常确认所有字段值都在枚举范围内。8. 运行结果与效果验证上面的示例运行后第一段输出是生成给模型的提示词此时你看到的是一段固定句式的英文指令。和手写自由提示词相比它的特点是字段名固定、命令明确、输出格式确定。第二段输出是 AgentWorker 返回的规范响应包含 request_id说明这次协作可以从头追踪到尾。判断一个受控英语方案是否成功不能只看“能不能用”要看四个验证维度。第一维是可解析率也就是消息被程序正确解析的比例。用 Pydantic 校验每条消息统计校验通过率。如果可解析率低于目标线说明词汇表或消息模型定义不清晰需要回头补全枚举值和字段说明。第二维是歧义率也就是模型接收到提示后返回不合格式的比例。理想情况下受控英语提示的歧义率应该明显低于自由文本提示。第三维是跨模型稳定性同一份受控消息发给不同模型看返回结构是否保持一致这是受控英语的核心收益之一。第四维是审计完整度检查每条消息是否都有自己的 request_id是否能在日志中形成完整链路。实际项目里建议在开发环境把这些验证写成自动化脚本。每当修改词汇表或消息模型时跑一遍历史用例回归保证新增规则不会破坏已有消息的兼容性。这里的核心思路是受控英语方案本身就是一种可测试的系统因为它在设计上就为程序化校验留出了位置。失败时的排查方向也应放在这四处消息校验失败先看枚举值提示词输出异常先看 PromptBuilder 的字段映射Agent 响应不符合预期先看意图路由逻辑链路追踪丢失先看 request_id 是否在每一条转移消息中都传递。9. 常见问题与排查思路受控英语落地过程中最容易出问题的往往不是概念难懂而是规则与实际系统的衔接。下面按出现频率整理几类问题。问题现象可能原因排查方式解决方案消息校验频繁失败枚举值覆盖不全业务出现词汇表外的新任务查看校验异常日志记录失败字段扩大受控词汇表但每个新增词都必须有明确定义和责任人模型输出不符合预期提示词模板太灵活或者受控词含义未在模型上下文里说明输出模型实际收到的提示词逐字段核对收紧模板必要时在提示词中附带简短术语定义Agent 协作出现死循环意图路由缺少兜底逻辑查看各 Agent 每次请求的意图和响应状态增加循环检测对重复请求做熔断处理请求链路无法追踪部分消息没有传递 request_id检查所有消息构造点在消息模型初始化时强制要求 request_id规则更新后旧消息格式不兼容消息模型增加必填字段或修改枚举值对比版本变更记录查看历史消息样例保持向后兼容新字段使用可选类型废弃字段标记 deprecated生产环境提示词被绕过Agent 直接使用自由文本调用模型接口检查代码路径确认所有模型调用是否经过提示词构建器在模型调用网关处统一拦截禁止直接调用这里真正容易踩坑的是第二项。很多团队把受控英语的字段名当成了“模型本来就应该理解的东西”结果缩写太多、术语太专模型反而产生新的歧义。更稳妥的做法是在提示词模板中加入一行术语说明或者让字段名足够自解释。比如把 CMD 改成 REQUEST把 DES 改成 ANALYSIS_RESULT。另外需要提醒的是受控词汇表不是一次性设计完成的它会在业务演进中持续调整。每次新增词汇都应该记录变更原因、使用场景和兼容性影响否则规范会逐渐腐化最后退回自由文本状态。10. 最佳实践与工程建议从工程落地角度看受控英语方案有几个值得坚持的原则。词汇表必须有owner。受控词汇表是统一规范的核心资产不能任由每个开发者自行扩充。建议在团队里指定规范维护人所有新增词汇走评审流程。每新增一个意图或任务类型都要回答三个问题它真的无法用现有词汇表达吗它会不会和其他词汇语义重叠它是否能在三个以上场景中复用如果答案是否定的就不要加入。模板与业务分离。PromptBuilder 这类组件只负责把结构化消息转换为提示词不包含业务逻辑。业务同学修改任务定义时改的是词汇表或消息字段不需要理解模板实现。这样提示词模板的变动频次会很低业务调整的成本也随之降低。安全边界要前置。受控英语给安全带来的最大收益是“非法输入可以被拦截”。在消息模型校验层所有不在枚举范围内的字段值都会被拒绝这相当于给 Agent 通信加了一层白名单过滤。生产环境中应在模型调用入口处强制做消息校验不要只在测试阶段做。同时注意payload 字段是自由文本仍然可能包含注入内容需要对 payload 做独立的敏感信息检查和长度限制。可观测性是规范的放大器。所有 Agent 消息进入日志时应保留意图、任务类型、请求状态和 request_id。后续做故障复盘时直接按 request_id 聚合所有相关消息即可还原一次完整协作。没有规范消息就没有这种便利有规范消息却不好好记录同样浪费了规范优势。最小权限原则同样适用。每个 Agent 节点只应拥有执行自身任务所需的权限不能因为受控英语字段校验通过就默认消息携带的操作是安全的。意图为 REQUEST 的消息可能仍然包含不合法指令必须结合权限系统做二次校验。版本管理要严格。消息模型和词汇表都是公共契约任何变更都涉及多个调用方。建议把 vocabulary.yaml 和 messages.py 纳入版本管理并带着版本号发布。合约变更时优先采用可兼容方式比如新增字段用可选类型新增枚举值不影响旧消息流通。基准回归要自动化。推荐维护一批标准测试用例覆盖高频任务类型的消息构造、提示词生成、模型调用模拟、异常消息拒绝等场景。这些用例在每次修改词汇表或消息模型后运行能有效防止“改一处破坏全线”的问题。在生产环境中也建议走灰度发布。先让新规范节点处理小流量复制消息对比新旧规范下的消息解析率和模型输出格式确认稳定后再逐步切流。整个受控英语体系一旦跑起来你会明显感受到排查 Agent 问题时的区别从“翻聊天记录猜上下文”变成“查结构化事件找节点”。11. 总结与后续学习方向Canon-A 所代表的受控英语方向本质上是在回答一个问题大模型交互能不能像软件工程一样拥有明确的接口契约。提示词不是一串碰运气的文本Agent 消息也不是随意交换的字符串它们都应该是可定义、可校验、可追踪的。这篇文章从受控英语的历史讲起拆解了 Canon-A 的提示规范与智能体通信两个侧面也提供了一个用 Python 和 Pydantic 实现的轻量级参考方案你可以直接把它改造成自己项目里的通信基础层。下一步的实践路径有三条。第一条是在你的现有项目里引入最小受控词汇表和消息模型先把日志分析、SQL 生成这类确定性任务迁移到受控提示词上观察跨模型稳定性变化。第二条是关注 Canon-A 官方后续发布的规范细节因为这类标准的价值在于被广泛采用功能、格式、示例都会比你现在看到的分析更完整。第三条是参与或借鉴类似规范社区的做法把 Agent 间消息协议当做一个长期演进的设计系统来经营而不是临时解决眼前通信问题。最后提醒一点受控英语和自然语言不是对立关系它们对应的是不同的任务复杂度。自然语言的开放性是宝贵的但它不应该是默认选项。下一次写提示词或定义 Agent 消息时先问一句这一次交互需要的到底是“理解”还是“执行”如果答案是执行那么一套受控英语规范会比一段精心措辞的自然语言更可靠也更值得长期维护。建议收藏本文当你开始搭建自己的 Agent 协作协议时随时可以回来对照实践。

相关新闻

最新新闻

日新闻

周新闻

月新闻