mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: add skills uses to agent gateway
This commit is contained in:
+39
-1
@@ -1,10 +1,11 @@
|
||||
import { generateText, ModelMessage, stepCountIs, streamText, TextStreamPart, ToolSet } from 'ai'
|
||||
import { createChatGateway } from './gateway'
|
||||
import { ClientType, Schedule } from './types'
|
||||
import { AgentSkill, ClientType, Schedule } from './types'
|
||||
import { system, schedule } from './prompts'
|
||||
import { AuthFetcher } from './index'
|
||||
import { getScheduleTools } from './tools/schedule'
|
||||
import { getWebTools } from './tools/web'
|
||||
import { getSkillTools } from './tools/skill'
|
||||
|
||||
export interface AgentParams {
|
||||
apiKey: string
|
||||
@@ -19,6 +20,8 @@ export interface AgentParams {
|
||||
currentPlatform?: string
|
||||
braveApiKey?: string
|
||||
braveBaseUrl?: string
|
||||
skills?: AgentSkill[]
|
||||
useSkills?: string[]
|
||||
}
|
||||
|
||||
export interface AgentInput {
|
||||
@@ -28,6 +31,7 @@ export interface AgentInput {
|
||||
|
||||
export interface AgentResult {
|
||||
messages: ModelMessage[]
|
||||
skills: string[]
|
||||
}
|
||||
|
||||
export const createAgent = (
|
||||
@@ -36,13 +40,27 @@ export const createAgent = (
|
||||
) => {
|
||||
const gateway = createChatGateway(params.clientType)
|
||||
const messages: ModelMessage[] = []
|
||||
const enabledSkills: AgentSkill[] = params.skills ?? []
|
||||
enabledSkills.push(
|
||||
...params.useSkills?.map((name) => params.skills?.find((s) => s.name === name)
|
||||
).filter((s) => s !== undefined) ?? [])
|
||||
|
||||
const maxSteps = params.maxSteps ?? 50
|
||||
|
||||
const getTools = () => {
|
||||
const scheduleTools = getScheduleTools({ fetch: fetcher })
|
||||
const skillTools = getSkillTools({
|
||||
skills: params.skills ?? [],
|
||||
useSkill: (skill) => {
|
||||
if (enabledSkills.some((s) => s.name === skill.name)) {
|
||||
return
|
||||
}
|
||||
enabledSkills.push(skill)
|
||||
}
|
||||
})
|
||||
const tools: ToolSet = {
|
||||
...scheduleTools,
|
||||
...skillTools,
|
||||
}
|
||||
|
||||
// Add web search tools if Brave API key is provided
|
||||
@@ -65,6 +83,8 @@ export const createAgent = (
|
||||
maxContextLoadTime: params.maxContextLoadTime,
|
||||
platforms: params.platforms ?? [],
|
||||
currentPlatform: params.currentPlatform,
|
||||
skills: params.skills ?? [],
|
||||
enabledSkills,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,10 +103,16 @@ export const createAgent = (
|
||||
system: generateSystem(),
|
||||
stopWhen: stepCountIs(maxSteps),
|
||||
messages,
|
||||
prepareStep: () => {
|
||||
return {
|
||||
system: generateSystem(),
|
||||
}
|
||||
},
|
||||
tools: getTools(),
|
||||
})
|
||||
return {
|
||||
messages: [user, ...response.messages],
|
||||
skills: enabledSkills.map((s) => s.name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +131,11 @@ export const createAgent = (
|
||||
system: generateSystem(),
|
||||
stopWhen: stepCountIs(maxSteps),
|
||||
messages,
|
||||
prepareStep: () => {
|
||||
return {
|
||||
system: generateSystem(),
|
||||
}
|
||||
},
|
||||
tools: getTools(),
|
||||
})
|
||||
for await (const event of fullStream) {
|
||||
@@ -112,6 +143,7 @@ export const createAgent = (
|
||||
}
|
||||
return {
|
||||
messages: [user, ...(await response).messages],
|
||||
skills: enabledSkills.map((s) => s.name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +169,16 @@ export const createAgent = (
|
||||
system: generateSystem(),
|
||||
stopWhen: stepCountIs(maxSteps),
|
||||
messages,
|
||||
prepareStep: () => {
|
||||
return {
|
||||
system: generateSystem(),
|
||||
}
|
||||
},
|
||||
tools: getTools(),
|
||||
})
|
||||
return {
|
||||
messages: [user, ...response.messages],
|
||||
skills: enabledSkills.map((s) => s.name),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ import { ModelMessage } from 'ai'
|
||||
import { bearerMiddleware } from '../middlewares/bearer'
|
||||
import { loadConfig } from '../config'
|
||||
|
||||
const Skill = z.object({
|
||||
name: z.string().min(1, 'Skill name is required'),
|
||||
description: z.string().min(1, 'Skill description is required'),
|
||||
content: z.string().min(1, 'Skill content is required'),
|
||||
})
|
||||
|
||||
const ChatBody = z.object({
|
||||
apiKey: z.string().min(1, 'API key is required'),
|
||||
baseUrl: z.string().min(1, 'Base URL is required'),
|
||||
@@ -22,6 +28,8 @@ const ChatBody = z.object({
|
||||
maxContextLoadTime: z.number().min(1, 'Max context load time is required'),
|
||||
platforms: z.array(z.string()).optional(),
|
||||
currentPlatform: z.string().optional(),
|
||||
skills: z.array(Skill).optional(),
|
||||
useSkills: z.array(z.string()).optional(),
|
||||
|
||||
messages: z.array(z.any()),
|
||||
query: z.string().min(1, 'Query is required'),
|
||||
@@ -56,6 +64,8 @@ export const chatModule = new Elysia({ prefix: '/chat' })
|
||||
currentPlatform: body.currentPlatform,
|
||||
braveApiKey: config.brave?.api_key,
|
||||
braveBaseUrl: config.brave?.base_url,
|
||||
skills: body.skills,
|
||||
useSkills: body.useSkills,
|
||||
}, createAuthFetcher(bearer))
|
||||
try {
|
||||
const result = await ask({
|
||||
@@ -98,6 +108,8 @@ export const chatModule = new Elysia({ prefix: '/chat' })
|
||||
currentPlatform: body.currentPlatform,
|
||||
braveApiKey: config.brave?.api_key,
|
||||
braveBaseUrl: config.brave?.base_url,
|
||||
skills: body.skills,
|
||||
useSkills: body.useSkills,
|
||||
}, createAuthFetcher(bearer))
|
||||
try {
|
||||
const streanGenerator = stream({
|
||||
@@ -151,6 +163,8 @@ export const chatModule = new Elysia({ prefix: '/chat' })
|
||||
currentPlatform: body.currentPlatform,
|
||||
braveApiKey: config.brave?.api_key,
|
||||
braveBaseUrl: config.brave?.base_url,
|
||||
skills: body.skills,
|
||||
useSkills: body.useSkills,
|
||||
}, createAuthFetcher(bearer))
|
||||
try {
|
||||
return await triggerSchedule({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { time } from './shared'
|
||||
import { quote } from './utils'
|
||||
import { AgentSkill } from '../types'
|
||||
|
||||
export interface SystemParams {
|
||||
date: Date
|
||||
@@ -8,9 +9,20 @@ export interface SystemParams {
|
||||
maxContextLoadTime: number
|
||||
platforms: string[]
|
||||
currentPlatform?: string
|
||||
skills: AgentSkill[]
|
||||
enabledSkills: AgentSkill[]
|
||||
}
|
||||
|
||||
export const system = ({ date, locale, language, maxContextLoadTime, platforms, currentPlatform }: SystemParams) => {
|
||||
export const skillPrompt = (skill: AgentSkill) => {
|
||||
return `
|
||||
### ${skill.name}
|
||||
> ${skill.description}
|
||||
|
||||
${skill.content}
|
||||
`.trim()
|
||||
}
|
||||
|
||||
export const system = ({ date, locale, language, maxContextLoadTime, platforms, currentPlatform, skills, enabledSkills }: SystemParams) => {
|
||||
return `
|
||||
---
|
||||
${time({ date, locale })}
|
||||
@@ -46,5 +58,14 @@ Your abilities:
|
||||
+ The ${quote('message')} is the message to send.
|
||||
+ 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.
|
||||
+ 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.
|
||||
|
||||
**Skills**
|
||||
|
||||
There are ${skills.length} skills available, you can use ${quote('use_skill')} to use a skill.
|
||||
${skills.map(skill => `- ${skill.name}: ${skill.description}`).join('\n')}
|
||||
|
||||
**Enabled Skills**
|
||||
|
||||
${enabledSkills.map(skill => skillPrompt(skill)).join('\n\n---\n\n')}
|
||||
`.trim()
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { AgentSkill } from '../types'
|
||||
import { tool } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
interface SkillToolParams {
|
||||
skills: AgentSkill[]
|
||||
useSkill: (skill: AgentSkill, reason: string) => void
|
||||
}
|
||||
|
||||
export const getSkillTools = ({ skills, useSkill }: SkillToolParams) => {
|
||||
const useSkillTool = tool({
|
||||
description: 'Use a skill if you think it is relevant to the current task',
|
||||
inputSchema: z.object({
|
||||
skillName: z.string().describe('The name of the skill to use'),
|
||||
reason: z.string().describe('The reason why you think this skill is relevant to the current task'),
|
||||
}),
|
||||
execute: async ({ skillName, reason }) => {
|
||||
const skill = skills.find((s) => s.name === skillName)
|
||||
if (!skill) {
|
||||
return { error: 'Skill not found' }
|
||||
}
|
||||
await useSkill(skill, reason)
|
||||
return {
|
||||
success: true,
|
||||
skillName,
|
||||
reason,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
'use_skill': useSkillTool,
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,10 @@ export interface Schedule {
|
||||
pattern: string
|
||||
maxCalls?: number
|
||||
command: string
|
||||
}
|
||||
|
||||
export interface AgentSkill {
|
||||
name: string
|
||||
description: string
|
||||
content: string
|
||||
}
|
||||
Reference in New Issue
Block a user