diff --git a/.env.example b/.env.example
index 06b1ab3c..32f26319 100644
--- a/.env.example
+++ b/.env.example
@@ -31,7 +31,6 @@ A2AT_PROMPT_RESOURCE_LOCAL_ROOT_DIR=
# Prompt compliance
A2AT_PROMPT_COMPLIANCE_ENABLED=false
-A2AT_PROMPT_COMPLIANCE_GUARDRAIL_PROVIDER=noop
# LLM runtime
# Supported providers: openai (OpenAI-compatible APIs like DeepSeek, Azure OpenAI, etc.)
@@ -55,12 +54,3 @@ A2AT_NEGOTIATION_STATE_STORE_TYPE=in_memory
A2AT_CRED_KEY=your-32-byte-hex-key-here
# Keep true for production HTTPS. Set false only for local/self-signed integration testing.
-# For LoadPsop HTTPS, false skips both server trust and hostname checks; no local CA file is needed.
-# The server still needs a TLS certificate. This does not bypass mTLS or change forwarding-platform TLS.
-A2A_SSL_VERIFY=false
-# Comma-separated classpath:, file: URI, or absolute file locations. Leave empty for bundled cards.
-# For a live OMC, point the city AgentCards at files exported by the registry center.
-A2A_AGENT_CARD_LOCATIONS=
-# Embedded startup validates both effective AgentCards against local network interfaces.
-# Set false for an external OMC, including one running on the same host. Empty defaults to true.
-A2A_EMBEDDED_OMC_ENABLED=
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a7795a2..f038705a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,8 +12,8 @@
- Separate authorization/notification lifecycle from workflow outcomes; never synthesize a subscription ACK.
- Observe serialized HTTP/JSON-RPC, real gRPC metadata/protobuf and dev vendor SDK traffic with mandatory redaction and
bounded SSE.
-- Add local missing-port SpringSpnDemo negotiation tests for direct and dev Order; real LLM/platform/OMC validation
- remains separate.
+- Add missing-input negotiation and independent transport-path regression tests; live model and production endpoint
+ validation remains separate.
- Refresh bilingual callback, architecture and integration contracts. No older SDK compatibility layer.
### Release hardening
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3cc2e404..c6f9daed 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,7 +22,7 @@ mvn -B clean verify
workflow-engine-sdk-java/
|-- workflow-engine/ SDK engine module
| +-- src/main/java/dev/openan/workflow/engine/
-| |-- client/ A2A transport, auth, extensions (package-private internals)
+| |-- client/ A2A transport, authentication, extensions, runtime APIs
| |-- control/ User-facing: ControlPoint, EventCallback, EventType
| |-- core/ Internal: WorkflowExecutor, ContextBuilder (package-private)
| |-- model/ Data models
@@ -137,8 +137,9 @@ Every Java file must start with the Apache 2.0 license header:
- Run the complete `mvn -B clean verify` reactor; sample failures block release.
- Documentation code snippets are compiled by DocumentedCallbackExampleTest; HostQuickStartTest executes the minimal host flow.
-- Check default single-city negotiation, final serialized protocol logs, independent extension lifecycle, cancellation and secret anonymization.
+- Check required-input negotiation, final serialized protocol logs, independent extension lifecycle, cancellation and secret anonymization.
- Verify published POM coordinates, sources/Javadoc artifacts and the absence of private configuration/reference documents.
-- Record the tested commit and Surefire reports. Offline providers and local OMC simulators are not live acceptance.
-- Live model/OMC validation requires authorized endpoints and a separate acceptance record. Never commit customer integration notes.
+- Record the tested commit and Surefire reports. Offline providers and local protocol simulators are not live acceptance.
+- Live model and dispatched-agent validation requires authorized endpoints and a separate acceptance record. Never
+ commit customer integration notes.
- Do not publish, tag, or push as a side effect of running tests.
diff --git a/README.md b/README.md
index 6ee6339f..732e8392 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[](https://adoptium.net/)
[](https://maven.apache.org/)
-A standalone SDK for executing multi-agent workflows over the [A2A protocol](https://a2aproject.github.io/a2a-java/)
+A Java SDK embedded in a host agent for executing multi-agent workflows over the [A2A protocol](https://a2aproject.github.io/a2a-java/)
with [A2A-T](https://projects.tmforum.org/a2aproject/telecommunication/extensions/) telecom extensions.
The engine handles workflow scheduling, A2A envelopes, transport, task waiting, authentication and TLS. Host callbacks
@@ -13,7 +13,7 @@ return final message content and own any A2A-T generation, schema, validation or
## Features
- **A2A-T Extension Support**: Task-T (structured task prompts), Negotiation-T (stateless auto negotiation loop),
- Authorization-T (independent whitelist operation), Notification-T (independent long-lived SSE subscription)
+ Authorization-T (independent authorization operation), Notification-T (independent long-lived SSE subscription)
- **Content-neutral callbacks**: final MessageContent, complete ReceivedMessage, local multi-output TaskResult and
explicit NegotiationReply.Send/Stop
- **Minimal A2A-T dependency**: a2a-t-core only in the engine; content generation and template queries use the host's
@@ -25,15 +25,14 @@ return final message content and own any A2A-T generation, schema, validation or
- **Protocol Logging**: actual HTTP/JSON-RPC boundaries and gRPC metadata/protobuf views;
bodies default on at DEBUG with mandatory secret-field redaction
-The SPN sample treats the protocol document as an input, not as executable truth. The pinned A2A-T SDK templates, slot
-schemas, canonical URIs, and validation results are authoritative. Protocol generation and validation fail closed; raw
-text is never sent under an A2A-T URI as a fallback.
+Protocol documents are integration inputs, not executable truth. The pinned A2A-T SDK templates, slot schemas,
+canonical URIs, and validation results are authoritative. Protocol generation and validation fail closed; raw text is
+never sent under an A2A-T URI as a fallback.
-In `SpringSpnDemo`, WAIMO sends a Task-T complaint to the integrator. The integrator loads the PSOP, dispatches two
-city-specific OMC diagnoses in parallel, joins both branches exactly once, and returns the real merged result over
-direct HTTP+JSON transport. Task-T, Authorization-T, and Notification-T each use an independent
-transport/runtime/context. Authorization and Notification are independently triggered integrator operations rather than
-DAG nodes; Notification keeps an explicit long-lived subscription until the recovery result, cancellation, or shutdown.
+The SDK is embedded in a host agent. It schedules workflow tasks to dispatched agents and returns complete results to
+the host callbacks. Task-T, Authorization-T, and Notification-T use independent transport/runtime/context instances.
+Authorization and Notification are independently triggered host-agent operations rather than DAG nodes; a notification
+subscription remains open until the host-defined terminal event, explicit cancellation, or shutdown.
## Quick Start
@@ -41,9 +40,10 @@ DAG nodes; Notification keeps an explicit long-lived subscription until the reco
A2A-T SDK `1.1.0` is published to Maven Central. Maven resolves it automatically; no SDK source checkout or local SDK
build is required. The engine depends only on
-`a2a-t-core`; hosts using content generation explicitly add `a2a-t-client:1.1.0`
-(and `a2a-t-server:1.1.0` for receiving-side validation). See
-the [A2A-T SDK dependency guide](docs/zh/A2AT-SDK-DEPENDENCY.md) for IDEA setup and upgrade checks.
+`a2a-t-core`; host agents using content generation explicitly add `a2a-t-client:1.1.0`. A dispatched-agent service that
+validates received extension content adds `a2a-t-server:1.1.0`. See the bilingual
+[Developer Guide](docs/en/DEVELOPER_GUIDE.md) / [开发者指南](docs/zh/DEVELOPER_GUIDE.md) for dependency and
+upgrade guidance.
```xml
@@ -80,7 +80,7 @@ HostQuickStart.main(new String[] {
```
This minimal example sends plain A2A content. For Task-T, Negotiation-T, Authorization-T and Notification-T content
-generation/validation in host business callbacks, follow SpringSpnDemo and the business callback guide.
+generation/validation in host-agent business callbacks, follow the business callback guide.
Final content callbacks, complete dependency inputs and negotiation
Send/Stop: [English](docs/en/BUSINESS_CALLBACKS.md) / [中文](docs/zh/BUSINESS_CALLBACKS.md).
@@ -127,7 +127,7 @@ graph TD
| Package | Key Classes | Description |
|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
-| `client` | `WorkflowEngineClient`, `DefaultWorkflowEngineClient`, `ExtensionSender`, `A2ATransport`, `AuthProvider`, `AgentAuthManager`, `WorkflowEngineClientConfig`, `CredentialCrypto`, `AgentCardJacksonModule` | A2A transport, auth, extensions (package-private internals) |
+| `client` | `WorkflowEngineClient`, `DefaultWorkflowEngineClient`, `ExtensionSender`, `A2ATransport`, `AuthProvider`, `WorkflowEngineClientConfig`, `CredentialCrypto`, `AgentCardJacksonModule` | Public transport, authentication, extension, and runtime APIs |
| `control` | `ControlPoint`, `DefaultControlPoint`, `EventCallback`, `EventType`, `NegotiationStrategy` | User-facing decision interfaces |
| `core` | `WorkflowExecutor`, `ContextBuilder` | DAG traversal, context assembly |
| `model` | `Workflow`, `WorkflowStep`, `Task`, `TaskRequest`, `MessageContent`, `TaskResult`, `NegotiationRequest`, `NegotiationReply`, `ExecutionResult` | Data models |
@@ -143,6 +143,7 @@ graph TD
- [Integration Guide](docs/en/INTEGRATION_GUIDE.md) - Setup, configuration, secondary development
- [API Reference](docs/en/API_REFERENCE.md) - Public interface and class documentation
+- [Business Callback Contract](docs/en/BUSINESS_CALLBACKS.md) - Host-agent callback inputs, outputs, and ownership
- [Design Document](docs/en/DESIGN.md) - Architecture, module structure, design decisions
- [Developer Guide](docs/en/DEVELOPER_GUIDE.md) - Internal architecture, contribution, debugging
@@ -150,9 +151,9 @@ graph TD
- [集成指南](docs/zh/INTEGRATION_GUIDE.md) - 安装、配置、二次开发
- [API 参考](docs/zh/API_REFERENCE.md) - 公共接口和类文档
+- [业务回调集成契约](docs/zh/BUSINESS_CALLBACKS.md) - 宿主智能体回调的输入、输出与职责边界
- [架构设计](docs/zh/DESIGN.md) - 架构、模块结构、设计决策
- [开发者指南](docs/zh/DEVELOPER_GUIDE.md) - 内部架构、贡献、调试
-- [集成方集成指南](docs/zh/集成方场景接入指南.md) - 集成方 Spring Boot 集成完整指南
## Modules
@@ -166,22 +167,9 @@ graph TD
[Apache License 2.0](LICENSE)
-### Inspect negotiation in the local Demo
+## Verification
-Running the local SpringSpnDemo without VM options now negotiates missing City1 input; City2 diagnoses directly. This is
-a local sample scenario, not an engine default. To use complete inputs in both cities, add this IDEA **VM option**:
-
-```text
--Da2at.samples.negotiation=false
-```
-
-By default only City1 loses its Task-T task object. Add `-Da2at.samples.negotiation.city=city2` or `both`
-to exercise City2 or both cities. The host retains city-scoped authoritative input and the complaint context is
-preserved. Expect DEMO_NEGOTIATION → INPUT_REQUIRED/PROPOSE → onNegotiation → ACCEPT → both diagnoses → one aggregate.
-External-OMC mode defaults to no injection and rejects an explicit true switch. The Demo passes its setting into the
-current Spring application context without modifying JVM-wide properties. Protocol observations are in the console and
-`logs/spn-demo.log` relative to the run directory.
-
-Run `SpringSpnDemoE2ETest` for direct transport. It tests the no-VM-option
-single-city default, explicit disable/enable and both-city negotiation with current SDK resources and an offline LLM
-provider. This is local protocol E2E evidence, not real model/platform/OMC validation.
+Run `mvn -B clean verify` before release. The reactor verifies the engine, Spring Boot starter, callback examples,
+protocol transport, negotiation, independent extension lifecycles, cancellation, error propagation, and credential
+redaction. These automated tests use controlled fixtures; production endpoints, identity services, and live model
+providers require a separate acceptance record.
diff --git a/docs/diagrams/workflow-engine-surrounding-systems.mmd b/docs/diagrams/workflow-engine-surrounding-systems.mmd
index c3906c96..33e5d8bc 100644
--- a/docs/diagrams/workflow-engine-surrounding-systems.mmd
+++ b/docs/diagrams/workflow-engine-surrounding-systems.mmd
@@ -14,50 +14,50 @@ flowchart TB
classDef local fill: #F8FAFC, stroke: #64748B, color: #0F172A, stroke-dasharray: 5 3
classDef agent fill: #F5F3FF, stroke: #7C3AED, color: #2E1065, stroke-width: 1.5px
- subgraph CONTROL["周边控制面与上游系统"]
+ subgraph CONTROL["External systems"]
direction LR
- WAIMO["WAIMO
上游任务发起方"]:::external
- REG["注册中心 / Registry Center
AgentCard 发布与查询"]:::external
- ORCH["编排中心 / Orchestration Center
工作流检索与 PSOP 加载"]:::external
+ CALLER["External A2A requester
Task request and final response"]:::external
+ REG["Registry center
AgentCard publication and discovery"]:::external
+ ORCH["Orchestration center
Workflow search and loading"]:::external
end
- subgraph HOST["宿主智能体:集成方 / Integrator Agent"]
+ subgraph HOST["Host agent"]
direction TB
- ENTRY["A2A 服务端入口
接收 Task-T,解析诊断意图"]:::host
- ADAPTER["宿主集成层
准备 AgentCard、Workflow、配置和业务上下文"]:::host
+ ENTRY["A2A server entry
Receive and validate inbound tasks"]:::host
+ ADAPTER["Host integration layer
Prepare AgentCards, Workflow, configuration and context"]:::host
- subgraph ENGINE["嵌入式 Workflow Execution Engine SDK"]
+ subgraph ENGINE["Embedded Workflow Execution Engine SDK"]
direction TB
- LOAD["发现辅助 API
RegistryClient / LoadPsop"]:::sdk
- RUN["工作流协议调度
ExecutePsop → WorkflowExecutor
Task-T + Negotiation-T"]:::sdk
- CALLBACK["宿主回调接口
ControlPoint / EventCallback / onFinish"]:::sdk
- EXT["流程外协议操作
ExtensionSender
Authorization-T / Notification-T"]:::sdk
+ LOAD["Discovery helpers
RegistryClient / LoadPsop"]:::sdk
+ RUN["Workflow protocol scheduling
ExecutePsop → WorkflowExecutor
Task-T + Negotiation-T"]:::sdk
+ CALLBACK["Host callbacks
ControlPoint / EventCallback / onFinish"]:::sdk
+ EXT["Independent protocol operations
ExtensionSender
Authorization-T / Notification-T"]:::sdk
end
- BIZ["集成方业务实现
业务接管、路由、汇总、持久化、通知处理"]:::host
+ BIZ["Host business implementation
Content, routing, aggregation, persistence and notification handling"]:::host
end
- subgraph SAMPLE["Demo 本地替代路径(非生产数据源)"]
+ subgraph SAMPLE["Optional local fixtures (non-production)"]
direction LR
- LOCALCARD["本地 AgentCard JSON
WorkbenchAgentCatalog"]:::local
- LOCALPSOP["本地 PSOP fallback
仅用于离线演示"]:::local
+ LOCALCARD["Local AgentCard JSON"]:::local
+ LOCALPSOP["Local Workflow fixture"]:::local
end
- subgraph DOWNSTREAM["下游业务智能体"]
- OMCS["地市 OMC 智能体群
地市 1 OMC | 地市 2 OMC"]:::agent
+ subgraph DOWNSTREAM["Dispatched agents"]
+ AGENTS["One or more dispatched agents"]:::agent
end
- WAIMO <-->|" Task-T 诊断任务 / 汇总 artifact "| ENTRY
- ENTRY -->|" 运行意图 "| ADAPTER
- REG -->|" 生产:查询 AgentCard "| LOAD
- ORCH -->|" search / load PSOP "| LOAD
- LOCALCARD -.->|" Demo 替代注册中心 "| ADAPTER
- LOCALPSOP -.->|" Demo 检索失败时 "| ADAPTER
- LOAD -->|" 发现结果交给宿主 "| ADAPTER
+ CALLER <-->|"A2A request / final result"| ENTRY
+ ENTRY -->|"Validated intent and input"| ADAPTER
+ REG -->|"Discover AgentCards"| LOAD
+ ORCH -->|"Search / load Workflow"| LOAD
+ LOCALCARD -.->|"Development fixture"| ADAPTER
+ LOCALPSOP -.->|"Development fixture"| ADAPTER
+ LOAD -->|"Discovery results"| ADAPTER
ADAPTER -->|" Workflow + AgentCards "| RUN
- RUN <-->|" 业务决策与执行结果 "| CALLBACK
- CALLBACK <-->|" 回调 "| BIZ
- ADAPTER -->|" 独立业务时机 "| EXT
- RUN <-->|" 向两地市并行 Task-T
必要时 Negotiation-T / 返回诊断结果 "| OMCS
- EXT <-->|" Authorization-T 一次性授权
Notification-T 长连接订阅与结果 SSE "| OMCS
- BIZ -->|" 汇总结果 "| ENTRY
+ RUN <-->|"Business decisions and results"| CALLBACK
+ CALLBACK <-->|"Callbacks"| BIZ
+ ADAPTER -->|"Independent business timing"| EXT
+ RUN <-->|"Task-T; Negotiation-T when required"| AGENTS
+ EXT <-->|"Authorization-T / Notification-T"| AGENTS
+ BIZ -->|"Final business result"| ENTRY
diff --git a/docs/en/API_REFERENCE.md b/docs/en/API_REFERENCE.md
index aca2b997..f20dd20c 100644
--- a/docs/en/API_REFERENCE.md
+++ b/docs/en/API_REFERENCE.md
@@ -10,6 +10,7 @@
| `dev.openan.workflow.engine.model` | Data models (Workflow, Task, results) |
| `dev.openan.workflow.engine.registry` | PSOP loading and AgentCard registry |
| `dev.openan.workflow.engine.runner` | Entry point for workflow execution |
+| `dev.openan.workflow.engine.spring` | Spring Boot server auto-configuration (starter) |
---
@@ -24,7 +25,7 @@ Entry point for executing a PSOP workflow. Uses the Builder pattern.
| Method | Type | Default | Description |
|------------------------------------------|----------|-------------|----------------------------------------------|
| `psop(Workflow)` | required | - | PSOP workflow definition |
-| `agentCards(List)` | required | `List.of()` | Agent cards for all agents in the workflow |
+| `agentCards(List)` | optional | `List.of()` | Cards for dispatched agents; required for remote steps unless a configured `engineClient` supplies transport |
| `controlPoint(ControlPoint)` | required | - | User decision implementation |
| `engineClient(WorkflowEngineClient)` | optional | null | Pre-configured client (null = auto-create) |
| `runtimeIntent(String)` | optional | `""` | Natural-language intent for context assembly |
@@ -42,7 +43,7 @@ ExecutionResult result = ExecutePsop.builder()
.psop(workflow)
.agentCards(cards)
.controlPoint(cp)
- .runtimeIntent("diagnose fault")
+ .runtimeIntent("analyze request")
.sslVerify(false)
.execute()
.get(10, TimeUnit.MINUTES);
@@ -84,7 +85,7 @@ Unchanged waiting state is observed with getTask.
`maxNegotiationExchanges` (default 3) bounds local interactions, independently of the SDK context's maxRounds. Timeout,
exhausted budget or a missing handler fails locally; no implicit Accept or synthesized Abort. Accept/Reject ACKs in
SUBMITTED/WORKING remain pending and are observed without resending the command. A business-sent Abort is never
-diagnosis success, even if the remote acknowledges it with COMPLETED.
+task success, even if the dispatched agent acknowledges it with COMPLETED.
### ExtensionSender
@@ -96,8 +97,8 @@ NotificationSubscription openNotification(String agentName, MessageContent conte
Authorization and notification accept host-generated final content. Use separate transport/runtime/context instances;
their outcomes do not gate the workflow. openNotification registers a handle before I/O, and passes it plus
-ReceivedMessage directly to the listener. acknowledgement () is the real ACK; timeout fails. close () requests closure;
-completion () observes actual stream termination.
+ReceivedMessage directly to the listener. acknowledgement() is the real ACK; timeout fails. close() requests closure;
+completion() observes actual stream termination.
### WorkflowEngineClientConfig
@@ -153,9 +154,9 @@ the same header name throw `SecurityException` instead of silently overwriting e
### A2atMessages
-Submit custom extensions via MessageContent (parts, metadata, extensions); no engine content-handler registration.
-A2atMessages.from (MetadataContent, List>) preserves SDK metadata and activates the corresponding URI; contextOf
-(ReceivedMessage) or contextOf (Map) reads/checks canonical negotiation context. This adapter uses
+Submit custom extensions via MessageContent(parts, metadata, extensions); no engine content-handler registration.
+A2atMessages.from(MetadataContent, List>) preserves SDK metadata and activates the corresponding URI; contextOf
+(ReceivedMessage) or contextOf(Map) reads/checks canonical negotiation context. This adapter uses
a2a-t-core only, never content generation or semantic validation.
### A2AJavaClientRuntime
@@ -231,6 +232,26 @@ custom normalization:
Map normalized = AgentCardNormalizer.normalize(rawMap);
```
+### Supporting public client types
+
+The following public types support advanced integrations. Prefer the higher-level interfaces above unless a custom
+runtime, diagnostic adapter, or explicit lifecycle requires them.
+
+| Type | Purpose |
+|-------------------------------|---------|
+| `A2ATExtension` | Canonical extension names and URIs |
+| `A2ATransport` | Low-level transport, authentication, response assembly, and subscription lifecycle |
+| `DefaultWorkflowEngineClient` | Default `WorkflowEngineClient` implementation |
+| `DefaultExtensionSender` | Default `ExtensionSender` implementation backed by an `A2ATransport` |
+| `DefaultA2AJavaClientRuntime` | Default A2A Java SDK runtime for HTTP/JSON-RPC/gRPC |
+| `CredentialCrypto` | AES-GCM credential encryption utility and command-line entry point |
+| `EnvFileLoader` | Explicit `.env` parser for host-owned configuration |
+| `SslContextFactory` | TLS context construction used by transport and discovery helpers |
+| `ProtocolResponses` | A2A event/result assembly helpers |
+| `ClientEventMapper` | Stable event projection for callbacks and diagnostics |
+| `WireLog` | Correlation context and protocol-observation facade |
+| `RemoteProblemException` | Structured remote problem response (`status`, `title`, `detail`, `type`, `timestamp`) |
+
---
## dev.openan.workflow.engine.control
@@ -249,6 +270,10 @@ interface ControlPoint {
onTask returns final parts/metadata/extensions; the engine sends them without generating or rewriting content.
onSelfTask returns local TaskResult, onRoute selects an allowed candidate, and onNegotiation returns Send or Stop.
Unimplemented callbacks fail explicitly. No echo-success, first-branch choice or automatic consent.
+
+`DefaultControlPoint` preserves those fail-fast defaults and can delegate `onNegotiation` to an injected
+`NegotiationStrategy`. Implement `NegotiationStrategy.resolve(NegotiationRequest)` when negotiation policy is the only
+custom decision; implement or build a full `ControlPoint` when task, self-task, or routing behavior also changes.
See [Business callback contract](BUSINESS_CALLBACKS.md) for fields and working examples.
### EventCallback
@@ -373,12 +398,6 @@ WorkflowExecutor executor = new WorkflowExecutor(
ExecutionResult result = executor.run().join();
```
-### ContextBuilder
-
-Package-private helper that selects upstream step results and creates a `WorkflowInput` according to `contextFrom`
-(omitted = direct predecessors, `[]` = no upstream input, `"*"` = all ancestors, explicit names = selective
-inheritance). It does not render prompts and is not part of the public API surface.
-
---
## dev.openan.workflow.engine.model
@@ -411,7 +430,7 @@ Static factory: `Workflow.fromMap(Map)` parses from orchestratio
|---------------|------------------------------------------------------------------------------------------------------------------------|
| `ALL_SUCCESS` | All subtasks must succeed |
| `ANY_SUCCESS` | Any subtask success is sufficient |
-| `SELF_LOOP` | The workflow agent handles the task locally via `onSelfTask`; no A2A-T message is sent. Success follows `ALL_SUCCESS`. |
+| `SELF_LOOP` | The host agent handles the task locally via `onSelfTask`; no A2A-T message is sent. Success follows `ALL_SUCCESS`. |
### TaskStatus
@@ -441,7 +460,7 @@ Task lifecycle status, used in `TASK_STATUS_CHANGED` events for cross-SDK consis
### TaskRequest / BusinessInput
-TaskRequest uses getXxx () accessors:
+TaskRequest uses getXxx() accessors:
| Field | Meaning |
|------------------------------|-----------------------------------------------------------------------------------|
@@ -451,8 +470,10 @@ TaskRequest uses getXxx () accessors:
| input | BusinessInput: exactly one of text or arbitrary JSON-serializable data; no schema |
| workflowInput | WorkflowInput(runtimeIntent, upstreamResults), separate from current input |
-BusinessInput.text (value) / BusinessInput.data (value) create input snapshots. WorkflowInput, UpstreamStepResult,
-ReceivedMessage and NegotiationRequest use record field () accessors.
+BusinessInput.text(value) / BusinessInput.data(value) create input snapshots. WorkflowInput, UpstreamStepResult,
+ReceivedMessage and NegotiationRequest use record field() accessors.
+`BusinessValues.snapshot`, `map`, and `list` expose the same defensive JSON snapshot behavior for host-owned values;
+nested arrays remain individual values and returned collections are unmodifiable.
| contextFrom | Upstream selection |
|-------------------------|----------------------------------------------|
@@ -477,7 +498,7 @@ record MessageContent(List> parts, Map metadata, Set taskMetadata, List artifacts) {}
```
-MessageContent.text (text), MessageContent.parts (parts), or the canonical constructor create snapshots. TextPart,
+MessageContent.text(text), MessageContent.parts(parts), or the canonical constructor create snapshots. TextPart,
DataPart and FilePart preserve order and part metadata. File references are not fetched. MessageContent has no role,
target, messageId, taskId, contextId or auth headers. A business metadata key named contextId does not override the A2A
envelope.
@@ -490,7 +511,7 @@ adjacent-part concatenation or nested-array flattening. Failed task status messa
partial artifacts are retained. Streaming append/replace is assembled per artifact; terminal snapshots do not duplicate
earlier chunks.
-TaskResult.success (List
```
-The engine pulls in A2A protocol transports and a2a-t-core only. Hosts generating A2A-T content explicitly add
-a2a-t-client; receiving hosts may also add a2a-t-server.
+The engine pulls in A2A protocol transports and a2a-t-core only. Host agents generating A2A-T content explicitly add
+a2a-t-client; dispatched-agent services that validate received content also add a2a-t-server.
## 2. Core Concepts
@@ -63,7 +63,7 @@ ExecutionResult result = ExecutePsop.builder()
.psop(workflow)
.agentCards(agentCards)
.controlPoint(new MyControlPoint())
- .runtimeIntent("Diagnose SPN fault")
+ .runtimeIntent("Analyze a service anomaly")
.lang("zh")
.sslVerify(false)
.credentialsConfigPath("agent_credentials.json")
@@ -119,7 +119,7 @@ WorkflowExecutor executor = new WorkflowExecutor(
new MyControlPoint(),
client,
new EventCallback(),
- "Diagnose fault",
+ "Analyze request",
"zh"
);
ExecutionResult result = executor.run().join();
@@ -140,13 +140,13 @@ Unchanged waiting state is observed with getTask.
`maxNegotiationExchanges` (default 3) bounds local interactions, independently of the SDK context's maxRounds. Timeout,
exhausted budget or a missing handler fails locally; no implicit Accept or synthesized Abort. Accept/Reject ACKs in
SUBMITTED/WORKING remain pending and are observed without resending the command. A business-sent Abort is never
-diagnosis success, even if the remote acknowledges it with COMPLETED.
+task success, even if the dispatched agent acknowledges it with COMPLETED.
### 6.2 Workflow Model Fields
| Field | Where | Meaning |
|-----------------------|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `steps[].stepType` | `WorkflowStep` | `AllSuccess` (default): every subtask must succeed; `AnySuccess`: any subtask success suffices; `SelfLoop`: the workflow agent handles the step locally via `onSelfTask` (no A2A-T message to the named agent). |
+| `steps[].stepType` | `WorkflowStep` | `AllSuccess` (default): every subtask must succeed; `AnySuccess`: any subtask success suffices; `SelfLoop`: the host agent handles the step locally via `onSelfTask` (no A2A-T message to the named agent). |
| `steps[].subtasks[]` | `Task` | Each has `agent`, `skill`, `description`. One `onTask` (or `onSelfTask` for SelfLoop) call per subtask. |
| `steps[].next[]` | `List` | Branch targets. `step` = next step name; `condition` = rule text. |
| `steps[].layer` | `WorkflowStep` | Orchestration-level hint; actual readiness is derived from DAG predecessors. |
@@ -175,7 +175,7 @@ seconds, and attaches the auth header to outbound requests.
```json
{
- "SPN Domain Agent": {
+ "Dispatched Agent": {
"bearerAuth": {
"login_url": "https://127.0.0.1:8080/auth/login",
"method": "POST",
@@ -233,10 +233,10 @@ Set `sslVerify=false` only for dev with self-signed certs.
## 9. A2A-T Environment (.env)
-The engine does not read A2A-T .env files or create LLM clients. If host callbacks use A2A-T, initialize
-A2ATClient/A2ATServer with a host-owned environment file containing provider/model/key/base URL and A2AT_LANGUAGE. The
-sample's a2atEnvPath setting is only a host/demo setting, not an engine builder option. Do not put OMC credential
-decryption ownership in LLM configuration: pass the secret explicitly through WorkflowEngineClientConfig.builder ()
+The engine does not read A2A-T .env files or create LLM clients. If host-agent callbacks use A2A-T, initialize
+A2ATClient/A2ATServer with a host-owned environment file containing provider/model/key/base URL and A2AT_LANGUAGE. A
+sample-specific a2atEnvPath is not an engine builder option. Keep dispatched-agent credential decryption separate from
+LLM configuration: pass the secret explicitly through WorkflowEngineClientConfig.builder()
.credentialEncryptionKey (key) when using encrypted built-in credentials, then pass that configured engineClient to
ExecutePsop. Custom AuthProvider owns its own token/configuration. Tests use the current SDK SPI with an offline
provider, not template overrides or production fallbacks.
@@ -273,8 +273,8 @@ public Flux execute(@PathVariable String psopId) {
### Cancellation
-ExecutePsop.builder ().execute () returns a CompletableFuture. Cancellation prevents late callback results from being
-sent and requests conversation cleanup. Hosts must separately cancel their own LLM/business work. A remote task already
+ExecutePsop.builder().execute() returns a CompletableFuture. Cancellation prevents late callback results from being
+ sent and requests conversation cleanup. Host agents must separately cancel their own LLM/business work. A dispatched task already
submitted may require an explicit cancelTask operation; local cancellation is not protocol Abort.
## 11. Checklist
@@ -291,13 +291,12 @@ submitted may require an explicit cancelTask operation; local cancellation is no
### Pretty protocol log display
Protocol logs default to pretty display: one header value per line and indented JSON bodies. SSE keeps event controls
-(id/event/comments); JSON appears between `=== SSE data (JSON display; not wire text) ===`
+(id/event/comments); JSON appears between `=== SSE data(JSON display; not wire text) ===`
and `=== End SSE data ===`, without repeating `data:` on every JSON line. Event boundaries remain separate. Set the
environment variable or JVM property `WORKFLOW_ENGINE_PROTOCOL_PRETTY=false` to retain redacted raw body text.
Formatting changes presentation only (JSON whitespace and SSE display labels), never transmitted bytes, metadata, number
-tokens or extension headers. The raw observer content remains unchanged. SSE pretty output is a display, not a
-replayable packet capture. Escaped newlines inside JSON strings stay escaped; invalid/incomplete or non-JSON content
-stays raw. Redaction and capacity limits still apply. The demo logs `NEGOTIATION_DEMO enabled=..., city=...` at startup.
-Locally City1 negotiates by default while City2 diagnoses directly;
-`-Da2at.samples.negotiation=false` disables the missing-input scenario. Look for `A2A-Extensions: .../Negotiation-T/v1`,
-the matching metadata key and `negotiationContext`; there is no dedicated Negotiation-T HTTP header.
+ tokens or extension headers. The raw observer content remains unchanged. SSE pretty output is a display, not a
+ replayable packet capture. Escaped newlines inside JSON strings stay escaped; invalid/incomplete or non-JSON content
+ stays raw. Redaction and capacity limits still apply. To verify negotiation traffic, look for
+ `A2A-Extensions: .../Negotiation-T/v1`, the matching metadata key and `negotiationContext`; there is no dedicated
+ Negotiation-T HTTP header.
diff --git a/docs/en/INTEGRATION_GUIDE.md b/docs/en/INTEGRATION_GUIDE.md
index 2e4c1f46..f73bc23d 100644
--- a/docs/en/INTEGRATION_GUIDE.md
+++ b/docs/en/INTEGRATION_GUIDE.md
@@ -40,15 +40,15 @@ Nonempty business conditions require an onRoute policy; this minimal workflow us
```java
Workflow workflow = Workflow.builder()
- .name("Fault Diagnosis")
+ .name("Service Analysis")
.steps(List.of(
WorkflowStep.builder()
- .name("diagnose")
+ .name("analyze")
.subtasks(List.of(
Task.builder()
- .agent("SPN Domain Agent")
- .skill("diagnosis")
- .description("Diagnose fault")
+ .agent("Dispatched Agent")
+ .skill("analysis")
+ .description("Analyze the request")
.build()))
.next(List.of(
JumpCondition.builder()
@@ -59,10 +59,10 @@ Workflow workflow = Workflow.builder()
.build(),
WorkflowStep.builder()
.name("merge")
- .stepType(StepType.SELF_LOOP) // self-loop: workbench merges locally, no A2A-T to self
+ .stepType(StepType.SELF_LOOP) // host-agent local step; no A2A-T send
.subtasks(List.of(
Task.builder()
- .agent("Transport Workbench Agent")
+ .agent("Host Agent")
.skill("aggregate")
.description("Merge results")
.build()))
@@ -137,7 +137,7 @@ CompletableFuture execution = ExecutePsop.builder()
.psop(workflow)
.agentCards(cards)
.controlPoint(callbacks)
- .runtimeIntent("SPN cross-city fault diagnosis")
+ .runtimeIntent("Analyze the service request")
.lang("zh")
.credentialsConfigPath("credentials.json")
.sslVerify(true)
@@ -159,10 +159,10 @@ Required: `psop`, `controlPoint`. All other config items have defaults.
### 5.1 .env File
-The engine does not read A2A-T .env files or create LLM clients. If host callbacks use A2A-T, initialize
-A2ATClient/A2ATServer with a host-owned environment file containing provider/model/key/base URL and A2AT_LANGUAGE. The
-sample's a2atEnvPath setting is only a host/demo setting, not an engine builder option. Do not put OMC credential
-decryption ownership in LLM configuration: pass the secret explicitly through WorkflowEngineClientConfig.builder ()
+The engine does not read A2A-T .env files or create LLM clients. If host-agent callbacks use A2A-T, initialize
+A2ATClient/A2ATServer with a host-owned environment file containing provider/model/key/base URL and A2AT_LANGUAGE. A
+sample-specific a2atEnvPath is not an engine builder option. Keep dispatched-agent credential decryption separate from
+LLM configuration: pass the secret explicitly through WorkflowEngineClientConfig.builder()
.credentialEncryptionKey (key) when using encrypted built-in credentials, then pass that configured engineClient to
ExecutePsop. Custom AuthProvider owns its own token/configuration. Tests use the current SDK SPI with an offline
provider, not template overrides or production fallbacks.
@@ -173,15 +173,13 @@ For agents requiring authentication, provide a JSON credentials file:
```json
{
- "SPN Domain Agent": {
+ "Dispatched Agent": {
"bearerAuth": {
- "login_url": "https://127.0.0.1:26335/rest/plat/smapp/v1/oauth/token",
- "method": "PUT",
+ "login_url": "https://agent.example.com/oauth/token",
+ "method": "POST",
"request_fields": {
- "grantType": "password",
- "userName": "admin",
- "value": "enc::",
- "ipaddr": "*"
+ "username": "service-account",
+ "password": "enc::"
},
"token_field": "accessSession",
"token_ttl": 3600
@@ -322,7 +320,7 @@ WorkflowEngineClientConfig config = WorkflowEngineClientConfig.builder()
- `AuthProvider` can be the sole authentication source even when `securityRequirements` is non-empty
- If both credentials and `AuthProvider` are configured, their headers are generated independently and merged; different
values for the same header fail fast
-- On auth failure (e.g. token retrieval throws), the exception propagates to `send()` and the request is blocked
+- On auth failure(e.g. token retrieval throws), the exception propagates to `send()` and the request is blocked
## 6. AgentCard Definition
@@ -330,7 +328,7 @@ AgentCards declare extensions via `capabilities.extensions`:
```json
{
- "name": "SPN Domain Agent",
+ "name": "Dispatched Agent",
"capabilities": {
"streaming": true,
"extensions": [
@@ -346,12 +344,12 @@ AgentCards declare extensions via `capabilities.extensions`:
},
{
"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Authorization-T/v1",
- "description": "Authorization whitelist",
+ "description": "Authorization operation",
"required": false
},
{
"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Notification-T/v1",
- "description": "Result notification subscription",
+ "description": "Notification subscription",
"required": false
}
]
@@ -399,7 +397,7 @@ Unchanged waiting state is observed with getTask.
`maxNegotiationExchanges` (default 3) bounds local interactions, independently of the SDK context's maxRounds. Timeout,
exhausted budget or a missing handler fails locally; no implicit Accept or synthesized Abort. Accept/Reject ACKs in
SUBMITTED/WORKING remain pending and are observed without resending the command. A business-sent Abort is never
-diagnosis success, even if the remote acknowledges it with COMPLETED.
+task success, even if the dispatched agent acknowledges it with COMPLETED.
```java
CompletableFuture sendAuthorization(String agentName, MessageContent content);
@@ -407,10 +405,10 @@ NotificationSubscription openNotification(String agentName, MessageContent conte
BiConsumer listener);
```
-The host generates final Authorization-T/Notification-T content and calls these methods on separate
-transport/runtime/context instances. The listener receives handle and complete ReceivedMessage and closes on the
-business recovery event. acknowledgement () and completion () separately represent ACK and actual stream exit, never
-workflow prerequisites.
+The host agent generates final Authorization-T/Notification-T content and calls these methods on separate
+transport/runtime/context instances. The listener receives the handle and complete ReceivedMessage, and closes on the
+host-defined terminal event. acknowledgement() and completion() separately represent ACK and actual stream exit;
+neither is a workflow prerequisite.
## 8. HTTPS Configuration
@@ -462,25 +460,12 @@ chunks. Observers cannot fail delivery. File references are recorded as referenc
requestId correlates each call; workflow calls additionally carry executionId/logicalTaskId/attempt,
agent/contextId/channel and remoteTaskId when known. These are local log fields, not wire metadata.
-### Inspect negotiation in the local Demo
+### Verify protocol and negotiation logs
-Running the local SpringSpnDemo without VM options now negotiates missing City1 input; City2 diagnoses directly. This is
-a local sample scenario, not an engine default. To use complete inputs in both cities, add this IDEA **VM option**:
-
-```text
--Da2at.samples.negotiation=false
-```
-
-By default only City1 loses its Task-T task object. Add `-Da2at.samples.negotiation.city=city2` or `both`
-to exercise City2 or both cities. The host retains city-scoped authoritative input and the complaint context is
-preserved. Expect DEMO_NEGOTIATION → INPUT_REQUIRED/PROPOSE → onNegotiation → ACCEPT → both diagnoses → one aggregate.
-External-OMC mode defaults to no injection and rejects an explicit true switch. The Demo passes its setting into the
-current Spring application context without modifying JVM-wide properties. Protocol observations are in the console and
-`logs/spn-demo.log` relative to the run directory.
-
-Run `SpringSpnDemoE2ETest` for direct transport. It tests the no-VM-option
-single-city default, explicit disable/enable and both-city negotiation with current SDK resources and an offline LLM
-provider. This is local protocol E2E evidence, not real model/platform/OMC validation.
+Use a controlled test in which one dispatched agent returns `INPUT_REQUIRED` with a valid Negotiation-T Propose. The
+expected sequence is request → Propose → `onNegotiation` → final Send/Stop → terminal task result. The `PROTOCOL` logger
+must show the observed request/response boundaries and correlation fields without exposing credentials. A declared
+Negotiation-T capability alone does not force negotiation.
## 10. Event Callback
@@ -492,7 +477,7 @@ EventCallback callback = new EventCallback() {
public void onEvent(String eventType, Map data) {
switch (eventType) {
case EventType.STEP_START -> System.out.println("Step started: " + data.get("step"));
- case EventType.AGENT_STATUS_UPDATE -> System.out.println(
+ case EventType.TASK_STATUS_CHANGED -> System.out.println(
data.get("agent") + " state: " + data.get("state"));
case EventType.NEGOTIATION_REQUEST -> System.out.println(
"Negotiation from " + data.get("agent"));
@@ -506,59 +491,51 @@ ExecutePsop.builder()
// ...
```
-Common event types: `STEP_START`, `STEP_COMPLETE`, `AGENT_REQUEST`,
-`AGENT_RESPONSE`, `NEGOTIATION_REQUEST`, `NEGOTIATION_RESOLVED`,
-`COMPLETE`, `ERROR`.
+Common event types: `START`, `STEP_START`, `TASK_REQUEST`,
+`TASK_RESPONSE`, `TASK_STATUS_CHANGED`, `STEP_COMPLETE`,
+`NEGOTIATION_REQUEST`, `NEGOTIATION_RESOLVED`, `NEGOTIATION_FAILED`,
+`ROUTE_DECISION`, `WORKFLOW_COMPLETE`, `COMPLETE`, `ERROR`, `CLOSE`.
## 11. Load Workflows from Orchestration Center
```java
// Search by intent
List results = LoadPsop.search(
- "https://127.0.0.1:5001", "SPN cross-city fault diagnosis", 5, null, false);
+ "https://orchestration.example.com", "analyze service request", 5, null, true);
// Load full workflow by ID
Workflow workflow = LoadPsop.load(
- "https://127.0.0.1:5001", results.get(0).getWorkflowId(), null, false);
+ "https://orchestration.example.com", results.get(0).getWorkflowId(), null, true);
```
## 12. Custom Extensions
-Construct final MessageContent (parts, metadata, extensions), with host-owned content generation/validation. No engine
+Construct final MessageContent(parts, metadata, extensions), with host-owned content generation/validation. No engine
handler or SDK instance registration. A2atMessages.from copies A2A-T metadata; other extensions can directly supply
metadata and activation URIs. AgentCard declarations never cause implicit generation.
-## External OMC startup checks
+## 13. External endpoints and startup ownership
-### Orchestration-center HTTPS development
+### 13.1 Orchestration-center HTTPS development
-The demo passes `A2A_SSL_VERIFY` (Spring property `a2a.ssl-verify`) to LoadPsop.search/load.
-For controlled development, set `A2A_SSL_VERIFY=false` or `--a2a.ssl-verify=false`.
-No local orchestration CA file is required: both calls skip chain and hostname checks, including
-self-signed certificates, missing SANs and mismatched SANs, and emit a `[Registry] INSECURE_TLS` warning.
+`LoadPsop.search/load` receives an explicit `sslVerify` argument. Keep it `true` in production. In a controlled
+development environment, `false` disables certificate-chain and hostname checks for that connection only and emits a
+`[Registry] INSECURE_TLS` warning. It does not modify JVM-wide TLS defaults.
The HTTPS server must still present a certificate, and client-certificate requirements (mTLS) still apply.
This mode cannot authenticate the server and is vulnerable to interception; do not use it in production.
Keep verification enabled in production, configure matching server SANs and trust its CA in the running JVM.
-The southbound `caCertsPath` setting is not forwarded to LoadPsop.
+The dispatched-agent `caCertsPath` setting is not forwarded to LoadPsop.
LoadPsop never changes JVM-wide SSLContext, SocketFactory or HostnameVerifier defaults.
-Southbound HTTP/JSON-RPC retains hostname checks when chain verification is disabled; vendor forwarding TLS
-remains managed by the vendor SDK/platform.
-
-### Downstream OMC startup
+Dispatched-agent HTTP/JSON-RPC retains hostname checks when chain verification is disabled.
-Set `A2A_AGENT_CARD_LOCATIONS` in the environment or local .env to comma-separated external
-AgentCard file locations. For SpringSpnDemo, set `A2A_EMBEDDED_OMC_ENABLED=false` or pass
-`--a2a.embedded-omc-enabled=false` when using externally managed OMCs.
-Use SpringWorkbenchApplication for a persistent host; the demo exits after its task.
+### 13.2 Dispatched-agent endpoints
-Embedded startup validates both effective city cards before starting either OMC, then uses those
-cards to bind. Missing cards, invalid URLs and non-local addresses fail early. Loopback and local
-network-interface addresses are accepted; non-loopback does not automatically mean remote.
-This does not probe OMCs or detect existing services. Disable embedded startup even when an externally
-managed OMC runs on this same machine. Keep real cards and credentials outside tracked sample resources.
+The engine consumes AgentCards and never starts dispatched-agent servers. The host agent owns AgentCard discovery,
+endpoint reachability checks, and any development fixtures. When using externally managed dispatched agents, do not
+start local fixtures on the same endpoints. Keep production AgentCards and credentials outside tracked sample resources.
-## Remote problem responses
+## 14. Remote problem responses
A successful HTTP envelope does not guarantee a successful task: SSE data can contain a top-level
problem object such as `{"status":429,"detail":"Active task limit reached","type":""}`.
@@ -570,7 +547,7 @@ first as an HTTP/authentication error by the SDK or adapter, without exposing it
such failures retain the generic failure path rather than fabricating errorDetails.
The error fails the call without fabricating a successful task, invoking onNegotiation, or automatically
-resubmitting it. For 400, inspect business inputs; for 429, inspect OMC capacity and existing tasks or
+resubmitting it. For 400, inspect business inputs; for 429, inspect dispatched-agent capacity and active tasks or
subscriptions before deciding whether to retry. Negotiation still requires a valid Negotiation-T Propose.
Use `RemoteProblemException.findIn(error)` to inspect the cause chain (null means no problem found);
workflow execution follows its existing failure path.
@@ -581,18 +558,16 @@ unrecognized SDK exceptions still expose only the exception type.
Independent authorization/subscription failures do not determine the workflow result.
Pretty logging does not modify the received problem payload.
-In the two-city sample, one failed diagnosis does not interrupt the other already dispatched call:
-its result or timeout is collected before the workflow returns `success=false`.
-The merge_analysis onSelfTask/LLM is not invoked; successful peer results remain in history,
-not in a fabricated overall diagnosis. There is no automatic retry, queuing or partial-success merge.
-Generic AnySuccess nodes retain their existing first-success semantics.
+For an `ALL_SUCCESS` step, one failed task does not retroactively cancel a peer call that was already dispatched: the
+peer result or timeout is collected before the workflow returns `success=false`. Downstream steps are not invoked;
+successful peer results remain in history rather than being turned into a fabricated aggregate. There is no automatic
+retry, queuing, or partial-success merge. `ANY_SUCCESS` nodes retain their declared first-success semantics.
Observe failures through TASK_RESPONSE or inspect result.history in `onFinish(result, events)`;
onTask prepares outbound content and is not an error-retry callback.
Executor node events include executionId; TASK_RESPONSE and history also include the logical taskId.
-The sample's northbound A2A task returns FAILED with per-agent errorCode, error and errorDetails
-in its status message, without a success-summary artifact. Demo shutdown closes subscriptions;
-a persistent host owns them independently of workflow results.
+The host agent decides how to expose a failed execution to its caller. It must not convert a failed execution into a
+success artifact. A persistent host owns notification subscriptions independently of workflow results.
Logging responsibilities: PROTOCOL captures observed traffic, correlated by requestId rather than
adjacent log entries; REMOTE_PROBLEM reports transport rejection; TASK_FAILED identifies the execution,
@@ -601,7 +576,7 @@ Sample TASK_RESPONSE logs bridge contextId and executionId. Recognized remote pr
while unexpected exceptions retain ERROR stack traces. Known credentials and Bearer/Basic values are redacted.
Logging configuration, pretty display and observer callbacks do not determine task outcomes.
-## 13. Interface Reference
+## 15. Interface Reference
| Interface/Class | Purpose |
|--------------------------------------------------------|-------------------------------------------------------------------------|
diff --git a/docs/zh/A2AT-SDK-DEPENDENCY.md b/docs/zh/A2AT-SDK-DEPENDENCY.md
deleted file mode 100644
index 35f20b14..00000000
--- a/docs/zh/A2AT-SDK-DEPENDENCY.md
+++ /dev/null
@@ -1,130 +0,0 @@
-# A2A-T SDK 正式版本依赖说明
-
-执行引擎首发版本只支持下列 A2A-T SDK 正式版本基线,不提供旧 SDK 兼容层:
-
-- 仓库:`project-openan/a2a-t-sdk-java`
-- 发布标签:`v1.1.0`,commit:`e42c83acce3e5ac2c245d36546ced0fa017b2b58`
-- Maven Central 版本:`1.1.0`,groupId `net.openan.a2a-t.sdk`
-- 引擎使用 `a2a-t-core`;samples/宿主显式使用 `a2a-t-client`,OMC 接收端使用 `a2a-t-server`
- 。引擎不传递引入内容生成、LLM、prompt/resources。
-
-## 已知上游生命周期缺口
-
-该正式版本中的 `A2ATClient`、`A2ATServer` 和 `LLMClient` 尚未实现
-`AutoCloseable`,因此调用过 OpenAI 兼容 LLM 后,底层 OpenAI Java 客户端的
-`DefaultSleeper` 定时线程无法由工作流引擎通过公共 API 关闭。`SpringSpnDemo` 的 离线协议测试不受此限制;真实模型调用后的生命周期仍需额外观察,
-一次性关闭内嵌 Tomcat 时可能提示上游线程生命周期告警。
-
-上游 SDK 应在新提交中补充幂等 `close()`,由 `A2ATClient/A2ATServer` 级联关闭其拥有的 LLM provider;执行引擎届时应升级到该新提交对应的新
-Maven 版本。禁止在不改变版本号的 情况下覆盖 `1.1.0`,否则 IDEA 与 CI 将得到不可复现的同坐标异内容制品。
-
-该 SDK 已发布到 [Maven Central](https://repo.maven.apache.org/maven2/net/openan/a2a-t/sdk/a2a-t-client/1.1.0/)。 本机和
-CI 直接解析正式制品,不再检出 SDK 源码或执行 SDK install,也不降级到其他版本。
-
-## 当前模板发现的资源隔离
-
-当前 SDK 的 PromptTemplateCatalog 会关闭 JarURLConnection 返回的 JarFile。 默认缓存可能让并行门面共用该句柄,导致 zip file
-closed。 samples 宿主 A2ATInitialization 在初始化线程临时包装资源 URL,仅该连接 useCaches=false, 完成后恢复类加载器;不更改全局
-URL 缓存或 SDK jar。 每次 SDK 升级需重新验证该隔离是否仍必要。
-
-SDK 当前内容接口接受字符串模板 URI,宿主通过 StandardTemplates.*.uri () 指定。 业务调用生成/校验 API,再经
-A2atMessages.from 转为最终 MessageContent。 引擎只检查协商上下文关联,不解释业务内容。
-完整契约见 [业务回调集成契约](BUSINESS_CALLBACKS.md)。
-
-## Maven / IDEA 接入
-
-根 POM 的 `a2a.t.sdk.version` 是唯一依赖版本来源,当前为 `1.1.0`。 在执行引擎目录直接执行:
-
-```powershell
-mvn -B -U clean verify
-```
-
-IDEA 使用该工程的 Maven 配置并执行 Reload All Maven Projects 即可;无需手工安装 A2A-T jar。
-如果企业或公共镜像尚未同步正式版本,应等待同步或按组织要求调整 Maven 镜像, 不要同坐标 install 本地构建物替代正式发布制品。本次通过本机镜像下载并核对
-SDK jar 的 SHA-1 与 Central 发布校验值一致。
-
-宿主若调用 A2A-T 内容生成,需显式声明:
-
-```xml
-
- net.openan.a2a-t.sdk
- a2a-t-client
- 1.1.0
-
-```
-
-需要接收端校验时同样显式添加 `a2a-t-server:1.1.0`;samples 已声明二者。
-
-## 升级 A2A-T SDK
-
-升级时更新根 `pom.xml` 的 `a2a.t.sdk.version`、本文发布标签和相关接入文档, 核对正式制品与源码/资源差异,并运行完整回归。CI
-直接使用根 POM,不单独维护版本或编译 SDK。
-
-禁止只覆盖本地同版本 jar。新版本至少要验证 Task-T 生成/校验、无状态 Negotiation-T metadata、Authorization-T、Notification-T
-长连接,以及现行传输路径: 验证直连 OMC 的 SpringSpnDemo 端到端流程。
-
-“只支持最新 A2A-T SDK”仅表示不兼容旧 SDK 版本和旧协议格式。
-
-## A2A Java SDK 基线
-
-执行引擎使用 Maven Central 已正式发布的 `org.a2aproject.sdk` `1.2.0.Final`,不采用尚未 发布制品的源码标签或 SNAPSHOT。该版本将
-`DefaultRequestHandler` 构造迁移到 Builder、 将任务校验收进请求处理器,并把 `Utils` 移到 `org.a2aproject.sdk.spec.util`
-。同时必须让
-`protobuf-java-util` 与其传递引入的 `protobuf-java:4.35.0` 对齐,避免 REST JSON 转换时 出现 `NoSuchMethodError`。Spring
-starter 通过仅暴露 `RequestHandler` 接口的委托隔离
-`DefaultRequestHandler` 中 CDI 专用注入点;SSE 端点使用 `SseEmitter` 输出规范事件,不能把 手工拼接的 `id:/data:`
-文本再次作为普通响应体发送。升级 A2A Java SDK 后必须重新运行直连 E2E。
-
-## 单一资源基线
-
-模板、slot schema、slot extraction 和 content validation 提示词全部来自锁定的 SDK jar。 samples 不再用同名 classpath 资源覆盖
-SDK 内容,也不保留旧 renderer/validator 的兼容提示词。 测试会核对实际加载资源来自 `a2a-t-resources`,避免升级后仍静默运行旧规则。
-
-Authorization-T 只接受当前 SDK 定义的规范数据形态:每条策略以从 1 连续递增的编号 `1.` 开头, 字段为“字段名是值”,字段间使用全角逗号
-`,`,多条策略用换行分隔。 例如:
-`1. 业务场景是业务投诉诊断,处置类型是业务抢通,操作名称是隧道调优,有效期是2026-06-01~2030-06-18`。
-日期范围与“永久生效”均支持;裸值列表、分号分隔和旧 `/` 格式不接受。 此前本节关于全角分号的描述不正确,已按 1.1.0 正式 jar 的
-slot.json 修正。
-
-## 示例业务接口调用复核(2026-08-31)
-
-此前发现的正文来源、填充结果丢弃、City2 缺参路径、Reject/Abort 分发及离线宽松校验问题, 现已在 samples
-业务层整改;通用引擎未重新引入内容生成或语义判断职责。
-
-| 路径 | 发送端生成 | 接收端校验/实际使用 |
-|----------------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
-| WAIMO → 集成方 | generateTaskPromptFromDataWithSchema | WorkbenchTaskInputParser 使用 validateTaskPromptAndDataFilling 的 filled.data |
-| 集成方 → 两地市任务 | WorkbenchControlPoint.onTask:generateTaskPromptFromDataWithSchema | 两 OMC 校验 Task-T,使用 filled.data 构造 SpnTaskInput |
-| 两地市 OMC → 集成方协商 | generateNegotiationProposePromptFromData,按实际缺失字段生成 | validateProposePromptAndDataFilling,按 items 从当前城市输入提取答案 |
-| 集成方 → OMC Accept | generateNegotiationAcceptPromptFromData | 从正式 metadata 取正文,validateAcceptPromptAndDataFilling 后合并请求字段并复核业务必填信息 |
-| 集成方 → OMC Reject/Abort | 对应 generateNegotiationRejectPromptFromData/generateNegotiationAbortPromptFromData | 对应 validateRejectPromptAndDataFilling/validateAbortPromptAndDataFilling,结束任务且不执行诊断 |
-| 独立授权 | generateAuthPromptFromDataWithSchema | validateAuthPromptAndDataFilling 后构造并应用 AuthorizationPolicy |
-| 独立订阅 | generateNotificationPromptFromDataWithSchema | validateNotificationPromptAndDataFilling 后构造 NotificationPolicy |
-
-自然语言 FromText 和结构化 FromDataWithSchema 是替代入口,不是同一消息必须串行全部执行的步骤。 本 Demo 主路径选择结构化数据;Task-T
-自然语言另有测试,不宣称覆盖 SDK 全部公开接口。 端口资源归属、投诉分类和 OSS 流水号是 SPN 业务层规则;正式集成应替换为宿主/OMC
-的实际业务判断。
-
-协商校验不再读取 parts 摘要;会核对 A2A taskId/contextId 和协商 id/round/maxRounds。 SDK 拒绝原始任务时,不使用未经验证的局部数据,重新协商完整必要字段。
-接收 Accept 后只合并本轮请求字段;错误正文、缺参和跨协商回复均不能进入诊断。
-
-1.1.0 的授权/通知生成入口面向授权操作及订阅请求,并不是任意业务结果生成接口。 诊断结果、授权回执和抢通事件由 OMC
-业务产生。RecoveryNotification 从正式 Notification-T artifact metadata 检查 SPN 最终抢通结果,避免仅凭 artifact
-名称/摘要就关闭订阅。 这不是对任意外部通知的通用语义验证,也不在工作流引擎内实施。
-
-完整角色映射、字段规则、FromText/FromData 选择、宿主拒绝策略和可重复验证范围见
-[业务回调指南第 9 节](BUSINESS_CALLBACKS.md#9-示例业务侧的-a2a-t-110-调用参考)。 严格离线测试只处理明确样例输入,不从模板示例臆造参数;不能代替真实
-LLM 和现网 OMC 联调。
-
-## 发布验证入口
-
-执行 `mvn -B clean verify`,必须包含 core、starter 与 samples;测试统计以当前提交的 Surefire XML 为准。
-发布前必须通过直连 SpringSpnDemo。
-默认本地演示为 City1 缺参协商、City2 完整输入;外部 OMC 不注入演示缺参。
-
-SSE pretty 展示保留事件控制字段,JSON 数据区明确标注为展示格式,不逐行重复 data:。
-设置 `WORKFLOW_ENGINE_PROTOCOL_PRETTY=false` 可查看脱敏后的原文,容量限制仍生效。
-Header 凭据始终脱敏,编排中心 query token 仅显示匿名标记,不存在敏感头放开开关。
-
-离线 LLM provider 和本地 OMC 结果不能代替真实模型/OMC 验收。
-真实环境验收须单独记录配置来源、版本、操作授权和脱敏报文,私有联调记录不得提交到公共仓库。
diff --git a/docs/zh/API_REFERENCE.md b/docs/zh/API_REFERENCE.md
index b50ff8d3..c95569cb 100644
--- a/docs/zh/API_REFERENCE.md
+++ b/docs/zh/API_REFERENCE.md
@@ -10,6 +10,7 @@
| `dev.openan.workflow.engine.model` | 数据模型(Workflow、Task、Result 等) |
| `dev.openan.workflow.engine.registry` | PSOP 加载和 AgentCard 注册 |
| `dev.openan.workflow.engine.runner` | 工作流执行入口 |
+| `dev.openan.workflow.engine.spring` | Spring Boot 服务端自动配置(starter) |
---
@@ -24,7 +25,7 @@
| 方法 | 类型 | 默认值 | 说明 |
|------------------------------------------|------|-------------|---------------------------------|
| `psop(Workflow)` | 必填 | - | PSOP 工作流定义 |
-| `agentCards(List)` | 必填 | `List.of()` | 工作流中所有智能体的 AgentCard |
+| `agentCards(List)` | 可选 | `List.of()` | 被调度智能体的 AgentCard;存在远程步骤且未传入已配置 `engineClient` 时必须提供 |
| `engineClient(WorkflowEngineClient)` | 可选 | null | 预配置客户端(null=自动创建) |
| `controlPoint(ControlPoint)` | 必填 | - | 用户决策实现 |
| `runtimeIntent(String)` | 可选 | `""` | 自然语言意图,用于上下文组装 |
@@ -42,7 +43,7 @@ ExecutionResult result = ExecutePsop.builder()
.psop(workflow)
.agentCards(cards)
.controlPoint(cp)
- .runtimeIntent("诊断故障")
+ .runtimeIntent("分析请求")
.sslVerify(false)
.execute()
.get(10, TimeUnit.MINUTES);
@@ -80,7 +81,7 @@ nextRound 或返回新 Propose。
Abort。 同一任务/会话/轮次的重复等待事件不会重复回调、重复提交;未变化状态通过 getTask 观察。
`maxNegotiationExchanges` 默认 3,是独立于 SDK context.maxRounds 的本地交互资源预算。 超时、预算耗尽、回调缺失均明确失败,不默认
Accept,也不自动生成 Abort。 Accept/Reject 的 SUBMITTED/WORKING ACK 仍需等待任务结果,不重发原命令。 业务发送 Abort 后,即使远端用
-COMPLETED 确认,也不能判为诊断成功。
+COMPLETED 确认,也不能判为任务成功。
### ExtensionSender
@@ -91,8 +92,8 @@ NotificationSubscription openNotification(String agentName, MessageContent conte
```
授权和订阅接收宿主生成的最终内容。三类操作使用独立 transport/runtime/context,成功与否不阻断工作流。openNotification 先注册
-handle 再开始 I/O,监听器直接收到 handle 和完整 ReceivedMessage。acknowledgement () 是实际 ACK;超时失败。close ()
-请求关闭,completion () 在流真正退出后完成。
+handle 再开始 I/O,监听器直接收到 handle 和完整 ReceivedMessage。acknowledgement() 是实际 ACK;超时失败。close()
+请求关闭,completion() 在流真正退出后完成。
### WorkflowEngineClientConfig
@@ -147,8 +148,8 @@ Header 生成不同值,引擎会抛出 `SecurityException`,不会静默覆
### A2atMessages
-通过 MessageContent (parts, metadata, extensions) 提交自定义扩展内容,不注册引擎内容处理器。A2atMessages.from
-(MetadataContent, List>) 保留 SDK metadata 原位置并激活对应 URI;contextOf (ReceivedMessage) 或 contextOf (Map<
+通过 MessageContent(parts, metadata, extensions) 提交自定义扩展内容,不注册引擎内容处理器。A2atMessages.from
+(MetadataContent, List>) 保留 SDK metadata 原位置并激活对应 URI;contextOf(ReceivedMessage) 或 contextOf(Map<
String,Object>) 读取并检查规范协商上下文。只有 a2a-t-core 依赖,没有内容生成或语义校验。
### A2AJavaClientRuntime
@@ -221,6 +222,25 @@ AgentCard card = mapper.readValue(
Map normalized = AgentCardNormalizer.normalize(rawMap);
```
+### 其他公开 client 类型
+
+以下公开类型面向高级集成。除非需要自定义 runtime、观测适配或显式生命周期管理,优先使用上述高层接口。
+
+| 类型 | 用途 |
+|-------------------------------|------|
+| `A2ATExtension` | 规范扩展名称与 URI |
+| `A2ATransport` | 底层传输、认证、响应组装与订阅生命周期 |
+| `DefaultWorkflowEngineClient` | `WorkflowEngineClient` 默认实现 |
+| `DefaultExtensionSender` | 基于 `A2ATransport` 的 `ExtensionSender` 默认实现 |
+| `DefaultA2AJavaClientRuntime` | HTTP/JSON-RPC/gRPC 的默认 A2A Java SDK runtime |
+| `CredentialCrypto` | AES-GCM 凭据加密工具与命令行入口 |
+| `EnvFileLoader` | 显式加载宿主自有 `.env` 配置 |
+| `SslContextFactory` | 传输与发现辅助 API 的 TLS 上下文构造 |
+| `ProtocolResponses` | A2A 事件与结果组装辅助方法 |
+| `ClientEventMapper` | 面向回调和诊断的稳定事件投影 |
+| `WireLog` | 关联上下文与协议观测门面 |
+| `RemoteProblemException` | 结构化远程问题响应(`status`、`title`、`detail`、`type`、`timestamp`) |
+
---
## dev.openan.workflow.engine.control
@@ -238,6 +258,9 @@ interface ControlPoint {
onTask 返回最终 parts/metadata/extensions,引擎封装发送,不再生成或改写内容。 onSelfTask 返回本地 TaskResult;onRoute
选择允许的候选;onNegotiation 返回 Send 或 Stop。 未实现的回调明确失败,不回显成功、不选首分支、不自动同意。
+
+`DefaultControlPoint` 保留上述快速失败默认值,并可把 `onNegotiation` 委托给注入的
+`NegotiationStrategy`。仅需定制协商策略时实现 `NegotiationStrategy.resolve(NegotiationRequest)`;同时需要定制任务、宿主本地任务或路由时,实现或构建完整 `ControlPoint`。
字段与完整示例见 [业务回调集成契约](BUSINESS_CALLBACKS.md)。
### EventCallback
@@ -358,11 +381,6 @@ WorkflowExecutor executor = new WorkflowExecutor(
ExecutionResult result = executor.run().join();
```
-### ContextBuilder
-
-包级私有辅助类,按 `contextFrom` 规则选择上游步骤结果并构造 `WorkflowInput`(省略 = 直接前驱,`[]` = 不聚合上游,`"*"` =
-所有祖先,指定名称 = 选择性聚合)。它不生成提示词,也不属于公共 API 接口。
-
---
## dev.openan.workflow.engine.model
@@ -395,7 +413,7 @@ ExecutionResult result = executor.run().join();
|---------------|-----------------------------------------------------------------------------------------|
| `ALL_SUCCESS` | 所有子任务必须成功 |
| `ANY_SUCCESS` | 任一子任务成功即可 |
-| `SELF_LOOP` | 工作流执行智能体通过 `onSelfTask` 本地处理,不发 A2A-T 消息。成功语义同 `ALL_SUCCESS`。 |
+| `SELF_LOOP` | 宿主智能体通过 `onSelfTask` 本地处理,不发 A2A-T 消息。成功语义同 `ALL_SUCCESS`。 |
### TaskStatus
@@ -425,7 +443,7 @@ ExecutionResult result = executor.run().join();
### TaskRequest / BusinessInput
-TaskRequest 使用 getXxx () 访问器:
+TaskRequest 使用 getXxx() 访问器:
| 字段 | 含义 |
|------------------------------|------------------------------------------------------------------|
@@ -435,8 +453,9 @@ TaskRequest 使用 getXxx () 访问器:
| input | BusinessInput:文本或任意 JSON 可序列化数据,二选一,不含 schema |
| workflowInput | WorkflowInput(runtimeIntent, upstreamResults),与当前输入分离 |
-BusinessInput.text (value) / BusinessInput.data (value) 创建输入快照。
-WorkflowInput、UpstreamStepResult、ReceivedMessage、NegotiationRequest 等 record 使用 field () 访问器。
+BusinessInput.text(value) / BusinessInput.data(value) 创建输入快照。
+WorkflowInput、UpstreamStepResult、ReceivedMessage、NegotiationRequest 等 record 使用 field() 访问器。
+`BusinessValues.snapshot`、`map` 和 `list` 为宿主自有数据提供相同的防御性 JSON 快照;嵌套数组仍是独立值,返回集合不可修改。
| contextFrom | 上游选择 |
|---------------|----------------------------------|
@@ -450,7 +469,7 @@ contextFrom 只选择证据,不建立执行依赖;依赖由 next 定义。
窗口结构:stepName → taskResults[] → outputs[] / receivedMessages[]。 TaskExecutionResult 还保留 agentName、skill、逻辑
taskId、taskDescription、status、 error、errorCode、errorDetails。多子任务不混合,嵌套数组仍作为一个输出项。 输出不要求来自
-LLM,也不要求符合投诉模板。
+LLM,也不强制符合特定领域模板。
### MessageContent / ReceivedMessage
@@ -459,7 +478,7 @@ record MessageContent(List> parts, Map metadata, Set taskMetadata, List artifacts) {}
```
-通过 MessageContent.text (text)、MessageContent.parts (parts) 或构造器创建快照。 TextPart、DataPart、FilePart 保留顺序及各自
+通过 MessageContent.text(text)、MessageContent.parts(parts) 或构造器创建快照。 TextPart、DataPart、FilePart 保留顺序及各自
metadata,文件引用不会自动下载。 MessageContent 不提供 role、目标、messageId、taskId、contextId 或认证头。 业务 metadata 中即使存在
contextId 字段,也不能覆盖真实 A2A 信封。
@@ -468,7 +487,7 @@ metadata 的业务结果仍可从完整视图读取。 便利 outputs 按 artifa
artifact 时,可提取成功终态或独立 Message 的正文。 FilePart 仅在完整视图提供。不解析文本、不拼接相邻文本、不拍平嵌套业务数组。
失败状态消息只保留作证据,不进入 outputs;已返回的有效部分 artifact 仍保留。 SSE append/replace 按 artifact 组装,最终快照不重复累加。
-本地 onSelfTask 返回 TaskResult.success (List),允许空列表; TaskResult.failure (code, message) 将错误与输出分开,builder
+本地 onSelfTask 返回 TaskResult.success(List),允许空列表; TaskResult.failure(code, message) 将错误与输出分开,builder
可保留有效部分输出。 远端 TaskResult 的 receivedMessages 是完整证据,便利输出由它派生。 远端 Task 只有 COMPLETED 才成功,独立
A2A Message 也可完成交互。 进度/协商提示不会因为含文本就变成工作流成功。
@@ -478,7 +497,7 @@ A2A Message 也可完成交互。 进度/协商提示不会因为含文本就
### SendMessageResult
-getReceivedMessages () 是保留层级的响应来源,getOutputs () 为便利投影;getTask ()/getTaskState ()
+getReceivedMessages() 是保留层级的响应来源,getOutputs() 为便利投影;getTask()/getTaskState()
保留实际远端状态,failureCode/failureMessage 为独立本地交互失败。text 和扁平 metadata 仅用于传输诊断,不用它们代替完整业务响应。
### ExecutionResult
@@ -523,13 +542,8 @@ getReceivedMessages () 是保留层级的响应来源,getOutputs () 为便利
| Authorization-T | `https://projects.tmforum.org/a2aproject/telecommunication/extensions/Authorization-T/v1` |
| Notification-T | `https://projects.tmforum.org/a2aproject/telecommunication/extensions/Notification-T/v1` |
-> **协商相关 metadata key 约定:**
->
-> - `negotiationContext`(SDK 契约):SDK 生成协商消息时写入,携带 `{id, round, maxRounds, performative}`。
-> validate API 依赖它区分协商消息与普通消息。
-> - `templateUri`(SDK 契约):SDK 生成消息时写入,标识渲染所用模板。
-> - `negotiation_message` / `negotiation_params`(引擎内部):接收校验后供自动循环和
-> 业务控制点读取,绝不作为线上的协议兼容键。
+规范协商 metadata 使用 `templateUri` 和 `negotiationContext`。后者携带
+`{id, round, maxRounds, performative}`,引擎解析协商消息时必须存在。引擎不会向线上报文添加私有协商状态键。
---
@@ -544,10 +558,10 @@ getReceivedMessages () 是保留层级的响应来源,getOutputs () 为便利
- 顶层远端 problem 错误通过 `RemoteProblemException` 保留;可用 `findIn(Throwable)` 查找包装异常中的原因。
工作流任务失败信息为 `remote.problem.`,详情进入 history / TASK_RESPONSE,不作为业务 outputs。
引擎节点事件携带 executionId,任务结果事件和 history 携带逻辑 taskId。
- 失败传播、双地市汇总规则和日志职责见 [集成指南](INTEGRATION_GUIDE.md#远端错误响应)。
+ 失败传播、并行步骤失败规则和日志职责见 [集成指南](INTEGRATION_GUIDE.md#14-远端错误响应)。
- 回调异常/空值/超时明确失败;SDK 内容错误由宿主转换为 BusinessFailure,必要时保留安全的 code/details。
-- maxNegotiationExchanges 耗尽或本地 Stop 只结束本地交互,不自动发送 Abort。业务 Send (Abort) 不算诊断成功。
+- maxNegotiationExchanges 耗尽或本地 Stop 只结束本地交互,不自动发送 Abort。业务 Send(Abort) 不算任务成功。
- 远端状态和错误与业务输出分开,失败状态消息不成为 outputs,已有部分 artifact 保留。
- 缺失必需认证信息时拒绝发送,不静默匿名访问。
- SDK 流退出/传输错误日志是传输观察,不等同于工作流最终状态;以 ExecutionResult 和远端任务状态判断结果。
@@ -619,11 +633,10 @@ a2at:
@Component
public class MyAgentExecutor implements AgentExecutor {
@Override
- public ExecuteResult execute(ExecuteRequest request) {
- // 业务逻辑
- return ExecuteResult.builder()
- .addTextPart("result text")
- .build();
+ public void execute(RequestContext context, AgentEmitter emitter) throws A2AError {
+ // business logic, then emit progress and the final result:
+ emitter.updateStatus(TaskState.WORKING, statusMessage);
+ emitter.addArtifact(List.of(new TextPart("result text")));
}
}
```
diff --git a/docs/zh/BUSINESS_CALLBACKS.md b/docs/zh/BUSINESS_CALLBACKS.md
index aaf31ee7..87a00761 100644
--- a/docs/zh/BUSINESS_CALLBACKS.md
+++ b/docs/zh/BUSINESS_CALLBACKS.md
@@ -37,7 +37,7 @@ ControlPoint callbacks = ControlPoint.builder()
## 2. TaskRequest 与上游窗口
-TaskRequest 使用 getXxx () 访问器:
+TaskRequest 使用 getXxx() 访问器:
| 字段 | 含义 |
|------------------------------|------------------------------------------------------------------|
@@ -47,8 +47,8 @@ TaskRequest 使用 getXxx () 访问器:
| input | BusinessInput:文本或任意 JSON 可序列化数据,二选一,不含 schema |
| workflowInput | WorkflowInput(runtimeIntent, upstreamResults),与当前输入分离 |
-BusinessInput.text (value) / BusinessInput.data (value) 创建输入快照。
-WorkflowInput、UpstreamStepResult、ReceivedMessage、NegotiationRequest 等 record 使用 field () 访问器。
+BusinessInput.text(value) / BusinessInput.data(value) 创建输入快照。
+WorkflowInput、UpstreamStepResult、ReceivedMessage、NegotiationRequest 等 record 使用 field() 访问器。
| contextFrom | 上游选择 |
|---------------|----------------------------------|
@@ -61,8 +61,8 @@ contextFrom 只选择证据,不建立执行依赖;依赖由 next 定义。
引擎不把窗口附加到 instruction/parts,也不调用 LLM 映射上游;由宿主决定怎么消费或映射下游输入。
窗口结构:stepName → taskResults[] → outputs[] / receivedMessages[]。 TaskExecutionResult 还保留 agentName、skill、逻辑
-taskId、taskDescription、status、 error、errorCode、errorDetails。多子任务不混合,嵌套数组仍作为一个输出项。 输出不要求来自
-LLM,也不要求符合投诉模板。
+taskId、taskDescription、status、error、errorCode、errorDetails。多子任务不混合,嵌套数组仍作为一个输出项。输出不要求来自
+LLM,也不要求符合特定领域模板。
## 3. 最终内容与完整响应
@@ -71,7 +71,7 @@ record MessageContent(List> parts, Map metadata, Set taskMetadata, List artifacts) {}
```
-通过 MessageContent.text (text)、MessageContent.parts (parts) 或构造器创建快照。 TextPart、DataPart、FilePart 保留顺序及各自
+通过 MessageContent.text(text)、MessageContent.parts(parts) 或构造器创建快照。 TextPart、DataPart、FilePart 保留顺序及各自
metadata,文件引用不会自动下载。 MessageContent 不提供 role、目标、messageId、taskId、contextId 或认证头。 业务 metadata 中即使存在
contextId 字段,也不能覆盖真实 A2A 信封。
@@ -80,7 +80,7 @@ metadata 的业务结果仍可从完整视图读取。 便利 outputs 按 artifa
artifact 时,可提取成功终态或独立 Message 的正文。 FilePart 仅在完整视图提供。不解析文本、不拼接相邻文本、不拍平嵌套业务数组。
失败状态消息只保留作证据,不进入 outputs;已返回的有效部分 artifact 仍保留。 SSE append/replace 按 artifact 组装,最终快照不重复累加。
-本地 onSelfTask 返回 TaskResult.success (List),允许空列表; TaskResult.failure (code, message) 将错误与输出分开,builder
+本地 onSelfTask 返回 TaskResult.success(List),允许空列表; TaskResult.failure(code, message) 将错误与输出分开,builder
可保留有效部分输出。 远端 TaskResult 的 receivedMessages 是完整证据,便利输出由它派生。 远端 Task 只有 COMPLETED 才成功,独立
A2A Message 也可完成交互。 进度/协商提示不会因为含文本就变成工作流成功。
@@ -90,22 +90,19 @@ A2A Message 也可完成交互。 进度/协商提示不会因为含文本就
WorkflowEngineClientConfig 或 ExecutePsop 承载 LLM 配置。
```java
-// sdk、data、schema、模板选择均属于宿主。
+// sdk、data、schema、模板选择均属于宿主智能体。
MetadataContent generated = sdk.generateTaskPromptFromDataWithSchema(
data, schema, StandardTemplates.PRIVATE_LINE_COMPLAINT.uri());
-MessageContent outgoing = A2atMessages.from(generated, List.of(new TextPart("诊断当前专线")));
+MessageContent outgoing = A2atMessages.from(generated, List.of(new TextPart("处理当前任务")));
```
-自然语言入口为 `sdk.generateTaskPromptFromText(text, StandardTemplates.PRIVATE_LINE_COMPLAINT.uri())`,随后同样调用
-A2atMessages.from。
+自然语言入口为 `sdk.generateTaskPromptFromText(text, StandardTemplates.PRIVATE_LINE_COMPLAINT.uri())`,随后同样调用 A2atMessages.from。
-自然语言、结构化生成和校验等能力直接调用宿主 SDK。 A2atMessages.from 只复制生成的 metadata 并激活对应扩展,parts 保持业务提供的内容;
-不生成、不校验业务语义。模板使用 StandardTemplates,不自行猜 URI。 可运行 SPN 示例见 WorkbenchControlPoint 和
-examples.negotiation.NegotiationStrategy。 A2ATInitialization 是 samples 宿主对锁定 SDK 的 JarFile 初始化隔离,不属于引擎。
+自然语言、结构化生成和校验等能力直接调用宿主 SDK。A2atMessages.from 只复制生成的 metadata 并激活对应扩展,parts 保持业务提供的内容;不生成、不校验业务语义。应选择当前 A2A-T SDK 发布的模板 URI,不自行构造。样例初始化工具属于宿主示例,不是引擎依赖。
## 5. 协商
-NegotiationRequest (task, originalSubmission, received, previousExchanges, remainingWait): task 是原始
+NegotiationRequest(task, originalSubmission, received, previousExchanges, remainingWait): task 是原始
TaskRequest,originalSubmission 是首次最终提交, received 是当前完整响应,previousExchanges 只含该会话的 Exchange
(received, reply), remainingWait 是本次交互剩余时间。引擎不规定业务 proposal 分类或 schema。
@@ -118,158 +115,59 @@ nextRound 或返回新 Propose。
Abort。 同一任务/会话/轮次的重复等待事件不会重复回调、重复提交;未变化状态通过 getTask 观察。
`maxNegotiationExchanges` 默认 3,是独立于 SDK context.maxRounds 的本地交互资源预算。 超时、预算耗尽、回调缺失均明确失败,不默认
Accept,也不自动生成 Abort。 Accept/Reject 的 SUBMITTED/WORKING ACK 仍需等待任务结果,不重发原命令。 业务发送 Abort 后,即使远端用
-COMPLETED 确认,也不能判为诊断成功。
+COMPLETED 确认,也不能判为任务成功。
业务可调用 validateProposePromptAndDataFilling,再选择 SDK 强类型 fromData 或自然语言生成接口。
-不得虚构缺失客户事实;样例只从本地当前地市任务输入提取实际请求的字段。 SDK 内容异常由宿主映射成 BusinessFailure (code,
+不得虚构缺失业务事实;只从当前任务的权威输入中提取实际请求的字段。SDK 内容异常由宿主映射成 BusinessFailure(code,
safeMessage, safeDetails); 引擎不识别 SDK 专属内容异常类,也不自动转存可能含敏感数据的原异常。
## 6. 路由、并发与失败
-RouteRequest (executionId, stepName, workflowInput, currentResults, candidates)。 候选为 RouteOption (nextStep,
-condition),返回 RouteDecision.builder ().nextStep (允许目标).build ()。 无条件边自动并行推进,条件分支只接受候选中的目标。
+RouteRequest(executionId, stepName, workflowInput, currentResults, candidates)。 候选为 RouteOption(nextStep,
+condition),返回 RouteDecision.builder().nextStep(允许目标).build()。 无条件边自动并行推进,条件分支只接受候选中的目标。
-回调可能并发,不要共享可变“当前任务/城市”字段。 每个工作流任务从内容准备到传输完成受客户端 timeout 总体限制,默认
+回调可能并发,不要共享可变的“当前任务”状态。每个工作流任务从内容准备到传输完成受客户端 timeout 总体限制,默认
sendTimeoutSeconds=600; 路由单独限制回调等待时间,dispatch/协商另有总等待截止时间。取消/超时后晚到结果不发送,
但不等于自动取消宿主正在运行的 LLM 或业务操作,宿主负责清理其资源。 同步回调入口应迅速返回,阻塞任务应交给异步执行器。
回调缺失、返回 null 或异常均明确失败;不确定发送失败不自动重发。
## 7. 独立授权与订阅
-ExtensionSender.sendAuthorization (agentName, finalContent) 返回 CompletableFuture。
-ExtensionSender.openNotification (agentName, finalContent, (handle, received) -> ...) 立即返回 handle。
-传入宿主生成的最终授权/订阅内容并激活对应扩展。 I/O 前注册 handle,早到事件可直接 handle.close (),不用捕获尚未赋值的外部变量。
-acknowledgement () 代表真实 ACK 或失败;超时不伪造成功。 close () 请求关闭,completion () 在流实际退出后完成。
-订阅保持到抢通结果、显式取消或宿主退出,不随单次工作流结束自动关闭。
+ExtensionSender.sendAuthorization(agentName, finalContent) 返回 CompletableFuture。
+ExtensionSender.openNotification(agentName, finalContent, (handle, received) -> ...) 立即返回 handle。
+传入宿主生成的最终授权/订阅内容并激活对应扩展。 I/O 前注册 handle,早到事件可直接 handle.close(),不用捕获尚未赋值的外部变量。
+acknowledgement() 代表真实 ACK 或失败;超时不伪造成功。 close() 请求关闭,completion() 在流实际退出后完成。
+订阅保持到宿主定义的终态事件、显式取消或宿主退出,不随单次工作流结束自动关闭。
-任务、授权、通知使用独立 transport/runtime/context。 授权和订阅成功与否均不作为工作流执行前提; 有效白名单只决定 OMC
-是否允许额外的自动抢通操作。
+任务、授权、通知使用独立 transport/runtime/context。授权和订阅成功与否均不作为工作流执行前提;授权策略只控制与该策略关联的宿主自定义业务操作。
## 8. 验证和日志
-### 查看真实协商与协议日志
+执行 `mvn -B clean verify`。Reactor 覆盖回调契约、完整响应组装、协商关联、独立扩展生命周期、失败传播、取消和协议日志脱敏。测试使用受控测试数据,不覆盖 SDK schema 或模板;这些是发布回归证据,不等于生产端点或真实模型语义认证。日志与认证配置见 [集成指南](INTEGRATION_GUIDE.md)。
-直接运行本地 SpringSpnDemo,默认 City1 缺参并协商、City2 参数完整直接诊断,无需添加 VM 参数。 Negotiation-T 扩展激活本身不强制协商;这是本地
-Demo 专门设置的场景,不是引擎默认行为。 要关闭本地缺参演示、让两城市都直接诊断,在 IDEA **VM options** 增加:
+## 9. 业务侧 A2A-T 1.1.0 调用参考
-```text
--Da2at.samples.negotiation=false
-```
-
-默认仅移除 City1 的 Task-T 任务对象;启用状态下增加 `-Da2at.samples.negotiation.city=city2` 或 `both`
-可演示 City2 或两城市同时缺参。宿主仍保留各城市的正确输入,原投诉上下文不变。预期链路是 `DEMO_NEGOTIATION` →
-`INPUT_REQUIRED / PROPOSE`
-→ 集成方 onNegotiation → `ACCEPT` → 两城市完成 → 一次汇总。 非内嵌 OMC 模式默认不注入缺参,显式设置
-`-Da2at.samples.negotiation=true` 也会被拒绝。 Demo 将开关传入当前 Spring 应用实例,不设置或修改 JVM 全局开关,不影响其他宿主。
-协议日志在控制台及以运行目录为基准的 `logs/spn-demo.log`。 本分支使用直连模式。
-
-离线重复验证:
-
-```powershell
-mvn -q -pl samples -am "-Dtest=SpringSpnDemoE2ETest" "-Dsurefire.failIfNoSpecifiedTests=false" test
-```
-
-每个测试类覆盖无 VM 参数的默认单城市协商、显式关闭、显式开启及两城市同时缺参。测试使用离线 LLM provider,但实际运行当前 SDK
-的 模板、校验和 HTTP/SSE;不是现网 OMC、平台或真实模型语义的验证。
-
-启动测试使用公开模拟凭据,不覆盖 SDK 模板或 schema。 运行时固定测试端口需空闲;本地工作流完成后最多观察首次抢通通知十秒,
-没有抢通通知不改变工作流结果。认证和日志配置详见 [集成指南](INTEGRATION_GUIDE.md)。
-
-## 9. 示例业务侧的 A2A-T 1.1.0 调用参考
+引擎只调度最终 A2A 消息;生成、校验、填充和业务判断属于宿主智能体与被调度智能体的业务代码。每个角色只调用自己所有消息需要的 API。
-引擎只调度最终 A2A 消息;下列生成、校验、填充和业务判断都在 samples 中完成。 不是每个智能体都调用全部 API,也不是同一消息依次调用
-FromText 和 FromData。
+| 业务位置 | 生成/接收校验入口 | 对结果的业务使用 |
+|-----------------------------------|-------------------------------------------------------------------------------------------------------------|----------------------|
+| 宿主智能体入站任务 | validateTaskPromptAndDataFilling | 使用已校验 `filled.data` 选择工作流并构建业务输入 |
+| onTask → 被调度智能体 | generateTaskPromptFromDataWithSchema 或 generateTaskPromptFromText → validateTaskPromptAndDataFilling | 从已校验数据构建输入,不直接使用扩展原文 |
+| 被调度智能体 → 宿主协商 | generateNegotiationProposePromptFromData 或 FromText → validateProposePromptAndDataFilling | 仅从当前任务权威数据回答实际请求项 |
+| 宿主 Accept → 被调度智能体 | generateNegotiationAcceptPromptFromData 或 FromText → validateAcceptPromptAndDataFilling | 合并已校验的请求字段,再执行领域校验 |
+| 宿主 Reject / Abort → 被调度智能体 | 阶段对应生成入口 → 阶段对应校验入口 | 校验原因并终止当前任务路径 |
+| 独立授权 | generateAuthPromptFromDataWithSchema → validateAuthPromptAndDataFilling | 从 `filled.data` 构建并应用 AuthorizationPolicy |
+| 独立订阅 | generateNotificationPromptFromDataWithSchema → validateNotificationPromptAndDataFilling | 构建 NotificationPolicy 并保持独立连接 |
-| 业务位置 | 生成/校验入口 | 对结果的业务使用 |
-|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
-| WAIMO → 集成方 | SpringSpnDemo:generateTaskPromptFromDataWithSchema;WorkbenchTaskInputParser:validateTaskPromptAndDataFilling | 将 filled.data 作为投诉参数,按任务检索工作流 |
-| 集成方 onTask → 两地市 OMC | WorkbenchControlPoint:generateTaskPromptFromDataWithSchema;NegotiationBaseAgentExecutor:validateTaskPromptAndDataFilling | 用 filled.data 构造 SpnTaskInput,交给 executeBusiness;不把原始协议提示词传给诊断 |
-| OMC 缺参 → 集成方 onNegotiation | 两地市共用 generateNegotiationProposePromptFromData;NegotiationStrategy:validateProposePromptAndDataFilling | 从 items 读取实际请求字段,仅从当前城市原始业务输入补充,不使用另一城市的数据 |
-| 集成方补参 → OMC | generateNegotiationAcceptPromptFromData;validateAcceptPromptAndDataFilling | 合并所请求字段的 filled.data,复核端口、投诉分类及 OSS 流水号后执行诊断 |
-| 集成方拒绝/终止 → OMC | generateNegotiationRejectPromptFromData/generateNegotiationAbortPromptFromData;对应 validateRejectPromptAndDataFilling/validateAbortPromptAndDataFilling | 校验原因后结束当前诊断任务,不进入诊断业务 |
-| 独立白名单授权 | ExtensionPrePositioner:generateAuthPromptFromDataWithSchema;PrePositionedExtensionHandler:validateAuthPromptAndDataFilling | 用 filled.data 构建并应用 AuthorizationPolicy |
-| 独立订阅 | ExtensionPrePositioner:generateNotificationPromptFromDataWithSchema;PrePositionedExtensionHandler:validateNotificationPromptAndDataFilling | 用 filled.data 构建 NotificationPolicy,保持独立通知连接 |
-
-### 输入选择与消息封装
-
-有结构化业务参数时使用上述 FromDataWithSchema;只有自然语言时,业务可改用 generateTaskPromptFromText (text, templateUri)
-、generateAuthPromptFromText (text, templateUri)、 generateNotificationPromptFromText (text, templateUri)。协商同时提供各阶段
+有结构化业务参数时使用上述 FromDataWithSchema;只有自然语言时,业务可改用 generateTaskPromptFromText(text, templateUri)
+、generateAuthPromptFromText(text, templateUri)、 generateNotificationPromptFromText(text, templateUri)。协商同时提供各阶段
FromText 和强类型 FromData。 这些是输入方式的选择,不是每条消息必须全部执行的流水线。FromDataWithSchema 也可能调用 SDK 配置的
LLM, 不能等同于完全不调用模型。
-无论选择哪一种生成入口,都保留返回 MetadataContent 的完整 metadata,再通过 A2atMessages.from (generated, List.of (new
-TextPart ("业务摘要"))) 转成引擎 MessageContent。 parts 必须有合法内容;摘要不是正式扩展正文,不能拿摘要替代校验输入。不要只复制
+无论选择哪一种生成入口,都保留返回 MetadataContent 的完整 metadata,再通过 A2atMessages.from(generated, List.of (new
+TextPart("业务摘要"))) 转成引擎 MessageContent。 parts 必须有合法内容;摘要不是正式扩展正文,不能拿摘要替代校验输入。不要只复制
promptText 而丢失 templateUri、negotiationContext 或扩展 URI。
-
-### 信息协商的数据与失败边界
-
-本场景按 SDK 投诉模板的业务字段协商:任务对象中包含本地市接入端口,任务上下文中包含投诉分类、
-OSS侧事件流水号及已有的时间和详情。已验证任务只有部分字段不足时仅请求这些字段; SDK 拒绝整个原始输入时不信任其中的局部内容,重新请求完整业务字段。当前请求项采用
-AND,所有请求字段必需。
-
-接收 Propose 的参数 schema 中,items 是非空字段名数组,relationship 是可空逻辑关系字符串; 其 description 说明“实际请求字段”和
-AND/OR 含义。Accept 校验 schema 则按本轮请求动态生成, 字段为非空字符串并列入 required。SpnTaskInput 的端口、流水号等规则仅是
-SPN 示例业务规则, 不是通用执行引擎的输出格式约束;正式 OMC 应接入自身资源清单与领域校验。
-
回复必须匹配 A2A taskId/contextId 及协商 id/round/maxRounds,正式正文从 Negotiation-T metadata 读取。
-一次有效回复只消费一次。错误正文、漏填、外地市端口或跨协商回复不能进入诊断。 Reject 表示业务拒绝提供;Abort
-表示无法核实信息或终止协商,均不是诊断成功。
-
-默认 NegotiationStrategy 允许提供已掌握的当前任务字段;无法核实时生成 Abort,不编造字段。 宿主可通过 mayProvideField (task,
-fieldName) 注入披露策略,拒绝时由示例生成 Reject:
-
-```java
-var strategy = new dev.openan.workflow.engine.examples.negotiation.NegotiationStrategy(envPath) {
- @Override
- protected boolean mayProvideField(
- dev.openan.workflow.engine.model.TaskRequest task, String fieldName) {
- return allowedFields.contains(fieldName); // 宿主已计算的业务允许字段集合
- }
-};
-var callbacks = new dev.openan.workflow.engine.examples.workbench.WorkbenchControlPoint(envPath, strategy);
-```
-
-这里的字段披露策略与 Authorization-T 的“自动抢通白名单”是两件事。
-
-### 结果和通知不要错用请求生成接口
-
-诊断结果、授权回执、业务抢通事件由 OMC 业务生成。1.1.0 的上述授权/通知入口面向授权操作和订阅请求, 不能把抢通事件再次交给订阅请求生成/校验接口。示例
-RecoveryNotification 按 SPN 业务事件格式检查正式 Notification-T artifact metadata:包含任务流水号、OSS流水号、端口、方案及完成结果等必要字段且状态为已结束,
-才确认收到最终结果并关闭该订阅;成功和失败都是“执行已结束”,不把失败结果冒充成功。 单独 artifact 名称、parts 摘要、订阅
-ACK、未启动方案均不能触发关闭。不完整事件仍交给通知回调观察。
-
-City1 上报的端口和 OSS 流水号来自本次已校验 SpnTaskInput,不再固定写成样例常量。 City2
-没有故障可以不产生抢通结果;授权/订阅失败和是否收到抢通通知,均不决定工作流诊断是否启动或成功。
-
-### 可重复验证范围
-
-EmbeddedA2AServerTest 覆盖正常填充数据、Accept 填充数据保留、错误正文、漏填、跨协商回复、 Reject 和
-Abort;NegotiationStrategyTest 覆盖城市输入隔离、不可核实的 Abort 和业务拒绝的 Reject; RecoveryNotificationTest
-覆盖完整成功/失败结果、仅摘要、错误命名和不完整事件。 SpringSpnDemoE2ETest 覆盖完整输入、City1 缺参、两城市同时缺参。 完整业务抢通链路由 SpnCrossCityE2ETest 验证。
-
-这些测试用真实 SDK 1.1.0 的生成和校验管线、真实 A2A HTTP/SSE。 离线 LLM provider
-仅解析明确的样例输入,拒绝错误摘要/缺参,不代表真实模型的语义质量或现网联调已通过。
-
-### 授权策略格式与协商字段(SDK 1.1.0)
-
-新增授权使用 SDK 当前的编号、标签、换行格式,不能传四个无标签的裸值:
-
-```text
-1. 业务场景是业务投诉诊断,处置类型是业务抢通,操作名称是隧道调优,有效期是2026-06-01~2030-06-18
-2. 业务场景是视频保障,处置类型是故障切换,操作名称是频谱重耕,有效期是永久生效
-```
-
-OMC 示例仅从 SDK validate-and-fill 的数据构造白名单,逐字段提取标签后的值, 按业务场景、处置类型、操作名称精确匹配,有效期包含起止日。全部规则解析成功才替换原策略;
-任意一条非法均保持原策略,不降级成默认授权。
-
-示例只保存一组内存白名单(固定示例 ID),不代表生产策略库: 删除请求使用 `1. 策略标识是<当前策略UUID>`;仅支持单个精确
-ID,不支持批量或条件删除。 查询省略策略列表或传空字符串,返回全部当前规则;不支持带条件查询,不会静默忽略条件。
-修改、条件查询、批量/条件删除需由集成方的策略库实现。该业务限制不代表 A2A-T SDK 不支持这些操作。
-
-信息协商仍先调用 SDK `validateProposePromptAndDataFilling`。当前中文信息协商模板由 typed API
-生成,示例从已通过语义校验的“所需信息项”中保留编号后的原始名称(如“任务对象”)用于回复,
-不把模型提取出的说明文字(如“本地市实际接入端口名称”)当成新字段,也不模糊映射到其他数据。 未知字段或本地数据缺失时
-Abort,业务披露策略拒绝时 Reject;不能用其他地市的信息补齐。 这些处理均在 samples 的业务层,不进入通用引擎。
+一次有效回复只消费一次。正文错误、字段不完整、任务不匹配或跨协商的回复不得执行业务。无法核实权威信息时生成 SDK Abort,不编造数据;宿主披露策略拒绝时生成 SDK Reject。授权策略是独立关注点。
-Notification-T 的失败原因字段使用完整名称“业务抢通方案执行失败原因”,执行失败时必填。 SDK 校验拒绝时记录错误码、slot
-错误和已提取参数的键名,方便定位;不绕过校验。 授权、订阅的成功与否仍不阻断独立工作流。
+Authorization-T 和 Notification-T 的生成接口用于生成请求,不用于包装任意业务结果。授权回执与通知事件是被调度智能体的业务输出,必须遵循选定模板和应用契约。订阅只在宿主定义的终态事件、显式取消或关闭时结束;ACK 本身不是终态业务事件。SDK 拒绝应保留安全的错误码、slot 错误与已提取参数键。授权或订阅失败仍不阻断工作流执行。
diff --git a/docs/zh/DESIGN.md b/docs/zh/DESIGN.md
index 11377939..a4aae8f8 100644
--- a/docs/zh/DESIGN.md
+++ b/docs/zh/DESIGN.md
@@ -37,17 +37,17 @@ flowchart TB
classDef local fill: #F8FAFC, stroke: #64748B, color: #0F172A, stroke-dasharray: 5 3
classDef agent fill: #F5F3FF, stroke: #7C3AED, color: #2E1065, stroke-width: 1.5px
- subgraph CONTROL["周边控制面与上游系统"]
+ subgraph CONTROL["外部系统"]
direction LR
- WAIMO["WAIMO
上游任务发起方"]:::external
- REG["注册中心 / Registry Center
AgentCard 发布与查询"]:::external
- ORCH["编排中心 / Orchestration Center
工作流检索与 PSOP 加载"]:::external
+ CALLER["外部 A2A 请求方
任务请求与最终响应"]:::external
+ REG["注册中心
AgentCard 发布与发现"]:::external
+ ORCH["编排中心
工作流检索与加载"]:::external
end
- subgraph HOST["宿主智能体:集成方 / Integrator Agent"]
+ subgraph HOST["宿主智能体"]
direction TB
- ENTRY["A2A 服务端入口
接收 Task-T,解析诊断意图"]:::host
- ADAPTER["宿主集成层
准备 AgentCard、Workflow、配置和业务上下文"]:::host
+ ENTRY["A2A 服务端入口
接收并校验入站任务"]:::host
+ ADAPTER["宿主集成层
准备 AgentCard、Workflow、配置和上下文"]:::host
subgraph ENGINE["嵌入式 Workflow Execution Engine SDK"]
direction TB
@@ -57,49 +57,45 @@ flowchart TB
EXT["流程外协议操作
ExtensionSender
Authorization-T / Notification-T"]:::sdk
end
- BIZ["集成方业务实现
业务接管、路由、汇总、持久化、通知处理"]:::host
+ BIZ["宿主业务实现
内容、路由、汇总、持久化与通知处理"]:::host
end
- subgraph SAMPLE["Demo 本地替代路径(非生产数据源)"]
+ subgraph SAMPLE["可选本地测试资源(非生产数据源)"]
direction LR
- LOCALCARD["本地 AgentCard JSON
WorkbenchAgentCatalog"]:::local
- LOCALPSOP["本地 PSOP fallback
仅用于离线演示"]:::local
+ LOCALCARD["本地 AgentCard JSON"]:::local
+ LOCALPSOP["本地 Workflow 测试数据"]:::local
end
- subgraph DOWNSTREAM["下游业务智能体"]
- OMCS["地市 OMC 智能体群
地市 1 OMC | 地市 2 OMC"]:::agent
+ subgraph DOWNSTREAM["被调度智能体"]
+ AGENTS["一个或多个被调度智能体"]:::agent
end
- WAIMO <-->|" Task-T 诊断任务 / 汇总 artifact "| ENTRY
- ENTRY -->|" 运行意图 "| ADAPTER
- REG -->|" 生产:查询 AgentCard "| LOAD
- ORCH -->|" search / load PSOP "| LOAD
- LOCALCARD -.->|" Demo 替代注册中心 "| ADAPTER
- LOCALPSOP -.->|" Demo 检索失败时 "| ADAPTER
- LOAD -->|" 发现结果交给宿主 "| ADAPTER
+ CALLER <-->|"A2A 任务 / 最终结果"| ENTRY
+ ENTRY -->|"已校验的意图与输入"| ADAPTER
+ REG -->|"发现 AgentCard"| LOAD
+ ORCH -->|"检索 / 加载 Workflow"| LOAD
+ LOCALCARD -.->|"开发测试数据"| ADAPTER
+ LOCALPSOP -.->|"开发测试数据"| ADAPTER
+ LOAD -->|"发现结果"| ADAPTER
ADAPTER -->|" Workflow + AgentCards "| RUN
RUN <-->|" 业务决策与执行结果 "| CALLBACK
CALLBACK <-->|" 回调 "| BIZ
ADAPTER -->|" 独立业务时机 "| EXT
- RUN <-->|" 向两地市并行 Task-T
必要时 Negotiation-T / 返回诊断结果 "| OMCS
- EXT <-->|" Authorization-T 一次性授权
Notification-T 长连接订阅与结果 SSE "| OMCS
- BIZ -->|" 汇总结果 "| ENTRY
+ RUN <-->|"Task-T;必要时 Negotiation-T"| AGENTS
+ EXT <-->|"Authorization-T / Notification-T"| AGENTS
+ BIZ -->|"最终业务结果"| ENTRY
```
-上图中的 SDK 是 **嵌入宿主智能体进程的库**,不是独立部署的编排服务。当前
-`SpringSpnDemo` 的宿主是集成方智能体,实际责任链如下:
+上图中的 SDK 是 **嵌入宿主智能体进程的库**,不是独立部署的编排服务:
-1. WAIMO 通过 A2A Task-T 调用集成方的服务端入口。
-2. 集成方准备执行输入:生产环境从注册中心获取下游 AgentCard,并根据任务意图从编排中心 搜索、加载 PSOP;SDK 提供可选的
- `RegistryClient` 和 `LoadPsop` 辅助 API,但何时发现、如何缓存、 失败策略均由宿主决定。
-3. 集成方把 `Workflow`、AgentCard、运行意图和业务回调交给 `ExecutePsop`。执行引擎遍历 DAG, 并行向两个地市 OMC 下发任务,并在必要时处理
- Negotiation-T;集成方通过 `ControlPoint` 接管 本地汇总、路由和澄清等业务操作。
-4. 集成方把汇总结果作为 Task-T artifact 和完成状态返回 WAIMO。
-5. Authorization-T 与 Notification-T 由集成方在各自业务时机通过 `ExtensionSender` 单独触发, 不属于 PSOP DAG,也不与工作流任务复用
- transport/runtime/context。
+1. 宿主智能体接收并校验入站 A2A 任务,准备执行意图和业务输入。
+2. 宿主智能体从注册中心获取被调度智能体的 AgentCard,并从编排中心加载 `Workflow`。
+ `RegistryClient` 和 `LoadPsop` 是可选辅助 API;发现时机、缓存与失败策略仍由宿主智能体负责。
+3. 宿主智能体把 `Workflow`、AgentCard、运行意图和回调交给 `ExecutePsop`。引擎遍历 DAG、并行下发就绪任务,并在需要时处理 Negotiation-T;`ControlPoint` 返回业务内容、本地结果、路由决策和协商回复。
+4. 宿主智能体消费 `ExecutionResult`,返回或持久化最终业务结果。
+5. Authorization-T 与 Notification-T 由宿主智能体在独立业务时机通过 `ExtensionSender` 触发,不属于 DAG,也不与工作流任务复用 transport/runtime/context。
-Demo 为保证离线可运行,`WorkbenchAgentCatalog` 从 classpath 加载 AgentCard;仅在编排中心搜索或 加载失败时使用本地 PSOP
-fallback。这两项是样例替代路径,不代表生产环境的数据来源或容灾策略。 图的可编辑源文件见
+本地 AgentCard 和 Workflow 只是开发测试资源,不代表生产发现或容灾策略。图的可编辑源文件见
[`docs/diagrams/workflow-engine-surrounding-systems.mmd`](../diagrams/workflow-engine-surrounding-systems.mmd)。
---
@@ -177,10 +173,9 @@ nextRound 或返回新 Propose。
Abort。 同一任务/会话/轮次的重复等待事件不会重复回调、重复提交;未变化状态通过 getTask 观察。
`maxNegotiationExchanges` 默认 3,是独立于 SDK context.maxRounds 的本地交互资源预算。 超时、预算耗尽、回调缺失均明确失败,不默认
Accept,也不自动生成 Abort。 Accept/Reject 的 SUBMITTED/WORKING ACK 仍需等待任务结果,不重发原命令。 业务发送 Abort 后,即使远端用
-COMPLETED 确认,也不能判为诊断成功。
+COMPLETED 确认,也不能判为任务成功。
-Authorization-T 和 Notification-T 是独立业务操作,不属于 DAG。宿主生成内容并使用独立发送器;失败不影响工作流。白名单仅影响
-OMC 自动抢通,订阅保持到业务主动关闭。
+Authorization-T 和 Notification-T 是独立业务操作,不属于 DAG。宿主生成内容并使用独立发送器;失败不影响工作流。授权策略只控制自身的业务操作,订阅保持到宿主主动关闭。
## 6. 条件路由
@@ -219,7 +214,7 @@ OMC 自动抢通,订阅保持到业务主动关闭。
sequenceDiagram
participant H as Host callbacks + A2A-T client
participant E as Workflow engine
- participant A as Remote agent
+ participant A as Dispatched agent
E->>H: onTask(TaskRequest + upstream window)
H->>H: Generate/validate final content
H-->>E: MessageContent
@@ -240,7 +235,7 @@ sequenceDiagram
sequenceDiagram
participant H as Host + A2A-T client
participant ES as Independent ExtensionSender
- participant A as OMC
+ participant A as Dispatched agent
H->>H: Generate final authorization content
H->>ES: sendAuthorization(agent, content)
ES->>A: One-shot authorization
@@ -251,7 +246,7 @@ sequenceDiagram
ES->>A: Independent long-lived stream
A-->>H: ACK via acknowledgement()
A-->>H: listener(handle, ReceivedMessage)
- H->>ES: handle.close() on recovery/cancel/shutdown
+ H->>ES: 终态事件 / 取消 / 停机时 handle.close()
ES-->>H: completion() after stream exits
```
@@ -259,7 +254,7 @@ sequenceDiagram
workflow-engine:A2A Java `1.2.0.Final`(REST/JSON-RPC/gRPC)、匹配的 gRPC runtime、
`net.openan.a2a-t.sdk:a2a-t-core:1.1.0`、Jackson、SLF4J。 纯引擎消费者不会传递引入 A2A-T client/server、LLM、prompt 或
-resources。 samples/宿主显式依赖 a2a-t-client,需要实现 OMC 接收端时另依赖 a2a-t-server。 注册中心和编排中心由宿主调用,可选择
+resources。宿主智能体显式依赖 a2a-t-client,校验接收内容的被调度智能体服务另依赖 a2a-t-server。注册中心和编排中心由宿主调用,可选择
RegistryClient/LoadPsop 辅助接口或自己的实现。 模板和 slot schema 来自锁定 SDK jar,样例不覆盖同名资源。
## 10. 设计决策总结
diff --git a/docs/zh/DEVELOPER_GUIDE.md b/docs/zh/DEVELOPER_GUIDE.md
index 202e0ee4..8e9bc938 100644
--- a/docs/zh/DEVELOPER_GUIDE.md
+++ b/docs/zh/DEVELOPER_GUIDE.md
@@ -14,8 +14,7 @@
```
-引擎会传递性引入 A2A 协议 SDK(`a2a-java-sdk-client`,含 REST、JSON-RPC、gRPC 传输) 和最小 A2A-T 核心(`a2a-t-core`
-)。生成内容的宿主显式依赖 a2a-t-client,OMC 接收端另引入 a2a-t-server。
+引擎会传递性引入 A2A 协议 SDK(`a2a-java-sdk-client`,含 REST、JSON-RPC、gRPC 传输) 和最小 A2A-T 核心(`a2a-t-core`)。生成 A2A-T 内容的宿主智能体显式依赖 a2a-t-client,校验接收内容的被调度智能体服务另引入 a2a-t-server。
## 2. 核心概念
@@ -61,7 +60,7 @@ ExecutionResult result = ExecutePsop.builder()
.psop(workflow)
.agentCards(agentCards)
.controlPoint(new MyControlPoint())
- .runtimeIntent("诊断SPN故障")
+ .runtimeIntent("分析服务异常")
.lang("zh")
.sslVerify(false)
.credentialsConfigPath("agent_credentials.json")
@@ -116,7 +115,7 @@ try (var client = new DefaultWorkflowEngineClient(agentCards, a2aRuntime,
new MyControlPoint(),
client,
new EventCallback(),
- "诊断故障",
+ "分析请求",
"zh"
);
ExecutionResult result = executor.run().join();
@@ -134,13 +133,13 @@ nextRound 或返回新 Propose。
Abort。 同一任务/会话/轮次的重复等待事件不会重复回调、重复提交;未变化状态通过 getTask 观察。
`maxNegotiationExchanges` 默认 3,是独立于 SDK context.maxRounds 的本地交互资源预算。 超时、预算耗尽、回调缺失均明确失败,不默认
Accept,也不自动生成 Abort。 Accept/Reject 的 SUBMITTED/WORKING ACK 仍需等待任务结果,不重发原命令。 业务发送 Abort 后,即使远端用
-COMPLETED 确认,也不能判为诊断成功。
+COMPLETED 确认,也不能判为任务成功。
### 6.2 工作流模型字段
| 字段 | 位置 | 含义 |
|-----------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `steps[].stepType` | `WorkflowStep` | `AllSuccess`(默认):所有子任务必须成功;`AnySuccess`:任一成功即可;`SelfLoop`:工作流智能体通过 `onSelfTask` 本地处理(不向命名智能体发送 A2A-T 消息)。 |
+| `steps[].stepType` | `WorkflowStep` | `AllSuccess`(默认):所有子任务必须成功;`AnySuccess`:任一成功即可;`SelfLoop`:宿主智能体通过 `onSelfTask` 本地处理(不向命名智能体发送 A2A-T 消息)。 |
| `steps[].subtasks[]` | `Task` | 每个含 `agent`、`skill`、`description`。每个子任务触发一次 `onTask`(或 SelfLoop 的 `onSelfTask`)调用。 |
| `steps[].next[]` | `List` | 分支目标。`step` = 下一步名称;`condition` = 规则文本。 |
| `steps[].layer` | `WorkflowStep` | 编排层级提示;实际就绪条件由 DAG 前驱关系决定。 |
@@ -167,7 +166,7 @@ AgentCard card = mapper.readValue(json, AgentCard.class);
```json
{
- "SPN Domain Agent": {
+ "Dispatched Agent": {
"bearerAuth": {
"login_url": "https://127.0.0.1:8080/auth/login",
"method": "POST",
@@ -225,9 +224,8 @@ WorkflowEngineClientConfig config = WorkflowEngineClientConfig.builder()
## 9. A2A-T 环境(.env)
-引擎不读取 A2A-T .env,也不创建 LLM client。业务回调需要 A2A-T 时, 宿主用自己的环境文件初始化 A2ATClient/A2ATServer,配置
-provider/model/key/base URL 和 A2AT_LANGUAGE。 样例的 a2atEnvPath 是宿主/Demo 配置,不是引擎 builder 参数。 OMC 凭据解密不与
-LLM 配置耦合:内置凭据模式通过 WorkflowEngineClientConfig.builder ().credentialEncryptionKey (key) 显式提供密钥, 再将配置好的
+引擎不读取 A2A-T .env,也不创建 LLM client。宿主智能体的业务回调需要 A2A-T 时,由宿主使用自有环境文件初始化 A2ATClient/A2ATServer,配置 provider/model/key/base URL 和 A2AT_LANGUAGE。样例中的 a2atEnvPath 不是引擎 builder 参数。被调度智能体凭据解密不与
+LLM 配置耦合:内置凭据模式通过 WorkflowEngineClientConfig.builder().credentialEncryptionKey (key) 显式提供密钥, 再将配置好的
engineClient 传给 ExecutePsop。自定义 AuthProvider 自行管理 token 和配置。 测试使用当前 SDK SPI 的离线
provider,不覆盖模板,也不是生产失败兜底。
@@ -279,11 +277,10 @@ public Flux execute(@PathVariable String psopId) {
### 协议日志 pretty 展示
协议日志默认以 pretty 形式展示:逐行打印头字段,缩进 JSON 正文。SSE 保留 id/event 等事件信息, JSON 在
-`=== SSE data (JSON display; not wire text) ===` 与 `=== End SSE data ===` 之间单独展示, 不再为展开后的每行 JSON 添加
+`=== SSE data(JSON display; not wire text) ===` 与 `=== End SSE data ===` 之间单独展示, 不再为展开后的每行 JSON 添加
`data:` 前缀。多个事件各自保留边界。
`WORKFLOW_ENGINE_PROTOCOL_PRETTY=false`(环境变量或同名 JVM 属性)可保留脱敏后的原始正文展示。 pretty 只影响显示(JSON 缩进及
SSE 数据区标记),不改实际发送字节、metadata、数值或扩展头; 原始观测正文仍保留,SSE pretty 展示不是可直接重放的抓包。 JSON
-字符串中的 `\\n` 保留转义,避免把业务字符串误写成非法 JSON;非 JSON/不完整正文保持原样。 日志仍执行脱敏和容量限制。Demo 启动记录
-`NEGOTIATION_DEMO enabled=..., city=...`; 本地默认 City1 协商、City2 直接诊断,`-Da2at.samples.negotiation=false` 关闭缺参演示。
-协商激活位于 `A2A-Extensions: .../Negotiation-T/v1`,不存在专用的 Negotiation-T HTTP 头; 正文检查对应 URI 的 metadata 和
+字符串中的 `\\n` 保留转义,避免把业务字符串误写成非法 JSON;非 JSON/不完整正文保持原样。日志仍执行脱敏和容量限制。验证协商流量时,
+检查 `A2A-Extensions: .../Negotiation-T/v1`;不存在专用的 Negotiation-T HTTP 头。正文检查对应 URI 的 metadata 和
`negotiationContext`,不要只检索大写 `NEGOTIATION-T`。
diff --git a/docs/zh/INTEGRATION_GUIDE.md b/docs/zh/INTEGRATION_GUIDE.md
index 9dd72fa2..87510315 100644
--- a/docs/zh/INTEGRATION_GUIDE.md
+++ b/docs/zh/INTEGRATION_GUIDE.md
@@ -38,15 +38,15 @@ A2A-T 工作流执行引擎是一个 Java SDK,用于基于 A2A 协议和 A2A-T
```java
Workflow workflow = Workflow.builder()
- .name("故障诊断")
+ .name("服务分析")
.steps(List.of(
WorkflowStep.builder()
- .name("diagnose")
+ .name("analyze")
.subtasks(List.of(
Task.builder()
- .agent("SPN Domain Agent")
- .skill("diagnosis")
- .description("诊断故障")
+ .agent("Dispatched Agent")
+ .skill("analysis")
+ .description("分析当前请求")
.build()))
.next(List.of(
JumpCondition.builder()
@@ -57,10 +57,10 @@ Workflow workflow = Workflow.builder()
.build(),
WorkflowStep.builder()
.name("merge")
- .stepType(StepType.SELF_LOOP) // 自环节点:工作台本地汇总,不发 A2A-T 给自己
+ .stepType(StepType.SELF_LOOP) // 宿主智能体本地步骤,不发送 A2A-T
.subtasks(List.of(
Task.builder()
- .agent("Transport Workbench Agent")
+ .agent("Host Agent")
.skill("aggregate")
.description("汇总结果")
.build()))
@@ -134,7 +134,7 @@ CompletableFuture execution = ExecutePsop.builder()
.psop(workflow)
.agentCards(cards)
.controlPoint(callbacks)
- .runtimeIntent("SPN跨城专线故障诊断与抢通")
+ .runtimeIntent("分析服务请求")
.lang("zh")
.credentialsConfigPath("credentials.json")
.sslVerify(true)
@@ -156,9 +156,8 @@ try {
### 5.1 .env 文件
-引擎不读取 A2A-T .env,也不创建 LLM client。业务回调需要 A2A-T 时, 宿主用自己的环境文件初始化 A2ATClient/A2ATServer,配置
-provider/model/key/base URL 和 A2AT_LANGUAGE。 样例的 a2atEnvPath 是宿主/Demo 配置,不是引擎 builder 参数。 OMC 凭据解密不与
-LLM 配置耦合:内置凭据模式通过 WorkflowEngineClientConfig.builder ().credentialEncryptionKey (key) 显式提供密钥, 再将配置好的
+引擎不读取 A2A-T .env,也不创建 LLM client。宿主智能体的业务回调需要 A2A-T 时,由宿主使用自有环境文件初始化 A2ATClient/A2ATServer,配置 provider/model/key/base URL 和 A2AT_LANGUAGE。样例中的 a2atEnvPath 不是引擎 builder 参数。被调度智能体凭据解密不与
+LLM 配置耦合:内置凭据模式通过 WorkflowEngineClientConfig.builder().credentialEncryptionKey (key) 显式提供密钥, 再将配置好的
engineClient 传给 ExecutePsop。自定义 AuthProvider 自行管理 token 和配置。 测试使用当前 SDK SPI 的离线
provider,不覆盖模板,也不是生产失败兜底。
@@ -168,15 +167,13 @@ provider,不覆盖模板,也不是生产失败兜底。
```json
{
- "SPN Domain Agent": {
+ "Dispatched Agent": {
"bearerAuth": {
- "login_url": "https://127.0.0.1:26335/rest/plat/smapp/v1/oauth/token",
- "method": "PUT",
+ "login_url": "https://agent.example.com/oauth/token",
+ "method": "POST",
"request_fields": {
- "grantType": "password",
- "userName": "admin",
- "value": "enc::",
- "ipaddr": "*"
+ "username": "service-account",
+ "password": "enc::"
},
"token_field": "accessSession",
"token_ttl": 3600
@@ -318,7 +315,7 @@ AgentCard 通过 `capabilities.extensions` 声明扩展点:
```json
{
- "name": "SPN Domain Agent",
+ "name": "Dispatched Agent",
"capabilities": {
"streaming": true,
"extensions": [
@@ -334,12 +331,12 @@ AgentCard 通过 `capabilities.extensions` 声明扩展点:
},
{
"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Authorization-T/v1",
- "description": "授权白名单",
+ "description": "授权操作",
"required": false
},
{
"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Notification-T/v1",
- "description": "结果通知订阅",
+ "description": "通知订阅",
"required": false
}
]
@@ -383,7 +380,7 @@ nextRound 或返回新 Propose。
Abort。 同一任务/会话/轮次的重复等待事件不会重复回调、重复提交;未变化状态通过 getTask 观察。
`maxNegotiationExchanges` 默认 3,是独立于 SDK context.maxRounds 的本地交互资源预算。 超时、预算耗尽、回调缺失均明确失败,不默认
Accept,也不自动生成 Abort。 Accept/Reject 的 SUBMITTED/WORKING ACK 仍需等待任务结果,不重发原命令。 业务发送 Abort 后,即使远端用
-COMPLETED 确认,也不能判为诊断成功。
+COMPLETED 确认,也不能判为任务成功。
```java
CompletableFuture sendAuthorization(String agentName, MessageContent content);
@@ -391,9 +388,7 @@ NotificationSubscription openNotification(String agentName, MessageContent conte
BiConsumer listener);
```
-宿主生成最终 Authorization-T/Notification-T 内容后调用上述接口;使用三类独立 transport/runtime/context。订阅监听器收到
-handle 与完整 ReceivedMessage,按业务收到抢通结果后关闭。handle.acknowledgement () 和 completion () 分别表示 ACK
-和真实流退出,不用它们的成功作为工作流前提。
+宿主智能体生成最终 Authorization-T/Notification-T 内容后调用上述接口;使用三类独立 transport/runtime/context。订阅监听器收到 handle 与完整 ReceivedMessage,在宿主定义的终态事件上关闭。handle.acknowledgement() 和 completion() 分别表示 ACK 和真实流退出,两者都不是工作流前提。
## 8. HTTPS 配置
@@ -439,30 +434,9 @@ dropped-capacity,禁用、截断、中断也有明确标记。 UTF-8 分片先
requestId 关联单次调用;工作流调用还带 executionId/logicalTaskId/attempt、 agent/contextId/channel,已知远端任务时附
remoteTaskId,均为本地日志字段,不污染协议 metadata。
-### 查看真实协商与协议日志
+### 验证协议与协商日志
-直接运行本地 SpringSpnDemo,默认 City1 缺参并协商、City2 参数完整直接诊断,无需添加 VM 参数。 Negotiation-T 扩展激活本身不强制协商;这是本地
-Demo 专门设置的场景,不是引擎默认行为。 要关闭本地缺参演示、让两城市都直接诊断,在 IDEA **VM options** 增加:
-
-```text
--Da2at.samples.negotiation=false
-```
-
-默认仅移除 City1 的 Task-T 任务对象;启用状态下增加 `-Da2at.samples.negotiation.city=city2` 或 `both`
-可演示 City2 或两城市同时缺参。宿主仍保留各城市的正确输入,原投诉上下文不变。预期链路是 `DEMO_NEGOTIATION` →
-`INPUT_REQUIRED / PROPOSE`
-→ 集成方 onNegotiation → `ACCEPT` → 两城市完成 → 一次汇总。 非内嵌 OMC 模式默认不注入缺参,显式设置
-`-Da2at.samples.negotiation=true` 也会被拒绝。 Demo 将开关传入当前 Spring 应用实例,不设置或修改 JVM 全局开关,不影响其他宿主。
-协议日志在控制台及以运行目录为基准的 `logs/spn-demo.log`。 本分支使用直连模式。
-
-离线重复验证:
-
-```powershell
-mvn -q -pl samples -am "-Dtest=SpringSpnDemoE2ETest" "-Dsurefire.failIfNoSpecifiedTests=false" test
-```
-
-每个测试类覆盖无 VM 参数的默认单城市协商、显式关闭、显式开启及两城市同时缺参。测试使用离线 LLM provider,但实际运行当前 SDK
-的 模板、校验和 HTTP/SSE;不是现网 OMC、平台或真实模型语义的验证。
+使用一个被调度智能体返回“`INPUT_REQUIRED` + 有效 Negotiation-T Propose”的受控测试。预期序列为:请求 → Propose → `onNegotiation` → 最终 Send/Stop → 任务终态。`PROTOCOL` 日志应显示实际观测到的请求/响应边界与关联字段,且不泄露凭据。AgentCard 声明 Negotiation-T 能力本身不会强制发起协商。
## 10. 事件回调
@@ -474,7 +448,7 @@ EventCallback callback = new EventCallback() {
public void onEvent(String eventType, Map data) {
switch (eventType) {
case EventType.STEP_START -> System.out.println("步骤开始: " + data.get("step"));
- case EventType.AGENT_STATUS_UPDATE -> System.out.println(
+ case EventType.TASK_STATUS_CHANGED -> System.out.println(
data.get("agent") + " 状态: " + data.get("state"));
case EventType.NEGOTIATION_REQUEST -> System.out.println(
"协商请求来自 " + data.get("agent"));
@@ -488,57 +462,47 @@ ExecutePsop.builder()
// ...
```
-常用事件类型:`STEP_START`、`STEP_COMPLETE`、`AGENT_REQUEST`、`AGENT_RESPONSE`、`NEGOTIATION_REQUEST`、`NEGOTIATION_RESOLVED`、
-`COMPLETE`、`ERROR`。
+常用事件类型:`START`、`STEP_START`、`TASK_REQUEST`、`TASK_RESPONSE`、
+`TASK_STATUS_CHANGED`、`STEP_COMPLETE`、`NEGOTIATION_REQUEST`、
+`NEGOTIATION_RESOLVED`、`NEGOTIATION_FAILED`、`ROUTE_DECISION`、
+`WORKFLOW_COMPLETE`、`COMPLETE`、`ERROR`、`CLOSE`。
## 11. 从编排中心加载工作流
```java
// 按意图搜索
List results = LoadPsop.search(
- "https://127.0.0.1:5001", "SPN跨城专线故障诊断", 5, null, false);
+ "https://orchestration.example.com", "分析服务请求", 5, null, true);
// 按 ID 加载完整工作流
Workflow workflow = LoadPsop.load(
- "https://127.0.0.1:5001", results.get(0).getWorkflowId(), null, false);
+ "https://orchestration.example.com", results.get(0).getWorkflowId(), null, true);
```
## 12. 自定义扩展
-直接构造最终 MessageContent (parts, metadata, extensions),由宿主管理扩展内容的生成/校验。无需注册引擎 handler 或 SDK
+直接构造最终 MessageContent(parts, metadata, extensions),由宿主管理扩展内容的生成/校验。无需注册引擎 handler 或 SDK
实例。A2atMessages.from 是 A2A-T metadata 复制辅助;非 A2A-T 扩展也可直接提供 metadata 和激活 URI。引擎不会仅因 AgentCard
声明而自动生成内容。
-## 远端 OMC 联调与启动校验
+## 13. 外部端点与启动责任
-### 编排中心 HTTPS 联调
+### 13.1 编排中心 HTTPS 联调
-Demo 将 `A2A_SSL_VERIFY`(Spring 属性 `a2a.ssl-verify`)传给 LoadPsop.search/load。
-受控开发环境可设置 `A2A_SSL_VERIFY=false` 或启动参数 `--a2a.ssl-verify=false`:
-执行引擎端不用下载或配置编排中心证书,搜索与加载连接均跳过证书链及主机名校验,
-包括自签名、缺少 SAN 或 SAN 不匹配的开发证书,并打印 `[Registry] INSECURE_TLS` 警告。
+`LoadPsop.search/load` 显式接收 `sslVerify` 参数。生产环境保持 `true`。受控开发环境可传入 `false`,仅对当前连接跳过证书链和主机名校验,并打印 `[Registry] INSECURE_TLS` 警告;不修改 JVM 全局 TLS 默认值。
“免配本地证书”不表示 HTTPS 服务端不需要证书,也不能绕过要求客户端证书的 mTLS。
该非验证模式不能确认服务端身份,存在中间人风险,仅用于受控联调。
生产保持 `true`,给服务端配置匹配实际 URL 的 SAN,并让运行 JVM 信任其 CA;
-引擎南向 `caCertsPath` 不会自动传给 LoadPsop。
+被调度智能体的 `caCertsPath` 不会自动传给 LoadPsop。
LoadPsop 不修改 JVM 全局 SSLContext、默认 SocketFactory 或 HostnameVerifier;
-引擎南向 HTTP/JSON-RPC 的 `false` 仍仅跳过证书链校验,自定义转发适配器的 TLS 由其 SDK/平台管理。
-
-### 下游 OMC 启动
+被调度智能体 HTTP/JSON-RPC 的 `false` 仍仅跳过证书链校验。
-在 IDEA 环境变量或本地 .env 中设置 `A2A_AGENT_CARD_LOCATIONS`,以逗号分隔外部 AgentCard 文件路径。
-运行 SpringSpnDemo 对接外部 OMC 时,显式设置 `A2A_EMBEDDED_OMC_ENABLED=false`,
-或使用 Program arguments `--a2a.embedded-omc-enabled=false`;不会为这些下游节点创建本地 JDK Server。
-持续接收任务和保持订阅应运行 SpringWorkbenchApplication,而不是执行一次后退出的 Demo。
+### 13.2 被调度智能体端点
-启用内嵌 OMC 时,Demo 在启动任意 OMC 前校验两张实际加载的地市卡片,并使用其地址启动。
-非本机地址、缺失卡片或非法 URL 会提前失败;回环地址和本机网卡地址都可绑定,
-不以“是否 127.0.0.1”判断远端。校验不探测 OMC,也不能判定本机某端口是否已有真实服务:
-即使外部 OMC 恰好部署在本机,也必须显式关闭内嵌服务。
-真实地址与凭据应放在本地外部文件,不覆盖仓库示例,不提交。
+引擎只消费 AgentCard,不启动被调度智能体服务。宿主智能体负责 AgentCard 发现、端点可达性校验与开发测试资源。使用外部管理的被调度智能体时,不得在相同端点上启动本地测试服务。生产 AgentCard 和凭据应保存在仓库外部。
-## 远端错误响应
+## 14. 远端错误响应
响应头成功不代表任务成功。HTTP 200 的 SSE data 也可能返回顶层错误对象,例如:
@@ -552,7 +516,7 @@ LoadPsop 不修改 JVM 全局 SSLContext、默认 SocketFactory 或 HostnameVeri
该识别以 SDK 实际交付的正文为限:真实 HTTP 非 2xx 可能先被 SDK/适配器作为 HTTP 或认证错误拒绝,
不保证可取得 problem 正文;没有该正文时仍走原有通用失败路径,不伪造 errorDetails。
错误不生成成功结果、不触发 onNegotiation,也不自动重新提交请求。400 应核对业务参数;
-429 应检查 OMC 活跃任务配额及已有任务/订阅,再由业务决定是否重试。
+429 应检查被调度智能体的容量、活跃任务和订阅,再由业务决定是否重试。
只有合法 Negotiation-T Propose 才进入协商回调。
直接调用客户端时可用 `RemoteProblemException.findIn(error)` 检查 cause 链(没有则返回 null);
@@ -563,17 +527,12 @@ LoadPsop 不修改 JVM 全局 SSLContext、默认 SocketFactory 或 HostnameVeri
预置授权、通知订阅的错误仍由其独立生命周期处理,不决定工作流执行结果。
协议 pretty 日志仅用于展示,不改写原始收到的错误对象。
-**当前双地市示例的失败流程**:两个诊断并行下发,一方失败后记录该方错误;
-已经下发的另一方继续等待结果或超时。当前批次结束后返回 `success=false`,
-不进入 `merge_analysis`,也不调用该节点的 onSelfTask/LLM。成功地市的结果留在 history 中,
-不冒充整体诊断结论。此处没有自动重试、排队或“部分成功后汇总”策略;
-通用引擎的 AnySuccess 节点仍按原有任一成功规则执行。
+对于 `ALL_SUCCESS` 步骤,一个任务失败不会追溯取消已下发的并行任务;引擎在另一任务返回或超时后报告 `success=false`。后续步骤不执行,成功的并行结果保留在 history,不伪造整体成功结果。引擎不自动重试、排队或部分成功汇总;`ANY_SUCCESS` 节点仍遵循声明的任一成功语义。
-集成方通过 TASK_RESPONSE 事件及时观察任务失败,或在 `onFinish(result, events)`
+宿主智能体通过 TASK_RESPONSE 事件及时观察任务失败,或在 `onFinish(result, events)`
检查 `result.history`;onTask 是发送前准备消息的回调,不是失败重试回调。
引擎节点事件带 executionId;TASK_RESPONSE 和 history 还带逻辑 taskId。
-Demo 的北向 A2A 任务返回 FAILED,状态消息包含失败地市的 errorCode、error 和 errorDetails,
-不产生成功汇总 artifact。整个演示退出时会关闭订阅;常驻宿主中的订阅由独立生命周期管理。
+宿主智能体决定如何向调用方暴露失败,但不得把失败执行转换为成功 artifact。常驻宿主以独立生命周期管理通知订阅。
**日志职责**:PROTOCOL 记录已观察到的请求/响应,按 requestId 关联并发报文;
REMOTE_PROBLEM 记录通信层识别的远端拒绝,TASK_FAILED 记录执行 ID、节点、逻辑任务 ID、
@@ -582,14 +541,14 @@ contextId 与 executionId,供跨层定位。已识别的远端问题使用 WAR
未知异常保留 ERROR 堆栈;错误详情中的已知凭据及 Bearer/Basic 值脱敏。
日志开关、pretty 展示和观察回调不会改变任务结果。
-## 13. 你需要使用的接口一览
+## 15. 接口一览
| 接口/类 | 用途 |
|--------------------------------------------------------|---------------------------------------------------------------|
| `ExecutePsop.Builder` | 工作流执行入口 |
| `ControlPoint` / `DefaultControlPoint` | 业务决策实现(onTask、onSelfTask、onRoute、onNegotiation 等) |
| `WorkflowEngineClient` / `DefaultWorkflowEngineClient` | 工作流发送(sendMessage、认证、扩展) |
-| `ExtensionSender` / `DefaultExtensionSender` | 前置授权请求与长连接通知订阅 |
+| `ExtensionSender` / `DefaultExtensionSender` | 独立 Authorization-T 操作与 Notification-T 长连接订阅 |
| `A2ATransport` | 共享通信层(A2A Java 客户端 runtime、认证、SSE 消费) |
| `WorkflowEngineClientConfig` | 配置(SSL、认证、A2A-T、协商轮数、自定义 Handler) |
| `AuthProvider` | 自定义认证 |
diff --git "a/docs/zh/\344\270\232\345\212\241\346\265\201.md" "b/docs/zh/\344\270\232\345\212\241\346\265\201.md"
deleted file mode 100644
index f11f9806..00000000
--- "a/docs/zh/\344\270\232\345\212\241\346\265\201.md"
+++ /dev/null
@@ -1,64 +0,0 @@
-# SPN 专线投诉诊断业务流
-
-## 集成方业务边界(2026-08-27 校准)
-
-1. WAIMO 通过 Task-T 将一个投诉诊断任务下发给集成方。集成方使用 SDK 校验并提取 投诉参数,搜索/加载工作流。
-2. 工作流向两个不同地市 OMC 并行下发含地市诊断输入的 Task-T。两路结果都完成后, 集成方 join 节点仅执行一次汇总分析,把真实汇总结果返回
- WAIMO。
-3. 集成方到 OMC 通过直连通道下发 Task-T 诊断任务。
-4. Authorization-T 和 Notification-T 由集成方在各自业务时机独立触发,不是工作流 DAG 节点。Task、Authorization、Notification
- 使用不同的 transport/runtime/context/通道。
-5. OMC 对诊断后的抢通方案做白名单精确匹配;命中后自动执行,并通过已建立的 Notification-T 长连接上报结果。集成方收到抢通结果后关闭对应订阅;ACK
- 不是抢通结果。
-
-### 表格一:专线业务投诉诊断接口(核心诊断)
-
-| 项目 | 详细说明 |
-|:--------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **接口名称** | 专线业务投诉诊断接口 |
-| **协议/模式** | A2A-T Task-T |
-| **调用方向** | 集成方智能体 -> SPN Domain Agent(OMC) |
-| **功能描述** | 集成方智能体下发诊断请求,OMC完成网络侧故障根因诊断并返回结果。 |
-| **请求参数** | **必填:**
1. 任务对象:业务名称、业务标识或接入端口三者至少一个
2. 投诉分类(专线质差 / 专线中断)
3. OSS 侧事件流水号
**可选:**
4. 问题发生时间
5. 投诉详情 |
-| **响应内容** | Task-T 只返回本地诊断结论:
1. 诊断结果
2. 故障根因/根因位置与描述
3. 修复建议。抢通方案和执行结果不拼入 Task-T 响应,统一走独立 Notification-T 订阅通道。 |
-
-### 表格二:专线业务投诉诊断协商接口(参数修正)
-
-| 项目 | 详细说明 |
-|:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **接口名称** | 专线业务投诉诊断协商接口 |
-| **协议/模式** | A2A-T Negotiation-T |
-| **调用方向** | SPN Domain Agent(OMC) <--> 集成方智能体(双向) |
-| **功能描述** | 当OMC发现任务参数有误或缺失时,反向发起协商,请求集成方智能体补全或修正信息。 |
-| **协商请求(OMC→集成方)** | **触发条件:**
1. 必选参数缺失(如任务对象、投诉分类或 OSS 侧事件流水号)
2. 参数取值错误(如投诉分类不在枚举范围内)
3. 参数语义校验错误(如应为逻辑口却下发了物理口)
**请求内容:** 明确指出缺失/错误的参数及修正建议。 |
-| **协商响应(集成方→OMC)** | 集成方智能体理解协商请求,返回强类型 Accept / Reject / Abort。Information Reject 必须对每个无法提供的请求信息项逐项说明原因,不能只返回一个汇总拒绝原因。 |
-| **协商完成(OMC)** | OMC确认协商信息完成后,继续执行诊断任务,并最终返回诊断结果(同表格一响应内容)。 |
-
-### 表格三:业务抢通消息订阅与上报接口(结果通知)
-
-| 项目 | 详细说明 |
-|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **接口名称** | 业务抢通消息订阅接口 / 消息上报 |
-| **协议/模式** | A2A-T Notification-T |
-| **功能描述** | 用于集成方订阅抢通结果,以及OMC通过SSE流主动上报抢通结果消息。 |
-| **订阅请求(集成方→OMC)** | **包含内容:**
1. 消息主题
2. 上报消息数据格式(需包括:投诉事件流水号、分段业务标识、抢通方案执行结果、抢通方案详情) |
-| **订阅响应(OMC→集成方)** | **成功:** Notification-T metadata 返回“订阅结果:成功”,任务进入 `WORKING`。
**失败:** 返回 `FAILED` 及失败原因。 |
-| **消息上报(OMC→集成方)** | 基于订阅时指定的数据格式,通过 **SSE** 先推送 `recovery-plan`(未启动),白名单命中并执行后再推送 `recovery-result`(已结束);结果、取消或宿主关闭才结束通道。 |
-
-### 表格四:专线业务抢通授权接口(自动执行授权)
-
-| 项目 | 详细说明 |
-|:---------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **接口名称** | 专线业务抢通授权接口 |
-| **协议/模式** | A2A-T Authorization-T |
-| **功能描述** | 集成方向 OMC 新增、修改、删除或查询抢通授权,OMC 据有效白名单决定是否自动执行抢通。 |
-| **授权请求(集成方→OMC)** | **包含内容:**
1. 操作类型:新增/修改/删除/查询授权策略
2. SDK 模板所定义的策略列表或查询条件 |
-| **授权响应(OMC→集成方)** | artifact 的 Authorization-T metadata 返回“授权操作执行结果”;新增/查询成功时还返回策略标识和策略列表,删除必须精确匹配策略标识。失败返回 `FAILED` 且保留原策略。 |
-| **后置操作(OMC内部)** | 授权成功后,当OMC在执行诊断后的业务抢通时,若匹配到已授权项,则**自动执行**业务抢通,并依据**表格三**的订阅通道上报抢通结果消息。 |
-
-## 引擎回调与依赖结果
-
-上述场景使用同一套业务回调:onTask 返回最终 MessageContent,引擎封装信封并发送;onSelfTask 返回 TaskResult。
-多地市、多任务的输出按步骤分组保留于 WorkflowInput,contextFrom 选择是否聚合,当前 instruction 不拼历史。 宿主自行调用 A2A-T
-SDK 生成协商回复,onNegotiation 返回 Send (content) 或 Stop;引擎只管理会话关联和等待。
-[业务回调集成契约](BUSINESS_CALLBACKS.md) 给出字段、最终内容与上下文选择的完整规则。
diff --git "a/docs/zh/\351\233\206\346\210\220\346\226\271\345\234\272\346\231\257\346\216\245\345\205\245\346\214\207\345\215\227.md" "b/docs/zh/\351\233\206\346\210\220\346\226\271\345\234\272\346\231\257\346\216\245\345\205\245\346\214\207\345\215\227.md"
deleted file mode 100644
index 9e20cd08..00000000
--- "a/docs/zh/\351\233\206\346\210\220\346\226\271\345\234\272\346\231\257\346\216\245\345\205\245\346\214\207\345\215\227.md"
+++ /dev/null
@@ -1,798 +0,0 @@
-# 工作流执行引擎 SDK 集成方场景接入指南
-
-> 交付基线:workflow engine `1.0.0`,A2A-T SDK
-> `1.1.0`(commit `e42c83acce3e5ac2c245d36546ced0fa017b2b58`),
-> A2A Java SDK `1.2.0.Final`。本执行引擎是首发版本,
-> 只支持该当前 A2A-T API/资源基线,不提供旧 SDK 状态机接口或旧报文 fallback。
-
-> 本文中的集成方是宿主智能体。代码示例保留 Workbench* 类名与 AgentCard 标识,便于对应真实源码;这些名称不构成 SDK 的业务绑定。
->
-> 面向集成方研发团队。本文档以 SPN 专线故障跨城协同诊断联创场景为例,从集成方视角说明接入 Workflow 执行引擎 SDK 的完整路径。
->
-> 业务基线:上游为 WAIMO;集成方接收 Task-T 后加载工作流,并行调度两地市 OMC,
-> 两路完成后汇总一次并返回 WAIMO。Authorization-T 和 Notification-T 是集成方在
-> 独立时机触发的协议操作,不是工作流 DAG 节点。
-
----
-
-## 1. 场景全景
-
-### 1.1 业务拓扑
-
-周边系统与 SDK 的可编辑架构图见 [架构设计](DESIGN.md)。
-
-集成方是运行时编排宿主,执行引擎 SDK 嵌入集成方进程,不是独立部署的服务。编排中心负责 PSOP 的存储、检索和加载,注册中心负责
-AgentCard 的发布和查询;集成方负责在适当时机获取这些 输入并交给 SDK。它接收 WAIMO 通过 A2A-T 下发的原始诊断任务,并行编排下游多地市
-SPN Domain Agent(OMC)执行故障根因诊断,汇总结果后上报。
-
-`SpringSpnDemo` 为便于离线运行,默认从 classpath 加载 AgentCard,并在编排中心不可用时使用本地 PSOP fallback。真实 OMC 联调可用
-`A2A_AGENT_CARD_LOCATIONS` 指定逗号或分号分隔的
-`classpath:`、`file:` URI 或绝对路径;显式文件缺失或内容无效时启动立即失败。这是样例特例, 生产集成应以注册中心和编排中心为数据源,并自行定义受控的失败策略。
-
-### 1.2 四类 A2A-T 交互
-
-SDK 在该场景中承载四类 A2A-T 交互,对应业务流文档的四个接口:
-
-| 序号 | A2A-T 扩展 | 业务含义 | 发起方 | 时机 |
-|------|-----------------|-------------------|-------------------------------------------------|------------------------------|
-| 1 | Task-T | 下发诊断任务 | 集成方 -> OMC | 工作流执行中 |
-| 2 | Negotiation-T | 参数修正协商 | OMC <-> 集成方 | OMC 发现参数缺失时反向发起 |
-| 3 | Authorization-T | 抢通授权 | 集成方 -> OMC | 工作流启动前,前置预定位 |
-| 4 | Notification-T | 抢通结果订阅/上报 | 集成方 -> OMC (订阅) / OMC -> 集成方 (SSE 上报) | 工作流启动前订阅,执行中上报 |
-
-序号 1、2 在工作流执行过程中发生,由 SDK 的 `WorkflowEngineClient` 承载。序号 3、4 是流程外的独立操作,Demo 只是在工作流启动前尝试,由
-SDK 的 `ExtensionSender` 承载;这个时间顺序不构成因果依赖,授权或订阅失败均不得阻止后续 Task-T 工作流。各路径使用同一类底层传输组件
-`A2ATransport`,但 runtime、context 和生命周期互相隔离:工作流发送使用任务级 transport,Notification-T 订阅使用集成方级
-transport,避免工作流结束时关闭长连接。
-
-### 1.3 SDK 自动处理 vs 集成方实现
-
-| 引擎承担 | 集成方承担 |
-|---------------------------------|---------------------------------------|
-| DAG、并行、任务关联、信封与传输 | onTask 返回最终 MessageContent |
-| 上游来源选择、完整响应组装 | 根据业务需要映射上下游输入 |
-| 协商关联、去重、等待 | SDK 生成/校验回复,返回 Send 或 Stop |
-| 认证机制与 Header 注入 | 凭据来源或自定义 AuthProvider |
-| 独立授权发送与订阅生命周期 | SDK 生成授权/订阅内容、处理业务结果 |
-
-A2A-T client/server、模板、schema、LLM 初始化属于宿主; workflow-engine 仅依赖 a2a-t-core,不管理内容生成。
-
-## 2. 集成全景图
-
-集成方集成 SDK 共需完成六步。先总后分,每步后续均有详细说明和代码示例。
-
-```mermaid
-graph LR
- S1["步骤 1
引入 Maven 依赖"]
- S2["步骤 2
准备配置文件
AgentCard + 认证 + .env"]
- S3["步骤 3
实现 AgentExecutor
A2A 服务端入口"]
- S4["步骤 4
实现 ControlPoint
工作流决策回调"]
- S5["步骤 5
组装运行管线
发现输入 -> 执行工作流 / 独立扩展"]
-
- S1 --> S2 --> S3 --> S4 --> S5
-```
-
-职责分布:
-
-| 组件 | 集成方实现 | SDK 提供 | 说明 |
-|-------------------------|---------------|-----------------|------------------------------------------------------------------|
-| `AgentExecutor` | 是 | 否 | 集成方的 A2A 服务端入口,接收 上层系统 下发的任务 |
-| `ControlPoint` | 是 (4 个方法) | 接口 + 默认实现 | 工作流决策: 任务下发、自处理、路由、协商 |
-| `AuthProvider` | 是 (可选) | 接口 | 自定义认证头注入 (企业 SSO 等),与 credentials.json 二选一或组合 |
-| `WorkbenchOrchestrator` | 是 | 否 | 组装编排管线的业务类 |
-| `WorkflowEngineClient` | 否 | 是 | 工作流发送门面 (Task-T + 协商循环) |
-| `ExtensionSender` | 否 | 是 | 前置预定位门面 (授权 + 订阅) |
-| `A2ATransport` | 否 | 是 | 共享传输层 (HTTP + 认证 + SSE) |
-| `ExecutePsop` | 否 | 是 | 高层运行器 (生命周期 + 事件 + 持久化) |
-
----
-
-## 3. 逐步集成
-
-### 步骤 1: 引入 Maven 依赖
-
-集成方项目 `pom.xml` 中添加:
-
-```xml
-
- dev.openan.workflow.sdk
- workflow-engine
- 1.0.0
-
-```
-
-如果集成方基于 Spring Boot 且需要 SDK 自动装配 A2A 服务端 (AgentCard 加载、REST 控制器、事件总线):
-
-```xml
-
- dev.openan.workflow.sdk
- spring-boot-starter
- 1.0.0
-
-```
-
-Spring Boot starter 会自动注册 `AgentCard`、`RequestHandler`、`RestHandler`、`A2AController` 等 Bean。集成方只需提供一个
-`AgentExecutor` 实现类标注 `@Component` 即可。
-
-### 步骤 2: 准备配置文件
-
-集成需要三类配置文件。
-
-#### 2.1 AgentCard (集成方自身的)
-
-集成方作为 A2A 服务端,需要声明自己的 AgentCard。该文件告知 上层故障系统如何调用集成方:
-
-```json
-{
- "name": "Transport Workbench Agent",
- "description": "传输工作台智能体: 接收上层诊断任务,编排多地市 OMC 执行诊断并汇总",
- "version": "1.0.0",
- "capabilities": {
- "streaming": true,
- "extensions": [
- {"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Task-T/v1", "required": false},
- {"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/NEGOTIATION-T", "required": false},
- {"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Authorization-T/v1", "required": false},
- {"uri": "https://projects.tmforum.org/a2aproject/telecommunication/extensions/Notification-T/v1", "required": false}
- ]
- },
- "defaultInputModes": ["text/plain"],
- "defaultOutputModes": ["text/plain"],
- "skills": [
- {
- "id": "cross-city-fault-diagnosis",
- "name": "跨城故障协同诊断",
- "description": "协同多地市 OMC 执行专线故障诊断并汇总结果",
- "tags": ["SPN", "fault-diagnosis"]
- }
- ],
- "supportedInterfaces": [
- {"protocolBinding": "HTTP+JSON", "url": "https://工作台地址/a2a/json"}
- ]
-}
-```
-
-下游 SPN Domain Agent 的 AgentCard 由厂商提供或从注册中心获取。集成方只需加载它们用于 SDK 通信。关键信息是 `name` (SDK
-中用此名称寻址) 和 `supportedInterfaces.url` (通信地址)。
-
-下游 AgentCard 的安全字段语义:
-
-- `securitySchemes` 表示智能体支持的认证方式,不等于强制要求
-- `securityRequirements` 表示当前对接必须满足的认证方式;缺失、`null` 或 `[]` 都表示不启用内置凭证认证
-- 如果集成方通过 `AuthProvider` 自行获取 OMC Token,推荐配置 `"securityRequirements": []`,`securitySchemes` 可以保留为能力声明
-- 如果希望内置 credentials 认证生效,`securityRequirements` 必须非空,并引用 `securitySchemes` 中声明的方案名
-
-AuthProvider-only 场景的下游 AgentCard 安全字段示例:
-
-```json
-{
- "securitySchemes": {
- "bearerAuth": {
- "httpAuthSecurityScheme": {
- "scheme": "Bearer"
- }
- }
- },
- "securityRequirements": []
-}
-```
-
-#### 2.2 认证配置
-
-SDK 提供两层认证机制,可单独使用或组合使用:
-
-| 认证方式 | 适用场景 | 配置方式 |
-|---------------------|---------------------------------------------------------------------------------|----------------------------|
-| Credentials 认证 | Agent 提供登录接口并使用 Bearer 或自定义认证头 | 提供 credentials.json 文件 |
-| 自定义 AuthProvider | 集成方自管 Token、企业 SSO、非标准认证头,或经自定义网关/协议转发获取 OMC Token | 实现 `AuthProvider` 接口 |
-
-**合并规则:** 两种认证来源分别生成 Header 后合并。`AuthProvider` 可以作为唯一认证来源;如果两者对同名 Header 生成不同值,引擎会抛出
-`SecurityException`。
-
-##### 方式一: Credentials 认证 (credentials.json)
-
-SPN Domain Agent (OMC) 使用 Bearer 认证。SDK 的 `AgentAuthManager`
-自动调用登录接口获取令牌并缓存刷新,集成方只需提供凭证文件:
-
-```json
-{
- "SPN Domain Agent City1": {
- "bearerAuth": {
- "login_url": "https://omc-city1地址/rest/plat/smapp/v1/oauth/token",
- "method": "PUT",
- "request_fields": {
- "grantType": "password",
- "userName": "admin",
- "value": "enc:加密后的密码",
- "ipaddr": "*"
- },
- "token_field": "accessSession",
- "token_ttl": 3600
- }
- },
- "SPN Domain Agent City2": {
- "bearerAuth": {
- "login_url": "https://omc-city2地址/rest/plat/smapp/v1/oauth/token",
- "method": "PUT",
- "request_fields": {
- "grantType": "password",
- "userName": "admin",
- "value": "enc:加密后的密码",
- "ipaddr": "*"
- },
- "token_field": "accessSession",
- "token_ttl": 3600
- }
- }
-}
-```
-
-键名必须与 AgentCard 的 `name` 字段一致。SDK 依据此键名匹配 AgentCard 与凭证。密码字段 `value` 使用 `enc:` 前缀标识加密内容,SDK
-的 `CredentialCrypto` 自动解密 (密钥通过 `A2AT_CRED_KEY` 环境变量注入,32 字节 hex)。
-
-**密码加密方法:**
-
-加密格式为 `enc::`,算法为 AES-256-GCM。SDK 提供两种加密方式:
-
-**方式一: 命令行工具 (推荐)**
-
-```bash
-# 1. 生成 32 字节密钥 (仅首次,妥善保存)
-openssl rand -hex 32
-# 输出示例: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
-
-# 2. 设置密钥环境变量
-set A2AT_CRED_KEY=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
-
-# 3. 加密密码 (输出 enc:... 格式,直接复制到 credentials.json)
-java -cp workflow-engine.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123"
-# 输出: enc:xxxxxxxxxxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
-
-# 也可以将密钥作为第二个参数传入
-java -cp workflow-engine.jar dev.openan.workflow.engine.client.CredentialCrypto "Admin@123" a1b2c3d4...
-```
-
-**方式二: 代码调用**
-
-```java
-// 设置密钥后调用
-System.setProperty("A2AT_CRED_KEY", "a1b2c3d4e5f6...");
-String encrypted = CredentialCrypto.encrypt("Admin@123");
-// encrypted = "enc:xxxxxxxxxxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
-```
-
-将输出的 `enc:...` 值直接填入 credentials.json 的 `value` 字段:
-
-```json
-{
- "request_fields": {
- "value": "enc:xxxxxxxxxxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
- }
-}
-```
-
-运行时 SDK 从 `A2AT_CRED_KEY` 环境变量读取密钥自动解密。密钥来源优先级:显式 credentialEncryptionKey > 操作系统
-A2AT_CRED_KEY > 同名 JVM 属性。引擎不自动读取宿主 LLM .env。
-
-凭证配置支持的完整字段:
-
-| 字段 | 类型 | 默认值 | 说明 |
-|----------------------|--------|--------------------|-------------------------------------------------------------------|
-| `login_url` | String | 必填 | 登录接口地址 |
-| `method` | String | `POST` | HTTP 方法 |
-| `content_type` | String | `application/json` | 请求 Content-Type |
-| `request_fields` | Map | - | 请求体字段 (替代 legacy 的 username/password) |
-| `token_field` | String | `accessSession` | 从响应 JSON 提取令牌的路径 (支持点号嵌套,如 `data.access_token`) |
-| `token_ttl` | int | 3600 | 令牌缓存有效期 (秒),到期前 60 秒自动刷新 |
-| `auth_header` | String | `Authorization` | 自定义认证头名称 (设置后引擎改用该 Header 注入凭证) |
-| `auth_header_prefix` | String | `Bearer ` | 自定义认证头前缀 (与 `auth_header` 配合使用) |
-
-##### 方式二: 自定义 AuthProvider
-
-当认证机制不属于标准 Bearer/API Key,或令牌来自外部身份提供商 (如企业 SSO) 时,实现 `AuthProvider` 接口:
-
-```java
-public interface AuthProvider {
- /**
- * 为发送消息注入认证头。
- *
- * @param agentName 目标 Agent 名称 (匹配 AgentCard.name)
- * @param agentCard Agent 卡片 (securitySchemes 可能为空)
- * @param headers 可变的请求头 Map,直接写入认证信息
- */
- void applyAuth(String agentName, AgentCard agentCard, Map headers);
-}
-```
-
-通过 `WorkflowEngineClientConfig` 注册:
-
-```java
-WorkflowEngineClientConfig config = WorkflowEngineClientConfig.builder()
- .authProvider((agentName, agentCard, headers) -> {
- // 从企业 SSO 获取令牌
- String token = ssoClient.getToken(agentName);
- headers.put("Authorization", "Bearer " + token);
- })
- .sslVerify(sslVerify)
- .build();
-```
-
-`AuthProvider` 在 **每次消息发送时**被调用,无论 AgentCard 是否声明了 `securitySchemes`。典型用途:
-
-- AgentCard 无 `securitySchemes` 但服务端仍要求认证
-- 认证机制非 A2A 标准类型 (Bearer、API Key、OAuth2)
-- 令牌来自企业统一身份认证平台 (如企业 SSO)
-- 需要注入非标准认证头 (如 `X-Custom-Token`、`X-Request-Signature`)
-- 消息经自定义 `A2AJavaClientRuntime` 协议转发时,认证 Header 已由 `AuthProvider` 注入 `callContext`,转发实现原样复制即可
-
-如果 Authorization-T / Notification-T 前置消息和工作流 Task-T 消息都需要认证,请把同一个 `AuthProvider` 配置到
-`extensionTransport` 和 `taskTransport` 的 `WorkflowEngineClientConfig` 中。
-
-##### 两种方式组合使用
-
-如果部分 Agent 使用 credentials 认证、部分使用自定义认证,或需要在 credentials 基础上追加额外头:
-
-```java
-WorkflowEngineClientConfig config = WorkflowEngineClientConfig.builder()
- .credentialsConfigPath(credentialsPath) // credentials 认证
- .authProvider((agentName, agentCard, headers) -> {
- // 追加企业级追踪头
- headers.put("X-Trace-Id", TraceContext.current().getTraceId());
- })
- .build();
-```
-
-`AuthProvider` 与 credentials 认证分别生成 Header 后再合并。`AuthProvider` 可以作为唯一认证来源,即使 AgentCard 的
-`securityRequirements` 非空且未配置 credentials。不同名称的 Header 可同时使用;如果两者对同名 Header(例如 `Authorization`
-)给出不同值,引擎会立即抛出 `SecurityException`,避免认证信息被顺序覆盖。建议每个认证 Header 只配置一个来源。
-
-#### 2.3 A2A-T SDK 环境 (.env)
-
-A2A-T SDK 的 LLM 配置 (用于 Task-T 提示词生成、场景识别等)。最小配置:
-
-```ini
-A2AT_LLM_PROVIDER=openai
-A2AT_LLM_MODEL=deepseek-chat
-A2AT_LLM_API_KEY=你的API密钥
-A2AT_LLM_BASE_URL=https://api.deepseek.com
-A2AT_LANGUAGE=zh-CN
-```
-
-集成方业务代码通过此配置初始化 A2A-T SDK,生成和校验协议内容。执行引擎不读取 LLM 配置,也不生成提示词。示例在配置缺失或生成失败时明确报错,不把原始输入降级为 Task-T 正文。
-
-### 步骤 3: 实现 AgentExecutor
-
-集成方实现 A2A Java AgentExecutor.execute (RequestContext, AgentEmitter) 接收 WAIMO, 并在自己的入口用
-A2ATServer.validateTaskPromptAndDataFilling 校验 Task-T。 metadata 中的 Task-T 正文与 templateUri 是内容入口,不用 parts
-长短决定读取哪个正文。 然后搜索工作流、获取 AgentCards、调用执行引擎,并通过 A2A emitter 返回 artifact/终态。
-
-可编译的完整实现见 samples 的 SpringWorkbenchExecutor、WorkbenchTaskInputParser、 WorkbenchOrchestrator;Spring starter
-只提供协议入口与生命周期,不替宿主决定业务或模板。 入口取消应停止对应本地执行并清理宿主资源;远端已提交任务需按业务策略显式取消。
-不要把错误或空结果伪装成成功诊断。
-
-### 步骤 4: 实现 ControlPoint
-
-```java
-interface ControlPoint {
- CompletableFuture onTask(TaskRequest request);
- CompletableFuture onSelfTask(TaskRequest request);
- CompletableFuture onRoute(RouteRequest request);
- CompletableFuture onNegotiation(NegotiationRequest request);
-}
-```
-
-onTask 返回最终 parts/metadata/extensions,引擎封装发送,不再生成或改写内容。 onSelfTask 返回本地 TaskResult;onRoute
-选择允许的候选;onNegotiation 返回 Send 或 Stop。 未实现的回调明确失败,不回显成功、不选首分支、不自动同意。
-字段与完整示例见 [业务回调集成契约](BUSINESS_CALLBACKS.md)。
-
-```java
-ControlPoint callbacks = ControlPoint.builder()
- .onTask(request -> CompletableFuture.completedFuture(
- MessageContent.text(request.getInstruction())))
- .onSelfTask(request -> CompletableFuture.completedFuture(
- TaskResult.success(List.of(Map.of(
- "sourceResults", request.getWorkflowInput().upstreamResults())))))
- .onRoute(request -> CompletableFuture.failedFuture(
- new IllegalStateException("Supply a routing policy for " + request.stepName())))
- .onNegotiation(request -> CompletableFuture.completedFuture(
- new NegotiationReply.Stop("manual.required", "Manual confirmation required")))
- .build();
-```
-
-生产 Task-T 生成及协商示例见 WorkbenchControlPoint / examples.negotiation.NegotiationStrategy;上述仅是普通 A2A 内容示例。
-
-### 步骤 5: 组装编排管线
-
-```java
-WorkflowEngineClientConfig config = WorkflowEngineClientConfig.builder()
- .sslVerify(true)
- .credentialsConfigPath(credentialsPath) // 内置认证;自定义认证时改为 authProvider 并不配置它
- .credentialEncryptionKey(credentialKey) // 宿主密钥;明文配置可不提供
- .maxNegotiationExchanges(3)
- .build();
-try (A2ATransport transport = new A2ATransport(cards, runtime, config)) {
- WorkflowEngineClient client = new DefaultWorkflowEngineClient(transport);
- ExecutionResult result = ExecutePsop.builder()
- .psop(workflow).agentCards(cards).controlPoint(callbacks)
- .engineClient(client).runtimeIntent(intent).execute().join();
-}
-```
-
-workflow、cards、callbacks、intent 由宿主取得或构造。 runtime 为 null 时使用默认直连实现;自定义 runtime 只替换传输,不替换业务回调。
-上面 transport 仅属于这次工作流;授权和通知使用另外的实例。 宿主自己的 A2ATClient 可在回调中复用,但要确保业务状态按
-execution/task 隔离。
-
-#### 5.1 前置预定位详解
-
-```java
-// authorizationSender 与 notificationSender 各自使用独立 transport/runtime。
-MessageContent authContent = A2atMessages.from(
- sdk.generateAuthPromptFromDataWithSchema(authData, authSchema,
- StandardTemplates.AUTHORIZATION_POLICY_MANAGEMENT.uri()),
- List.of(new TextPart("新增动网操作授权")));
-authorizationSender.sendAuthorization(agentName, authContent)
- .whenComplete((result, error) -> recordIndependentAuthorization(result, error));
-
-MessageContent notificationContent = A2atMessages.from(
- sdk.generateNotificationPromptFromDataWithSchema(notifData, notifSchema,
- StandardTemplates.SERVICE_RECOVERY.uri()),
- List.of(new TextPart("订阅业务抢通事件")));
-NotificationSubscription subscription = notificationSender.openNotification(
- agentName, notificationContent, (handle, received) -> {
- if (received.artifacts().stream().anyMatch(a -> "recovery-result".equals(a.name()))) {
- handle.close();
- }
- });
-// 将 handle 加入宿主订阅集合;ACK/失败都仅影响这个独立操作。
-// 监听器使用传入的 handle,不依赖此时外部 subscription 变量是否赋值。
-subscription.acknowledgement().whenComplete((ack, error) -> recordIndependentSubscription(ack, error));
-```
-
-sdk、业务数据、schema、recordIndependent* 均由宿主提供,不是引擎隐式服务。 内容生成失败也只记录本次独立操作失败,不能阻止任务
-DAG。 close () 幂等请求关闭;completion () 等待真实流退出。工作流完成不能关闭独立订阅。 实际生命周期与异常处理见 samples 的
-ExtensionPrePositioner / WorkbenchExtensionLifecycle。
-
-#### 5.2 PSOP 工作流搜索与加载
-
-工作流 (PSOP) 存储在编排中心。SDK 提供搜索和加载方法:
-
-```java
-// 语义搜索: 根据原始意图匹配最相关的工作流
-List results = LoadPsop.search(orchUrl, messageText, 3, null, sslVerify);
-String psopId = results.get(0).getWorkflowId();
-
-// 加载完整工作流定义
-Workflow workflow = LoadPsop.load(orchUrl, psopId, null, sslVerify);
-```
-
-生产环境应明确编排中心不可用时的业务失败策略,不能默认执行可能过期的本地流程。
-`SpringSpnDemo` 为离线演示提供本地 PSOP fallback;接入方只有在完成版本一致性、审计和变更治理后, 才应自行设计受控的本地兜底。
-
-#### 5.3 EventCallback -- 事件追踪
-
-`ExecutePsop` 在执行过程中产出全量事件。集成方可实现 `EventCallback` 追踪执行进度、记录日志、向前端推送:
-
-```java
-private EventCallback createLogCallback() {
- return new EventCallback() {
- @Override
- public void onEvent(String type, Map data) {
- switch (type) {
- case EventType.START -> log.info("[开始] 工作流: {}", data.get("workflow"));
- case EventType.STEP_START -> log.info("[步骤开始] {}", data.get("step"));
- case EventType.AGENT_REQUEST -> log.info("[发送] -> {}", data.get("agent"));
- case EventType.AGENT_RESPONSE -> log.info("[接收] <- {}", data.get("agent"));
- case EventType.NEGOTIATION_REQUEST ->
- log.info("[协商请求] agent={}, 第{}次交互", data.get("agent"), data.get("exchange"));
- case EventType.NEGOTIATION_RESOLVED ->
- log.info("[协商完成] agent={}", data.get("agent"));
- // 以下事件类型为预留,当前 SDK 版本不会触发
- // (独立操作不进入工作流事件流)
- case EventType.AUTHORIZATION_REQUEST ->
- log.info("[授权请求] agent={}", data.get("agent"));
- case EventType.NOTIFICATION ->
- log.info("[通知] agent={}", data.get("agent"));
- case EventType.ROUTE_DECISION ->
- log.info("[路由] {} -> {}", data.get("step"), data.get("next"));
- case EventType.STEP_COMPLETE -> log.info("[步骤完成] {}", data.get("step"));
- case EventType.COMPLETE -> log.info("[完成]");
- case EventType.ERROR -> log.error("[错误] {}", data.get("error"));
- case EventType.CLOSE -> log.info("[关闭]");
- }
- }
- };
-}
-```
-
-事件类型完整列表见 SDK 的 `EventType` 常量类。关键事件: `start` -> `step_start` -> `agent_request` / `agent_response` (含
-`negotiation_*`) -> `step_complete` -> `complete` / `error` -> `close`。
-
-> **注意:** `authorization_request` 和 `notification` 事件类型在当前 SDK 版本中不会被触发 (独立操作不进入工作流事件流)
-> 。这些事件类型为未来扩展预留。独立授权通过 SendMessageResult,订阅通过 handle 的 ACK 和监听器取得响应,不经过事件流。
-
----
-
-## 4. 完整调用时序
-
-```mermaid
-sequenceDiagram
- participant H as Host callbacks + A2A-T client
- participant E as Workflow engine
- participant A as Remote agent
- E->>H: onTask(TaskRequest + upstream window)
- H->>H: Generate/validate final content
- H-->>E: MessageContent
- E->>A: A2A envelope + unchanged content
- opt INPUT_REQUIRED with valid Propose
- A-->>E: Task status + Negotiation-T Propose
- E->>H: onNegotiation(originalSubmission, received, history)
- H->>H: Validate proposal and generate reply
- H-->>E: Send(MessageContent) or local Stop
- E->>A: Same task/context with final reply content
- end
- A-->>E: Task result / artifacts
- E->>H: onSelfTask(selected complete upstream results)
- H-->>E: TaskResult
-```
-
-```mermaid
-sequenceDiagram
- participant H as Host + A2A-T client
- participant ES as Independent ExtensionSender
- participant A as OMC
- H->>H: Generate final authorization content
- H->>ES: sendAuthorization(agent, content)
- ES->>A: One-shot authorization
- A-->>H: Independent result, never gates workflow
- H->>H: Generate final subscription content
- H->>ES: openNotification(agent, content, listener)
- ES-->>H: Registered handle
- ES->>A: Independent long-lived stream
- A-->>H: ACK via acknowledgement()
- A-->>H: listener(handle, ReceivedMessage)
- H->>ES: handle.close() on recovery/cancel/shutdown
- ES-->>H: completion() after stream exits
-```
-
-只有远端 `INPUT_REQUIRED` 携带有效 Negotiation-T Propose 才进入 `onNegotiation`。 终态不会重启协商,普通 INPUT_REQUIRED
-明确报告不支持的交互。 宿主自行校验、理解 Propose,并用自己的 A2A-T client 生成最终 Accept/Reject/Abort。 通过
-`A2atMessages.contextOf(request.received())` 取得收到的上下文; 结束回复保持相同 id、round、maxRounds,最后允许的一轮仍可回答,不自行
-nextRound 或返回新 Propose。
-
-返回 `new NegotiationReply.Send(content)` 发送最终内容; 返回 `new NegotiationReply.Stop(code, reason)` 只在本地停止,不生成
-Abort。 同一任务/会话/轮次的重复等待事件不会重复回调、重复提交;未变化状态通过 getTask 观察。
-`maxNegotiationExchanges` 默认 3,是独立于 SDK context.maxRounds 的本地交互资源预算。 超时、预算耗尽、回调缺失均明确失败,不默认
-Accept,也不自动生成 Abort。 Accept/Reject 的 SUBMITTED/WORKING ACK 仍需等待任务结果,不重发原命令。 业务发送 Abort 后,即使远端用
-COMPLETED 确认,也不能判为诊断成功。
-
-### 查看真实协商与协议日志
-
-直接运行本地 SpringSpnDemo,默认 City1 缺参并协商、City2 参数完整直接诊断,无需添加 VM 参数。 Negotiation-T 扩展激活本身不强制协商;这是本地
-Demo 专门设置的场景,不是引擎默认行为。 要关闭本地缺参演示、让两城市都直接诊断,在 IDEA **VM options** 增加:
-
-```text
--Da2at.samples.negotiation=false
-```
-
-默认仅移除 City1 的 Task-T 任务对象;启用状态下增加 `-Da2at.samples.negotiation.city=city2` 或 `both`
-可演示 City2 或两城市同时缺参。宿主仍保留各城市的正确输入,原投诉上下文不变。预期链路是 `DEMO_NEGOTIATION` →
-`INPUT_REQUIRED / PROPOSE`
-→ 集成方 onNegotiation → `ACCEPT` → 两城市完成 → 一次汇总。 非内嵌 OMC 模式默认不注入缺参,显式设置
-`-Da2at.samples.negotiation=true` 也会被拒绝。 Demo 将开关传入当前 Spring 应用实例,不设置或修改 JVM 全局开关,不影响其他宿主。
-协议日志在控制台及以运行目录为基准的 `logs/spn-demo.log`。 本分支使用直连模式。
-
-离线重复验证:
-
-```powershell
-mvn -q -pl samples -am "-Dtest=SpringSpnDemoE2ETest" "-Dsurefire.failIfNoSpecifiedTests=false" test
-```
-
-每个测试类覆盖无 VM 参数的默认单城市协商、显式关闭、显式开启及两城市同时缺参。测试使用离线 LLM provider,但实际运行当前 SDK
-的 模板、校验和 HTTP/SSE;不是现网 OMC、平台或真实模型语义的验证。
-
-### Demo 端到端 A2A-T SDK 调用清单
-
-`SpringSpnDemo` 完整业务链路(两地市并行诊断、City1 缺参协商、City2 直接诊断)按业务时序对 A2A-T SDK 的全部调用如下。授权与订阅是独立于工作流
-DAG 的操作,使用各自的独立通道 (授权为一次性通道,订阅为长连接通道),与 Task-T 报文无关;Demo 的调用时机是在工作流任务 下发
-**之前**先行完成授权与订阅,再启动两地市诊断。样例覆盖全部 8 个 validate 接口和全部 7 个 fromData 生成接口,均为真实调用;fromText
-生成系列未在样例中使用(见文末说明)。
-
-| # | 业务时机 | 调用方(角色) | A2A-T SDK 接口 | 样例实现 |
-|----|-------------------------------------------------------------------------------------------|------------------|------------------------------------------------------------------------------|---------------------------------|
-| 1 | WAIMO 下发投诉任务,集成方入口校验 Task-T 并提取任务参数 | 集成方(服务端) | `A2ATServer.validateTaskPromptAndDataFilling` | `WorkbenchTaskInputParser` |
-| 2 | 集成方生成抢通白名单授权请求,经独立一次性通道发送 OMC(任务下发前) | 集成方(客户端) | `A2ATClient.generateAuthPromptFromDataWithSchema` | `ExtensionPrePositioner` |
-| 3 | OMC 收到授权报文,校验并按白名单执行,返回 ACK | OMC(服务端) | `A2ATServer.validateAuthPromptAndDataFilling` | `PrePositionedExtensionHandler` |
-| 4 | 集成方生成业务抢通事件订阅请求,经独立长连接通道向 OMC 订阅(任务下发前) | 集成方(客户端) | `A2ATClient.generateNotificationPromptFromDataWithSchema` | `ExtensionPrePositioner` |
-| 5 | OMC 校验订阅报文,建立 SSE 上报;后续抢通结果经该长连接推回集成方 | OMC(服务端) | `A2ATServer.validateNotificationPromptAndDataFilling` | `PrePositionedExtensionHandler` |
-| 6 | 集成方按地市生成并行 Task-T;不依赖独立授权或订阅成功 | 集成方(客户端) | `A2ATClient.generateTaskPromptFromDataWithSchema` | `SpringSpnDemo` |
-| 7 | OMC 校验 Task-T,提取任务对象/上下文;参数完整则直接诊断(City2) | OMC(服务端) | `A2ATServer.validateTaskPromptAndDataFilling` | `NegotiationBaseAgentExecutor` |
-| 8 | OMC 发现必选参数缺失,生成协商 Propose,随 INPUT_REQUIRED 返回(City1) | OMC(服务端) | `A2ATServer.generateNegotiationProposePromptFromData` | `NegotiationBaseAgentExecutor` |
-| 9 | 集成方 `onNegotiation` 回调:校验入站 Propose,提取所需补充字段 | 集成方(客户端) | `A2ATClient.validateProposePromptAndDataFilling` | `NegotiationStrategy` |
-| 10 | 集成方从当前地市业务输入补参,生成协商结尾报文(Accept;拒绝/终止对应 Reject/Abort 变体) | 集成方(客户端) | `A2ATClient.generateNegotiationAcceptPromptFromData`(及 Reject/Abort 变体) | `NegotiationStrategy` |
-| 11 | OMC 校验集成方回传的协商结尾报文,提取补参后重新执行诊断 | OMC(客户端) | `A2ATClient.validateAcceptPromptAndDataFilling`(及 Reject/Abort 变体) | `NegotiationBaseAgentExecutor` |
-
-两个组装辅助贯穿上述调用(属执行引擎的通用工具,非 SDK 接口):
-
-- `A2atMessages.from(MetadataContent, parts)`:把 SDK 生成的 `MetadataContent`(模板 URI、扩展 URI、 协商上下文
- metadata)封装为发送用的 `MessageContent`,不生成、不解释业务内容;
-- `A2atMessages.contextOf(received)`:从收到的协商报文中取回 `NegotiationContext`,协商结尾回复 保持相同
- id/round/maxRounds(步骤 6)。
-
-说明:
-
-- 全部生成都走 **fromData(结构化输入、确定性渲染)**;`generateTaskPromptFromText` 等 fromText 系列 (自然语言入口,含一步
- LLM 抽取)接口在样例中未使用,接口契约见
- [A2A-T SDK 依赖说明](A2AT-SDK-DEPENDENCY.md)。
-- 协商由 **OMC 侧触发**(缺参时返回 INPUT_REQUIRED + Propose),集成方无法选择哪个地市协商; 样例通过删除 City1
- 的任务对象字段构造该输入条件,两城市的补参互相隔离。
-- 输入内容(任务参数、补参值、授权策略、订阅条件)全部来自样例构造的业务数据(`SpnCasePrompts`), SDK 接口输出不做任何打桩;运行
- `SpringSpnDemo` 使用真实 LLM 配置。
-
-## 5. 集成方需交付物清单
-
-| 交付物 | 类型 | 说明 |
-|--------------------------------------------|---------|--------------------------------------------------------------------------|
-| `TransportWorkbenchExecutor` | Java 类 | 实现 `AgentExecutor`,A2A 服务端入口 |
-| `WorkbenchControlPoint` | Java 类 | 继承 `DefaultControlPoint`,实现 onTask/onSelfTask/onRoute/onNegotiation |
-| `WorkbenchOrchestrator` | Java 类 | 组装编排管线 (加载卡片 -> 检索工作流 -> 执行) |
-| `WorkbenchAuthProvider` (可选) | Java 类 | 实现 `AuthProvider`,自定义认证逻辑 (如企业 SSO) |
-| `agentcard/transport_workbench_agent.json` | JSON | 集成方自身的 AgentCard |
-| `spn_agent_credentials.json` (可选) | JSON | 下游 OMC 的认证凭证 (使用 Credentials 认证时需要) |
-| `.env` | INI | A2A-T SDK 的 LLM 配置 |
-| `application.yml` (Spring Boot) | YAML | `a2at.server.agent-card` 等配置项 |
-
----
-
-## 6. 生产环境注意事项
-
-### 6.1 TLS 与证书
-
-生产环境 OMC 通常使用自签名证书。`WorkflowEngineClientConfig` 中:
-
-- `sslVerify(true)`: 校验证书 (需配置 CA 信任库 `caCertsPath`)
-- `sslVerify(false)`: 仅跳过证书链校验,主机名校验仍启用;只用于受控的本地诊断 (HTTP/JSON-RPC 会继续加载 mTLS 客户端证书;默认
- gRPC 使用 plaintext,不能同时配置 mTLS/CRL)
-
-```java
-WorkflowEngineClientConfig.builder()
- .sslVerify(true)
- .caCertsPath("/path/to/ca-certs.pem")
- // OMC 要求双向 TLS 时配置:
- .clientCertPath("/path/to/client-cert.pem")
- .clientKeyPath("/path/to/client-key.pem")
- .credentialsConfigPath(credentialsPath)
- .build()
-```
-
-### 6.2 凭证加密与自定义认证
-
-**Credentials 认证:** `spn_agent_credentials.json` 中的密码字段使用 `enc:` 前缀标识加密内容。SDK 的 `CredentialCrypto` 使用
-AES-GCM 对称加密自动解密。加密密钥通过环境变量 `A2AT_CRED_KEY` 注入 (32 字节 hex)。生产环境不应明文存储密码。
-
-**自定义 AuthProvider:** 如果集成方已有统一的认证体系 (如企业 SSO、OAuth2 网关),可直接实现 `AuthProvider` 接口,无需维护
-credentials.json 文件。`AuthProvider` 在每次消息发送时调用,适合令牌生命周期由外部管理的场景:
-
-```java
-public class SsoAuthProvider implements AuthProvider {
- private final SsoClient ssoClient;
-
- @Override
- public void applyAuth(String agentName, AgentCard agentCard, Map headers) {
- String token = ssoClient.getAccessToken(agentName);
- headers.put("Authorization", "Bearer " + token);
- }
-}
-```
-
-### 6.3 协商轮次限制
-
-WorkflowEngineClientConfig.builder ().maxNegotiationExchanges (3) 配置本地交互预算, 与收到的 SDK context.maxRounds
-分开。耗尽/超时只本地失败,不自动生成 Abort。 结束回复保留收到的 id/round/maxRounds,最后一轮仍可回答。
-
-### 6.4 SSE 长连接管理
-
-`openNotification` 建立的 SSE 订阅通道是长连接。SDK 的 `A2ATransport` 管理连接池和超时。集成方应在服务关闭时调用
-`extensionTransport.close()` 释放连接。`ExecutePsop` 仅自动关闭它自行创建的 client/transport;传入已有 engineClient 时由集成方负责关闭。不要把任务级
-transport 与 `ExtensionSender` 共用,否则 Notification-T 长连接会在工作流结束时被关闭。
-
-### 6.5 并发与并行
-
-SDK 的 `WorkflowExecutor` 按 DAG 已激活前驱的完成情况调度就绪步骤;无依赖关系的就绪步骤及步骤内子任务可并行执行。如果
-OMC 有并发限制,通过 DAG 依赖边或宿主限流控制;layer 不是执行顺序约束。
-
-### 6.6 错误处理
-
-引擎处理远端协议状态和结果解包;onTask 只返回 MessageContent。onSelfTask 返回 TaskResult,失败原因与 outputs 分离。异常和失败
-future 由引擎记录;不要把未发送成功包装成成功输出。详见 [业务回调契约](BUSINESS_CALLBACKS.md)。
-
-## 7. 接口速查表
-
-```java
-interface ControlPoint {
- CompletableFuture onTask(TaskRequest request);
- CompletableFuture onSelfTask(TaskRequest request);
- CompletableFuture onRoute(RouteRequest request);
- CompletableFuture onNegotiation(NegotiationRequest request);
-}
-```
-
-onTask 返回最终 parts/metadata/extensions,引擎封装发送,不再生成或改写内容。 onSelfTask 返回本地 TaskResult;onRoute
-选择允许的候选;onNegotiation 返回 Send 或 Stop。 未实现的回调明确失败,不回显成功、不选首分支、不自动同意。
-字段与完整示例见 [业务回调集成契约](BUSINESS_CALLBACKS.md)。
-
-```java
-CompletableFuture sendAuthorization(String agentName, MessageContent content);
-NotificationSubscription openNotification(String agentName, MessageContent content,
- BiConsumer listener);
-```
-
-- ExecutePsop.builder ().execute ():执行完整工作流。
-- WorkflowEngineClient.sendMessage (String, MessageContent):DAG 外发送最终内容;DAG 内由执行器调用 dispatch。
-- LoadPsop.search/load、RegistryClient.fetchAgentCards:可选发现辅助。
-- AuthProvider.applyAuth (String, AgentCard, Map):宿主认证,不接收引擎强制规定的凭据文件结构。
-- A2atMessages.from / contextOf:metadata 转换与规范上下文辅助,不是内容 SDK 门面。
-
-## 8. 与 Demo 的关键区别
-
-本次集成是生产环境接入,与 Demo 示例代码的关键区别:
-
-| 维度 | Demo (samples) | 生产环境 |
-|---------------|--------------------------------|-------------------------------------------------------|
-| AgentExecutor | 模拟实现,返回固定文本 | 对接真实 上层故障系统,需完整 A2A 消息时序 |
-| OMC Agent | 模拟 Agent,预置响应 | 真实 SPN Domain Agent,Bearer 认证、真实诊断逻辑 |
-| AgentCard | 从 classpath 加载 JSON | 从注册中心获取或厂商提供 |
-| 认证 | credentials.json 明文密码 | 加密存储 (enc: 前缀) 或自定义 AuthProvider (企业 SSO) |
-| TLS | sslVerify=false | 生产证书校验 |
-| PSOP | fallback 固定 ID | 编排中心语义搜索 + 动态加载 |
-| 事件 | 日志打印 | SSE 推送前端 + 持久化数据库 |
-| 协商 | 结构化规则示例(typed reply) | 宿主 SDK 生成最终回复,回调返回 Send 或 Stop |
-| 抢通结果 | 通过独立订阅接收,收到结果关闭 | 通过 Notification-T SSE 通道接收并入库 |
-
----
-
-## 9. 集成自查清单
-
-- Maven 是否解析到 `a2a-t-client:1.1.0`,且 jar 为 Central 发布制品而非本地同坐标重打包
-- AgentCard 是否包含 `name`、`description`、`version`、`capabilities`、`defaultInputModes`、`defaultOutputModes`、`skills`、
- `supportedInterfaces`
-- 下游 AgentCard 的 `securitySchemes` / `securityRequirements` 是否按实际认证方式配置;AuthProvider-only 推荐
- `securityRequirements: []`
-- `AuthProvider` 是否已同时配置到前置预定位 transport 和工作流任务 transport
-- 前置预定位 transport 是否为集成方级生命周期,工作流 transport 是否为任务级生命周期
-- onTask 是否仅准备 MessageContent,由引擎判断远端终态;本地 onSelfTask 是否明确返回业务成功或失败
-- `openNotification` 是否传入回调,并在服务关闭时释放前置预定位 transport
-
-## 协议日志
-
-将 `PROTOCOL` logger 设为 DEBUG,可查看实际传输边界的观测记录。 HTTP/JSON-RPC 记录 A2A SDK 处理后的实际序列化正文和应用头;
-A2A-Version 只在真实请求有该头时出现,不为日志展示补造 Header。 gRPC 记录实际 metadata 和 protobuf 的 JSON 展示,不伪装成
-HTTP JSON 报文。 JDK 自动网络头、HTTP/2 帧、TLS 密文及服务端字节不在此观测范围。
-
-`MODEL_PREVIEW` 只是高层预览,默认关闭,不是协议证据。
-
-```properties
-logger.protocol.name=PROTOCOL
-logger.protocol.level=DEBUG
-logger.protocol.additivity=true
-WORKFLOW_ENGINE_PROTOCOL_INCLUDE_BODY=true
-WORKFLOW_ENGINE_PROTOCOL_MAX_BODY_CHARS=100000
-```
-
-DEBUG 开启时正文观测默认开启;敏感部署可显式禁用。JVM 同名属性优先于环境变量。 认证头、Cookie、Token
-及识别出的口令字段强制脱敏,不提供关闭脱敏的开关。 这是字段级脱敏,不能自动识别所有个人信息和业务敏感内容,生产应另定日志策略。
-缓冲有界:原始收集器按该数值限制字节,输出文本按字符限制; 超限 SSE 帧整帧丢弃至下个分隔符并标记
-dropped-capacity,禁用、截断、中断也有明确标记。 UTF-8 分片先组装再解码,日志观察失败不改变报文投递;文件引用不会为打印日志而下载。
-requestId 关联单次调用;工作流调用还带 executionId/logicalTaskId/attempt、 agent/contextId/channel,已知远端任务时附
-remoteTaskId,均为本地日志字段,不污染协议 metadata。
-
-### 协议日志 pretty 展示
-
-协议日志默认以 pretty 形式展示:逐行打印头字段,缩进 JSON 正文。SSE 保留 id/event 等事件信息, JSON 在
-`=== SSE data (JSON display; not wire text) ===` 与 `=== End SSE data ===` 之间单独展示, 不再为展开后的每行 JSON 添加
-`data:` 前缀。多个事件各自保留边界。
-`WORKFLOW_ENGINE_PROTOCOL_PRETTY=false`(环境变量或同名 JVM 属性)可保留脱敏后的原始正文展示。 pretty 只影响显示(JSON 缩进及
-SSE 数据区标记),不改实际发送字节、metadata、数值或扩展头; 原始观测正文仍保留,SSE pretty 展示不是可直接重放的抓包。 JSON
-字符串中的 `\\n` 保留转义,避免把业务字符串误写成非法 JSON;非 JSON/不完整正文保持原样。 日志仍执行脱敏和容量限制。Demo 启动记录
-`NEGOTIATION_DEMO enabled=..., city=...`; 本地默认 City1 协商、City2 直接诊断,`-Da2at.samples.negotiation=false` 关闭缺参演示。
-协商激活位于 `A2A-Extensions: .../Negotiation-T/v1`,不存在专用的 Negotiation-T HTTP 头; 正文检查对应 URI 的 metadata 和
-`negotiationContext`,不要只检索大写 `NEGOTIATION-T`。