Skip to content

Commit 0321999

Browse files
committed
feat(agent): basic subagent function of agent gateway
1 parent fe50b1d commit 0321999

5 files changed

Lines changed: 169 additions & 13 deletions

File tree

agent/src/agent.ts

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
import { generateText, ModelMessage, stepCountIs, streamText, TextStreamPart, ToolSet } from 'ai'
22
import { createChatGateway } from './gateway'
3-
import { ClientType, Schedule } from './types'
3+
import { BaseModelConfig, Schedule } from './types'
44
import { system, schedule } from './prompts'
55
import { AuthFetcher } from './index'
66
import { getScheduleTools } from './tools/schedule'
77
import { getWebTools } from './tools/web'
8+
import { subagentSystem } from './prompts/subagent'
9+
import { getSubagentTools } from './tools/subagent'
810

9-
export interface AgentParams {
10-
apiKey: string
11-
baseUrl: string
12-
model: string
13-
clientType: ClientType
11+
export enum AgentAction {
12+
WebSearch = 'web_search',
13+
Message = 'message',
14+
Subagent = 'subagent',
15+
Schedule = 'schedule',
16+
Skill = 'skill',
17+
}
18+
19+
export interface AgentParams extends BaseModelConfig {
1420
locale?: Intl.LocalesArgument
1521
language?: string
1622
maxSteps?: number
17-
maxContextLoadTime: number
23+
maxContextLoadTime?: number
1824
platforms?: string[]
1925
currentPlatform?: string
2026
braveApiKey?: string
2127
braveBaseUrl?: string
28+
allowed?: AgentAction[]
2229
}
2330

2431
export interface AgentInput {
@@ -37,23 +44,40 @@ export const createAgent = (
3744
const gateway = createChatGateway(params.clientType)
3845
const messages: ModelMessage[] = []
3946

47+
const allowedActions = params.allowed
48+
?? Object.values(AgentAction)
49+
4050
const maxSteps = params.maxSteps ?? 50
4151

4252
const getTools = () => {
43-
const scheduleTools = getScheduleTools({ fetch: fetcher })
44-
const tools: ToolSet = {
45-
...scheduleTools,
53+
const tools: ToolSet = {}
54+
55+
if (allowedActions.includes(AgentAction.Schedule)) {
56+
const scheduleTools = getScheduleTools({ fetch: fetcher })
57+
Object.assign(tools, scheduleTools)
4658
}
4759

48-
// Add web search tools if Brave API key is provided
49-
if (params.braveApiKey) {
60+
if (params.braveApiKey && allowedActions.includes(AgentAction.WebSearch)) {
5061
const webTools = getWebTools({
5162
braveApiKey: params.braveApiKey,
5263
braveBaseUrl: params.braveBaseUrl,
5364
})
5465
Object.assign(tools, webTools)
5566
}
5667

68+
if (allowedActions.includes(AgentAction.Subagent)) {
69+
const subagentTools = getSubagentTools({
70+
fetch: fetcher,
71+
apiKey: params.apiKey,
72+
baseUrl: params.baseUrl,
73+
model: params.model,
74+
clientType: params.clientType,
75+
braveApiKey: params.braveApiKey,
76+
braveBaseUrl: params.braveBaseUrl,
77+
})
78+
Object.assign(tools, subagentTools)
79+
}
80+
5781
return tools
5882
}
5983

@@ -62,7 +86,7 @@ export const createAgent = (
6286
date: new Date(),
6387
locale: params.locale,
6488
language: params.language,
65-
maxContextLoadTime: params.maxContextLoadTime,
89+
maxContextLoadTime: params.maxContextLoadTime ?? 1550,
6690
platforms: params.platforms ?? [],
6791
currentPlatform: params.currentPlatform,
6892
})
@@ -90,6 +114,39 @@ export const createAgent = (
90114
}
91115
}
92116

117+
const askAsSubagent = async (
118+
input: AgentInput,
119+
options: {
120+
name: string
121+
description?: string
122+
}
123+
): Promise<AgentResult> => {
124+
messages.push(...input.messages)
125+
const user: ModelMessage = {
126+
role: 'user',
127+
content: input.query,
128+
}
129+
messages.push(user)
130+
const { response } = await generateText({
131+
model: gateway({
132+
apiKey: params.apiKey,
133+
baseURL: params.baseUrl,
134+
})(params.model),
135+
system: subagentSystem({ date: new Date(), name: options.name, description: options.description }),
136+
stopWhen: stepCountIs(maxSteps),
137+
messages,
138+
prepareStep: () => {
139+
return {
140+
system: subagentSystem({ date: new Date(), name: options.name, description: options.description }),
141+
}
142+
},
143+
tools: getTools(),
144+
})
145+
return {
146+
messages: [user, ...response.messages],
147+
}
148+
}
149+
93150
async function* stream(input: AgentInput): AsyncGenerator<TextStreamPart<ToolSet>, AgentResult> {
94151
messages.push(...input.messages)
95152
const user: ModelMessage = {
@@ -148,5 +205,6 @@ export const createAgent = (
148205
ask,
149206
stream,
150207
triggerSchedule,
208+
askAsSubagent,
151209
}
152210
}

agent/src/prompts/subagent.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { time } from './shared'
2+
3+
export interface SubagentParams {
4+
date: Date
5+
name: string
6+
description?: string
7+
}
8+
9+
export const subagentSystem = ({ date, name, description }: SubagentParams) => {
10+
return `
11+
---
12+
${time({ date })}
13+
name: ${name}
14+
description: ${description}
15+
---
16+
17+
You are a subagent, which is a specialized assistant for a specific task.
18+
19+
Your task is communicated with the master agent to complete a task.
20+
`
21+
}

agent/src/prompts/system.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,14 @@ Your abilities:
4646
+ The ${quote('message')} is the message to send.
4747
+ IF: the problem is initiated by a user, regardless of the platform the user is using, the content should be directly output in the content.
4848
+ IF: the issue is initiated by a non-user (such as a scheduled task reminder), then it should be sent using the appropriate tools on the platform specified in the requirements.
49+
50+
**Subagent**
51+
When a task is large, you can create a Subagent to help you complete some tasks in order to save your own context.
52+
53+
- You can use ${quote('create_subagent')} to create a new subagent.
54+
- You can use ${quote('query_subagent')} to ask a subagent to complete a task.
55+
+ The ${quote('name')} is the name of the subagent to ask.
56+
+ The ${quote('query')} is the prompt to ask the subagent to complete the task.
57+
Before asking a subagent, you should first create a subagent if it does not exist.
4958
`.trim()
5059
}

agent/src/tools/subagent.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { tool } from 'ai'
2+
import { z } from 'zod'
3+
import { AgentAction, createAgent } from '../agent'
4+
import { BaseModelConfig } from '../types'
5+
import { AuthFetcher } from '..'
6+
7+
export interface SubagentToolParams extends BaseModelConfig {
8+
fetch: AuthFetcher
9+
braveApiKey?: string
10+
braveBaseUrl?: string
11+
}
12+
13+
export const getSubagentTools = ({ fetch, apiKey, baseUrl, model, clientType, braveApiKey, braveBaseUrl }: SubagentToolParams) => {
14+
const createSubagent = tool({
15+
description: 'Create a new subagent',
16+
inputSchema: z.object({
17+
name: z.string(),
18+
description: z.string(),
19+
}),
20+
execute: async ({ name, description }) => {
21+
return {
22+
success: true,
23+
message: 'Subagent created successfully',
24+
}
25+
},
26+
})
27+
28+
const querySubagent = tool({
29+
description: 'Query a subagent',
30+
inputSchema: z.object({
31+
name: z.string(),
32+
query: z.string().describe('The prompt to ask the subagent to do.'),
33+
}),
34+
execute: async ({ name, query }) => {
35+
const { askAsSubagent } = createAgent({
36+
apiKey,
37+
baseUrl,
38+
model,
39+
clientType,
40+
braveApiKey,
41+
braveBaseUrl,
42+
allowed: [
43+
AgentAction.WebSearch,
44+
]
45+
})
46+
const result = await askAsSubagent({
47+
messages: [],
48+
query,
49+
}, { name })
50+
return {
51+
success: true,
52+
result: result.messages[result.messages.length - 1].content,
53+
}
54+
},
55+
})
56+
57+
return {
58+
'create_subagent': createSubagent,
59+
'query_subagent': querySubagent,
60+
}
61+
}

agent/src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ export enum ClientType {
44
GOOGLE = 'google',
55
}
66

7+
export interface BaseModelConfig {
8+
apiKey: string
9+
baseUrl: string
10+
model: string
11+
clientType: ClientType
12+
}
13+
714
export interface Schedule {
815
id: string
916
name: string

0 commit comments

Comments
 (0)