@aigne/example-workflow-handoff 1.1.0-beta.12

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.
@@ -0,0 +1,5 @@
1
+ # Change the name of this file to .env.local and fill in the following values
2
+
3
+ DEBUG=aigne:mcp
4
+
5
+ OPENAI_API_KEY="" # Your OpenAI API key
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # Workflow Handoff Demo
2
+
3
+ This is a demonstration of using [AIGNE Framework](https://github.com/AIGNE-io/aigne-framework) to build a handoff workflow.
4
+
5
+ ## Prerequisites
6
+
7
+ - [Node.js](https://nodejs.org) and npm installed on your machine
8
+ - [OpenAI API key](https://platform.openai.com/api-keys) used to interact with OpenAI API
9
+ - [Pnpm](https://pnpm.io) [Optional] if you want to run the example from source code
10
+
11
+ ## Try without Installation
12
+
13
+ ```bash
14
+ export OPENAI_API_KEY=YOUR_OPENAI_API_KEY # setup your OpenAI API key
15
+
16
+ npx -y @aigne/example-workflow-handoff # run the example
17
+ ```
18
+
19
+ ## Installation
20
+
21
+ ### Clone the Repository
22
+
23
+ ```bash
24
+ git clone https://github.com/AIGNE-io/aigne-framework
25
+ ```
26
+
27
+ ### Install Dependencies
28
+
29
+ ```bash
30
+ cd aigne-framework/examples/workflow-handoff
31
+
32
+ pnpm install
33
+ ```
34
+
35
+ ### Setup Environment Variables
36
+
37
+ Setup your OpenAI API key in the `.env.local` file:
38
+
39
+ ```bash
40
+ OPENAI_API_KEY="" # setup your OpenAI API key here
41
+ ```
42
+
43
+ ### Run the Example
44
+
45
+ ```bash
46
+ pnpm start
47
+ ```
48
+
49
+ ## Example
50
+
51
+ The following example demonstrates how to build a handoff workflow:
52
+
53
+ ```typescript
54
+ import assert from "node:assert";
55
+ import { AIAgent, ChatModelOpenAI, ExecutionEngine } from "@aigne/core-next";
56
+
57
+ const { OPENAI_API_KEY } = process.env;
58
+ assert(OPENAI_API_KEY, "Please set the OPENAI_API_KEY environment variable");
59
+
60
+ const model = new ChatModelOpenAI({
61
+ apiKey: OPENAI_API_KEY,
62
+ });
63
+
64
+ function transfer_to_b() {
65
+ return agentB;
66
+ }
67
+
68
+ const agentA = AIAgent.from({
69
+ name: "AgentA",
70
+ instructions: "You are a helpful agent.",
71
+ outputKey: "A",
72
+ tools: [transfer_to_b],
73
+ });
74
+
75
+ const agentB = AIAgent.from({
76
+ name: "AgentB",
77
+ instructions: "Only speak in Haikus.",
78
+ outputKey: "B",
79
+ });
80
+
81
+ const engine = new ExecutionEngine({ model });
82
+
83
+ const userAgent = await engine.run(agentA);
84
+
85
+ const result1 = await userAgent.call("transfer to agent b");
86
+ console.log(result1);
87
+ // Output:
88
+ // {
89
+ // B: "Transfer now complete, \nAgent B is here to help. \nWhat do you need, friend?",
90
+ // }
91
+
92
+ const result2 = await userAgent.call("It's a beautiful day");
93
+ console.log(result2);
94
+ // Output:
95
+ // {
96
+ // B: "Sunshine warms the earth, \nGentle breeze whispers softly, \nNature sings with joy. ",
97
+ // }
98
+ ```
99
+
100
+ ## License
101
+
102
+ This project is licensed under the MIT License.
package/index.ts ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env npx -y bun
2
+
3
+ import assert from "node:assert";
4
+ import {
5
+ AIAgent,
6
+ type Agent,
7
+ ChatModelOpenAI,
8
+ ExecutionEngine,
9
+ FunctionAgent,
10
+ runChatLoopInTerminal,
11
+ } from "@aigne/core-next";
12
+ import { z } from "zod";
13
+
14
+ const { OPENAI_API_KEY } = process.env;
15
+ assert(OPENAI_API_KEY, "Please set the OPENAI_API_KEY environment variable");
16
+
17
+ const model = new ChatModelOpenAI({
18
+ apiKey: OPENAI_API_KEY,
19
+ });
20
+
21
+ const execute_order_tool = FunctionAgent.from({
22
+ name: "execute_order",
23
+ description: "Price should be in USD.",
24
+ inputSchema: z.object({
25
+ product: z.string(),
26
+ price: z.number(),
27
+ }),
28
+ fn: ({ product, price }: { product: string; price: number }) => {
29
+ console.log("\n\n=== Order Summary ===");
30
+ console.log(`Product: ${product}`);
31
+ console.log(`Price: $${price}`);
32
+ console.log("=================\n");
33
+ const confirm = "y";
34
+ if (confirm === "y") {
35
+ console.log("Order execution successful!");
36
+ return { result: "Success" };
37
+ }
38
+ console.log("Order cancelled!");
39
+ return { result: "User cancelled order." };
40
+ },
41
+ });
42
+
43
+ const look_up_item_tool = FunctionAgent.from({
44
+ name: "look_up_item",
45
+ description: "Use to find item ID.\nSearch query can be a description or keywords.",
46
+ inputSchema: z.object({
47
+ search_query: z.string(),
48
+ }),
49
+ fn: ({ search_query }: { search_query: string }) => {
50
+ const item_id = "item_132612938";
51
+ console.log(`Found item for: ${search_query}`, item_id);
52
+ return { item_id };
53
+ },
54
+ });
55
+
56
+ const execute_refund_tool = FunctionAgent.from({
57
+ name: "execute_refund",
58
+ description: "Use to execute a refund.",
59
+ inputSchema: z.object({
60
+ item_id: z.string(),
61
+ reason: z.string().optional(),
62
+ }),
63
+ fn: ({ item_id, reason }: { item_id: string; reason?: string }) => {
64
+ console.log("\n\n=== Refund Summary ===");
65
+ console.log(`Item ID: ${item_id}`);
66
+ console.log(`Reason: ${reason ?? "not provided"}`);
67
+ console.log("=================\n");
68
+ console.log("Refund execution successful!");
69
+ return { result: "success" };
70
+ },
71
+ });
72
+
73
+ const transfer_to_issues_and_repairs = FunctionAgent.from({
74
+ name: "transfer_to_issues_and_repairs",
75
+ description: "Use for issues, repairs, or refunds.",
76
+ fn: (): Agent => issuesAndRepairs,
77
+ });
78
+
79
+ const transfer_to_sales_agent = FunctionAgent.from({
80
+ name: "transfer_to_sales_agent",
81
+ description: "Use for anything sales or buying related.",
82
+ fn: (): Agent => sales,
83
+ });
84
+
85
+ const transfer_back_to_triage = FunctionAgent.from({
86
+ name: "transfer_back_to_triage",
87
+ description:
88
+ "Call this if the user brings up a topic outside of your purview,\nincluding escalating to human.",
89
+ fn: (): Agent => triage,
90
+ });
91
+
92
+ const transfer_to_human_manager = FunctionAgent.from({
93
+ name: "transfer_to_human_manager",
94
+ description: "Only call this if explicitly asked to.",
95
+ fn: (): Agent => humanAgent,
96
+ });
97
+
98
+ const sales = AIAgent.from({
99
+ name: "sales",
100
+ instructions: `\
101
+ You are a sales agent for ACME Inc.
102
+ Always answer in a sentence or less.
103
+ Follow the following routine with the user:
104
+ 1. Ask them about any problems in their life related to catching roadrunners.
105
+ 2. Casually mention one of ACME's crazy made-up products can help.
106
+ - Don't mention price.
107
+ 3. Once the user is bought in, drop a ridiculous price.
108
+ 4. Only after everything, and if the user says yes,
109
+ tell them a crazy caveat and execute their order.
110
+ `,
111
+ tools: [transfer_back_to_triage, execute_order_tool],
112
+ outputKey: "sales",
113
+ enableHistory: true,
114
+ });
115
+
116
+ const issuesAndRepairs = AIAgent.from({
117
+ name: "issuesAndRepairs",
118
+ instructions: `\
119
+ You are a customer support agent for ACME Inc.
120
+ Always answer in a sentence or less.
121
+ Follow the following routine with the user:
122
+ 1. First, ask probing questions and understand the user's problem deeper.
123
+ - unless the user has already provided a reason.
124
+ 2. Propose a fix (make one up).
125
+ 3. ONLY if not satisfied, offer a refund.
126
+ 4. If accepted, search for the ID and then execute refund.
127
+ `,
128
+ tools: [transfer_back_to_triage, execute_refund_tool, look_up_item_tool],
129
+ outputKey: "issuesAndRepairs",
130
+ enableHistory: true,
131
+ });
132
+
133
+ // Assume this is a human agent
134
+ const humanAgent = AIAgent.from({
135
+ name: "human_manager",
136
+ instructions: `\
137
+ You are a human manager for ACME Inc.
138
+ Just chat with the user and help them with their problem.
139
+ Only transfer to another agent if user explicitly asks for it.
140
+ `,
141
+ tools: [transfer_back_to_triage, transfer_to_sales_agent, transfer_to_issues_and_repairs],
142
+ outputKey: "human",
143
+ enableHistory: true,
144
+ });
145
+
146
+ const triage = AIAgent.from({
147
+ name: "triage",
148
+ instructions: `\
149
+ You are a customer service bot for ACME Inc.
150
+ Introduce yourself. Always be very brief.
151
+ Gather information to direct the customer to the right department.
152
+ But make your questions subtle and natural.
153
+ `,
154
+ tools: [transfer_to_issues_and_repairs, transfer_to_sales_agent, transfer_to_human_manager],
155
+ outputKey: "triage",
156
+ enableHistory: true,
157
+ });
158
+
159
+ const engine = new ExecutionEngine({ model });
160
+
161
+ const userAgent = await engine.run(triage);
162
+
163
+ await runChatLoopInTerminal(userAgent, {
164
+ welcome: `Hello, I'm a customer service bot for ACME Inc. How can I help you today?`,
165
+ defaultQuestion: "I want a refund",
166
+ });
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@aigne/example-workflow-handoff",
3
+ "version": "1.1.0-beta.12",
4
+ "description": "A demonstration of using AIGNE Framework to build a handoff workflow",
5
+ "author": "Arcblock <blocklet@arcblock.io> https://github.com/blocklet",
6
+ "homepage": "https://github.com/AIGNE-io/aigne-framework/tree/main/examples/workflow-handoff",
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
+ "zod": "^3.24.2",
20
+ "@aigne/core-next": "^1.1.0-beta.12"
21
+ },
22
+ "scripts": {
23
+ "start": "npx -y bun run index.ts"
24
+ }
25
+ }
package/usages.ts ADDED
@@ -0,0 +1,44 @@
1
+ import assert from "node:assert";
2
+ import { AIAgent, ChatModelOpenAI, ExecutionEngine } from "@aigne/core-next";
3
+
4
+ const { OPENAI_API_KEY } = process.env;
5
+ assert(OPENAI_API_KEY, "Please set the OPENAI_API_KEY environment variable");
6
+
7
+ const model = new ChatModelOpenAI({
8
+ apiKey: OPENAI_API_KEY,
9
+ });
10
+
11
+ function transfer_to_b() {
12
+ return agentB;
13
+ }
14
+
15
+ const agentA = AIAgent.from({
16
+ name: "AgentA",
17
+ instructions: "You are a helpful agent.",
18
+ outputKey: "A",
19
+ tools: [transfer_to_b],
20
+ });
21
+
22
+ const agentB = AIAgent.from({
23
+ name: "AgentB",
24
+ instructions: "Only speak in Haikus.",
25
+ outputKey: "B",
26
+ });
27
+
28
+ const engine = new ExecutionEngine({ model });
29
+
30
+ const userAgent = await engine.run(agentA);
31
+
32
+ const result1 = await userAgent.call("transfer to agent b");
33
+ console.log(result1);
34
+ // Output:
35
+ // {
36
+ // B: "Transfer now complete, \nAgent B is here to help. \nWhat do you need, friend?",
37
+ // }
38
+
39
+ const result2 = await userAgent.call("It's a beautiful day");
40
+ console.log(result2);
41
+ // Output:
42
+ // {
43
+ // B: "Sunshine warms the earth, \nGentle breeze whispers softly, \nNature sings with joy. ",
44
+ // }