
通信学中有一个极简而有力的模型——拉斯韦尔(Harold Lasswell)的 5W:Who(谁)→ Says What(说了什么)→ In Which Channel(经何渠道)→ To Whom(对谁说)→ With What Effect(取得何效)。它揭示了一个事实:任何一次可靠的通信,都必须同时约束"主体、内容、通道、对象、效果"五件事;少约束任何一件,通信就会在工程上退化为"碰运气"。
协议,本质上是人类为"机器之间的通信"写下的这份约束。它不是一张接口清单——接口清单只回答"有哪些地址";协议要回答的是谁在什么时机、以什么结构、经哪条通道、把什么交给谁,并如何证明它被正确地交付了。
本文以 ooderAgent 的技能中枢(skillCenter)与其节点之间的 北向协议(Northbound Protocol) 为对象,从 5W 五个维度剖析其定义、架构、原理与边界,并重点论证:在企业级智能体平台中,一份被规范化、强类型化、可演进的北向协议,是"目录权威、统一寻址、生命周期治理、安全统一"得以成立的前提。
前置术语对齐边界约定
术语 | 定义 |
|---|---|
北向 Northbound | 节点 → 中心的控制面(注册/激活/登录/心跳/上报/查询/解析),走 HTTP,前缀 /api/sc/** |
南向 Southbound | 中心 → 节点的数据面(任务下发/事件投递),走 MQTT |
三分层 | 按调用方划分的三组端点:nodeClient(写)/appClient(读)/adminClient(运维) |
四角色 | 声明者 Declarer/执行者 Executor/提供者 Provider/解析者 Resolver |
nodeId | 节点主键,命名 <sysId>-<port>(如 studio-8104) |
accountId / agentAccount | 主体标识:节点级 accountId = nodeId;agent 级 agentAccount = agentId(拟人化主键) |
sessionToken | 登录签发的会话凭据(区别于静态管理令牌) |
resolve | 寻址解析:只回答"这次投给谁",不投递、不执行 |
bootstrap | 自举参数(心跳/上报路径等),activate 与 clone 共用同一构造 |
目录层 / 驻留层 | 目录层 = 逻辑技能(COUNT(DISTINCT SKILL_ID));驻留层 = 可执行副本(COUNT(*)) |
墓碑 tombstone | 软删标记,用于增量同步下发"删除事件" |
热 / 冷数据 | 承载节点在线(经 ENDPOINT 检索)/离线(按登记路径静态直读) |
fail-closed | 校验不通过即拒绝的安全默认 |
边界 | 规定 |
|---|---|
方向 | 北向不承载业务数据;南向不做寻址决策(寻址由中心 resolve 定,南向只按结论投递) |
语义 | /api/sc/** 是"节点向中心报到";/api/agent/** 是"节点对外提供服务",两者方向相反 |
权威 | 目录/地址/节点/会话以中心为准;agent 名册以 Studio roster 为单主,中心仅存只读镜像 |
What · 是什么 控制面契约 HTTP ⊥ MQTT
北向协议 = 节点与技能中心之间的"控制面契约"。它管"台账、目录、寻址、鉴权",不管业务数据的搬运。
它由三部分构成:通道约定(走 HTTP 控制面,与 MQTT 数据面正交)、报文规范(端点、字段、信封、错误码)、序列化契约(类型/时间/可空性/长度/版本)。

图 1 南北向通道与三分层:控制面(HTTP)与数据面(MQTT)正交
层 | 路径前缀 | 调用方 | 职责 | 鉴权 |
|---|---|---|---|---|
nodeClient(写) | /api/sc/node/** | 各节点 | register / activate / login / heartbeat / skills / kb / scene / roster / summary | register 免;其余需登录后的 sessionToken |
appClient(读) | /api/sc/app/** | 各服务消费方 | 查询节点/技能/知识/名册、resolve、clone | 需 token,并经受信端口校验 |
adminClient(运维) | /api/sc/admin/** | 管理统计台 | 统计与运维视图 | X-Cluster-Admin-Token |
SDK 兼容面 | /api/v1/** | 历史 SDK 客户端 | 技能市场式查询(不纳入三分层) | 按 HTTP 方法分流 |
设计原则:三类调用方的信任级别、生命周期、变更节奏完全不同——写端跟随节点版本,读端跟随消费方版本,运维端跟随管理台。按调用方分层,使得任一方的演进都被局部化,不会牵动全局。
register → activate → login → heartbeat(周期) → report(技能/知识/场景/名册/摘要)北向协议不执行任何技能,只回答四个问题:
角色 | 回答 | 权威载体 | 协议落点 |
|---|---|---|---|
声明者 Declarer | "定义的定义"是什么 | 代码(C1)/ 声明式(C2) | POST /node/skills/apply |
执行者 Executor | 谁能执行 | 进程内驻留代码 | 不上行(中心只存驻留摘要) |
提供者 Provider | 谁现在可达 | 节点上报的技能行 | PUT /node/{id}/skills |
解析者 Resolver | 这次投给谁 | 中心 | POST /app/resolve |
若"说了什么"没有统一结构,字段表即为纸糊。北向协议将这一层单独立为序列化契约——一份唯一映射表:
逻辑类型 | JSON | Java(DTO) | MySQL |
|---|---|---|---|
字符串 / 枚举 | string | String | VARCHAR(n) |
长整型(时间戳 / REV) | number(int64) | Long | BIGINT |
整型(计数 / 端口) | number(int32) | Integer | INTEGER |
浮点(负载) | number(double) | Double | DOUBLE |
布尔 | boolean | Boolean | INTEGER(0/1) |
对象 | object | 嵌套 DTO | *_JSON(TEXT) |
数组 | array | List<X> | *_JSON / 关联表 |
四条纪律:时间一律 epoch millis 且以 At 结尾;请求侧"缺失 ≡ null ≡ 不修改"(显式清空用 [] / "");长度上限与 DDL 逐字对齐、越界即拒;X-SC-Protocol 版本头协商。
背景 · 架构
四层 + 横切
部署结构
技能中枢(skillCenter) 是一个独立目录服务:它不执行任何技能,而是作为全平台技能事实的唯一权威,回答四个问题——
服务端遵循 web → service → engine → repository 四层,横切关注点(序列化、鉴权、持久化)下沉为独立组件:
层 | 职责 | 代表组件 |
|---|---|---|
web | 端点、边界校验、DTO 收发 | ScNodeController(写)、ScNodeSkillController、ScReportController、ScAppController(读)、ScAdminController(运维)、SkillCatalogV1Controller(SDK 面) |
service | 用例编排 + 事务边界 | SkillCenterService |
engine | 领域集成:注册/上报/克隆编排、VFS 接缝 | NodeRegistrationEngine、ReportEngine、CloneEngine、VfsBridge / UplinkVfsBridge |
repository | 持久化与幂等 DDL | SkillCenterRepository、SchemaInitializer |
横切·安全 | 分层鉴权、版本协商 | ScTokenAuthFilter、JdsClusterAuthPort(JdsAuthAdapter / SharedTokenJdsAuthPort)、ProtocolVersionFilter |
横切·序列化 | fastjson2 统一编解码 | FastJson2Config、ProtocolJson |

图 2 SkillCenter 服务端分层架构(web → service → engine → repository + 横切)
中心以 MySQL 库 ooder_skillcenter 承载,表族按职责分组:
组 | 表 | 承载 |
|---|---|---|
节点与会话 | SC_NODE · SC_SESSION | 节点台账/负载、登录会话 |
技能 | SC_SKILL · SC_SKILL_CAPABILITY | 技能驻留摘要、能力面 |
名册镜像 | SC_AGENT | Studio roster 的只读镜像(含 LLM 运行面) |
知识三要素 | SC_KB · SC_KB_BINDING · SC_KB_ADDRESS | 目录、绑定关系、地址登记 |
场景与路由 | SC_SCENE · SC_SCENE_PARTICIPANT · SC_SCENE_CAP_BINDING · SC_SCENE_KB_BINDING · SC_NODE_SCENE · SC_NODE_ROUTE | 场景实体、参与方、能力/知识绑定、节点声明 |
摘要与配置 | SC_SUMMARY · SC_CONFIG | 上下文/任务摘要、实例级配置 |
两种口径:目录层(COUNT(DISTINCT SKILL_ID))回答"有哪些技能";驻留层(COUNT(*))等于"技能数 × 在线宿主数"。协议必须写明"每个统计数在回答哪个问题"。
侧 | 组件 | 职责 |
|---|---|---|
共享契约模块 | ooder-cluster-client(net.ooder.cluster.dto) | 协议契约单一真源:全部 DTO + ProtocolJson + SkillCenterNodeReporter |
节点接入 | SkillCenterNodeReporter | register / activate / login / heartbeat(全平台唯一实现) |
中心查询 | SkillCenterHttpClient | 读端消费(skills / resolve / roster / clone …) |
上行器 | SkillCenterUplink / SkillCenterKbUplink / SkillCenterSceneUplink / SkillCenterSummaryUplink | 技能/知识/场景/摘要上行 |
名册镜像 | SkillCenterRosterMirror | Studio roster → 中心 |
维度 | 内容 |
|---|---|
服务 | skill-center,端口 8125,独立 Spring Boot 进程 |
存储 | MySQL ooder_skillcenter(目录/地址/节点/会话);VFS 承载配置文件形态的原始文件 |
实体 | LuceneServer 承载索引与文档(唯一实体),中心不存内容 |
节点侧 | 本地 SQLite 承载运行态,经上报通道入库中心 |
配置注入 | systemd 单元 + drop-in(skill-center.service.d/{db,auth}.conf)注入 DB 连接与鉴权令牌 |
南北向通道 | 北向 HTTP(控制面);南向 MQTT(数据面,ooder/node/{nodeId}/inbox 定向、ooder/event/A2A/{sceneGroupId} 组播) |

图 3 部署结构:控制面(HTTP)/ 数据面(MQTT)与三类存储的职责分工
Why · 核心作用
契约优先
承重结构
把 AI 推向生产的企业会先后撞上三堵墙:模型墙(底座半年一代,调用写死各处)、编排墙(一次请求经过哪些环节说不清)、技能墙(工具/能力/场景散落在不同宿主进程:有哪些技能?谁能执行?这次投给谁?下线了如何保证不再被调到?)。
前两堵墙归平台工程(Platform Engineering)与智能体运行时(Agent Runtime)解决;第三堵墙——技能的统一目录、寻址与治理——正是北向协议要回答的问题域。
当"说了什么"缺乏结构约束时,系统不会报错,而是以若干可分类的静默失效退化。这是"接口"与"协议"的分水岭:
失效模式 | 机理 | 后果 |
|---|---|---|
类型歧义 | 字段无统一类型映射 | 同义字段类型漂移(如端口 Integer vs String) |
静默 null | 以 Map 手写读写、字段名靠字符串 | 拼写错误不编译报错、运行时静默丢失 |
非原子写 | 多语句写未事务化 | 中途失败留"半覆盖" |
并发丢更新 | 读-改-写非原子 | 版本号(REV)回退 |
值域失控 | 枚举未校验 | 任意字符串入库 |
静默空包 | 非法入参不报错 | 调用方误判"无数据" |
边界失守 | 无长度校验 | 依赖 DB 截断/报错 |
语义缺失 | 删除无墓碑 | 无法表达"取消登记" |
上述失效的共同点:它们都不"炸",只"沉默"。这正是 5W 中"Says What"失守后的典型症状。
# | 问题 | 内容 |
|---|---|---|
P-1 | 序列化契约 | 统一数据类型、时间格式、可空性语义、长度上限、版本协商 |
P-2 | 报文规范集中 | 端点、信封、错误码集中为一张总表,杜绝"未纳管端点" |
P-3 | 报文内容完整 | 每个报文字段集必须足以回答其对应的问题("有哪些技能"要答得完整) |
规范协议即以这三问为纲:把"序列化契约"立为地基,把"报文规范"集中成总表,再以 4 张附录(枚举 / 必填 / 长度 / 错误码)收口。
企业级能力 | 协议提供的支撑 |
|---|---|
目录权威 | 统一报文与去重口径,使"有哪些技能"可被确定地回答(目录层 vs 驻留层) |
统一寻址 | resolve 以 ONLINE/ACTIVE 为准,寻址决策与数据投递分离,避免路由散落 |
生命周期治理 | 注册→激活→登录→心跳→上报→注销的状态机 + 墓碑 + 增量游标 |
可替换 / 可演进 | 契约不变则底座/骨架可换;版本协商 + 双写期让"变化"成为契约的一部分 |
安全统一 | 三层客户端令牌策略统一收口(fail-closed),消除"哪条路径没校验"的黑洞 |
边界清晰 | 控制面/数据面正交,中心只登记目录与地址,实体归 LuceneServer,取数直达 |
理论支点 | 出处 | 在协议中的投影 |
|---|---|---|
契约优先(Contract-First) | Design by Contract(Meyer, 1986) | 先定协议与 DTO,再由双端实现反向驱动 |
控制面 / 数据面分离 | SDN / Kubernetes / Service Mesh | 北向(台账/寻址)与南向(投递/执行)正交 |
信息论 | Shannon, 1948 | 版本协商、UTF-8 强制、错误码表 = 对抗"熵增"的冗余 |
幂等与 HTTP 语义 | RFC 7231 | register 按主键 upsert;heartbeat 幂等;DELETE 幂等且连带清理 |
制品供应链治理 | SemVer / 制品库 / OpenSSF SLSA | 版本分离、墓碑、增量游标 |
依赖倒置(DIP) | SOLID | 中心不引重依赖(受信端口化、VFS 接缝),底座可替换 |
Who · 谁
四角色
权威载体
参与方 | 位置 | 说什么 | 权威 |
|---|---|---|---|
节点 / 宿主 | 各进程 | register / activate / login / heartbeat / 上报 | 自身运行态 |
技能中枢 skillCenter | :8125 | 下发身份、bootstrap、目录、寻址结论 | ✅ 目录 / 地址 / 节点 / 会话 |
Studio | 桌面宿主 | 名册上行 | ✅ agent 名册(中心只读镜像) |
消费方 | 各服务 | 查询 / resolve | — |
管理台 | 运维面 | 统计 / 诊断 | 只读消费 |
协议的可治理性建立在"同一职责只有一份实现"之上。全平台节点接入客户端 SkillCenterNodeReporter 收敛为唯一实现(置于轻量契约模块 ooder-cluster-client);集群接入实现同样收敛为单一 ClusterRegistrationClient,各方按配置委派而非各自实现。
原则:若允许多份实现并行演化,协议即名存实亡。收敛,是实现层对协议的履约。
实体 | 权威载体 | 说明 |
|---|---|---|
Node | 中心 SC_NODE | 节点台账 + 负载 |
agentAccount | Studio roster(中心 SC_AGENT 只读镜像) | 单主:Studio 说、中心记 |
Skill | 中心 SC_SKILL | 全量覆盖 或 行级 upsert |
Session | 中心 SC_SESSION | 有效 + 未过期 + ACTIVE |
KnowledgeBase | 中心 SC_KB / SC_KB_ADDRESS | 目录与地址,不是内容 |
命名纪律:nodeId = <sysId>-<port>(禁用 hostname);节点级 accountId = nodeId,agent 级 agentAccount = agentId,二者不同层。
When · 何时
生命周期
版本协商

图 4 节点接入生命周期:register → activate → login → heartbeat → report → offline
register activate(校验口令) heartbeat(存活) TTL 超时
(无) ──────────▶ REGISTERED ──────────▶ ACTIVE ──────────▶ (保持 ACTIVE)
└── heartbeat ────┴──────────────────┴──▶ ONLINE ──▶ OFFLINE硬规则:心跳是存活信号、不是状态断言——只允许 REGISTERED/OFFLINE → ONLINE,不得把 ACTIVE 降级;寻址 resolve 只接受 ONLINE 与 ACTIVE。
同一动作(心跳)在不同状态下含义不同:协议必须显式区分"存活"与"状态变更"。
分层鉴权有一条不可并行的前置:先完成客户端收敛,再开启鉴权。否则旧实现会"双重失效"(既打退役路径、又无 token)。动作的次序本身,就是协议的一部分。
↑ 回到目录
Where · 边界
单前缀
取数直达
归处 | 前缀 | 已退役旧路径 |
|---|---|---|
写 | /api/sc/node/** | /api/sc/nodes/**、/api/sc/skills/**、/api/sc/roster/sync |
读 | /api/sc/app/** | /api/sc/kb、/api/sc/scene、/api/sc/clone |
运维 | /api/sc/admin/** | — |
规范前缀是单前缀、不保留别名——"过渡别名"会让协议永远无法收敛。
上行报地址,下行克内容;两侧同源(同一 SC_* 表),不产生第二套账。
技能层 skillId ──绑定 SC_KB_BINDING(SKILL)──▶ 目录层 kbId
目录层 kbId ──地址登记 SC_KB_ADDRESS(nodeId+endpoint+indexPath)──▶ 地址层
地址层 ──取数直达──▶ 实体层 LuceneServer({base}/index/lucene/{kbId})中心 MySQL · ooder_skillcenter目录(SC_KB)· 地址(SC_KB_ADDRESS)· 节点 · 会话取数直达(不经中心)LuceneServer · 索引与文档(唯一实体){base}/index/lucene/{kbId}VFS配置文件形态登记 vfsPath客户端 SQLite运行态数据经上报入库中心冷热地图:热 → 经 ENDPOINT 在线检索;冷 → 按 INDEX_PATH / VFS_PATH 静态直读
图 5 知识三要素:中心只登记目录与地址,实体归 LuceneServer,取数直达
要素 | 归谁 | 载体 |
|---|---|---|
目录层(kbId) | 中心 | SC_KB |
地址层(在哪) | 中心 | SC_KB_ADDRESS |
实体层(索引/文档) | LuceneServer | 取数直达,不经中心 |
状态 | 判定 | 访问路径 |
|---|---|---|
热 | 承载节点 ONLINE/ACTIVE | 经 ENDPOINT 在线检索 |
冷 | 承载节点 OFFLINE | 按登记的 INDEX_PATH / VFS_PATH 静态直读 |
节点离线后若只有 nodeId,知识即不可达;唯有登记 indexPath,才支撑冷数据静态直读——这就是"上行报地址"的全部意义。
回到拉斯韦尔。一次通信的五问,在协议里被翻译成五条工程纪律:
5W | 传输学之问 | 北向协议之答 |
|---|---|---|
Who | 谁在说 | 四角色 + accountId=nodeId / agentAccount=agentId |
What | 说了什么 | 序列化契约 + 报文总表 + DTO 单一真源 |
Channel | 经何渠道 | 北向 HTTP 控制面 ⊥ 南向 MQTT 数据面 |
Whom | 对谁说 | 中心权威(目录)+ 解析者 resolve |
Effect | 取得何效 | 生命周期状态机 + 治理 + 可演进的契约 |
北向协议最终交付的,不是端点数量,而是三重企业级能力:
从"功能落地"到"秩序落地",中间隔着的,正是一份被认真对待的协议。
维度 | 理论锚点 | 出处 |
|---|---|---|
What | 传播 5W 模型 | Lasswell, The Structure and Function of Communication in Society, 1948 |
What | 通信的数学理论 | Shannon, A Mathematical Theory of Communication, 1948 |
Why | 契约优先设计 | Contract-First / Design by Contract(Meyer, 1986) |
Why | 控制面 / 数据面分离 | SDN / Kubernetes / Service Mesh |
Who | 依赖倒置原则 | Martin, DIP(SOLID) |
When | 并发一致性与乐观并发 | 原子自增 / REV 版本游标 |
Where | 制品库与供应链分级 | SemVer / Nexus·Artifactory / OpenSSF SLSA |
Where | 存储职责分离 | 目录(元数据) ⊥ 实体(索引/文档) |
How | 边界类型化与不可变契约 | DTO + @JSONField 钉名 + validate() |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。