| title | Aevatar.Scripting 架构文档 |
|---|---|
| status | active |
| owner | eanzhao |
- 文档状态:
Active - 文档版本:
v16 - 更新时间:
2026-03-16 - 适用范围:
src/Aevatar.Scripting.*与相关test/Aevatar.Scripting.*/test/Aevatar.Integration.Tests - 非范围:
Aevatar.Foundation.*的内部 runtime 实现细节;本文只说明当前生效的 scripting 主链与文档入口
当前应按以下顺序阅读:
- 当前实现收口:
docs/architecture/2026-03-14-scripting-gagent-behavior-parity-implementation-closeout.md
- 当前 typed authoring 设计:
docs/architecture/2026-03-14-scripting-typed-authoring-surface-detailed-design.md
- 后续定义源、原生物化与运行语义提案:
docs/architecture/2026-03-14-scripting-native-readmodel-materialization-detailed-design.mddocs/architecture/2026-03-14-scripting-protobuf-definition-source-detailed-design.mddocs/architecture/2026-03-14-scripting-runtime-semantics-protobuf-options-detailed-design.md
- 本文:
- 作为 scripting 总览与文档索引,帮助快速定位当前主链
以下文档保留为历史设计留痕,不再作为当前实现依据:
docs/architecture/2026-03-13-scripting-gagent-behavior-parity-refactor-blueprint.mddocs/architecture/2026-03-13-scripting-gagent-behavior-parity-detailed-design.md
若本文与 implementation-closeout 发生冲突,以 implementation-closeout 和实际代码为准。
上面三份 2026-03-14 提案文档讨论的是后续方向,不代表当前代码已经切到 script package(cs + proto)。它们的编译模型明确是“definition/provisioning 阶段的动态包编译”,不是解决方案构建期静态预编译。
Aevatar.Scripting 当前已经不是 ScriptRuntimeGAgent + IScriptPackageRuntime + payload bag read model 架构。
当前生效主链是:
- 脚本作者以
ScriptBehavior<TState,TReadModel>编写强类型command / signal / domain event行为。 - 当前态 readmodel 语义必须显式收敛为
OnEvent(... apply: ...) + ProjectState(...):事件只推进 actor state,readmodel 只从当前 state 派生。 - 定义侧把脚本编译为
ScriptBehaviorDescriptor + ScriptGAgentContract。 - runtime provisioning 必须显式携带
ScriptDefinitionSnapshot;RuntimeScriptProvisioningService不再中途侧读 definition readmodel,也不再轮询等待投影。 - 运行侧由
ScriptBehaviorGAgent宿主脚本行为,并在 commit 后发布CommittedStateEventPublished(state_event + state_root)观察流。 - 写侧 dispatcher 会基于 post-event state 调
BuildReadModel(...),再把 semantic/native committed fact 一并发布出去。 - 读侧由
ScriptReadModelProjector/ScriptDefinitionSnapshotProjector/ScriptCatalogEntryProjector/ScriptNativeDocumentProjector/ScriptNativeGraphProjector基于 committed observation 构建当前态与 native readmodel。 - 查询只通过
ScriptReadModelQueryReader -> ScriptReadModelQueryApplicationService读取 persisted snapshot/document;read-side 不再执行 behavior query,也不再暴露 declared-query authoring/runtime 契约。 - 演化链继续由
ScriptEvolutionSessionGAgent / ScriptEvolutionManagerGAgent / ScriptCatalogGAgent承担治理与索引职责。
当前 actor 边界也已经进一步收紧:
IScriptBehaviorRuntimeCapabilities不再暴露GetReadModelSnapshotAsync(...)这类跨 actor readmodel 侧读能力。- scripting behavior 在 actor turn 内只能发布消息、调度 self continuation、调用 AI/definition/provisioning/evolution 等显式应用端口。
- 读取其他 actor 的已提交事实必须回到正式 query/readmodel 入口,不能通过 runtime capability 在脚本内部侧读。
当前 runtime semantics 也已经明确收紧:
- 所有 scripting 行为契约消息都必须显式声明
(aevatar.scripting.runtime.scripting_runtime)。 - 不再接受
google.protobuf.Empty / StringValue / Struct这类 wrapper message 的宿主自动兜底语义。 - wrapper 类型仍可作为普通 protobuf 载荷存在于宿主边界,但不能再充当 scripting command / signal / event 的隐式协议定义。
当前 read model schema/materialization 也已经同步收紧:
- scripting 内核只接受 protobuf scalar、proto3 optional、子消息和
google.protobuf.Timestamp作为 read model 结构。 google.protobuf.StringValue / Int32Value / BoolValue / BytesValue等 wrapper field 不再允许出现在 scripting read model schema 中。- 需要表达“可空标量”时,应优先使用 proto3
optional或显式子消息,而不是 wrapper leaf。 - 动态 script package 的
.proto冷编译阶段也会直接拒绝wrappers.proto和google.protobuf.*Value引用,避免把 wrapper 重新带回脚本协议。
%%{init: {"maxTextSize": 100000, "flowchart": {"useMaxWidth": false, "nodeSpacing": 10, "rankSpacing": 50}, "themeVariables": {"fontSize": "10px"}}}%%
flowchart LR
DEFAPI["Host / Definition Commands"] --> DEF["ScriptDefinitionGAgent"]
DEF --> CMP["RoslynScriptBehaviorCompiler"]
CMP --> ART["ScriptBehaviorArtifact"]
RUNAPI["Host / Runtime Provisioning"] --> PROV["RuntimeScriptProvisioningService<br/>explicit ScriptDefinitionSnapshot"]
PROV --> ACT["ScriptBehaviorGAgent"]
ACT --> DISP["ScriptBehaviorDispatcher"]
DISP --> ART
ART --> AUTHOR["ScriptBehavior<TState,TReadModel>"]
ACT --> FACT["CommittedStateEventPublished<br/>(state_event + state_root)"]
FACT --> PROJ["State-backed Projectors"]
PROJ --> DEFRM["ScriptDefinitionSnapshotDocument"]
PROJ --> CATRM["ScriptCatalogEntryDocument"]
PROJ --> RM["ScriptReadModelDocument"]
FACT --> LIVE["ScriptExecutionSessionEventProjector"]
LIVE --> HUB["ProjectionSessionEventHub<EventEnvelope>"]
RM --> QRY["ScriptReadModelQueryReader"]
QRY --> HOSTQ["Host / Snapshot Endpoints"]
EVOAPI["Host / Evolution Commands"] --> SES["ScriptEvolutionSessionGAgent"]
SES --> CAT["ScriptCatalogGAgent"]
SES --> MGR["ScriptEvolutionManagerGAgent"]
SES --> DEF
脚本作者默认面对的是强类型 API,而不是 Any:
ScriptBehavior<TState,TReadModel>IScriptBehaviorBuilder<TState,TReadModel>ScriptCommandContext<TState>ScriptFactContextProjectState(Func<TState?, ScriptFactContext, TReadModel?>)
当前 authoring surface 已经不再包含 OnQuery<TQuery, TResult>(...)。
OnEvent<TEvent>(...) 也不再接受 project: 参数。
Any 只保留在宿主边界、持久化边界和跨 actor 边界。
写侧权威事实是 actor committed state 与 committed domain fact。
这意味着:
CommittedStateEventPublished现在携带state_event + state_root,作为 scripting current-state readmodel 的统一观察输入。ScriptDomainFactCommitted继续表达脚本业务事实,但 current-state projection 不再要求读侧用 reducer 从旧文档补算当前态;每一条 committed fact 自身携带的read_model_payload/native_document/native_graph都必须对应它自己的 post-event state/version。- runtime provisioning 必须显式使用 write-side 已得出的
ScriptDefinitionSnapshot,而不是中间层再去读 definition readmodel。 - native document / graph 物化计划已经前移到 write-side;projection 只消费
ScriptDomainFactCommitted内的 durablenative_document/native_graph子契约。
当前 persisted read model root 是 ScriptReadModelDocument。
它仍是容器式 document root,但已经是正式、一等的 actor-scoped current-state readmodel,不再是临时 Dictionary<string, Any> bag。
当前演化链路保持 actor-owned 治理:
ScriptEvolutionSessionGAgent负责 proposal executionScriptEvolutionManagerGAgent负责长期索引与治理镜像ScriptCatalogGAgent负责激活 revision 与回滚历史ScriptDefinitionGAgent负责定义、编译结果与 contract 快照
以下对象已经从 scripting 主链删除,不应再出现在新文档或新设计里:
IScriptPackageRuntimeScriptRuntimeGAgentScriptRuntimeExecutionOrchestratorScriptExecutionReadModelScriptRunDomainEventCommitted.state_payloadsScriptRunDomainEventCommitted.read_model_payloadsScriptReadModelQueryService- read-side declared query / behavior query 执行
RuntimeScriptProvisioningService内部 definition snapshot polling fallback- 直接用 projection store 直读替代正式 query facade 的做法
OnQuery<TQuery, TResult>/ExecuteQueryAsync(...)/QueryTypeUrls
| 分层 | 项目 | 当前职责 |
|---|---|---|
| Abstractions | Aevatar.Scripting.Abstractions |
Proto 契约、typed authoring surface、descriptor/contract 模型 |
| Core | Aevatar.Scripting.Core |
ScriptBehaviorGAgent、definition/catalog/evolution actors、核心状态机 |
| Application | Aevatar.Scripting.Application |
运行 dispatch、命令/查询应用服务、interaction 组合 |
| Infrastructure | Aevatar.Scripting.Infrastructure |
Roslyn 编译、artifact loader、端口实现 |
| Projection | Aevatar.Scripting.Projection |
committed fact 投影、execution live sink、read model 查询实现 |
| Hosting | Aevatar.Scripting.Hosting |
DI 组装、Host API、JSON/protobuf 边界适配 |
依赖方向仍然满足:
- 上层依赖抽象,不反向依赖具体 Infrastructure。
- CQRS/AGUI 继续复用统一 Projection Pipeline。
- Actor 运行态与 session 生命周期不在中间层用进程内字典持有事实状态。
- 写侧必须继续基于 actor + event sourcing,不回退为 process-local runner。
- query 只读 read model,不回退到 runtime actor 内部状态直读。
- 运行期
publish/send/self-signal/durable-timeout语义必须保持 runtime-neutral。 - 影响业务语义、控制流、稳定读取的数据必须强类型建模,不重新退回 bag。
- Scripting 与 Workflow/CQRS Core 继续共享统一 envelope / projection 主链,不引入第二套 read-side pipeline。
- projection 不得再解析 behavior artifact 或编译 native materialization plan;native materialization 必须来自 actor write-side durable contract。
- runtime semantics 必须 descriptor-first,禁止再依赖
google.protobuf.*wrapper fallback 推断 command / signal / event 语义。
这轮整理后,文档应按以下心智模型理解:
2026-03-14-*scripting 文档:当前有效2026-03-13-*scripting 文档:历史设计基线- 本文:总览入口,不再复述已经删除的旧运行链