16、AgentChat

16、AgentChat

AgentChat

AgentChat提供了一个框架,探讨多智能体(AI Agents)在对话场景中的协作机制,旨在通过分工合作提升复杂任务解决能力。该框架不仅要确定在每个回合中哪个Agent应该响应,还要评估对话何时达到预期目标并及时终止协作。

目前基于该框架可以实现多个不同类型的Agent的相互的交互。也就是多个ChatCompletionAgent能够在同一次对话中协同工作。

AgentChat 在SK中作为一个抽象基类存在,AgentGroupChatAgentChat的具体实现:

  • 定义了Agent之间协作的入口点,支持单个智能体或多个智能体启动
  • 支持两种响应模式:signle-turnmulti-turn
  • 内置多种 Agent 选择策略,也可灵活定制策略
  • 针对multi-turn内置了多种会话终止策略
  • 提供了状态管理功能

当前AgentChat 处于实验阶段,后期可能会有大的变更!!!

使用时要禁用warning:#pragma warning disable SKEXP0110

1
2
3
4
5
6
7
8
9
//通过引入Config/PrepareEnvWithDI.cs文件来快速安装依赖包并导入已抽象的类文件,然后注册并激活AI 服务:

#!import Config/PrepareEnvWithDI.cs

using PolyglotKernel= Microsoft.DotNet.Interactive.Kernel;// 引入交互式的内核命名空间,以便用户输入
var aiProviderCode = await PolyglotKernel.GetInputAsync("请输入AI服务提供商编码:");

var kernel = GetKernel(aiProviderCode);
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
1
2
#r "nuget:Microsoft.SemanticKernel.Agents.Core,*-*"
#r "nuget: Microsoft.SemanticKernel.Agents.OpenAI, *-*"

单智能体启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using Microsoft.SemanticKernel.Agents;
using PolyglotKernel = Microsoft.DotNet.Interactive.Kernel;// 引入交互式的内核命名空间,以便用户输入

#pragma warning disable SKEXP0110

// Create an empty chat
AgentGroupChat chat = new ();

// Create a chat completion agent
ChatCompletionAgent agent =
new()
{
Name = "QA-Agent",
Instructions = "Ask me anything!",
Kernel = kernel
};

// Add the agent to the chat
chat.AddAgent(agent);

// Get a user request and then add it to the chat
var userRequest = await PolyglotKernel.GetInputAsync("请输入您的问题:");
ChatMessageContent message = new(AuthorRole.User, userRequest);
chat.AddChatMessage(message);

// Invoke the agent and display the response
await foreach (ChatMessageContent message in chat.InvokeAsync(agent))
{
message.Display();
}

// Get the chat history for the agent
var agentHistory = await chat.GetChatMessagesAsync(agent).ToArrayAsync();

agentHistory.Display();

多智能体协作

以下的示例中将定义两个智能体,这两个智能体通过协作来实现用户文案的优化。

  • Reviewer Agent:审查并提供改进方向
  • Writer Agent:负责根据建议重写内容
1
2
const string ReviewerName = "Reviewer";
const string WriterName = "Writer";

定义剪贴板插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System.ComponentModel;
using System.Diagnostics;

private sealed class ClipboardAccess
{
[KernelFunction]
[Description("Copies the provided content to the clipboard.")]
public static void SetClipboard(string content)
{
if (string.IsNullOrWhiteSpace(content))
{
return;
}
using Process clipProcess = Process.Start(
new ProcessStartInfo
{
FileName = "clip",
RedirectStandardInput = true,
UseShellExecute = false,
});
clipProcess.StandardInput.Write(content);
clipProcess.StandardInput.Close();
}
}

定义 ReviewerAgent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Agents;

var toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();

ChatCompletionAgent agentReviewer =
new()
{
Name = ReviewerName,
Instructions =
"""
你的责任是审查并识别如何改进用户提供的内容。
如果用户对已提供的内容提出了输入或指导,请说明如何解决这些输入。
切勿直接进行修正或提供示例。
一旦内容在后续回复中更新,你将再次审查内容,直到满意为止。
始终使用可用工具将满意内容复制到剪贴板,并通知用户。

**规则:**
- 仅识别具体且可操作的建议。
- 确认之前的建议是否已解决。
- 切勿重复之前的建议。
""",
Kernel = toolKernel,
Arguments =
new KernelArguments(
new OpenAIPromptExecutionSettings()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
})
};

定义 WriterAgent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ChatCompletionAgent agentWriter =
new()
{
Name = WriterName,
Instructions =
"""
你的唯一职责是根据审查建议重写内容。

- 始终应用所有审查指示。
- 始终完整修订内容,无需解释。
- 切勿直接与用户对话。
""",
Kernel = kernel,
};

定义 Chat

为了确保两个Agent能够协作完成指定任务,定义AgentGroupChat时需要考虑:

  1. 选择Agent发言顺序的策略
  2. 确定何时退出聊天循环

定义选择策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma warning disable SKEXP0110

KernelFunction selectionFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
检查提供的 RESPONSE 并选择下一位参与者。
仅返回被选中参与者的名称,无需解释。
切勿选择当前 RESPONSE 中指定的参与者。

仅从以下参与者中选择:
- {{{ReviewerName}}}
- {{{WriterName}}}

选择下一位参与者时始终遵循以下规则:
- 若 RESPONSE 是用户输入,则由 {{{ReviewerName}}} 接续。
- 若 RESPONSE 来自 {{{ReviewerName}}},则由 {{{WriterName}}} 接续。
- 若 RESPONSE 来自 {{{WriterName}}},则由 {{{ReviewerName}}} 接续。

RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");

selectionFunction.Display();

定义终止策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma warning disable SKEXP0110

const string TerminationToken = "yes";

KernelFunction terminationFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
检查 RESPONSE 并确定内容是否已被视为满意。
如果内容满意,仅用一个词回应,无需解释:{{{TerminationToken}}}。
如果提供了具体建议,则内容不满意。
如果未提出修正建议,则内容满意。

RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");

这两种策略仅需读取最新消息即可,因此可以手动指定ChatHistoryTruncationReducer 来缩减令牌:

1
2
3
4
5
using Microsoft.SemanticKernel.ChatCompletion;

#pragma warning disable SKEXP0001

ChatHistoryTruncationReducer historyReducer = new(1);

组合Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#pragma warning disable SKEXP0110

using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;

AgentGroupChat chat =
new(agentReviewer, agentWriter)
{
ExecutionSettings = new AgentGroupChatSettings
{
SelectionStrategy =
new KernelFunctionSelectionStrategy(selectionFunction, kernel)
{
// Always start with the editor agent.
InitialAgent = agentReviewer,
// Save tokens by only including the final response
HistoryReducer = historyReducer,
// The prompt variable name for the history argument.
HistoryVariableName = "lastmessage",
// Returns the entire result value as a string.
ResultParser = (result) => result.GetValue<string>() ?? agentReviewer.Name
},
TerminationStrategy =
new KernelFunctionTerminationStrategy(terminationFunction, kernel)
{
// Only evaluate for editor's response
Agents = [agentReviewer],
// Save tokens by only including the final response
HistoryReducer = historyReducer,
// The prompt variable name for the history argument.
HistoryVariableName = "lastmessage",
// Limit total number of turns
MaximumIterations = 12,
// Customer result parser to determine if the response is "yes"
ResultParser = (result) => result.GetValue<string>()?.Contains(TerminationToken, StringComparison.OrdinalIgnoreCase) ?? false
}
}
};

Console.WriteLine("Ready!");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System.Text.Json;
using PolyglotKernel = Microsoft.DotNet.Interactive.Kernel;// 引入交互式的内核命名空间,以便用户输入
#pragma warning disable SKEXP0001

bool isComplete = false;
do
{
Console.WriteLine();
Console.Write("> ");
string input = await PolyglotKernel.GetInputAsync();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
input = input.Trim();
if (input.Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
if (input.Equals("RESET", StringComparison.OrdinalIgnoreCase))
{
await chat.ResetAsync();
Console.WriteLine("[Conversation has been reset]");
continue;
}
chat.AddChatMessage(new ChatMessageContent(AuthorRole.User, input));
chat.IsComplete = false;
try
{
await foreach (ChatMessageContent response in chat.InvokeAsync())
{
Console.WriteLine();
Console.WriteLine($"{response.AuthorName.ToUpperInvariant()}:{Environment.NewLine}{response.Content}");
}
}
catch (HttpOperationException exception)
{
Console.WriteLine(exception.Message);
if (exception.InnerException != null)
{
Console.WriteLine(exception.InnerException.Message);
if (exception.InnerException.Data.Count > 0)
{
Console.WriteLine(JsonSerializer.Serialize(exception.InnerException.Data, new JsonSerializerOptions() { WriteIndented = true }));
}
}
}
} while (!isComplete);

> 
REVIEWER:
你的内容提供了一些信息,但还需要进一步改善才能更加清晰和全面。以下是一些具体且可操作的建议:  

1. **结构化信息**:将信息分段,并使用标题或编号来帮助读者更容易地理解各个部分。例如,你可以使用“框架概述”、“响应模式”和“策略”这类小标题。

2. **详细描述每个要素**:当前内容提到了一些专业术语,如“响应模式”和“会话终止策略”。为每个术语添加简短的定义或描述,以帮助理解。例如,解释“signle-turn”和“multi-turn”模式的区别。

3. **实例说明**:提供使用场景或实例,展示`AgentChat`和`AgentGroupChat`在实际应用中的作用。这样可以让读者更具体地理解这些概念。

4. **逻辑流程**:阐述`AgentChat`框架运行的基本流程,比如如何确定哪个Agent响应,以及如何评估对话的终止。

5. **语言简化**:有些句子略显复杂,可以试着简化语言结构,使其更易读。

请根据这些建议进行修改,提升内容的可读性和完整性。

WRITER:
`AgentChat`框架旨在探讨多智能体在对话场景中的协作机制,以提高复杂任务的解决能力。其主要功能包括:  

### 框架概述  
- **任务分工**:通过明确哪个Agent在每个回合中响应,以及何时终止对话,确保协作高效进行。  
- **Agent交互**:支持`ChatCompletionAgent`和`OpenAIAssistantAgent`在同一对话中的协同工作。

### AgentChat在SK中的位置  
- 作为一个抽象基类存在,具体实现为`AgentGroupChat`。

### AgentGroupChat功能  
- **协作入口点**:定义了启动单个智能体或多个智能体的机制。  
- **响应模式**:支持`single-turn`和`multi-turn`两种模式,分别对应单步和多步对话策略。  
- **选择策略**:内置多种Agent选择策略,允许灵活定制以适应特定需求。  
- **会话终止策略**:特别针对`multi-turn`模式提供了多种终止选项,确保对话在适当时机结束。  
- **状态管理**:提供对会话状态的管理功能,便于对话流的控制和监控。

通过这些功能,`AgentGroupChat`框架在多Agent系统中提供了一种结构化的方法来处理复杂对话任务的分配和管理。

REVIEWER:
内容已经更新并复制到剪贴板。改进后的版本更具结构性和详细性。

> 
作者

步步为营

发布于

2025-04-15

更新于

2025-04-15

许可协议