
当两个大语言模型智能体被要求用一条极短消息完成一次信息传递而这条消息又不允许直接把完整属性写进去时它们不会老老实实地把自然语言压缩成几十个字符而是会逐渐开始使用一种彼此约定好的简短表达。这种在交互压力下自发形成的“黑话”就是 Emergent Language涌现语言。GlossoGen 并不是某个现成商业产品而是一套面向多智能体 LLM 交互的研究与实验设计思路它把“让智能体自己发明一套专用词汇”这件事变成可复现、可度量、可维护的工程方案。这篇文章会从涌现语言的核心概念讲起先用一个最小实验框架理解智能体之间如何通信再给出 GlossoGen 的协议设计、可运行代码、验证指标、排查链路和生产化建议。文章适合正在研究 Multi-Agent 系统、被多智能体协作中的 token 成本或上下文窗口问题困扰或者想跑一个可复现的大模型涌现语言实验的开发者。读完可以直接照着搭建自己的多智能体通信实验并用一套明确指标判断“词汇是否真的涌现了”。1. 先理解 Emergent Language智能体为什么会发明“黑话”1.1 Emergent Language 的通俗含义人类在不停切换语境时会形成一种压缩表达。两个经常配合的同事之间说“把昨天的 3 号模板跑一遍”彼此都知道“3 号模板”指的是哪份文件、跑完要看哪些字段、结果发给谁。这里省掉的大量背景信息就是双方共享的“隐含上下文”。Emergent Language 在计算机科学中也是一回事只不过对话双方换成了 LLM Agent。它指的是多个智能体在完成协作任务时为了提高通信效率、压缩信息量自发形成的一种不同于自然语言的简化符号系统。注意“自发”这个词。如果两个智能体只是共享一份固定的提示词里面写死“用 ok 表示成功用 fail 表示失败”这不算涌现语言。真正的涌现语言必须是智能体在交互过程中根据任务反馈动态协商、逐步稳定下来的表达方式。一个简单的示意图如下两个 AgentSender发送者和 Receiver接收者一个受限通信信道每次只能传少量字符一个共同任务Receiver 必须根据 Sender 的消息选中正确对象Sender 的职责把对象属性压缩成一条短消息Receiver 的职责解读消息并做出选择反馈机制选对则消息被强化选错则消息需要调整在这个循环中如果任务是可重复的双方就会慢慢形成稳定的短表达这就是最小形态的涌现语言。1.2 多智能体 LLM 交互为什么会遇到通信瓶颈传统多智能体系统的通信方式通常很直接A 把完整上下文传给 BB 处理完再把完整结果传给 C。换成 LLM Agent 之后这个做法会很快遇到三个问题。一个是 token 成本。把一段 2000 字的状态描述传给另一个 Agent每次产生一次 API 调用就要为这些输入付费几轮迭代后 token 消耗会非常可观。第二个是上下文窗口。Agent 的上下文是有限的如果每个协作轮次都要把完整背景塞进去留给推理和新增内容的空间就会被挤占。第三个问题是歧义。多个 Agent 各自维护着自己的上下文如果互相之间不约定术语同一个概念在不同 Agent 口中可能是完全不同的描述协作一长必然出偏差。GlossoGen 的设计动机正是针对这三个问题让智能体之间建立一套动态维护的专用词汇表以压缩表达代替完整自然语言减少 token 消耗、缩短有效上下文、提高通信的一致性。1.3 与固定提示词模板的本质区别有人可能会问既然要让 Agent 之间用短码通信为什么不直接在系统提示里写死“b-sq 表示 blue square”这种做法可以工作但它有三点不足词汇是人工指定的不是涌现出来的。Agent 没有表达选择权也就无法根据实际任务动态调整表达粒度。词汇是静态的。任务变了、属性变了、Agent 数量变了人工词表就要跟着改。词汇缺乏稳定性反馈。哪个词好用、哪个词总被误解Agent 自身感知不到。GlossoGen 的做法不同。它把词汇表看成一种运行时资源由智能体自己写入、使用、评分、淘汰。每一轮交互都会反馈词条的使用效果系统定期清理无效条目保留高成功率的条目。这种机制更像人和人之间的语言演化而不是一份写死在配置文件里的字典。1.4 涌现语言研究中的三个核心难点开始写代码之前要先明确这类实验最难的地方在哪里否则后面很容易被实验结果搞得一头雾水。第一是语义稳定性。Sendor 发明了一个新词Receiver 是否真的理解了它如果 Receiver 只是猜测了一个正确答案但不是通过理解词义选对的那下一轮换一个目标对象时这个词就可能失效。第二是收敛速度。理想情况下两个 Agent 对话几十轮后词汇表应趋于稳定不再频繁制造新词。如果词表一直在膨胀说明双方没有形成共识实验设计多半出了问题。第三是可解释性。自造的短码对 Agent 来说有意义对人类研究者却未必。我们需要一套方法把“b-sq”这类词映射回人类可读的属性描述并判断这个缩写是否合理。这三点的解决方式会在后面 GlossoGen 的协议设计和验证部分展开。2. 用参考游戏搭建最小实验两个 Agent 如何互相理解2.1 为什么选择参考游戏作为起点研究涌现语言最常见也最可控的实验框架是参考游戏Referential Game。参考游戏的基本结构如下候选集合N 个对象每个对象都有若干属性Sender 视野看到目标对象但看不到 Receiver 的候选列表Receiver 视野看到完整候选列表但看不到 Sender 的目标通信约束Sender 只能发送一条长度受限的消息目标Receiver 根据消息从候选列表中选出目标对象这个框架看起来简单但已经涵盖了涌现语言的全部核心要素信息压缩、语义协商、共享词汇、错误反馈。使用参考游戏做实验还有一个好处任务结果可以量化。每次选择只有对或者错研究者和程序都能轻易判断。对于探索性实验这是最重要的工程友好性。2.2 实验数据的准备先用属性组合不要急着上图片真正研究视觉场景下的涌现语言通常需要用图像数据集。但在起步阶段强烈建议先用结构化属性对象。原因是结构化数据是稳定的不涉及图像编码和视觉特征提取属性可以精确描述便于判断 Receiver 是否真的理解了新词数据生成非常简单几行代码就能造出成千上万个对象例如可以生成如下形式的对象ID颜色形状尺寸位置obj_01bluecirclelargetopobj_02redsquaresmallbottomobj_03greentrianglemediumtop每个对象只有三个属性。候选列表可以从全量对象中随机抽取 4 到 6 个Sender 只能看到目标对象的 ID 和属性Receiver 看到候选对象的完整列表。在真实项目中设计对象数据结构时关键是可以把这些对象序列化成 JSON方便后续塞进 LLM 的 prompt。下面是一个对象集合的示例{ objects: [ { id: obj_01, properties: { color: blue, shape: circle, size: large } }, { id: obj_02, properties: { color: red, shape: square, size: large } }, { id: obj_03, properties: { color: blue, shape: square, size: small } } ] }实际编写代码时可以用数据类来管理这些对象方便随机抽取候选集、切换目标对象、序列化 prompt。2.3 为什么从两个 Agent 开始而不是直接上多智能体群多智能体系统听起来更接近标题里的“Complex Multi-Agent Interactions”但实验设计必须遵循增量原则。两个 Agent 时语言协商只需要双方达成一致词汇表收敛相对容易。一旦扩展到三个以上 Agent新词得到共识的难度会急剧上升。A 发明的词 B 懂C 可能不懂B 按自己的理解改了一下A 又不认识了。新增一个 Agent就新增一份语义漂移风险。推荐的路径是先用两个 Agent 跑通参考游戏验证“短码通信 动态词表”是否可行再固定词表让第三个 Agent 通过读取词表加入对话观察它能否快速复用已有词汇最后放开新词生成权限让多个 Agent 通过投票方式达成新词共识本文后面的代码示例只展示第一步也就是两个 Agent 的最小闭环。第四步的多体扩展思路会在最后一节展开。2.4 学习环境和生产环境的实验差异参考游戏的最小实现在本地笔记本上就能完成并不需要高性能显卡。原因很简单真正参与推理的是 LLM API 或本地 Ollama 服务本地代码只负责拼 prompt、调 API、解析结果、维护词表。学习环境建议按这个标准配置项目学习环境正式实验环境候选对象数量10 到 20 个1000 个以上每轮候选集大小4 到 68 到 20通信长度上限20 字符50 字符或按任务定制模型便宜的小模型或本地模型任选但需要控制并发持久化无或 JSON 文件数据库记录完整对话可观测性打印日志指标采集、轨迹回放如果一开始就在学习环境里堆大量候选对象和复杂任务实验周期会被拉得很长而且很难判断是词表机制的问题还是数据规模的问题。3. GlossoGen 的核心机制把“黑话”当作运行时资源来管理3.1 GlossoGen 的设计目标GlossoGen 这个名字可以拆开理解Glosso 指词汇表glossaryGen 指生成或演化generation。整个机制的核心目标是在信息瓶颈下通过动态词表提升任务成功率、降低 token 成本同时尽量保证词汇含义可被人类审计。它不是一个具体的模型或 API而是一种行为约束。向 Agent 的系统提示中注入规则让它按照规则生成新词、复用旧词、提交理解释义再由程序统一管理词表。被管理的不只是词条本身还包括词条的生命周期。3.2 动态词汇表的数据结构在设计数据表之前先看每一轮交互需要记录哪些信息当前消息文本Sender 认为它表达的是哪个对象Receiver 到底选了哪个对象如果选错Receiver 对消息的本地理解是什么本轮消息用到了哪些已有词条或者是否产生了一个新词基于这些信息一个词汇条目至少包含以下字段字段类型含义termstring智能体发明的短码canonical_propertiesJSONSender 声称的完整属性描述definition_by_senderstringSender 用自然语言写的释义definition_by_receiverstringReceiver 自己的释义usage_countint累计使用次数success_countint累计成功次数success_ratefloatsuccess_count / usage_countstable_scorefloat基于成功率和含义一致度的综合评分last_used_roundint最近一次使用的轮次其中 stable_score 可以选择简单公式例如stable_score success_rate * usage_count / (usage_count 1)这个公式的意思是使用次数越多、成功率越高词条越稳定。刚创建的词条即使第一轮成功stable_score 也不会太高避免某个一次性的幸运成功被长期保留。3.3 词条的生命周期生成、复用、校验、淘汰GlossoGen 的词表管理可以按以下顺序执行Sender 在生成消息前先查询当前词表找出能覆盖目标对象属性的已有词条。如果已有词条能拼出消息优先复用旧词避免制造新词。如果旧词不足以表达目标对象或者需要表达的属性组合从未出现Sender 可以发明一个新词。新词必须附带 canonical_properties 和 definition_by_sender否则程序拒绝写入词表。Receiver 收到消息后若消息中包含未知词程序要求 Receiver 返回自己的理解也就是 definition_by_receiver。程序对比 sender 释义和 receiver 释义若两者属性集合一致认为含义一致否则记录不一致。本轮结束后更新涉及词条的 usage_count、success_count、success_rate、stable_score。当 stable_score 低于阈值或长期未使用程序将词条标记为淘汰。这个流程的关键点是词汇是否保留不只看本轮是否成功还要看双方理解是否一致。否则就会出现一种假象Sender 写了一个缩写Receiver 其实猜错了含义但因为候选集里正确对象特征太明显最后选对了。这样积累出来的词表是虚的换一批数据就可能崩溃。3.4 与固定 System Prompt 的组合方式GlossoGen 不排斥 System Prompt。相反它需要一个稳定的 System Prompt 来定义行为规则。例如Sender 的系统提示可以写你是参考游戏中的发送者。 你的任务是向接收者发送一条不超过 20 个字符的消息帮助它从候选对象中选出指定目标对象。 可以使用已有词表中的词条。已有词条的格式如下 b-sq blue square r-ci red circle 不允许直接书写完整属性例如 blue square 是不允许的。 如果没有合适词条你可以发明一个新词。 发明新词后必须按如下格式附上释义 NEW_TERM: xxx CANONICAL: {color: blue, shape: square}这样的提示既给 Agent 保留了发明空间又把格式约束在程序可解析的范围内。Reveiver 的系统提示则侧重反向解释你是参考游戏中的接收者。 你会收到一条短消息并看到一个候选对象列表。 请根据消息含义选出正确的对象 ID。 如果消息中有你不理解的词请使用理解中的属性组合来猜测并在最终输出中附上你的理解释义。 输出格式必须为 { choice: obj_01, interpretation: {...} }有了这两个提示程序就可以用代码解析输出不需要依赖 Agent 自由输出。3.5 为什么不能只依赖 Prompt 做“黑话管理”许多开发者在多智能体项目里遇到通信成本问题时第一反应是改 Prompt告诉 Agent “说话简短一点”“不要输出长解释”。这种做法确实能压缩输出但存在两个不可控点。第一Agent 每次对话都是独立的。它可能每次编造出不同的缩写下一轮又改一个因为没有外部记忆告诉它之前用过什么词。第二程序无法评估 “简短表达” 是否真的有效。哪怕 Agent 一直在用固定缩写程序也看不到这些缩写的历史成功率和含义一致性。GlossoGen 的思路是把“记忆”和“评估”从 Prompt 中抽离出来交给程序层的数据结构。Prompt 只负责定义行为边界词表的持久化、评分、淘汰全部由代码管理。这样才能保证实验可复现、可观察、可调优。4. 最小可运行实现两个 LLM Agent 通过参考游戏形成共享词表4.1 前置环境准备代码示例使用 Python 3.9 以上版本通过 OpenAI 兼容接口调用大模型。这里不限定具体厂商OpenAI、DeepSeek、Moonshot、本地 Ollama 等只要提供 OpenAI 兼容的 /v1/chat/completions 接口都可以使用。需要安装的依赖只有一个客户端 SDK。以最常用的 openai 库为例pip install openai如果使用本地模型可以先用 Ollama 启动一个兼容服务。例如ollama pull qwen2.5:7b ollama serve启动后本地服务默认监听 11434 端口OpenAI SDK 可以通过 base_url 指向它。在项目目录下创建配置信息。为了不把密钥写死在代码里建议从环境变量读取export OPENAI_API_KEYyour-api-key export OPENAI_BASE_URLhttps://api.openai.com/v1本地模型环境则不需要 API Keybase_url 指向本地端口即可export OPENAI_API_KEYollama export OPENAI_BASE_URLhttp://localhost:11434/v1注意不同模型对 JSON 输出格式的遵循程度不一样。如果发现返回结果经常解析失败可以在代码中加入重试机制或者在后处理中做容错。4.2 项目结构与文件职责最小实验只需要两个文件glossogen/ ├── config.py # 模型、路径、词表上限等配置 ├── main.py # 实验主逻辑 └── glossary.json # 运行后生成的动态词表如果实验轮次多了建议再加一个experiment_logs/目录把每轮输入输出原始日志落盘方便出问题时回溯。config.py 示例# config.py MODEL_NAME gpt-4o-mini MAX_MESSAGE_LENGTH 20 MAX_ROUNDS 30 GLOSSARY_PATH glossary.json CANDIDATE_SIZE 5 TEMPERATURE 0.2 MAX_TOKENS 256TEMPERATURE设置较低是希望 Agent 的输出尽量稳定不要每次生成新词都完全不同。如果实验目的是观察多样性能可以调高但必须接受词汇表收敛变慢。4.3 对象集合与候选集生成先从最简单的对象集合开始。这里生成 12 个对象属性为颜色和形状的组合import itertools import json import random COLORS [blue, red, green, yellow] SHAPES [circle, square, triangle] SIZES [large, small] objects [] idx 1 for color, shape, size in itertools.product(COLORS, SHAPES, SIZES): objects.append({ id: fobj_{idx:02d}, properties: { color: color, shape: shape, size: size } }) idx 1 print(len(objects))这里使用 itertools.product 生成全组合一共 24 个对象。实际运行中可以把这个列表持久化为 JSON 文件也可以直接放在内存里。每次实验轮次从全量对象中随机抽取一个目标对象作为 ground truth再抽取 4 个干扰对象作为候选集。候选集中必须包含目标对象否则任务无解。def sample_round(objects, cand_size5, seedNone): if seed is not None: random.seed(seed) candidate random.sample(objects, cand_size) target random.choice(candidate) return candidate, target注意这里 random.sample 返回的候选集是无序的需要把顺序固定以后再传给 Receiver避免 Receiver 因为列表顺序变化产生感知偏差。4.4 Sender 与 Receiver 的提示词构造Sender 的目标是生成一条短消息。它不知道候选集只知道目标对象的属性以及当前词表里有哪些词条可用。def build_sender_prompt(target, glossary_entries): prompt f你是参考游戏中的发送者。 目标对象属性如下 {json.dumps(target, ensure_asciiFalse)} 当前可用词表 {json.dumps(glossary_entries, ensure_asciiFalse, indent2)} 要求 1. 输出一条不超过 {MAX_MESSAGE_LENGTH} 个字符的消息。 2. 优先复用词表中已有词条。 3. 如果没有合适词条可以发明一个新词。 4. 如果发明新词额外输出新词的属性释义。 5. 输出必须是可以解析的 JSON格式如下 {{ message: 短消息内容, new_terms: [ {{term: 缩写, canonical_properties: {{color: ..., shape: ..., size: ...}}}} ] }} return promptReceiver 的提示词则必须包含候选集完整信息避免它靠猜。def build_receiver_prompt(message, candidate, glossary_entries): prompt f你是参考游戏中的接收者。 你收到发送者的一条短消息 {message} 候选对象如下 {json.dumps(candidate, ensure_asciiFalse, indent2)} 当前可用词表 {json.dumps(glossary_entries, ensure_asciiFalse, indent2)} 要求 1. 根据消息从候选中选出最符合含义的对象 ID。 2. 如果消息中的词你不理解根据候选属性猜测其含义并输出你的理解。 3. 输出必须是 JSON {{ choice: 选择的对象ID, interpretation: {{ color: 你理解的颜色, shape: 你理解的形状, size: 你理解的尺寸 }} }} return prompt两个提示词的关键设计点是所有输出格式都用 JSON 明确约束并且要求 Agent 把“新词释义”或“理解释义”输出出来。没有这些释义程序就无法完成语义一致性校验。4.5 主循环一轮参考游戏的完整流程接下来把整个一轮交互串起来。import json from openai import OpenAI import config client OpenAI() glossary [] def call_llm(messages, temperatureconfig.TEMPERATURE, max_tokensconfig.MAX_TOKENS): resp client.chat.completions.create( modelconfig.MODEL_NAME, messagesmessages, temperaturetemperature, max_tokensmax_tokens, ) return resp.choices[0].message.content def parse_json(raw): # 简单容错尝试直接解析失败则去除 markdown 代码块前缀 text raw.strip() if text.startswith(): text text.strip() if text.startswith(json): text text[4:].strip() return json.loads(text) def run_round(candidate, target, glossary): # 1. Sender 生成消息 sender_prompt build_sender_prompt(target, glossary) sender_raw call_llm([{role: user, content: sender_prompt}]) sender_data parse_json(sender_raw) message sender_data.get(message, ) new_terms sender_data.get(new_terms, []) # 2. 如果 Sender 发明了新词先写入词表 for term_info in new_terms: glossary.append({ term: term_info[term], canonical_properties: term_info[canonical_properties], definition_by_sender: term_info[canonical_properties], definition_by_receiver: None, usage_count: 1, success_count: 0, success_rate: 0.0, stable_score: 0.0, last_used_round: 0 }) # 3. Receiver 根据消息选择对象 receiver_prompt build_receiver_prompt(message, candidate, glossary) receiver_raw call_llm([{role: user, content: receiver_prompt}]) receiver_data parse_json(receiver_raw) choice receiver_data.get(choice, ) interpretation receiver_data.get(interpretation, None) # 4. 判断本轮是否成功 success choice target[id] # 5. 更新命中的词条 for entry in glossary: if entry[term] in message: entry[usage_count] 1 if success: entry[success_count] 1 entry[success_rate] entry[success_count] / entry[usage_count] entry[stable_score] entry[success_rate] * entry[usage_count] / (entry[usage_count] 1) if interpretation and entry[definition_by_receiver] is None: entry[definition_by_receiver] interpretation entry[last_used_round] 0 return message, choice, success, interpretation主循环里把每轮结果输出出来并把词表保存到 JSON 文件import os, time def main(): all_objects load_objects() round_num 0 for round_num in range(1, config.MAX_ROUNDS 1): candidate, target sample_round(all_objects, config.CANDIDATE_SIZE) message, choice, success, interpretation run_round(candidate, target, glossary) print(fRound {round_num}: msg{message}, choice{choice}, success{success}) for entry in glossary: entry[last_used_round] round_num with open(config.GLOSSARY_PATH, w, encodingutf-8) as f: json.dump(glossary, f, ensure_asciiFalse, indent2) time.sleep(0.5) print(实验结束词表条目数:, len(glossary)) if __name__ __main__: main()这个主流程虽然简单但已经具备最小闭环输入候选集和目标对象输出消息和历史词表每轮都能观察成功率变化。4.6 运行结果示例一条正常轨迹假设目标对象是{color: blue, shape: square, size: large}候选集里有蓝色方形、蓝色圆形、红色方形。Sender 如果发现词表里已有b-sq可以直接发送{message: b-sq}Receiver 收到后若理解正确会返回{choice: obj_01, interpretation: {color: blue, shape: square, size: large}}任务成功词表里b-sq条目的 success_count 加 1。如果候选集里有两个蓝色方形但尺寸不同b-sq就表达不完整。这时 Sender 需要发明新词例如b-sq-lg并附上完整属性释义。这一轮就可能失败但失败本身就是词表演化的信号。4.7 学习环境最容易踩的三个坑第一个坑是模型不遵守 JSON 输出格式。很多模型在输出 JSON 时会在前后加 markdown 代码块甚至夹杂自然语言。对策是解析时做容错并在提示词中明确要求“只输出 JSON”。实践中还可以在 System Prompt 里加一句“不要输出解释只输出 JSON”。第二个坑是温度设置过高导致同一对象每次生成不同新词。若希望看到稳定的词语涌现temperature 不要超过 0.5。真相比大学里跑的实验更“热闹”但词汇表很难收敛。第三个坑是候选集顺序变化影响 Receiver 判断。每轮传给 Receiver 的候选列表必须固定顺序否则模型可能因为列表顺序不同而选中不同对象干扰成功率统计。正确做法是在同一轮内candidate 列表顺序固定目标对象位置不变。5. 验证“语言真的涌现了”指标体系与对照实验5.1 先确认三个核心指标跑完几十轮后不能只看最后成功率就下结论。建议至少统计三个指标。成功率Receiver 正确选中目标对象的比例。它反映通信整体是否有效。如果成功率长期在随机水平附近说明消息没有传递足够信息或者 Receiver 的解读不稳定。压缩率单条消息的平均字符数除以同场景下完整自然语言描述的平均字符数。压缩率越低说明 Agent 确实在用短表达替代长语句。但压缩率不能单独看如果压缩过头导致消息完全不可识别成功率会掉下来。词表收敛速度新增词条数量随轮次的变化。理想情况下前 10 轮会陆续出现新词之后新增频率快速下降词表总数趋于稳定。如果每轮都有大量新词出现说明双方没有形成稳定共识。5.2 对照组完整自然语言通信只跑 GlossoGen 实验并不能说明这种机制有效因为缺少对比。建议同时跑一组“完整自然语言”对照Sender 不允许发明新词只能用完整属性描述来通信。再把这组对话的成功率和 token 成本记录成基线。实验组平均消息长度平均每轮输入 token成功率词表条数完整自然语言约 30 字符高高0GlossoGen 固定词表约 10 字符中中固定GlossoGen 动态词表约 8 字符中低逐步上升动态在理想情况下动态词表实验的成功率会随着轮次增加而接近基线而消息长度远低于基线。如果跑完多组实验后成功率始终低于基线就要检查词表是否产生了歧义或者是否缺少足够的反馈信息。5.3 定性检验看新词是否具备可解释性量化指标之外还要人工抽查词表。重点检查两个问题。第一新词是否和属性存在合理对应关系。例如如果 sender 生成的新词是b-ci释义为蓝色圆形这是合理的。如果 sender 生成了随机字符串x9f2而释义是蓝色圆形虽然也能用但可解释性和复用性都会差一些。第二sender 释义与 receiver 释义是否一致。当词表里存在释义不一致的条目时说明双方的理解产生了分歧。这类词条应该降低评分最终淘汰。实践中可以把词表导出为表格逐条看 canonical_properties 和 definition_by_receiver 的差异。{ term: b-sq, canonical_properties: { color: blue, shape: square, size: large }, definition_by_receiver: { color: blue, shape: square, size: large }, success_rate: 0.9, stable_score: 0.82 }如果两个 definition 完全一致这条词条就是健康的。5.4 哪些现象说明实验设计需要返工跑完实验后如果出现以下四种现象不要急着调参先回到设计层检查词汇表爆炸每轮都在生成新词传播收敛失败。可能原因候选属性组合太多20 字符的通信上限过严或 temperature 过高。高成功率但低可解释性消息成功引导了选择但 receiver 的释义与 sender 释义差异巨大。这说明 receiver 可能是在根据候选对象反推而非理解词义。词条频繁重合不同对象生成了相同缩写导致歧义。例如红色圆形和红色方形都生成了r-。这是因为新词规则没有把完整属性写进去或 prompt 对缩写规范要求太弱。成功率一直随机模型完全不理解短码连 “b-sq” 都不认识可能是 prompt 里没有给足词表信息也可能是模型能力不足以完成符号推理。遇到这些情况时优先调高释义约束只有“保持属性完整映射”的新词才允许写入词表再缩小候选对象属性规模重新跑实验。6. 常见问题排查从 API 报错到语义漂移6.1 高频故障现象与处理路径以下表格整理了在搭建和运行 GlossoGen 实验中最常见的故障。问题现象可能原因检查方式处理建议API 返回 rejected request schema or tool payload请求参数格式出错通常是 tools 参数或 JSON payload 与接口要求不一致检查请求体中 messages、tools、tool_choice 是否合法打印完整请求体临时去掉 tools 参数先跑通纯文本请求按接口文档重新组装 payloadrequest timed out模型推理时间过长、并发过高或网络不稳定查看单次请求耗时检查队列和并发设置降低 max_tokens减小候选对象数量切换本地模型或更高并发配额JSOSN 解析失败模型输出了解释文本或 markdown 包裹的 JSON打印原始返回内容在解析前做字符串清理提高 temperature 不改提示词里明确“只输出 JSON”新词每轮都不一样temperature 过高或模型没有看到历史词表检查词表是否在 prompt 中传入查看 system prompt将 temperature 降到 0.2 以下确认词表以最新条目传入提示词Receiver 总是选错对象候选集顺序不稳定、消息歧义大、候选集中属性太相似检查每轮日志中 candidate 顺序是否固定固定 candidate 顺序给 receiver 提供更多属性信息减少候选集中相似对象词表条目不断膨胀没有强制复用旧词或每轮目标属性组合变化太大统计新增词条频率检查 sender prompt 中复旧词指令在 prompt 中增强“优先复用已有词条”给新词添加录取条件只有完全匹配属性时才允许新增Receiver 的释义与 Sender 不一致模型只根据候选对象反推而不是理解消息本身对比同一词条的双端释义要求 Receiver 在收到未知词时输出理解程序对不一致词条降低 stable_score 并淘汰6.2 排查顺序从输入到模型能力逐步排除遇到故障时建议按下面的顺序排查而不是直接把错误日志丢给搜索引擎。先确认输入数据。候选集中是否存在重复 id目标 id 是否在候选集里如果目标不在候选中Receiver 永远不可能选对日志不会报错但结果全错。再确认 API 请求参数。messages 列表中每条消息的角色是否合理model 参数填的是否正确对于 OpenAI 兼容接口base_url 是否指向了正确的服务地址然后查上下文内容。词表有没有传进 prompt候选对象 JSON 是否完整有些模型对超长属性描述渲染不好要检查是否存在截断。再查返回结果解析。模型输出到底是合法 JSON 还是夹杂了解释文本。如果是解析失败打印原始文本即可定位。最后才是模型能力问题。如果以上都没问题但模型仍然无法把b-sq映射回蓝色方形说明当前模型符号推理能力不足。此时可以切换更大模型或者在提示词中加入“词表是对属性的可靠映射”说明。6.3 一个典型的排错案例某次实验中Sender 每轮都生成新词词表在 20 轮后膨胀到 30 条成功率却一直不高。检查日志后发现两个因素。第一sender prompt 里虽然写了“优先复用词表”但刷新词表后上一轮的新词刚写入 JSON 文件还没有传回内存导致 Sender 永远看不到最近生成的新词。第二系统温度设置为 0.8导致模型每次生成缩写的方式都有差异。修复方法每轮运行前从同一份 glossary 内存变量构造 prompt临时不要用文件读改写把 temperature 下调到 0.2。重跑后词表在 8 轮内趋于稳定成功率开始上升。注意词表的读写必须和 prompt 构造使用同一份数据快照。文件读写只用于持久化不应该在每轮 prompt 构造时反复读文件否则容易出现“上一轮新词本轮看不到”的问题。6.4 日志记录是排查的底线多智能体实验的调试远比单 Agent 复杂因为错误可能发生在任意一方。建议每轮都记录如下字段round轮次target_id目标对象 IDcandidate_ids候选集 ID 列表保持顺序messageSender 生成的短消息sender_rawSender 原始返回receiver_rawReceiver 原始返回choiceReceiver 的选择success是否成功used_terms消息中命中的词表条目new_terms本轮新生成的词表条目把这些字段写入 JSON Lines 文件后续分析和排错会节省大量时间。7. 从实验走向生产稳定性、可观测性与成本控制7.1 什么时候值得在生产中使用 GlossoGen不是所有多智能体场景都需要引入动态词表。判断是否适合时可以逐项核对以下条件通信对象相对固定Agent 长期合作而不是每次都临时组队。如果每轮都是新 Agent词汇表没有共享基础机制价值有限。任务模式重复Agent 反复处理相似的属性描述例如运维告警、订单状态、工单字段。重复才有涌现压缩的空间。上下文窗口吃紧Agent 的 system prompt 已经很长再塞入完整通信内容会很勉强。token 成本敏感每轮通信的输入 token 较高压缩表达能带来可量化的成本节省。如果一个项目同时满足前三条就值得建立一套词汇表管理模块。7.2 成本控制本地模型、缓存与批量实验学习阶段对成本敏感的话优先使用本地模型。Ollama 的 OpenAI 兼容接口可以直接复用上一节的代码。唯一改动是 config 中的 model 和 base_urlMODEL_NAME qwen2.5:7b OPENAI_BASE_URL http://localhost:11434/v1本地模型在复杂属性推理上的能力不如大型云模型但跑通流程、验证指标本不需要太强的模型。实验阶段还可以把已经调用过的 prompt 和响应缓存起来。例如如果同一轮实验用固定 seed 重复运行可以用 seed 作为缓存键避免大量重复请求。生产环境则不推荐这种缓存因为用户输入是多样的缓存命中率会很低。7.3 词表的持久化与版本管理词表是多智能体协作的重要资产不能只放在内存里。推荐使用 SQLite 或 JSON 文件持久化并在每次运行结束后备份。表结构可以比实验阶段更完整增加创建时间、更新时间、版本号字段CREATE TABLE glossary ( id INTEGER PRIMARY KEY AUTOINCREMENT, term TEXT UNIQUE NOT NULL, canonical_properties TEXT NOT NULL, definition_by_sender TEXT, definition_by_receiver TEXT, usage_count INTEGER DEFAULT 0, success_count INTEGER DEFAULT 0, success_rate REAL DEFAULT 0.0, stable_score REAL DEFAULT 0.0, created_at TEXT, updated_at TEXT, last_used_round INTEGER );每次实验或每次重要版本发布都保存一份词表快照方便回滚。如果换了模型旧词表能否继续生效也需要验证建议在词表中记录“创建时使用的模型名”。7.4 可观测性设计生产环境中的多智能体系统除了业务日志还需要监控以下指标每轮输入 token 和输出 token词条新增数量、命中数量、淘汰数量词条平均 success_rate平均消息长度任务成功率随轮次的变化这些指标都可以通过结构化日志或 Prometheus 等监控系统采集。监控的价值在于当词表出现语义漂移时你能在早期看到 success_rate 下降而不是等到业务失败后才回头查对话记录。注意多智能体系统里任何 Agent 的行为变化都可能导致词表失效。模型版本升级、提示词微调、候选集分布变化都应当触发行回归实验确认词表语义是否仍然成立。7.5 安全与可解释性让多个 Agent 自己发明表达方式虽然节省了 token但也带来了一个可解释性风险系统运行时人类可能完全不知道 Agent 之间在“说什么”。应对思路是强制要求每个词条保留自然语言释义并且只允许使用满足结构约束的新词。程序侧需要做到所有词条必须存在 canonical_properties 和 definition_by_senderReceiver 必须输出 interpretation否则本轮以失败计词汇表最终必须经过人工审核才能进入生产生产环境建议关闭自动生成新词的权限改为人工批准词条这些限制会降低词汇演化的自由度但能保证系统行为可审计适合对稳定性要求较高的生产场景。7.6 从实验到生产的清单在上线前建议逐项核对词表是否持久化并按版本保存每个词条是否都有双端释义是否记录了每轮对话原始日志是否设置了成功率下降告警是否明确了新词写入的权限自动生成还是人工审批是否在模型升级前执行了回归实验是否把词表文件纳入了备份策略这些检查点做完GlossoGen 才真正从一个研究思路变成一个可维护的生产组件。8. 扩展方向从二体协商到复杂多智能体协作8.1 从两个 Agent 扩展到多个 Agent当两个 Agent 的参考游戏跑通后扩展多 Agent 要处理的一个关键问题是“新词共识”。两人世界里A 发明一个词B 理解即可。三人世界里A 发明的词还需要 C 也理解。一种可行方案是投票机制。当某个 Sender 发明新词时把词条和释义广播给所有 Agent超过半数 Agent 表示理解并给出一致的释义后该词条才写入正式词表。这样能避免少数 Agent 的“私人语言”。另一种更工程化的方案是分级词表private 词表单个 Agent 自己维护适合个人状态描述shared 词表多个 Agent 共享适合团队通信global 词表所有 Agent 强制使用适合系统级指令分级调度可以减少全局词表的维护成本同时保留 Agent 之间的个性化表达空间。8.2 从固定属性到复杂任务对象参考游戏里的对象只有颜色、形状、尺寸属性有限且稳定。真实生产任务中对象可以是告警事件、订单、工单、日志片段属性多且可能动态变化。遇到动态属性时词条不能只映射静态属性而应该映射“属性模板”。例如词条crit-db可以表示“数据库相关的高优先级告警”。模板里的具体内容由 Agent 在运行时填充。这种做法更接近大型系统中的专用术语而不是对静态对象的压缩编码。此时词汇条目的 canonical_properties 字段可以改成 canonical_schema用来描述匹配规则{ term: crit-db, canonical_schema: { severity: critical, category: database }, example: 数据库连接池耗尽 }8.3 用强化学习与可微通信代替 API 调用基于 LLM API 的 GlossoGen 是研究“涌现语言”的一种工程实现。它的局限在于Agent 每一步行为都由模型黑盒决策研究者无法在模型内部引入可微的梯度信号。如果转向学术研究可以探索更基础的方法使用可微的通信信道代替 LLM API。经典的参考游戏中Sender 网络输出一个离散符号序列Receiver 网络基于该序列预测目标对象。为了绕过离散符号的不可导问题可以使用 Gumbel-Softmax 估计梯度或使用 REINFORCE 类策略梯度方法。这类方法能够直接观测到词汇嵌入的演化轨迹但工程复杂度会明显上升。对于工程博客读者API 版本已经足够支撑一个完整实验周期为什么还要讲这条路因为理解可微通信的基本原理有助于解释 LLM 版本实验里容易出现的“新词不稳定”“语义漂移”等现象。毕竟底层机制都是信息瓶颈和反馈信号在起作用。8.4 一条可复用的学习路径如果你打算长期做多智能体涌现语言研究建议按这条路径推进完成本文的最小实验两个 Agent、三属性对象、30 轮参考游戏。统计三个指标成功率、压缩率、词表收敛速度对实验现象建立直觉。跑一组自然语言基线对照组量化 GlossoGen 的收益与损失。扩展候选集规模增加属性数量看词表是否能继续收敛。引入第三个 Agent让新词必须经过多 Agent 校验才能写入。切换真实任务数据从固定属性转向动态模板。如有研究需要再转向可微通信模型做深入分析。每一步都先确认实验结果可解释再进入下一步。许多多智能体实验做不下去都是因为跳到太复杂的环境最后无法定位失败原因。GlossoGen 的价值不在于让 Agent 发明奇怪的缩写而在于把“通信成本”和“语义稳定性”这两个问题放进了同一个系统里管理。它给多智能体 LLM 交互提供了一条可复现、可量化、可审计的技术路径。对普通项目来说最值得记住的一点是多方协作中的术语不是靠写死在提示词里获得的而是在充足反馈中逐步稳定下来的。与其让每个 Agent 自由发挥表达方式不如让程序像语言学家一样把每一条新词、每一次误解、每一轮修正都记录下来让词汇表随着协作一起演化。