@aigne/example-workflow-group-chat 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.local.example +5 -0
- package/README.md +79 -0
- package/index.ts +158 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Workflow Group Chat Demo
|
|
2
|
+
|
|
3
|
+
This is a demonstration of using [AIGNE Framework](https://github.com/AIGNE-io/aigne-framework) to build a group chat workflow.
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
flowchart LR
|
|
7
|
+
|
|
8
|
+
manager(Group Manager)
|
|
9
|
+
user(User)
|
|
10
|
+
writer(Writer)
|
|
11
|
+
editor(Editor)
|
|
12
|
+
illustrator(Illustrator)
|
|
13
|
+
|
|
14
|
+
manager ==2 request to speak==> writer
|
|
15
|
+
manager --4 request to speak--> illustrator
|
|
16
|
+
|
|
17
|
+
writer -.3 group message.-> manager
|
|
18
|
+
writer -..-> editor
|
|
19
|
+
writer -..-> illustrator
|
|
20
|
+
writer -..-> user
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
classDef inputOutput fill:#f9f0ed,stroke:#debbae,stroke-width:2px,color:#b35b39,font-weight:bolder;
|
|
24
|
+
classDef processing fill:#F0F4EB,stroke:#C2D7A7,stroke-width:2px,color:#6B8F3C,font-weight:bolder;
|
|
25
|
+
|
|
26
|
+
class manager inputOutput
|
|
27
|
+
class user processing
|
|
28
|
+
class writer processing
|
|
29
|
+
class editor processing
|
|
30
|
+
class illustrator processing
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Prerequisites
|
|
34
|
+
|
|
35
|
+
- [Node.js](https://nodejs.org) and npm installed on your machine
|
|
36
|
+
- [OpenAI API key](https://platform.openai.com/api-keys) used to interact with OpenAI API
|
|
37
|
+
- [Pnpm](https://pnpm.io) [Optional] if you want to run the example from source code
|
|
38
|
+
|
|
39
|
+
## Try without Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
export OPENAI_API_KEY=YOUR_OPENAI_API_KEY # setup your OpenAI API key
|
|
43
|
+
|
|
44
|
+
npx -y @aigne/example-workflow-group-chat # run the example
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
### Clone the Repository
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/AIGNE-io/aigne-framework
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Install Dependencies
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd aigne-framework/examples/workflow-group-chat
|
|
59
|
+
|
|
60
|
+
pnpm install
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Setup Environment Variables
|
|
64
|
+
|
|
65
|
+
Setup your OpenAI API key in the `.env.local` file:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
OPENAI_API_KEY="" # setup your OpenAI API key here
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Run the Example
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm start
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
This project is licensed under the MIT License.
|
package/index.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env npx -y bun
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import {
|
|
6
|
+
AIAgent,
|
|
7
|
+
ExecutionEngine,
|
|
8
|
+
FunctionAgent,
|
|
9
|
+
OpenAIChatModel,
|
|
10
|
+
PromptTemplate,
|
|
11
|
+
UserAgent,
|
|
12
|
+
createMessage,
|
|
13
|
+
getMessage,
|
|
14
|
+
logger,
|
|
15
|
+
} from "@aigne/core";
|
|
16
|
+
import inquirer from "inquirer";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
const { OPENAI_API_KEY } = process.env;
|
|
20
|
+
assert(OPENAI_API_KEY, "Please set the OPENAI_API_KEY environment variable");
|
|
21
|
+
|
|
22
|
+
const gpt = new OpenAIChatModel({
|
|
23
|
+
apiKey: OPENAI_API_KEY,
|
|
24
|
+
model: "gpt-4o",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const DEFAULT_TOPIC = "DEFAULT_TOPIC";
|
|
28
|
+
|
|
29
|
+
const writer = AIAgent.from({
|
|
30
|
+
name: "writer",
|
|
31
|
+
description: "Writer for creating any text content",
|
|
32
|
+
publishTopic: DEFAULT_TOPIC,
|
|
33
|
+
memory: { subscribeTopic: DEFAULT_TOPIC },
|
|
34
|
+
instructions: "You are a Writer. You produce good work.",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const editor = AIAgent.from({
|
|
38
|
+
name: "editor",
|
|
39
|
+
description: "Editor for planning and reviewing the content",
|
|
40
|
+
publishTopic: DEFAULT_TOPIC,
|
|
41
|
+
memory: { subscribeTopic: DEFAULT_TOPIC },
|
|
42
|
+
instructions: `\
|
|
43
|
+
You are an Editor. Plan and guide the task given by the user.
|
|
44
|
+
Provide critical feedbacks to the draft and illustration produced by Writer and Illustrator.
|
|
45
|
+
Approve if the task is completed and the draft and illustration meets user's requirements.`,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const generateImage = FunctionAgent.from({
|
|
49
|
+
name: "generate_image",
|
|
50
|
+
description: "Generate an image",
|
|
51
|
+
inputSchema: z.object({
|
|
52
|
+
character_appearance: z.string(),
|
|
53
|
+
style_attributes: z.string(),
|
|
54
|
+
worn_and_carried: z.string(),
|
|
55
|
+
scenario: z.string(),
|
|
56
|
+
}),
|
|
57
|
+
fn: (input) => {
|
|
58
|
+
return { ...input, url: `https://example.com/${randomUUID()}.jpg` };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const illustrator = AIAgent.from({
|
|
63
|
+
name: "illustrator",
|
|
64
|
+
description: "An illustrator for creating images",
|
|
65
|
+
publishTopic: DEFAULT_TOPIC,
|
|
66
|
+
memory: { subscribeTopic: DEFAULT_TOPIC },
|
|
67
|
+
instructions: `\
|
|
68
|
+
You are an Illustrator. You use the generate_image tool to create images given user's requirement.
|
|
69
|
+
Make sure the images have consistent characters and style.`,
|
|
70
|
+
tools: [generateImage],
|
|
71
|
+
toolChoice: "auto",
|
|
72
|
+
outputSchema: z.object({
|
|
73
|
+
images: z
|
|
74
|
+
.array(
|
|
75
|
+
z.object({
|
|
76
|
+
url: z.string().describe("The URL of the image"),
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
79
|
+
.describe("The images created by the illustrator"),
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let isFirstQuestion = true;
|
|
84
|
+
|
|
85
|
+
const user = UserAgent.from({
|
|
86
|
+
name: "user",
|
|
87
|
+
description: "User for providing final approval",
|
|
88
|
+
publishTopic: DEFAULT_TOPIC,
|
|
89
|
+
memory: { subscribeTopic: DEFAULT_TOPIC },
|
|
90
|
+
async process() {
|
|
91
|
+
logger.globalSpinner.stop();
|
|
92
|
+
const { question } = await inquirer.prompt([
|
|
93
|
+
{
|
|
94
|
+
type: "input",
|
|
95
|
+
name: "question",
|
|
96
|
+
message: "💬",
|
|
97
|
+
required: true,
|
|
98
|
+
default: isFirstQuestion
|
|
99
|
+
? "Please write a short story about the gingerbread man with up to 3 photo-realistic illustrations."
|
|
100
|
+
: undefined,
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
103
|
+
isFirstQuestion = false;
|
|
104
|
+
logger.globalSpinner.start();
|
|
105
|
+
return createMessage(question);
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const roles = [writer, editor, illustrator, user];
|
|
110
|
+
|
|
111
|
+
const manager = AIAgent.from({
|
|
112
|
+
name: "manager",
|
|
113
|
+
subscribeTopic: DEFAULT_TOPIC,
|
|
114
|
+
publishTopic: (output) => output.role,
|
|
115
|
+
memory: { subscribeTopic: DEFAULT_TOPIC },
|
|
116
|
+
instructions: PromptTemplate.from(`\
|
|
117
|
+
You are participating in a role-playing game. The available roles are:
|
|
118
|
+
|
|
119
|
+
<roles>
|
|
120
|
+
{{roles}}
|
|
121
|
+
</roles>
|
|
122
|
+
|
|
123
|
+
Instructions:
|
|
124
|
+
1. Read the following conversation history
|
|
125
|
+
2. Identify the last speaking role in the conversation.
|
|
126
|
+
3. If the last role is **not** "user," respond as "user" to approve and continue.
|
|
127
|
+
4. Otherwise, select the next role **logically** based on the context of the conversation (do not repeat the same role unless necessary).
|
|
128
|
+
5. Make sure responses align with the role’s personality and purpose in the game.
|
|
129
|
+
`).format({
|
|
130
|
+
roles: roles.map((i) => `${i.topic}: ${i.description}`).join("\n"),
|
|
131
|
+
}),
|
|
132
|
+
outputSchema: z.object({
|
|
133
|
+
role: z
|
|
134
|
+
.union(assertZodUnionArray(roles.map((i) => z.literal(i.topic))))
|
|
135
|
+
.describe("The next role to play"),
|
|
136
|
+
}),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const engine = new ExecutionEngine({
|
|
140
|
+
model: gpt,
|
|
141
|
+
agents: [user, manager, writer, editor, illustrator],
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
engine.subscribe(DEFAULT_TOPIC, (message) => {
|
|
145
|
+
console.log(
|
|
146
|
+
"------------- Received message -------------\n",
|
|
147
|
+
`${message.source}:`,
|
|
148
|
+
getMessage(message.message) || message.message,
|
|
149
|
+
"\n--------------------------------------------",
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await engine.call(user, {});
|
|
154
|
+
|
|
155
|
+
function assertZodUnionArray<T extends z.ZodType>(union: T[]): [T, T, ...T[]] {
|
|
156
|
+
if (!(union.length >= 2)) throw new Error("Union must have at least 2 items");
|
|
157
|
+
return union as [T, T, ...T[]];
|
|
158
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aigne/example-workflow-group-chat",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "A demonstration of using AIGNE Framework to build a group chat workflow",
|
|
5
|
+
"author": "Arcblock <blocklet@arcblock.io> https://github.com/blocklet",
|
|
6
|
+
"homepage": "https://github.com/AIGNE-io/aigne-framework/tree/main/examples/workflow-group-chat",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/AIGNE-io/aigne-framework"
|
|
11
|
+
},
|
|
12
|
+
"bin": "index.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
".env.local.example",
|
|
15
|
+
"*.ts",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"openai": "^4.89.0",
|
|
20
|
+
"zod": "^3.24.2",
|
|
21
|
+
"@aigne/core": "^1.3.0"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"start": "npx -y bun run index.ts",
|
|
25
|
+
"lint": "tsc --noEmit"
|
|
26
|
+
}
|
|
27
|
+
}
|