资讯动态

Semantic Router 多协议适配层(Protocol-Neutral Codec Matrix)实现解析:从 Wire JSON 到中立请求的一次解码

发布时间:2026/10/14 17:37:22 来源:尧图企业网站定制
后端API网关模型推理服务AI Agent【免费下载链接】semantic-routerAn open, programmable decision layer for models and compute.项目地址https://gitcode.com/gh_mirrors/sem/semantic-router点击查看免费下载Semantic Router仓库根目录 README.md是一个开放、可编程的模型与计算决策层。其数据面Router data plane面临一个核心问题客户端可能以 OpenAI Chat Completions、OpenAI Responses、Anthropic Messages 等多种线协议wire format发来请求而最终选中的后端模型可能只原生支持其中一种协议响应又必须回到客户端原始协议。本文围绕仓库中已落地status: Implemented的提案 website/docs/proposals/multi-protocol-adaptor.md 展开系统讲解协议中立编解码矩阵的设计一次解码进入中立语义、一次编码离开中立语义、codec 与路由策略严格分层以及支撑它的能力声明、翻译规则、流式引擎、usage 结算与 18 格 E2E 验证矩阵。读完本文你将掌握该数据面如何在不增加路由策略协议分支的前提下为任意客户端格式与任意后端协议提供全配对翻译。一、总体设计一次解码、一次编码、职责三分提案给出的数据流如下mermaid 图来自 multi-protocol-adaptor.md其核心结论Outcome可以概括为三点一次解码、一次编码无论客户端或后端采用何种线格式Router 只对一份协议中立protocol-neutral语义请求做路由决策。Wire JSON 在入口ingress被解码一次在 provider 边界被编码一次响应体与流式事件沿反向路径解码/编码后返回客户端。原始 wire 对象从不进入语义路由也就不会带着未经验证的字段穿透策略层。职责三分传输transport拥有监听器、连接、重试与转发路由核心routing core拥有语义模型选择与请求/响应策略codec只负责把 wire 契约映射到中立类型不拥有任何一方的生命周期。Standalone 是默认传输--gateway extproc则把同一套路由核心挂接到 Envoy。注册表不可变codec 注册表registry在构造后不可变新增一种线格式只需新增一对 buffered/streaming codec、声明能力并补齐矩阵测试路由策略不会因为新增 codec 而长出协议分支。仓库实现中这一分工落在两个 Go 包上pkg/llmprotocol中立语义契约与pkg/protocolcodec线协议编解码引擎详见 src/semantic-router/pkg/protocolcodec/engine.go 与 src/semantic-router/pkg/protocolcodec/registry.go。二、中立契约Neutral contractpkg/llmprotocol 是什么pkg/llmprotocol是 codec 与 Router 策略共享的唯一语义契约src/semantic-router/pkg/llmprotocol/types.go 包注释明确写道Wire JSON belongs to codecs; it must not leak into this package.。它表示有序的 instructions、messages 与多模态内容块Content按Kind闭合区分text、image、audio、video、file、tool_call、tool_result、reasoning以及独立的generated_image工具定义、工具调用、工具结果、托管式图像生成控制hosted image-generation与 tool choiceauto/none/required/named/image_generation采样与结构化输出约束Sampling中的 temperature、top_p、top_k、min_p、repetition_penalty、max_output_tokens、seed、penalties、stop 序列OutputFormat的text/json_object/json_schemareasoning 控制与 reasoning 内容模式enabled/disabled/adaptive、effort、budget、display以及区分完整文本与摘要的ReasoningScope响应备选Alternatives与停止原因StopReason共 9 种枚举provider 请求身份与类型化传输错误TransportError与ProtocolError带 provenance 的 token usage——标准输入、缓存读、缓存写、reasoning 输出、其他输出与合计受信任的传输元数据TrustedMetadatanamespace/actor/subject/session/agent/task/turn/correlation 标识codec 不能从客户端输入填充这些字段。值得强调的边界原始 wire 对象不进入语义路由。Envelope虽然可以保留有界的同格式表示细节src/semantic-router/pkg/llmprotocol/policy.go 定义其为 bounded、ephemeral 的 wire fidelity 状态且注释明确禁止它被序列化进日志、快照、YAML 或 usage 记录但它不是路由状态不能用于绕过校验。从实现看中立契约对缺省值显式化也很讲究applyRequestSemanticDefaults会在解码后把有工具就默认tool_choiceauto这一 provider 文档化缺省显式写入中立请求engine.go从而避免路由插件因客户端是否显式写出缺省而行为不同。三、Codec 契约与不可变注册表3.1 每个 codec 要做什么每个注册的 codec 都是无状态、可并发安全的声明自己的线格式与能力并实现四个缓冲操作文档列出的四项把请求解码为中立请求DecodeRequest把中立请求编码给后端EncodeRequest把响应解码为中立响应DecodeResponse把中立响应编码给客户端EncodeResponse。在 registry.go 中这四个操作对应MessageCodec接口此外还有独立的TransportErrorCodecDecodeTransportError/EncodeTransportError。传输错误使用独立的类型化契约因为 HTTP 错误不是失败的模型响应资源——这一点在 pkg/llmprotocol/errors.go 中有明确注释TransportError代表非 2xx API 响应而Response.Error是模型生成结果例如 Responses 资源 status 为failed。流式 codec 创建请求作用域request-scoped的 decoder/encoder二者交换中立事件stream.go 的StreamDecoder.Push/Finalize与StreamEncoder.Push/Finalize注册表中不存任何可变流状态。3.2 注册表构建不可变、强制无状态Registry构造后不可变Resolve从不暴露底层 mapregistry.go。NewRegistry会强制校验拒绝 nil codec拒绝Stateless() false的 codec因为一个注册 codec 可能被并发调用格式标识必须匹配^[a-z][a-z0-9._-]{0,127}$每个格式必须实现流式拒绝重复注册拒绝空注册表。内建注册表由四种 codec 组成NewRegistry(OpenAIChatCodec{}, OpenAIResponsesCodec{}, AnthropicMessagesCodec{}, ImagesCodec{})见 NewBuiltinRegistry。其中openai.images.v1是 sink 方言Responses 的托管图像生成请求会被重编码到/v1/images/generations生成的图像再解码回中立GeneratedImage输出项types.go。3.3 能力声明编码前显式检查能力capability以不可变位集bitset形式声明src/semantic-router/pkg/llmprotocol/capabilities.go覆盖文本、图像/音频/视频/文件输入输出、工具与并行工具、reasoning含 adaptive/signature/display/budget、结构化输出JSON object / strict schema / strict tool schema、流式、缓存记账、权威 usage、多候选、采样控制top_k/min_p/seed/penalties/stop、缓存指令与隔离、对话状态、自定义工具、matched stop sequence、图像生成、文本 verbosity 等 40 项。RequiredCapabilities(request)会从请求中推导所需能力集例如请求含 strict tool schema、自定义工具、reasoning 或图像生成时分别置位而能力检查发生在编码之前RequireCapabilities在EncodeRequest/EncodeResponse/TranslateRequest中均在 encode 前调用见 engine.go使不支持的 tools、媒体、多候选、严格 schema、reasoning 或流式行为变得显式、可测试。四、支持矩阵与翻译规则4.1 支持矩阵Wire formatBuffered requestBuffered responseStreamingToolsImagesStructured outputUsageOpenAI Chat CompletionsDecode and encodeDecode and encodeSSE decode and encodeYesInputJSON object and schemaAuthoritative when presentOpenAI ResponsesDecode and encodeDecode and encodeEvent decode and encodeYesInput plus hosted image-generation lifecycleJSON schemaAuthoritative when presentAnthropic MessagesDecode and encodeDecode and encodeEvent decode and encodeYesInputSupported schema subsetAuthoritative when present完整的两两配对矩阵pairwise matrix——请求、响应、传输错误、流式——都被测试覆盖matrix_test.go 对三种内建格式两两执行TranslateRequest后再用目标格式解码回验。请求可以以任意受支持的客户端格式进入以所选 provider 模型声明的格式离开响应则回到客户端原始格式。4.2 翻译是语义翻译不是字段复制翻译规则Translation rules是文档中的关键约定具有等效中立含义的字段被保留目标不支持的字段以类型化unsupported_feature错误失败未知字段只允许在未修改的同格式same-format缓冲往返中存活跨格式翻译与语义变更semantic mutation会拒绝未知字段而不是静默丢弃请求与响应的Generation在语义变更后递增诊断是有界的通过既有 Router 可观测性契约返回codec 从不抓取 URL、解析文件、认证调用者或调用 provider。引擎层通过translationDecodePolicy落实这一规则engine.go只有当UnknownFields UnknownPreserveSameFormat且源格式等于目标格式且未被变更时才允许保留未知字段一旦跨格式或存在 mutation立即切换到UnknownRejectSourceDisabled。DecodeRequestForMutation则是 Router 入口使用的严格契约——模型选择与策略插件必然会在解码后修改请求因此入口绝不允许接受会被静默擦除的不透明字段。TranslateTransportError特意不走DecodeResponse失败的模型 Response 资源与 API 传输错误是两种 wire 对象engine.go。类型化错误类别在 pkg/llmprotocol/errors.go 中枚举invalid_request、authentication、permission、not_found、conflict、unsupported_feature、rate_limited、upstream_unavailable、upstream_timeout、internal。ProtocolError支持Unwrap以透传Cause。嵌套字段的拒绝行为由测试固化例如 official_schema_contract_test.goChat message name、prompt cache breakpoint、Responses defer_loading/reasoning mode、Anthropic eager tool 等嵌套字段均须返回ErrorUnsupportedFeature。五、流式引擎事件先解码、终结唯一、usage 结算5.1 中立事件与请求作用域每个 provider 事件在进入策略或客户端编码前先被解码为中立事件stream.go 的EventTyperesponse.started、output.item.started、output.text.delta、output.reasoning.delta、tool.call.delta、image_generation.progress、output.item.completed、usage.updated、response.completed、response.failed、provider.opaque。事件带序号Sequence、内容索引ContentIndex独立于 ItemIndex与失败作用域FailureTransport/FailureResponse。分片网络帧由 wire decoder 缓冲——Router 策略从不解析不完整的 JSON 或 SSE 记录。StreamEngineengine.go强制终结状态唯一response.completed或response.failed出现后流即 terminal再次 Push 返回stream_terminal冲突错误成功终结延迟发布deferSuccessfulTerminalprovider 的语义成功事件被暂存直到 HTTP body 干净地到达流尾——否则终结帧 后续分片中的畸形残片可能在尾部字节被校验前就发布成功engine.go有界诊断诊断数量被Limits.Diagnostics截断超限时写入diagnostic_truncated最终 usage 结算流终止时最终 provider usage 替换中间估计值。流式场景没有同格式字节级重放路径因此strictStreamPolicy会拒绝未知字段——接受未知字段就意味着静默丢弃 provider 语义engine.go。5.2 图像生成的生命周期状态机托管式图像生成复用同一流引擎。输出项从in_progress开始进度可经generating与有序部分图像推进最终恰好一次以completed或failed结束。以下情况会在客户端成功终结发布前失败向后转换backward transitions、稀疏的部分图像索引sparse partial indexes、冲突的终结状态、以及进度事件上携带结果数据result data on a progress event。图像生成测试与 fixture 覆盖了每个已发布选项、null结果与空 payload 的区分、有序进度、连续的部分图像索引、终结成功/失败、畸形 base64 与目标能力拒绝见 src/semantic-router/pkg/protocolcodec/image_generation_test.go。5.3 Router 自产响应不走 provider 形状Router 自产响应如拒绝、诊断、合成回复通过NewEventStreamEncoder直接用中立事件编码到公开线格式engine.go不构造中间 provider 形状的流。取消与背压backpressure跟随所选传输的请求生命周期。编码器同样受能力检查约束RequiredEventCapabilities如 target 无法表达 matched stop sequence 或 custom tool事件会以类型化错误失败。六、Usage 与成本provenance 优先pricing 是部署元数据6.1 Token 计数保留 provenanceToken 计数是带指针的Value *int64使缺省与权威零值可区分Provenance区分authoritative/derived/estimated/unknowntypes.go。这样记账accounting可以区分权威 provider usage 与派生、估算或不可用的值。Usage结构体覆盖标准输入、缓存读、缓存写、reasoning 输出、其他输出、输入合计、输出合计与总计。6.2 定价留在 provider model 上定价是部署元数据挂在每个 provider model 上而routing.modelCards只描述语义能力不拥有连接或定价数据src/semantic-router/pkg/catalog/types.go 的Pricing结构与此对应providers: models: - name: local/fast pricing: currency: USD prompt_per_1m: 0.20 cached_input_per_1m: 0.02 cache_write_per_1m: 0.25 completion_per_1m: 0.80规则货币可选省略时按 USD 记账配置的费率必须有限且非负显式 0 表示免费费率。语义能力modelCards与连接/定价数据的分层使得能力声明可复用于路由决策而定价只服务于成本结算。七、安全边界可信元数据只能由传输填充客户端可控的 header 与 body 元数据一律不可信。只有经过认证的传输边界才能填充 trusted identity / session / task / correlation 字段TrustedMetadata且必须在传输层确立这些字段之后codec 不能把 wire 元数据提升为 trusted 元数据——中立请求的Trusted字段types.go注释明确populated by the Router after transport authentication. Codecs never populate trusted fields from client headers公共推理监听器与管理监听器保持分离codec 层本身不认证调用者、不代理请求、不管理模型 worker——这些分别是前端与运行时的职责。八、验证契约schema 钉扎 fixture 语料 18 格 E2E8.1 Schema 契约钉扎到上游修订版模式契约钉扎到已发布的上游修订版OpenAI OpenAPI690521b1753dce0c6d6b275f583d22537679cff9、Anthropic SDKd19dea9ed85bbb5fdb2d6f20fb6f903920ed23fa。测试将每个顶层字段、嵌套对象字段与已发布的 union 判别符闭合到显式的语义semantic、仅传输transport-only、扩展extension或不支持unsupported处置类别。顶层请求/响应清单对已发布的 OpenAI OpenAPI schema 与生成的 Anthropic Messages 类型封闭——新发布的字段不能穿过未类型化的 JSON 桶它必须映射到中立契约、作为显式不支持特性失败、或被记录为有意从客户端表示中省略的有界 provider 元数据相关测试见 official_schema_contract_test.go 与 official_schema_inventory_test.go。8.2 E2E provider 模拟器与 Go 一致性测试E2E provider 模拟器属于同一契约其原生 Chat、Responses、Messages 边界使用修订钉扎的闭合清单对未知顶层字段以 provider 原生错误拒绝并在无未类型化规范化步骤的情况下保留嵌套 wire 对象。Go 一致性测试 provider_simulator_contract_test.go 把模拟器清单schema_contract.json含schema_revision、官方/扩展请求响应字段、usage 字段与 required 字段与 codec 的请求/响应/usage wire 清单直接比对。模拟器测试重放每个已发布顶层请求字段codec goldens 闭合嵌套字段与 union 判别符——这防止宽松的 mock 隐藏 ExtProc 丢弃或发明的字段。8.3 人类可读 fixture 约定fixture 采用稳定的输入/输出命名约定NNN-{client-protocol}-{case}-in.json NNN-{client-protocol}-{case}-{backend-protocol}-out.json每个请求、响应、传输错误、流、能力边界与类型化拒绝输入对每种内建目标协议恰好有一个期望输出。流 fixture 保留精确的 SSE transcript并逐字节重放一次以证明传输 chunk 边界不改变语义golden 测试见 golden_matrix_test.go。语料覆盖畸形与截断 JSON、重复字段、非法 union 与枚举、有序多模态内容、工具与工具结果、结构化输出、reasoning、usage、取消、超时、不完整流、流中途失败、身份变更、序列违规、托管图像生成与资源限制。8.4 部署级 18 格矩阵部署级覆盖是必需的 18 格矩阵3 client protocols x 3 native backend protocols x 2 modes 18 E2E cells每个格穿越 Envoy 与 ExtProc验证客户端原生缓冲信封或 SSE 生命周期要求翻译后的输出带确定性后端标记并拒绝泄漏的后端 wire 形状。另有附加 E2E 契约覆盖每个后端协议的结构化输出、缓冲 provider 错误、工具调用延续、不完整流与部分输出后的错误。--gateway extproc与 standalone 两种模式的差异与选用见 website/docs/installation/k8s/gateways.mdHelm chart 中gateway.mode: extproc时 Router 在 50051 端口服务 ext_proc gRPC。九、扩展清单新增一种线格式要做什么一个新 codec 只有在提供以下全部内容时才算完成稳定的 wire-format 标识符匹配^[a-z][a-z0-9._-]{0,127}$显式的能力声明capability declaration严格的 buffered 请求、响应与传输错误 codec请求作用域的流解码与编码request-scoped stream decoding and encoding畸形输入与不支持特性的测试对每种内建格式的两两矩阵覆盖权威 usage 与终结事件测试ExtProc 回归覆盖证明路由行为不变。由于注册表不可变、能力检查前置、未知字段策略严格扩展者不需要触碰任何路由策略代码——这正是该设计策略无协议分支的工程回报。十、关键文件导航提案原文website/docs/proposals/multi-protocol-adaptor.md中立语义契约src/semantic-router/pkg/llmprotocol/types.go、src/semantic-router/pkg/llmprotocol/errors.go、src/semantic-router/pkg/llmprotocol/capabilities.go、src/semantic-router/pkg/llmprotocol/policy.go、src/semantic-router/pkg/llmprotocol/stream.go编解码引擎与注册表src/semantic-router/pkg/protocolcodec/engine.go、src/semantic-router/pkg/protocolcodec/registry.go各线格式 codeccodec_openai_chat.go、codec_openai_responses.go、codec_anthropic_messages.go、images_codec.go均位于 src/semantic-router/pkg/protocolcodec矩阵与契约测试matrix_test.go、official_schema_contract_test.go、provider_simulator_contract_test.go、golden_matrix_test.go定价元数据src/semantic-router/pkg/catalog/types.go相关文档Router API、Semantic Router 系统总览、Kubernetes 网关部署选项赞分享后端API网关模型推理服务AI Agent【免费下载链接】semantic-routerAn open, programmable decision layer for models and compute.项目地址https://gitcode.com/gh_mirrors/sem/semantic-router点击查看免费下载相关推荐Operit OpenCode Provider 路由隔离多协议请求边界与专用适配实现解析Operit OpenCode Provider 路由隔离多协议请求边界与专用适配实现解析 导读 本文基于 Operit 仓库的 docs/TODO/openAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Pixie 的 PostgreSQL Wire Protocol 解析器从 v3 协议消息到 pgsql_events 观测数据Pixie 的 PostgreSQL Wire Protocol 解析器从 v3 协议消息到 pgsql_events 观测数据 本文深入剖析开源可观测性平台可观测性云原生btcd wire 包深度解析用 Go 实现比特币点对点线上协议Wire Protocolbtcd wire 包深度解析用 Go 实现比特币点对点线上协议Wire Protocol btcd 的 wire 子包仓库路径 wire/ https区块链创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑