AG-UI基于灵活的事件驱动架构构建,可实现前端应用程序和Agent之间无缝、高效的通信。事件驱动是整个AG-UI的核心,整个协议层以如下的形式为应用程序与Agent之间的交互提供一个灵活的通信基础。也就是说,不论底层采用何种通信协议,面向用户的前端应用总是采用上面这个模式与Agent交互:通过传入RunAgentInput类型定义的输入,得到一个Observable<BaseEvent>对象。
run(input: RunAgentInput) -> Observable<BaseEvent>
export interface Observable {
next: (value: T) => void;
error: (err: any) => void;
complete: () => void;
}
表示输入的RunAgentInput和各种事件类型组成了AG-UI最核心内容。在AG-UI详解-02:AG-UI = RunAgentInput + BaseEvent中,我们针对Python SDK对各种RunAgentInput和各种事件类型,以及基于这些事件类型对相应数据的传输方式进行了详细介绍。这篇文章会使用概括性的语言简单介绍这些类型在.NET SDK种的定义。
1. 消息
AG-UI采用消息作为前端应用和Agent交互的媒介。所有的消息类型均继承自如下这个AGUIMessage基类,每个消息具有通过id字段表示的唯一标识。role和表示生成消息的对象所扮演的角色
public abstract class AGUIMessage
{
public string? Id { get; set; }
public abstract string Role { get; }
}
AG-UI为消息定义了其中角色,我们可以利用静态类型AGUIRoles种定义的字符串常量得到对应的角色名称。
public static class AGUIRoles
{
public const string System = "system";
public const string User = "user";
public const string Assistant = "assistant";
public const string Developer = "developer";
public const string Tool = "tool";
public const string Activity = "activity";
public const string Reasoning = "reasoning";
}
SDK针对七种角色定义了对应的消息类型,具体的类型定义如下。
public sealed class AGUISystemMessage : AGUIMessage
{
public override string Role => AGUIRoles.System;
public string Content { get; set; } = string.Empty;
public string? Name { get; set; }
public string? EncryptedValue { get; set; }
}
public sealed class AGUIUserMessage : AGUIMessage
{
public override string Role => AGUIRoles.User;
public string? Name { get; set; }
public string? EncryptedValue { get; set; }
public AGUIUserContent Content { get; set; }
}
public sealed class AGUIAssistantMessage : AGUIMessage
{
public override string Role => AGUIRoles.Assistant;
public string? Content { get; set; }
public string? Name { get; set; }
public string? EncryptedValue { get; set; }
public IList<AGUIToolCall>? ToolCalls { get; set; }
}
public sealed class AGUIToolMessage : AGUIMessage
{
public override string Role => AGUIRoles.Tool;
public string Content { get; set; } = string.Empty;
public string ToolCallId { get; set; } = string.Empty;
public string? Error { get; set; }
public string? EncryptedValue { get; set; }
}
public sealed class AGUIDeveloperMessage : AGUIMessage
{
public override string Role => AGUIRoles.Developer;
public string Content { get; set; } = string.Empty;
public string? Name { get; set; }
public string? EncryptedValue { get; set; }
}
public sealed class AGUIReasoningMessage : AGUIMessage
{
public override string Role => AGUIRoles.Reasoning;
public string Content { get; set; } = string.Empty;
public string? EncryptedValue { get; set; }
}
public sealed class AGUIActivityMessage : AGUIMessage
{
public override string Role => AGUIRoles.Activity;
public string ActivityType { get; set; } = string.Empty;
public JsonElement Content { get; set; }
}
除了AGUIUserMessage和AGUIActivityMessage的Content属性分别返回一个AGUIUserContent和JsonElement对象之外,其余消息类型的同名属性直接返回字符串文本作为消息主体内容。AGUIUserContent是一个结构体,我们可以提供字符串文本或者一组AGUIInputContent对象的形式来创建一个AGUIUserContent,指定的内容将作为它的Value属性。IsText属性用于判断AGUIInputContent是否仅仅包含常规的文本内容。
public readonly struct AGUIUserContent : IReadOnlyList<AGUIInputContent>
{
public AGUIUserContent(string text) => Value = text;
public AGUIUserContent(IList<AGUIInputContent> parts) => Value = parts;
public object? Value { get; }
public bool IsText => Value is string;
}
AGUIUserContent同时实现了IReadOnlyList<AGUIInputContent>接口,意味着可以作为一个AGUIInputContent对象的集合,AGUIInputContent是用户输入的多模态消息内容类型的基类。这是一个抽象类,仅定义了如下这个唯一的表示内容类型的Type属性。静态类型AGUIInputContentTypes利用常量定义了六种标准的内容类型,包括文本、图片、音频、视频、文档和一般二进制内容。
public abstract class AGUIInputContent
{
public abstract string Type { get; }
}
public static class AGUIInputContentTypes
{
public const string Text = "text";
public const string Image = "image";
public const string Audio = "audio";
public const string Video = "video";
public const string Document = "document";
public const string Binary = "binary";
}
SDK同样为这六种内容形态定义了对应的输入内容类型。
public sealed class AGUITextInputContent : AGUIInputContent
{
public override string Type => AGUIInputContentTypes.Text;
public string Text { get; set; } = string.Empty;
}
public sealed class AGUIImageInputContent : AGUIMediaInputContent
{
public override string Type => AGUIInputContentTypes.Image;
}
public sealed class AGUIAudioInputContent : AGUIMediaInputContent
{
public override string Type => AGUIInputContentTypes.Audio;
}
public sealed class AGUIVideoInputContent : AGUIMediaInputContent
{
public override string Type => AGUIInputContentTypes.Video;
}
public sealed class AGUIDocumentInputContent : AGUIMediaInputContent
{
public override string Type => AGUIInputContentTypes.Document;
}
public sealed class AGUIBinaryInputContent : AGUIInputContent
{
public override string Type => AGUIInputContentTypes.Binary;
public string MimeType { get; set; } = string.Empty;
public string? Id { get; set; }
public string? Url { get; set; }
public string? Data { get; set; }
public string? Filename { get; set; }
}
除了AGUITextInputContent和AGUIBinaryInputContent直接继承自AGUIInputContent以外,其余四个类型都是如下这个AGUIMediaInputContent类型的派生类。它利用Metadata属性返回的JsonElement提供进一步描述消息内容的元数据。
public abstract class AGUIMediaInputContent : AGUIInputContent
{
public AGUIInputContentSource Source { get; set; } = null!;
public JsonElement? Metadata { get; set; }
}
AGUIMediaInputContent利用Source属性返回的AGUIInputContentSource定义内容的来源。抽象类AGUIInputContentSource利用抽象属性Type返回来源类型。定义在静态类AGUIInputContentSourceTypes中的两个常量定义了两种典型来源的类型名称:
- data:直接将内容荷载内嵌的消息中;
- url:来源与指定URL指向的网络资源。
public abstract class AGUIInputContentSource
{
public abstract string Type { get; }
}
public static class AGUIInputContentSourceTypes
{
public const string Data = "data";
public const string Url = "url";
}
SDK为上述两种来源定义了对应的类型AGUIInputContentDataSource和AGUIInputContentUrlSource。
public sealed class AGUIInputContentDataSource : AGUIInputContentSource
{
public override string Type => AGUIInputContentSourceTypes.Data;
public string Value { get; set; } = string.Empty;
public string MimeType { get; set; } = string.Empty;
}
public sealed class AGUIInputContentUrlSource : AGUIInputContentSource
{
public override string Type => AGUIInputContentSourceTypes.Url;
public string Value { get; set; } = string.Empty;
public string? MimeType { get; set; }
}
2. 调用输入(RunAgentInput)
作为前前端应用向Agent的输入,RunAgentInput针对不同的调用场景,包括常规调用、恢复调用(利用Resume提供恢复数据)以及针对时间旅行的调用(通过ParentRunId在现有的某次调用后的状态上发起调用),提供必要的输入,具体定义如下。
public sealed class RunAgentInput
{
public string ThreadId { get; set; } = string.Empty;
public string RunId { get; set; } = string.Empty;
public string? ParentRunId { get; set; }
public IList<AGUIMessage> Messages { get; set; } = [];
public IList<AGUITool>? Tools { get; set; }
public JsonElement? State { get; set; }
public IList<AGUIResume>? Resume { get; set; }
public IList<AGUIContext>? Context { get; set; }
public JsonElement ForwardedProperties { get; set; }
}
调用Agent提供的静态上下文体现为一组AGUIContext对象,每个AGUIContext对象利用Description和Value描述单个上下文数据项。RunAgentInput的Tools用来向Agent提供一组在前端应用执行的工具。每个工具通过具有如下定义的AGUITool类型表示,会提供工具的名称、描述、参数Schema和相关的元数据。
public sealed class AGUIContext
{
public string Description { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
}
public sealed class AGUITool
{
public string Name { get; set; } = string.Empty;、
public string? Description { get; set; }
public JsonElement Parameters { get; set; }
public JsonElement? Metadata { get; set; }
}
public sealed class AGUIResume
{
public string InterruptId { get; set; } = string.Empty;
public string Status { get; set; } = ResumeStatus.Resolved;
public JsonElement? Payload { get; set; }
}
public static class ResumeStatus
{
public const string Resolved = "resolved";
public const string Cancelled = "cancelled";
}
对于针对中断状态的恢复调用,我们会利用Resume属性提供一组AGUIResume对象。每个AGUIResume对象是对每个表示中断的Interrup对象的答复,两者利用表示中断ID的InterruptId属性进行关联。Status属性提供两种标准的恢复状态(resolved或者cancelled),额外的输入内容通过Payload属性提供。
3. 事件(Events)
Agent响应的事件类型向前端应用返回各种类型的数据,所有的事件类型都是如下这个抽象基类BaseEvent,定义其中的三个属性分别表示事件类型、创建的时间戳和原始事件内容。
public abstract class BaseEvent
{
public abstract string Type { get; }
public long? Timestamp { get; set; }
public JsonElement? RawEvent { get; set; }
}
3.1 生命周期事件(Lifecycle Events)
如下所示的五个事件类型用于跟踪整个Agent调用的生命周期,RunStartedEvent与RunFinishedEvent/RunErrorEvent代表运行Agent的起止边界,StepStartedEvent和StepFinishedEvent是在整个Agent执行阶段划定的执行步骤的起止边界。
public sealed class RunStartedEvent : BaseEvent
{
public override string Type => AGUIEventTypes.RunStarted;
public string ThreadId { get; set; } = string.Empty;
public string RunId { get; set; } = string.Empty;
public string? ParentRunId { get; set; }
public RunAgentInput? Input { get; set; }
}
public sealed class RunFinishedEvent : BaseEvent
{
public override string Type => AGUIEventTypes.RunFinished;
public string ThreadId { get; set; } = string.Empty;
public string RunId { get; set; } = string.Empty;
public JsonElement? Result { get; set; }
public RunFinishedOutcome? Outcome { get; set; }
}
public sealed class RunErrorEvent : BaseEvent
{
public override string Type => AGUIEventTypes.RunError;
public string Message { get; set; } = string.Empty;
public string? Code { get; set; }
}
public sealed class StepStartedEvent : BaseEvent
{
public override string Type => AGUIEventTypes.StepStarted;
public string StepName { get; set; } = string.Empty;
}
public sealed class StepFinishedEvent : BaseEvent
{
public override string Type => AGUIEventTypes.StepFinished;
public string StepName { get; set; } = string.Empty;
}
如果Agent运行正常结束,RunFinishedEvent事件被输出,其Outcome返回的RunFinishedOutcome用来确定是否是因中断而结束。如果是,会返回一个RunFinishedInterruptOutcome对象,否则返回一个RunFinishedSuccessOutcome对象。
public abstract class RunFinishedOutcome
{
public abstract string Type { get; }
}
public static class RunFinishedOutcomeTypes
{
public const string Success = "success";
public const string Interrupt = "interrupt";
}
public sealed class RunFinishedSuccessOutcome : RunFinishedOutcome
{
public override string Type => RunFinishedOutcomeTypes.Success;
}
public sealed class RunFinishedInterruptOutcome : RunFinishedOutcome
{
public override string Type => RunFinishedOutcomeTypes.Interrupt;
public IList<AGUIInterrupt> Interrupts { get; set; } = [];
}
3.2 文本消息事件(Text Message Events)
如下三种事件类型被Agent用来向前端应用实时返回生成的文本消息内容。TextMessageStartEvent和TextMessageEndEvent是针对单条文本消息创建的起止边界,TextMessageContentEvent事件利用Delta属性实时创数当前生成的内容。.NET SDK并没有定义TextMessageChunkEvent这种事件类型。
public sealed class TextMessageStartEvent : BaseEvent
{
public override string Type => AGUIEventTypes.TextMessageStart;
public string MessageId { get; set; } = string.Empty;
public string Role { get; set; } = string.Empty;
public string? Name { get; set; }
}
public sealed class TextMessageEndEvent : BaseEvent
{
public override string Type => AGUIEventTypes.TextMessageEnd;
public string MessageId { get; set; } = string.Empty;
}
public sealed class TextMessageContentEvent : BaseEvent
{
public override string Type => AGUIEventTypes.TextMessageContent;
public string MessageId { get; set; } = string.Empty;
public string Delta { get; set; } = string.Empty;
}
3.3 工具调用事件(Tool Call Events)
如下四种事件被Agent用来向前端应用传输工具调用和工具调用生成的结果。其中ToolCallStartEvent和ToolCallEndEvent事件作为工具传输的起止边界,并在之中利用一个或者多个ToolCallArgsEvent事件进行参数传输。工具执行的结果作为一个整体利用ToolCallResultEvent事件进行传输。.NET SDK同样没有提供ToolCallChunkEvent事件类型。
public sealed class ToolCallStartEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ToolCallStart;
public string? ParentMessageId { get; set; }
public string ToolCallId { get; set; } = string.Empty;
public string ToolCallName { get; set; } = string.Empty;
}
public sealed class ToolCallEndEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ToolCallEnd;
public string ToolCallId { get; set; } = string.Empty;
}
public sealed class ToolCallArgsEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ToolCallArgs;
public string ToolCallId { get; set; } = string.Empty;
public string Delta { get; set; } = string.Empty;
}
public sealed class ToolCallResultEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ToolCallResult;
public string MessageId { get; set; } = string.Empty;
public string ToolCallId { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public string? Role { get; set; }
}
3.4 状态管理事件(State Message Events)
全量快照 + 增量变更的状态同步由如下两个事件完成。Agent利用StateSnapshotEvent事件将承载自身整体状态的快照传输给前端应用,对于后续针对此快照的增量变更,则依次利用StateDeltaEvent事件同步给前端应用。MessagesSnapshotEvent则是专门同步对话历史设计的事件类型。
public sealed class StateSnapshotEvent : BaseEvent
{
public override string Type => AGUIEventTypes.StateSnapshot;
public JsonElement Snapshot { get; set; }
}
public sealed class StateDeltaEvent : BaseEvent
{
public override string Type => AGUIEventTypes.StateDelta;
public JsonElement Delta { get; set; }
}
public sealed class MessagesSnapshotEvent : BaseEvent
{
public override string Type => AGUIEventTypes.MessagesSnapshot;
public IList<AGUIMessage> Messages { get; set; } = [];
}
3.5 活动事件(Acitivity Events)
活动(Activity)是AG‑UI用来表达Agent正在做什么的消息,用于把任务执行状态清晰地暴露给前端应用,而不是混在自然语言回复里。Agent利用ActivitySnapshotEvent和ActivityDeltaEvent事件向前端应用同步ActivityMessage。ActivitySnapshotEvent用于同步承载某条ActivityMessage整体状态的快照,ActivityDeltaEvent采用JSON Patch的形式用来传输在此快照建立后针对它的更新。
public sealed class ActivitySnapshotEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ActivitySnapshot;
public string MessageId { get; set; } = string.Empty;
public string ActivityType { get; set; } = string.Empty;
public JsonElement Content { get; set; }
public bool? Replace { get; set; }
}
public sealed class ActivityDeltaEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ActivityDelta;
public string MessageId { get; set; } = string.Empty;
public string ActivityType { get; set; } = string.Empty;
public JsonElement Patch { get; set; }
}
3.6 推理事件(Reasoning Events)
如下的七个用来传入承载LLM推理内容的消息。ReasoningStartEvent和ReasoningEndEvent作为整个推理流程的起止边界,其MessageId属性表示推理上下文的ID。针对每个推理消息的传输,既可以利用ReasoningMessageStartEvent->ReasoningMessageContentEvent->ReasoningMessageEndEvent这种严格确定边界的模式来传输,也可以采用单一ReasoningMessageChunkEvent事件来传输。后者这几种事件的MessageId属性表示针对ReasoningMessage的ID。
public sealed class ReasoningStartEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningStart;
public string MessageId { get; set; } = string.Empty;
}
public sealed class ReasoningEndEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningEnd;
public string MessageId { get; set; } = string.Empty;
}
public sealed class ReasoningMessageStartEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningMessageStart;
public string MessageId { get; set; } = string.Empty;
public string Role { get; set; } = "reasoning";
}
public sealed class ReasoningMessageEndEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningMessageEnd;
public string MessageId { get; set; } = string.Empty;
}
public sealed class ReasoningMessageContentEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningMessageContent;
public string MessageId { get; set; } = string.Empty;
public string Delta { get; set; } = string.Empty;
}
public sealed class ReasoningMessageChunkEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningMessageChunk;
public string? MessageId { get; set; }
public string? Delta { get; set; }
}
如果推理的内容不便公开,可以利用ReasoningEncryptedValueEvent事件携带加密的推理内容。这使得Agent既能够在对话轮次之间保留推理状态,而无需向前端应用暴露原始内容。前端应用以不透明的方式存储和转发这些加密值——只有Agent(或授权的后端)才能对其进行解密。ReasoningEncryptedValueEvent既可以承载ReasoningMessage或者工具调用的密文,具体的场景由作为子类型的Subtype决定("tool-call"或者 “message”),EntityId表示ReasoningMessage或者工具调用的ID。
public sealed class ReasoningEncryptedValueEvent : BaseEvent
{
public override string Type => AGUIEventTypes.ReasoningEncryptedValue;
public string Subtype { get; set; } = string.Empty;
public string EntityId { get; set; } = string.Empty;
public string EncryptedValue { get; set; } = string.Empty;
}
3.8 特殊事件
除了上述这些常规的事件,AG-UI还定义了两个特殊的事件类型。RawEvent可以用作容器,用于存放来自外部系统或不遵循AG-UI协议的源的事件。这种事件类型通过将事件封装在标准化格式中,实现了与其他基于事件的系统之间的互操作性。CustomEvent事件提供了一种扩展机制,用于实现标准事件类型未涵盖的功能。与充当传递容器的原始事件不同,自定义事件是协议的显式组成部分,但其语义由应用程序定义。
public sealed class RawEvent : BaseEvent
{
public override string Type => AGUIEventTypes.Raw;
public JsonElement Event { get; set; }
public string? Source { get; set; }
}
public sealed class CustomEvent : BaseEvent
{
public override string Type => AGUIEventTypes.Custom;
public string Name { get; set; } = string.Empty;
public JsonElement? Value { get; set; }
}

772

被折叠的 条评论
为什么被折叠?



