[ISSUE #14804] Add Agent management and RAD specifications (#15545)

Assisted-by: Claude Code
This commit is contained in:
杨翊 SionYang
2026-07-21 16:37:55 +08:00
committed by GitHub
parent c3232baa95
commit 15d5154685
51 changed files with 8179 additions and 246 deletions
+12
View File
@@ -210,6 +210,10 @@ English:
[AI Resource Model Spec](./specs/en/ai/ai-resource-model-spec.md),
[AI Resource Lifecycle Spec](./specs/en/ai/ai-resource-lifecycle-spec.md),
[AI Registry Adaptor Spec](./specs/en/ai/ai-registry-adaptor-spec.md),
[Agent Management Spec](./specs/en/ai/agent-management-spec.md),
[Remote Agent Discovery Protocol Spec](./specs/en/ai/rad-protocol-spec.md),
[Agent API Spec](./specs/en/ai/agent-api-spec.md),
[Agent Storage Spec](./specs/en/ai/agent-storage-spec.md),
[MCP Server Spec](./specs/en/ai/mcp-server-spec.md),
[A2A Agent Spec](./specs/en/ai/a2a-agent-spec.md),
[Prompt Spec](./specs/en/ai/prompt-spec.md),
@@ -231,6 +235,8 @@ English:
- Testing model:
[API Integration Test Spec](./specs/en/testing/api-integration-test-spec.md),
[Java SDK Integration Test Spec](./specs/en/testing/java-sdk-integration-test-spec.md)
- Shared schemas:
[JSON Schema Index](./specs/schemas/README.md)
Simplified Chinese:
@@ -287,6 +293,10 @@ Simplified Chinese:
[AI 资源模型规范](./specs/zh-cn/ai/ai-resource-model-spec.md)
[AI 资源生命周期规范](./specs/zh-cn/ai/ai-resource-lifecycle-spec.md)
[AI Registry 适配器规范](./specs/zh-cn/ai/ai-registry-adaptor-spec.md)
[Agent 管理规范](./specs/zh-cn/ai/agent-management-spec.md)
[Remote Agent Discovery 协议规范](./specs/zh-cn/ai/rad-protocol-spec.md)
[Agent API 规范](./specs/zh-cn/ai/agent-api-spec.md)
[Agent 存储规范](./specs/zh-cn/ai/agent-storage-spec.md)
[MCP Server 规范](./specs/zh-cn/ai/mcp-server-spec.md)
[A2A Agent 规范](./specs/zh-cn/ai/a2a-agent-spec.md)
[Prompt 规范](./specs/zh-cn/ai/prompt-spec.md)
@@ -308,6 +318,8 @@ Simplified Chinese:
- 测试模型:
[API 集成测试规范](./specs/zh-cn/testing/api-integration-test-spec.md)
[Java SDK 集成测试规范](./specs/zh-cn/testing/java-sdk-integration-test-spec.md)
- 共享 Schema
[JSON Schema 索引](./specs/schemas/README.md)
This section is a quick implementation checklist for agents. If it conflicts
with the specs, follow the specs and update this checklist.
+2
View File
@@ -370,6 +370,8 @@
<exclude>**/predefined-classes-config.json</exclude>
<exclude>**/serialization-config.json</exclude>
<exclude>**/filter-config.json</exclude>
<!-- Strict JSON Schema cannot carry a source-code comment header. -->
<exclude>specs/schemas/**/*.schema.json</exclude>
<exclude>**/disk_cache_test/**</exclude>
<exclude>**/failover_test/**</exclude>
<exclude>lefthook.yml</exclude>
+6
View File
@@ -47,6 +47,7 @@ Nacos design
-> Extension model
-> Security model
-> Testing model
-> Shared JSON Schemas
```
规范层次:
@@ -75,9 +76,14 @@ Nacos 顶层设计
-> 扩展模型
-> 安全模型
-> 测试模型
-> 共享 JSON Schema
```
Available languages:
- [English](en/README.md)
- [简体中文](zh-cn/README.md)
Shared machine-readable contracts / 共享的机器可读契约:
- [JSON Schema Index / JSON Schema 索引](schemas/README.md)
+4
View File
@@ -75,6 +75,10 @@ extension mechanisms, and cross-cutting security rules.
- [API Integration Test Spec](testing/api-integration-test-spec.md)
- [Java SDK Integration Test Spec](testing/java-sdk-integration-test-spec.md)
## Shared Schemas
- [JSON Schema Index](../schemas/README.md)
Agent guidance files such as [AGENTS.md](../../AGENTS.md) should summarize these
specs for local execution. The specs remain the rule source when API guidance is
used by humans, AI agents, templates, or validation tools.
+9 -1
View File
@@ -30,10 +30,18 @@ behavior through the [HTTP API](../http-api/api-spec.md),
- [AI Resource Lifecycle Spec](ai-resource-lifecycle-spec.md)
- [AI Registry Adaptor Spec](ai-registry-adaptor-spec.md)
## Agent Registry
- [Agent Management Spec](agent-management-spec.md)
- [Remote Agent Discovery Protocol Spec](rad-protocol-spec.md)
- [Agent API Spec](agent-api-spec.md)
- [Agent Storage Spec](agent-storage-spec.md)
- [A2A Agent Binding And Compatibility Spec](a2a-agent-spec.md)
- [Shared JSON Schema Index](../../schemas/README.md)
## Resource Types
- [MCP Server Spec](mcp-server-spec.md)
- [A2A Agent Spec](a2a-agent-spec.md)
- [Prompt Spec](prompt-spec.md)
- [Skill Spec](skill-spec.md)
- [AgentSpec Spec](agentspec-spec.md)
+129 -67
View File
@@ -14,95 +14,157 @@
limitations under the License.
-->
# A2A Agent Spec
# A2A Agent Binding And Compatibility Spec
This document defines the AI Registry contract for A2A AgentCard resources.
| Item | Value |
| --- | --- |
| Status | Experimental target compatibility contract |
| Activation | Canonical Agent write-path cutover |
## 1. Identity
This document defines A2A as a protocol binding of the canonical Nacos Agent
resource and specifies the compatibility facade for historical AgentCard APIs.
The canonical model is defined by the
[Agent Management Spec](agent-management-spec.md); remote discovery follows the
[RAD Protocol Spec](rad-protocol-spec.md).
Canonical A2A Agent identity is:
## 1. Activation, Current Baseline, And Identity
Before feature activation, the current Nacos runtime may continue to persist
`type=a2a` resources and use the legacy Config and Naming layouts. That
implementation remains conforming to the current A2A baseline; this target
spec does not claim that it has already migrated.
Sections 2 through 7 become normative for new requests only after the
canonical Agent write path is activated. Activation and mixed-version rollout
must be explicit: before cutover, the legacy model remains the fact source;
after cutover, new writes use the canonical Agent model and the legacy surface
is the compatibility facade defined here.
A2A is not a top-level AI resource type. The canonical identity is:
```text
namespaceId -> a2a -> agentName
namespaceId -> agent -> agentName -> version -> protocol=a2a
```
`agentName` is the public resource name. Current storage may encode the agent
name before writing Config data. Encoding is an implementation detail and must
not change public identity.
The historical `namespaceId -> a2a -> agentName` identity is compatibility
only. All legacy requests are adapted to `type=agent`; no new `a2a` metadata or
version store may be created after the canonical write path is enabled.
## 2. Domain Model
## 2. A2A Call Interface
An A2A Agent resource contains AgentCard metadata and versioned AgentCard
details. It may include:
An A2A binding is one `AgentCallInterface` with:
- agent name, description, provider, capabilities, skills, authentication, and
protocol information;
- registration type;
- version list and latest published version;
- service-style endpoints represented by agent interfaces.
| Agent field | A2A mapping |
| --- | --- |
| `protocol` | Canonical token `a2a`. |
| `protocolVersion` | Normalized A2A protocol version used for fast filtering. |
| `descriptorMediaType` | AgentCard JSON media type. |
| `nativeDescriptor` | Complete normalized AgentCard, without losing supported upstream fields. |
| `declaredEndpoints` | Derived from root URL and supported/additional interfaces. |
| `endpointSourceOrder` | Derived from the compatibility registration type. |
The current AgentCard model follows the Google A2A 1.0.0 protocol fields. Nacos
keeps several 0.x-compatible fields, such as root-level `url`,
`protocolVersion`, `preferredTransport`, `additionalInterfaces`, and
`supportsAuthenticatedExtendedCard`, for compatibility with existing clients.
These legacy fields are compatibility inputs only and may be removed after the
1.0.0 model becomes the only supported contract.
The current descriptor baseline supports A2A 1.0 fields and the existing 0.x
compatibility fields. Adapter normalization must not replace the stored native
descriptor with a synthetic generic Agent object.
When no version is specified, runtime query resolves the latest version.
`registrationType=URL` maps to `[DECLARED,RUNTIME]` and
`registrationType=SERVICE` maps to `[RUNTIME,DECLARED]`. Registration type is a
legacy projection field, not part of Agent identity or the new APIs.
## 3. Endpoint Model
## 3. Legacy Definition Writes
A2A endpoints may be registered by runtime clients and represented through a
Naming service under the A2A endpoint group.
Legacy AgentCard release and Admin update requests validate the same AgentName
and version syntax as canonical Agent APIs. A successful write creates or uses
the Agent metadata row, stores one A2A call interface, and takes the target
version directly online without introducing a separate legacy draft pipeline.
Endpoint resolution rules:
Rules:
- endpoints are attached to a specific agent version;
- endpoint metadata should include transport, protocol binding, protocol
version, path, query, TLS support, and tenant data when available;
- when multiple compatible endpoints exist, the current implementation chooses
one randomly; this is not a stable policy contract and should be refined by a
future endpoint-selection spec.
- the first online version always becomes `latest`;
- for a new later version, `setAsLatest=true` moves `latest` and `false`
preserves the current valid pointer;
- a standard Agent publish or online operation always moves `latest`;
- deleting or taking the current latest offline selects the greatest remaining
online Agent version, or removes `latest` when none remains;
- releasing an already-online exact version through the Client SDK is a
successful no-op;
- an existing exact version with different canonical content is a conflict;
0.1.0 does not provide same-version force overwrite;
- deleting a missing Agent or version is a successful no-op only where the
historical API already promises that behavior.
Naming is endpoint infrastructure. The A2A Agent remains an AI Registry
resource.
Direct-online, conflict rejection, deletion, and latest changes must emit audit
records without logging the complete descriptor or sensitive endpoint metadata.
## 4. API And SDK Behavior
## 4. Legacy Runtime Endpoint Writes
- Admin APIs may register, query, update, delete, list, and operate A2A
AgentCard versions.
- Client APIs and SDKs may query AgentCards, release AgentCards where supported,
register/deregister endpoints, batch replace endpoints owned by the current
client, and subscribe to AgentCard changes.
- gRPC payloads include AgentCard query/release and endpoint registration
requests as defined by the [gRPC API Spec](../grpc-api/api-spec.md).
Legacy single and batch endpoint operations retain replacement semantics under
this compatibility scope:
## 5. Current Compatibility Storage
```text
publisher + namespaceId + agentName + exactVersion + protocol=a2a
```
Current A2A implementation stores AgentCard metadata and versions through
Config-shaped records and uses Naming services for endpoints. This is
compatibility storage. The canonical model should be
`ai_resource + ai_resource_version`.
Single register replaces the scope with one endpoint; batch register replaces
it with the submitted set. The adapter maps the exact version to
`runtimeVersion=version` and `versionRange=[version]` and writes through the
canonical Runtime Endpoint Registry.
## 6. Pending Migration Issues
The Registry stores separate publisher contribution groups for different exact
versions even when they use the same public endpoint natural key. Legacy
deregister removes only the requested exact-version contribution group. This
internal compatibility operation is intentionally narrower than RAD
`Deregister`, which removes the current publisher's bindings for the submitted
natural endpoint keys.
- Migrate AgentCard metadata and version rows to the standard AI resource
model.
- Define migration from encoded Config data ids to resourceName identity.
- Define endpoint ownership, connection cleanup, and deterministic endpoint
selection.
- Align latest-version behavior with the shared label model.
- Introduce a protocol-neutral abstraction above A2A, such as `RemoteAgent` or
`AgentService`. Nacos is a unified registry and should not bind its AI
registry model to one specific agent protocol unless that protocol has become
a clear community standard. A2A should remain one supported protocol binding
under the higher-level remote-agent resource model.
Endpoint publication may precede Agent or Version creation. It never creates an
Agent definition implicitly.
## 7. Evolution Note
## 5. Legacy Query Projection
A2A protocol versions, AgentCard fields, security schemes, and endpoint
representation may evolve rapidly. The current baseline is A2A 1.0.0, while
0.x-compatible fields are retained only to avoid breaking existing clients. This
spec may need incompatible revisions when the upstream A2A model changes or when
Nacos introduces a protocol-neutral remote-agent abstraction. Such changes must
define migration and compatibility behavior.
The compatibility query first selects an online version containing a valid
`protocol=a2a` call interface. An explicit version is case-sensitive; otherwise
the Agent `latest` pointer is used. Client runtime reads also require the Agent
to be enabled and visible.
Projection rules:
| Query mode | Result |
| --- | --- |
| `URL` | Return the stored native AgentCard and its declared interfaces. |
| `SERVICE` with matching Runtime Endpoints | Project the deterministic Runtime Endpoint set into AgentCard interfaces and root URL. |
| `SERVICE` with no matching Runtime Endpoint | Fall back to the stored declared AgentCard. |
Runtime projection excludes `enabled=false` endpoints and retains
`healthy=false` endpoints because the legacy DTO has no health field. The
projection order is stable: priority first, then the endpoint natural key. New
RAD-only fields such as source revision, health, priority, weight, and general
metadata are not added to legacy DTOs.
Legacy list and version-list APIs read Agent metadata plus online A2A versions.
Legacy subscription events pass through the same projection as GET. A legacy
subscription may remain registered when the initial target is absent; this is a
compatibility behavior and is not the RAD Watch contract.
## 6. Compatibility Surfaces
| Surface | State and window |
| --- | --- |
| Java `A2aService` and legacy A2A gRPC payloads | Compatibility-only; no removal version is set. |
| Admin `/v3/admin/ai/a2a` and `A2aMaintainerService` | Supported through the 4.0.x compatibility window. |
| Console `/v3/console/ai/a2a` | Supported through the 3.4.x compatibility window. |
Legacy paths, payload type names, DTOs, ability keys, authorization identity,
and response wrappers remain stable during their windows. New Agent/RAD APIs
must not expose `registrationType`, `setAsLatest`, or AgentCard-specific list
wrappers.
Historical data migration, mixed-version dual read/write, source switching,
rollback, and cleanup are rolling-upgrade concerns and are not defined by this
API compatibility spec.
## 7. Evolution
Changes in upstream AgentCard fields or A2A protocol versions are handled by the
A2A adapter and versioned Agent call interface. They must not redefine the
canonical Agent identity or the protocol-neutral RAD result.
+384
View File
@@ -0,0 +1,384 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Agent API Spec
| Item | Value |
|---|---|
| Status | Experimental target contract; not an implemented-surface inventory |
| Target line | Nacos 3.3 |
| Scope | Client HTTP/gRPC, Admin HTTP/Maintainer SDK, and Console HTTP bindings for Agent and RAD |
This document binds the [Agent Management Spec](agent-management-spec.md) and
the [RAD Protocol Spec](rad-protocol-spec.md) to Nacos APIs. It is normative
for an implementation that advertises the new Agent/RAD abilities. Existing
A2A APIs remain governed by the [A2A Agent Spec](a2a-agent-spec.md) until the
new binding is implemented and negotiated.
## 1. API Families And Common Rules
| Surface | Transport | Primary caller | Responsibility |
|---|---|---|---|
| Client | HTTP and gRPC | Agent consumers and runtime publishers | Search, Discover, Watch, register, and deregister |
| Admin | HTTP | Maintainer SDK and management integrations | Agent CRUD, Version lifecycle, and runtime inspection |
| Console | HTTP | Nacos Console UI | UI-oriented facade over Admin semantics |
HTTP APIs follow the Nacos v3 conventions:
- Client paths start with `/v3/client/ai/agents`.
- Admin paths start with `/v3/admin/ai/agents`.
- Console paths start with `/v3/console/ai/agents`.
- Responses use `Result<T>`. New controllers use `@NacosApi`,
`@Since(version = "3.3.0")`, the matching `ApiType`, `SignType.AI`, and
`READ` or `WRITE` authorization.
- GET inputs are query parameters. Write inputs are JSON bodies. `agentName`
is compared verbatim and is not a path variable.
- gRPC continues to use the common Nacos `Payload` stream and
`metadata.type`; it does not add a protobuf service method.
The six RAD root messages are reused without creating another domain model.
Java may represent `AgentCatalogPage` as an equivalent
`Page<AgentCatalogEntry>`. `Result<T>`, gRPC wrappers,
`ClientLivenessInfo`, and Console-only views are binding objects and are not
part of the RAD Schema.
### 1.1 Namespace Rules
| Caller | Rule |
|---|---|
| Ordinary Client SDK | The SDK instance is bound to one namespace. Public methods do not accept a namespace argument. The proxy copies the request and injects the bound value before transport. |
| Client HTTP caller | `namespaceId` may be supplied explicitly. When omitted, the binding inserts the normalized default namespace `public` before invoking RAD. |
| Maintainer SDK and Admin API | A Maintainer SDK instance is not namespace-bound. Every request explicitly supplies `namespaceId`; no default-namespace overload is provided. |
If an ordinary Client SDK accepts a model that already contains a nonempty
`namespaceId`, it must reject a value different from the SDK namespace and
must not mutate the caller's object.
### 1.2 Concurrency, Results, And Errors
Agent metadata updates use `expectedMetaVersion`. Draft content updates use
`expectedContentDigest`. Lists are paged; a `RuntimeEndpointSnapshot` is a
complete, non-paged snapshot.
| Condition | Required result |
|---|---|
| Missing or invalid field, invalid URI/range, or duplicate endpoint natural key | Standard parameter error |
| Invisible or absent Discover target | `RESOURCE_NOT_FOUND`; no visibility distinction |
| Endpoint pre-registration when no Agent definition exists | Accepted after structural, authorization, quota, and conflict validation |
| Metadata CAS, content CAS, or publisher-payload conflict | `RESOURCE_CONFLICT` |
| Invalid Version lifecycle transition | `ILLEGAL_STATE` |
| HTTP heartbeat for unknown client | HTTP 404 and the distinct `HTTP_CLIENT_NOT_FOUND` application code |
| Unsupported negotiated transport capability | Local `FEATURE_NOT_SUPPORTED`; no remote request |
| Deregistration of a missing contribution | Success without change |
| Valid runtime query with no instances | Success with `items=[]` |
| Discover filter matches no value | A typed empty result as defined by RAD, not `NOT_FOUND` |
HTTP status and `Result.code` use the common v3 exception mapping. gRPC
responses expose equivalent error categories. The numeric value for a new
application error is allocated by the implementation change; it must not alias
ordinary `RESOURCE_NOT_FOUND`.
## 2. Client API
### 2.1 Java SDK Contract
The user-facing interface is named `AgentDiscoveryService`; the RAD acronym is
not required in application code. During A2A compatibility:
```text
AiService extends AgentDiscoveryService, A2aService
```
| Capability | Method | Input | Result |
|---|---|---|---|
| Search | `searchAgents` | `AgentSearchRequest` without a caller-controlled namespace | `Page<AgentCatalogEntry>` |
| Discover | `discoverAgent` | `AgentReference` | `AgentDiscoveryResult` |
| Filtered Discover | `discoverAgent` | `AgentReference`, `AgentDiscoveryFilter` | `AgentDiscoveryResult` |
| Watch | `subscribeAgent` | Reference, optional Filter, Listener | Current `AgentDiscoveryResult` |
| Cancel Watch | `unsubscribeAgent` | Same Reference, Filter, and Listener identity | `void` |
| Register | `registerAgentEndpoints` | `AgentEndpointRegistrationBatch` | `void` |
| Deregister | `deregisterAgentEndpoints` | `AgentEndpointDeregistrationBatch` | `void` |
`subscribeAgent` returns the current complete result and later delivers
complete replacement results. `getAll`, `selectOneHealthy`, protocol choice,
priority/weight selection, and actual Agent calling are local SDK helpers, not
additional remote operations.
Register is a natural-key upsert and does not replace omitted endpoints. The
SDK stores registration batches as redo intent. The initial implementation may
omit a new generic Agent-definition publish method, but existing
`A2aService.releaseAgentCard` remains functional through the compatibility
adapter. A later Client SDK revision will provide an optional code-first Agent
publish operation: `autoSubmit=false` creates a draft, and `autoSubmit=true`
runs the ordinary submit Pipeline. It is not force-publish and endpoint
registration never creates a definition implicitly.
### 2.2 Transport Matrix
| Capability | HTTP | gRPC |
|---|:---:|:---:|
| Search | Yes | Yes |
| Discover | Yes | Yes |
| Watch and push | No | Yes |
| Register and Deregister | Yes | Yes |
| Publisher heartbeat | Yes | Uses the gRPC connection lifecycle |
An HTTP-only SDK fails Watch locally; it must not simulate Watch by polling.
After a write timeout, an SDK may change transport only when it knows the
server did not process the request. An unknown gRPC write result must not be
blindly repeated through HTTP.
### 2.3 Client HTTP Paths
| Method | Path | Input | Result |
|---|---|---|---|
| GET | `/v3/client/ai/agents/search` | RAD search query | `Result<Page<AgentCatalogEntry>>` |
| GET | `/v3/client/ai/agents` | RAD reference and optional filter query | `Result<AgentDiscoveryResult>` |
| POST | `/v3/client/ai/agents/endpoints` | `AgentEndpointRegistrationBatch` | `Result<ClientLivenessInfo>` |
| DELETE | `/v3/client/ai/agents/endpoints` | JSON `AgentEndpointDeregistrationBatch` | `Result<Void>` |
| PUT | `/v3/client/ai/agents/endpoints/heartbeat` | No body | `Result<ClientLivenessInfo>` |
Search query names equal RAD field names. Repeated `tagsAll` values use AND;
repeated `protocolsAny` values use OR. `agentNameContains` is a literal,
case-sensitive substring.
Discover maps `agentName`, `version`, and `label` directly. Repeated filter
parameters are `protocol`, `transport`, and `endpointSource`.
`protocolVersion` is singular. `metadataSelector` is one URL-encoded JSON
object rather than dynamic `metadata.<key>` parameter names.
The Endpoint path deliberately uses only POST and DELETE. POST already upserts
complete endpoint values, so a general PUT would introduce ambiguous partial
update semantics. GET is unnecessary because consumers use Discover and
maintainers use `RuntimeEndpointSnapshot`. DELETE with a JSON body is the only
0.1 binding and requires clients and gateways that preserve that body.
### 2.4 HTTP Publisher Identity And Liveness
Endpoint write and heartbeat requests require:
```text
X-Nacos-Client-Id: http-<ipToken>-<processToken>-<clientSequence>-<createTimestamp>
Request-Module: AI
```
The server treats the client id as an opaque 1-to-256-character value matching
`[A-Za-z0-9._:-]+`. The official generator uses only
`[A-Za-z0-9-]`, includes at least 96 bits of random process entropy, uses
`clientSequence` to distinguish SDK instances in one process, and may include
a diagnostic PID token. The id is stable across retry, server switch, and
redo; a process restart creates a new id. It is routing identity, not a
credential.
`ClientLivenessInfo` contains only:
```text
heartbeatIntervalMillis < unhealthyTimeoutMillis < expireTimeoutMillis
```
The latest successful registration or heartbeat response controls scheduling.
One heartbeat keeps the whole client alive, independent of endpoint count.
Endpoint writes also refresh liveness. A client with no remaining endpoints is
removed and stops heartbeats.
| State | Runtime behavior |
|---|---|
| `ACTIVE` | Contributions use their current Naming health. |
| `UNHEALTHY` | After `unhealthyTimeoutMillis`, contributions remain discoverable with `healthy=false`. |
| `EXPIRED` | After `expireTimeoutMillis`, all contributions owned by the client are removed. |
The server routes HTTP publisher state by `clientId` through Distro type
`AI_AGENT_HTTP_CLIENT`. Only the responsible node owns the native client,
`lastActiveTime`, and timeout task. Peers receive complete client state needed
to rebuild the Naming/RAD projection. A new owner starts its failover grace
period only after receiving a complete snapshot; otherwise it returns
`HTTP_CLIENT_NOT_FOUND`, and the client redoes every expected endpoint group.
The first accepted write binds the client id to authenticated identity and
namespace. Later mismatches are rejected. The same string in another module
does not share liveness or cleanup state.
### 2.5 gRPC Payloads And Abilities
| Request | Response | Semantics |
|---|---|---|
| `AgentSearchRequest` | `AgentSearchResponse` | Search and return a page of catalog entries |
| `AgentDiscoveryRequest` | `AgentDiscoveryResponse` | One Discover |
| `AgentSubscribeRequest` | `AgentSubscribeResponse` | Subscribe or unsubscribe; a successful subscription returns an opaque `watchKey` and the current complete result |
| `AgentDiscoveryNotifyRequest` | `AgentDiscoveryNotifyResponse` | Push a `SNAPSHOT` or `TERMINATED` event for one `watchKey` and receive an acknowledgement |
| `AgentEndpointRegisterRequest` | `AgentEndpointOperationResponse` | Register one RAD batch |
| `AgentEndpointDeregisterRequest` | `AgentEndpointOperationResponse` | Deregister one RAD batch |
All requests report module `ai`. gRPC endpoint contributions belong to
`RequestMeta.connectionId`; no client id or heartbeat payload is added.
Disconnect removes that connection's contributions. Reconnect obtains a new
connection id and redoes endpoints and subscriptions.
`AgentSubscribeResponse.watchKey` is the binding-defined opaque identity for
the accepted wire subscription. The SDK maps it to the canonical local Watch
identity and does not parse it. `AgentDiscoveryNotifyRequest` contains
`watchKey` and `eventType`:
- `SNAPSHOT` requires one complete `AgentDiscoveryResult` and has no error;
- `TERMINATED` contains no result and, in this version, requires
`errorCode=NOT_FOUND`;
- either event is acknowledged with `AgentDiscoveryNotifyResponse`;
- `TERMINATED` closes only the identified Watch on the shared Payload
connection. It does not close that connection or any other Watch.
The SDK atomically replaces the cached result for `SNAPSHOT`. For
`TERMINATED`, it delivers the terminal status and removes only that Watch and
its redo state before acknowledging. After reconnect, the SDK discards the old
connection-scoped `watchKey`, subscribes again using its canonical local Watch
identity, and stores the new response `watchKey` and current result. These
request and response types are Nacos gRPC binding objects; they do not add to
the six RAD root messages.
The target ability keys are:
| Constant | Wire key | Meaning |
|---|---|---|
| `SERVER_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | Server accepts RAD Search, Discover, and Watch payloads |
| `SERVER_AGENT_ENDPOINT_V1` | `agentEndpointV1` | Server accepts RAD endpoint publication payloads |
| `SDK_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | SDK accepts RAD discovery push |
Legacy `SERVER_AGENT_REGISTRY`, `SERVER_AGENT_CARD_V1`, and
`SDK_AGENT_REGISTRY` gate only the old A2A contract. Absence of a new ability
does not authorize sending a RAD payload through a legacy fallback.
### 2.6 Idempotency And Redo
| Event | Required behavior |
|---|---|
| Repeat identical Register | Success without semantic change |
| Register changed non-identity fields | Upsert that publisher contribution |
| Duplicate natural key in one batch | Reject the complete batch |
| Repeat Deregister | Success without change |
| Repeat heartbeat | Refresh only client liveness |
| HTTP timeout | Retry with the same client id and identical payload using backoff |
| `HTTP_CLIENT_NOT_FOUND` | Mark all local endpoint intent unregistered and redo by complete group |
| gRPC reconnect | Redo endpoints and subscriptions under the new connection id |
| Cross-transport deregistration | Forbidden; one publisher identity cannot remove another transport's contribution |
The SDK records expected state before the first write. Shutdown performs a
best-effort deregistration; expiry remains the cleanup fallback. Parameter,
authorization, and publisher-conflict errors do not enter infinite redo.
## 3. Admin API And Maintainer SDK
Admin reads do not run an implicit data-plane Discover and do not inject
runtime endpoints into a Version descriptor.
### 3.1 Agent And Read Views
| Method | Path | Action | Result |
|---|---|---|---|
| POST | `/v3/admin/ai/agents` | Create Agent and initial draft atomically | `Result<AgentOverview>` |
| GET | `/v3/admin/ai/agents` | Read Agent and first bounded Version-summary page | `Result<AgentOverview>` |
| PUT | `/v3/admin/ai/agents` | Update writable Agent fields using metadata CAS | `Result<Agent>` |
| DELETE | `/v3/admin/ai/agents` | Delete Agent definition and Version content | `Result<Void>` |
| GET | `/v3/admin/ai/agents/list` | Filter and page Agent summaries | `Result<Page<AgentSummary>>` |
| GET | `/v3/admin/ai/agents/versions` | Page Version summaries | `Result<Page<AgentVersionSummary>>` |
| GET | `/v3/admin/ai/agents/version` | Read one exact Version definition | `Result<AgentVersionDetail>` |
| GET | `/v3/admin/ai/agents/runtime-endpoints` | Read one protocol's complete runtime snapshot, optionally filtered by Version | `Result<RuntimeEndpointSnapshot>` |
Runtime query input is `namespaceId + agentName + protocol + version?`.
`protocol` is required. Omitting `version` returns one item per natural
Endpoint key for the protocol with all bindings; supplying it retains only
matching bindings. The query does
not apply `endpointSourceOrder`, does not require a definition to exist, and
returns an empty item array when no instance exists.
Create contains writable Agent fields and a required `initialDraft`. Agent,
Version row, and Storage writes have one logical atomic outcome and compensate
partial failures. Update may change presentation, tags, extensions, enabled
state, owner, and scope, but not identity, Version content, labels, or the
derived catalog. Definition deletion immediately prevents ordinary discovery;
it does not delete independently owned runtime publications.
### 3.2 Version Lifecycle Paths
| Method | Path | Transition or action | Result |
|---|---|---|---|
| POST | `/v3/admin/ai/agents/draft` | Create a new draft, optionally copying one exact Version | `Result<AgentVersionDetail>` |
| PUT | `/v3/admin/ai/agents/draft` | Update one draft using content-digest CAS | `Result<AgentVersionDetail>` |
| DELETE | `/v3/admin/ai/agents/draft` | Delete one draft | `Result<Void>` |
| POST | `/v3/admin/ai/agents/submit` | `draft -> reviewing`, or the shared no-Pipeline transition | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/publish` | `reviewed -> online` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/force-publish` | Audited Pipeline bypass to `online` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/redraft` | `reviewed -> draft` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/online` | `offline -> online` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/offline` | `online -> offline` | `Result<AgentVersionSummary>` |
| PUT | `/v3/admin/ai/agents/labels` | Update custom labels; `latest` stays server-managed | `Result<Agent>` |
Every action identifies `namespaceId + agentName + exact version`; an omitted
version never means latest for a write. `force-publish` uses ordinary Agent
WRITE permission rather than a new permission point, but every success and
failure records caller, resource identity, prior and target state, result,
request id, and time. Audit records omit descriptor and sensitive metadata.
The initial release does not expose a same-Version forced content replacement.
### 3.3 Maintainer SDK
`AiMaintainerService.agent()` returns `AgentMaintainerService`.
`AiMaintainerService.a2a()` remains during its compatibility window. The Agent
maintainer interface maps one-to-one to Admin HTTP and uses Request/Command
objects for compound writes. It is not namespace-bound, requires
`namespaceId` on every call, and does not add a Maintainer gRPC transport.
## 4. Console API
Console uses `/v3/console/ai/agents` and mirrors every Admin relative path,
request, result, lifecycle rule, and authorization intent. It is a UI facade,
not a second Agent application service.
The only Console-specific response is `ConsoleRuntimeEndpointView`, which
wraps `RuntimeEndpointSnapshot` and adds:
```text
namingServiceRef { namespaceId, groupName, serviceName }
```
The backend computes this reference; the browser does not implement the Agent
name codec or Naming service composer. A Version page first reads
`AgentVersionDetail`, creates protocol tabs from `callInterfaces[]`, and lazily
loads one runtime snapshot per selected protocol with the current Version
filter. It may query a CallInterface that omits `RUNTIME`; in that case it
shows any registered state separately and explains that it is not currently
discoverable through that Version. Runtime editing is not part of the initial
Agent Console API; the UI links to the Naming instance page for enable or
disable operations.
Console does not expose RAD Search, Discover, Watch, endpoint publication, or
remote Agent calling.
## 5. Implementation And Compatibility Requirements
An implementation must complete these together before advertising an Agent or
RAD ability:
1. API models, validation, error mapping, authorization, and audit;
2. gRPC payload registration and ability negotiation;
3. HTTP publisher Distro state, liveness, idempotency, and redo;
4. Java SDK namespace binding, cache, Watch, reconnect, and endpoint redo;
5. Admin/Maintainer and Console contracts;
6. old A2A facade conversion; and
7. OpenAPI, Java SDK, and Maintainer SDK integration-test scenario matrices and
coverage registries.
Legacy Console A2A APIs are supported through the Nacos 3.4 line. Legacy Admin
and Maintainer A2A APIs remain through the Nacos 4.0 compatibility boundary.
Historical data migration and mixed-version rolling-upgrade behavior are a
separate specification and must not be inferred from this API-only contract.
+409
View File
@@ -0,0 +1,409 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Agent Management Spec
This document defines the protocol-neutral Agent management model for Nacos AI
Registry. It refines the [AI Resource Model Spec](ai-resource-model-spec.md),
the [AI Resource Lifecycle Spec](ai-resource-lifecycle-spec.md), and the
[Naming specs](../naming/README.md).
This is the normative target contract for the Agent model migration. A server
or SDK must not advertise this contract until the corresponding behavior is
implemented. Before that capability is advertised, the current
[A2A Agent Spec](a2a-agent-spec.md) remains the runtime contract.
## 1. Scope And Boundaries
The model separates three facts with different lifecycles:
```text
Agent
1 --- N AgentVersion
1 --- N AgentCallInterface
1 --- N DECLARED Endpoint
Runtime publisher --- N RUNTIME Endpoint
RUNTIME Endpoint --- versionRange match ---> AgentVersion + AgentCallInterface
```
| Fact | Responsibility | Fact source |
| --- | --- | --- |
| `Agent` | Stable identity, catalog metadata, ownership, visibility, and version governance. | `ai_resource` |
| `AgentVersion` | One versioned calling definition, mutable only while it is a draft. | `ai_resource_version` and AI Storage |
| `AgentCallInterface` | One protocol binding and its declared addresses. | `AgentVersionContent` |
| `RuntimeEndpoint` | A live publisher's callable address and compatible version range. | Naming runtime state |
A2A is the first protocol adapter. The common Agent model must not contain a
union of A2A-specific capability, skill, security, task, or message fields.
Those fields remain in the protocol-native descriptor.
This spec does not define MCP resources or actual remote invocation. Nacos
returns calling metadata; it does not proxy Agent messages, tasks, sessions,
streams, retries, or credentials. Search, Discover, Watch, and runtime
publication wire objects are defined by the
[RAD Protocol Spec](rad-protocol-spec.md). Runtime publication ownership,
physical storage, Naming mapping, codecs, and revision algorithms are defined
by the [Agent Storage Spec](agent-storage-spec.md).
## 2. Identity And Validation
### 2.1 Agent Identity
The canonical Agent identity is:
```text
namespaceId + resourceType=agent + agentName
```
`agentName` is the public `resourceName` and has these rules:
- it contains 1 to 64 printable ASCII characters in the inclusive range
`U+0020..U+007E`;
- it contains at least one non-space character;
- it is stored exactly as submitted and is case-sensitive;
- the server must not trim, lowercase, slug, or otherwise rewrite it; and
- it is immutable after creation.
`displayName` is an optional Unicode presentation field. A presentation layer
must use `agentName` when `displayName` is absent or blank. `displayName` never
participates in identity, authorization, storage keys, or endpoint matching.
Exact lookup compares the original `agentName`. A name filter performs literal
substring matching; persistence implementations must escape wildcard
characters such as `%` and `_` instead of interpreting them as patterns.
### 2.2 Version Identity
An Agent Version identity is:
```text
namespaceId + resourceType=agent + agentName + version
```
`version` uses `MAJOR.MINOR.PATCH[-PRERELEASE]` and is at most 64 characters:
- `MAJOR`, `MINOR`, and `PATCH` are `0` or a positive integer without a
leading zero;
- `PRERELEASE` contains one or more dot-separated `[0-9A-Za-z-]+`
identifiers, and a numeric-only identifier has no leading zero;
- build metadata introduced by `+` is not accepted;
- the original value is stored and compared case-sensitively; and
- all Agent write paths, including compatibility facades, apply these rules.
Version precedence compares major, minor, and patch numerically. A release is
higher than its prerelease. Prerelease identifiers are compared from left to
right: numeric identifiers use numeric order and are lower than non-numeric
identifiers; non-numeric identifiers use case-sensitive ASCII order; a longer
otherwise-equal sequence is higher.
Version labels match `[A-Za-z0-9][A-Za-z0-9._-]{0,63}` and are
case-sensitive. `latest` is reserved for the server-managed pointer; it cannot
be created, replaced, or removed through a custom-label write.
### 2.3 Endpoint Identity
DECLARED and RUNTIME sources use the same `Endpoint` value object. Within one
Agent protocol group, its natural identity is:
```text
(namespaceId, agentName, protocol,
normalizedHost(uri), effectivePort(uri), normalizedTransport)
```
There is no public `endpointId`. URI path, query, metadata, priority, and
weight do not participate in identity and may be updated by the same publisher.
## 3. Agent Resource
The Agent resource contains the following fields:
| Field | Required | Meaning |
| --- | :---: | --- |
| `namespaceId` | Yes | Isolation boundary. |
| `agentName` | Yes | Stable public identity. |
| `displayName` | No | Unicode presentation name. |
| `description` | No | Catalog description. |
| `iconUrl` | No | Catalog icon URI. |
| `provider` | No | Provider `name` and `url`; this is not the management owner. |
| `tags[]` | No | Public catalog and exact-match search tags. |
| `extensions` | No | Namespaced `Map<String, JsonValue>` for public Agent-level extensions. |
| `status` | Yes | `enable` or `disable`. |
| `owner` | Yes | Management owner. |
| `scope` | Yes | Visibility scope. |
| `versionInfo` | Read-only | Shared editing, reviewing, online-count, and label summary. |
| `versionCatalog` | Read-only | Compact catalog of online versions and protocols. |
| `metaVersion` | Read-only | Metadata CAS version. |
| `createTime`, `updateTime` | Read-only | Audit timestamps. |
The following invariants apply:
- Agent metadata does not embed protocol descriptors, endpoints, health state,
or complete version history.
- `tags` is the only public generic classification list in this version.
- `extensions` does not affect identity, authorization, version selection,
endpoint selection, or default search. It must not contain credentials or
server-internal state.
- Updating catalog or extension fields advances `metaVersion` but does not
create an Agent Version.
- A protocol adapter may initialize missing catalog fields from a native
descriptor only when the Agent is first created. Later descriptor updates
do not overwrite independently governed Agent metadata.
`versionCatalog` contains `latestVersion` and `onlineVersions[]`. Each online
entry contains only `version`, `labels[]`, and `protocols[]`. It is derived by
the server and is not a client-writable fact.
## 4. Agent Version And Lifecycle
### 4.1 Version Metadata And Content
An Agent Version exposes this metadata:
| Field | Required | Meaning |
| --- | :---: | --- |
| `namespaceId`, `agentName`, `version` | Yes | Exact version identity. |
| `status` | Yes | Shared AI Resource version status. |
| `callInterfaces[]` | Yes | Ordered protocol bindings; at least one. |
| `author` | No | Version author. |
| `changeDescription` | No | Version change description. |
| `contentDigest` | Read-only | SHA-256 digest of canonical version content. |
| `createTime`, `updateTime` | Read-only | Audit timestamps. |
The complete storage payload is one `AgentVersionContent` object:
```text
AgentVersionContent
kind = AgentVersionContent
schemaVersion = 1
callInterfaces[]
```
Canonical serialization uses RFC 8785 JCS and preserves the business order of
CallInterfaces and declared Endpoints. `contentDigest` uses
`sha256:<lowercase hex>` over the canonical UTF-8 bytes. Exact I-JSON and
validation rules are defined by the [Agent Storage Spec](agent-storage-spec.md).
### 4.2 Lifecycle Rules
Agent Versions use the shared lifecycle:
| Status | Content mutable | Available to ordinary RAD discovery |
| --- | :---: | :---: |
| `draft` | Yes | No |
| `reviewing` | No | No |
| `reviewed` | No | No |
| `online` | No | Yes |
| `offline` | No | No |
`ai_resource_version.status` is the lifecycle fact source.
`publishPipelineInfo` records review execution and outcome only.
One Agent may have at most one editing version and one reviewing version.
Content becomes frozen when a draft enters reviewing. Reviewed, online, and
offline content must not be updated in place. This version of the contract does
not provide a forced same-version content replacement operation.
`latest` is a server-managed label and must always point to an online version.
The following Agent-specific rules refine the common AI lifecycle rule:
- every successful standard publish or online transition makes its target
Version `latest`;
- legacy A2A publication with `setAsLatest=false` is the only exception in the
initial release and preserves the current valid `latest`;
- publishing the first online Version establishes `latest` even through that
compatibility path;
- deleting or offlining the current `latest` selects the SemVer-greatest
remaining online Version;
- removing the last online Version removes `latest`; and
- deleting or offlining an online Version other than the current `latest` does
not trigger recalculation.
Whenever online status or labels change, the server must rebuild
`versionCatalog` and the derived protocol search tokens as one logical update.
When at least one online version exists, exactly one valid `latestVersion` must
exist and must occur in `onlineVersions`.
Agent metadata, Agent Version definitions, and Runtime Endpoints do not own one
another's lifecycle. Deleting or disabling an Agent definition changes its
read projection but does not delete still-live runtime publisher state.
## 5. Call Interfaces And Declared Endpoints
### 5.1 AgentCallInterface
Each Agent Version contains an ordered, non-empty `callInterfaces[]` list.
Each item contains:
| Field | Required | Meaning |
| --- | :---: | --- |
| `protocol` | Yes | Canonical protocol token, unique within the Version. |
| `protocolVersion` | No | Fast protocol negotiation value; not interface identity. |
| `descriptorMediaType` | Yes | Media type of `nativeDescriptor`. |
| `nativeDescriptor` | Yes | Complete protocol-native descriptor. |
| `endpointSourceOrder[]` | Yes | Non-empty ordered set of `RUNTIME` and `DECLARED`. |
| `declaredEndpoints[]` | No | Static endpoint projection derived by the adapter. |
The canonical protocol token matches
`[A-Za-z0-9][A-Za-z0-9-]{0,31}` and is compared case-sensitively. The same
token is used by CallInterface uniqueness, endpoint publication, RAD filters,
and Naming service composition.
The order of `callInterfaces[]` is the default protocol preference. Reordering
the list changes `contentDigest`. The first interface with a usable endpoint is
the SDK's default selection candidate.
`endpointSourceOrder` contains no duplicate and has one of the following
meanings:
- `[RUNTIME, DECLARED]` prefers live addresses and keeps declared addresses as
fallback;
- `[DECLARED, RUNTIME]` prefers declared addresses;
- `[RUNTIME]` or `[DECLARED]` allows only that source in ordinary discovery.
Source order belongs to one CallInterface, not to the whole Version. It does
not prevent runtime publication. Management queries may inspect Runtime
Endpoints even when a CallInterface omits `RUNTIME`.
### 5.2 Endpoint Value Object
| Field | Required | Meaning |
| --- | :---: | --- |
| `uri` | Yes | Complete callable URI. |
| `transport` | Yes | Canonical transport token. |
| `priority` | No | Lower values have higher priority. |
| `weight` | No | Load weight among endpoints with equal priority. |
| `metadata` | No | Flat zone, environment, data-center, and extension labels. |
The URI has a non-empty scheme and host. Its port is explicit or can be derived
as a valid `1..65535` default for the scheme. DNS hosts use a case-insensitive
canonical form; IP literals use stable IPv4 or IPv6 representation.
An adapter derives and validates `declaredEndpoints` from
`nativeDescriptor`. Clients must not edit the two representations
independently. When the same natural endpoint occurs more than once, the first
descriptor occurrence determines list position while the native descriptor
remains byte-for-byte represented by the canonical content.
## 6. Management Read Models
Management APIs use bounded views rather than one unbounded aggregate:
| View | Contains | Excludes |
| --- | --- | --- |
| `AgentSummary` | Presentation, governance, and version-catalog summary. | Descriptor, Endpoint, full history, extensions. |
| `AgentOverview` | Full Agent and a bounded page of Version summaries. | Version payload and Runtime Endpoint. |
| `AgentVersionSummary` | Version, status, author, change description, digest, and timestamps. | CallInterface payload. |
| `AgentVersionDetail` | Exact Version metadata and complete CallInterfaces. | Runtime Endpoint. |
| `RuntimeEndpointSnapshot` | Raw runtime snapshot for one Agent and protocol, optionally filtered by Version. | Descriptor, publisher identity, final discoverability decision. |
`RuntimeEndpointSnapshot` is not paged. It contains:
```text
namespaceId / agentName / protocol / version?
items[] {
endpoint, bindings[] { runtimeVersion, versionRange },
state, enabled, healthy, lastUpdatedTime
}
state = AVAILABLE | DISABLED | UNHEALTHY
```
State evaluation is ordered: `enabled=false` is `DISABLED`; otherwise
`healthy=false` is `UNHEALTHY`; all other items are `AVAILABLE`.
`lastUpdatedTime` changes only when public Endpoint content, enabled state, or
aggregate health changes. A heartbeat alone does not change it.
`protocol` is required. Without `version`, the snapshot contains one effective
item per natural Endpoint key for that protocol and all of its Version
bindings. With `version`, it retains only bindings matching the supplied
Version and omits an item when no binding remains. Missing instances produce
an empty `items[]`.
The snapshot does not apply `endpointSourceOrder` and does not claim that an
item is discoverable. A console combines Version detail and snapshots only as
separate read facts.
RAD catalog, discovery, and watch objects are data-plane views and are defined
only by the [RAD Protocol Spec](rad-protocol-spec.md). In particular,
`AgentDiscoveryResult` combines one online Version definition with permitted
DECLARED and RUNTIME Endpoint sets; it is never stored as a fact.
## 7. Capacity And Security
The target management model enforces these limits before writing an Agent or
Version fact:
| Field | Limit |
| --- | ---: |
| `displayName`, `provider.name` | 128 Unicode code points. |
| `description` | 2048 characters. |
| Icon, provider, or declared Endpoint URI | 2048 characters. |
| Public tags | 32 items, 64 characters each. |
| Agent `extensions` | 32 items; key 128 characters; canonical JSON total 16 KiB. |
| `protocol`, `protocolVersion` | 32 and 64 characters. |
| CallInterfaces per Version | 16. |
| Declared Endpoints per CallInterface | 64. |
| Endpoint metadata | 32 items; key 64 and value 256 characters. |
| `AgentVersionContent` | 1 MiB. |
Public tags and internal protocol tokens share the persistence capacity of
`biz_tags`; the server validates the canonical combined length before atomically
accepting either a tag or online-protocol change.
Descriptors, extensions, and Endpoint metadata must not contain plaintext
credentials. Audit records must not log complete native descriptors, security
schemes, or sensitive Endpoint metadata. Runtime publication and physical
storage limits are defined by the
[Agent Storage Spec](agent-storage-spec.md).
## 8. A2A Compatibility Boundary
After migration, old A2A APIs are compatibility facades over the Agent model;
they do not create a second AgentCard fact source.
| A2A value | Agent model projection |
| --- | --- |
| AgentCard name and version | `agentName` and Agent Version identity. |
| Complete AgentCard | A2A CallInterface `nativeDescriptor`. |
| A2A protocol version | CallInterface `protocolVersion` and native descriptor. |
| Root URL and supported/additional interfaces | Adapter-derived declared Endpoints. |
| `registrationType=URL` | Declared-first source order. |
| `registrationType=SERVICE` | Runtime-first source order. |
| Runtime A2A endpoint version | `runtimeVersion` and exact `[version]` range. |
The first implementation supports only the A2A protocol, so old A2A latest
and common Agent latest use the same label. The adapter reconstructs old query
DTOs from the native descriptor and the applicable Endpoint projection. It
uses declared addresses for URL-style reads and runtime addresses for
service-style reads, with declared addresses as the compatibility fallback
when no runtime address exists.
New writes through old APIs apply the identity, Version, immutability, and
capacity rules in this spec. They may use an audited internal direct-online
transition to preserve code-first A2A publication, but they must not overwrite
different content in an already published Version.
Runtime A2A publication and deregistration projection are defined by the
[Agent Storage Spec](agent-storage-spec.md).
Historical Config rows, historical Naming layouts, mixed-version cluster
dual-read or dual-write, source cutover, rollback, and malformed historical
identities belong to a separate rolling-upgrade and data-migration contract.
They are not relaxed by this target model.
Agent and AgentSpec resources may reference each other through a general
resource relation, but neither owns the other's lifecycle. This version does
not add Agent-specific `sourceRef`, `defaultInterfaceId`, `interfaceId`,
`descriptorDigest`, or random Endpoint identifiers.
+630
View File
@@ -0,0 +1,630 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Agent Storage Spec
This document defines the internal persistence, runtime-publication, Naming
mapping, codec, digest, and revision contract for the
[Agent Management Spec](agent-management-spec.md). The
[RAD Protocol Spec](rad-protocol-spec.md) defines the external data-plane
messages; this document defines how Nacos produces their facts.
This is the normative target contract for the Agent model migration. A server
must not advertise Agent or RAD capability until the storage behavior required
by this contract is implemented. Existing A2A storage remains governed by the
[A2A Agent Spec](a2a-agent-spec.md) before that cutover.
## 1. Storage Responsibilities
Agent state is split by lifecycle and access pattern:
```text
Agent metadata -----------------------> ai_resource
Agent Version metadata ---------------> ai_resource_version
CallInterface + DECLARED Endpoint ----> AI Storage
|
+-- built-in nacos_config provider
RUNTIME publisher contributions ------> Naming Client runtime state
```
| Store | Owns | Does not own |
| --- | --- | --- |
| `ai_resource` | Agent identity, catalog, governance, version summary, and derived online catalog. | Version payload or runtime health. |
| `ai_resource_version` | Exact Version identity, lifecycle status, author, storage pointer, and pipeline state. | CallInterface payload or runtime endpoint. |
| AI Storage | Canonical `AgentVersionContent` bytes for one Version. | Resource identity, lifecycle, labels, or visibility. |
| Naming Client state | Live publisher contributions, health, enabled state, and version bindings. | Agent definition or Version lifecycle. |
The service must not persist a merged `AgentDiscoveryResult`. Summary,
management detail, catalog, discovery, and watch objects are read projections
over these facts.
## 2. AI Resource Persistence
### 2.1 Agent Resource Row
The canonical Agent identity is
`namespaceId + type=agent + name=agentName`. It maps to `ai_resource` as
follows:
| `ai_resource` field | Agent mapping |
| --- | --- |
| `namespace_id`, `type`, `name` | `namespaceId`, constant `agent`, original `agentName`. |
| `c_desc` | `description`. |
| `status` | `enable` or `disable`. |
| `owner`, `scope` | Same-named governance fields. |
| `biz_tags` | Public tags plus server-derived online-protocol tokens. |
| `ext` | Typed `AgentResourceExt`. |
| `c_from` | Creation, import, or synchronization source. |
| `version_info` | Shared editing, reviewing, online-count, and label summary. |
| `meta_version` | Metadata CAS version. |
| `gmt_create`, `gmt_modified` | Audit timestamps. |
`AgentResourceExt` has this fixed schema-version-1 shape:
| Field | Owner | Meaning |
| --- | --- | --- |
| `schemaVersion` | Server | Constant `1`. |
| `displayName`, `iconUrl`, `provider` | User, after validation | Catalog presentation. |
| `extensions` | User, after validation | Public Agent-level extensions. |
| `versionCatalog` | Server | Derived online Version catalog. |
`versionCatalog` contains `latestVersion` and `onlineVersions[]`; each entry
contains only `version`, `labels[]`, and `protocols[]`. Version status and
`version_info.labels` remain the facts. Publish, online, offline, delete, label,
or latest changes rebuild the catalog as one logical Resource update.
Protocol search tokens in `biz_tags` use the reserved prefix
`__nacos.agent.protocol:`. A user tag must not use `__nacos.agent.`. Read
projections remove internal tokens. Public tags and internal tokens share the
canonical persistence limit, and a write that would exceed that limit is
rejected atomically.
AgentName and Version identity are compared case-sensitively in DAO queries,
unique constraints, caches, labels, and authorization keys. Implementations
must not rely on a database's default case-insensitive collation.
### 2.2 Agent Version Row
Each Agent Version maps to one `ai_resource_version` row:
| `ai_resource_version` field | Agent Version mapping |
| --- | --- |
| `namespace_id`, `type`, `name`, `version` | Exact Version identity. |
| `status` | `draft`, `reviewing`, `reviewed`, `online`, or `offline`. |
| `author`, `c_desc` | Author and change description. |
| `storage` | Provider, opaque key, digest, media type, schema, and size. |
| `publish_pipeline_info` | Review execution and result. |
| `gmt_create`, `gmt_modified` | Audit timestamps. |
The physical Version field and every new Agent write support the same maximum
of 64 characters. The storage schema does not create a wider public identity
space than the Agent management contract.
Version list operations read only Resource and Version rows. Exact Version
detail performs one AI Storage read after resolving the Version row.
## 3. Agent Version Content In AI Storage
### 3.1 Content Object And Storage Pointer
One Version has exactly one complete storage object:
```text
AgentVersionContent
kind = AgentVersionContent
schemaVersion = 1
callInterfaces[]
protocol / protocolVersion
descriptorMediaType / nativeDescriptor
endpointSourceOrder[]
declaredEndpoints[]
```
Canonical serialization uses RFC 8785 JSON Canonicalization Scheme (JCS).
Array order is therefore preserved for CallInterfaces, source preference, and
declared Endpoints, while object-member order and JSON number representation
are normalized. Duplicate object keys and values that cannot be represented as
I-JSON are rejected. `contentDigest` is `sha256:<lowercase hex>` over the
UTF-8 JCS bytes.
The Version row's `storage` JSON contains:
| Field | Value or meaning |
| --- | --- |
| `provider` | Storage provider; built-in value is `nacos_config`. |
| `key` | Provider-opaque key. |
| `keyFormat` | `agent-version-config-v1` for the built-in provider. |
| `agentNameCodec` | `rad-ascii-v1` for the built-in provider. |
| `contentDigest` | `sha256:<lowercase hex>`. |
| `mediaType` | `application/vnd.nacos.agent-version+json`. |
| `schemaVersion` | `1`. |
| `size` | Canonical content byte count. |
Upper layers treat `StorageKey.key` as opaque. A replacement provider keeps
the one-Version/one-object rule but owns its physical key. The built-in
provider uses the mapping in section 3.2.
### 3.2 Built-in Nacos Config Mapping
The `agent-version-config-v1` provider key carries this logical Config
coordinate. Its serialized `StorageKey.key` remains provider-opaque to the
Agent service.
| Logical value | Logical `config_info` coordinate |
| --- | --- |
| `namespaceId` | `tenant_id=namespaceId`. |
| Content category | `group_id=agent-version`. |
| `agentName`, `version` | `data_id=agent__<encodedAgentId>__<version>.json`. |
| `AgentVersionContent` | Canonical JSON `content` with `type=json`. |
The built-in provider then applies the common `NacosAiConfigKeyCodec` to the
complete logical group and data id. A safe value within the Config limits is
stored unchanged. An overlong data id uses the codec's deterministic
`sha256.<digest>` physical fallback. The physical key is consequently not
always reversible, and no upper layer may derive Agent identity from it.
Valid Agent identity must not be rejected merely because this logical data id
is longer than the Config physical limit.
A draft update overwrites the same key. Content becomes immutable when the
Version enters reviewing. `contentDigest` never participates in the data id;
it validates content and cache equality. Read, review, and publish operations
must verify the Storage pointer, byte count, and digest.
### 3.3 RAD ASCII AgentName Codec
Config data ids and Naming service names share
`RadAsciiAgentIdCodec` with codec id `rad-ascii-v1`:
1. input is the original 1-to-64-character printable-ASCII `agentName`;
2. if the entire input matches `[A-Za-z0-9-]+`, return it unchanged;
3. otherwise output `enc-<body>`;
4. in encoded form, preserve ASCII letters and digits and encode every other
character, including `-`, as `-DDD`, where `DDD` is its three-digit decimal
ASCII value;
5. preserve letter case and never trim or lowercase; and
6. decode only a segment already known to use this codec, rejecting truncated,
non-decimal, out-of-range, or non-canonical escapes.
Examples:
```text
Nacos-Agent -> Nacos-Agent
Nacos Agent -> enc-Nacos-032Agent
name-ok.1:2 -> enc-name-045ok-0461-0582
```
The output contains only `[A-Za-z0-9-]`. Codec version 1 intentionally does not
reserve raw names beginning with `enc-`. Consequently, a raw safe name and the
encoded result of another name can theoretically produce the same physical
segment. Version 1 accepts this low-probability ambiguity and defines no
collision index, reservation, or atomic encoded-id mapping. Public identity
always comes from `ai_resource.name`; code must not infer it by decoding an
untyped physical key. A future collision-free codec requires a new codec id and
an explicit migration contract rather than changing `rad-ascii-v1` in place.
Version uses only letters, digits, `.` and `-` and is not processed by the
AgentName codec. The generic Config physical-key codec may hash the complete
logical data id solely to satisfy its physical length limit; this does not
truncate, hash, or rewrite either public identity field.
## 4. Runtime Publication Model
### 4.1 Public Endpoint And Version Binding
DECLARED and RUNTIME sources share the Endpoint value object. Within one Agent
protocol group, the public Endpoint natural key is:
```text
(namespaceId, agentName, protocol,
normalizedHost(uri), effectivePort(uri), normalizedTransport)
```
URI path, query, priority, weight, and metadata are public Endpoint payload but
do not participate in that natural key. There is no public Endpoint id.
A runtime Version binding contains:
| Field | Meaning |
| --- | --- |
| `runtimeVersion` | Actual running implementation Version. |
| `versionRange` | Agent Versions served by the publication. |
An absent range is normalized to exact `[runtimeVersion]`. A range is one
Maven-style continuous interval whose boundaries and comparisons use the
case-sensitive Agent Version rules, not Maven `ComparableVersion`.
Canonical forms include exact `[1.0.6]`, bounded `[1.0.0,2.0.0)`, lower-bounded
`[1.0.0,)`, and upper-bounded `(,2.0.0]`. They contain no whitespace, have at
least one bound, and use exact form when equal bounds are included. Interval
unions and discrete sets are invalid. `runtimeVersion` must match its range.
### 4.2 Publication Commands
`AgentEndpointRegistrationBatch` contains:
```text
namespaceId / agentName / runtimeVersion / versionRange? / protocol
endpoints[1..1000]
```
All Endpoints in a batch share the Version binding and protocol. A one-item
array is the generic single-Endpoint form. The command itself is not persisted.
The server validates the complete batch before applying it atomically. A
duplicate natural key rejects the batch. Registration upserts only listed
contributions and does not remove omitted Endpoints; repeating identical
content succeeds without a semantic change.
`AgentEndpointDeregistrationBatch` contains only `namespaceId`, `agentName`,
`protocol`, and `endpoints[] {uri, transport}`. For the current publisher, each
natural Endpoint key removes all of that publisher's Version-binding groups.
The caller does not submit or cache endpoint ids, runtime Versions, ranges,
metadata, priority, or weight.
### 4.3 Internal Publisher Contributions
The internal publication-group identity is:
```text
publisherIdentity
+ namespaceId + agentName + protocol
+ runtimeVersion + canonicalVersionRange
```
An Endpoint contribution identity appends the public Endpoint natural key.
This distinction is mandatory:
- one publisher may bind the same host, port, and transport through multiple
runtime-Version/range groups;
- Version bindings do not create version-specific Naming services or duplicate
the public Endpoint in a target discovery result;
- registering the same contribution identity is an upsert; and
- the new generic deregistration command removes every matching group for that
publisher and natural Endpoint.
The compatibility adapter may use an internal group-delete operation. It
deletes only one exact publication group and is not part of the public RAD
command set. The old A2A exact-Version deregistration uses this operation so it
does not remove the same publisher's contributions for another Version.
Across all live contributions, the same public natural Endpoint key must have
one canonical public Endpoint payload, regardless of publisher identity or
whether their Version ranges overlap. A registration whose URI scheme, path,
query, priority, weight, or public metadata differs from the existing payload is
rejected as a contribution conflict. Contributions may add distinct Version
bindings only when their canonical Endpoint payload is identical.
### 4.4 Bindings Aggregation
Naming publisher contributions are aggregated into an Endpoint projection
with a canonical `bindings[]` array:
```json
[
{"runtimeVersion":"1.0.6","versionRange":"[1.0.0,2.0.0)"}
]
```
The array is deduplicated and sorted in ascending Agent SemVer order by
`runtimeVersion`, then in ascending case-sensitive string order by
`versionRange`. This exact array is the Version-matching fact.
Each effective publisher record stores it under
`__nacos.agent.endpoint.bindings__`. When the array contains exactly one item,
the record also writes these diagnostic and initial-release compatibility
mirrors:
```text
__nacos.agent.endpoint.version__ = runtimeVersion
__nacos.agent.endpoint.versionRange__ = versionRange
```
When the array contains more than one item, both singular keys are removed.
Readers always use `bindings` when present and must not merge stale singular
keys into it.
`RuntimeEndpointSnapshot` aggregates publisher contributions without exposing
publisher identity. It contains exactly one item per public natural Endpoint
key, with the canonical Endpoint payload and all effective `bindings[]`. A
Version-filtered snapshot retains only matching bindings and omits an item when
none remain.
RAD discovery first filters bindings for the target Version, then aggregates
equal natural keys into one public Endpoint. Because every inconsistent payload
is rejected at write time, one target Version never produces two
different public payloads for the same natural key.
### 4.5 Pre-registration And Lifecycle
Runtime publication is independent from Agent definition creation. The server
accepts a structurally valid, authorized publication even when the Agent,
Version, or CallInterface does not exist. Registration success means runtime
intent was accepted; it does not imply current discoverability.
Registration validates AgentName, runtime Version, range, protocol, Endpoint,
authorization, capacity, and contribution conflicts. It does not validate
definition existence or Version lifecycle status.
Publisher identity is internal:
- gRPC contributions belong to a connection id;
- HTTP contributions belong to a validated client id and use one client-level
heartbeat; and
- public management and RAD objects do not expose identity or publisher count.
Disconnect or client expiration removes only that publisher's contributions.
Other equal contributions remain. Aggregate `healthy` is true when at least one
matching live contribution is healthy and false only when all are unhealthy.
Heartbeat-only and publisher-count-only changes do not change the public
projection.
`enabled` is an independent Naming operational state and is not overwritten by
heartbeats. Agent Endpoint metadata must not set Naming heartbeat interval,
heartbeat timeout, or instance-delete timeout keys. Explicit deregistration,
publisher loss, or Naming cleanup ends runtime state. Agent disable, Version
offline, or definition deletion only removes it from applicable discovery
projections.
## 5. Runtime Mapping To Naming
### 5.1 Service And Cluster Identity
The logical Naming scope is:
```text
namespaceId
+ groupName=agent-endpoints
+ serviceName=radServiceName(encodedAgentId, protocol)
+ clusterName=normalizedTransport
```
Canonical protocol tokens match
`[A-Za-z0-9][A-Za-z0-9-]{0,31}`. The service-name algorithm is:
```text
rad-<encodedAgentId.length>-<encodedAgentId>-<protocol>
```
The decimal length has no leading zero. It counts ASCII characters, which is
equivalent to Java `String.length()` for `encodedAgentId`. The result preserves
case, contains only `[A-Za-z0-9-]`, starts with an alphanumeric character, may
end with an alphanumeric character or `-`, contains no Version, and is at most
512 characters.
Examples:
```text
Nacos-Agent / a2a -> rad-11-Nacos-Agent-a2a
Nacos Agent / a2a -> rad-18-enc-Nacos-032Agent-a2a
```
The alphabet guarantees that `lb://<serviceName>` can be parsed as a Gateway
URI. It does not define a DNS name and does not lowercase the case-sensitive
Nacos service identity. An integration that normalizes service ids to lowercase
is outside this compatibility guarantee.
`clusterName` is normalized transport and matches
`[0-9A-Za-z-]{1,64}`. Transport is stored in both cluster identity and reserved
metadata and must agree on read.
### 5.2 Instance Field Mapping
| Agent runtime field | Naming field |
| --- | --- |
| `namespaceId` | Service namespace. |
| fixed group | `agent-endpoints`. |
| encoded Agent and protocol | Canonical service name from section 5.1. |
| normalized transport | `Instance.clusterName`. |
| normalized URI host and effective port | `Instance.ip`, `Instance.port`. |
| URI path | `__nacos.agent.endpoint.path__`. |
| normalized transport | `__nacos.agent.endpoint.transport__`. |
| URI scheme | `__nacos.agent.endpoint.protocol__`. |
| legacy A2A protocol version | Optional `__nacos.agent.endpoint.protocolVersion__`. |
| HTTPS state | `__nacos.agent.endpoint.supportTls__`. |
| raw URI query | `__nacos.agent.endpoint.query__`. |
| native tenant, when present | `__nacos.agent.endpoint.tenant__`. |
| canonical bindings | `__nacos.agent.endpoint.bindings__`. |
| single-binding diagnostic mirrors | `__nacos.agent.endpoint.version__`, `__nacos.agent.endpoint.versionRange__`. |
| priority | `__nacos.agent.endpoint.priority__`. |
| weight | `Instance.weight`. |
| public Endpoint metadata | Remaining `Instance.metadata`. |
| runtime state | `Instance.enabled`, `Instance.healthy`, `ephemeral=true`. |
User metadata must not override any `__nacos.agent.endpoint.*__` key. The server
constructs and validates the complete Naming metadata before accepting a
publication. Missing range input is canonicalized before writing `bindings`.
`__nacos.agent.endpoint.protocolVersion__` is a legacy-only compatibility fact.
Only the A2A compatibility adapter writes it. It is excluded from public RAD
Endpoint metadata and Runtime revision input. When projecting an old A2A
response, the adapter prefers this value and falls back to the target
CallInterface `protocolVersion` when it is absent. The aggregate writes this
singular key only while every represented A2A contribution reports the same
value; if values differ, it removes the key and each exact-Version projection
uses its target CallInterface fallback. A disagreement is not a public Endpoint
payload conflict.
The public natural key maps to service, cluster, IP, and port. Path and query
remain payload metadata. No Version appears in serviceName or clusterName, so
the service count does not grow with compatible Agent Versions.
### 5.3 Naming Fact Boundary
Naming Client publisher contributions, including their canonical bindings, are
the RUNTIME fact source. Ordinary Naming `ServiceInfo` may collapse equal IP and
port entries, apply selector or health-protection behavior, and cannot preserve
all Agent publication groups. It is not a RAD fact source.
Agent runtime reads aggregate raw publisher contributions from the Naming
Client/index path, then apply binding and enabled filters. They must not forward
a standard Naming Java SDK subscription result as a RAD watch snapshot.
Operational Naming metadata for `enabled` and `weight` has its normal
precedence over runtime publication values. The Agent projection still retains
unhealthy instances and exposes their raw aggregate health; it does not apply
Naming health-protection fallback.
## 6. Runtime Discovery Projection
A RUNTIME Endpoint is eligible for one target discovery result only when:
1. the Agent exists, is visible, and is enabled;
2. the target Version is online;
3. the target Version has the same protocol CallInterface and permits the
`RUNTIME` source;
4. at least one effective binding contains the target Version; and
5. the Naming Endpoint has `enabled=true`.
An eligible Endpoint with `healthy=false` remains in RAD output. SDK
`selectOneHealthy` filters it; get-all and watch retain it. A disabled Endpoint
is absent.
The projection uses the target Version's CallInterface for protocol version,
descriptor, and endpoint-source order. Runtime contributions never override
those definition fields; the legacy-only Naming protocol-version metadata is
ignored by RAD.
## 7. Runtime Source Revision
For each
`(namespaceId, agentName, targetVersion, protocol, source=RUNTIME)`, the server
generates an opaque `sourceRevision` after it:
1. aggregates live publisher contributions;
2. selects bindings that contain the target Version;
3. validates one canonical payload per natural key;
4. removes `enabled=false` and retains both health states;
5. sorts Endpoints by natural key and metadata by key; and
6. computes MurmurHash3 x64 128 over canonical bytes.
The external token is:
```text
murmur3-x64-128-v1:<32 lowercase hex>
```
Canonical input contains URI, transport, effective priority and weight, public
Endpoint metadata, and `healthy`. It excludes runtimeVersion, versionRange,
publisher identity and count, heartbeat time, last-updated time, and Naming
internal revisions. Runtime Version and range do not enter the hash because the
target projection has already filtered them. Range or enabled changes alter
membership; health changes alter returned content. Both therefore advance the
revision when the target projection changes.
The empty set has a stable revision. An additional or removed redundant
publisher does not change it. The token is only cache equality and watch
deduplication; it is not identity, authorization, CAS, or tamper protection.
All nodes use seed `0`. Canonical bytes start with an unsigned four-byte
big-endian Endpoint count. For each ordered Endpoint, the six included fields
are encoded in the stated order as RFC 8785 JSON UTF-8 and each field is
prefixed with its unsigned four-byte big-endian byte length. The empty set is
exactly `uint32be(0)`. The Murmur result emits `h1` followed by `h2`, each as an
unsigned eight-byte big-endian value, and then lowercase hexadecimal. These
rules are also machine-readable in internal storage schema version 1.
Implementations mark semantic projections dirty, coalesce bursts, and cache
the result. They must not hash every heartbeat or every discovery read.
Persistent AgentVersion content continues to use SHA-256. A DECLARED endpoint
set uses the Version `contentDigest` as its opaque source revision.
## 8. Read, Write, Cache, And Consistency Paths
| Read | Facts read | AI Storage read |
| --- | --- | :---: |
| Management Agent list or RAD Search | `ai_resource` page. | No |
| Agent overview | Resource plus bounded Version-row page. | No |
| Exact Version detail | One Version row. | One |
| Runtime Endpoint snapshot | Raw Naming publisher contributions for one protocol; optional binding filter. | No |
| RAD Discover | Resource, online Version, cached content, and eligible runtime projection. | Once on digest miss |
| Change | Write target | Consistency rule |
| --- | --- | --- |
| Agent catalog, governance, extensions | `ai_resource`. | `metaVersion` CAS. |
| Create or update draft | AI Storage fixed key plus Version row. | Pointer, bytes, size, and digest agree. |
| Publish, online, offline, delete, label/latest | Version row plus Resource summaries. | Rebuild derived catalog and protocol tokens. |
| Runtime register, heartbeat, deregister | Naming Client runtime state. | Does not write AI Resource or Storage. |
Cache validators follow facts:
| Fact | Validator |
| --- | --- |
| Agent metadata | `metaVersion`. |
| Agent Version content | `contentDigest`. |
| Target runtime projection | `sourceRevision`. |
An AI Storage provider guarantees atomic bytes for one StorageKey and the read
consistency it declares. Agent Registry owns orchestration across Resource,
Version, Storage pointer, digest, and derived catalog. It performs validation,
idempotent retry, and failure compensation. Publish must reread content and
validate the digest.
A successful Storage write followed by a failed metadata write produces an
observable incomplete operation that is retried or cleaned as orphan content.
Digest mismatch must never return unverified content. `versionCatalog`,
protocol tokens, and Resource version summaries are rebuildable derived data;
their consistency is not delegated to Storage providers.
## 9. Capacity And Security
| Runtime or physical field | Limit |
| --- | ---: |
| `runtimeVersion` | 64 characters. |
| Canonical `versionRange` | 256 characters; one continuous interval. |
| Registration batch | 1 to 1000 Endpoints. |
| Runtime Endpoints per Agent and protocol | 1000, subject to a lower cluster quota. |
| Final Endpoint metadata | 32 public items; key 64 and value 256 characters. |
| Final Naming metadata | Sum of Java `String.length()` for keys and values is 1024. |
| Agent Version physical Config data id | 255 characters, enforced by `NacosAiConfigKeyCodec`; an overlong logical id uses its SHA-256 fallback. |
| Agent Version content | 1 MiB. |
The server validates the complete generated metadata, including reserved keys,
before writing Naming. It rejects an overflow and never truncates or silently
drops fields.
AI Storage content, Endpoint metadata, and publisher state must not contain
plaintext credentials. Logs and audit events must not expose complete native
descriptors, security schemes, publisher identities to ordinary users, or
sensitive Endpoint metadata.
## 10. A2A Runtime Compatibility Boundary
The A2A adapter is the first consumer of this storage contract:
| Legacy A2A fact | New storage projection |
| --- | --- |
| AgentCard definition | A2A `AgentCallInterface.nativeDescriptor` in Version content. |
| Root and additional interfaces | Adapter-derived DECLARED Endpoints. |
| Runtime AgentEndpoint Version | `runtimeVersion=version`, `versionRange=[version]`. |
| Runtime calling protocol | Canonical Agent protocol token `a2a`. |
| Legacy endpoint transport and URI parts | Common Endpoint and reserved Naming metadata. |
Old single and batch registrations keep an exact-Version replacement scope:
```text
(publisherIdentity, namespaceId, agentName, protocol=a2a,
runtimeVersion=version, versionRange=[version])
```
The compatibility adapter replaces that internal group. Old deregistration
deletes only that exact group, even when the same publisher and physical
Endpoint have bindings for other Versions. New RAD deregistration instead
deletes all bindings for the supplied natural Endpoint under the current
publisher.
After cutover, compatibility writes use the new AI Resource, AI Storage, and
Naming layouts. Historical Config rows, historical Naming services, mixed
cluster dual-read or dual-write, cutover, rollback, and malformed historical
identity handling belong to a separate rolling-upgrade and migration contract.
+15 -13
View File
@@ -28,7 +28,7 @@ AI Registry owns:
- AI resource metadata, versions, labels, status, scope, owner, and business
tags;
- resource type contracts for MCP Server, A2A Agent, Prompt, Skill, and
- resource type contracts for MCP Server, Agent, Prompt, Skill, and
AgentSpec;
- runtime query and subscription behavior for supported AI resources;
- management workflows such as draft creation, review, publish, force publish,
@@ -40,7 +40,7 @@ AI Registry does not own:
- Config resource semantics, even when the default AI storage implementation
stores resource content through Config;
- Naming service semantics, even when MCP or A2A endpoints are represented by
- Naming service semantics, even when MCP or Agent endpoints are represented by
Naming services and instances;
- community registry protocol definitions exposed by the
[AI Registry Adaptor Spec](ai-registry-adaptor-spec.md);
@@ -92,17 +92,18 @@ Detailed field and lifecycle rules are defined by the
## 4. Resource Type Inventory
| Type | Standard identity | Current persistence shape | Spec |
| Type | Standard identity | Current or approved target persistence shape | Spec |
| --- | --- | --- | --- |
| `mcp` | `namespaceId -> mcp -> mcpName` | Currently uses Config records for MCP metadata/version/tool/resource data and Naming services for endpoints. | [MCP Server Spec](mcp-server-spec.md) |
| `a2a` | `namespaceId -> a2a -> agentName` | Currently uses Config records for AgentCard metadata/version data and Naming services for endpoints. | [A2A Agent Spec](a2a-agent-spec.md) |
| `agent` | `namespaceId -> agent -> agentName` | Approved target: `ai_resource`, `ai_resource_version`, AI storage, and Naming-backed runtime endpoint publications. Historical A2A storage remains a compatibility source until migration. | [Agent Management Spec](agent-management-spec.md) |
| `prompt` | `namespaceId -> prompt -> promptKey` | Uses `ai_resource`, `ai_resource_version`, and AI storage; legacy Prompt data may be migrated. | [Prompt Spec](prompt-spec.md) |
| `skill` | `namespaceId -> skill -> name` | Uses `ai_resource`, `ai_resource_version`, AI storage, and a lightweight manifest for discovery. | [Skill Spec](skill-spec.md) |
| `agentspec` | `namespaceId -> agentspec -> name` | Uses `ai_resource`, `ai_resource_version`, and AI storage. | [AgentSpec Spec](agentspec-spec.md) |
MCP and A2A are AI Registry resources even when their current persistence is not
fully adapted to `ai_resource`. Their canonical specs must be written against
the standard identity and must record current compatibility storage separately.
An A2A AgentCard is a protocol binding inside an `agent` version. The historical
`a2a` resource identity and APIs are compatibility facades described by the
[A2A Agent Spec](a2a-agent-spec.md); they must not create a second canonical
Agent identity.
## 5. Interface Surfaces
@@ -113,7 +114,7 @@ AI Registry is exposed through multiple surfaces:
| `/v3/client/ai/...` | Runtime clients and agent frameworks. | Query known resources, download runtime artifacts, subscribe, and register client-owned endpoints. |
| `/v3/admin/ai/...` | Management tools and Maintainer SDK. | Create, update, list, publish, delete, upload, import, and operate versions. |
| `/v3/console/ai/...` | Nacos console UI. | UI orchestration over the same domain semantics. |
| gRPC AI requests | Java Client SDK runtime traffic. | Query and release MCP/A2A/Prompt resources and register endpoints where supported. |
| gRPC AI requests | Java Client SDK runtime traffic. | Query AI resources, perform RAD discovery and subscription, and publish client-owned endpoints where supported. |
| Java SDK | Runtime application integration. | See the [Java SDK Implementation Spec](../sdk/sdk-java-impl-spec.md). |
| Java Maintainer SDK | Typed management integration. | Should align with Admin API semantics and the resource type specs. |
| AI Registry adaptor | External community registry clients. | Optional compatibility endpoints on a separate port; see the [AI Registry Adaptor Spec](ai-registry-adaptor-spec.md). |
@@ -143,13 +144,14 @@ AI Registry is exposed through multiple surfaces:
- MCP Server should migrate its durable metadata and version model from
Config-shaped records to the standard `ai_resource` and `ai_resource_version`
model while preserving existing data compatibility.
- A2A Agent should migrate AgentCard metadata and version data from
Config-shaped records to the standard AI resource model.
- Historical A2A AgentCard and Naming endpoint data must migrate to the Agent
model through the rolling-upgrade plan; the legacy APIs remain projections,
not an independent resource store.
- Prompt has a migration path from legacy Config-shaped Prompt data to the
standard AI resource model. Legacy mappings must remain compatibility
storage, not formal Config resource semantics.
- Endpoint selection policy for A2A currently uses random choice among
compatible endpoints. A future spec should define pluggable or deterministic
endpoint selection if needed.
- RAD returns a deterministic endpoint set. Health filtering, priority/weight
selection, and load balancing are client-side policies and do not change the
Registry snapshot.
- AI resource schemas and protocol-specific payloads may require major revision
as upstream MCP, A2A, and agent package ecosystems evolve.
+14 -4
View File
@@ -86,7 +86,8 @@ operation. It accepts only `draft`, `reviewing`, and `reviewed` versions;
must explicitly redraft the version when further editing is required after a
rejected result.
- Publish moves the version to `online`, clears working pointers, increments
`onlineCnt` when needed, and the server manages the `latest` label.
`onlineCnt` when needed, and the server manages the `latest` label according
to the resource type spec.
- Publish and force-publish requests may keep the historical
`updateLatestLabel` parameter for compatibility. This parameter is deprecated;
new clients must not send it. When it is absent or `true`, the published
@@ -96,9 +97,18 @@ operation. It accepts only `draft`, `reviewing`, and `reviewed` versions;
map.
- Force publish applies the same successful state transition as publish while
skipping pipeline approval checks.
- After any online/offline status change, the server must recalculate `latest`
from the current online versions and point it to the greatest online version.
If no online version remains, the server must remove `latest`.
- Unless a type spec defines a deterministic refinement, a successful publish
or online operation makes the target version `latest`. When the current
latest version is deleted or taken offline, the default replacement is the
greatest remaining online version; if no online version remains, the server
removes `latest`. A type refinement must still keep `latest` server-managed,
point it to an online version, and define deletion/offline fallback.
- The Agent type refines this rule only for its legacy A2A direct-online
facade: `setAsLatest=false` may preserve the current valid pointer. Standard
Agent publish and online operations still move `latest`, and deletion or
offline of the current pointer selects the greatest remaining online Agent
version. See the [Agent Management Spec](agent-management-spec.md) and the
[A2A Agent Spec](a2a-agent-spec.md).
Pipeline extension behavior is defined by the
[AI Publish Pipeline Plugin Spec](../plugin/ai-pipeline-plugin-spec.md). This
+10 -1
View File
@@ -44,7 +44,7 @@ still `resourceName`.
| Field | Meaning |
| --- | --- |
| `namespaceId` | Namespace isolation boundary. |
| `type` | Resource type, such as `prompt`, `skill`, or `agentspec`. |
| `type` | Resource type, such as `agent`, `prompt`, `skill`, or `agentspec`. |
| `name` | Stable resource name. |
| `desc` | Resource description. |
| `status` | Resource metadata status, currently `enable` or `disable`. |
@@ -109,6 +109,15 @@ Storage extension behavior is defined by the
[AI Storage Plugin Spec](../plugin/ai-storage-plugin-spec.md). Database dialect
behavior is defined by the [Data Source Dialect Plugin Spec](../plugin/datasource-dialect-plugin-spec.md).
Type-owned JSON must have an explicit schema contract. For `type=agent`, `ext`
contains the directory extension and derived online-version catalog, while the
version `storage` points to one complete Agent version content object. Exact
fields and rebuild rules are defined by the
[Agent Management Spec](agent-management-spec.md) and the
[Agent Storage Spec](agent-storage-spec.md). Runtime Agent endpoints are not
stored in `AiResourceVersion.storage` because they follow a client-owned Naming
lifecycle.
## 6. Visibility
AI resources implement visibility through the shared visibility plugin model.
+705
View File
@@ -0,0 +1,705 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Remote Agent Discovery Protocol Spec
| Item | Value |
|---|---|
| Status | Experimental; normative for protocol version `0.1.0` |
| Protocol version | `0.1.0` |
| Scope | Remote Agent search, discovery, watch, and runtime endpoint publication |
| Goal | Return Agent calling descriptors and currently available addresses through a small, stable model |
This document defines the transport-independent core semantics of the Nacos
Remote Agent Discovery (RAD) protocol. HTTP, gRPC, and SDK bindings may use
native types, but their wire fields and observable behavior MUST be equivalent
to this specification.
## 1. Positioning And Scope
RAD answers two questions:
1. Which Agents may satisfy a requirement when the target is not known?
2. Which calling protocols and endpoints are available for a selected Agent
version?
RAD returns metadata needed to call a remote Agent. It does not proxy the call
and does not define Agent message, task, or session protocols.
### 1.1 Operations
RAD 0.1.0 defines five operations:
| Operation | Input | Output | Semantics |
|---|---|---|---|
| `Search` | `AgentSearchRequest` | `AgentCatalogPage` | Search candidate Agents with pagination |
| `Discover` | `AgentDiscoveryRequest` | `AgentDiscoveryResult` | Return one complete calling snapshot for an Agent version |
| `Watch` | `AgentDiscoveryRequest` | `AgentDiscoveryResult` stream | Return the initial and subsequent complete replacement snapshots |
| `Register` | `AgentEndpointRegistrationBatch` | Success or error | Register or update runtime endpoints |
| `Deregister` | `AgentEndpointDeregistrationBatch` | Success or error | Deregister runtime endpoints |
`Watch` reuses the `Discover` request and result. RAD does not add an event
envelope around watched snapshots.
### 1.2 Out Of Scope
RAD 0.1.0 does not define Agent management lifecycle, client connection and
reconnection, internal storage, historical compatibility, MCP, call proxying,
credentials, retries, or load balancing. Agent resource and version semantics
are defined by the [Agent Management Spec](./agent-management-spec.md).
## 2. Common Constraints
### 2.1 Namespace
Every operation executes in exactly one effective namespace.
- A top-level request carries `namespaceId` once.
- Nested Agent references, filters, and endpoints do not repeat it.
- A binding may obtain the value from client configuration or request context.
It MUST normalize the default namespace to `public` before entering RAD core
semantics.
- Cache, watch, authorization, and publisher-contribution keys MUST include the
effective namespace.
`namespaceId` contains 1 to 64 characters from `[A-Za-z0-9_-]`.
### 2.2 Agent, Protocol, And Label Identity
The public Agent identity is `(namespaceId, agentName)`.
`agentName` MUST:
- contain 1 to 64 printable ASCII characters;
- contain at least one non-space character;
- be compared case-sensitively and verbatim;
- not be trimmed, lowercased, slugged, or otherwise rewritten.
`protocol` contains 1 to 32 characters and matches
`[A-Za-z0-9][A-Za-z0-9-]{0,31}`. `label` contains 1 to 64 characters and
matches `[A-Za-z0-9][A-Za-z0-9._-]{0,63}`. Both are case-sensitive.
`latest` is a reserved label that resolves to the Agent's current latest
version. It MUST NOT appear in `AgentVersionCatalog.labels`.
### 2.3 Agent Version
An Agent version uses `MAJOR.MINOR.PATCH[-PRERELEASE]` and has a maximum length
of 64 characters. Numeric core identifiers MUST NOT contain leading zeroes.
Prerelease identifiers are dot-separated `[0-9A-Za-z-]+` values. A prerelease
identifier containing only digits MUST NOT contain leading zeroes unless it is
exactly `0`.
RAD 0.1.0 does not accept build metadata. Version identity and comparison are
case-sensitive. Ordering follows SemVer precedence and MUST NOT first convert
the version to a fixed-width integer.
### 2.4 Version Range
`versionRange` uses Maven/POM-style interval brackets, but every boundary is an
Agent version from Section 2.3 and comparison uses RAD SemVer rather than
Maven `ComparableVersion`.
| Form | Match rule |
|---|---|
| `[1.0.6]` | Match only `1.0.6` |
| `[1.0.0,1.0.6]` | `1.0.0 <= version <= 1.0.6` |
| `[1.0.0,2.0.0)` | `1.0.0 <= version < 2.0.0` |
| `[1.0.0,)` | `version >= 1.0.0` |
| `(,2.0.0)` | `version < 2.0.0` |
RAD 0.1.0 accepts one exact version or one continuous interval. It does not
accept a union of versions or intervals. An expression contains no spaces and
has at least one boundary. A missing lower boundary uses `(` and a missing
upper boundary uses `)`.
When both boundaries exist, the lower boundary MUST precede the upper boundary.
Equal boundaries are valid only when both ends are inclusive; the server then
canonicalizes `[version,version]` to `[version]`. Every other equal-boundary
form is invalid. An interval such as `[1.0.0,2.0.0)` promises only the stated
SemVer comparisons; prerelease versions are evaluated by SemVer precedence.
The server stores and compares the canonical form.
### 2.5 Protocol Version Negotiation
A binding declares support for RAD 0.1.0 through its documentation or Nacos
capability negotiation. RAD root messages do not carry a protocol-version or
schema-version field.
## 3. Public Model
### 3.1 Root Messages
The schema exposes exactly six root messages:
| Root message | Purpose |
|---|---|
| `AgentSearchRequest` | `Search` request |
| `AgentCatalogPage` | `Search` result |
| `AgentDiscoveryRequest` | `Discover` and `Watch` request |
| `AgentDiscoveryResult` | `Discover` and `Watch` complete snapshot |
| `AgentEndpointRegistrationBatch` | `Register` request |
| `AgentEndpointDeregistrationBatch` | `Deregister` request |
A language binding may reuse an exactly equivalent native type. For example,
Java may implement `AgentCatalogPage` as `Page<AgentCatalogEntry>` rather than
introducing another page class.
### 3.2 Common JSON Rules
- An absent optional value is omitted rather than represented as `null`.
- Ordinary objects reject unknown properties.
- Only `nativeDescriptor` and explicitly declared `metadata` maps are open
content.
- An optional request array contains at least one item when present. An empty
response collection is explicitly returned as `[]`.
- An empty filter object `{}` means no filtering.
- An empty metadata object `{}` is canonicalized to field omission. An empty
`metadataSelector` is equivalent to no metadata filtering.
### 3.3 `AgentSearchRequest`
| Field | Required | Semantics |
|---|:---:|---|
| `namespaceId` | Yes | Effective namespace |
| `agentNameContains` | No | Case-sensitive literal substring match on `agentName` |
| `tagsAll[]` | No | Agent contains every supplied tag |
| `protocolsAny[]` | No | At least one online version contains any supplied protocol |
| `pageNo` | No | One-based page number; default `1` |
| `pageSize` | No | Page size; default `20`, maximum `100` |
Characters such as `%` and `_` that are special to a backing query language
MUST be treated as literals.
### 3.4 `AgentCatalogPage`, `AgentCatalogEntry`, And `AgentVersionCatalog`
`AgentCatalogPage` contains:
```text
totalCount / pageNumber / pagesAvailable / pageItems[]
```
Each `pageItems[]` entry is an `AgentCatalogEntry` relative to the request
namespace:
```text
agentName / displayName? / description? / iconUrl? / provider?
tags? / latestVersion
versions[] AgentVersionCatalog {
version
labels[]?
protocols[]
}
```
Rules:
- `versions` lists every online version in descending SemVer order. Versions do
not repeat, and `protocols` contains at least one unique value.
- There is no product-level hard limit on the number of online versions in an
entry. The list MUST NOT be silently truncated. A binding's global response
size limit still applies and produces its standard oversized-response error.
- A non-reserved label points to at most one version. `latest` MUST NOT appear
in `labels`, and `latestVersion` MUST match one listed `version`.
- The entry does not repeat `namespaceId` and does not return protocol
descriptors, endpoints, health, or management fields.
- Search does not promise a currently healthy endpoint. Discover determines
current callability.
### 3.5 `AgentReference`
| Field | Required | Semantics |
|---|:---:|---|
| `agentName` | Yes | Agent name in the effective namespace |
| `version` | No | Select one online exact version |
| `label` | No | Resolve a label to one online version at request time |
`version` and `label` are mutually exclusive. When both are absent, or when
`label` is `latest`, the reference resolves the current latest version.
### 3.6 `AgentDiscoveryFilter`
Every filter field is optional:
| Field | Semantics |
|---|---|
| `protocols[]` | Allowed calling protocols |
| `protocolVersion` | Exact match against candidate interfaces |
| `transports[]` | Allowed transports |
| `endpointSources[]` | Allowed `RUNTIME` or `DECLARED` sources |
| `metadataSelector` | Endpoint metadata contains every exact key/value pair |
Values within one array are ORed; distinct fields are ANDed. A filter only
prunes one discovery result. It does not select another Agent version and does
not perform load balancing.
### 3.7 `Endpoint`
All operations reuse one `Endpoint` model:
| Field | Required | Semantics |
|---|:---:|---|
| `uri` | Yes | Complete absolute calling URI, at most 2048 characters |
| `transport` | Yes | Canonical transport; 1 to 64 `[0-9A-Za-z-]` characters |
| `priority` | No | Lower is preferred; integer `0..2147483647`, default `0` |
| `weight` | No | Weight within a priority; number `0..10000`, default `1` |
| `metadata` | No | At most 32 flat string key/value entries |
| `healthy` | Conditionally | Present only and always in a `RUNTIME` discovery result |
Context rules:
- Register MUST NOT submit `healthy`.
- A `DECLARED` endpoint MUST NOT contain `healthy`.
- A `RUNTIME` discovery endpoint MUST contain `healthy`.
- Deregister submits only `uri` and `transport`, the endpoint natural-key
fields represented by the public object.
Runtime endpoints do not use `endpointId`.
### 3.8 Endpoint Natural Key And Normalization
The runtime endpoint natural key is:
```text
(namespaceId, agentName, protocol,
normalizedHost(uri), effectivePort(uri), normalizedTransport)
```
Path, query, metadata, priority, and weight do not participate in identity.
Two endpoints in the same group cannot coexist only by using different paths.
Normalization rules:
- A URI contains a scheme and host and has an explicit or inferable effective
port. `http` and `ws` infer port `80`; `https` and `wss` infer port `443`.
Every other scheme requires an explicit port.
- A URI MUST NOT contain user-info or a fragment.
- Scheme and DNS host are lowercased; a DNS host uses an ASCII A-label.
- IPv4 and IPv6 use stable text forms.
- The output URI includes the effective port explicitly.
- Transport uses the Registry-accepted canonical value and is not
automatically case-folded.
- Priority and weight are materialized as `0` and `1` before comparison.
- Metadata keys are sorted before comparison; map order does not affect
equality.
### 3.9 `EndpointSet`
Declared and runtime sources share one object:
```text
EndpointSet {
source = DECLARED | RUNTIME
sourceRevision
endpoints[]
}
```
`source` determines the `healthy` constraint. `AgentDiscoveryResult` does not
return `endpointSourceOrder`. The Registry emits `endpointSets[]` in the source
order declared by the selected Agent version and preserves the relative order
of remaining sources after filtering. A declared but currently empty source is
returned with `endpoints=[]` and a stable `sourceRevision`.
### 3.10 `AgentDiscoveryCallInterface` And `AgentDiscoveryResult`
```text
AgentDiscoveryResult
├── namespaceId / agentName / version / contentDigest
└── callInterfaces[] AgentDiscoveryCallInterface
├── protocol / protocolVersion?
├── descriptorMediaType / nativeDescriptor
└── endpointSets[]
├── source / sourceRevision
└── endpoints[]
```
`AgentDiscoveryCallInterface` is a data-plane projection. It is intentionally
different from the management-plane `AgentCallInterface` defined by the
[Agent Management Spec](./agent-management-spec.md): the discovery view omits
management and source-order fields and contains resolved endpoint sets.
Rules:
- `version` is the online exact version resolved from `AgentReference`.
- One version has at most 16 calling interfaces. Protocols do not repeat.
- Calling interfaces retain their order in the Agent version definition.
- `nativeDescriptor` is any non-null JSON value.
- `descriptorMediaType` describes `nativeDescriptor`.
- `endpointSets[]` is authoritative for this discovery snapshot. Addresses
inside `nativeDescriptor` MUST NOT override it.
- The result does not return display fields, owner, scope, extensions, or
publisher identity.
### 3.11 Digest And Revision
`contentDigest` identifies the complete immutable version content:
- It is `sha256:` followed by 64 lowercase hexadecimal characters.
- It covers ordered calling interfaces, `nativeDescriptor`, internal source
order, and declared endpoints.
- It excludes status, latest, labels, management metadata, and runtime
endpoints.
- A consumer compares the complete value and does not calculate it.
Each `(namespaceId, agentName, version, protocol, source)` has one
`sourceRevision`:
- It is an opaque equality token. It cannot be ordered or compared across
scopes.
- It changes when endpoint membership, URI, transport, priority, weight,
public metadata, or health changes.
- Heartbeat time, publisher count, or an internal storage revision does not by
itself require a change.
- An empty endpoint set still has a stable revision.
- A `DECLARED` set uses the Version `contentDigest`. A Nacos `RUNTIME` set uses
`murmur3-x64-128-v1:<32 lowercase hex>` generated by the deterministic
projection contract in the [Agent Storage Spec](agent-storage-spec.md).
Consumers still treat both forms as opaque and do not calculate them.
### 3.12 Endpoint Batches
`AgentEndpointRegistrationBatch` contains:
```text
namespaceId / agentName / runtimeVersion / protocol
versionRange? # default: exact range [runtimeVersion]
endpoints[] # 1..1000
```
`runtimeVersion` is the deployed implementation version. `versionRange`
describes Agent versions that the deployment can serve. When absent, the
server canonicalizes it to `[runtimeVersion]`. `runtimeVersion` MUST be
contained in the effective range.
`AgentEndpointDeregistrationBatch` contains:
```text
namespaceId / agentName / protocol
endpoints[] { uri, transport }
```
For every supplied natural key, Deregister removes all bindings contributed by
the current publisher, including bindings from different internal
`runtimeVersion` and `versionRange` groups.
## 4. Search
Search MUST:
1. return only visible, enabled Agents with at least one online version and a
valid latest version;
2. apply `agentNameContains`, `tagsAll`, and `protocolsAny`;
3. sort by the original `agentName` in case-sensitive ascending ASCII order;
4. provide stable pagination for the same request and data snapshot;
5. return `totalCount`, `pageNumber`, `pagesAvailable`, and `pageItems`;
6. avoid loading or returning complete descriptors and endpoints.
`pageNo` defaults to `1`; `pageSize` defaults to `20` and is at most `100`.
## 5. Discover
`AgentDiscoveryRequest` contains:
```text
namespaceId
reference: AgentReference
filter?: AgentDiscoveryFilter
```
The Registry performs Discover in this order:
1. Find `agentName` verbatim in the effective namespace.
2. Resolve the target version using `version`, `label`, or latest.
3. Verify visibility, Agent enabled state, and target online state.
4. Load calling interfaces in version-definition order.
5. Retain runtime publications whose `versionRange` contains the target exact
version.
6. Exclude `enabled=false` runtime instances and retain both
`healthy=true` and `healthy=false` instances.
7. Aggregate matching contributions with the same public endpoint natural key.
8. Apply the optional filter.
9. Return a complete snapshot ordered by calling interface, source, priority,
and stable natural key.
The fixed shapes for an empty filtered result are:
| Unmatched level | Result shape |
|---|---|
| `protocols` or `protocolVersion` | `callInterfaces=[]` |
| `endpointSources` | Keep the interface and return `endpointSets=[]` |
| `transports` or `metadataSelector` | Keep the endpoint set and return `endpoints=[]` |
A runtime endpoint with `healthy=false` remains in the result. Selecting only
healthy instances, applying priority and weight, and defining fallback when no
healthy instance exists are consumer concerns.
## 6. Watch
Watch uses the same request and result as Discover.
- The Registry first evaluates Discover. If it returns `NOT_FOUND`, no watch is
created.
- A successful Watch first emits the current complete
`AgentDiscoveryResult`.
- Every later notification is another complete replacement result, without an
event envelope.
- A changed resolved version, `contentDigest`, or any `sourceRevision` produces
a new snapshot.
- Matching runtime registration, update, deregistration, or liveness changes
produce a snapshot when the public projection changes.
- Internal changes that do not change the public projection SHOULD NOT produce
duplicate notifications.
- If a previously discoverable target becomes `NOT_FOUND`, the binding sends a
terminal `NOT_FOUND` status and closes the watch.
- A consumer atomically replaces its previous snapshot with each new result.
- Subscriber identity, acknowledgement, reconnect, replay, and backpressure
are binding concerns.
A binding may use its own transport envelope for snapshot and terminal
delivery. Such an envelope is not a RAD public model and does not extend the
six root messages in Section 3.1.
The equivalent cancellation key includes `namespaceId`, canonical
`AgentReference`, filter, and subscriber identity.
## 7. Register And Deregister
### 7.1 Validation And Pre-registration
Register verifies:
- request structure, endpoint constraints, authorization, and capacity;
- valid `runtimeVersion` and `versionRange`, with the runtime version contained
in the range;
- no duplicate natural key in one batch and no publication conflict described
in Section 7.3;
- the request does not submit or overwrite `protocolVersion`.
Register does not require the Agent, the runtime version, a range boundary, a
version within the range, or a corresponding calling interface to exist. It
therefore supports endpoint pre-registration.
Pre-registration creates only a runtime publication. It does not implicitly
create an Agent, version, or calling interface and does not enter ordinary
Discover early. Discover still requires a visible and enabled Agent, an online
target version, and a calling interface that allows the `RUNTIME` source. It
uses the target version's descriptor and `protocolVersion`.
Agent and version definitions do not own publication lifecycle. Creating,
publishing, taking offline, or deleting a definition changes only the
discovery projection. Register, Deregister, and publisher liveness manage the
publication itself.
### 7.2 Batch, Idempotency, And Atomicity
One registration batch belongs to one:
```text
(namespaceId, agentName, protocol, runtimeVersion, versionRange)
```
Rules:
- The Registry validates all endpoints before atomically applying one batch.
- Register adds or updates only listed natural keys and does not replace
omitted endpoints.
- Repeating identical content for the same publisher succeeds without a
change.
- A changed non-identity field performs an upsert.
- A duplicate natural key within one batch rejects the whole batch.
- Deregister removes only the current publisher's contributions.
- Deregistering a missing contribution succeeds without a change.
- Transactions across batches are not guaranteed.
A single-endpoint operation uses an `endpoints[]` of length one; RAD does not
define separate single-item commands.
### 7.3 Publication Groups And Multiple Publishers
A binding supplies an opaque publisher identity and liveness semantics. The
identity does not enter discovery results.
The Registry may keep multiple internal publication groups for one public
endpoint natural key, distinguished by `runtimeVersion` and canonical
`versionRange`. A publisher may therefore contribute the same public endpoint
through more than one compatible-version declaration. Discover first retains
groups matching its target version and then aggregates them to one public
endpoint.
Contributions that can project to the same public endpoint MUST agree on the
canonical URI, transport, priority, weight, and metadata. A conflicting
registration returns `CONFLICT`. Health is aggregated across matching active
contributions:
- at least one healthy contribution produces `healthy=true`;
- all contributions unhealthy produces `healthy=false`;
- removing one publisher removes only its contributions;
- a publisher-count change that leaves the public endpoint unchanged does not
change `sourceRevision`.
## 8. Ordering And Capacity
Calling interfaces use version-definition order. Endpoint sets use declared
source order. Endpoints sort by ascending priority and then stable natural key.
Health does not change order, and weight does not participate in Registry
sorting.
One version has at most 16 calling interfaces. One declared endpoint set has at
most 64 endpoints. One runtime endpoint set and one endpoint batch each have at
most 1000 endpoints. The online-version list has no separate product limit and
follows the response-size rule in Section 3.4.
## 9. Binding Profiles
A binding advertises the profiles and optional capabilities it supports:
| Profile or capability | Required operations |
|---|---|
| Consumer profile | `Search`, `Discover` |
| Publisher profile | `Register`, `Deregister` |
| Watch capability | `Watch` |
A conforming binding implements at least one profile. Watch is optional at the
RAD core level. The Nacos HTTP binding implements the Consumer and Publisher
profiles but does not implement Watch. The Nacos full gRPC profile implements
all five operations.
## 10. Error Semantics
A binding maps these abstract categories to its concrete response model:
| Category | Typical case |
|---|---|
| `INVALID_ARGUMENT` | Invalid field, mutually exclusive fields, duplicate natural key, invalid range, or runtime version outside its range |
| `NOT_FOUND` | Discover target is absent, invisible, disabled, or not online; watched target later disappears |
| `PERMISSION_DENIED` | Caller cannot operate in the target namespace |
| `RESOURCE_EXHAUSTED` | Endpoint or publication capacity is full, or a complete response exceeds a binding limit |
| `CONFLICT` | Publication contents conflict or a concurrent state conflicts |
| `UNSUPPORTED_CAPABILITY` | Binding does not support the requested operation |
| `UNAVAILABLE` | Registry cannot currently form a trustworthy snapshot or apply a write |
An invisible resource and a nonexistent resource both appear as `NOT_FOUND`
to prevent visibility side channels. An empty filter result is not an error and
uses the shapes in Section 5.
## 11. Security
The Registry performs namespace and permission checks before every operation.
Search, Discover, and Watch also apply resource visibility. Register does not
skip authorization when the Agent definition is absent. Publisher identity is
not a credential.
Descriptors, URIs, and metadata are untrusted input and MUST NOT store
plaintext credentials. Discovery results MUST NOT expose connection ownership,
publisher identity, heartbeat data, or internal routing information. Endpoint
metadata MUST NOT use Nacos-reserved internal keys.
## 12. Schema And Evolution
The normative companion is the
[RAD 0.1.0 JSON Schema](../../schemas/ai/rad/0.1.0/rad-protocol.schema.json),
using JSON Schema Draft 2020-12. Ordinary objects use strict property sets;
only metadata maps and `nativeDescriptor` are open content. Schema defaults are
annotations; implementations materialize effective values.
Adding a field, changing `required`, widening a union, or changing an enum
requires a new RAD protocol version. Domain validation additionally verifies
SemVer, version/label exclusivity, reserved labels, endpoint natural keys,
source/health conditions, range boundaries, ordering, runtime-version
containment, batch atomicity, and capacity. JSON Schema validates only the
coarse syntax of a version-range string and does not replace domain validation.
## 13. Examples
### 13.1 Discover Request
```json
{
"namespaceId": "public",
"reference": {"agentName": "Order Agent", "label": "latest"},
"filter": {
"protocols": ["a2a"],
"transports": ["JSONRPC"],
"endpointSources": ["RUNTIME", "DECLARED"],
"metadataSelector": {"zone": "cn-hangzhou-h"}
}
}
```
### 13.2 Discover Result
```json
{
"namespaceId": "public",
"agentName": "Order Agent",
"version": "1.0.6",
"contentDigest": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"callInterfaces": [{
"protocol": "a2a",
"protocolVersion": "1.0",
"descriptorMediaType": "application/json",
"nativeDescriptor": {"name": "Order Agent", "version": "1.0.6"},
"endpointSets": [{
"source": "RUNTIME",
"sourceRevision": "murmur3-x64-128-v1:0123456789abcdef0123456789abcdef",
"endpoints": [{
"uri": "https://10.0.0.8:8443/a2a",
"transport": "JSONRPC",
"priority": 0,
"weight": 1,
"metadata": {"zone": "cn-hangzhou-h"},
"healthy": true
}]
}, {
"source": "DECLARED",
"sourceRevision": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"endpoints": []
}]
}]
}
```
### 13.3 Register Request
```json
{
"namespaceId": "public",
"agentName": "Order Agent",
"runtimeVersion": "1.0.6",
"versionRange": "[1.0.0,2.0.0)",
"protocol": "a2a",
"endpoints": [{
"uri": "https://10.0.0.8:8443/a2a",
"transport": "JSONRPC",
"metadata": {"zone": "cn-hangzhou-h"}
}]
}
```
### 13.4 Deregister Request
```json
{
"namespaceId": "public",
"agentName": "Order Agent",
"protocol": "a2a",
"endpoints": [{
"uri": "https://10.0.0.8:8443/a2a",
"transport": "JSONRPC"
}]
}
```
@@ -43,7 +43,7 @@ The current Java SDK declares support for:
| `SDK_CLIENT_FUZZY_WATCH` | Client can use fuzzy watch for Config or Naming. |
| `SDK_CLIENT_DISTRIBUTED_LOCK` | Client can use the distributed lock feature. |
| `SDK_MCP_REGISTRY` | Client can use MCP registry runtime features. |
| `SDK_AGENT_REGISTRY` | Client can use Agent and AgentCard runtime features. |
| `SDK_AGENT_REGISTRY` | Client can use the legacy A2A Agent and AgentCard runtime features. |
The current server declares support for:
@@ -53,12 +53,31 @@ The current server declares support for:
| `SERVER_FUZZY_WATCH` | Config or Naming fuzzy watch is supported. |
| `SERVER_DISTRIBUTED_LOCK` | Distributed Lock is supported. |
| `SERVER_MCP_REGISTRY` | MCP registry operations are supported. |
| `SERVER_AGENT_REGISTRY` | Agent and AgentCard registry operations are supported. |
| `SERVER_AGENT_REGISTRY` | Legacy A2A Agent and AgentCard registry operations are supported. |
| `SERVER_AGENT_CARD_V1` | A2A AgentCard 1.0 protocol fields are supported. |
Adding a new ability requires both a named key and a domain rule that explains
what behavior is gated by the ability.
### 2.1 Experimental Agent/RAD Target Abilities
The [Agent API Spec](../ai/agent-api-spec.md) approves the following target
abilities for the Nacos 3.3 line. They describe the target contract and are not
current runtime abilities until the corresponding constants and handlers are
implemented.
| Mode | Constant | Wire key | Meaning |
|---|---|---|---|
| `SERVER` | `SERVER_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | Server accepts RAD Search, Discover, and Watch payloads. |
| `SERVER` | `SERVER_AGENT_ENDPOINT_V1` | `agentEndpointV1` | Server accepts RAD runtime Endpoint publication payloads. |
| `SDK_CLIENT` | `SDK_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | SDK accepts RAD discovery push payloads. |
Ability names are scoped by mode, so the same `agentDiscoveryV1` wire key is
valid in both `SERVER` and `SDK_CLIENT` tables. Legacy
`SERVER_AGENT_REGISTRY`, `SERVER_AGENT_CARD_V1`, and `SDK_AGENT_REGISTRY`
continue to gate only the old A2A contract. They are not a fallback for any RAD
operation.
## 3. gRPC Negotiation Flow
The runtime client negotiates abilities during gRPC connection setup:
@@ -109,15 +128,29 @@ features:
- Distributed Lock must require `SERVER_DISTRIBUTED_LOCK` because the feature is
experimental and not universally available.
- AI MCP registry operations must require `SERVER_MCP_REGISTRY`.
- AI Agent and AgentCard operations must require `SERVER_AGENT_REGISTRY`.
- Legacy A2A Agent and AgentCard operations must require
`SERVER_AGENT_REGISTRY`.
- A2A AgentCard 1.0 fields should require `SERVER_AGENT_CARD_V1` or use an
explicitly documented compatibility conversion.
- RAD Search, Discover, and Watch requests must require
`SERVER_AGENT_DISCOVERY_V1`.
- RAD runtime Endpoint registration and deregistration must require
`SERVER_AGENT_ENDPOINT_V1`.
- The server may push either `SNAPSHOT` or `TERMINATED`
`AgentDiscoveryNotifyRequest` only when the client advertises
`SDK_AGENT_DISCOVERY_V1`. The ability covers acknowledgement and isolated
termination of one `watchKey`; it never authorizes closing the shared
Payload connection for a terminal Watch.
Feature code should not cache a positive ability result beyond the current
connection. It should query the runtime connection ability when the operation is
about to execute or when a cached value is known to belong to the current
connection.
After reconnect, the client must negotiate the ability again before restoring
Watch intent. A restored subscription obtains a new connection-scoped opaque
`watchKey` from `AgentSubscribeResponse`; the prior key must not be reused.
## 6. Compatibility Rules
Ability negotiation is a mixed-version compatibility mechanism. It should be
@@ -32,6 +32,7 @@ Client runtime uses several local data classes:
| Naming service-info cache | Server push or query response | Last known instances for subscribed or queried services. | Recovery cache only. |
| Naming failover data | User or extension provided local failover source | Overrides discovery view while failover switch is enabled. | Local discovery override only. |
| Redo data | SDK register, subscribe, or endpoint operation | Restores runtime intent after reconnect. | Runtime intent only. |
| RAD discovery and Watch state (target) | Discover result or Watch registration | Last complete Agent discovery snapshot and Watch intent. | Recovery cache and runtime intent only. |
Local data must not be treated as server-side committed state unless a domain
spec explicitly says so.
@@ -133,21 +134,102 @@ Naming redo covers:
Persistent Naming service state is server-owned and should not be restored by
client redo unless the domain explicitly treats the operation as runtime intent.
AI redo covers runtime endpoint and subscription intent, such as MCP or agent
endpoint registration. AI resource publish/delete semantics remain governed by
the [AI Registry Spec](../ai/ai-registry-spec.md).
AI redo covers runtime endpoint and subscription intent, such as MCP or Agent
Endpoint registration. AI resource publish/delete semantics remain governed by
the [AI Registry Spec](../ai/ai-registry-spec.md). The target Agent/RAD rules
are specified in Section 8.
Config listeners are recovered through listener resync and fuzzy watch resync.
Config publish/delete operations are not automatically redone by the Client SDK.
## 8. Shutdown
## 8. Agent And RAD Target Recovery Contract
This section defines the target recovery contract for the new Agent/RAD SDK. It
does not describe a currently implemented capability. It becomes active only
when the Agent/RAD abilities from the
[Agent API Spec](../ai/agent-api-spec.md) are implemented and negotiated.
### 8.1 Endpoint Publication Redo Identity
The SDK keeps desired Endpoint publication state per canonical publication
group and stores the complete Batch payload needed for replay. A materialized
registration redo key contains:
```text
(namespaceId, agentName, protocol, runtimeVersion,
canonicalVersionRange, sortedCanonicalEndpointNaturalKeys)
```
The missing `versionRange` value is canonicalized to `[runtimeVersion]` before
key construction. Every Endpoint key is normalized according to the
[RAD Protocol Spec](../ai/rad-protocol-spec.md), and the set is deduplicated
and sorted in stable ASCII order. URI input order, map order, and omitted
default values therefore cannot create different redo identities.
The SDK maintains one canonical Endpoint map for each
`(namespaceId, agentName, protocol, runtimeVersion, canonicalVersionRange)`
group. Register merges the submitted Endpoint upserts into that map and
atomically replaces the materialized redo record. Deregister removes each
submitted natural key from every local group for the same namespace, Agent,
and protocol, matching RAD's cross-binding deregistration semantics. The redo
payload retains complete URI, priority, weight, and metadata values; these
non-identity values are not discarded merely because they are absent from the
redo key.
### 8.2 HTTP And gRPC Publisher Recovery
An HTTP Agent publisher generates one `X-Nacos-Client-Id` for an SDK instance.
That id remains stable across request retries, server selection changes,
failover, heartbeat, and redo for the lifetime of the SDK instance. A process
restart creates a new id.
When any Agent Endpoint request returns `HTTP_CLIENT_NOT_FOUND`, the SDK marks
all Endpoint redo records owned by that HTTP client as unregistered and redoes
every complete desired publication group. Retrying only the failed Endpoint is
insufficient because the server has declared the complete HTTP client state
absent.
gRPC Endpoint intent is owned by the current connection id. After reconnect,
the SDK obtains a new connection id, marks all Endpoint redo records from the
old connection unregistered, and replays complete desired groups under the new
connection. HTTP and gRPC publisher records stay separate; one transport must
not deregister contributions owned by the other.
### 8.3 Watch Recovery Identity
The canonical local Watch key contains:
```text
(namespaceId, canonicalAgentReference, canonicalFilter, listenerIdentity)
```
Reference canonicalization preserves the distinction between an exact
version, a label, and latest. Filter canonicalization applies defaults and
sorts and deduplicates set-valued fields. Listener identity is the same
listener instance used to cancel the Watch.
The SDK stores the connection-scoped opaque `watchKey` returned by
`AgentSubscribeResponse` alongside this canonical local identity. Incoming
`SNAPSHOT` and `TERMINATED` notifications use that wire key only for lookup;
it is not parsed or used as the redo identity.
On gRPC disconnect, the SDK marks each Watch record unregistered. After a new
connection is established, it restores the same canonical local Watch keys,
discards each old wire key, and stores the new `watchKey` returned with the
initial complete result. A `TERMINATED` notification with
`errorCode=NOT_FOUND` removes only the identified Watch record and its cached
snapshot, so it is not retried on later reconnects. Detailed replacement,
acknowledgement, missed-push, and terminal behavior is defined by the
[Runtime Push And Reconnect Spec](runtime-push-reconnect-spec.md).
## 9. Shutdown
SDK shutdown must clear in-memory redo state, stop background retry tasks, close
transport clients, and stop local cache/failover refresh tasks. Shutdown should
not delete user-maintained failover files or server-derived snapshots unless the
user explicitly calls a cache cleanup operation.
## 9. Pending Issues
## 10. Pending Issues
- Naming redo currently uses its own implementation while newer AI redo uses
common redo abstractions. The implementations should converge on the shared
+87 -5
View File
@@ -54,6 +54,11 @@ Domain rules:
- AI push behavior is versioned by each AI resource spec and must keep the same
identity rules as the corresponding query API.
The target RAD Watch carries a complete discovery snapshot rather than only a
change identity. That snapshot is authoritative for replacement of the local
RAD discovery cache, while the Registry remains the resource authority and a
Discover re-query can refresh the snapshot.
## 3. Server-Side Connection State
Runtime listener or subscription state is scoped to a server-side connection id.
@@ -107,7 +112,86 @@ Client recovery is defined in detail by the
selection and liveness are defined by the
[Client Connection And Failover Spec](client-connection-failover-spec.md).
## 6. Ordering
## 6. Agent And RAD Target Watch Contract
This section defines the target gRPC Watch contract for Agent/RAD. It does not
describe a currently implemented capability. An implementation must not expose
or advertise this behavior until the Agent/RAD abilities in the
[Agent API Spec](../ai/agent-api-spec.md) are implemented and negotiated. The
initial Nacos HTTP binding supports Discover but not Watch.
### 6.1 Identity And Initial Result
The canonical SDK Watch key is:
```text
(namespaceId, canonicalAgentReference, canonicalFilter, listenerIdentity)
```
The canonical reference preserves whether the caller selected an exact
version, a label, or latest. Canonical Filter construction applies defaults and
sorts and deduplicates set-valued fields. Listener identity is the same
listener instance supplied to cancellation. An implementation may multiplex
wire subscriptions, but it must preserve this public identity and callback
isolation.
The server evaluates Discover before creating a Watch. `NOT_FOUND` creates no
server or client Watch state. A successful `AgentSubscribeResponse` returns a
connection-scoped opaque `watchKey` and the current complete
`AgentDiscoveryResult`. The SDK maps that key to its canonical local Watch
key and does not parse it.
RAD itself still has exactly six root messages and defines no protocol-level
event envelope. The Nacos gRPC binding uses
`AgentDiscoveryNotifyRequest(watchKey, eventType, result?, errorCode?)` to
multiplex Watch events on the shared Payload connection. This request is a
binding object, not another RAD root message.
### 6.2 Complete Replacement And Listener Delivery
For `eventType=SNAPSHOT`, `AgentDiscoveryNotifyRequest` requires one complete
`AgentDiscoveryResult` and has no error. The client atomically replaces the
previous snapshot for the identified Watch with each accepted result and then
acknowledges it with `AgentDiscoveryNotifyResponse`. It must not merge calling
interfaces, Endpoint sets, or Endpoints from different snapshots. A changed resolved version,
`contentDigest`, or `sourceRevision` identifies a potentially new snapshot;
these tokens support equality and deduplication, not ordering.
An empty filtered shape is a valid complete result and replaces the previous
snapshot. Naming push-empty protection does not apply to RAD. Listener
exceptions are isolated from connection processing and from other listeners;
they do not turn an already accepted snapshot into an unacknowledged event.
### 6.3 Terminal Disappearance
If a previously discoverable target becomes absent, invisible, disabled, or
has no discoverable online target version, the server sends
`AgentDiscoveryNotifyRequest` with `eventType=TERMINATED`, no result, and
`errorCode=NOT_FOUND`. This event closes only the identified `watchKey`; the
shared Payload connection and every other Watch remain active. The client
delivers the terminal status, removes only that local Watch key and cached
snapshot, acknowledges the terminal event, and does not redo that Watch on
later reconnects. An existing Agent whose Filter currently matches no
interface or Endpoint remains a successful empty `SNAPSHOT` and is not
terminal.
### 6.4 Missed Push And Reconnect
When connection loss, a rejected notification, or binding-specific gap
detection means a push may have been missed, the client must re-run Discover
with the same namespace, canonical reference, and canonical Filter, then
atomically replace its cached snapshot. It must not reconstruct the missing
state by applying locally inferred deltas.
On gRPC disconnect, the server removes connection-scoped Watch state. The SDK
marks the corresponding local Watch records unregistered. After reconnect it
uses the new connection id to restore the same canonical local Watch keys. It
discards each old wire `watchKey`; every successful resubscription supplies a
new opaque `watchKey` and initial complete result, which becomes the new
snapshot before subsequent pushes. A terminal result during recovery follows
Section 6.3 instead of remaining in redo state.
## 7. Ordering
Push delivery order is scoped to the local event and task paths of a node. It is
not a global total order across the cluster.
@@ -122,7 +206,7 @@ Domain specs must define when a local serving view is visible:
- Naming persistent service and metadata visibility are defined by the
[Naming Persistent CP Consistency Spec](../naming/naming-persistent-cp-consistency-spec.md).
## 7. Failure Rules
## 8. Failure Rules
- A missing connection should cancel or skip push for that connection.
- A push timeout does not prove that the client failed to observe the change;
@@ -130,10 +214,8 @@ Domain specs must define when a local serving view is visible:
- A client must be able to recover from missed push by re-query, resync, or redo.
- Server push must not hide authorization failures in the underlying query path.
## 8. Pending Issues
## 9. Pending Issues
- AI runtime push and reconnect behavior should be refined when AI SDK
subscription APIs stabilize.
- Push retry, timeout, and reconnect recovery observations should follow the
shared field and label guidance in the
[Observability Hooks Spec](../design/foundation-observability-hooks-spec.md).
@@ -129,6 +129,7 @@ The following items are current compatibility or deprecation examples:
[nacos-api-legacy-adapter](https://github.com/nacos-group/nacos-api-legacy-adapter);
- pre-spec v3 compatibility endpoints;
- AI Prompt legacy endpoints and legacy Pipeline REST-style endpoints;
- legacy A2A AgentCard Java, gRPC, Admin, Maintainer, and Console facades;
- Naming API-defined service selector fields and request parameters;
- Config aggregation fields and related database columns;
- historical plugin configuration keys;
@@ -165,7 +166,27 @@ Rules:
Domain specs should mention legacy v1/v2 behavior only as migration context or
when a current compatibility path depends on it.
## 10. Related Specs
## 10. Legacy A2A Agent Facades
The canonical Agent model uses `type=agent`, protocol-neutral versions, and RAD
discovery. Historical A2A AgentCard surfaces are compatibility-only and are
adapted at the server boundary according to the
[A2A Agent Spec](../ai/a2a-agent-spec.md).
Compatibility windows are intentionally different by audience:
- Java `A2aService` and legacy A2A gRPC payloads have no removal version yet;
- Admin `/v3/admin/ai/a2a` and `A2aMaintainerService` remain supported through
the 4.0.x compatibility window;
- Console `/v3/console/ai/a2a` remains supported through the 3.4.x
compatibility window and may be removed after the bundled UI migrates.
No new capability may be added only to these facades. New development targets
the Agent Management and RAD contracts. Historical data and mixed-server
rolling upgrade are a separate migration plan and do not extend the API window
by themselves.
## 11. Related Specs
- [HTTP API Spec](../http-api/api-spec.md)
- [V3 API Surface](../http-api/v3-api-surface.md)
@@ -175,3 +196,5 @@ when a current compatibility path depends on it.
- [Persistence And Dump Spec](foundation-persistence-dump-spec.md)
- [Integration And Adapter Spec](../integration/integration-adapter-spec.md)
- [Plugin Specs](../plugin/README.md)
- [Agent Management Spec](../ai/agent-management-spec.md)
- [RAD Protocol Spec](../ai/rad-protocol-spec.md)
@@ -34,6 +34,10 @@ Current AP-style implementations are:
| Distro | Naming runtime state | Synchronize ephemeral client-owned service instance state between server nodes. |
| Config Notify | Config cache and listener visibility | Notify peer nodes that a Config resource changed so local dump cache and listeners can refresh. |
Section 6 defines the approved target Distro contract for Agent HTTP Client
state. It is not included in the current-implementation table above and does
not claim that `AI_AGENT_HTTP_CLIENT` is already implemented or advertised.
The historical `APProtocol` interface exists in the consistency module, but the
current active AP implementations are the Distro foundation and the Config
Notify path. New specs should describe AP semantics directly instead of assuming
@@ -146,7 +150,131 @@ Naming Distro transport is carried by
`DistroDataRequest` / `DistroDataResponse` over the
[Internal RPC And Cluster Request Spec](foundation-internal-rpc-spec.md).
## 6. Config Notify Contract
## 6. Target AI Agent HTTP Client Distro Contract
This section defines the target design for HTTP Runtime Endpoint publisher
state. It becomes an active runtime contract only after the server implements
it and advertises the corresponding Agent/RAD capability.
The target flow is:
```text
Agent HTTP request
-> route by clientId to responsible server
-> mutate complete HTTP Client state
-> Distro full-state CHANGE or DELETE
-> peer Client state
-> Naming and RAD runtime events
```
### 6.1 Resource Identity And Routing
| Item | Required target semantics |
| --- | --- |
| Distro resource type | Constant `AI_AGENT_HTTP_CLIENT`. |
| `resourceKey` and `responsibleId` | Exact opaque HTTP `clientId`. |
| Responsibility | Distro's stable client-id shard selects one responsible server. |
| Module isolation | The module is not concatenated into the shard key; resource type and a dedicated Client manager isolate Agent state from the same client id used by another module. |
| Native owner | Only the responsible server owns the native HTTP Client, `lastActiveTime`, and timeout scheduling. |
| Remote entry | A non-owner routes the mutation or heartbeat to the responsible server; it does not create an independent liveness timer. |
The first successful Endpoint registration binds the Client to one authenticated
subject and one `namespaceId`. The state stores a stable subject identifier,
not credentials or access tokens. Every later registration, deregistration, and
heartbeat must use the same authenticated subject. Every later request that
contains a namespace must use the bound namespace; a heartbeat without a body
uses the stored namespace binding. A mismatch is rejected and does not refresh
liveness.
`clientId` is a routing and publisher-ownership identifier, not an
authentication credential. Reusing the same text under another Distro resource
type must not renew, mutate, verify, snapshot, or delete the Agent Client.
### 6.2 Complete State And Operations
One synchronized Client datum is a complete replacement state containing:
| Field group | Content |
| --- | --- |
| Identity | `clientId`, bound `namespaceId`, and authenticated subject identifier. |
| Convergence | Domain revision and semantic liveness state. |
| Liveness | `lastActiveTime`, `heartbeatIntervalMillis`, `unhealthyTimeoutMillis`, and `expireTimeoutMillis`. |
| Publications | Every Agent Endpoint publication group and its complete Endpoint contributions. |
Endpoint groups include AgentName, canonical protocol, runtime Version,
canonical Version range, and Endpoint payload. A Distro datum never carries a
partial Endpoint patch. Credentials, request headers, and raw authentication
material are excluded.
`AI_AGENT_HTTP_CLIENT` accepts this operation set:
| Operation | Semantics |
| --- | --- |
| `CHANGE` | Idempotently create or replace one complete Client state. Local Endpoint changes and semantic liveness transitions emit this operation. |
| `DELETE` | Remove the complete Client and all of its publisher contributions. Missing state is an idempotent no-op. |
| `VERIFY` | Compare client id, existence, and domain revision; mismatch schedules targeted repair. |
| `SNAPSHOT` | Transfer the complete set of full Client states owned by the source snapshot. |
| `QUERY` | Return one complete Client state by client id, or a typed not-found result. |
Creation uses `CHANGE`; this target does not require a separate `ADD` semantic.
Apply logic rejects stale revisions, accepts duplicate identical state, and
must not merge fields from out-of-order complete replacements. `VERIFY` and
`QUERY` repair with a complete state, never with an Endpoint delta.
An ordinary heartbeat refreshes `lastActiveTime` only on the responsible
server. It does not broadcast on every interval. A semantic transition between
active and unhealthy, an Endpoint change, or deletion advances the domain
revision and synchronizes the latest complete state.
### 6.3 Timeout And Failover
The server returns and stores timeout values satisfying:
```text
heartbeatIntervalMillis < unhealthyTimeoutMillis < expireTimeoutMillis
```
Heartbeat and successful Endpoint writes refresh Client activity. After
`unhealthyTimeoutMillis`, that Client's contributions remain in RAD/Naming
projections but become unhealthy; an Endpoint shared with another healthy
publisher may still aggregate to `healthy=true`. The Client transition emits a
full-state `CHANGE`. Activity restored before expiry returns the Client to
active and emits `CHANGE` when the public health projection changes. After
`expireTimeoutMillis`, the responsible server emits `DELETE` and removes every
contribution. Deregistering the last Endpoint removes the empty Client
immediately.
On responsibility transfer, a new owner may activate timeout scheduling only
after it has installed a complete state from local replica data, `SNAPSHOT`, or
`QUERY` and verified its identity and revision. It then starts a failover grace
window equal to that Client's `expireTimeoutMillis`, measured from takeover.
During the grace window it must not expire the Client solely because the
replicated `lastActiveTime` is old. A valid heartbeat ends the grace and resumes
normal timeout calculation; no heartbeat by the grace deadline expires the
Client.
If the new owner cannot obtain a complete state, it must not synthesize an
empty Client. Heartbeat returns `HTTP_CLIENT_NOT_FOUND`, causing the SDK to mark
all desired Endpoint groups unregistered and redo complete registration
batches with the same client id. A registration can create the missing state;
deregistration of missing state remains a successful no-op.
### 6.4 Apply Events And Visibility
Applying local mutation, remote `CHANGE`, repaired `QUERY`, or `SNAPSHOT` state
must materialize the same Agent HTTP Client and raw Naming publisher
contributions. A semantic apply emits the Naming Client/service change events
needed to rebuild indexes and instance aggregation, and the RAD runtime
projection/watch events needed to refresh Endpoint snapshots and
`sourceRevision`.
Applying `DELETE` emits the corresponding removal events. Duplicate state with
the same semantic revision emits no duplicate domain change. `VERIFY` alone
does not mutate domain state or emit a discovery event. Remote apply follows
internal RPC authentication and source validation; it restores the stored
authenticated-subject binding but never treats `clientId` as authority.
## 7. Config Notify Contract
Config Notify is an AP-style change propagation path. It is not a durable
storage protocol and does not carry authoritative config content.
@@ -183,7 +311,7 @@ For Config, AP notification success means peer nodes were told to refresh their
serving state. It does not replace persistence success, and it does not make the
push payload authoritative content.
## 7. Failure Semantics
## 8. Failure Semantics
AP consumers must handle partial success.
@@ -198,20 +326,22 @@ Rules:
- AP recovery must be observable through logs, metrics, trace, or diagnostics;
- AP failure must not silently turn runtime state into durable metadata.
## 8. Boundary Rules
## 9. Boundary Rules
- AP consistency is eventual convergence, not strong consistency.
- Local `NotifyCenter` events are not AP consistency by themselves; they become
part of AP behavior only when a domain defines remote propagation and repair.
- Distro is the formal shared AP framework for runtime data. Config Notify is a
Config-specific AP notification path for cache/listener visibility.
- Distro is the formal shared AP framework for runtime data. Naming currently
uses it for ephemeral Client state; `AI_AGENT_HTTP_CLIENT` joins that
framework only after its target capability is implemented. Config Notify is
a Config-specific AP notification path for cache/listener visibility.
- AP paths must not be used for permissions, namespace metadata, persistent
service metadata, plugin state, or database schema state.
- AP payloads are internal cluster contracts unless an interface spec exposes
them explicitly.
- AP transport must follow internal RPC auth, source, payload, and retry rules.
## 9. Related Specs
## 10. Related Specs
- [Foundation Capabilities Spec](foundation-capabilities-spec.md)
- [Internal RPC And Cluster Request Spec](foundation-internal-rpc-spec.md)
@@ -224,4 +354,6 @@ Rules:
- [Config Spec](../config/config-spec.md)
- [Config Listener And Watch Spec](../config/config-listener-watch-spec.md)
- [Naming Consistency And Client State Spec](../naming/naming-consistency-client-spec.md)
- [Agent Storage Spec](../ai/agent-storage-spec.md)
- [RAD Protocol Spec](../ai/rad-protocol-spec.md)
- [gRPC API Spec](../grpc-api/api-spec.md)
+17 -13
View File
@@ -96,7 +96,7 @@ field, or compatibility field.
resourceType is a type classifier. It is suitable for shared governance models
that contain multiple resource types, such as AI Registry resource types:
`mcp`, `a2a`, `prompt`, `skill`, and `agentspec`.
`mcp`, `agent`, `prompt`, `skill`, and `agentspec`.
resourceType is not a business grouping. AI resources should not introduce a
Group identity field unless a domain spec explicitly defines additional
@@ -114,7 +114,7 @@ Different domains expose domain-specific names:
| Config | `dataId` |
| Naming service | `serviceName` |
| MCP Server | `name` or `mcpName` |
| A2A AgentCard | `name` or `agentName` |
| Agent | `agentName` |
| Prompt | `promptKey` |
| Skill | `name` |
| AgentSpec | `name` |
@@ -210,7 +210,7 @@ NamespaceId -> resourceType -> resourceName
```
It covers AI Registry resources such as
[MCP Server](../ai/mcp-server-spec.md), [A2A AgentCard](../ai/a2a-agent-spec.md),
[MCP Server](../ai/mcp-server-spec.md), [Agent](../ai/agent-management-spec.md),
[Prompt](../ai/prompt-spec.md), [Skill](../ai/skill-spec.md), and
[AgentSpec](../ai/agentspec-spec.md). The shared AI model is defined by the
[AI Registry Spec](../ai/ai-registry-spec.md) and the
@@ -253,21 +253,25 @@ resourceName. MCP-specific metadata includes protocol, front protocol,
repository, packages, icons, website URL, local or remote server config,
endpoint spec, tool spec, status, and discovered capabilities.
### 6.2 A2A AgentCard
### 6.2 Agent
A2A AgentCard canonical resource identity is:
Agent canonical resource identity is:
```text
namespaceId -> a2a -> agentName
namespaceId -> agent -> agentName
```
AgentCard resources describe agent capabilities, skills, supported interfaces,
provider information, security schemes, signatures, and endpoint metadata.
An Agent owns directory and governance metadata. Each Agent version owns an
ordered set of protocol-neutral call interfaces. A2A is one protocol binding
whose native descriptor is an AgentCard; it is not a second top-level AI
resource identity. Runtime endpoints have a client-owned lifecycle and are
projected into Agent discovery without becoming version content.
`registrationType` participates in AgentCard lookup and compatibility
semantics, but it is not the top-level second-layer field. Its relation with
resourceName, version, and endpoint should be defined by a specific A2A domain
spec.
The complete model is defined by the
[Agent Management Spec](../ai/agent-management-spec.md). Remote consumer
discovery follows the [RAD Protocol Spec](../ai/rad-protocol-spec.md), while
legacy AgentCard APIs are compatibility facades defined by the
[A2A Agent Spec](../ai/a2a-agent-spec.md).
### 6.3 Prompt
@@ -312,7 +316,7 @@ namespaceId -> agentspec -> agentSpecName
```
AgentSpec assembles agent configuration by referencing prompts, skills, MCP
servers, A2A agents, or other required resources. AgentSpec should reference
servers, Agents, or other required resources. AgentSpec should reference
other resources by stable identity and version or label, not by storage
implementation details.
+23
View File
@@ -236,6 +236,29 @@ AI payload semantics are defined by the
| `BatchAgentEndpointRequest` | `AgentEndpointResponse` | write | `agentName`, `endpoints` | Replace this client's endpoints for an Agent. |
| `QueryPromptRequest` | `QueryPromptResponse` | read | `namespace`, `promptKey`, `version`, `label`, `md5` | Query Prompt by version, label, latest, or md5. |
The following Agent/RAD payloads are the approved Experimental target defined
by the [Agent API Spec](../ai/agent-api-spec.md). They are not part of the
current implemented payload inventory until their classes, handlers, SPI
registrations, and negotiated abilities are present in the runtime.
| Target request type | Target response type | Direction | Contract |
| --- | --- | --- | --- |
| `AgentSearchRequest` | `AgentSearchResponse` | read | Search the Agent catalog and return one page of `AgentCatalogEntry` values. |
| `AgentDiscoveryRequest` | `AgentDiscoveryResponse` | read | Discover one Agent and return one complete `AgentDiscoveryResult`. |
| `AgentSubscribeRequest` | `AgentSubscribeResponse` | read | Subscribe or unsubscribe an Agent reference and optional filter; subscribe returns an opaque `watchKey` and the current complete result. |
| `AgentDiscoveryNotifyRequest` | `AgentDiscoveryNotifyResponse` | server push | Push one `SNAPSHOT` or `TERMINATED` event for a `watchKey` and receive an acknowledgement. |
| `AgentEndpointRegisterRequest` | `AgentEndpointOperationResponse` | write | Upsert one runtime Endpoint registration batch owned by the current connection. |
| `AgentEndpointDeregisterRequest` | `AgentEndpointOperationResponse` | write | Idempotently remove one runtime Endpoint deregistration batch owned by the current connection. |
For this target binding, `AgentDiscoveryNotifyRequest` contains `watchKey` and
`eventType`. `SNAPSHOT` requires a complete `AgentDiscoveryResult` and no
error. `TERMINATED` requires no result and `errorCode=NOT_FOUND`. The client
acknowledges both event types. A terminal event ends only the identified Watch
on the shared Payload connection; it does not end the connection or another
Watch. `AgentSubscribeResponse` is the source of the connection-scoped opaque
`watchKey`, including after reconnect. These wrappers remain gRPC binding
objects and do not extend RAD's six root messages.
Skill ZIP download and AgentSpec assembly are Java SDK interface capabilities,
but current Java client implementation uses HTTP/config composition rather than a
dedicated gRPC payload.
+44 -2
View File
@@ -186,7 +186,49 @@ The default auth plugin is shipped with Nacos, so its v3 auth endpoints should
follow the Nacos HTTP API rules and the
[Auth Plugin Spec](../auth/auth-plugin-spec.md).
## 8. Documentation Gap Notes
## 8. Approved Agent/RAD Target Surface
The following paths are the approved Experimental target from the
[Agent API Spec](../ai/agent-api-spec.md). They are not part of the current
implemented inventory or the controller counts in Section 3 until the
corresponding controllers, authorization, transport bindings, and tests are
implemented.
Client target paths:
| Method | Path | Contract |
| --- | --- | --- |
| GET | `/v3/client/ai/agents/search` | Search the Agent catalog. |
| GET | `/v3/client/ai/agents` | Discover one Agent, with an optional discovery filter. |
| POST | `/v3/client/ai/agents/endpoints` | Upsert a runtime Endpoint registration batch. |
| DELETE | `/v3/client/ai/agents/endpoints` | Deregister a runtime Endpoint batch using a JSON body. |
| PUT | `/v3/client/ai/agents/endpoints/heartbeat` | Refresh one HTTP publisher client's liveness. |
Admin and Console target paths use the prefixes
`/v3/admin/ai/agents` and `/v3/console/ai/agents`, respectively. Console is a
UI facade over the same relative management contract.
| Relative path | Methods | Contract |
| --- | --- | --- |
| *(base path)* | GET, POST, PUT, DELETE | Read, create, update, or delete an Agent definition. |
| `/list` | GET | List Agent summaries. |
| `/versions` | GET | List Version summaries. |
| `/version` | GET | Read one exact Version definition. |
| `/runtime-endpoints` | GET | Read one complete, non-paged runtime Endpoint snapshot. |
| `/draft` | POST, PUT, DELETE | Create, update, or delete a draft. |
| `/submit` | POST | Submit a draft. |
| `/publish` | POST | Publish a reviewed Version. |
| `/force-publish` | POST | Perform an audited Pipeline bypass. |
| `/redraft` | POST | Return a reviewed Version to draft. |
| `/online` | POST | Bring an offline Version online. |
| `/offline` | POST | Take an online Version offline. |
| `/labels` | PUT | Update custom Version labels. |
The target does not add Client HTTP Watch or Endpoint-list GET APIs. Watch and
push use the negotiated gRPC binding; runtime inspection uses the Admin or
Console `/runtime-endpoints` path.
## 9. Documentation Gap Notes
This is not a bug list. It records places where the current documentation and
code appear to describe different surfaces.
@@ -213,7 +255,7 @@ code appear to describe different surfaces.
module-level `ControllerAdvice` classes that may return plain text error
bodies. They should converge to `NacosApiExceptionHandler` for v3 APIs.
## 9. Deprecated Compatibility Notes
## 10. Deprecated Compatibility Notes
Some v3 AI APIs were released before this spec existed and were later replaced by
clearer lifecycle or REST-style APIs. These old endpoints should be treated as
@@ -63,6 +63,41 @@ instance metadata keys for core behavior:
| `preserved.ip.delete.timeout` | Heartbeat deletion timeout override. |
| `preserved.instance.id.generator` | Instance id generator selection. |
The complete `__nacos.agent.endpoint.*__` namespace is reserved for the Agent
Runtime Endpoint projection. Its version-1 keys are:
| Key | Meaning |
| --- | --- |
| `__nacos.agent.endpoint.path__` | URI path. |
| `__nacos.agent.endpoint.transport__` | Canonical transport; it must agree with the Naming cluster. |
| `__nacos.agent.endpoint.protocol__` | URI scheme, not the Agent CallInterface protocol token. |
| `__nacos.agent.endpoint.protocolVersion__` | Optional legacy A2A protocol-version compatibility fact. |
| `__nacos.agent.endpoint.supportTls__` | Whether the projected URI uses TLS. |
| `__nacos.agent.endpoint.query__` | Raw URI query. |
| `__nacos.agent.endpoint.tenant__` | Protocol-native tenant when present. |
| `__nacos.agent.endpoint.version__` | Single-binding runtime Version compatibility and diagnostic mirror. |
| `__nacos.agent.endpoint.versionRange__` | Single-binding canonical Version range compatibility and diagnostic mirror. |
| `__nacos.agent.endpoint.bindings__` | Canonical JSON array of runtime Version and Version-range bindings. |
| `__nacos.agent.endpoint.priority__` | Endpoint priority; a lower number has higher priority. |
Only the Agent Runtime Registry may write keys under this prefix. Public
runtime and operational metadata writes must reject them, so the ordinary
operational-over-runtime priority rule does not override Agent projection
facts. Endpoint weight, enabled state, and health continue to use their native
Naming Instance fields rather than reserved metadata keys.
Only the A2A compatibility adapter writes `protocolVersion`. Public RAD
Endpoint metadata and Runtime revision exclude it. The old A2A response
projection prefers this value and falls back to the target Agent CallInterface
`protocolVersion` when it is absent.
`bindings` is the Version-matching fact. When it has exactly one item, the
Registry also writes `version` and `versionRange` as mirrors. When it has more
than one item, the Registry removes both singular keys. Readers use `bindings`
when present and must not merge stale singular values. The exact canonical
array and Runtime projection rules are defined by the
[Agent Storage Spec](../ai/agent-storage-spec.md).
New core behavior must not be bound to arbitrary user metadata keys. If a
metadata key changes Naming behavior, it must be reserved and documented.
@@ -123,5 +158,6 @@ after recovery.
- [Naming Resource Spec](naming-resource-spec.md)
- [Naming Health And Protection Spec](naming-health-protection-spec.md)
- [Naming Consistency And Client State Spec](naming-consistency-client-spec.md)
- [Agent Storage Spec](../ai/agent-storage-spec.md)
- [Event Dispatch And NotifyCenter Spec](../design/foundation-event-dispatch-spec.md)
- [Compatibility And Deprecation Spec](../design/compatibility-deprecation-spec.md)
+27
View File
@@ -100,6 +100,32 @@ The SHA-256 fallback is deterministic but not reversible. Logical resource
identity remains owned by AI resource metadata. `save`, `get`, and `delete` must
apply exactly the same physical mappings.
### Agent Logical Coordinate
For `type=agent`, the Agent domain constructs this logical Nacos Config
coordinate before handing the provider an opaque `StorageKey`:
```text
group = agent-version
dataId = agent__<rad-ascii-v1(agentName)>__<version>.json
```
`rad-ascii-v1` and the complete Agent Version storage contract are defined by
the [Agent Storage Spec](../ai/agent-storage-spec.md). This coordinate is a
logical provider input, not a physical Config identity exposed to callers.
The built-in provider must pass both logical segments through the common
`NacosAiConfigKeyCodec`; it must not bypass that codec because the Agent domain
already encoded `agentName`. A safe value within the physical limit remains
identical to the logical value. The common codec owns all length and
reserved-shape handling: an overlong candidate uses its deterministic SHA-256
fallback, and that physical result is not reversible.
Upper layers may persist the logical key format and content digest, but must not
parse a physical Config key, require it to be reversible, or reconstruct Agent
identity from it. `save`, `get`, and `delete` always recompute the physical
coordinate through the same codec.
The provider does not dual-read coordinates produced by an earlier physical
mapping. Existing affected `nacos_config` rows must therefore be migrated in a
coordinated maintenance window before nodes using only the new mapping start.
@@ -123,6 +149,7 @@ The following properties select a provider for an AI resource domain:
nacos.ai.prompt.storage.provider=nacos_config
nacos.ai.skill.storage.provider=nacos_config
nacos.ai.agentspec.storage.provider=nacos_config
nacos.ai.agent.storage.provider=nacos_config
```
They are domain routing policy, not private configuration definitions owned by
+60 -7
View File
@@ -57,9 +57,12 @@ lifecycle behavior, or exception mapping change.
`NamingMaintainService` is deprecated after 3.3.0. New management integrations
should use `nacos-maintainer-client`.
One Java SDK instance is bound to one namespace. Applications that need multiple
namespaces should create separate SDK instances and close them when no longer
used.
One Java Client SDK instance is bound to one namespace. Applications that need
multiple namespaces should create separate Client SDK instances and close them
when no longer used. Public runtime interfaces do not expose a namespace
argument; their implementations use the namespace bound at construction.
This rule does not apply to the Maintainer SDK: its Agent management interface
is not namespace-bound and requires an explicit namespace on every call.
## 3. Java Client SDK Configuration
@@ -171,11 +174,54 @@ SDK instead of `ConfigService`.
The selector overload of `getServicesOfServer` is deprecated and remains only as
a compatibility surface.
### 5.3 AiService and A2aService
### 5.3 AiService, AgentDiscoveryService, And A2aService
`AiService` extends `A2aService`.
Resource semantics are defined by the [AI Registry Spec](../ai/ai-registry-spec.md)
and the individual AI resource type specs.
The Agent/RAD contract in this subsection is a target contract, not an
inventory of currently implemented Java methods. It becomes active only after
the new Agent/RAD abilities are implemented and negotiated. Until then, the
existing `AiService` and `A2aService` methods remain the active compatibility
surface.
The target inheritance is:
```text
AiService extends AgentDiscoveryService, A2aService
```
Adding this parent must not make an already compiled third-party `AiService`
implementation fail linkage immediately. Newly inherited methods use
compatibility default bridges that report unsupported behavior until an
implementation overrides them; the official Nacos implementation overrides the
complete target surface.
`AgentDiscoveryService` provides these namespace-bound methods:
| Capability | Methods | Contract |
| --- | --- | --- |
| Search | `searchAgents` | Accept `AgentSearchRequest` and return `Page<AgentCatalogEntry>`. |
| Discover | `discoverAgent` overloads | Accept `AgentReference`, with an optional `AgentDiscoveryFilter`, and return one complete `AgentDiscoveryResult`. |
| Watch | `subscribeAgent` overloads | Accept the same reference, optional Filter, and listener; return the current complete result and later deliver complete replacement results. |
| Cancel Watch | `unsubscribeAgent` overloads | Remove the Watch identified by the same reference, Filter, and listener identity. |
| Register Endpoint | `registerAgentEndpoints` | Register one `AgentEndpointRegistrationBatch` and retain it as redo intent. |
| Deregister Endpoint | `deregisterAgentEndpoints` | Deregister one `AgentEndpointDeregistrationBatch` owned by this SDK publisher. |
These public methods do not accept `namespaceId`. The proxy copies the caller's
request or Batch, injects the SDK namespace into the transport object, and does
not mutate the caller's object. If a shared input model already carries a
nonempty namespace different from the SDK namespace, the proxy rejects it
locally. Target Watch, cache, and redo behavior follows the
[Client Local Cache And Redo Spec](../client/client-local-cache-redo-spec.md)
and the
[Runtime Push And Reconnect Spec](../client/runtime-push-reconnect-spec.md).
The inherited `A2aService` remains a compatibility facade. New Agent
applications use `AgentDiscoveryService`; existing AgentCard calls continue
through the A2A compatibility adapter.
Resource semantics are defined by the [AI Registry Spec](../ai/ai-registry-spec.md),
the [Agent API Spec](../ai/agent-api-spec.md), the
[RAD Protocol Spec](../ai/rad-protocol-spec.md), and the individual AI resource
type specs. The currently implemented compatibility methods include:
| Capability | Methods | Contract |
| --- | --- | --- |
@@ -290,6 +336,13 @@ maintenance belong to the Maintainer SDK.
- `agentSpec()` for AgentSpec management;
- `pipeline()` for Pipeline management.
The target Agent management addition is `agent()`, which returns
`AgentMaintainerService`. This is a target contract and must not be documented
as currently implemented until the new Agent Admin API is available.
`AgentMaintainerService` maps one-to-one to that Admin HTTP API. Its instance is
not namespace-bound, and every method explicitly carries `namespaceId`.
`a2a()` remains available for its compatibility window.
Runtime AI registration and subscription can remain in `AiService`; broad AI
resource management belongs to `AiMaintainerService`.
+32 -7
View File
@@ -44,8 +44,9 @@ the capabilities that a runtime application normally needs:
- read known configuration items and subscribe to their changes;
- register and deregister the current application instance;
- query and subscribe to known services used by the application;
- register, resolve, and subscribe to runtime AI resources, such as MCP
endpoints, A2A agent endpoints, Prompt, Skill, and AgentSpec resources;
- register, discover, and subscribe to runtime AI resources, including callable
Agent endpoints, while retaining historical MCP, A2A, Prompt, Skill, and
AgentSpec compatibility surfaces;
- use optional runtime primitives such as
[distributed lock](../lock/lock-spec.md) when the language SDK supports them;
- manage its own lifecycle, local cache, listeners, and connections according to
@@ -77,15 +78,39 @@ capabilities that are intentionally absent from the Client SDK:
dump, and metadata operations;
- service, instance, cluster metadata, subscriber, client, and health-check
maintenance;
- AI resource management for MCP, A2A, Prompt, Skill, AgentSpec, and Pipeline
resources;
- AI resource management for Agent, MCP, A2A, Prompt, Skill, AgentSpec, and
Pipeline resources;
- paginated and filterable access to large management datasets.
The Maintainer SDK should be treated as a typed facade over the Nacos Admin API
surface. When a capability is only useful for management, UI, gateway, or
operation tools, it belongs here instead of the Client SDK.
## 4. Security Rules
## 4. Agent And RAD Target Contract
This section defines the target SDK contract for Agent management and
[Remote Agent Discovery (RAD)](../ai/rad-protocol-spec.md). It does not claim
that an SDK already implements these capabilities. Until the Agent/RAD
abilities defined by the [Agent API Spec](../ai/agent-api-spec.md) are
implemented and negotiated, the existing A2A SDK surface remains the active
compatibility contract.
The target Client SDK must:
- bind each SDK instance to one namespace and omit namespace arguments from
public Agent discovery, watch, registration, and deregistration methods;
- expose Agent Search, Discover with and without a Filter, Watch and cancel,
and runtime Endpoint Register and Deregister;
- inject the bound namespace into a transport request without mutating a
caller-owned object; and
- preserve Watch and Endpoint publication intent across reconnect according to
the client recovery specs.
The target Maintainer SDK is not namespace-bound. Every Agent management call
must explicitly identify its namespace. It exposes the new Agent management
facade while retaining the A2A management facade for its compatibility window.
## 5. Security Rules
SDK capability design must follow least privilege:
@@ -100,7 +125,7 @@ SDK capability design must follow least privilege:
- SDK documentation should make data-leakage risks visible when an API can list
or export a large amount of configuration, service, client, or metadata.
## 5. Transport and API Alignment
## 6. Transport and API Alignment
The SDK contract is a semantic contract, not a transport contract:
@@ -117,7 +142,7 @@ The SDK contract is a semantic contract, not a transport contract:
language-idiomatic exceptions or result types without hiding server-side
semantics.
## 6. Multi-language Alignment
## 7. Multi-language Alignment
Java is currently the baseline implementation for defining shared SDK
semantics. Other language SDKs should align with the same capability families:
+86
View File
@@ -0,0 +1,86 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Nacos JSON Schemas
This directory contains machine-readable contracts that accompany the Nacos
specifications. All schemas use JSON Schema Draft 2020-12.
## Public schemas
Public protocol and management schemas use semantic-version directories:
```text
ai/<domain>/<MAJOR.MINOR.PATCH>/<schema-name>.schema.json
```
Public schemas use resolvable `https://nacos.io/schemas/...` identifiers and
declare their lifecycle through `x-status`. A `0.x` schema marked
`experimental` is available for implementation and interoperability testing,
but is not yet a stable compatibility promise.
Public schema versions follow these rules:
- a breaking contract change creates a new semantic version;
- an additive, backward-compatible contract change also creates a new version;
- an editorial clarification that does not alter validation belongs in the
prose specification, not in an already published schema; and
- experimental status does not permit an existing version directory to be
rewritten after publication.
## Internal schemas
Internal persistence and projection schemas use integer versions:
```text
ai/<domain>/internal/v<schemaVersion>/<schema-name>.schema.json
```
An internal serialized object carries the matching integer `schemaVersion`
when its definition includes that field. Internal schemas use a Nacos URN
rather than a public URL. Any change that affects stored bytes, physical-key
composition, parsing, canonicalization, or generated internal objects creates
the next integer version and requires an explicit reader or migration policy.
## Frozen-version rule
Every version directory is immutable once merged into a release branch.
Corrections that alter validation or serialization must be published under a
new directory. Implementations may support several versions concurrently, but
must select a schema explicitly and must never infer a newer contract from an
older directory name.
## Bundle and entry-point convention
Schema files in this directory are definition bundles. Their top level does
not use a broad `oneOf` to guess the message type. Instead, `x-entrypoints`
maps each supported object name to an explicit `$defs` reference. Validators
must select the expected entry point, for example:
```text
https://nacos.io/schemas/ai/rad/0.1.0/rad-protocol.schema.json#/$defs/AgentDiscoveryRequest
```
This keeps validation deterministic and prevents one transport message from
being accepted accidentally where another was expected.
## Current schemas
| Schema | Version | Status | Purpose |
| --- | --- | --- | --- |
| `ai/rad/0.1.0/rad-protocol.schema.json` | `0.1.0` | Experimental | RAD Search, Discover, Watch snapshot, and Runtime Endpoint publication objects. |
| `ai/agent/0.1.0/agent-management.schema.json` | `0.1.0` | Experimental | Public Agent management resources and bounded read views. |
| `ai/agent/internal/v1/agent-storage.schema.json` | `1` | Internal experimental | Agent resource extension, version content and storage pointer, Naming projection, codecs, composers, and digest contracts. |
@@ -0,0 +1,870 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://nacos.io/schemas/ai/agent/0.1.0/agent-management.schema.json",
"$comment": "Copyright 1999-2026 Alibaba Group Holding Ltd. Licensed under the Apache License, Version 2.0.",
"title": "Nacos Agent Management Model 0.1.0",
"description": "Definition bundle for public Agent resources, exact Version definitions, and bounded management read views.",
"x-status": "experimental",
"x-schema-version": "0.1.0",
"x-entrypoints": {
"Agent": "#/$defs/Agent",
"AgentSummary": "#/$defs/AgentSummary",
"AgentOverview": "#/$defs/AgentOverview",
"AgentVersionSummary": "#/$defs/AgentVersionSummary",
"AgentVersionDetail": "#/$defs/AgentVersionDetail",
"AgentCallInterface": "#/$defs/AgentCallInterface",
"RuntimeEndpointSnapshot": "#/$defs/RuntimeEndpointSnapshot"
},
"$defs": {
"NamespaceId": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[A-Za-z0-9_-]+$"
},
"AgentName": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"allOf": [
{
"pattern": "^[ -~]+$"
},
{
"pattern": "[!-~]"
}
]
},
"AgentVersion": {
"type": "string",
"minLength": 5,
"maxLength": 64,
"pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?$"
},
"VersionRange": {
"type": "string",
"minLength": 3,
"maxLength": 256,
"anyOf": [
{
"pattern": "^\\[(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?\\]$"
},
{
"pattern": "^(?:\\[|\\()(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?,(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\]|\\))$"
},
{
"pattern": "^(?:\\[|\\()(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?,\\)$"
},
{
"pattern": "^\\(,(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\]|\\))$"
}
],
"description": "An exact version or one continuous Maven-style interval using Agent version comparison. Semantic bound checks require domain validation."
},
"Label": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
},
"NonLatestLabel": {
"allOf": [
{
"$ref": "#/$defs/Label"
},
{
"not": {
"const": "latest"
}
}
]
},
"ProtocolToken": {
"type": "string",
"minLength": 1,
"maxLength": 32,
"pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,31}$"
},
"ProtocolVersion": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[!-~]+$"
},
"TransportToken": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[0-9A-Za-z-]{1,64}$"
},
"ContentDigest": {
"type": "string",
"pattern": "^sha256:[0-9a-f]{64}$"
},
"EpochMillis": {
"type": "integer",
"minimum": 0,
"maximum": 9223372036854775807
},
"AbsoluteUri": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"format": "uri"
},
"AbsoluteEndpointUri": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"format": "uri",
"allOf": [
{
"pattern": "^[A-Za-z][A-Za-z0-9+.-]*://"
},
{
"pattern": "^[^#]+$"
},
{
"not": {
"pattern": "^[A-Za-z][A-Za-z0-9+.-]*://[^/?#]*@"
}
}
]
},
"MediaType": {
"type": "string",
"minLength": 3,
"maxLength": 128,
"pattern": "^[!-~]+/[!-~]+$"
},
"NonNullJsonValue": {
"not": {
"type": "null"
}
},
"AgentProvider": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"url": {
"$ref": "#/$defs/AbsoluteUri"
}
}
},
"Tag": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"not": {
"pattern": "^__nacos\\.agent\\."
}
},
"Extensions": {
"type": "object",
"maxProperties": 32,
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"additionalProperties": true,
"description": "Namespaced public extension values. The canonical JSON representation of the whole map is limited to 16 KiB by domain validation."
},
"AgentResourceStatus": {
"type": "string",
"enum": [
"enable",
"disable"
]
},
"AgentVersionStatus": {
"type": "string",
"enum": [
"draft",
"reviewing",
"reviewed",
"online",
"offline"
]
},
"VersionLabels": {
"type": "object",
"propertyNames": {
"$ref": "#/$defs/Label"
},
"additionalProperties": {
"$ref": "#/$defs/AgentVersion"
}
},
"AgentVersionInfo": {
"type": "object",
"additionalProperties": false,
"required": [
"onlineCnt",
"labels"
],
"properties": {
"editingVersion": {
"$ref": "#/$defs/AgentVersion"
},
"reviewingVersion": {
"$ref": "#/$defs/AgentVersion"
},
"onlineCnt": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
},
"labels": {
"$ref": "#/$defs/VersionLabels"
}
}
},
"VersionCatalogEntry": {
"type": "object",
"additionalProperties": false,
"required": [
"version",
"labels",
"protocols"
],
"properties": {
"version": {
"$ref": "#/$defs/AgentVersion"
},
"labels": {
"type": "array",
"uniqueItems": true,
"items": {
"$ref": "#/$defs/NonLatestLabel"
}
},
"protocols": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/ProtocolToken"
}
}
}
},
"VersionCatalog": {
"type": "object",
"additionalProperties": false,
"required": [
"onlineVersions"
],
"properties": {
"latestVersion": {
"$ref": "#/$defs/AgentVersion"
},
"onlineVersions": {
"type": "array",
"items": {
"$ref": "#/$defs/VersionCatalogEntry"
}
}
},
"allOf": [
{
"if": {
"properties": {
"onlineVersions": {
"maxItems": 0
}
}
},
"then": {
"not": {
"required": [
"latestVersion"
]
}
},
"else": {
"required": [
"latestVersion"
]
}
}
],
"description": "latestVersion must identify one onlineVersions item; Version and label uniqueness require domain validation."
},
"Agent": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"status",
"owner",
"scope",
"versionInfo",
"versionCatalog",
"metaVersion",
"createTime",
"updateTime"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"displayName": {
"type": "string",
"maxLength": 128
},
"description": {
"type": "string",
"maxLength": 2048
},
"iconUrl": {
"$ref": "#/$defs/AbsoluteUri"
},
"provider": {
"$ref": "#/$defs/AgentProvider"
},
"tags": {
"type": "array",
"maxItems": 32,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/Tag"
}
},
"extensions": {
"$ref": "#/$defs/Extensions"
},
"status": {
"$ref": "#/$defs/AgentResourceStatus"
},
"owner": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"scope": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"versionInfo": {
"$ref": "#/$defs/AgentVersionInfo"
},
"versionCatalog": {
"$ref": "#/$defs/VersionCatalog"
},
"metaVersion": {
"type": "integer",
"minimum": 0,
"maximum": 9223372036854775807
},
"createTime": {
"$ref": "#/$defs/EpochMillis"
},
"updateTime": {
"$ref": "#/$defs/EpochMillis"
}
}
},
"AgentSummary": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"status",
"owner",
"scope",
"versionInfo",
"versionCatalog",
"metaVersion",
"createTime",
"updateTime"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"displayName": {
"type": "string",
"maxLength": 128
},
"description": {
"type": "string",
"maxLength": 2048
},
"iconUrl": {
"$ref": "#/$defs/AbsoluteUri"
},
"provider": {
"$ref": "#/$defs/AgentProvider"
},
"tags": {
"type": "array",
"maxItems": 32,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/Tag"
}
},
"status": {
"$ref": "#/$defs/AgentResourceStatus"
},
"owner": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"scope": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"versionInfo": {
"$ref": "#/$defs/AgentVersionInfo"
},
"versionCatalog": {
"$ref": "#/$defs/VersionCatalog"
},
"metaVersion": {
"type": "integer",
"minimum": 0,
"maximum": 9223372036854775807
},
"createTime": {
"$ref": "#/$defs/EpochMillis"
},
"updateTime": {
"$ref": "#/$defs/EpochMillis"
}
}
},
"AgentVersionSummary": {
"type": "object",
"additionalProperties": false,
"required": [
"version",
"status",
"contentDigest",
"createTime",
"updateTime"
],
"properties": {
"version": {
"$ref": "#/$defs/AgentVersion"
},
"status": {
"$ref": "#/$defs/AgentVersionStatus"
},
"author": {
"type": "string",
"maxLength": 128
},
"changeDescription": {
"type": "string",
"maxLength": 2048
},
"contentDigest": {
"$ref": "#/$defs/ContentDigest"
},
"createTime": {
"$ref": "#/$defs/EpochMillis"
},
"updateTime": {
"$ref": "#/$defs/EpochMillis"
}
}
},
"AgentVersionSummaryPage": {
"type": "object",
"additionalProperties": false,
"required": [
"totalCount",
"pageNumber",
"pagesAvailable",
"pageItems"
],
"properties": {
"totalCount": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
},
"pageNumber": {
"type": "integer",
"minimum": 1,
"maximum": 2147483647
},
"pagesAvailable": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
},
"pageItems": {
"type": "array",
"maxItems": 100,
"items": {
"$ref": "#/$defs/AgentVersionSummary"
}
}
}
},
"AgentOverview": {
"type": "object",
"additionalProperties": false,
"required": [
"agent",
"versionPage"
],
"properties": {
"agent": {
"$ref": "#/$defs/Agent"
},
"versionPage": {
"$ref": "#/$defs/AgentVersionSummaryPage"
}
}
},
"MetadataKey": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"allOf": [
{
"not": {
"enum": [
"preserved.heart.beat.interval",
"preserved.heart.beat.timeout",
"preserved.ip.delete.timeout"
]
}
},
{
"not": {
"pattern": "^__nacos\\.agent\\.endpoint\\."
}
}
]
},
"Metadata": {
"type": "object",
"maxProperties": 32,
"propertyNames": {
"$ref": "#/$defs/MetadataKey"
},
"additionalProperties": {
"type": "string",
"maxLength": 256
}
},
"DeclaredEndpoint": {
"type": "object",
"additionalProperties": false,
"required": [
"uri",
"transport"
],
"properties": {
"uri": {
"$ref": "#/$defs/AbsoluteEndpointUri"
},
"transport": {
"$ref": "#/$defs/TransportToken"
},
"priority": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647,
"default": 0
},
"weight": {
"type": "number",
"minimum": 0,
"maximum": 10000,
"default": 1.0
},
"metadata": {
"$ref": "#/$defs/Metadata"
}
}
},
"EndpointSource": {
"type": "string",
"enum": [
"RUNTIME",
"DECLARED"
]
},
"AgentCallInterface": {
"type": "object",
"additionalProperties": false,
"required": [
"protocol",
"descriptorMediaType",
"nativeDescriptor",
"endpointSourceOrder"
],
"properties": {
"protocol": {
"$ref": "#/$defs/ProtocolToken"
},
"protocolVersion": {
"$ref": "#/$defs/ProtocolVersion"
},
"descriptorMediaType": {
"$ref": "#/$defs/MediaType"
},
"nativeDescriptor": {
"$ref": "#/$defs/NonNullJsonValue"
},
"endpointSourceOrder": {
"type": "array",
"minItems": 1,
"maxItems": 2,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/EndpointSource"
}
},
"declaredEndpoints": {
"type": "array",
"maxItems": 64,
"items": {
"$ref": "#/$defs/DeclaredEndpoint"
}
}
}
},
"AgentVersionDetail": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"version",
"status",
"callInterfaces",
"contentDigest",
"createTime",
"updateTime"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"version": {
"$ref": "#/$defs/AgentVersion"
},
"status": {
"$ref": "#/$defs/AgentVersionStatus"
},
"callInterfaces": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"items": {
"$ref": "#/$defs/AgentCallInterface"
}
},
"author": {
"type": "string",
"maxLength": 128
},
"changeDescription": {
"type": "string",
"maxLength": 2048
},
"contentDigest": {
"$ref": "#/$defs/ContentDigest"
},
"createTime": {
"$ref": "#/$defs/EpochMillis"
},
"updateTime": {
"$ref": "#/$defs/EpochMillis"
}
}
},
"RuntimeVersionBinding": {
"type": "object",
"additionalProperties": false,
"required": [
"runtimeVersion",
"versionRange"
],
"properties": {
"runtimeVersion": {
"$ref": "#/$defs/AgentVersion"
},
"versionRange": {
"$ref": "#/$defs/VersionRange"
}
},
"description": "runtimeVersion must be contained by versionRange; this semantic relation requires domain validation."
},
"RuntimeEndpointSnapshotEndpoint": {
"type": "object",
"additionalProperties": false,
"required": [
"uri",
"transport"
],
"properties": {
"uri": {
"$ref": "#/$defs/AbsoluteEndpointUri"
},
"transport": {
"$ref": "#/$defs/TransportToken"
},
"priority": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647,
"default": 0
},
"weight": {
"type": "number",
"minimum": 0,
"maximum": 10000,
"default": 1.0
},
"metadata": {
"$ref": "#/$defs/Metadata"
}
}
},
"RuntimeEndpointState": {
"type": "string",
"enum": [
"AVAILABLE",
"DISABLED",
"UNHEALTHY"
]
},
"RuntimeEndpointSnapshotItem": {
"type": "object",
"additionalProperties": false,
"required": [
"endpoint",
"bindings",
"state",
"enabled",
"healthy",
"lastUpdatedTime"
],
"properties": {
"endpoint": {
"$ref": "#/$defs/RuntimeEndpointSnapshotEndpoint"
},
"bindings": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/RuntimeVersionBinding"
}
},
"state": {
"$ref": "#/$defs/RuntimeEndpointState"
},
"enabled": {
"type": "boolean"
},
"healthy": {
"type": "boolean"
},
"lastUpdatedTime": {
"$ref": "#/$defs/EpochMillis"
}
},
"oneOf": [
{
"properties": {
"state": {
"const": "DISABLED"
},
"enabled": {
"const": false
}
}
},
{
"properties": {
"state": {
"const": "UNHEALTHY"
},
"enabled": {
"const": true
},
"healthy": {
"const": false
}
}
},
{
"properties": {
"state": {
"const": "AVAILABLE"
},
"enabled": {
"const": true
},
"healthy": {
"const": true
}
}
}
]
},
"RuntimeEndpointSnapshot": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"protocol",
"items"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"protocol": {
"$ref": "#/$defs/ProtocolToken"
},
"version": {
"$ref": "#/$defs/AgentVersion"
},
"items": {
"type": "array",
"maxItems": 1000,
"items": {
"$ref": "#/$defs/RuntimeEndpointSnapshotItem"
}
}
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,752 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://nacos.io/schemas/ai/rad/0.1.0/rad-protocol.schema.json",
"$comment": "Copyright 1999-2026 Alibaba Group Holding Ltd. Licensed under the Apache License, Version 2.0.",
"title": "Nacos Remote Agent Discovery (RAD) Protocol 0.1.0",
"description": "Definition bundle for RAD Search, Discover, Watch snapshots, and Runtime Endpoint publication. Select an explicit x-entrypoints reference for validation.",
"x-status": "experimental",
"x-protocol-version": "0.1.0",
"x-entrypoints": {
"AgentSearchRequest": "#/$defs/AgentSearchRequest",
"AgentCatalogPage": "#/$defs/AgentCatalogPage",
"AgentDiscoveryRequest": "#/$defs/AgentDiscoveryRequest",
"AgentDiscoveryResult": "#/$defs/AgentDiscoveryResult",
"AgentEndpointRegistrationBatch": "#/$defs/AgentEndpointRegistrationBatch",
"AgentEndpointDeregistrationBatch": "#/$defs/AgentEndpointDeregistrationBatch"
},
"$defs": {
"NamespaceId": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[A-Za-z0-9_-]+$"
},
"AgentName": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"allOf": [
{
"pattern": "^[ -~]+$"
},
{
"pattern": "[!-~]"
}
]
},
"AgentVersion": {
"type": "string",
"minLength": 5,
"maxLength": 64,
"pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?$"
},
"VersionRange": {
"type": "string",
"minLength": 3,
"maxLength": 256,
"anyOf": [
{
"pattern": "^\\[(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?\\]$"
},
{
"pattern": "^(?:\\[|\\()(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?,(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\]|\\))$"
},
{
"pattern": "^(?:\\[|\\()(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?,\\)$"
},
{
"pattern": "^\\(,(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\]|\\))$"
}
],
"description": "An exact version or one continuous Maven-style interval using RAD version comparison. Bound ordering, equal-bound canonicalization, and runtimeVersion containment require domain validation."
},
"Label": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
},
"NonLatestLabel": {
"allOf": [
{
"$ref": "#/$defs/Label"
},
{
"not": {
"const": "latest"
}
}
]
},
"ProtocolToken": {
"type": "string",
"minLength": 1,
"maxLength": 32,
"pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,31}$"
},
"ProtocolVersion": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[!-~]+$"
},
"TransportToken": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[0-9A-Za-z-]{1,64}$"
},
"EndpointSource": {
"type": "string",
"enum": [
"RUNTIME",
"DECLARED"
]
},
"ContentDigest": {
"type": "string",
"pattern": "^sha256:[0-9a-f]{64}$"
},
"SourceRevision": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[!-~]+$",
"description": "Opaque equality token scoped to one Agent Version, protocol, and source."
},
"AbsoluteEndpointUri": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"format": "uri",
"allOf": [
{
"pattern": "^[A-Za-z][A-Za-z0-9+.-]*://"
},
{
"pattern": "^[^#]+$"
},
{
"not": {
"pattern": "^[A-Za-z][A-Za-z0-9+.-]*://[^/?#]*@"
}
}
],
"description": "A hierarchical absolute URI. Domain validation additionally requires a host and an explicit or scheme-derived effective port."
},
"MediaType": {
"type": "string",
"minLength": 3,
"maxLength": 128,
"pattern": "^[!-~]+/[!-~]+$"
},
"NonNullJsonValue": {
"not": {
"type": "null"
}
},
"Tag": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"MetadataKey": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"allOf": [
{
"not": {
"enum": [
"preserved.heart.beat.interval",
"preserved.heart.beat.timeout",
"preserved.ip.delete.timeout"
]
}
},
{
"not": {
"pattern": "^__nacos\\.agent\\.endpoint\\."
}
}
]
},
"Metadata": {
"type": "object",
"maxProperties": 32,
"propertyNames": {
"$ref": "#/$defs/MetadataKey"
},
"additionalProperties": {
"type": "string",
"maxLength": 256
}
},
"AgentProvider": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"url": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"format": "uri"
}
}
},
"AgentCatalogVersion": {
"type": "object",
"additionalProperties": false,
"required": [
"version",
"protocols"
],
"properties": {
"version": {
"$ref": "#/$defs/AgentVersion"
},
"labels": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/NonLatestLabel"
}
},
"protocols": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/ProtocolToken"
}
}
}
},
"AgentCatalogEntry": {
"type": "object",
"additionalProperties": false,
"required": [
"agentName",
"latestVersion",
"versions"
],
"properties": {
"agentName": {
"$ref": "#/$defs/AgentName"
},
"displayName": {
"type": "string",
"maxLength": 128
},
"description": {
"type": "string",
"maxLength": 2048
},
"iconUrl": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"format": "uri"
},
"provider": {
"$ref": "#/$defs/AgentProvider"
},
"tags": {
"type": "array",
"minItems": 1,
"maxItems": 32,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/Tag"
}
},
"latestVersion": {
"$ref": "#/$defs/AgentVersion"
},
"versions": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/AgentCatalogVersion"
}
}
}
},
"AgentSearchRequest": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentNameContains": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[ -~]+$"
},
"tagsAll": {
"type": "array",
"minItems": 1,
"maxItems": 32,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/Tag"
}
},
"protocolsAny": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/ProtocolToken"
}
},
"pageNo": {
"type": "integer",
"minimum": 1,
"maximum": 2147483647,
"default": 1
},
"pageSize": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20
}
}
},
"AgentCatalogPage": {
"type": "object",
"additionalProperties": false,
"required": [
"totalCount",
"pageNumber",
"pagesAvailable",
"pageItems"
],
"properties": {
"totalCount": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
},
"pageNumber": {
"type": "integer",
"minimum": 1,
"maximum": 2147483647
},
"pagesAvailable": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
},
"pageItems": {
"type": "array",
"maxItems": 100,
"items": {
"$ref": "#/$defs/AgentCatalogEntry"
}
}
}
},
"AgentReference": {
"type": "object",
"additionalProperties": false,
"required": [
"agentName"
],
"properties": {
"agentName": {
"$ref": "#/$defs/AgentName"
},
"version": {
"$ref": "#/$defs/AgentVersion"
},
"label": {
"$ref": "#/$defs/Label"
}
},
"not": {
"required": [
"version",
"label"
]
}
},
"AgentDiscoveryFilter": {
"type": "object",
"additionalProperties": false,
"properties": {
"protocols": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/ProtocolToken"
}
},
"protocolVersion": {
"$ref": "#/$defs/ProtocolVersion"
},
"transports": {
"type": "array",
"minItems": 1,
"maxItems": 16,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/TransportToken"
}
},
"endpointSources": {
"type": "array",
"minItems": 1,
"maxItems": 2,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/EndpointSource"
}
},
"metadataSelector": {
"$ref": "#/$defs/Metadata"
}
}
},
"AgentDiscoveryRequest": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"reference"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"reference": {
"$ref": "#/$defs/AgentReference"
},
"filter": {
"$ref": "#/$defs/AgentDiscoveryFilter"
}
}
},
"EndpointKey": {
"type": "object",
"additionalProperties": false,
"required": [
"uri",
"transport"
],
"properties": {
"uri": {
"$ref": "#/$defs/AbsoluteEndpointUri"
},
"transport": {
"$ref": "#/$defs/TransportToken"
}
}
},
"PublicationEndpoint": {
"type": "object",
"additionalProperties": false,
"required": [
"uri",
"transport"
],
"properties": {
"uri": {
"$ref": "#/$defs/AbsoluteEndpointUri"
},
"transport": {
"$ref": "#/$defs/TransportToken"
},
"priority": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647,
"default": 0
},
"weight": {
"type": "number",
"minimum": 0,
"maximum": 10000,
"default": 1.0
},
"metadata": {
"$ref": "#/$defs/Metadata"
}
}
},
"DeclaredEndpoint": {
"type": "object",
"additionalProperties": false,
"required": [
"uri",
"transport"
],
"properties": {
"uri": {
"$ref": "#/$defs/AbsoluteEndpointUri"
},
"transport": {
"$ref": "#/$defs/TransportToken"
},
"priority": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647,
"default": 0
},
"weight": {
"type": "number",
"minimum": 0,
"maximum": 10000,
"default": 1.0
},
"metadata": {
"$ref": "#/$defs/Metadata"
}
}
},
"RuntimeEndpoint": {
"type": "object",
"additionalProperties": false,
"required": [
"uri",
"transport",
"healthy"
],
"properties": {
"uri": {
"$ref": "#/$defs/AbsoluteEndpointUri"
},
"transport": {
"$ref": "#/$defs/TransportToken"
},
"priority": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647,
"default": 0
},
"weight": {
"type": "number",
"minimum": 0,
"maximum": 10000,
"default": 1.0
},
"metadata": {
"$ref": "#/$defs/Metadata"
},
"healthy": {
"type": "boolean"
}
}
},
"DeclaredEndpointSet": {
"type": "object",
"additionalProperties": false,
"required": [
"source",
"sourceRevision",
"endpoints"
],
"properties": {
"source": {
"const": "DECLARED"
},
"sourceRevision": {
"$ref": "#/$defs/SourceRevision"
},
"endpoints": {
"type": "array",
"maxItems": 64,
"items": {
"$ref": "#/$defs/DeclaredEndpoint"
}
}
}
},
"RuntimeEndpointSet": {
"type": "object",
"additionalProperties": false,
"required": [
"source",
"sourceRevision",
"endpoints"
],
"properties": {
"source": {
"const": "RUNTIME"
},
"sourceRevision": {
"$ref": "#/$defs/SourceRevision"
},
"endpoints": {
"type": "array",
"maxItems": 1000,
"items": {
"$ref": "#/$defs/RuntimeEndpoint"
}
}
}
},
"EndpointSet": {
"oneOf": [
{
"$ref": "#/$defs/DeclaredEndpointSet"
},
{
"$ref": "#/$defs/RuntimeEndpointSet"
}
]
},
"AgentDiscoveryCallInterface": {
"type": "object",
"additionalProperties": false,
"required": [
"protocol",
"descriptorMediaType",
"nativeDescriptor",
"endpointSets"
],
"properties": {
"protocol": {
"$ref": "#/$defs/ProtocolToken"
},
"protocolVersion": {
"$ref": "#/$defs/ProtocolVersion"
},
"descriptorMediaType": {
"$ref": "#/$defs/MediaType"
},
"nativeDescriptor": {
"$ref": "#/$defs/NonNullJsonValue"
},
"endpointSets": {
"type": "array",
"maxItems": 2,
"items": {
"$ref": "#/$defs/EndpointSet"
}
}
}
},
"AgentDiscoveryResult": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"version",
"contentDigest",
"callInterfaces"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"version": {
"$ref": "#/$defs/AgentVersion"
},
"contentDigest": {
"$ref": "#/$defs/ContentDigest"
},
"callInterfaces": {
"type": "array",
"maxItems": 16,
"items": {
"$ref": "#/$defs/AgentDiscoveryCallInterface"
}
}
}
},
"AgentEndpointRegistrationBatch": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"runtimeVersion",
"protocol",
"endpoints"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"runtimeVersion": {
"$ref": "#/$defs/AgentVersion"
},
"versionRange": {
"$ref": "#/$defs/VersionRange",
"description": "When omitted, the effective canonical value is [runtimeVersion]."
},
"protocol": {
"$ref": "#/$defs/ProtocolToken"
},
"endpoints": {
"type": "array",
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/PublicationEndpoint"
}
}
}
},
"AgentEndpointDeregistrationBatch": {
"type": "object",
"additionalProperties": false,
"required": [
"namespaceId",
"agentName",
"protocol",
"endpoints"
],
"properties": {
"namespaceId": {
"$ref": "#/$defs/NamespaceId"
},
"agentName": {
"$ref": "#/$defs/AgentName"
},
"protocol": {
"$ref": "#/$defs/ProtocolToken"
},
"endpoints": {
"type": "array",
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/EndpointKey"
}
}
}
}
}
}
+4
View File
@@ -74,5 +74,9 @@
- [API 集成测试规范](testing/api-integration-test-spec.md)
- [Java SDK 集成测试规范](testing/java-sdk-integration-test-spec.md)
## 共享 Schema
- [JSON Schema 索引](../schemas/README.md)
[AGENTS.md](../../AGENTS.md) 等 Agent 指南文件应只摘要这些规范以便本地执行。
当人、AI agent、模板或校验工具使用 API 指南时,规范仍然是规则来源。
+9 -1
View File
@@ -30,10 +30,18 @@ AI Registry 规范定义 Nacos 3.x 的 AI 资源模型。它扩展
- [AI 资源生命周期规范](ai-resource-lifecycle-spec.md)
- [AI Registry 适配器规范](ai-registry-adaptor-spec.md)
## Agent Registry
- [Agent 管理规范](agent-management-spec.md)
- [Remote Agent Discovery 协议规范](rad-protocol-spec.md)
- [Agent API 规范](agent-api-spec.md)
- [Agent 存储规范](agent-storage-spec.md)
- [A2A Agent Binding 与兼容规范](a2a-agent-spec.md)
- [共享 JSON Schema 索引](../../schemas/README.md)
## 资源类型
- [MCP Server 规范](mcp-server-spec.md)
- [A2A Agent 规范](a2a-agent-spec.md)
- [Prompt 规范](prompt-spec.md)
- [Skill 规范](skill-spec.md)
- [AgentSpec 规范](agentspec-spec.md)
+96 -52
View File
@@ -14,80 +14,124 @@
limitations under the License.
-->
# A2A Agent 规范
# A2A Agent Binding 与兼容规范
本文档定义 A2A AgentCard 资源在 AI Registry 中的领域契约。
| 项目 | 值 |
| --- | --- |
| 状态 | 实验性目标兼容契约 |
| 生效条件 | 标准 Agent 写路径切换 |
## 1. 身份
本文定义 A2A 作为 Nacos 标准 Agent 资源的一种协议 Binding,并规定历史 AgentCard API
的兼容 facade。标准模型由 [Agent 管理规范](agent-management-spec.md)定义;远程发现遵循
[RAD 协议规范](rad-protocol-spec.md)。
A2A Agent 标准身份为:
## 1. 生效、当前基线与身份
功能激活前,当前 Nacos Runtime 可以继续保存 `type=a2a` 资源,并使用旧 Config 与
Naming 布局。该实现仍符合当前 A2A 基线;本目标规范不表示它已经完成迁移。
标准 Agent 写路径激活后,第 2~7 节才对新请求成为规范性要求。激活和混合版本发布
必须显式执行:切换前以旧模型为事实源;切换后新写入使用标准 Agent 模型,旧接口成为
本文定义的兼容 Facade。
A2A 不是顶层 AI 资源类型。标准身份为:
```text
namespaceId -> a2a -> agentName
namespaceId -> agent -> agentName -> version -> protocol=a2a
```
`agentName` 是公开资源名。当前存储可能会先编码 agent name 再写入 Config 数据。
编码是实现细节,不应改变公开身份
历史 `namespaceId -> a2a -> agentName` 身份仅用于兼容。所有旧请求都适配到
`type=agent`;启用标准写路径后,不得继续创建新的 `a2a` 元数据或版本存储
## 2. 领域模型
## 2. A2A CallInterface
A2A Agent 资源包含 AgentCard 元数据和版本化 AgentCard 详情,可以包含
A2A Binding 是一个 `AgentCallInterface`
- agent name、description、provider、capabilities、skills、authentication 和协议字段;
- registration type
- version list 和 latest published version
- 通过 agent interface 表达的 service-style endpoints。
| Agent 字段 | A2A 映射 |
| --- | --- |
| `protocol` | 标准 token `a2a`。 |
| `protocolVersion` | 用于快速过滤的规范化 A2A 协议版本。 |
| `descriptorMediaType` | AgentCard JSON 媒体类型。 |
| `nativeDescriptor` | 完整规范化 AgentCard,不丢失已支持的上游字段。 |
| `declaredEndpoints` | 从 root URL 和 supported/additional interfaces 派生。 |
| `endpointSourceOrder` | 从兼容 registration type 派生。 |
当前 AgentCard 模型以 Google A2A 1.0.0 协议字段为准。Nacos 为兼容已有客户端,
仍保留若干 0.x 兼容字段,例如根级 `url``protocolVersion`
`preferredTransport``additionalInterfaces`
`supportsAuthenticatedExtendedCard`。这些字段只作为兼容输入存在;当 1.0.0
模型成为唯一支持契约后,legacy 字段可以被统一删除。
当前 descriptor 基线支持 A2A 1.0 字段和现有 0.x 兼容字段。Adapter 规范化时不得
用拼装出的通用 Agent 对象替换保存的 native descriptor。
未指定版本时,运行时查询解析 latest 版本。
`registrationType=URL` 映射为 `[DECLARED,RUNTIME]`
`registrationType=SERVICE` 映射为 `[RUNTIME,DECLARED]`。Registration type 是旧投影
字段,不参与 Agent 身份,也不进入新 API。
## 3. Endpoint 模型
## 3. 旧定义写入
A2A endpoint 可以由运行时客户端注册,并通过 A2A endpoint group 下的 Naming
service 表达。
旧 AgentCard release 和 Admin update 使用与标准 Agent API 相同的 AgentName 与 Version
校验。写入成功时创建或复用 Agent 元数据行,保存一个 A2A CallInterface,并将目标 Version
直接置为 online,不增加独立的旧 draft Pipeline。
Endpoint 解析规则:
固定规则:
- endpoint 绑定到具体 agent 版本
- endpoint metadata 应在可用时包含 transport、protocol binding、protocol version、
path、query、TLS support 和 tenant 数据
- 当存在多个兼容 endpoint 时,当前实现随机选择一个;这不是稳定策略契约,后续应由
endpoint-selection 规范细化。
- 首个 online Version 总是成为 `latest`
- 新增后续 Version 时,`setAsLatest=true` 移动 `latest``false` 保留当前有效指针;
- 标准 Agent publish 或 online 操作总是移动 `latest`
- 删除或下线当前 latest 时,选择剩余 online Agent Version 中最大的一个;没有剩余版本时删除 `latest`
- Client SDK 重复 release 已 online 的精确 Version 时成功 no-op
- 已存在精确 Version 的 canonical 内容不同时返回冲突;0.1.0 不提供同版本强制覆盖;
- 只有历史 API 已承诺幂等删除时,删除不存在的 Agent 或 Version 才成功 no-op。
Naming 是 endpoint 基础设施。A2A Agent 仍然是 AI Registry 资源
直接上线、冲突拒绝、删除和 latest 变化必须写审计日志,但不得记录完整 descriptor 或敏感 Endpoint metadata
## 4. API 和 SDK 行为
## 4. 旧 Runtime Endpoint 写入
- Admin API 可以注册、查询、更新、删除、列表和操作 A2A AgentCard 版本。
- Client API 和 SDK 可以查询 AgentCard、在支持时发布 AgentCard、注册/注销
endpoint、批量替换当前客户端拥有的 endpoint,并订阅 AgentCard 变更。
- gRPC payload 包含 AgentCard 查询/发布和 endpoint 注册请求,详见
[gRPC API 规范](../grpc-api/api-spec.md)。
旧单条和批量 Endpoint 操作在以下兼容 scope 内保持全量替换语义:
## 5. 当前兼容存储
```text
publisher + namespaceId + agentName + exactVersion + protocol=a2a
```
当前 A2A 实现通过 Config 形态记录保存 AgentCard 元数据和版本,并通过 Naming
service 表达 endpoint。这是兼容存储。标准模型应为
`ai_resource + ai_resource_version`
单条注册将 scope 替换为一个 Endpoint,批量注册替换为提交的集合。Adapter 将精确版本映射为
`runtimeVersion=version``versionRange=[version]`,并写入标准 Runtime Endpoint Registry。
## 6. 待迁移问题
即使不同精确版本使用相同的公开 Endpoint 自然键,Registry 也会保存不同的 publisher contribution
分组。旧 deregister 只删除请求精确版本的 contribution group。该内部兼容操作有意窄于 RAD
`Deregister`;后者会删除当前 publisher 对所提交自然键的全部 bindings。
- 将 AgentCard 元数据和版本行迁移到标准 AI 资源模型
- 定义从编码 Config dataId 到 resourceName 身份的迁移。
- 定义 endpoint 所有权、连接清理和确定性 endpoint 选择。
- 让 latest-version 行为与共享 label 模型对齐。
- 在 A2A 之上引入协议无关抽象,例如 `RemoteAgent``AgentService`。Nacos
是统一注册中心,不应将 AI Registry 模型绑定到某一个特定 agent 协议,除非该协议
已经成为明确的社区标准。A2A 应作为更高层 remote-agent 资源模型下的一种协议绑定。
Endpoint 可以先于 Agent 或 Version 定义发布,但不得隐式创建 Agent 定义
## 7. 演进说明
## 5. 旧查询投影
A2A 协议版本、AgentCard 字段、安全方案和 endpoint 表达都可能快速演进。当前基线为
A2A 1.0.0,0.x 兼容字段只为避免破坏已有客户端而保留。当上游 A2A 模型变化,或
Nacos 引入协议无关的 remote-agent 抽象时,本规范可能需要不兼容调整;调整必须定义
迁移和兼容行为。
兼容查询先选择一个包含合法 `protocol=a2a` CallInterface 的 online Version。显式 Version
执行大小写敏感精确查询;未指定时使用 Agent `latest`。Client 运行时读取还要求 Agent enabled 且可见。
投影规则:
| 查询模式 | 结果 |
| --- | --- |
| `URL` | 返回保存的 native AgentCard 及其声明 interfaces。 |
| `SERVICE` 且存在匹配 Runtime Endpoint | 将确定性 Runtime Endpoint 集合投影到 AgentCard interfaces 和 root URL。 |
| `SERVICE` 且无匹配 Runtime Endpoint | 回退到保存的声明 AgentCard。 |
Runtime 投影排除 `enabled=false`,保留 `healthy=false`,因为旧 DTO 没有健康字段。投影先按
priority、再按 Endpoint 自然键稳定排序。source revision、health、priority、weight 和通用 metadata
等 RAD 新字段不进入旧 DTO。
旧 list/version-list 从 Agent 元数据和 online A2A Version 投影。旧订阅事件必须经过与 GET
相同的投影。初始目标不存在时,旧订阅可以继续保留;这是兼容行为,不属于 RAD Watch 契约。
## 6. 兼容表面
| 表面 | 状态与窗口 |
| --- | --- |
| Java `A2aService` 和旧 A2A gRPC Payload | 仅兼容;当前不设删除版本。 |
| Admin `/v3/admin/ai/a2a``A2aMaintainerService` | 兼容到 4.0.x 窗口。 |
| Console `/v3/console/ai/a2a` | 兼容到 3.4.x 窗口。 |
兼容窗口内,旧路径、Payload type、DTO、能力位、鉴权身份和响应包装保持稳定。新 Agent/RAD API
不得暴露 `registrationType``setAsLatest` 或 AgentCard 专属列表包装。
历史数据迁移、混合版本双读双写、事实源切换、回滚和清理属于滚动升级设计,不由本 API 兼容规范定义。
## 7. 演进
上游 AgentCard 字段或 A2A 协议版本变化由 A2A Adapter 和版本化 Agent CallInterface 处理,
不得重新定义标准 Agent 身份或协议无关的 RAD 结果。
+336
View File
@@ -0,0 +1,336 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Agent API 规范
| 项目 | 值 |
|---|---|
| 状态 | 实验性目标契约;不是当前已实现 API 清单 |
| 目标版本线 | Nacos 3.3 |
| 范围 | Agent 与 RAD 的 Client HTTP/gRPC、Admin HTTP/Maintainer SDK 和 Console HTTP Binding |
本文把 [Agent 管理规范](agent-management-spec.md)和
[RAD 协议规范](rad-protocol-spec.md)绑定到 Nacos API。实现声明新的 Agent/RAD
能力位后,必须遵循本文。在新 Binding 实现并完成能力协商前,现有 A2A API 仍由
[A2A Agent 规范](a2a-agent-spec.md)约束。
## 1. API 分层与公共规则
| 接口面 | 传输 | 主要调用方 | 职责 |
|---|---|---|---|
| Client | HTTP 和 gRPC | Agent 使用方与 Runtime 发布方 | Search、Discover、Watch、注册和注销 |
| Admin | HTTP | Maintainer SDK 与管理集成 | Agent CRUD、Version 生命周期和 Runtime 查看 |
| Console | HTTP | Nacos Console UI | 面向 UI 的 Admin 语义 Facade |
HTTP API 遵循 Nacos v3 约定:
- Client 路径以 `/v3/client/ai/agents` 开头;
- Admin 路径以 `/v3/admin/ai/agents` 开头;
- Console 路径以 `/v3/console/ai/agents` 开头;
- 响应使用 `Result<T>`;新 Controller 使用 `@NacosApi`
`@Since(version = "3.3.0")`、对应 `ApiType``SignType.AI` 以及 `READ`
`WRITE` 鉴权;
- GET 输入使用 query,写入使用 JSON body`agentName` 按原值比较,不作为
PathVariable
- gRPC 继续使用统一 Nacos `Payload` stream 和 `metadata.type`,不增加 proto
service method。
六个 RAD 根消息直接复用,不再创建一套领域模型。Java 可以用字段等价的
`Page<AgentCatalogEntry>` 实现 `AgentCatalogPage``Result<T>`、gRPC wrapper、
`ClientLivenessInfo` 和 Console 专用视图属于 Binding 对象,不进入 RAD Schema。
### 1.1 Namespace 规则
| 调用方 | 规则 |
|---|---|
| 普通 Client SDK | SDK 实例绑定一个 namespace。公开方法不接受 namespace 参数;Proxy 复制请求并在传输前注入绑定值。 |
| Client HTTP 调用方 | 可以显式提交 `namespaceId`;省略时 Binding 在进入 RAD 前注入规范化默认值 `public`。 |
| Maintainer SDK 和 Admin API | Maintainer SDK 实例不绑定 namespace;每个请求都显式提交 `namespaceId`,不提供默认 namespace 重载。 |
如果普通 Client SDK 接受的模型中已带非空 `namespaceId`,它必须拒绝与 SDK namespace
不同的值,并且不得修改调用方原对象。
### 1.2 并发、结果与错误
Agent 元数据更新使用 `expectedMetaVersion`draft 内容更新使用
`expectedContentDigest`。列表使用分页;`RuntimeEndpointSnapshot` 是完整、不分页的快照。
| 条件 | 必须返回的结果 |
|---|---|
| 字段缺失或非法、URI/range 非法、Endpoint 自然键重复 | 标准参数错误 |
| Discover 目标不可见或不存在 | `RESOURCE_NOT_FOUND`,不区分可见性 |
| 不存在 Agent 定义时 Endpoint 预注册 | 完成结构、鉴权、配额和冲突校验后接受 |
| 元数据 CAS、内容 CAS 或 Publisher payload 冲突 | `RESOURCE_CONFLICT` |
| Version 生命周期转换非法 | `ILLEGAL_STATE` |
| HTTP heartbeat 找不到 Client | HTTP 404 和独立应用码 `HTTP_CLIENT_NOT_FOUND` |
| 协商后的传输不支持能力 | 本地 `FEATURE_NOT_SUPPORTED`,不发送远程请求 |
| 注销不存在的 contribution | 成功且不发生变更 |
| 合法 Runtime 查询没有实例 | 成功返回 `items=[]` |
| Discover Filter 没有匹配 | 按 RAD 返回类型化空结果,不返回 `NOT_FOUND` |
HTTP 状态与 `Result.code` 使用通用 v3 异常映射;gRPC Response 暴露等价错误类别。
新增应用错误码的数值由实现变更统一分配,但不得与普通 `RESOURCE_NOT_FOUND` 混用。
## 2. Client API
### 2.1 Java SDK 契约
面向用户的接口命名为 `AgentDiscoveryService`,应用代码不必感知 RAD 缩写。A2A
兼容期内:
```text
AiService extends AgentDiscoveryService, A2aService
```
| 能力 | 方法 | 输入 | 返回 |
|---|---|---|---|
| Search | `searchAgents` | 不允许调用方控制 namespace 的 `AgentSearchRequest` | `Page<AgentCatalogEntry>` |
| Discover | `discoverAgent` | `AgentReference` | `AgentDiscoveryResult` |
| 过滤 Discover | `discoverAgent` | `AgentReference``AgentDiscoveryFilter` | `AgentDiscoveryResult` |
| Watch | `subscribeAgent` | Reference、可选 Filter、Listener | 当前 `AgentDiscoveryResult` |
| 取消 Watch | `unsubscribeAgent` | 相同 Reference、Filter 和 Listener identity | `void` |
| 注册 | `registerAgentEndpoints` | `AgentEndpointRegistrationBatch` | `void` |
| 注销 | `deregisterAgentEndpoints` | `AgentEndpointDeregistrationBatch` | `void` |
`subscribeAgent` 返回当前完整结果,之后投递完整替换结果。`getAll`
`selectOneHealthy`、协议选择、priority/weight 选址和实际 Agent Calling 是 SDK 本地
helper,不增加远程操作。
注册是按自然键 upsert,不替换未提交 Endpoint。SDK 将注册 Batch 保存为 redo 意图。
首个实现可以不增加通用 Agent 定义发布方法,但现有 `A2aService.releaseAgentCard`
必须通过兼容 Adapter 保持可用。后续 Client SDK 提供可选的代码式 Agent 发布:
`autoSubmit=false` 创建 draft`autoSubmit=true` 执行普通 submit Pipeline;它不是
force-publish,注册 Endpoint 也不会隐式创建定义。
### 2.2 传输矩阵
| 能力 | HTTP | gRPC |
|---|:---:|:---:|
| Search | 是 | 是 |
| Discover | 是 | 是 |
| Watch 和 Push | 否 | 是 |
| 注册和注销 | 是 | 是 |
| Publisher heartbeat | 是 | 复用 gRPC connection lifecycle |
HTTP-only SDK 在本地拒绝 Watch,不得通过轮询伪装 Watch。写入超时后,只有 SDK
确定服务端未处理请求时才允许切换传输;结果未知的 gRPC 写入不得盲目通过 HTTP 重试。
### 2.3 Client HTTP 路径
| Method | Path | 输入 | 返回 |
|---|---|---|---|
| GET | `/v3/client/ai/agents/search` | RAD Search query | `Result<Page<AgentCatalogEntry>>` |
| GET | `/v3/client/ai/agents` | RAD Reference 和可选 Filter query | `Result<AgentDiscoveryResult>` |
| POST | `/v3/client/ai/agents/endpoints` | `AgentEndpointRegistrationBatch` | `Result<ClientLivenessInfo>` |
| DELETE | `/v3/client/ai/agents/endpoints` | JSON `AgentEndpointDeregistrationBatch` | `Result<Void>` |
| PUT | `/v3/client/ai/agents/endpoints/heartbeat` | 无 body | `Result<ClientLivenessInfo>` |
Search query 名称与 RAD 字段相同。重复 `tagsAll` 取 AND,重复 `protocolsAny` 取 OR
`agentNameContains` 是大小写敏感的 literal substring。
Discover 直接映射 `agentName``version``label`。重复 Filter 参数为 `protocol`
`transport``endpointSource``protocolVersion` 为单值。`metadataSelector` 使用一个
URL encoded JSON object,而不是动态 `metadata.<key>` 参数名。
Endpoint 路径只使用 POST 和 DELETE。POST 已经 upsert 完整 Endpoint 值,通用 PUT 会
引入不明确的局部更新语义。GET 也没有必要:消费者使用 Discover,管理员使用
`RuntimeEndpointSnapshot`。0.1 Binding 只定义带 JSON body 的 DELETE,并要求 Client
和 Gateway 保留该 body。
### 2.4 HTTP Publisher Identity 与活性
Endpoint 写入和 heartbeat 必须携带:
```text
X-Nacos-Client-Id: http-<ipToken>-<processToken>-<clientSequence>-<createTimestamp>
Request-Module: AI
```
服务端将 Client id 视为匹配 `[A-Za-z0-9._:-]+`、长度 1256 的 opaque 值。
官方生成器只使用 `[A-Za-z0-9-]`,包含至少 96 bit 的进程随机熵,以
`clientSequence` 区分同进程的 SDK 实例,并可加入诊断用 PID token。重试、切换
Server 和 redo 保持 id;进程重启生成新 id。它是路由身份,不是 credential。
`ClientLivenessInfo` 只包含:
```text
heartbeatIntervalMillis < unhealthyTimeoutMillis < expireTimeoutMillis
```
最近一次成功注册或 heartbeat 响应决定调度。一个 heartbeat 维持整个 Client 活性,
与 Endpoint 数量无关;Endpoint 写入同样刷新活性。没有剩余 Endpoint 的 Client 被删除并停止 heartbeat。
| 状态 | Runtime 行为 |
|---|---|
| `ACTIVE` | Contribution 使用当前 Naming health。 |
| `UNHEALTHY` | 超过 `unhealthyTimeoutMillis` 后 Contribution 仍可发现,但 `healthy=false`。 |
| `EXPIRED` | 超过 `expireTimeoutMillis` 后删除 Client 拥有的全部 Contribution。 |
服务端根据 `clientId`,使用 Distro type `AI_AGENT_HTTP_CLIENT` 路由 HTTP Publisher
状态。只有责任节点持有 native Client、`lastActiveTime` 和超时任务;Peer 接收重建
Naming/RAD 投影所需的完整 Client state。新 Owner 只有在收到完整 Snapshot 后才启动
故障转移宽限,否则返回 `HTTP_CLIENT_NOT_FOUND`Client 随后 redo 全部期望 Endpoint
分组。首次写入把 Client id 绑定到鉴权主体和 namespace;后续不匹配时拒绝。同一字符串
在其他模块不共享活性和清理状态。
### 2.5 gRPC Payload 与能力位
| Request | Response | 语义 |
|---|---|---|
| `AgentSearchRequest` | `AgentSearchResponse` | Search 并返回目录分页 |
| `AgentDiscoveryRequest` | `AgentDiscoveryResponse` | 一次 Discover |
| `AgentSubscribeRequest` | `AgentSubscribeResponse` | 订阅或取消;订阅成功时返回不透明 `watchKey` 和当前完整结果 |
| `AgentDiscoveryNotifyRequest` | `AgentDiscoveryNotifyResponse` | 为一个 `watchKey` Push `SNAPSHOT``TERMINATED` 事件并接收 ACK |
| `AgentEndpointRegisterRequest` | `AgentEndpointOperationResponse` | 注册一个 RAD Batch |
| `AgentEndpointDeregisterRequest` | `AgentEndpointOperationResponse` | 注销一个 RAD Batch |
所有 Request 的 module 为 `ai`。gRPC Endpoint Contribution 归属于
`RequestMeta.connectionId`,不增加 Client id 或 heartbeat Payload。连接断开后删除该
Connection 的 Contribution;重连取得新 connection id,并 redo Endpoint 和订阅。
`AgentSubscribeResponse.watchKey` 是 Binding 为已接受 Wire Subscription 定义的不透明
身份。SDK 将它映射到规范化本地 Watch 身份,不解析其内容。
`AgentDiscoveryNotifyRequest` 包含 `watchKey``eventType`
- `SNAPSHOT` 必须携带一个完整 `AgentDiscoveryResult`,且不携带错误;
- `TERMINATED` 不携带 Result,并且本版本固定要求 `errorCode=NOT_FOUND`
- 两种事件都使用 `AgentDiscoveryNotifyResponse` 确认;
- `TERMINATED` 只关闭共享 Payload Connection 上由 `watchKey` 标识的 Watch,不关闭
Connection 或其他 Watch。
SDK 对 `SNAPSHOT` 原子替换缓存结果。对于 `TERMINATED`SDK 投递终止状态,仅删除该
Watch 及其 Redo State,再发送 ACK。Reconnect 后,SDK 丢弃旧 Connection 维度的
`watchKey`,使用规范化本地 Watch 身份重新订阅,并保存新 Response 中的 `watchKey`
和当前结果。这些 Request 和 Response 是 Nacos gRPC Binding 对象,不增加 RAD 的六个
根消息。
目标能力位如下:
| 常量 | Wire key | 含义 |
|---|---|---|
| `SERVER_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | Server 接受 RAD Search、Discover 和 Watch Payload |
| `SERVER_AGENT_ENDPOINT_V1` | `agentEndpointV1` | Server 接受 RAD Endpoint Publication Payload |
| `SDK_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | SDK 接受 RAD Discovery Push |
`SERVER_AGENT_REGISTRY``SERVER_AGENT_CARD_V1``SDK_AGENT_REGISTRY` 只约束
旧 A2A 契约。新能力位缺失时,不得通过旧能力位 fallback 发送 RAD Payload。
### 2.6 幂等与 Redo
| 事件 | 必须行为 |
|---|---|
| 重复相同 Register | 成功且语义不变 |
| Register 修改非身份字段 | Upsert 当前 Publisher Contribution |
| 同一 Batch 出现重复自然键 | 拒绝整个 Batch |
| 重复 Deregister | 成功且语义不变 |
| 重复 heartbeat | 只刷新 Client 活性 |
| HTTP timeout | 保持 Client id 和 payload,退避重试 |
| `HTTP_CLIENT_NOT_FOUND` | 将全部本地 Endpoint 意图标记为未注册,并按完整分组 redo |
| gRPC reconnect | 使用新 connection id redo Endpoint 和订阅 |
| 跨传输注销 | 禁止;一个 Publisher identity 不能删除另一传输的 Contribution |
SDK 在第一次写入前记录期望状态。Shutdown 执行 best-effort 注销,expire 作为清理兜底。
参数、鉴权和 Publisher 冲突错误不进入无限 redo。
## 3. Admin API 与 Maintainer SDK
Admin 读取不隐式执行数据面 Discover,也不把 Runtime Endpoint 注入 Version descriptor。
### 3.1 Agent 与读取视图
| Method | Path | 动作 | 返回 |
|---|---|---|---|
| POST | `/v3/admin/ai/agents` | 原子创建 Agent 和 initial draft | `Result<AgentOverview>` |
| GET | `/v3/admin/ai/agents` | 读取 Agent 和首个有界 Version Summary page | `Result<AgentOverview>` |
| PUT | `/v3/admin/ai/agents` | 使用 metadata CAS 更新 Agent 可写字段 | `Result<Agent>` |
| DELETE | `/v3/admin/ai/agents` | 删除 Agent 定义及 Version 内容 | `Result<Void>` |
| GET | `/v3/admin/ai/agents/list` | 筛选和分页 Agent Summary | `Result<Page<AgentSummary>>` |
| GET | `/v3/admin/ai/agents/versions` | 分页读取 Version Summary | `Result<Page<AgentVersionSummary>>` |
| GET | `/v3/admin/ai/agents/version` | 读取一个精确 Version 定义 | `Result<AgentVersionDetail>` |
| GET | `/v3/admin/ai/agents/runtime-endpoints` | 读取一个 Protocol 的完整 Runtime Snapshot,可按 Version 过滤 | `Result<RuntimeEndpointSnapshot>` |
Runtime 查询输入为 `namespaceId + agentName + protocol + version?``protocol` 必填。
省略 `version` 时,对该 Protocol 的每个 Endpoint 自然键返回一项及其全部 Binding;提交时只保留匹配 Binding。
查询不应用 `endpointSourceOrder`,不要求定义存在,没有 Instance 时返回空 items。
Create 包含 Agent 可写字段和必填 `initialDraft`。Agent、Version row 与 Storage 写入
具有一个逻辑原子结果,并补偿局部失败。Update 可以修改展示信息、tags、extensions、
enabled 状态、owner 和 scope,但不能修改身份、Version 内容、label 或派生 Catalog。
删除定义后,普通 Discover 立即不可见,但不会删除生命周期独立的 Runtime Publication。
### 3.2 Version 生命周期路径
| Method | Path | 转换或动作 | 返回 |
|---|---|---|---|
| POST | `/v3/admin/ai/agents/draft` | 创建新 draft,可复制一个精确 Version | `Result<AgentVersionDetail>` |
| PUT | `/v3/admin/ai/agents/draft` | 使用 content-digest CAS 更新 draft | `Result<AgentVersionDetail>` |
| DELETE | `/v3/admin/ai/agents/draft` | 删除 draft | `Result<Void>` |
| POST | `/v3/admin/ai/agents/submit` | `draft -> reviewing`,或统一的无 Pipeline 转换 | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/publish` | `reviewed -> online` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/force-publish` | 经审计地绕过 Pipeline 到 `online` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/redraft` | `reviewed -> draft` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/online` | `offline -> online` | `Result<AgentVersionSummary>` |
| POST | `/v3/admin/ai/agents/offline` | `online -> offline` | `Result<AgentVersionSummary>` |
| PUT | `/v3/admin/ai/agents/labels` | 更新自定义 label`latest` 仍由 Server 管理 | `Result<Agent>` |
每个动作都以 `namespaceId + agentName + exact version` 标识目标;写入时省略 Version
永远不表示 latest。`force-publish` 使用普通 Agent WRITE 权限,不增加权限点,但成功和
失败都记录调用主体、资源身份、原状态、目标状态、结果、request id 和时间。审计不记录
descriptor 或敏感 metadata。首版不提供同 Version 强制替换内容的 API。
### 3.3 Maintainer SDK
`AiMaintainerService.agent()` 返回 `AgentMaintainerService`;兼容期内继续保留
`AiMaintainerService.a2a()`。Agent Maintainer 接口一一映射 Admin HTTP,复杂写入使用
Request/Command 对象。它不绑定 namespace,每次调用都要求 `namespaceId`,不增加
Maintainer gRPC 传输。
## 4. Console API
Console 使用 `/v3/console/ai/agents`,镜像 Admin 的每个相对路径、请求、结果、生命周期
和鉴权意图。它是 UI Facade,不是第二套 Agent Application Service。
唯一 Console 专用响应为 `ConsoleRuntimeEndpointView`,它包装
`RuntimeEndpointSnapshot` 并增加:
```text
namingServiceRef { namespaceId, groupName, serviceName }
```
Backend 计算该引用,Browser 不实现 AgentName codec 或 Naming service composer。
Version 页面先读取 `AgentVersionDetail`,从 `callInterfaces[]` 构建 Protocol 页签,并在
选中页签时按当前 Version 懒加载一个 Runtime Snapshot。即使 CallInterface 没有
`RUNTIME`,它也可以查询并单独展示已注册状态,同时说明这些地址当前不会进入该 Version
的 Discover。首版 Agent Console API 不提供 Runtime 修改;UI 跳转 Naming Instance
页面执行 enable 或 disable。
Console 不提供 RAD Search、Discover、Watch、Endpoint Publication 或远程 Agent Calling。
## 5. 实现与兼容要求
实现必须一起完成以下工作,才能声明 Agent 或 RAD 能力:
1. API 对象、校验、错误映射、鉴权和审计;
2. gRPC Payload 注册与能力协商;
3. HTTP Publisher Distro 状态、活性、幂等和 redo;
4. Java SDK namespace 绑定、缓存、Watch、重连和 Endpoint redo
5. Admin/Maintainer 与 Console 契约;
6. 旧 A2A Facade 转换;
7. OpenAPI、Java SDK 和 Maintainer SDK 集成测试场景矩阵与 coverage registry。
旧 Console A2A API 支持到 Nacos 3.4 版本线;旧 Admin 和 Maintainer A2A API 保留到
Nacos 4.0 兼容边界。历史数据迁移和混合版本滚动升级属于独立规范,不得从本 API-only
契约推断。
+359
View File
@@ -0,0 +1,359 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Agent 管理规范
本文档定义 Nacos AI Registry 中协议无关的 Agent 管理模型,并细化
[AI 资源模型规范](ai-resource-model-spec.md)、
[AI 资源生命周期规范](ai-resource-lifecycle-spec.md)和
[Naming 规范](../naming/README.md)。
本文档是 Agent 模型迁移的规范目标契约。服务端或 SDK 在对应行为实现之前不得声明支持
本契约。在相关能力声明之前,当前运行时契约仍以
[A2A Agent 规范](a2a-agent-spec.md)为准。
## 1. 范围与边界
本模型拆分三类具有不同生命周期的事实:
```text
Agent
1 --- N AgentVersion
1 --- N AgentCallInterface
1 --- N DECLARED Endpoint
Runtime publisher --- N RUNTIME Endpoint
RUNTIME Endpoint --- versionRange match ---> AgentVersion + AgentCallInterface
```
| 事实 | 职责 | 事实源 |
| --- | --- | --- |
| `Agent` | 稳定身份、目录元数据、owner、可见性和版本治理。 | `ai_resource` |
| `AgentVersion` | 一个版本化调用定义,只在 draft 阶段可修改。 | `ai_resource_version` 和 AI Storage |
| `AgentCallInterface` | 一个协议绑定及其声明地址。 | `AgentVersionContent` |
| `RuntimeEndpoint` | 活跃 publisher 的可调用地址和兼容版本范围。 | Naming 运行时状态 |
A2A 是首个协议 Adapter。通用 Agent 模型不得包含 A2A 专有 capability、skill、
security、task 或 message 字段的并集。这些字段保留在协议原生 descriptor 中。
本规范不定义 MCP 资源或实际远程调用。Nacos 返回调用元信息,但不代理 Agent message、
task、session、stream、retry 或 credential。Search、Discover、Watch 和运行时发布的
线上对象由 [RAD 协议规范](rad-protocol-spec.md)定义。Runtime 发布归属、物理存储、
Naming 映射、codec 和 revision 算法由
[Agent 存储规范](agent-storage-spec.md)定义。
## 2. 身份与校验
### 2.1 Agent 身份
Agent 标准身份为:
```text
namespaceId + resourceType=agent + agentName
```
`agentName` 是公开 `resourceName`,规则如下:
- 包含 164 个 `U+0020..U+007E` 范围内的可打印 ASCII 字符;
- 至少包含一个非空格字符;
- 按提交原值保存并区分大小写;
- 服务端不得 trim、lowercase、slug 或进行其他改写;
- 创建后不可变。
`displayName` 是可选的 Unicode 展示字段。`displayName` 缺失或为空白时,展示层必须
使用 `agentName``displayName` 不参与身份、鉴权、存储 key 或 Endpoint 匹配。
精确查询比较原始 `agentName`。名称过滤执行 literal substring 匹配;持久化实现必须
转义 `%``_` 等通配符字符,而不是把它们解释为 pattern。
### 2.2 Version 身份
Agent Version 身份为:
```text
namespaceId + resourceType=agent + agentName + version
```
`version` 使用 `MAJOR.MINOR.PATCH[-PRERELEASE]`,最长 64 个字符:
- `MAJOR``MINOR``PATCH``0` 或不带前导零的正整数;
- `PRERELEASE` 包含一个或多个以点分隔的 `[0-9A-Za-z-]+` 标识,纯数字标识不得
带前导零;
- 不接受由 `+` 引入的 build metadata
- 原值保存并按大小写敏感比较;
- 包括兼容 facade 在内的所有 Agent 写入路径都执行这些规则。
Version 顺序首先按 major、minor、patch 数值比较。正式版高于同核心版本的 prerelease。
Prerelease 标识从左到右比较:纯数字标识按数值排序且低于非数字标识;非数字标识按
大小写敏感的 ASCII 顺序比较;其他部分相同时,标识更多的序列更高。
Version label 匹配 `[A-Za-z0-9][A-Za-z0-9._-]{0,63}` 并按大小写敏感比较。
`latest` 是服务端管理的保留指针,不能通过自定义 label 写入创建、替换或删除。
### 2.3 Endpoint 身份
DECLARED 和 RUNTIME 来源使用同一个 `Endpoint` 值对象。在一个 Agent 协议分组中,其
自然身份为:
```text
(namespaceId, agentName, protocol,
normalizedHost(uri), effectivePort(uri), normalizedTransport)
```
不存在公开 `endpointId`。URI path、query、metadata、priority 和 weight 不参与身份,
同一 publisher 可以更新这些字段。
## 3. Agent 资源
Agent 资源包含以下字段:
| 字段 | 必选 | 含义 |
| --- | :---: | --- |
| `namespaceId` | 是 | 隔离边界。 |
| `agentName` | 是 | 稳定公开身份。 |
| `displayName` | 否 | Unicode 展示名称。 |
| `description` | 否 | 目录描述。 |
| `iconUrl` | 否 | 目录图标 URI。 |
| `provider` | 否 | 提供方 `name``url`;它不是管理 owner。 |
| `tags[]` | 否 | 公开目录标签和精确匹配检索标签。 |
| `extensions` | 否 | 用于公开 Agent 级扩展的命名空间化 `Map<String, JsonValue>`。 |
| `status` | 是 | `enable``disable`。 |
| `owner` | 是 | 管理 owner。 |
| `scope` | 是 | 可见性 scope。 |
| `versionInfo` | 只读 | 共享的 editing、reviewing、online count 和 label 摘要。 |
| `versionCatalog` | 只读 | online Version 和 protocol 的紧凑目录。 |
| `metaVersion` | 只读 | 元数据 CAS 版本。 |
| `createTime``updateTime` | 只读 | 审计时间。 |
固定以下不变量:
- Agent 元数据不内嵌协议 descriptor、Endpoint、健康状态或完整版本历史。
- `tags` 是本版本唯一的公开通用分类列表。
- `extensions` 不影响身份、鉴权、版本选择、Endpoint 选择或默认检索。其中不得包含
credential 或服务端内部状态。
- 更新目录或扩展字段会推进 `metaVersion`,但不会创建 Agent Version。
- 协议 Adapter 只能在首次创建 Agent 时使用 native descriptor 初始化调用方未提供的
目录字段。后续 descriptor 更新不得覆盖独立治理的 Agent 元数据。
`versionCatalog` 包含 `latestVersion``onlineVersions[]`。每个 online 条目只包含
`version``labels[]``protocols[]`。它由服务端派生,不是客户端可写事实。
## 4. Agent Version 与生命周期
### 4.1 Version 元数据与内容
Agent Version 暴露以下元数据:
| 字段 | 必选 | 含义 |
| --- | :---: | --- |
| `namespaceId``agentName``version` | 是 | 精确版本身份。 |
| `status` | 是 | 共享 AI Resource Version 状态。 |
| `callInterfaces[]` | 是 | 有序协议绑定;至少一个。 |
| `author` | 否 | Version 作者。 |
| `changeDescription` | 否 | Version 变更说明。 |
| `contentDigest` | 只读 | canonical Version 内容的 SHA-256 摘要。 |
| `createTime``updateTime` | 只读 | 审计时间。 |
完整存储 payload 是一个 `AgentVersionContent` 对象:
```text
AgentVersionContent
kind = AgentVersionContent
schemaVersion = 1
callInterfaces[]
```
Canonical 序列化使用 RFC 8785 JCS,并保留 CallInterface 和 declared Endpoint 的业务
顺序。`contentDigest` 对 canonical UTF-8 bytes 计算并使用 `sha256:<lowercase hex>`
准确的 I-JSON 与校验规则由 [Agent 存储规范](agent-storage-spec.md)定义。
### 4.2 生命周期规则
Agent Version 使用共享生命周期:
| 状态 | 内容可修改 | 可进入普通 RAD 发现 |
| --- | :---: | :---: |
| `draft` | 是 | 否 |
| `reviewing` | 否 | 否 |
| `reviewed` | 否 | 否 |
| `online` | 否 | 是 |
| `offline` | 否 | 否 |
`ai_resource_version.status` 是生命周期事实源。`publishPipelineInfo` 只记录审核执行过程
和结果。
一个 Agent 最多存在一个 editing Version 和一个 reviewing Version。Draft 进入
reviewing 后内容冻结。Reviewed、online 和 offline 内容不得原地更新。本版本契约不提供
强制替换同 Version 内容的操作。
`latest` 是服务端管理的 label,并且必须始终指向 online Version。以下 Agent
专属规则细化通用 AI 生命周期规则:
- 每次标准 publish 或 online 转换成功后,都将目标 Version 设为 `latest`
- legacy A2A 发布的 `setAsLatest=false` 是首版唯一例外,并保留当前有效的
`latest`
- 即使通过上述兼容路径发布,首个 online Version 仍然会建立 `latest`
- 删除或下线当前 `latest` 时,选择剩余 online Version 中 SemVer 最大的一个;
- 删除最后一个 online Version 时删除 `latest`
- 删除或下线当前 `latest` 之外的 online Version 时,不触发重算。
online 状态或 label 变化时,服务端必须在一次逻辑更新中重建 `versionCatalog` 和派生的
protocol 检索 token。只要存在 online Version,就必须存在且仅存在一个有效的
`latestVersion`,并且它必须出现在 `onlineVersions` 中。
Agent 元数据、Agent Version 定义和 Runtime Endpoint 之间不互相拥有生命周期。删除或
disable Agent 定义会改变读取投影,但不会删除仍然活跃的运行时 publisher 状态。
## 5. CallInterface 与 Declared Endpoint
### 5.1 AgentCallInterface
每个 Agent Version 包含一个有序、非空的 `callInterfaces[]` 列表。每项包含:
| 字段 | 必选 | 含义 |
| --- | :---: | --- |
| `protocol` | 是 | 规范 protocol token;同一 Version 内唯一。 |
| `protocolVersion` | 否 | 快速协议协商值;不作为接口身份。 |
| `descriptorMediaType` | 是 | `nativeDescriptor` 的媒体类型。 |
| `nativeDescriptor` | 是 | 完整协议原生 descriptor。 |
| `endpointSourceOrder[]` | 是 | `RUNTIME``DECLARED` 的非空有序集合。 |
| `declaredEndpoints[]` | 否 | Adapter 派生的静态 Endpoint 投影。 |
规范 protocol token 匹配 `[A-Za-z0-9][A-Za-z0-9-]{0,31}`,并按大小写敏感比较。
CallInterface 唯一性、Endpoint 发布、RAD filter 和 Naming serviceName 组合使用相同 token。
`callInterfaces[]` 顺序是默认协议偏好。重新排序会改变 `contentDigest`。第一个仍有可用
Endpoint 的接口是 SDK 默认选择候选。
`endpointSourceOrder` 不包含重复项,语义如下:
- `[RUNTIME, DECLARED]` 优先使用运行时地址,并以声明地址作为后备;
- `[DECLARED, RUNTIME]` 优先使用声明地址;
- `[RUNTIME]``[DECLARED]` 在普通发现中只允许对应来源。
来源顺序属于单个 CallInterface,而不是整个 Version。它不阻止运行时发布。即使某个
CallInterface 不包含 `RUNTIME`,管控查询仍可查看 Runtime Endpoint。
### 5.2 Endpoint 值对象
| 字段 | 必选 | 含义 |
| --- | :---: | --- |
| `uri` | 是 | 完整可调用 URI。 |
| `transport` | 是 | 规范 transport token。 |
| `priority` | 否 | 数值越小优先级越高。 |
| `weight` | 否 | 同一 priority 内 Endpoint 的负载权重。 |
| `metadata` | 否 | 扁平的 zone、environment、data center 和扩展 label。 |
URI 包含非空 scheme 和 host。port 必须显式给出,或能根据 scheme 解析成 `1..65535`
范围内的有效默认值。DNS host 使用大小写无关的 canonical 形式;IP literal 使用稳定的
IPv4 或 IPv6 表达。
Adapter 从 `nativeDescriptor` 派生并校验 `declaredEndpoints`。客户端不得独立编辑这两种
表达。同一自然 Endpoint 多次出现时,第一次 descriptor 出现位置决定列表位置,而 native
descriptor 仍由 canonical content 完整表达。
## 6. 管控面读取模型
管控 API 使用有界视图,而不是一个无界聚合对象:
| 视图 | 包含 | 不包含 |
| --- | --- | --- |
| `AgentSummary` | 展示、治理和 Version Catalog 摘要。 | Descriptor、Endpoint、完整历史、extensions。 |
| `AgentOverview` | 完整 Agent 和有界的 Version Summary page。 | Version payload 和 Runtime Endpoint。 |
| `AgentVersionSummary` | Version、status、author、change description、digest 和时间。 | CallInterface payload。 |
| `AgentVersionDetail` | 精确 Version 元数据和完整 CallInterface。 | Runtime Endpoint。 |
| `RuntimeEndpointSnapshot` | 一个 Agent 和 protocol 的原始运行时快照,可按 Version 过滤。 | Descriptor、publisher identity、最终可发现性结论。 |
`RuntimeEndpointSnapshot` 不分页,包含:
```text
namespaceId / agentName / protocol / version?
items[] {
endpoint, bindings[] { runtimeVersion, versionRange },
state, enabled, healthy, lastUpdatedTime
}
state = AVAILABLE | DISABLED | UNHEALTHY
```
状态按顺序判定:`enabled=false``DISABLED`;否则 `healthy=false``UNHEALTHY`
其他项为 `AVAILABLE`。只有公开 Endpoint 内容、enabled 或聚合健康状态变化时,
`lastUpdatedTime` 才变化;单纯 heartbeat 不改变它。
`protocol` 必填。没有 `version` 时,Snapshot 对该 protocol 下每个 Endpoint 自然键返回一个有效项
及其全部 Version binding;指定 `version` 时,只保留命中该 Version 的 binding,并在没有
剩余 binding 时移除该项。没有实例时返回空 `items[]`。Snapshot 不应用
`endpointSourceOrder`,也不声明某一项可发现。Console 只把 Version detail 和 Snapshot 作为
独立读取事实进行组合。
RAD Catalog、Discover 和 Watch 对象属于数据面视图,只由
[RAD 协议规范](rad-protocol-spec.md)定义。特别是,`AgentDiscoveryResult` 将一个 online
Version 定义与允许的 DECLARED 和 RUNTIME Endpoint set 组合,但不作为事实保存。
## 7. 容量与安全
目标管控模型在写入任何 Agent 或 Version 事实之前执行以下上限:
| 字段 | 上限 |
| --- | ---: |
| `displayName``provider.name` | 128 Unicode code point。 |
| `description` | 2048 字符。 |
| Icon、provider 或 declared Endpoint URI | 2048 字符。 |
| 公开 tag | 32 项,每项 64 字符。 |
| Agent `extensions` | 32 项;key 128 字符;canonical JSON 合计 16 KiB。 |
| `protocol``protocolVersion` | 32 和 64 字符。 |
| 每个 Version 的 CallInterface | 16。 |
| 每个 CallInterface 的 Declared Endpoint | 64。 |
| Endpoint metadata | 32 项;key 64、value 256 字符。 |
| `AgentVersionContent` | 1 MiB。 |
公开 tag 和内部 protocol token 共享 `biz_tags` 的持久化容量;服务端在原子接受 tag 或
online protocol 变化之前,必须校验 canonical 合计长度。
Descriptor、extension 和 Endpoint metadata 不得包含明文 credential。审计记录不得记录
完整 native descriptor、security scheme 或敏感 Endpoint metadata。Runtime 发布和物理存储
上限由 [Agent 存储规范](agent-storage-spec.md)定义。
## 8. A2A 兼容边界
迁移后,旧 A2A API 是 Agent 模型上的兼容 facade,不再创建第二个 AgentCard 事实源。
| A2A 值 | Agent 模型投影 |
| --- | --- |
| AgentCard name 和 version | `agentName` 和 Agent Version 身份。 |
| 完整 AgentCard | A2A CallInterface `nativeDescriptor`。 |
| A2A protocol version | CallInterface `protocolVersion` 和 native descriptor。 |
| Root URL 和 supported/additional interface | Adapter 派生的 declared Endpoint。 |
| `registrationType=URL` | Declared-first 来源顺序。 |
| `registrationType=SERVICE` | Runtime-first 来源顺序。 |
| Runtime A2A endpoint version | `runtimeVersion` 和精确 `[version]` range。 |
首个实现只支持 A2A protocol,因此旧 A2A latest 和通用 Agent latest 使用同一个 label。
Adapter 根据 native descriptor 和适用的 Endpoint 投影重建旧查询 DTO。URL 形态读取使用
declared 地址,service 形态读取使用 runtime 地址;不存在 runtime 地址时使用 declared 地址
作为兼容后备。
通过旧 API 发起的新写入执行本规范的身份、Version、不可变和容量规则。为保留代码优先的
A2A 发布能力,它们可以使用有审计的内部直接 online 转换,但不得覆盖已经发布 Version 中
不同的内容。
Runtime A2A 发布和注销投影由 [Agent 存储规范](agent-storage-spec.md)定义。
历史 Config row、历史 Naming layout、混合版本集群双读或双写、事实源切换、回滚和异常历史
身份属于独立的滚动升级与数据迁移契约。本目标模型不因此放宽规则。
Agent 和 AgentSpec 可以通过通用资源关系互相引用,但不互相拥有生命周期。本版本不增加
Agent 专用 `sourceRef``defaultInterfaceId``interfaceId``descriptorDigest` 或随机
Endpoint 标识。
+548
View File
@@ -0,0 +1,548 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Agent 存储规范
本文档定义 [Agent 管理规范](agent-management-spec.md)的内部持久化、运行时发布、
Naming 映射、codec、digest 和 revision 契约。
[RAD 协议规范](rad-protocol-spec.md)定义外部数据面消息;本文档定义 Nacos 如何生成其事实。
本文档是 Agent 模型迁移的规范目标契约。服务端在实现本契约要求的存储行为之前不得声明
支持 Agent 或 RAD 能力。在切换之前,现有 A2A 存储仍由
[A2A Agent 规范](a2a-agent-spec.md)约束。
## 1. 存储职责
Agent 状态按照生命周期和访问模式拆分:
```text
Agent metadata -----------------------> ai_resource
Agent Version metadata ---------------> ai_resource_version
CallInterface + DECLARED Endpoint ----> AI Storage
|
+-- built-in nacos_config provider
RUNTIME publisher contributions ------> Naming Client runtime state
```
| 存储 | 拥有 | 不拥有 |
| --- | --- | --- |
| `ai_resource` | Agent 身份、目录、治理、Version 摘要和派生的 online 目录。 | Version payload 或运行时健康状态。 |
| `ai_resource_version` | 精确 Version 身份、生命周期状态、作者、Storage pointer 和 Pipeline 状态。 | CallInterface payload 或 Runtime Endpoint。 |
| AI Storage | 一个 Version 的 canonical `AgentVersionContent` bytes。 | 资源身份、生命周期、label 或可见性。 |
| Naming Client 状态 | 活跃 publisher contribution、健康、enabled 状态和 Version binding。 | Agent 定义或 Version 生命周期。 |
服务端不得持久化合并后的 `AgentDiscoveryResult`。Summary、管控详情、Catalog、Discover 和
Watch 对象都是上述事实的读取投影。
## 2. AI Resource 持久化
### 2.1 Agent Resource 行
标准 Agent 身份是 `namespaceId + type=agent + name=agentName`,按以下方式映射到
`ai_resource`
| `ai_resource` 字段 | Agent 映射 |
| --- | --- |
| `namespace_id``type``name` | `namespaceId`、常量 `agent`、原始 `agentName`。 |
| `c_desc` | `description`。 |
| `status` | `enable``disable`。 |
| `owner``scope` | 同名治理字段。 |
| `biz_tags` | 公开 tag 和服务端派生的 online protocol token。 |
| `ext` | 强类型 `AgentResourceExt`。 |
| `c_from` | 创建、导入或同步来源。 |
| `version_info` | 共享的 editing、reviewing、online count 和 label 摘要。 |
| `meta_version` | 元数据 CAS 版本。 |
| `gmt_create``gmt_modified` | 审计时间。 |
`AgentResourceExt` 具有固定的 schema version 1 结构:
| 字段 | 所有者 | 含义 |
| --- | --- | --- |
| `schemaVersion` | 服务端 | 常量 `1`。 |
| `displayName``iconUrl``provider` | 用户,服务端校验 | 目录展示。 |
| `extensions` | 用户,服务端校验 | 公开 Agent 级扩展。 |
| `versionCatalog` | 服务端 | 派生的 online Version 目录。 |
`versionCatalog` 包含 `latestVersion``onlineVersions[]`;每个条目只包含
`version``labels[]``protocols[]`。Version status 和 `version_info.labels` 仍然是
事实。Publish、online、offline、delete、label 或 latest 变化时,目录作为一次 Resource 逻辑
更新进行重建。
`biz_tags` 中的 protocol 检索 token 使用保留前缀 `__nacos.agent.protocol:`。用户 tag
不得使用 `__nacos.agent.`。读取投影移除内部 token。公开 tag 和内部 token 共享 canonical
持久化上限,超限写入必须原子拒绝。
AgentName 和 Version 身份在 DAO 查询、唯一约束、cache、label 和鉴权 key 中按大小写敏感
比较。实现不得依赖数据库默认的大小写不敏感 collation。
### 2.2 Agent Version 行
每个 Agent Version 映射为一行 `ai_resource_version`
| `ai_resource_version` 字段 | Agent Version 映射 |
| --- | --- |
| `namespace_id``type``name``version` | 精确 Version 身份。 |
| `status` | `draft``reviewing``reviewed``online``offline`。 |
| `author``c_desc` | 作者和变更说明。 |
| `storage` | Provider、opaque key、digest、媒体类型、schema 和大小。 |
| `publish_pipeline_info` | 审核执行过程和结果。 |
| `gmt_create``gmt_modified` | 审计时间。 |
Version 物理字段和所有新 Agent 写入使用相同的 64 字符上限。存储 Schema 不创建超出
Agent 管理契约的更宽公开身份空间。
Version 列表操作只读取 Resource 和 Version row。精确 Version detail 在定位 Version row 后
执行一次 AI Storage 读取。
## 3. AI Storage 中的 Agent Version 内容
### 3.1 内容对象与 Storage Pointer
一个 Version 恰好对应一个完整 Storage 对象:
```text
AgentVersionContent
kind = AgentVersionContent
schemaVersion = 1
callInterfaces[]
protocol / protocolVersion
descriptorMediaType / nativeDescriptor
endpointSourceOrder[]
declaredEndpoints[]
```
Canonical 序列化使用 RFC 8785 JSON Canonicalization SchemeJCS)。因此
CallInterface、来源偏好和 declared Endpoint 的数组顺序保持不变,而对象成员顺序和 JSON
数字表示被规范化。包含重复对象 key 或不能表示为 I-JSON 的值时拒绝请求。
`contentDigest` 是对 UTF-8 JCS bytes 计算的 `sha256:<lowercase hex>`。Version 行的
`storage` JSON 包含:
| 字段 | 值或含义 |
| --- | --- |
| `provider` | Storage provider;内置值为 `nacos_config`。 |
| `key` | Provider opaque key。 |
| `keyFormat` | 内置 provider 使用 `agent-version-config-v1`。 |
| `agentNameCodec` | 内置 provider 使用 `rad-ascii-v1`。 |
| `contentDigest` | `sha256:<lowercase hex>`。 |
| `mediaType` | `application/vnd.nacos.agent-version+json`。 |
| `schemaVersion` | `1`。 |
| `size` | Canonical 内容字节数。 |
上层将 `StorageKey.key` 视为 opaque。替换 provider 时仍保持一个 Version 对应一个对象,
但物理 key 由 provider 自己管理。内置 provider 使用第 3.2 节的映射。
### 3.2 内置 Nacos Config 映射
`agent-version-config-v1` provider key 携带下列逻辑 Config 坐标;其序列化后的
`StorageKey.key` 对 Agent service 保持 provider opaque。
| 逻辑值 | 逻辑 `config_info` 坐标 |
| --- | --- |
| `namespaceId` | `tenant_id=namespaceId`。 |
| 内容类别 | `group_id=agent-version`。 |
| `agentName``version` | `data_id=agent__<encodedAgentId>__<version>.json`。 |
| `AgentVersionContent` | `type=json` 的 canonical JSON `content`。 |
内置 provider 随后对完整逻辑 group 和 data id 应用通用 `NacosAiConfigKeyCodec`。Config
限制内的安全值原样保存;超长 data id 使用 codec 确定性的 `sha256.<digest>` 物理回退。
因此物理 key 不一定可逆,任何上层都不得从中推导 Agent 身份。不能仅因为逻辑 data id
长于 Config 物理限制而拒绝合法 Agent 身份。
更新 draft 时覆盖相同 key。Version 进入 reviewing 后内容不可变。`contentDigest` 不参与
data id,只用于内容校验和缓存相等性。读取、审核和发布操作必须校验 Storage pointer、
byte count 和 digest。
### 3.3 RAD ASCII AgentName Codec
Config data id 和 Naming serviceName 共用 codec id 为 `rad-ascii-v1`
`RadAsciiAgentIdCodec`
1. 输入是原始的 1~64 个可打印 ASCII 字符 `agentName`
2. 整个输入匹配 `[A-Za-z0-9-]+` 时原样返回;
3. 否则输出 `enc-<body>`
4. 编码形式保留 ASCII 字母和数字,其他所有字符(包括 `-`)都编码为 `-DDD`,其中
`DDD` 是三位十进制 ASCII 值;
5. 保留字母大小写,永不 trim 或 lowercase
6. 只解码已经确定使用该 codec 的物理段,并拒绝截断、非十进制、越界或非 canonical
escape。
示例:
```text
Nacos-Agent -> Nacos-Agent
Nacos Agent -> enc-Nacos-032Agent
name-ok.1:2 -> enc-name-045ok-0461-0582
```
输出只包含 `[A-Za-z0-9-]`。Codec version 1 不保留以 `enc-` 开头的原始名称命名空间,
因此一个原始安全名称与另一个名称的编码结果理论上可能生成相同物理段。Version 1 接受该低概率
歧义,不定义冲突索引、前缀保留或原子的 encoded-id 映射。公开身份始终来自
`ai_resource.name`;代码不得通过解码无类型物理 key 推断身份。未来如需无冲突 codec,必须使用
新的 codec id 和显式迁移契约,不能原地改变 `rad-ascii-v1`
Version 只使用字母、数字、`.``-`,不经过 AgentName codec。通用 Config 物理 key
codec 可以仅为满足物理长度限制而散列整个逻辑 data id;这不会截断、散列或改写任一公开
身份字段。
## 4. Runtime 发布模型
### 4.1 公开 Endpoint 与 Version Binding
DECLARED 和 RUNTIME 来源共用 Endpoint 值对象。在一个 Agent protocol 分组中,公开 Endpoint
自然键为:
```text
(namespaceId, agentName, protocol,
normalizedHost(uri), effectivePort(uri), normalizedTransport)
```
URI path、query、priority、weight 和 metadata 是公开 Endpoint payload,但不参与自然键。
不存在公开 Endpoint id。
运行时 Version binding 包含:
| 字段 | 含义 |
| --- | --- |
| `runtimeVersion` | 实际运行的实现 Version。 |
| `versionRange` | 该 publication 可以服务的 Agent Version。 |
缺失 range 时规范化为精确 `[runtimeVersion]`。Range 是单个 Maven 风格连续区间,其边界和
比较使用大小写敏感的 Agent Version 规则,而不是 Maven `ComparableVersion`
Canonical 形式包括精确 `[1.0.6]`、有界 `[1.0.0,2.0.0)`、仅下界 `[1.0.0,)`
仅上界 `(,2.0.0]`。其中不含空白,至少存在一个边界;相同且闭合的上下界使用精确形式。
区间并集和离散集合非法。`runtimeVersion` 必须命中其 range。
### 4.2 发布命令
`AgentEndpointRegistrationBatch` 包含:
```text
namespaceId / agentName / runtimeVersion / versionRange? / protocol
endpoints[1..1000]
```
批次中全部 Endpoint 共享 Version binding 和 protocol。单元素数组就是通用单 Endpoint 形式。
命令本身不持久化。服务端在原子应用前校验完整批次。批次包含重复自然键时整体拒绝。
注册只 upsert 列出的 contribution,不删除未列出的 Endpoint;重复提交相同内容成功且不产生
语义变化。
`AgentEndpointDeregistrationBatch` 只包含 `namespaceId``agentName``protocol`
`endpoints[] {uri, transport}`。对于当前 publisher,每个自然 Endpoint key 会删除该 publisher
的全部 Version binding group。调用方不提交或缓存 endpoint id、runtime Version、range、
metadata、priority 或 weight。
### 4.3 内部 Publisher Contribution
内部 publication group 身份为:
```text
publisherIdentity
+ namespaceId + agentName + protocol
+ runtimeVersion + canonicalVersionRange
```
Endpoint contribution 身份在此基础上追加公开 Endpoint 自然键。必须保持以下区分:
- 同一 publisher 可以通过多个 runtime-Version/range group 绑定相同 host、port 和 transport
- Version binding 不创建按 Version 划分的 Naming Service,也不在目标发现结果中复制公开
Endpoint
- 注册相同 contribution 身份执行 upsert
- 新的通用注销命令删除当前 publisher 下与该自然 Endpoint 匹配的全部 group。
兼容 Adapter 可以使用内部 group-delete 操作。该操作只删除一个精确 publication group
不属于公开 RAD 命令集。旧 A2A 精确 Version 注销使用该操作,因此不会误删同一 publisher
在其他 Version 上的 contribution。
全部活跃 contribution 中,相同公开自然 Endpoint key 必须具有唯一的 canonical 公开 Endpoint
payload,不受 publisher identity 或 Version range 是否相交影响。如果 URI scheme、path、query、priority、
weight 或公开 metadata 与已有 payload 不同,则以 contribution 冲突拒绝注册。只有 canonical
Endpoint payload 完全相同时,contribution 才能为同一自然键增加不同 Version binding。
### 4.4 Binding 聚合
Naming publisher contribution 聚合为包含 canonical `bindings[]` 数组的 Endpoint 投影:
```json
[
{"runtimeVersion":"1.0.6","versionRange":"[1.0.0,2.0.0)"}
]
```
数组去重,并先按 `runtimeVersion` 的 Agent SemVer 升序排序,再按 `versionRange`
大小写敏感字符串升序排序。该精确数组是 Version 匹配事实。
每条有效 publisher record 使用 `__nacos.agent.endpoint.bindings__` 保存该数组。数组恰好
包含一项时,同时写入以下诊断和首版兼容镜像:
```text
__nacos.agent.endpoint.version__ = runtimeVersion
__nacos.agent.endpoint.versionRange__ = versionRange
```
数组包含多项时移除两个 singular key。读取方在 `bindings` 存在时始终以其为准,不得将
过期 singular key 合并进去。
`RuntimeEndpointSnapshot` 聚合 publisher contribution,但不暴露 publisher identity。每个公开
自然 Endpoint key 恰好对应一项,包含 canonical Endpoint payload 和全部有效 `bindings[]`
按 Version 过滤的 Snapshot 只保留命中的 binding,并在没有剩余 binding 时删除该项。
RAD Discover 先按目标 Version 过滤 binding,再将相同自然键聚合为一个公开 Endpoint。由于写入
阶段会拒绝任何内容不一致的 payload,因此同一目标 Version 不会为同一自然键生成两个
不同公开 payload。
### 4.5 预注册与生命周期
Runtime 发布与 Agent 定义创建解耦。即使 Agent、Version 或 CallInterface 不存在,服务端也
接受结构合法且鉴权通过的发布。注册成功表示运行时意图已接受,不表示当前可发现。
注册校验 AgentName、runtime Version、range、protocol、Endpoint、鉴权、容量和 contribution
冲突。它不校验定义是否存在或 Version 生命周期状态。
Publisher identity 是内部状态:
- gRPC contribution 归属于 connection id
- HTTP contribution 归属于校验后的 client id,并使用一个 Client 级 heartbeat
- 公开管控和 RAD 对象不暴露 identity 或 publisher count。
断连或 Client 过期只删除该 publisher 的 contribution,其他相同 contribution 继续存在。只要
至少一个匹配的活跃 contribution 健康,聚合 `healthy` 就为 true;只有全部不健康时才为 false。
仅 heartbeat 或 publisher 数量变化不会改变公开投影。
`enabled` 是独立的 Naming 运维状态,不被 heartbeat 覆盖。Agent Endpoint metadata 不得设置
Naming heartbeat interval、heartbeat timeout 或 instance-delete timeout key。显式注销、
publisher 丢失或 Naming cleanup 会结束运行时状态。Agent disable、Version offline 或定义删除
只会把它从适用的发现投影中移除。
## 5. Runtime 到 Naming 的映射
### 5.1 Service 与 Cluster 身份
Naming 逻辑范围为:
```text
namespaceId
+ groupName=agent-endpoints
+ serviceName=radServiceName(encodedAgentId, protocol)
+ clusterName=normalizedTransport
```
规范 protocol token 匹配 `[A-Za-z0-9][A-Za-z0-9-]{0,31}`。ServiceName 算法为:
```text
rad-<encodedAgentId.length>-<encodedAgentId>-<protocol>
```
十进制长度不带前导零。它计算 ASCII 字符数;对 `encodedAgentId` 而言,等价于 Java
`String.length()`。结果保留大小写,只包含 `[A-Za-z0-9-]`,以字母或数字开头;protocol
token 可以以 `-` 结尾。结果不包含 Version,并且总长不超过 512。
示例:
```text
Nacos-Agent / a2a -> rad-11-Nacos-Agent-a2a
Nacos Agent / a2a -> rad-18-enc-Nacos-032Agent-a2a
```
该字符集只保证 `lb://<serviceName>` 能作为 Gateway URI 正常解析;它不定义 DNS 名称,也不
lowercase 大小写敏感的 Nacos Service 身份。会把 service id 规范化为小写的集成不在该兼容
保证内。
`clusterName` 是 normalized transport,匹配 `[0-9A-Za-z-]{1,64}`。Transport 同时存入
Cluster identity 和保留 metadata,读取时必须一致。
### 5.2 Instance 字段映射
| Agent 运行时字段 | Naming 字段 |
| --- | --- |
| `namespaceId` | Service namespace。 |
| 固定 group | `agent-endpoints`。 |
| 编码后的 Agent 和 protocol | 第 5.1 节的规范 serviceName。 |
| normalized transport | `Instance.clusterName`。 |
| normalized URI host 和 effective port | `Instance.ip``Instance.port`。 |
| URI path | `__nacos.agent.endpoint.path__`。 |
| normalized transport | `__nacos.agent.endpoint.transport__`。 |
| URI scheme | `__nacos.agent.endpoint.protocol__`。 |
| 旧 A2A protocol version | 可选 `__nacos.agent.endpoint.protocolVersion__`。 |
| HTTPS 状态 | `__nacos.agent.endpoint.supportTls__`。 |
| 原始 URI query | `__nacos.agent.endpoint.query__`。 |
| native tenant,非空时 | `__nacos.agent.endpoint.tenant__`。 |
| canonical binding | `__nacos.agent.endpoint.bindings__`。 |
| 单 binding 诊断镜像 | `__nacos.agent.endpoint.version__``__nacos.agent.endpoint.versionRange__`。 |
| priority | `__nacos.agent.endpoint.priority__`。 |
| weight | `Instance.weight`。 |
| 公开 Endpoint metadata | 其余 `Instance.metadata`。 |
| 运行状态 | `Instance.enabled``Instance.healthy``ephemeral=true`。 |
用户 metadata 不得覆盖任何 `__nacos.agent.endpoint.*__` key。服务端在接受 publication 前
构造并校验完整 Naming metadata。缺失的 range 输入在写 `bindings` 前完成 canonicalize。
`__nacos.agent.endpoint.protocolVersion__` 仅用于旧 A2A 兼容。只有 A2A 兼容 Adapter 可以写入;
它不属于公开 RAD Endpoint metadata,也不进入 Runtime revision。反向投影旧 A2A 响应时,Adapter
优先使用该值;缺失时回退到目标 CallInterface 的 `protocolVersion`。只有当一个聚合实例
中全部 A2A contribution 的该值相同时才写入这个单值 key;值不同时移除 key,并由每个精确
Version 投影使用目标 CallInterface fallback。该差异不构成公开 Endpoint payload 冲突。
公开自然键映射为 Service、Cluster、IP 和 port。Path 和 query 仍然是 payload metadata。
ServiceName 和 clusterName 都不包含 Version,因此 Service 数量不会随兼容 Agent Version 增长。
### 5.3 Naming 事实边界
包含 canonical binding 的 Naming Client publisher contribution 是 RUNTIME 事实源。普通 Naming
`ServiceInfo` 可能合并相同 IP 和 port、应用 selector 或健康保护行为,并且不能保留所有 Agent
publication group,因此不是 RAD 事实源。
Agent 运行时读取从 Naming Client/index 路径聚合原始 publisher contribution,再应用 binding 和
enabled filter。它们不得把标准 Naming Java SDK subscription 结果直接转发为 RAD Watch 快照。
`enabled``weight` 的 Naming 运维 metadata 按普通规则优先于运行时 publication 值。Agent
投影仍然保留 unhealthy Instance 并暴露其原始聚合健康状态,不应用 Naming 健康保护回退。
## 6. Runtime 发现投影
RUNTIME Endpoint 只有在同时满足以下条件时才进入一个目标发现结果:
1. Agent 存在、调用者可见且已 enable;
2. 目标 Version 为 online
3. 目标 Version 存在相同 protocol 的 CallInterface,并允许 `RUNTIME` 来源;
4. 至少一个有效 binding 包含目标 Version
5. Naming Endpoint 的 `enabled=true`
符合条件但 `healthy=false` 的 Endpoint 仍然进入 RAD 输出。SDK `selectOneHealthy` 会过滤它;
get-all 和 Watch 保留它。Disabled Endpoint 不出现。
投影使用目标 Version 的 CallInterface 获取 protocol Version、descriptor 和 endpoint source order。
Runtime contribution 永远不覆盖这些定义字段;RAD 忽略 Naming 中仅供旧兼容使用的 protocol-version
metadata。
## 7. Runtime Source Revision
针对每个 `(namespaceId, agentName, targetVersion, protocol, source=RUNTIME)`,服务端在完成
以下步骤后生成 opaque `sourceRevision`
1. 聚合活跃 publisher contribution
2. 选择包含目标 Version 的 binding
3. 校验每个自然键只有一个 canonical payload
4. 移除 `enabled=false`,并保留两种健康状态;
5. 按自然键排序 Endpoint,按 key 排序 metadata
6. 对 canonical bytes 计算 MurmurHash3 x64 128。
外部 token 为:
```text
murmur3-x64-128-v1:<32 lowercase hex>
```
Canonical 输入包含 URI、transport、effective priority 和 weight、公开 Endpoint metadata 和
`healthy`。它不包含 runtimeVersion、versionRange、publisher identity 和 count、heartbeat
时间、last-updated time 或 Naming 内部 revision。Runtime Version 和 range 不进入 hash
因为目标投影已经完成过滤。Range 或 enabled 变化会改变成员;health 变化会改变返回内容。
目标投影变化时,两者都会推进 revision。
空集合具有稳定 revision。增加或删除冗余 publisher 不改变它。Token 只用于 cache equality 和
Watch 去重,不用于身份、鉴权、CAS 或防篡改。
所有节点使用 seed `0`。Canonical bytes 以 unsigned 四字节 big-endian Endpoint 数量开头;
对每个有序 Endpoint,按上述顺序将六个字段编码成 RFC 8785 JSON UTF-8,并在每个字段前
写入 unsigned 四字节 big-endian byte length。空集合恰好为 `uint32be(0)`。Murmur 结果先输出
`h1` 再输出 `h2`,每个都是 unsigned 八字节 big-endian 值,最后编码为小写十六进制。内部
Storage Schema version 1 同时以机器可读形式记录这些规则。
实现对语义投影标脏、合并突发变化并缓存结果,不得对每次 heartbeat 或每次 Discover 读取都
执行 hash。
持久化 AgentVersion 内容继续使用 SHA-256。DECLARED Endpoint set 使用 Version
`contentDigest` 作为 opaque source revision。
## 8. 读写、缓存与一致性路径
| 读取 | 读取事实 | AI Storage 读取 |
| --- | --- | :---: |
| 管控 Agent 列表或 RAD Search | `ai_resource` page。 | 否 |
| Agent overview | Resource 和有界 Version-row page。 | 否 |
| 精确 Version detail | 一行 Version。 | 一次 |
| Runtime Endpoint Snapshot | 一个 protocol 的原始 Naming publisher contribution;可选 binding filter。 | 否 |
| RAD Discover | Resource、online Version、缓存内容和符合条件的 runtime 投影。 | Digest 未命中时一次 |
| 变化 | 写入目标 | 一致性规则 |
| --- | --- | --- |
| Agent 目录、治理、extensions | `ai_resource`。 | `metaVersion` CAS。 |
| 创建或更新 draft | AI Storage 固定 key 和 Version row。 | Pointer、bytes、size 和 digest 一致。 |
| Publish、online、offline、delete、label/latest | Version row 和 Resource 摘要。 | 重建派生目录和 protocol token。 |
| Runtime register、heartbeat、deregister | Naming Client 运行时状态。 | 不写 AI Resource 或 Storage。 |
缓存校验值跟随事实:
| 事实 | 校验值 |
| --- | --- |
| Agent 元数据 | `metaVersion`。 |
| Agent Version 内容 | `contentDigest`。 |
| 目标 Runtime 投影 | `sourceRevision`。 |
AI Storage provider 保证单个 StorageKey 的原子 bytes 和它声明的读取一致性。Agent Registry
负责跨 Resource、Version、Storage pointer、digest 和派生目录的编排,并执行校验、幂等重试
和失败补偿。Publish 前必须重新读取内容并校验 digest。
Storage 写入成功但元数据写入失败时,形成可观测的不完整操作,并通过重试或孤儿内容清理处理。
Digest 不一致时不得返回未校验内容。`versionCatalog`、protocol token 和 Resource Version
摘要是可重建派生数据;其一致性不得下放给 Storage provider。
## 9. 容量与安全
| Runtime 或物理字段 | 上限 |
| --- | ---: |
| `runtimeVersion` | 64 字符。 |
| Canonical `versionRange` | 256 字符;一个连续区间。 |
| 注册批次 | 11000 个 Endpoint。 |
| 每个 Agent 和 protocol 的 Runtime Endpoint | 1000,可由集群配额调低。 |
| 最终 Endpoint metadata | 32 个公开项;key 64、value 256 字符。 |
| 最终 Naming metadata | key 和 value 的 Java `String.length()` 合计 1024。 |
| Agent Version 物理 Config data id | 255 字符,由 `NacosAiConfigKeyCodec` 保证;超长逻辑 id 使用其 SHA-256 回退。 |
| Agent Version content | 1 MiB。 |
服务端在写 Naming 前校验包含保留 key 的完整生成 metadata。超限时拒绝,不得截断或静默
丢弃字段。
AI Storage content、Endpoint metadata 和 publisher state 不得包含明文 credential。日志和审计
事件不得向普通用户暴露完整 native descriptor、security scheme、publisher identity 或敏感
Endpoint metadata。
## 10. A2A Runtime 兼容边界
A2A Adapter 是本存储契约的首个使用方:
| 旧 A2A 事实 | 新存储投影 |
| --- | --- |
| AgentCard 定义 | Version content 中的 A2A `AgentCallInterface.nativeDescriptor`。 |
| Root 和 additional interface | Adapter 派生的 DECLARED Endpoint。 |
| Runtime AgentEndpoint Version | `runtimeVersion=version``versionRange=[version]`。 |
| Runtime 调用 protocol | 规范 Agent protocol token `a2a`。 |
| 旧 Endpoint transport 和 URI 部分 | 通用 Endpoint 和 Naming 保留 metadata。 |
旧 single 和 batch 注册保留精确 Version replacement scope
```text
(publisherIdentity, namespaceId, agentName, protocol=a2a,
runtimeVersion=version, versionRange=[version])
```
兼容 Adapter 替换该内部 group。旧注销只删除该精确 group,即使同一 publisher 和物理
Endpoint 还存在其他 Version binding 也不会误删。新的 RAD 注销则删除当前 publisher 下所提交
自然 Endpoint 的全部 binding。
切换后,兼容写入使用新的 AI Resource、AI Storage 和 Naming layout。历史 Config row、历史
Naming Service、混合集群双读或双写、切换、回滚和异常历史身份处理属于独立的滚动升级与
迁移契约。
+11 -11
View File
@@ -26,7 +26,7 @@ AI Registry 是 Nacos 中负责 AI 资源注册、治理、发现和分发的领
AI Registry 负责:
- AI 资源元数据、版本、标签、状态、scope、owner 和业务标签;
- MCP Server、A2A Agent、Prompt、Skill、AgentSpec 等资源类型契约;
- MCP Server、Agent、Prompt、Skill、AgentSpec 等资源类型契约;
- 已支持 AI 资源的运行时查询和订阅行为;
- draft 创建、审核、发布、强制发布、上线/下线、删除、上传、导入和下载等管理流程;
- AI 发布流水线、存储插件、可见性、鉴权和 Trace 钩子的领域使用方式。
@@ -34,7 +34,7 @@ AI Registry 负责:
AI Registry 不负责:
- Config 资源语义,即使默认 AI 存储实现通过 Config 保存资源内容;
- Naming service 语义,即使 MCP 或 A2A endpoint 通过 Naming service 和 instance 表达;
- Naming service 语义,即使 MCP 或 Agent endpoint 通过 Naming service 和 instance 表达;
- [AI Registry 适配器规范](ai-registry-adaptor-spec.md)暴露的社区 registry 协议定义;
- 插件扩展契约。流水线、存储、资源导入、可见性和 Trace 的扩展规则由对应插件规范定义。
@@ -75,16 +75,16 @@ JSON。
## 4. 资源类型清单
| Type | 标准身份 | 当前持久化形态 | 规范 |
| Type | 标准身份 | 当前或已批准目标持久化形态 | 规范 |
| --- | --- | --- | --- |
| `mcp` | `namespaceId -> mcp -> mcpName` | 当前通过 Config 记录 MCP 元数据、版本、tool、resource 数据,通过 Naming service 表达 endpoint。 | [MCP Server 规范](mcp-server-spec.md) |
| `a2a` | `namespaceId -> a2a -> agentName` | 当前通过 Config 记录 AgentCard 元数据和版本数据,通过 Naming service 表达 endpoint。 | [A2A Agent 规范](a2a-agent-spec.md) |
| `agent` | `namespaceId -> agent -> agentName` | 已批准目标为 `ai_resource``ai_resource_version`、AI 存储和 Naming 承载的 Runtime Endpoint publication;迁移完成前,历史 A2A 存储仍是兼容来源。 | [Agent 管理规范](agent-management-spec.md) |
| `prompt` | `namespaceId -> prompt -> promptKey` | 使用 `ai_resource``ai_resource_version` 和 AI 存储;旧 Prompt 数据可迁移。 | [Prompt 规范](prompt-spec.md) |
| `skill` | `namespaceId -> skill -> name` | 使用 `ai_resource``ai_resource_version`、AI 存储和轻量 discovery manifest。 | [Skill 规范](skill-spec.md) |
| `agentspec` | `namespaceId -> agentspec -> name` | 使用 `ai_resource``ai_resource_version` 和 AI 存储。 | [AgentSpec 规范](agentspec-spec.md) |
MCP 和 A2A 即使当前持久化尚未完全适配 `ai_resource`,也仍属于 AI Registry 资源。
它们的标准规范应以标准身份为准,并把当前兼容存储单独记录
A2A AgentCard 是 `agent` Version 内的一种协议 binding。历史 `a2a` 资源身份和 API
是 [A2A Agent 规范](a2a-agent-spec.md)定义的兼容 facade,不得形成第二套标准 Agent 身份
## 5. 接口面
@@ -95,7 +95,7 @@ AI Registry 通过多个接口面暴露:
| `/v3/client/ai/...` | 运行时客户端和 Agent framework。 | 查询已知资源、下载运行时产物、订阅,以及注册客户端拥有的 endpoint。 |
| `/v3/admin/ai/...` | 管理工具和 Maintainer SDK。 | 创建、更新、列表、发布、删除、上传和版本运维。 |
| `/v3/console/ai/...` | Nacos 控制台。 | 围绕相同领域语义进行 UI 编排。 |
| gRPC AI requests | Java Client SDK 运行时流量。 | 查询和发布 MCP/A2A/Prompt 资源,并在支持时注册 endpoint。 |
| gRPC AI requests | Java Client SDK 运行时流量。 | 查询 AI 资源、执行 RAD 发现与订阅,并在支持时发布客户端拥有的 endpoint。 |
| Java SDK | 运行时应用集成。 | 参见 [Java SDK 实现规范](../sdk/sdk-java-impl-spec.md)。 |
| Java Maintainer SDK | 类型化管理集成。 | 应与 Admin API 语义和资源类型规范保持一致。 |
| AI Registry 适配器 | 外部社区 registry 客户端。 | 独立端口上的可选兼容端点,参见 [AI Registry 适配器规范](ai-registry-adaptor-spec.md)。 |
@@ -121,11 +121,11 @@ AI Registry 通过多个接口面暴露:
- MCP Server 应将持久元数据和版本模型从 Config 形态记录迁移到标准的
`ai_resource``ai_resource_version` 模型,同时保留现有数据兼容。
- A2A Agent 应将 AgentCard 元数据和版本数据从 Config 形态记录迁移到标准 AI
资源模型
- 历史 A2A AgentCard 和 Naming endpoint 数据必须通过滚动升级方案迁移到 Agent 模型;
旧 API 只是投影视图,不再拥有独立资源存储
- Prompt 已有从旧 Config 形态 Prompt 数据迁移到标准 AI 资源模型的路径。旧映射
必须作为兼容存储,而不是正式 Config 资源语义。
- A2A 当前在多个兼容 endpoint 中随机选择 endpoint。未来如有需要,应定义可插拔或
确定性的 endpoint 选择规范
- RAD 返回确定性的 endpoint 集合。健康过滤、priority/weight 选择和负载均衡属于客户端策略,
不改变 Registry snapshot
- 随着 MCP、A2A 和 Agent 包生态演进,AI 资源 schema 和协议 payload 可能需要
大幅调整。
+10 -4
View File
@@ -78,15 +78,21 @@ create/upload draft
- 流水线执行状态可以写入 `publishPipelineInfo``pipeline_execution`
- 流水线通过和拒绝都会把版本改为 `reviewed`;拒绝后如果需要继续编辑,用户必须显式
redraft 该版本。
- Publish 会把版本改为 `online`,清理 working 指针,按需增加 `onlineCnt`,并由服务端维护
`latest` label。
- Publish 会把版本改为 `online`,清理 working 指针,按需增加 `onlineCnt`,并由服务端按照
资源类型规范维护 `latest` label。
- Publish 和 force-publish 请求可以为兼容历史调用保留 `updateLatestLabel` 参数;
该参数已废弃,新客户端不得继续发送。未指定或指定为 `true` 时,发布版本成为服务端维护的
最新版本。标签更新 API 必须忽略客户端传入的 `latest` label key,并把当前服务端维护的
`latest` 值合并回最终 labels map。
- Force publish 会在跳过流水线通过校验的同时,执行与 publish 成功时一致的状态转换。
- 任意上线/下线状态变更完成后,服务端必须基于当前在线版本重新计算 `latest`
并指向最大的在线版本;没有任何在线版本时,服务端必须删除 `latest`
- 除非类型规范给出确定性细化,成功的 publish 或 online 操作会使目标版本成为 `latest`
当前 latest 被删除或下线时,默认选择剩余 online Version 中最大的一个;不存在 online
Version 时删除 `latest`。类型细化仍必须保证 `latest` 由服务端管理、只指向 online Version
并定义删除或下线时的回退规则。
- Agent 类型只对旧 A2A 直接上线 facade 细化该规则:`setAsLatest=false` 可以保留当前有效指针。
标准 Agent publish 和 online 仍会移动 `latest`;当前指针被删除或下线时,选择剩余 online
Agent Version 中最大的一个。详见 [Agent 管理规范](agent-management-spec.md)和
[A2A Agent 规范](a2a-agent-spec.md)。
流水线扩展行为由 [AI 发布流水线插件规范](../plugin/ai-pipeline-plugin-spec.md)定义。
本领域规范只定义 AI 资源生命周期如何响应流水线结果。
+7 -1
View File
@@ -43,7 +43,7 @@ namespaceId + resourceType + resourceName + version
| 字段 | 含义 |
| --- | --- |
| `namespaceId` | Namespace 隔离边界。 |
| `type` | 资源类型,例如 `prompt``skill``agentspec`。 |
| `type` | 资源类型,例如 `agent``prompt``skill``agentspec`。 |
| `name` | 稳定资源名。 |
| `desc` | 资源描述。 |
| `status` | 资源元数据状态,目前为 `enable``disable`。 |
@@ -103,6 +103,12 @@ Labels 不得指向 draft 或 reviewing 版本。运行时客户端可以通过
存储扩展行为由 [AI 存储插件规范](../plugin/ai-storage-plugin-spec.md)定义。数据库
方言行为由 [数据源方言插件规范](../plugin/datasource-dialect-plugin-spec.md)定义。
类型自有 JSON 必须具有明确的 Schema 契约。对 `type=agent``ext` 保存目录扩展和
派生的在线版本目录,Version `storage` 指向一个完整的 Agent Version 内容对象。
精确字段和重建规则由 [Agent 管理规范](agent-management-spec.md)与
[Agent 存储规范](agent-storage-spec.md)定义。Runtime Agent Endpoint 遵循客户端拥有的
Naming 生命周期,不写入 `AiResourceVersion.storage`
## 6. 可见性
AI 资源通过共享的可见性插件模型实现可见性。
+630
View File
@@ -0,0 +1,630 @@
<!--
Copyright 1999-2026 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Remote Agent Discovery 协议规范
| 项目 | 值 |
|---|---|
| 状态 | 实验性;对协议版本 `0.1.0` 具有规范性 |
| 协议版本 | `0.1.0` |
| 范围 | Remote Agent 搜索、发现、订阅和运行时端点发布 |
| 目标 | 通过少量稳定对象返回 Agent 调用描述和当前可用地址 |
本文定义 Nacos Remote Agent DiscoveryRAD)协议与传输无关的核心语义。
HTTP、gRPC 和 SDK Binding 可以使用各自的本地类型,但线上字段和可观察行为必须与
本文等价。
## 1. 定位与范围
RAD 回答两个问题:
1. 目标未知时,哪些 Agent 可能满足需求;
2. 选定 Agent 版本后,哪些调用协议和端点当前可用。
RAD 返回调用远程 Agent 所需的元数据,但不代理调用,也不定义 Agent 消息、任务或
会话协议。
### 1.1 操作
RAD 0.1.0 定义五个操作:
| 操作 | 输入 | 输出 | 语义 |
|---|---|---|---|
| `Search` | `AgentSearchRequest` | `AgentCatalogPage` | 分页搜索候选 Agent |
| `Discover` | `AgentDiscoveryRequest` | `AgentDiscoveryResult` | 返回一个 Agent 版本的完整调用快照 |
| `Watch` | `AgentDiscoveryRequest` | `AgentDiscoveryResult` 流 | 返回初始和后续的完整替换快照 |
| `Register` | `AgentEndpointRegistrationBatch` | 成功或错误 | 注册或更新运行时端点 |
| `Deregister` | `AgentEndpointDeregistrationBatch` | 成功或错误 | 注销运行时端点 |
`Watch` 复用 `Discover` 的请求和结果。RAD 不在订阅快照外增加事件信封对象。
### 1.2 范围外内容
RAD 0.1.0 不定义 Agent 管理生命周期、客户端连接与重连、内部存储、历史兼容、
MCP、调用代理、凭据、重试或负载均衡。Agent 资源和版本语义由
[Agent 管理规范](./agent-management-spec.md)定义。
## 2. 公共约束
### 2.1 命名空间
每个操作都在且只在一个生效命名空间中执行。
- 顶层请求只携带一次 `namespaceId`
- 嵌套的 Agent 引用、Filter 和 Endpoint 不重复携带该字段。
- Binding 可以从客户端配置或请求上下文取得该值,但必须在进入 RAD 核心语义前将
缺省命名空间规范化为 `public`
- 缓存、订阅、鉴权和发布者贡献键必须包含生效命名空间。
`namespaceId` 包含 164 个 `[A-Za-z0-9_-]` 字符。
### 2.2 Agent、Protocol 与 Label 身份
Agent 的公开身份是 `(namespaceId, agentName)`
`agentName` 必须:
- 包含 1~64 个可打印 ASCII 字符;
- 至少包含一个非空格字符;
- 按大小写敏感的原值进行比较;
- 不执行 trim、转小写、slug 或其他改写。
`protocol` 包含 132 个字符并匹配
`[A-Za-z0-9][A-Za-z0-9-]{0,31}``label` 包含 164 个字符并匹配
`[A-Za-z0-9][A-Za-z0-9._-]{0,63}`。两者均大小写敏感。
`latest` 是保留 Label,用于解析 Agent 当前的 latest 版本。它不得出现在
`AgentVersionCatalog.labels` 中。
### 2.3 Agent 版本
Agent 版本采用 `MAJOR.MINOR.PATCH[-PRERELEASE]`,总长不超过 64 个字符。
核心数值标识不得包含前导零。Prerelease 标识由 `.` 分隔,每一项匹配
`[0-9A-Za-z-]+`;只包含数字的 Prerelease 标识不得带前导零,除非它恰好是 `0`
RAD 0.1.0 不接受 build metadata。版本身份和比较均大小写敏感,顺序遵循 SemVer
优先级,不得先将版本转换为固定宽度整数。
### 2.4 版本范围
`versionRange` 使用 Maven/POM 风格的区间括号,但每个边界必须是第 2.3 节定义的
Agent 版本,比较使用 RAD SemVer,而不是 Maven `ComparableVersion`
| 形式 | 匹配规则 |
|---|---|
| `[1.0.6]` | 只匹配 `1.0.6` |
| `[1.0.0,1.0.6]` | `1.0.0 <= version <= 1.0.6` |
| `[1.0.0,2.0.0)` | `1.0.0 <= version < 2.0.0` |
| `[1.0.0,)` | `version >= 1.0.0` |
| `(,2.0.0)` | `version < 2.0.0` |
RAD 0.1.0 只接受一个精确版本或一个连续区间,不接受多个版本或区间的并集。表达式
不得包含空格,并且至少包含一个边界。缺失下界时使用 `(`,缺失上界时使用 `)`
上下界都存在时,下界必须早于上界。只有两端均为闭区间时才允许上下界相等,此时
服务端将 `[version,version]` 规范化为 `[version]`;其他上下界相等的形式均非法。
`[1.0.0,2.0.0)` 只承诺所述 SemVer 比较,Prerelease 版本仍按 SemVer 优先级判断。
服务端存储并比较规范化后的形式。
### 2.5 协议版本协商
Binding 通过其文档或 Nacos 能力协商声明支持 RAD 0.1.0。RAD 根消息本身不携带
协议版本或 Schema 版本字段。
## 3. 公共模型
### 3.1 根消息
Schema 只暴露以下六个根消息:
| 根消息 | 用途 |
|---|---|
| `AgentSearchRequest` | `Search` 请求 |
| `AgentCatalogPage` | `Search` 结果 |
| `AgentDiscoveryRequest` | `Discover``Watch` 请求 |
| `AgentDiscoveryResult` | `Discover``Watch` 完整快照 |
| `AgentEndpointRegistrationBatch` | `Register` 请求 |
| `AgentEndpointDeregistrationBatch` | `Deregister` 请求 |
语言 Binding 可以复用字段完全等价的本地类型。例如 Java 可以使用
`Page<AgentCatalogEntry>` 实现 `AgentCatalogPage`,不必再引入一个分页类。
### 3.2 通用 JSON 规则
- 可选值缺失时省略字段,不使用显式 `null`
- 普通对象不接受未知属性。
- 只有 `nativeDescriptor` 和明确声明的 `metadata` Map 是开放内容。
- 可选请求数组一旦出现就至少包含一项;响应中的空集合明确返回 `[]`
- 空 Filter 对象 `{}` 表示不进行过滤。
- 空 Metadata 对象 `{}` 规范化为省略该字段;空 `metadataSelector` 等价于不进行
Metadata 过滤。
### 3.3 `AgentSearchRequest`
| 字段 | 必选 | 语义 |
|---|:---:|---|
| `namespaceId` | 是 | 生效命名空间 |
| `agentNameContains` | 否 | 对 `agentName` 执行大小写敏感的字面量子串匹配 |
| `tagsAll[]` | 否 | Agent 包含全部给定 Tag |
| `protocolsAny[]` | 否 | 至少一个在线版本包含任一给定 Protocol |
| `pageNo` | 否 | 从 1 开始的页码,缺省为 `1` |
| `pageSize` | 否 | 每页数量,缺省为 `20`,最大为 `100` |
`%``_` 等对底层查询语言具有特殊含义的字符必须作为普通字面量处理。
### 3.4 `AgentCatalogPage`、`AgentCatalogEntry` 与 `AgentVersionCatalog`
`AgentCatalogPage` 包含:
```text
totalCount / pageNumber / pagesAvailable / pageItems[]
```
每个 `pageItems[]` 是一个相对于请求命名空间的 `AgentCatalogEntry`
```text
agentName / displayName? / description? / iconUrl? / provider?
tags? / latestVersion
versions[] AgentVersionCatalog {
version
labels[]?
protocols[]
}
```
规则:
- `versions` 按 SemVer 降序列出全部在线版本。Version 不重复,`protocols` 至少包含
一个不重复的值。
- 每个条目的在线版本数量没有产品级硬上限,列表不得被静默截断。Binding 的全局
响应大小限制仍然生效;超限时返回该 Binding 的标准响应过大错误。
- 一个非保留 Label 最多指向一个 Version。`latest` 不得出现在 `labels` 中,且
`latestVersion` 必须匹配一个已列出的 `version`
- 条目不重复返回 `namespaceId`,也不返回协议描述、Endpoint、健康状态或管理字段。
- Search 不承诺当前存在健康 Endpoint,当前可调用性由 Discover 判断。
### 3.5 `AgentReference`
| 字段 | 必选 | 语义 |
|---|:---:|---|
| `agentName` | 是 | 生效命名空间中的 Agent 名称 |
| `version` | 否 | 选择一个在线的精确版本 |
| `label` | 否 | 在请求时将 Label 解析为一个在线版本 |
`version``label` 互斥。两者都缺失或 `label``latest` 时,解析当前 latest
版本。
### 3.6 `AgentDiscoveryFilter`
Filter 的全部字段都是可选字段:
| 字段 | 语义 |
|---|---|
| `protocols[]` | 允许的调用协议 |
| `protocolVersion` | 与候选接口进行精确匹配 |
| `transports[]` | 允许的传输类型 |
| `endpointSources[]` | 允许的 `RUNTIME``DECLARED` 来源 |
| `metadataSelector` | Endpoint Metadata 包含全部精确键值 |
同一数组内按 OR 匹配,不同字段之间按 AND 匹配。Filter 只裁剪一次发现结果,不会
选择另一个 Agent 版本,也不执行负载均衡。
### 3.7 `Endpoint`
所有操作复用一个 `Endpoint` 模型:
| 字段 | 必选 | 语义 |
|---|:---:|---|
| `uri` | 是 | 完整绝对调用 URI,最长 2048 个字符 |
| `transport` | 是 | 规范化传输类型,包含 1~64 个 `[0-9A-Za-z-]` 字符 |
| `priority` | 否 | 越小越优先,整数 `0..2147483647`,缺省为 `0` |
| `weight` | 否 | 同一 Priority 内的权重,数字 `0..10000`,缺省为 `1` |
| `metadata` | 否 | 最多 32 个扁平字符串键值 |
| `healthy` | 条件必选 | 只在 `RUNTIME` 发现结果中出现,并且必须出现 |
上下文规则:
- Register 不得提交 `healthy`
- `DECLARED` Endpoint 不得包含 `healthy`
- `RUNTIME` 发现结果中的 Endpoint 必须包含 `healthy`
- Deregister 只提交 `uri``transport`,它们是公开对象中代表 Endpoint
自然键的字段。
运行时 Endpoint 不使用 `endpointId`
### 3.8 Endpoint 自然键与规范化
运行时 Endpoint 的自然键是:
```text
(namespaceId, agentName, protocol,
normalizedHost(uri), effectivePort(uri), normalizedTransport)
```
Path、Query、Metadata、Priority 和 Weight 不参与身份。同一分组内的两个 Endpoint
不能只依靠不同 Path 并存。
规范化规则:
- URI 包含 Scheme 和 Host,并具有显式或可推导的有效端口。`http``ws` 推导
端口 `80``https``wss` 推导端口 `443`;其他 Scheme 必须显式提供端口。
- URI 不得包含 User-info 或 Fragment。
- Scheme 和 DNS Host 转为小写,DNS Host 使用 ASCII A-label。
- IPv4 和 IPv6 使用稳定文本形式。
- 输出 URI 必须显式包含生效端口。
- Transport 使用 Registry 接受的规范值,不自动转换大小写。
- Priority 和 Weight 在比较前分别物化为 `0``1`
- Metadata 按 Key 排序后比较,Map 顺序不影响相等性。
### 3.9 `EndpointSet`
声明来源和运行时来源复用一个对象:
```text
EndpointSet {
source = DECLARED | RUNTIME
sourceRevision
endpoints[]
}
```
`source` 决定 `healthy` 约束。`AgentDiscoveryResult` 不返回
`endpointSourceOrder`。Registry 按选中 Agent 版本声明的来源顺序输出
`endpointSets[]`,应用 Filter 后保持剩余来源的相对顺序。已经声明但当前为空的来源
仍以 `endpoints=[]` 和稳定的 `sourceRevision` 返回。
### 3.10 `AgentDiscoveryCallInterface` 与 `AgentDiscoveryResult`
```text
AgentDiscoveryResult
├── namespaceId / agentName / version / contentDigest
└── callInterfaces[] AgentDiscoveryCallInterface
├── protocol / protocolVersion?
├── descriptorMediaType / nativeDescriptor
└── endpointSets[]
├── source / sourceRevision
└── endpoints[]
```
`AgentDiscoveryCallInterface` 是数据面投影视图,有意区别于
[Agent 管理规范](./agent-management-spec.md)定义的管控面 `AgentCallInterface`
发现视图不包含管理字段和来源顺序字段,而是包含已经解析的 EndpointSet。
规则:
- `version` 是从 `AgentReference` 最终解析出的在线精确版本。
- 一个版本最多包含 16 个调用接口,Protocol 不重复。
- 调用接口保持 Agent 版本定义中的顺序。
- `nativeDescriptor` 可以是任意非 null JSON 值。
- `descriptorMediaType` 描述 `nativeDescriptor`
- `endpointSets[]` 是本次发现快照的权威地址,`nativeDescriptor` 中的地址不得覆盖它。
- 结果不返回展示字段、Owner、Scope、Extensions 或发布者身份。
### 3.11 Digest 与 Revision
`contentDigest` 标识完整且不可变的版本内容:
- 格式为 `sha256:` 加 64 位小写十六进制字符。
- 覆盖有序调用接口、`nativeDescriptor`、内部来源顺序和声明 Endpoint。
- 不覆盖状态、latest、Label、管理元数据或运行时 Endpoint。
- Consumer 只比较完整值,不自行计算。
每个 `(namespaceId, agentName, version, protocol, source)` 具有一个
`sourceRevision`
- 它是不透明的相等性 Token,不能排序或跨作用域比较。
- Endpoint 成员、URI、Transport、Priority、Weight、公开 Metadata 或健康状态变化时
必须改变。
- 心跳时间、发布者数量或内部存储 Revision 本身不要求它改变。
- 空 EndpointSet 也具有稳定 Revision。
- `DECLARED` 集合使用 Version `contentDigest`。Nacos `RUNTIME` 集合使用
`murmur3-x64-128-v1:<32 lowercase hex>`,并按照
[Agent 存储规范](agent-storage-spec.md)中的确定性投影契约生成。Consumer 仍把两种形式
都视为 opaque,不自行计算。
### 3.12 Endpoint Batch
`AgentEndpointRegistrationBatch` 包含:
```text
namespaceId / agentName / runtimeVersion / protocol
versionRange? # 缺省为精确范围 [runtimeVersion]
endpoints[] # 1..1000
```
`runtimeVersion` 是实际部署的实现版本。`versionRange` 描述该部署可以服务的 Agent
版本。字段缺失时,服务端将其规范化为 `[runtimeVersion]``runtimeVersion` 必须包含
在生效范围内。
`AgentEndpointDeregistrationBatch` 包含:
```text
namespaceId / agentName / protocol
endpoints[] { uri, transport }
```
对于每个给定自然键,Deregister 删除当前发布者贡献的全部 Binding,包括不同内部
`runtimeVersion``versionRange` 分组中的 Binding。
## 4. Search
Search 必须:
1. 只返回调用方可见、已启用、至少存在一个在线版本且 latest 有效的 Agent
2. 应用 `agentNameContains``tagsAll``protocolsAny`
3. 按原始 `agentName` 进行大小写敏感的 ASCII 升序排序;
4. 对相同请求和数据快照提供稳定分页;
5. 返回 `totalCount``pageNumber``pagesAvailable``pageItems`
6. 不加载或返回完整描述和 Endpoint。
`pageNo` 缺省为 `1``pageSize` 缺省为 `20` 且最大为 `100`
## 5. Discover
`AgentDiscoveryRequest` 包含:
```text
namespaceId
reference: AgentReference
filter?: AgentDiscoveryFilter
```
Registry 按以下顺序执行 Discover
1. 在生效命名空间中按原值查找 `agentName`
2. 使用 `version``label` 或 latest 解析目标版本。
3. 校验可见性、Agent Enabled 状态和目标 Online 状态。
4. 按版本定义顺序加载调用接口。
5. 保留 `versionRange` 包含目标精确版本的运行时 Publication。
6. 排除 `enabled=false` 的运行时 Instance,保留 `healthy=true`
`healthy=false` 的 Instance。
7. 聚合具有相同公开 Endpoint 自然键的匹配贡献。
8. 应用可选 Filter。
9. 按调用接口、来源、Priority 和稳定自然键返回完整快照。
过滤后无匹配结果的固定形态为:
| 无匹配层级 | 返回形态 |
|---|---|
| `protocols``protocolVersion` | `callInterfaces=[]` |
| `endpointSources` | 保留接口并返回 `endpointSets=[]` |
| `transports``metadataSelector` | 保留 EndpointSet 并返回 `endpoints=[]` |
`healthy=false` 的运行时 Endpoint 仍保留在结果中。只选择健康 Instance、应用
Priority 和 Weight,以及无健康 Instance 时是否回退,都属于 Consumer 职责。
## 6. Watch
Watch 使用与 Discover 相同的请求和结果。
- Registry 首先执行 Discover;如果返回 `NOT_FOUND`,则不创建订阅。
- 成功的 Watch 首先发送当前完整 `AgentDiscoveryResult`
- 后续每次通知都是不带事件信封的完整替换结果。
- 解析出的版本、`contentDigest` 或任一 `sourceRevision` 发生变化时产生新快照。
- 匹配的运行时注册、更新、注销或存活状态变化在公开投影变化时产生新快照。
- 未改变公开投影的内部变化不应产生重复通知。
- 之前可发现的目标变为 `NOT_FOUND` 时,Binding 发送终止 `NOT_FOUND` 状态并关闭
Watch。
- Consumer 收到每个新结果后,整体替换旧快照。
- 订阅者身份、确认、重连、重放和背压由 Binding 定义。
Binding 可以使用自己的传输信封投递快照与终止状态。该信封不属于 RAD 公共模型,
也不扩展第 3.1 节的六个根消息。
等价的取消键包含 `namespaceId`、规范化 `AgentReference`、Filter 和订阅者身份。
## 7. Register 与 Deregister
### 7.1 校验与预注册
Register 校验:
- 请求结构、Endpoint 约束、权限和容量;
- `runtimeVersion``versionRange` 合法,并且范围包含 Runtime Version
- 同一 Batch 不存在重复自然键,也不存在第 7.3 节的 Publication 冲突;
- 请求不提交或覆盖 `protocolVersion`
Register 不要求 Agent、Runtime Version、范围边界、范围内 Version 或对应调用接口
已经存在,因此支持 Endpoint 预注册。
预注册只创建运行时 Publication,不隐式创建 Agent、Version 或调用接口,也不会提前
进入普通 Discover。Discover 仍然要求 Agent 可见且 Enabled、目标 Version Online
并且调用接口允许 `RUNTIME` 来源;它使用目标版本自身的描述和 `protocolVersion`
Agent 和 Version 定义不拥有 Publication 生命周期。定义的创建、上线、下线或删除只
改变发现投影;Publication 本身由 Register、Deregister 和发布者存活状态管理。
### 7.2 Batch、幂等与原子性
一个注册 Batch 属于同一个:
```text
(namespaceId, agentName, protocol, runtimeVersion, versionRange)
```
规则:
- Registry 先校验全部 Endpoint,再原子应用一个 Batch。
- Register 只新增或更新列出的自然键,不替换未提交的 Endpoint。
- 同一发布者重复提交相同内容时成功但不产生变化。
- 非身份字段变化时执行 Upsert。
- 同一 Batch 出现重复自然键时整体拒绝。
- Deregister 只删除当前发布者的贡献。
- Deregister 不存在的贡献时成功但不产生变化。
- 跨 Batch 不承诺事务。
单 Endpoint 操作使用长度为 1 的 `endpoints[]`RAD 不定义单独的单条命令。
### 7.3 Publication 分组与多发布者
Binding 提供不透明的发布者身份和存活语义,发布者身份不进入发现结果。
Registry 可以为一个公开 Endpoint 自然键保存多个内部 Publication 分组,并使用
`runtimeVersion` 和规范化后的 `versionRange` 区分分组。因此,一个发布者可以通过
多个兼容版本声明贡献同一个公开 Endpoint。Discover 先保留与目标版本匹配的分组,
再将其聚合为一个公开 Endpoint。
能够投影为同一个公开 Endpoint 的贡献必须具有相同的规范化 URI、Transport、
Priority、Weight 和 Metadata;冲突注册返回 `CONFLICT`。健康状态在匹配的有效贡献间
聚合:
- 至少一个健康贡献时得到 `healthy=true`
- 全部贡献都不健康时得到 `healthy=false`
- 移除一个发布者时只移除其贡献;
- 发布者数量变化但公开 Endpoint 未变化时,不改变 `sourceRevision`
## 8. 顺序与容量
调用接口使用版本定义顺序,EndpointSet 使用声明的来源顺序,Endpoint 先按 Priority
升序,再按稳定自然键排序。健康状态不改变顺序,Weight 不参与 Registry 排序。
一个版本最多包含 16 个调用接口;一个声明 EndpointSet 最多包含 64 个 Endpoint
一个运行时 EndpointSet 和一个 Endpoint Batch 分别最多包含 1000 个 Endpoint。
在线版本列表没有独立的产品上限,遵循第 3.4 节的响应大小规则。
## 9. Binding Profile
Binding 声明其支持的 Profile 和可选能力:
| Profile 或能力 | 必须支持的操作 |
|---|---|
| Consumer Profile | `Search``Discover` |
| Publisher Profile | `Register``Deregister` |
| Watch 能力 | `Watch` |
符合 RAD 的 Binding 至少实现一个 Profile。Watch 在 RAD 核心层是可选能力。Nacos
HTTP Binding 实现 Consumer 和 Publisher Profile,但不实现 WatchNacos 完整 gRPC
Profile 实现全部五个操作。
## 10. 错误语义
Binding 将以下抽象类别映射到具体响应模型:
| 类别 | 典型场景 |
|---|---|
| `INVALID_ARGUMENT` | 字段非法、互斥字段冲突、自然键重复、范围非法或 Runtime Version 不在范围内 |
| `NOT_FOUND` | Discover 目标不存在、不可见、禁用或未上线;已订阅目标之后消失 |
| `PERMISSION_DENIED` | 调用方无权操作目标命名空间 |
| `RESOURCE_EXHAUSTED` | Endpoint 或 Publication 容量已满,或完整响应超过 Binding 限制 |
| `CONFLICT` | Publication 内容冲突或并发状态冲突 |
| `UNSUPPORTED_CAPABILITY` | Binding 不支持请求的操作 |
| `UNAVAILABLE` | Registry 当前无法形成可信快照或应用写入 |
不可见资源与不存在资源都表现为 `NOT_FOUND`,避免可见性侧信道。Filter 无匹配不是
错误,必须使用第 5 节规定的空结果形态。
## 11. 安全规则
Registry 在每个操作前执行命名空间和权限校验。Search、Discover 和 Watch 还要应用
资源可见性。Agent 定义尚不存在时,Register 也不能跳过权限校验。发布者身份不是
调用凭据。
Descriptor、URI 和 Metadata 都是不可信输入,不得保存明文凭据。发现结果不得暴露
连接归属、发布者身份、心跳或内部路由信息。Endpoint Metadata 不得使用 Nacos 内部
保留 Key。
## 12. Schema 与演进
规范性配套文件是使用 JSON Schema Draft 2020-12 的
[RAD 0.1.0 JSON Schema](../../schemas/ai/rad/0.1.0/rad-protocol.schema.json)。
普通对象使用严格属性集合,只有 Metadata Map 和 `nativeDescriptor` 是开放内容。
Schema Default 只是注解,生效值由实现物化。
新增字段、改变 `required`、扩大联合类型或改变枚举都需要新的 RAD 协议版本。领域
校验还要检查 SemVer、Version/Label 互斥、保留 Label、Endpoint 自然键、
Source/Health 条件、范围边界、顺序、Runtime Version 包含关系、Batch 原子性和容量。
JSON Schema 只校验 Version Range 字符串的粗略语法,不能替代领域校验。
## 13. 示例
### 13.1 Discover 请求
```json
{
"namespaceId": "public",
"reference": {"agentName": "Order Agent", "label": "latest"},
"filter": {
"protocols": ["a2a"],
"transports": ["JSONRPC"],
"endpointSources": ["RUNTIME", "DECLARED"],
"metadataSelector": {"zone": "cn-hangzhou-h"}
}
}
```
### 13.2 Discover 结果
```json
{
"namespaceId": "public",
"agentName": "Order Agent",
"version": "1.0.6",
"contentDigest": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"callInterfaces": [{
"protocol": "a2a",
"protocolVersion": "1.0",
"descriptorMediaType": "application/json",
"nativeDescriptor": {"name": "Order Agent", "version": "1.0.6"},
"endpointSets": [{
"source": "RUNTIME",
"sourceRevision": "murmur3-x64-128-v1:0123456789abcdef0123456789abcdef",
"endpoints": [{
"uri": "https://10.0.0.8:8443/a2a",
"transport": "JSONRPC",
"priority": 0,
"weight": 1,
"metadata": {"zone": "cn-hangzhou-h"},
"healthy": true
}]
}, {
"source": "DECLARED",
"sourceRevision": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"endpoints": []
}]
}]
}
```
### 13.3 Register 请求
```json
{
"namespaceId": "public",
"agentName": "Order Agent",
"runtimeVersion": "1.0.6",
"versionRange": "[1.0.0,2.0.0)",
"protocol": "a2a",
"endpoints": [{
"uri": "https://10.0.0.8:8443/a2a",
"transport": "JSONRPC",
"metadata": {"zone": "cn-hangzhou-h"}
}]
}
```
### 13.4 Deregister 请求
```json
{
"namespaceId": "public",
"agentName": "Order Agent",
"protocol": "a2a",
"endpoints": [{
"uri": "https://10.0.0.8:8443/a2a",
"transport": "JSONRPC"
}]
}
```
@@ -41,7 +41,7 @@ Ability name 在同一 mode 内必须唯一。Ability key 定义是连接两侧
| `SDK_CLIENT_FUZZY_WATCH` | 客户端可以使用 Config 或 Naming fuzzy watch。 |
| `SDK_CLIENT_DISTRIBUTED_LOCK` | 客户端可以使用分布式锁功能。 |
| `SDK_MCP_REGISTRY` | 客户端可以使用 MCP registry 运行时功能。 |
| `SDK_AGENT_REGISTRY` | 客户端可以使用 Agent 和 AgentCard 运行时功能。 |
| `SDK_AGENT_REGISTRY` | 客户端可以使用旧 A2A Agent 和 AgentCard 运行时功能。 |
当前服务端声明支持:
@@ -51,11 +51,27 @@ Ability name 在同一 mode 内必须唯一。Ability key 定义是连接两侧
| `SERVER_FUZZY_WATCH` | 支持 Config 或 Naming fuzzy watch。 |
| `SERVER_DISTRIBUTED_LOCK` | 支持分布式锁。 |
| `SERVER_MCP_REGISTRY` | 支持 MCP registry 操作。 |
| `SERVER_AGENT_REGISTRY` | 支持 Agent 和 AgentCard registry 操作。 |
| `SERVER_AGENT_REGISTRY` | 支持旧 A2A Agent 和 AgentCard registry 操作。 |
| `SERVER_AGENT_CARD_V1` | 支持 A2A AgentCard 1.0 协议字段。 |
新增 ability 需要同时提供具名 key 和领域规则,说明该 ability 控制的行为。
### 2.1 实验性 Agent/RAD 目标能力
[Agent API 规范](../ai/agent-api-spec.md)为 Nacos 3.3 版本线确定下列目标能力位。
它们描述目标契约;在对应常量和 Handler 完成实现前,不属于当前 Runtime 能力。
| Mode | 常量 | Wire key | 含义 |
|---|---|---|---|
| `SERVER` | `SERVER_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | Server 接受 RAD Search、Discover 和 Watch Payload。 |
| `SERVER` | `SERVER_AGENT_ENDPOINT_V1` | `agentEndpointV1` | Server 接受 RAD Runtime Endpoint 发布 Payload。 |
| `SDK_CLIENT` | `SDK_AGENT_DISCOVERY_V1` | `agentDiscoveryV1` | SDK 接受 RAD Discovery Push Payload。 |
Ability name 按 Mode 划分作用域,因此 `agentDiscoveryV1` 可以同时作为 `SERVER`
`SDK_CLIENT` 表中的 Wire key。旧 `SERVER_AGENT_REGISTRY`
`SERVER_AGENT_CARD_V1``SDK_AGENT_REGISTRY` 继续只控制旧 A2A 契约,不作为任何
RAD 操作的 fallback。
## 3. gRPC 协商流程
运行时客户端在 gRPC connection setup 阶段协商能力:
@@ -94,12 +110,21 @@ Unknown 不是成功。新功能应优先返回 fail-fast unsupported error
- Config 和 Naming fuzzy watch 必须要求 `SERVER_FUZZY_WATCH`
- 分布式锁必须要求 `SERVER_DISTRIBUTED_LOCK`,因为该功能实验性且不保证所有服务端可用。
- AI MCP registry 操作必须要求 `SERVER_MCP_REGISTRY`
- AI Agent 和 AgentCard 操作必须要求 `SERVER_AGENT_REGISTRY`
- 旧 A2A Agent 和 AgentCard 操作必须要求 `SERVER_AGENT_REGISTRY`
- A2A AgentCard 1.0 字段应要求 `SERVER_AGENT_CARD_V1`,或使用显式文档化的兼容转换。
- RAD Search、Discover 和 Watch 请求必须要求 `SERVER_AGENT_DISCOVERY_V1`
- RAD Runtime Endpoint 注册和注销必须要求 `SERVER_AGENT_ENDPOINT_V1`
- 只有 Client 声明 `SDK_AGENT_DISCOVERY_V1` 时,Server 才能 Push `SNAPSHOT`
`TERMINATED` 类型的 `AgentDiscoveryNotifyRequest`。该能力同时覆盖 ACK 和按
`watchKey` 隔离终止一个 Watch;它不允许因某个 Watch 终止而关闭共享 Payload
Connection。
功能代码不应把 positive ability result 缓存在当前 connection 生命周期之外。执行操作前应查询
运行时 connection ability,或确认缓存值属于当前 connection。
Reconnect 后,Client 必须重新协商能力,再恢复 Watch 意图。恢复后的订阅从
`AgentSubscribeResponse` 取得新的 Connection 维度不透明 `watchKey`,不得复用旧 Key。
## 6. 兼容规则
能力协商是混合版本兼容机制。新增运行时行为前应优先使用能力协商,而不是增加临时版本判断。版本号
@@ -31,6 +31,7 @@ redo 行为。本文展开[客户端运行时规范](client-runtime-spec.md)中
| Naming service-info cache | 服务端 push 或 query response | 订阅或查询服务的最后已知实例。 | 仅恢复缓存。 |
| Naming failover data | 用户或扩展提供的本地 failover source | failover switch 开启时覆盖 discovery view。 | 仅本地 discovery override。 |
| Redo data | SDK register、subscribe 或 endpoint 操作 | reconnect 后恢复运行时意图。 | 仅运行时意图。 |
| RAD 发现与 Watch 状态(目标) | Discover 结果或 Watch 注册 | 最后一个完整 Agent 发现快照和 Watch 意图。 | 仅恢复缓存和运行时意图。 |
除非领域规范显式说明,本地数据不得被视为服务端已提交状态。
@@ -115,19 +116,84 @@ Naming redo 覆盖:
持久 Naming service 状态由服务端持有,除非领域明确把某操作视为运行时意图,否则不应由客户端
redo 恢复。
AI redo 覆盖运行时 endpoint 和 subscription intent,例如 MCP 或 agent endpoint 注册。AI resource
publish/delete 语义仍由 [AI Registry 规范](../ai/ai-registry-spec.md)约束。
AI redo 覆盖运行时 endpoint 和 subscription intent,例如 MCP 或 Agent Endpoint 注册。AI resource
publish/delete 语义仍由 [AI Registry 规范](../ai/ai-registry-spec.md)约束。目标 Agent/RAD
规则在第 8 节定义。
Config listener 通过 listener resync 和 fuzzy watch resync 恢复。Client SDK 不会自动 redo Config
publish/delete 操作。
## 8. Shutdown
## 8. Agent 与 RAD 目标恢复契约
本节定义新 Agent/RAD SDK 的目标恢复契约,不表示当前已经实现该能力。只有
[Agent API 规范](../ai/agent-api-spec.md)中的 Agent/RAD 能力完成实现并经过协商后,
本节才成为生效契约。
### 8.1 Endpoint 发布 Redo 身份
SDK 按规范化 Publication 分组维护期望 Endpoint 发布状态,并保存重放所需的完整 Batch
Payload。物化后的注册 redo key 包含:
```text
(namespaceId, agentName, protocol, runtimeVersion,
canonicalVersionRange, sortedCanonicalEndpointNaturalKeys)
```
构造 Key 前,缺失的 `versionRange` 规范化为 `[runtimeVersion]`。每个 Endpoint Key
按 [RAD 协议规范](../ai/rad-protocol-spec.md)进行规范化,集合去重后按稳定 ASCII 顺序
排序。因此 URI 输入顺序、Map 顺序和省略的缺省值不会产生不同 redo 身份。
SDK 为每个
`(namespaceId, agentName, protocol, runtimeVersion, canonicalVersionRange)`
分组维护一个规范化 Endpoint Map。Register 把提交的 Endpoint Upsert 合并到该 Map
并原子替换物化后的 redo record。Deregister 按 RAD 跨 Binding 注销语义,从相同
namespace、Agent 和 protocol 的全部本地分组中移除每个已提交自然键。Redo Payload
保留完整 URI、Priority、Weight 和 Metadata;不能因为这些非身份值不进入 redo key
就丢弃它们。
### 8.2 HTTP 与 gRPC Publisher 恢复
HTTP Agent Publisher 为一个 SDK 实例生成一个 `X-Nacos-Client-Id`。在该 SDK 实例
生命周期内,这个 Id 在请求重试、Server 切换、故障转移、Heartbeat 和 Redo 时保持稳定;
进程重启后生成新 Id。
任一 Agent Endpoint 请求返回 `HTTP_CLIENT_NOT_FOUND` 时,SDK 将该 HTTP Client
拥有的全部 Endpoint redo record 标记为未注册,并 redo 每个完整期望 Publication 分组。
只重试失败 Endpoint 不充分,因为 Server 已声明整个 HTTP Client 状态不存在。
gRPC Endpoint 意图归属于当前 connection id。Reconnect 后,SDK 获取新的 connection id
把旧 Connection 的全部 Endpoint redo record 标记为未注册,并在新 Connection 下重放完整
期望分组。HTTP 与 gRPC Publisher record 必须隔离;一种 Transport 不得注销另一种
Transport 拥有的 Contribution。
### 8.3 Watch 恢复身份
规范化本地 Watch Key 包含:
```text
(namespaceId, canonicalAgentReference, canonicalFilter, listenerIdentity)
```
Reference 规范化保持精确 Version、Label 和 Latest 之间的区别。Filter 规范化应用缺省值,
并对集合字段排序和去重。Listener identity 是取消 Watch 时使用的同一 Listener 实例。
SDK 将 `AgentSubscribeResponse` 返回的 Connection 维度不透明 `watchKey` 与该规范化本地
身份一同保存。收到的 `SNAPSHOT``TERMINATED` 通知只使用 Wire Key 定位记录;SDK
不解析该 Key,也不把它作为 Redo 身份。
gRPC Disconnect 时,SDK 将每条 Watch record 标记为未注册。新 Connection 建立后,
它恢复相同的规范化本地 Watch Key,丢弃各旧 Wire Key,并保存随初始完整结果返回的新
`watchKey`。携带 `errorCode=NOT_FOUND``TERMINATED` 通知只删除所标识的 Watch
record 及其缓存快照,因此后续 Reconnect 不再重试。完整替换、ACK、Missed Push 和终止
行为由[运行时推送与重连规范](runtime-push-reconnect-spec.md)定义。
## 9. Shutdown
SDK shutdown 必须清理内存 redo state、停止后台 retry task、关闭 transport client,并停止本地
cache/failover refresh task。除非用户显式调用缓存清理操作,shutdown 不应删除用户维护的 failover
文件或服务端派生 snapshot。
## 9. 待处理问题
## 10. 待处理问题
- Naming redo 当前仍使用独立实现,较新的 AI redo 使用通用 redo 抽象。后续实现应收敛到共享 redo
模型。
@@ -48,6 +48,9 @@ Push message 通知运行时客户端:服务端视图可能已经变化。Push
resubscription 刷新。
- AI push 行为由各 AI resource spec 版本化定义,并必须保持与对应 query API 相同的身份规则。
目标 RAD Watch 携带完整 Discovery Snapshot,而不是只携带变化身份。该 Snapshot 是替换本地
RAD Discovery Cache 的权威值;Registry 仍是资源权威,Discover Re-query 可以刷新 Snapshot。
## 3. 服务端 Connection 状态
运行时 listener 或 subscription state 绑定到服务端 connection id。连接关闭时,服务端必须移除
@@ -93,7 +96,68 @@ Reconnect 后,客户端必须恢复运行时意图:
客户端恢复细节由[客户端本地缓存与 Redo 规范](client-local-cache-redo-spec.md)定义。连接选择和
存活由[客户端连接与故障切换规范](client-connection-failover-spec.md)定义。
## 6. 顺序
## 6. Agent 与 RAD 目标 Watch 契约
本节定义 Agent/RAD 的目标 gRPC Watch 契约,不表示当前已经实现该能力。在
[Agent API 规范](../ai/agent-api-spec.md)中的 Agent/RAD 能力完成实现并经过协商前,
实现不得暴露或声明支持该行为。首版 Nacos HTTP Binding 支持 Discover,但不支持 Watch。
### 6.1 身份与初始结果
规范化 SDK Watch Key 为:
```text
(namespaceId, canonicalAgentReference, canonicalFilter, listenerIdentity)
```
规范化 Reference 保留调用方选择的是精确 Version、Label 还是 Latest。规范化 Filter
应用缺省值,并对集合字段排序和去重。Listener identity 是取消时传入的同一 Listener
实例。实现可以复用 Wire Subscription,但必须保留该公开身份并隔离 Callback。
Server 在创建 Watch 前执行 Discover。`NOT_FOUND` 不创建服务端或客户端 Watch State。
订阅成功的 `AgentSubscribeResponse` 返回 Connection 维度的不透明 `watchKey` 和当前
完整 `AgentDiscoveryResult`。SDK 将该 Key 映射到规范化本地 Watch Key,不解析其内容。
RAD 本身仍然只有六个根消息,不定义协议层 Event Envelope。Nacos gRPC Binding 使用
`AgentDiscoveryNotifyRequest(watchKey, eventType, result?, errorCode?)` 在共享 Payload
Connection 上复用 Watch 事件;该 Request 是 Binding 对象,不是新的 RAD 根消息。
### 6.2 完整替换与 Listener 投递
`eventType=SNAPSHOT` 时,`AgentDiscoveryNotifyRequest` 必须携带一个完整
`AgentDiscoveryResult` 且不携带错误。Client 收到后,按标识的 Watch 原子替换上一个
Snapshot,再通过 `AgentDiscoveryNotifyResponse` 发送 ACK。不得合并不同 Snapshot 中的
Calling Interface、EndpointSet 或 Endpoint。解析出的 Version、`contentDigest`
`sourceRevision` 变化表示可能存在新 Snapshot;这些 Token 只用于相等比较和去重,
不能用于排序。
过滤后为空是合法的完整结果,并会替换上一个 Snapshot。Naming Push-empty Protection
不适用于 RAD。Listener 异常必须与 Connection 处理和其他 Listener 隔离;它不会把已经
接受的 Snapshot 变成未确认事件。
### 6.3 目标消失
之前可发现的目标变为不存在、不可见、Disabled,或不再存在可发现的 Online 目标 Version
时,Server 发送 `eventType=TERMINATED`、不携带 Result 且
`errorCode=NOT_FOUND``AgentDiscoveryNotifyRequest`。该事件只关闭标识的
`watchKey`;共享 Payload Connection 和其他 Watch 保持有效。Client 投递终止状态,
只删除对应本地 Watch Key 和缓存 Snapshot,发送 ACK,并且后续 Reconnect 不再 Redo
该 Watch。Agent 仍存在但 Filter 当前不匹配任何 Interface 或 Endpoint 时,仍返回成功的
`SNAPSHOT`,不属于终止状态。
### 6.4 Missed Push 与 Reconnect
Connection 丢失、通知被拒绝或 Binding 特定的 Gap Detection 表明可能遗漏 Push 时,
Client 必须使用相同 Namespace、规范化 Reference 和规范化 Filter 重新执行 Discover
然后原子替换缓存 Snapshot。不得通过应用本地推导出的 Delta 重建遗漏状态。
gRPC Disconnect 时,Server 移除 Connection 维度 Watch StateSDK 将对应本地 Watch
record 标记为未注册。Reconnect 后,SDK 使用新的 connection id 恢复相同的规范化本地
Watch Key,并丢弃各旧 Wire `watchKey`;每次成功重新订阅都返回新的不透明 `watchKey`
和初始完整结果,后者在后续 Push 前成为新的完整 Snapshot。恢复期间收到终止结果时,
按第 6.3 节处理,不继续保留在 Redo State 中。
## 7. 顺序
Push delivery order 只在某个节点的本地 event 和 task path 内成立。它不是跨集群全局 total order。
@@ -106,15 +170,14 @@ Push delivery order 只在某个节点的本地 event 和 task path 内成立。
- Naming 持久服务和元数据可见性由
[Naming 持久服务 CP 一致性规范](../naming/naming-persistent-cp-consistency-spec.md)定义。
## 7. 失败规则
## 8. 失败规则
- connection 不存在时,应取消或跳过对该 connection 的 push。
- push timeout 不证明客户端没有观察到变化,只表示服务端没有在超时时间内收到成功 ack。
- 客户端必须能够通过 re-query、resync 或 redo 从 missed push 中恢复。
- Server push 不得隐藏底层 query path 的鉴权失败。
## 8. 待处理问题
## 9. 待处理问题
- AI runtime push 和 reconnect 行为应在 AI SDK subscription API 稳定后进一步细化。
- push retry、timeout 和 reconnect recovery 观测应遵循
[可观测钩子规范](../design/foundation-observability-hooks-spec.md)中的共享字段和 label 指引。
@@ -103,6 +103,7 @@ Schema 清理应平衡正确性和运维成本。冗余字段可以为了避免
[nacos-api-legacy-adapter](https://github.com/nacos-group/nacos-api-legacy-adapter)
- spec 出现前发布的 v3 兼容端点;
- AI Prompt legacy endpoints 和旧 Pipeline REST 风格端点;
- 旧 A2A AgentCard Java、gRPC、Admin、Maintainer 和 Console facade
- Naming API 定义的 service selector 字段和请求参数;
- Config 聚合配置字段及相关数据库列;
- 历史插件配置 key
@@ -131,7 +132,21 @@ Schema 清理应平衡正确性和运维成本。冗余字段可以为了避免
领域规范只应在迁移上下文中提及 legacy v1/v2 行为,或在当前兼容路径依赖它时进行说明。
## 10. 相关规范
## 10. 旧 A2A Agent Facade
标准 Agent 模型使用 `type=agent`、协议无关 Version 和 RAD 发现。历史 A2A AgentCard
表面仅用于兼容,并按照 [A2A Agent 规范](../ai/a2a-agent-spec.md)在服务端边界适配。
不同受众采用不同兼容窗口:
- Java `A2aService` 和旧 A2A gRPC Payload 当前不设删除版本;
- Admin `/v3/admin/ai/a2a``A2aMaintainerService` 兼容到 4.0.x 窗口;
- Console `/v3/console/ai/a2a` 兼容到 3.4.x 窗口,内置 UI 完成迁移后可以移除。
不得只向这些 facade 增加新能力。新增开发以 Agent Management 和 RAD 契约为目标。
历史数据与混合 Server 滚动升级使用独立迁移方案,本身不延长 API 兼容窗口。
## 11. 相关规范
- [HTTP API 规范](../http-api/api-spec.md)
- [V3 API 范围](../http-api/v3-api-surface.md)
@@ -141,3 +156,5 @@ Schema 清理应平衡正确性和运维成本。冗余字段可以为了避免
- [持久化与 Dump 规范](foundation-persistence-dump-spec.md)
- [集成与适配器规范](../integration/integration-adapter-spec.md)
- [插件规范](../plugin/README.md)
- [Agent 管理规范](../ai/agent-management-spec.md)
- [RAD 协议规范](../ai/rad-protocol-spec.md)
@@ -31,6 +31,9 @@ AP 和 CP 是 CAP 理论中的一致性选择。在 Nacos 中,AP 路径优先
| Distro | Naming 运行时状态 | 在服务端节点之间同步临时、客户端拥有的服务实例状态。 |
| Config Notify | Config 缓存与 listener 可见性 | 通知 peer 节点某个 Config 资源发生变化,使本地 dump 缓存和 listener 刷新。 |
第 6 节定义 Agent HTTP Client 状态已确定的目标 Distro 契约。它不属于上表的当前实现,也不
表示 `AI_AGENT_HTTP_CLIENT` 已经实现或对外声明。
历史上 `consistency` 模块中存在 `APProtocol` 接口,但当前活跃 AP 实现是 Distro 基础能力和
Config Notify 路径。新的规范应直接描述 AP 语义,而不应假设所有 AP 行为都必须实现 `APProtocol`
@@ -126,7 +129,110 @@ Naming Distro 传输通过
`DistroDataRequest` / `DistroDataResponse` 承载,并遵循
[内部 RPC 与集群请求规范](foundation-internal-rpc-spec.md)。
## 6. Config Notify 契约
## 6. AI Agent HTTP Client Distro 目标契约
本节定义 HTTP Runtime Endpoint publisher 状态的目标设计。只有服务端完成实现并声明对应
Agent/RAD 能力后,它才成为有效运行时契约。
目标流程为:
```text
Agent HTTP request
-> route by clientId to responsible server
-> mutate complete HTTP Client state
-> Distro full-state CHANGE or DELETE
-> peer Client state
-> Naming and RAD runtime events
```
### 6.1 资源身份与路由
| 项目 | 目标语义要求 |
| --- | --- |
| Distro resource type | 常量 `AI_AGENT_HTTP_CLIENT`。 |
| `resourceKey``responsibleId` | 原始 opaque HTTP `clientId`。 |
| 责任归属 | Distro 使用稳定 client-id 分片选择唯一责任节点。 |
| 模块隔离 | module 不拼入分片 keyresource type 和专用 Client manager 将 Agent 状态与其他模块使用的同名 client id 隔离。 |
| Native owner | 只有责任节点拥有 native HTTP Client、`lastActiveTime` 和超时调度。 |
| 远端入口 | 非 owner 将变更或 heartbeat 路由到责任节点,不创建独立活性计时器。 |
首次 Endpoint 注册成功时,将 Client 绑定到一个鉴权主体和一个 `namespaceId`。状态只保存稳定
主体标识,不保存 credential 或 access token。后续 registration、deregistration 和 heartbeat
必须使用相同鉴权主体;后续包含 namespace 的请求必须使用已绑定 namespace,无 body 的 heartbeat
使用已保存的 namespace binding。不匹配时拒绝请求且不刷新活性。
`clientId` 只是路由和 publisher 归属标识,不是鉴权凭据。在其他 Distro resource type 下复用
相同文本时,不得续约、修改、verify、snapshot 或删除 Agent Client。
### 6.2 完整状态与操作
一个同步 Client datum 是完整替换状态,包含:
| 字段组 | 内容 |
| --- | --- |
| 身份 | `clientId`、绑定的 `namespaceId` 和鉴权主体标识。 |
| 收敛 | 领域 revision 和语义活性状态。 |
| 活性 | `lastActiveTime``heartbeatIntervalMillis``unhealthyTimeoutMillis``expireTimeoutMillis`。 |
| Publication | 全部 Agent Endpoint publication group 及其完整 Endpoint contribution。 |
Endpoint group 包含 AgentName、canonical protocol、runtime Version、canonical Version range
和 Endpoint payload。Distro datum 永远不携带局部 Endpoint patch,并排除 credential、请求
header 和原始鉴权材料。
`AI_AGENT_HTTP_CLIENT` 接受以下 operation
| Operation | 语义 |
| --- | --- |
| `CHANGE` | 幂等创建或替换一个完整 Client state。本地 Endpoint 变化和语义活性转换产生该操作。 |
| `DELETE` | 删除完整 Client 及其全部 publisher contribution;状态不存在时幂等 no-op。 |
| `VERIFY` | 比较 client id、存在性和领域 revision;不匹配时调度定向修复。 |
| `SNAPSHOT` | 传输来源 snapshot 拥有的完整 Client state 集合。 |
| `QUERY` | 按 client id 返回一个完整 Client state,或返回强类型 not-found 结果。 |
创建使用 `CHANGE`;本目标不要求独立 `ADD` 语义。Apply 逻辑拒绝过期 revision,接受重复的
相同状态,并且不得把乱序完整替换中的字段合并。`VERIFY``QUERY` 使用完整 state 修复,
不得使用 Endpoint delta。
普通 heartbeat 只在责任节点刷新 `lastActiveTime`,不在每个间隔广播。Active/unhealthy 之间的
语义转换、Endpoint 变化或删除会推进领域 revision,并同步最新完整状态。
### 6.3 超时与故障转移
服务端返回并保存满足以下关系的超时值:
```text
heartbeatIntervalMillis < unhealthyTimeoutMillis < expireTimeoutMillis
```
Heartbeat 和成功的 Endpoint 写入会刷新 Client 活性。超过 `unhealthyTimeoutMillis` 后,该
Client 的 contribution 仍保留在 RAD/Naming 投影中,但变为 unhealthy;与其他健康 publisher
共享的 Endpoint 仍可能聚合为 `healthy=true`。Client 转换产生完整状态 `CHANGE`。过期前恢复
活性时 Client 回到 active,并在公开健康投影发生变化时产生 `CHANGE`。超过
`expireTimeoutMillis` 后,责任节点产生 `DELETE` 并移除全部 contribution。注销最后一个
Endpoint 时立即删除空 Client。
责任转移时,新 owner 只有在从本地 replica、`SNAPSHOT``QUERY` 安装完整 state,并校验其
identity 和 revision 后,才能启动超时调度。随后从接管时刻开始一个与该 Client
`expireTimeoutMillis` 等长的 failover grace window。Grace 期间不得只因为复制的
`lastActiveTime` 过旧就使 Client 过期。合法 heartbeat 会结束 grace 并恢复普通超时计算;到
grace deadline 仍没有 heartbeat 时,Client 过期。
新 owner 无法取得完整 state 时不得合成空 Client。Heartbeat 返回 `HTTP_CLIENT_NOT_FOUND`
使 SDK 将全部期望 Endpoint group 标记为未注册,并使用同一 client id redo 完整 registration
batch。Registration 可以创建缺失 state;注销缺失 state 仍成功 no-op。
### 6.4 Apply 事件与可见性
Apply 本地变更、远端 `CHANGE`、修复后的 `QUERY``SNAPSHOT` state 时,必须物化相同的
Agent HTTP Client 和原始 Naming publisher contribution。发生语义变化的 apply 会产生 Naming
Client/service change 事件以重建 index 和 instance 聚合,同时产生 RAD runtime projection/watch
事件以刷新 Endpoint snapshot 和 `sourceRevision`
Apply `DELETE` 会产生对应删除事件。相同语义 revision 的重复 state 不产生重复领域变更。
`VERIFY` 本身不修改领域状态或产生 discovery 事件。远端 apply 遵循内部 RPC 的鉴权和来源校验;
它会恢复已保存的鉴权主体 binding,但永远不把 `clientId` 当作权限凭据。
## 7. Config Notify 契约
Config Notify 是 AP 风格的变更传播路径。它不是持久存储协议,也不承载权威配置内容。
@@ -156,7 +262,7 @@ Config write or delete
对于 ConfigAP notify 成功表示 peer 节点已被通知刷新服务状态。它不替代持久化成功,也不使推送
payload 成为权威内容。
## 7. 失败语义
## 8. 失败语义
AP 使用方必须处理部分成功。
@@ -170,18 +276,19 @@ AP 使用方必须处理部分成功。
- AP 恢复过程必须可以通过日志、指标、trace 或诊断观察;
- AP 失败不得静默地把运行时状态转化为持久元数据。
## 8. 边界规则
## 9. 边界规则
- AP 一致性是最终收敛,不是强一致。
- 本地 `NotifyCenter` 事件本身不是 AP 一致性;只有领域定义了远端传播和修复行为时,它才成为
AP 行为的一部分。
- Distro 是运行时数据的正式共享 AP 框架。Config Notify 是 Config 特定的缓存/listener 可见性
AP 通知路径。
- Distro 是运行时数据的正式共享 AP 框架。Naming 当前使用它同步临时 Client state
`AI_AGENT_HTTP_CLIENT` 只在目标能力实现后加入该框架。Config Notify 是 Config 特定的
cache/listener 可见性 AP 通知路径。
- AP 路径不得用于权限、namespace 元数据、持久服务元数据、插件状态或数据库 schema 状态。
- 除非接口规范显式暴露,AP payload 是内部集群契约。
- AP 传输必须遵循内部 RPC 的鉴权、来源、payload 和重试规则。
## 9. 相关规范
## 10. 相关规范
- [基础能力规范](foundation-capabilities-spec.md)
- [内部 RPC 与集群请求规范](foundation-internal-rpc-spec.md)
@@ -194,4 +301,6 @@ AP 使用方必须处理部分成功。
- [Config 规范](../config/config-spec.md)
- [Config 监听与订阅规范](../config/config-listener-watch-spec.md)
- [Naming 一致性与客户端状态规范](../naming/naming-consistency-client-spec.md)
- [Agent 存储规范](../ai/agent-storage-spec.md)
- [RAD 协议规范](../ai/rad-protocol-spec.md)
- [gRPC API 规范](../grpc-api/api-spec.md)
+13 -11
View File
@@ -86,7 +86,7 @@ Group 适合表达同一类微服务资源的业务隔离,例如应用、业
### 3.2 resourceType
resourceType 是资源类型分组。它适合表达一类共享治理模型中的不同资源类型,例如
AI Registry 中的 `mcp``a2a``prompt``skill``agentspec`
AI Registry 中的 `mcp``agent``prompt``skill``agentspec`
resourceType 不表达业务分组。AI 资源不应再引入 Group 作为身份字段,除非对应领域
规范明确给出额外语义。
@@ -102,7 +102,7 @@ resourceName 是在 `NamespaceId + Group/resourceType` 下稳定标识资源的
| Config | `dataId` |
| Naming service | `serviceName` |
| MCP Server | `name``mcpName` |
| A2A AgentCard | `name` `agentName` |
| Agent | `agentName` |
| Prompt | `promptKey` |
| Skill | `name` |
| AgentSpec | `name` |
@@ -193,7 +193,7 @@ NamespaceId -> resourceType -> resourceName
```
它覆盖 [MCP Server](../ai/mcp-server-spec.md)、
[A2A AgentCard](../ai/a2a-agent-spec.md)、[Prompt](../ai/prompt-spec.md)、
[Agent](../ai/agent-management-spec.md)、[Prompt](../ai/prompt-spec.md)、
[Skill](../ai/skill-spec.md)、[AgentSpec](../ai/agentspec-spec.md) 等 AI Registry
资源。共享 AI 模型由 [AI Registry 规范](../ai/ai-registry-spec.md)和
[AI 资源模型规范](../ai/ai-resource-model-spec.md)定义。
@@ -232,19 +232,21 @@ MCP 特有元数据包括 protocol、front protocol、repository、packages、ic
website URL、本地或远程 server config、endpoint spec、tool spec、status 和自动
发现的 capabilities。
### 6.2 A2A AgentCard
### 6.2 Agent
A2A AgentCard 的标准资源身份为:
Agent 的标准资源身份为:
```text
namespaceId -> a2a -> agentName
namespaceId -> agent -> agentName
```
AgentCard 资源描述 Agent 的能力、skills、supported interfaces、provider 信息、
security schemes、signatures 和 endpoint 元数据
Agent 拥有目录和治理元数据。每个 Agent Version 拥有有序的协议无关调用接口。
A2A 是其中一种协议 binding,其原生描述为 AgentCard;它不是第二种顶层 AI 资源身份
Runtime Endpoint 由客户端生命周期持有,并投影到 Agent 发现结果中,但不成为 Version 内容。
`registrationType` 参与 AgentCard 查询和兼容语义,但它不是顶层第二层字段。需要
在具体 A2A 领域规范中定义它与 resourceName、version 和 endpoint 的关系。
完整模型由 [Agent 管理规范](../ai/agent-management-spec.md)定义;远程调用方发现遵循
[RAD 协议规范](../ai/rad-protocol-spec.md),旧 AgentCard API 作为兼容 facade 由
[A2A Agent 规范](../ai/a2a-agent-spec.md)定义。
### 6.3 Prompt
@@ -285,7 +287,7 @@ AgentSpec 的标准资源身份为:
namespaceId -> agentspec -> agentSpecName
```
AgentSpec 通过引用 Prompt、Skill、MCP Server、A2A Agent 或其他必要资源来组装
AgentSpec 通过引用 Prompt、Skill、MCP Server、Agent 或其他必要资源来组装
Agent 配置。AgentSpec 应通过稳定身份和 version 或 label 引用其他资源,不应引用
存储实现细节。
+21
View File
@@ -219,6 +219,27 @@ AI payload 语义由 [AI Registry 规范](../ai/ai-registry-spec.md)和各资源
| `BatchAgentEndpointRequest` | `AgentEndpointResponse` | write | `agentName`, `endpoints` | 替换当前客户端为某个 Agent 注册的 endpoints。 |
| `QueryPromptRequest` | `QueryPromptResponse` | read | `namespace`, `promptKey`, `version`, `label`, `md5` | 按版本、标签、latest 或 md5 查询 Prompt。 |
下列 Agent/RAD Payload 是 [Agent API 规范](../ai/agent-api-spec.md)确定的实验性目标。
在 Runtime 中具备对应类、Handler、SPI 注册和协商能力位之前,它们不属于当前已实现
Payload 清单。
| 目标 Request type | 目标 Response type | 方向 | 契约 |
| --- | --- | --- | --- |
| `AgentSearchRequest` | `AgentSearchResponse` | read | 搜索 Agent 目录并返回一页 `AgentCatalogEntry`。 |
| `AgentDiscoveryRequest` | `AgentDiscoveryResponse` | read | 发现一个 Agent 并返回完整的 `AgentDiscoveryResult`。 |
| `AgentSubscribeRequest` | `AgentSubscribeResponse` | read | 订阅或取消订阅 Agent Reference 和可选 Filter;订阅时返回不透明 `watchKey` 和当前完整结果。 |
| `AgentDiscoveryNotifyRequest` | `AgentDiscoveryNotifyResponse` | server push | 为一个 `watchKey` 推送 `SNAPSHOT``TERMINATED` 事件并接收 ACK。 |
| `AgentEndpointRegisterRequest` | `AgentEndpointOperationResponse` | write | Upsert 归当前 Connection 所有的一批 Runtime Endpoint 注册。 |
| `AgentEndpointDeregisterRequest` | `AgentEndpointOperationResponse` | write | 幂等删除归当前 Connection 所有的一批 Runtime Endpoint 注册。 |
在该目标 Binding 中,`AgentDiscoveryNotifyRequest` 包含 `watchKey`
`eventType``SNAPSHOT` 必须携带完整 `AgentDiscoveryResult` 且不携带错误;
`TERMINATED` 不携带 Result,并固定要求 `errorCode=NOT_FOUND`。Client 对两种事件都
发送 ACK。终止事件只结束共享 Payload Connection 上由该 `watchKey` 标识的 Watch
不结束 Connection 或其他 Watch。`AgentSubscribeResponse` 是 Connection 维度不透明
`watchKey` 的来源,Reconnect 后也由新 Response 提供。这些 Wrapper 仍属于 gRPC
Binding 对象,不扩展 RAD 的六个根消息。
Skill ZIP 下载和 AgentSpec 组装属于 Java SDK interface 能力,但当前 Java 客户端
实现使用 HTTP/config 组合,不对应专用 gRPC payload。
+40 -2
View File
@@ -172,7 +172,45 @@ V3 Auth API 位于默认鉴权插件中,而不是 core 模块中:
默认鉴权插件随 Nacos 一起发布,因此它的 v3 auth 端点应遵循 Nacos HTTP API 规范和
[鉴权插件规范](../auth/auth-plugin-spec.md)。
## 8. 文档 Gap 记录
## 8. 已批准的 Agent/RAD 目标 API 面
下列路径是 [Agent API 规范](../ai/agent-api-spec.md)确定的实验性目标。在对应
Controller、鉴权、传输 Binding 和测试完成前,它们不属于当前已实现 API 清单,也不计入
第 3 节的 Controller 统计。
Client 目标路径:
| Method | Path | 契约 |
| --- | --- | --- |
| GET | `/v3/client/ai/agents/search` | 搜索 Agent 目录。 |
| GET | `/v3/client/ai/agents` | 发现一个 Agent,可附带 Discovery Filter。 |
| POST | `/v3/client/ai/agents/endpoints` | Upsert 一批 Runtime Endpoint 注册。 |
| DELETE | `/v3/client/ai/agents/endpoints` | 使用 JSON body 注销一批 Runtime Endpoint。 |
| PUT | `/v3/client/ai/agents/endpoints/heartbeat` | 刷新一个 HTTP Publisher Client 的活性。 |
Admin 和 Console 目标路径分别使用 `/v3/admin/ai/agents`
`/v3/console/ai/agents` 前缀。Console 是相同相对管理契约的 UI Facade。
| 相对路径 | Method | 契约 |
| --- | --- | --- |
| *Base path* | GET, POST, PUT, DELETE | 读取、创建、更新或删除 Agent 定义。 |
| `/list` | GET | 列举 Agent Summary。 |
| `/versions` | GET | 列举 Version Summary。 |
| `/version` | GET | 读取一个精确 Version 定义。 |
| `/runtime-endpoints` | GET | 读取一个完整、不分页的 Runtime Endpoint Snapshot。 |
| `/draft` | POST, PUT, DELETE | 创建、更新或删除 Draft。 |
| `/submit` | POST | 提交 Draft。 |
| `/publish` | POST | 发布 Reviewed Version。 |
| `/force-publish` | POST | 经审计地绕过 Pipeline。 |
| `/redraft` | POST | 将 Reviewed Version 退回 Draft。 |
| `/online` | POST | 将 Offline Version 上线。 |
| `/offline` | POST | 将 Online Version 下线。 |
| `/labels` | PUT | 更新自定义 Version Label。 |
目标 API 不增加 Client HTTP Watch 或 Endpoint List GET。Watch 和 Push 使用协商后的
gRPC BindingRuntime 查看使用 Admin 或 Console 的 `/runtime-endpoints` 路径。
## 9. 文档 Gap 记录
这不是 bug 列表,而是记录当前文档和代码可能描述了不同 API 面的地方。
@@ -196,7 +234,7 @@ V3 Auth API 位于默认鉴权插件中,而不是 core 模块中:
`ControllerAdvice`,可能返回纯文本错误体。它们应在 v3 API 上收敛到
`NacosApiExceptionHandler`
## 9. 废弃兼容说明
## 10. 废弃兼容说明
部分 v3 AI API 在本规范建立之前已经发布,后续又被更清晰的生命周期 API 或
REST 风格 API 替代。这些旧端点应视为废弃兼容 API:
@@ -57,6 +57,37 @@ Naming 同时区分两类元数据来源:
| `preserved.ip.delete.timeout` | 心跳删除超时覆盖值。 |
| `preserved.instance.id.generator` | instance id 生成器选择。 |
完整的 `__nacos.agent.endpoint.*__` 命名空间保留给 Agent Runtime Endpoint 投影。
Version 1 使用以下 key
| Key | 含义 |
| --- | --- |
| `__nacos.agent.endpoint.path__` | URI path。 |
| `__nacos.agent.endpoint.transport__` | 规范 transport;必须与 Naming cluster 一致。 |
| `__nacos.agent.endpoint.protocol__` | URI scheme,不是 Agent CallInterface protocol token。 |
| `__nacos.agent.endpoint.protocolVersion__` | 可选的旧 A2A protocol-version 兼容事实。 |
| `__nacos.agent.endpoint.supportTls__` | 投影 URI 是否使用 TLS。 |
| `__nacos.agent.endpoint.query__` | 原始 URI query。 |
| `__nacos.agent.endpoint.tenant__` | 存在时保存 protocol native tenant。 |
| `__nacos.agent.endpoint.version__` | 单 binding runtime Version 兼容和诊断镜像。 |
| `__nacos.agent.endpoint.versionRange__` | 单 binding canonical Version range 兼容和诊断镜像。 |
| `__nacos.agent.endpoint.bindings__` | Runtime Version 和 Version range binding 的 canonical JSON 数组。 |
| `__nacos.agent.endpoint.priority__` | Endpoint priority;数值越小优先级越高。 |
只有 Agent Runtime Registry 可以写入该前缀下的 key。公开 runtime 和 operational metadata
写入必须拒绝这些 key,因此普通 operational-over-runtime 优先级规则不覆盖 Agent 投影事实。
Endpoint weight、enabled 状态和健康状态继续使用 Naming Instance 原生字段,不使用保留
metadata key。
只有 A2A 兼容 Adapter 可以写入 `protocolVersion`。公开 RAD Endpoint metadata 和 Runtime
revision 都排除该值。反向投影旧 A2A 响应时优先使用该值;缺失时回退到目标 Agent
CallInterface 的 `protocolVersion`
`bindings` 是 Version 匹配事实。数组恰好包含一项时,Registry 同时写入 `version`
`versionRange` 镜像;数组包含多项时移除两个 singular key。读取方在 `bindings` 存在时以其
为准,不得合并过期 singular 值。精确 canonical 数组和 Runtime 投影规则由
[Agent 存储规范](../ai/agent-storage-spec.md)定义。
新的核心行为不得绑定到任意用户元数据 key。如果某个元数据 key 会改变 Naming 行为,必须被保留并
写入文档。
@@ -109,5 +140,6 @@ Service metadata、cluster metadata 和 instance metadata 操作通过 CP metada
- [Naming 资源规范](naming-resource-spec.md)
- [Naming 健康检查与保护规范](naming-health-protection-spec.md)
- [Naming 一致性与客户端状态规范](naming-consistency-client-spec.md)
- [Agent 存储规范](../ai/agent-storage-spec.md)
- [事件分发与 NotifyCenter 规范](../design/foundation-event-dispatch-spec.md)
- [兼容与废弃策略规范](../design/compatibility-deprecation-spec.md)
@@ -84,6 +84,28 @@ Nacos 资源身份。
SHA-256 兜底具有确定性但不可逆,逻辑资源身份仍由 AI 资源元数据持有;`save``get`
`delete` 必须使用完全一致的物理映射。
### Agent 逻辑坐标
对于 `type=agent`Agent 领域在向 provider 传递 opaque `StorageKey` 前构造以下 Nacos
Config 逻辑坐标:
```text
group = agent-version
dataId = agent__<rad-ascii-v1(agentName)>__<version>.json
```
`rad-ascii-v1` 和完整 Agent Version 存储契约由
[Agent 存储规范](../ai/agent-storage-spec.md)定义。该坐标是 provider 的逻辑输入,不是向
调用方暴露的物理 Config 身份。
内置 provider 必须把两个逻辑段都传给通用 `NacosAiConfigKeyCodec`;不能因为 Agent 领域已经
编码 `agentName` 就跳过该 codec。物理限制内的安全值与逻辑值相同。长度和保留格式处理完全由
通用 codec 负责:超长候选值使用其确定性 SHA-256 兜底,得到的物理结果不可逆。
上层可以持久化逻辑 key format 和 content digest,但不得解析物理 Config key、要求物理 key
可逆,或根据物理 key 重建 Agent 身份。`save``get``delete` 始终通过同一个 codec 重新
计算物理坐标。
provider 不会双读旧版物理映射产生的坐标。对已受影响的 `nacos_config` 存量数据,
必须在只使用新映射的节点启动前,通过协调的维护窗口完成迁移。迁移必须仅限 AI 自有
坐标,提前校验目标唯一键冲突,并在坐标改写后重建 Config 缓存。`nacos-ai-prompt` group
@@ -101,6 +123,7 @@ AI 存储 provider 接入统一插件 state。禁用非 critical provider 后,
nacos.ai.prompt.storage.provider=nacos_config
nacos.ai.skill.storage.provider=nacos_config
nacos.ai.agentspec.storage.provider=nacos_config
nacos.ai.agent.storage.provider=nacos_config
```
它们属于领域路由策略,不是 `ai-storage:nacos_config` 所拥有的私有配置 definitions。
+47 -5
View File
@@ -52,8 +52,10 @@ Java Client SDK 是现有运行时应用行为的基准。它的连接、server
`NamingMaintainService` 在 3.3.0 后已废弃。新的管理类接入应使用
`nacos-maintainer-client`
一个 Java SDK 实例绑定一个命名空间。需要访问多个命名空间的应用应创建多个
SDK 实例,并在不再使用时关闭实例。
一个 Java Client SDK 实例绑定一个命名空间。需要访问多个命名空间的应用应创建多个
Client SDK 实例,并在不再使用时关闭实例。公开运行时接口不暴露 namespace 参数,
实现使用构造时绑定的 namespace。该规则不适用于 Maintainer SDK:其 Agent 管理接口
不绑定 namespace,并要求每次调用都显式传入 namespace。
## 3. Java Client SDK 配置模型
@@ -151,10 +153,45 @@ context,而不是修改请求 payload 或让无关 SDK 调用失败。默认 N
`getServicesOfServer` 的 selector overload 已废弃,仅作为兼容面保留。
### 5.3 AiService 和 A2aService
### 5.3 AiService、AgentDiscoveryService 和 A2aService
`AiService` 继承 `A2aService`
资源语义由 [AI Registry 规范](../ai/ai-registry-spec.md)和各 AI 资源类型规范定义。
本节的 Agent/RAD 契约是目标契约,不是当前已经实现的 Java 方法清单。只有新的
Agent/RAD 能力完成实现并经过协商后才生效;在此之前,现有 `AiService`
`A2aService` 方法仍是生效的兼容面。
目标继承关系为:
```text
AiService extends AgentDiscoveryService, A2aService
```
增加该父接口时,不能让已经编译的第三方 `AiService` 实现立即发生 linkage failure。新增的
继承方法使用兼容 default bridge,在实现未 override 时报告不支持;Nacos 官方实现 override
完整目标接口面。
`AgentDiscoveryService` 提供以下 namespace-bound 方法:
| 能力 | 方法 | 契约 |
| --- | --- | --- |
| Search | `searchAgents` | 接受 `AgentSearchRequest`,返回 `Page<AgentCatalogEntry>`。 |
| Discover | `discoverAgent` 重载 | 接受 `AgentReference` 和可选 `AgentDiscoveryFilter`,返回一个完整 `AgentDiscoveryResult`。 |
| Watch | `subscribeAgent` 重载 | 接受相同 Reference、可选 Filter 和 Listener;返回当前完整结果,后续传递完整替换结果。 |
| 取消 Watch | `unsubscribeAgent` 重载 | 按相同 Reference、Filter 和 Listener identity 移除 Watch。 |
| 注册 Endpoint | `registerAgentEndpoints` | 注册一个 `AgentEndpointRegistrationBatch`,并保留为 redo 意图。 |
| 注销 Endpoint | `deregisterAgentEndpoints` | 注销该 SDK Publisher 拥有的一个 `AgentEndpointDeregistrationBatch`。 |
这些公开方法不接受 `namespaceId`。Proxy 复制调用方的 Request 或 Batch,把 SDK
namespace 注入传输对象,并且不修改调用方对象。如果共享输入模型已经携带与 SDK namespace
不同的非空值,Proxy 在本地拒绝。目标 Watch、Cache 和 Redo 行为遵循
[客户端本地缓存与 Redo 规范](../client/client-local-cache-redo-spec.md)和
[运行时推送与重连规范](../client/runtime-push-reconnect-spec.md)。
继承的 `A2aService` 继续作为兼容 Facade。新的 Agent 应用使用
`AgentDiscoveryService`;现有 AgentCard 调用继续通过 A2A 兼容 Adapter 工作。
资源语义由 [AI Registry 规范](../ai/ai-registry-spec.md)、
[Agent API 规范](../ai/agent-api-spec.md)、[RAD 协议规范](../ai/rad-protocol-spec.md)
以及各 AI 资源类型规范定义。当前已经实现的兼容方法包括:
| 能力 | 方法 | 契约 |
| --- | --- | --- |
@@ -256,6 +293,11 @@ Maintainer SDK 中暴露存储 ID 选择器的方法,例如批量删除中的
- `agentSpec()`AgentSpec 管理;
- `pipeline()`Pipeline 管理。
目标 Agent 管理能力新增 `agent()`,返回 `AgentMaintainerService`。这是目标契约,
在新的 Agent Admin API 可用前不得描述为当前已经实现。
`AgentMaintainerService` 与该 Admin HTTP API 一一映射;实例不绑定 namespace,
每个方法都显式携带 `namespaceId``a2a()` 在兼容窗口内继续保留。
运行时 AI 注册和订阅可以继续保留在 `AiService`;大范围 AI 资源管理属于
`AiMaintainerService`
+25 -6
View File
@@ -39,8 +39,8 @@ Client SDK 面向应用运行时访问,应只暴露运行时应用通常需要
- 读取已知配置项,并订阅这些配置的变更;
- 注册和注销当前应用实例;
- 查询和订阅应用已知依赖的服务;
- 注册、解析和订阅运行时 AI 资源,例如 MCP endpoint、A2A agent endpoint
Prompt、Skill 和 AgentSpec
- 注册、发现和订阅运行时 AI 资源,包括可调用 Agent Endpoint,并继续保留历史
MCP、A2A、Prompt、Skill 和 AgentSpec 兼容面
- 在语言 SDK 支持时,提供[分布式锁](../lock/lock-spec.md)等可选运行时原语;
- 按[客户端运行时规范](../client/README.md)管理自身生命周期、本地缓存、监听器和连接。
@@ -63,13 +63,32 @@ Maintainer SDK 面向管理接入,可以暴露 Client SDK 有意不包含的
- 命名空间、集群、服务端状态、readiness/liveness、日志级别等维护能力;
- 配置的列表、搜索、发布、删除、历史、beta、dump 和元数据管理;
- 服务、实例、集群元数据、订阅者、客户端、健康检查等注册中心维护能力;
- MCP、A2A、Prompt、Skill、AgentSpec 和 Pipeline 等 AI 资源管理;
- Agent、MCP、A2A、Prompt、Skill、AgentSpec 和 Pipeline 等 AI 资源管理;
- 对大规模管理数据提供分页和过滤能力。
Maintainer SDK 应被视为 Nacos Admin API 能力面的类型化门面。只对管理、UI、
网关或运维工具有意义的能力,应归入 Maintainer SDK,而不是 Client SDK。
## 4. 安全规则
## 4. Agent 与 RAD 目标契约
本节定义 Agent 管理和 [Remote Agent DiscoveryRAD](../ai/rad-protocol-spec.md)
的目标 SDK 契约,不表示任一 SDK 当前已经实现这些能力。在
[Agent API 规范](../ai/agent-api-spec.md)定义的 Agent/RAD 能力完成实现和协商前,
现有 A2A SDK 接口仍是生效的兼容契约。
目标 Client SDK 必须:
- 每个 SDK 实例绑定一个 namespace,公开的 Agent 发现、Watch、注册和注销方法
不传 namespace
- 提供 Agent Search、带或不带 Filter 的 Discover、Watch 与取消 Watch,以及运行时
Endpoint Register 和 Deregister
- 在不修改调用方对象的前提下,把绑定的 namespace 注入传输请求;
- 按客户端恢复规范在 reconnect 后恢复 Watch 和 Endpoint 发布意图。
目标 Maintainer SDK 不绑定 namespace;每个 Agent 管理调用都必须显式标识 namespace。
它提供新的 Agent 管理 Facade,并在 A2A 兼容窗口内继续保留 A2A 管理 Facade。
## 5. 安全规则
SDK 能力设计必须遵循最小权限原则:
@@ -80,7 +99,7 @@ SDK 能力设计必须遵循最小权限原则:
- 当 API 可以列举或导出大量配置、服务、客户端或元数据时,SDK 文档应明确说明
可能的数据泄露风险。
## 5. 传输和 API 对齐
## 6. 传输和 API 对齐
SDK 契约是语义契约,而不是传输契约:
@@ -96,7 +115,7 @@ SDK 契约是语义契约,而不是传输契约:
- SDK 错误应将 Nacos 错误码和校验失败映射为符合语言习惯的异常或结果类型,
同时保留服务端语义。
## 6. 多语言 SDK 对齐
## 7. 多语言 SDK 对齐
Java 目前是定义共享 SDK 语义的基准实现。其他语言 SDK 应对齐相同的能力分类: