[AG-UI详解-03]AG-UI = RunAgentInput + BaseEvent[.NET SDK篇]

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; }
}

除了AGUIUserMessageAGUIActivityMessageContent属性分别返回一个AGUIUserContentJsonElement对象之外,其余消息类型的同名属性直接返回字符串文本作为消息主体内容。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; }
}

除了AGUITextInputContentAGUIBinaryInputContent直接继承自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为上述两种来源定义了对应的类型AGUIInputContentDataSourceAGUIInputContentUrlSource

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对象利用DescriptionValue描述单个上下文数据项。RunAgentInputTools用来向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调用的生命周期,RunStartedEventRunFinishedEvent/RunErrorEvent代表运行Agent的起止边界,StepStartedEventStepFinishedEvent是在整个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用来向前端应用实时返回生成的文本消息内容。TextMessageStartEventTextMessageEndEvent是针对单条文本消息创建的起止边界,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用来向前端应用传输工具调用和工具调用生成的结果。其中ToolCallStartEventToolCallEndEvent事件作为工具传输的起止边界,并在之中利用一个或者多个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利用ActivitySnapshotEventActivityDeltaEvent事件向前端应用同步ActivityMessageActivitySnapshotEvent用于同步承载某条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推理内容的消息。ReasoningStartEventReasoningEndEvent作为整个推理流程的起止边界,其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; }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值