Skip to content

Commit

Permalink
支持群组聊天 (#28)
Browse files Browse the repository at this point in the history
* 创建基础群聊调用

* Update

* Update

* Update

* 支持群组的添加/修改/删除

* Update

* Update

* 添加群组助理更新逻辑

* Add doc

* Update

* Update

* Update

* 更新 README

---------

Co-authored-by: Anran Zhang <anranzhang@microsoft.com>
  • Loading branch information
Richasy and Anran Zhang authored Jun 28, 2024
1 parent 99af30e commit 5f7d34c
Show file tree
Hide file tree
Showing 104 changed files with 4,325 additions and 287 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

A Windows desktop application that integrates chat, text-to-image, text-to-speech, and machine translation, supports the current mainstream AI services, and offers an excellent desktop AI experience.

<a title="Get from Microsoft" href="https://www.microsoft.com/store/apps/9NB0NB3MLQTM?launch=true&amp;mode=full" target="_blank"><picture><source srcset="https://get.microsoft.com/images/en-US%20light.svg" media="(prefers-color-scheme: dark)"><source srcset="https://get.microsoft.com/images/en-US%20dark.svg" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"><img src="https://get.microsoft.com/images/en-US%20dark.svg" width="144"></picture></a>

English · [简体中文](./README.zh-CN.md)

<!-- SHIELD GROUP -->
Expand Down
2 changes: 2 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

集聊天、文生图、文本转语音、机器翻译于一身的 Windows 桌面应用,支持目前主流的 AI 服务,提供优秀的桌面 AI 体验。

<a title="从Microsoft获取" href="https://www.microsoft.com/store/apps/9NB0NB3MLQTM?launch=true&amp;mode=full" target="_blank"><picture><source srcset="https://get.microsoft.com/images/zh-CN%20light.svg" media="(prefers-color-scheme: dark)"><source srcset="https://get.microsoft.com/images/zh-CN%20dark.svg" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"><img src="https://get.microsoft.com/images/zh-CN%20dark.svg" width="144"></picture></a>

[English](./README.md) · 简体中文

<!-- SHIELD GROUP -->
Expand Down
Binary file added docs/assets/en/create-group-item.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/en/group-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/zh/create-group-item.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/zh/group-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions docs/en/group-chat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Group Chat (Experiment)

> [!TIP]
> Group chat is an experimental feature that became available in version `2.2307.1.0`. Currently, it is not applicable to all models. For specific limitations, please refer to [Limitations](#limitations).
We know that models can take on specific roles to perform certain tasks. Have you ever thought about letting different models play different roles and then working together to solve target problems?

Group chat is an exploration in this direction.

## Prerequisites

Since it's a group chat, you need to create "group members" first.

These group members are **agents**. You need to create two or more agents according to the guidelines in [Agent and Preset](./agent-preset) before you can gather them into a group.

## Limitations

1. Some models only support one-on-one interactions (their message queue strictly requires an alternating sequence of one AI message and one user message). Such models (e.g., ERNIE `QianFan`) do not support group chats because multiple agent messages are generated at once in a group.
2. Different models have different context windows. Please try to place models with smaller context windows at the front of the group members list.
3. Group members speak in order, and users can only adjust the target after each round of speaking but cannot interrupt in the middle (though they can cancel in the middle).

## Simple Example

### Creating agents

Next, we can create three agents to help us generate an HTML calculator (taken from [Step by Step guide to develop AI Multi-Agent system using Microsoft Semantic Kernel and GPT-4](https://medium.com/@akshaykokane09/step-by-step-guide-to-develop-ai-multi-agent-system-using-microsoft-semantic-kernel-and-gpt-4o-f5991af40ea6)).

| Agent Name | Program Manager |
| -------------- | ---------------- |
| Instruction | You are a program manager which will take the requirement and create a plan for creating an app. The Program Manager understands the user requirements and forms the detailed documents with requirements and costing. |

| Agent Name | Software Engineer |
| -------------- | ----------------- |
| Instruction | You are a Software Engineer, and your goal is to develop a web app using HTML and JavaScript (JS) by considering all the requirements given by the Program Manager. |

| Agent Name | Manager |
| -------------- | --------------- |
| Instruction | You are a manager who will review the software engineer's code and make sure all client requirements are completed. Once all client requirements are completed, you can approve the request by just responding "approve". |

When creating agents, you can set their models freely as long as the models meet the [Limitations](#limitations).

### Creating a Group

<div style="max-width:300px">

![Creating a Group](../assets/en/create-group-item.png)

</div>

Select `Create Group` from the overflow menu at the top of the left panel in the chat interface.

Configure the basic settings in the group panel that pops up.

![Group Panel](../assets/en/group-dialog.png)

Besides choosing agents, you'll notice two other parameters:

1. **Termination Text**
This is a `brake pad`. When the AI-generated text contains the termination text you set, the application will judge that the goal has been achieved and terminate the current group conversation. In this example, the project manager will ultimately determine if the goal has been achieved and give the "approve" verdict, so our termination text here is `approve`.
2. **Maximum Session Rounds**
A complete round is considered after all agents generate their content once. AI will negotiate for one round after another based on your set goal. To avoid token management getting out of control, you can set a maximum session round. Once it reaches this round, it will forcibly interrupt the AI discussion. Based on our preset, considering that the project manager might suggest modifications, we can set the maximum rounds to 5.

> [!IMPORTANT]
> Currently, group sessions will call agents sequentially from top to bottom according to the order you set. So, please plan the execution order when creating the group.
### Start the Conversation

Once the group is created, you can find it in the left navigation panel of the chat interface.

Click to start the conversation.

In the input box, you can enter the following:

```text
I want to develop a calculator app. It should have a basic calculator appearance and get final approval from the project manager.
```

Then, you will see the `Product Manager`, `Software Engineer`, and `Project Manager` speak in turn, ultimately generating code that includes `HTML`, `CSS`, and `JavaScript`.

This is the charm of AI groups.
80 changes: 80 additions & 0 deletions docs/group-chat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 群组聊天(实验)

> [!TIP]
> 群组聊天是一项实验性功能,在 `2.2307.1.0` 中开始提供,目前并不是所有模型都能适用,具体的限制条件请查看 [限制条件](#限制条件)
我们知道,模型是可以扮演角色执行特定任务的,那么你是否想过,让不同的模型扮演不同的角色,然后让它们协同工作,解决目标问题呢?

群组聊天就是对这个方向的探索。

## 前置条件

既然是群组聊天,那你就需要先创建“群员”。

这个群员就是 **助理** ,你需要先按照 [助理与预设](./agent-preset) 中的指引创建两个以上的助理,然后才能把它们归集到一个群组中。

## 限制条件

1. 有些模型仅支持一对一(其消息队列严格要求一条 AI 信息,一条用户信息交替),这类模型(比如文心一言)是不受群组支持的,因为群组内一次会产生多条助理信息。
2. 不同模型的上下文窗口不同,请尽量把上下文窗口小的模型放在群成员前列。
3. 群组成员按照先后顺序发言,用户只能在每轮发言结束后调整目标,而不能中途插入(但可以中途取消)。

## 简单示例

### 创建助理

接下来,我们可以创建三个助理,来帮助我们生成一个 HTML 的计算器。(取自 [Step by Step guide to develop AI Multi-Agent system using Microsoft Semantic Kernel and GPT-4o](https://medium.com/@akshaykokane09/step-by-step-guide-to-develop-ai-multi-agent-system-using-microsoft-semantic-kernel-and-gpt-4o-f5991af40ea6)

|助理名称|产品经理|
|-|-|
|指令|You are a program manager which will take the requirement and create a plan for creating app. Program Manager understands the user requirements and form the detail documents with requirements and costing.|

|助理名称|软件工程师|
|-|-|
|指令|You are Software Engieer, and your goal is develop web app using HTML and JavaScript (JS) by taking into consideration all the requirements given by Program Manager.|

|助理名称|项目经理|
|-|-|
|指令|You are manager which will review software engineer code, and make sure all client requirements are completed. Once all client requirements are completed, you can approve the request by just responding "approve"|

创建助理时,你可以随意设定其模型,只要该模型符合 [限制条件](#限制条件) 即可。

### 创建群组

<div style="max-width:300px">

![创建群组](./assets/zh/create-group-item.png)

</div>

在聊天界面左侧面板顶部的溢出菜单中选择 `创建群组`

在弹出的群组面板中进行基本的配置。

![群组面板](./assets/zh/group-dialog.png)

除了选择助理外,你会注意到还有两个参数:

1. **终止文本**
这是一个 `刹车片`,当 AI 生成的文本中出现你设定的终止文本后,应用会判断目标达成,然后终止当前的群组对话。在当前示例中,项目经理最终会判断目标是否达成,并给出 `approve` 的判定,所以我们这里的终止文本就是 `approve`
2. **最大会话轮次**
所有助理依次生成完内容记作一轮,AI 会根据你设定的目标进行一轮又一轮的磋商,为了避免我们的 token 管理失控,你可以设定最大会话轮次,达到该轮次,则强行中断 AI 们的讨论。根据我们的预设,考虑到项目经理可能提出修改意见,这里我们可以把最大轮次设置为5。

> [!IMPORTANT]
> 目前群组会话会按照你设置的助理顺序从上往下依次调用,所以在创建群组时请规划好执行顺序。
### 开始对话

创建群组后,你就能在聊天界面的左侧导航面板中找到你创建的群组了。

点击即可开始对话。

在输入框中,我们可以输入以下内容:

```text
我想开发一个计算器应用。具备基础的计算器外观,并从项目经理处获得最终许可。
```

然后,你就可以看到 `产品经理``软件工程师``项目经理` 依次发言,最终生成了一份包含 `HTML`, `CSS``Javascript` 的代码。

这就是 AI 群组的魅力。
161 changes: 137 additions & 24 deletions src/Console/RodelChat.Console/ChatService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Rodel. All rights reserved.

using System.Text.Json;
#define USE_GROUP

using Microsoft.Extensions.Hosting;
using RodelAgent.Interfaces;
using RodelAgent.Statics;
Expand All @@ -15,6 +16,7 @@
public sealed class ChatService : IHostedService
{
private readonly IChatClient _chatClient;
private readonly IChatParametersFactory _chatParametersFactory;
private readonly IStringResourceToolkit _localizer;
private ProviderType _currentType;
private ChatSession _currentSession;
Expand All @@ -23,10 +25,12 @@ public sealed class ChatService : IHostedService
/// Initializes a new instance of the <see cref="ChatService"/> class.
/// </summary>
public ChatService(
IChatParametersFactory chatParametersFactory,
IChatClient chatClient,
IHostApplicationLifetime lifetime,
IStringResourceToolkit localizer)
{
_chatParametersFactory = chatParametersFactory;
_chatClient = chatClient;
_localizer = localizer;
lifetime.ApplicationStopping.Register(_chatClient.Dispose);
Expand All @@ -37,11 +41,28 @@ public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
#if USE_PRESET
var preset = AskSessionPreset();
var session = _chatClient.CreateSession(preset);
_currentType = session.Provider;
await RunAIAsync(session);
#if USE_GROUP
var agents = CreateNewsAgents();
var message = AskInput();
var preset = new ChatGroupPreset
{
Agents = agents.Select(a => a.Id).ToList(),
Id = "group",
MaxRounds = 6,
Name = "Group",
TerminateText = ["approve", "批准"],
};
var group = _chatClient.CreateSession(preset);
var chatMsg = ChatMessage.CreateUserMessage(message);
await _chatClient.SendGroupMessageAsync(
group.Id,
chatMsg,
(response) =>
{
HandleMessageResponse(response);
},
agents,
CancellationToken.None);
#else
var provider = AskProvider();
await RunAIAsync(provider);
Expand Down Expand Up @@ -71,6 +92,111 @@ private async Task RunAIAsync(ChatSession session)
await LoopMessageAsync(session);
}

private List<ChatSessionPreset> CreateCoderAgents()
{
_ = this;
var progamManagerText =
"""
You are a program manager which will take the requirement and create a plan for creating app. Program Manager understands the
user requirements and form the detail documents with requirements and costing.
""";

var softwareEngineerText =
"""
You are Software Engieer, and your goal is develop web app using HTML and JavaScript (JS) by taking into consideration all
the requirements given by Program Manager.
""";

var managerText =
"""
You are manager which will review software engineer code, and make sure all client requirements are completed.
Once all client requirements are completed, you can approve the request by just responding "approve"
""";

var programManager = new ChatSessionPreset
{
Name = "Program Manager",
Provider = ProviderType.AzureOpenAI,
Model = "gpt-4o",
Id = "program-manager",
SystemInstruction = progamManagerText,
Parameters = _chatParametersFactory.CreateChatParameters(ProviderType.AzureOpenAI),
};

var softwareEngineer = new ChatSessionPreset
{
Name = "Software Engineer",
Provider = ProviderType.ZhiPu,
Model = "glm-4",
Id = "software-engineer",
SystemInstruction = softwareEngineerText,
Parameters = _chatParametersFactory.CreateChatParameters(ProviderType.ZhiPu),
};

var manager = new ChatSessionPreset
{
Name = "Manager",
Provider = ProviderType.AzureOpenAI,
Model = "gpt-4o",
Id = "manager",
SystemInstruction = managerText,
Parameters = _chatParametersFactory.CreateChatParameters(ProviderType.AzureOpenAI),
};

return [programManager, softwareEngineer, manager];
}

private List<ChatSessionPreset> CreateNewsAgents()
{
var reporterText =
"""
You are a reporter which will take the news and create a news article. Reporter understands the news and form the detail article with
news and images.
""";
var editorText =
"""
You are editor, and your goal is to review the news article, and make sure all the news are correct.
""";

var publishManagerText =
"""
You are publish manager which will review editor news article, and make sure all news are correct.
Once all news are correct, you can approve the request (with 'approve' keyword) and give final news article to publish.
""";

var reporter = new ChatSessionPreset
{
Name = "Reporter",
Provider = ProviderType.AzureOpenAI,
Model = "gpt-4o",
Id = "reporter",
SystemInstruction = reporterText,
Parameters = _chatParametersFactory.CreateChatParameters(ProviderType.AzureOpenAI),
};

var editor = new ChatSessionPreset
{
Name = "Editor",
Provider = ProviderType.ZhiPu,
Model = "glm-4",
Id = "editor",
SystemInstruction = editorText,
Parameters = _chatParametersFactory.CreateChatParameters(ProviderType.ZhiPu),
};

var publishManager = new ChatSessionPreset
{
Name = "Publish Manager",
Provider = ProviderType.AzureOpenAI,
Model = "gpt-4o",
Id = "publish-manager",
SystemInstruction = publishManagerText,
Parameters = _chatParametersFactory.CreateChatParameters(ProviderType.AzureOpenAI),
};

return [reporter, editor, publishManager];
}

private async Task LoopMessageAsync(ChatSession session)
{
#if USE_SYSTEM_PROMPT
Expand Down Expand Up @@ -127,24 +253,6 @@ private ProviderType AskProvider()
return provider;
}

private ChatSessionPreset AskSessionPreset()
{
// Get presets file list in Presets folder without extension name.
var presetFiles = Directory.GetFiles(Path.Combine(AppContext.BaseDirectory, "Presets"), "*.json");
var presetNames = presetFiles.Select(p => Path.GetFileNameWithoutExtension(p)!).ToList();

var presetName = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title(GetString("SelectPreset"))
.PageSize(10)
.AddChoices(presetNames));

var presetFile = Path.Combine(AppContext.BaseDirectory, "Presets", $"{presetName}.json");
var presetContent = File.ReadAllText(presetFile);
var preset = JsonSerializer.Deserialize<ChatSessionPreset>(presetContent);
return preset;
}

private ChatModel AskModel(ProviderType type)
{
var models = _chatClient.GetModels(type);
Expand Down Expand Up @@ -264,6 +372,11 @@ private void PrintAssistantMessage(ChatMessage response)
Padding = new Padding(2, 2, 2, 2),
};

if (!string.IsNullOrEmpty(response.Author))
{
panel.Header = new PanelHeader(response.Author);
}

AnsiConsole.Write(panel);
}

Expand Down
Binary file modified src/Core/RodelAgent.Context/Assets/chat.db
Binary file not shown.
Loading

0 comments on commit 5f7d34c

Please sign in to comment.