mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat: skills
This commit is contained in:
+25
-4
@@ -1,5 +1,5 @@
|
||||
import { generateText, ImagePart, LanguageModelUsage, ModelMessage, stepCountIs, streamText, UserModelMessage } from 'ai'
|
||||
import { AgentInput, AgentParams, allActions, HTTPMCPConnection, MCPConnection, Schedule } from './types'
|
||||
import { AgentInput, AgentParams, AgentSkill, allActions, HTTPMCPConnection, MCPConnection, Schedule } from './types'
|
||||
import { system, schedule, user, subagentSystem } from './prompts'
|
||||
import { AuthFetcher } from './index'
|
||||
import { createModel } from './model'
|
||||
@@ -22,6 +22,7 @@ export const createAgent = ({
|
||||
allowedActions = allActions,
|
||||
channels = [],
|
||||
mcpConnections = [],
|
||||
skills = [],
|
||||
currentChannel = 'Unknown Channel',
|
||||
identity = {
|
||||
botId: '',
|
||||
@@ -33,6 +34,18 @@ export const createAgent = ({
|
||||
auth,
|
||||
}: AgentParams, fetch: AuthFetcher) => {
|
||||
const model = createModel(modelConfig)
|
||||
const enabledSkills: AgentSkill[] = []
|
||||
|
||||
const enableSkill = (skill: string) => {
|
||||
const agentSkill = skills.find(s => s.name === skill)
|
||||
if (agentSkill) {
|
||||
enabledSkills.push(agentSkill)
|
||||
}
|
||||
}
|
||||
|
||||
const getEnabledSkills = () => {
|
||||
return enabledSkills.map(skill => skill.name)
|
||||
}
|
||||
|
||||
const getDefaultMCPConnections = (): MCPConnection[] => {
|
||||
const fs: HTTPMCPConnection = {
|
||||
@@ -52,8 +65,8 @@ export const createAgent = ({
|
||||
language,
|
||||
maxContextLoadTime: activeContextTime,
|
||||
channels,
|
||||
skills: [],
|
||||
enabledSkills: [],
|
||||
skills,
|
||||
enabledSkills,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,6 +76,7 @@ export const createAgent = ({
|
||||
model: modelConfig,
|
||||
brave,
|
||||
identity,
|
||||
enableSkill,
|
||||
})
|
||||
const defaultMCPConnections = getDefaultMCPConnections()
|
||||
const { tools: mcpTools, close: closeMCP } = await getMCPTools([
|
||||
@@ -99,6 +113,7 @@ export const createAgent = ({
|
||||
const ask = async (input: AgentInput) => {
|
||||
const userPrompt = generateUserPrompt(input)
|
||||
const messages = [...input.messages, userPrompt]
|
||||
input.skills.forEach(skill => enableSkill(skill))
|
||||
const systemPrompt = generateSystemPrompt()
|
||||
const { tools, close } = await getAgentTools()
|
||||
const { response, reasoning, text, usage } = await generateText({
|
||||
@@ -125,6 +140,7 @@ export const createAgent = ({
|
||||
usage,
|
||||
text: cleanedText,
|
||||
attachments: allAttachments,
|
||||
skills: getEnabledSkills(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,12 +185,14 @@ export const createAgent = ({
|
||||
reasoning: reasoning.map(part => part.text),
|
||||
usage,
|
||||
text,
|
||||
skills: getEnabledSkills(),
|
||||
}
|
||||
}
|
||||
|
||||
const triggerSchedule = async (params: {
|
||||
schedule: Schedule
|
||||
messages: ModelMessage[]
|
||||
skills: string[]
|
||||
}) => {
|
||||
const scheduleMessage: UserModelMessage = {
|
||||
role: 'user',
|
||||
@@ -183,6 +201,7 @@ export const createAgent = ({
|
||||
]
|
||||
}
|
||||
const messages = [...params.messages, scheduleMessage]
|
||||
params.skills.forEach(skill => enableSkill(skill))
|
||||
const { tools, close } = await getAgentTools()
|
||||
const { response, reasoning, text, usage } = await generateText({
|
||||
model,
|
||||
@@ -199,12 +218,14 @@ export const createAgent = ({
|
||||
reasoning: reasoning.map(part => part.text),
|
||||
usage,
|
||||
text,
|
||||
skills: getEnabledSkills(),
|
||||
}
|
||||
}
|
||||
|
||||
async function* stream(input: AgentInput): AsyncGenerator<AgentAction> {
|
||||
const userPrompt = generateUserPrompt(input)
|
||||
const messages = [...input.messages, userPrompt]
|
||||
input.skills.forEach(skill => enableSkill(skill))
|
||||
const systemPrompt = generateSystemPrompt()
|
||||
const attachmentsExtractor = new AttachmentsStreamExtractor()
|
||||
const result: {
|
||||
@@ -320,9 +341,9 @@ export const createAgent = ({
|
||||
yield {
|
||||
type: 'agent_end',
|
||||
messages: [userPrompt, ...strippedMessages],
|
||||
skills: [],
|
||||
reasoning: result.reasoning,
|
||||
usage: result.usage!,
|
||||
skills: getEnabledSkills(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createAgent } from '../agent'
|
||||
import { createAuthFetcher, getBaseUrl, getBraveConfig } from '../index'
|
||||
import { ModelConfig } from '../types'
|
||||
import { bearerMiddleware } from '../middlewares/bearer'
|
||||
import { AllowedActionModel, AttachmentModel, IdentityContextModel, MCPConnectionModel, ModelConfigModel, ScheduleModel } from '../models'
|
||||
import { AgentSkillModel, AllowedActionModel, AttachmentModel, IdentityContextModel, MCPConnectionModel, ModelConfigModel, ScheduleModel } from '../models'
|
||||
import { allActions } from '../types'
|
||||
|
||||
const AgentModel = z.object({
|
||||
@@ -14,6 +14,7 @@ const AgentModel = z.object({
|
||||
currentChannel: z.string(),
|
||||
allowedActions: z.array(AllowedActionModel).optional().default(allActions),
|
||||
messages: z.array(z.any()),
|
||||
usableSkills: z.array(AgentSkillModel).optional().default([]),
|
||||
skills: z.array(z.string()),
|
||||
identity: IdentityContextModel,
|
||||
attachments: z.array(AttachmentModel).optional().default([]),
|
||||
@@ -37,6 +38,7 @@ export const chatModule = new Elysia({ prefix: '/chat' })
|
||||
bearer: bearer!,
|
||||
baseUrl: getBaseUrl(),
|
||||
},
|
||||
skills: body.usableSkills,
|
||||
brave: getBraveConfig(),
|
||||
}, authFetcher)
|
||||
return ask({
|
||||
@@ -66,6 +68,7 @@ export const chatModule = new Elysia({ prefix: '/chat' })
|
||||
bearer: bearer!,
|
||||
baseUrl: getBaseUrl(),
|
||||
},
|
||||
skills: body.usableSkills,
|
||||
brave: getBraveConfig(),
|
||||
}, authFetcher)
|
||||
for await (const action of stream({
|
||||
@@ -101,11 +104,13 @@ export const chatModule = new Elysia({ prefix: '/chat' })
|
||||
bearer: bearer!,
|
||||
baseUrl: getBaseUrl(),
|
||||
},
|
||||
skills: body.usableSkills,
|
||||
brave: getBraveConfig(),
|
||||
}, authFetcher)
|
||||
return triggerSchedule({
|
||||
schedule: body.schedule,
|
||||
messages: body.messages,
|
||||
skills: body.skills,
|
||||
})
|
||||
}, {
|
||||
body: AgentModel.extend({
|
||||
|
||||
@@ -35,6 +35,8 @@ export const system = ({
|
||||
'time-now': date.toISOString(),
|
||||
}
|
||||
|
||||
console.log('enabledSkills', enabledSkills)
|
||||
|
||||
return `
|
||||
---
|
||||
${Bun.YAML.stringify(headers)}
|
||||
|
||||
@@ -7,17 +7,19 @@ import { getMemoryTools } from './memory'
|
||||
import { getSubagentTools } from './subagent'
|
||||
import { getContactTools } from './contact'
|
||||
import { getMessageTools } from './message'
|
||||
import { getSkillTools } from './skill'
|
||||
|
||||
export interface ToolsParams {
|
||||
fetch: AuthFetcher
|
||||
model: ModelConfig
|
||||
brave?: BraveConfig
|
||||
identity: IdentityContext
|
||||
enableSkill: (skill: string) => void
|
||||
}
|
||||
|
||||
export const getTools = (
|
||||
actions: AgentAction[],
|
||||
{ fetch, model, brave, identity }: ToolsParams
|
||||
{ fetch, model, brave, identity, enableSkill }: ToolsParams
|
||||
) => {
|
||||
const tools: ToolSet = {}
|
||||
if (actions.includes(AgentAction.Web) && brave) {
|
||||
@@ -44,5 +46,9 @@ export const getTools = (
|
||||
const messageTools = getMessageTools({ fetch, identity })
|
||||
Object.assign(tools, messageTools)
|
||||
}
|
||||
return tools
|
||||
if (actions.includes(AgentAction.Skill)) {
|
||||
const skillTools = getSkillTools({ useSkill: enableSkill })
|
||||
Object.assign(tools, skillTools)
|
||||
}
|
||||
return tools
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import { AgentSkill } from '../types'
|
||||
import { tool } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
interface SkillToolParams {
|
||||
skills: AgentSkill[]
|
||||
useSkill: (skill: AgentSkill, reason: string) => void
|
||||
useSkill: (skill: string) => void
|
||||
}
|
||||
|
||||
export const getSkillTools = ({ skills, useSkill }: SkillToolParams) => {
|
||||
export const getSkillTools = ({ useSkill }: SkillToolParams) => {
|
||||
const useSkillTool = tool({
|
||||
description: 'Use a skill if you think it is relevant to the current task',
|
||||
inputSchema: z.object({
|
||||
@@ -15,11 +13,7 @@ export const getSkillTools = ({ skills, useSkill }: SkillToolParams) => {
|
||||
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)
|
||||
useSkill(skillName)
|
||||
return {
|
||||
success: true,
|
||||
skillName,
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface AgentParams {
|
||||
mcpConnections?: MCPConnection[]
|
||||
identity?: IdentityContext
|
||||
auth: AgentAuthContext
|
||||
skills?: AgentSkill[]
|
||||
}
|
||||
|
||||
export interface AgentInput {
|
||||
@@ -64,6 +65,5 @@ export interface AgentSkill {
|
||||
name: string
|
||||
description: string
|
||||
content: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
metadata: Record<string, any>
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user