多层级Agent

使用Agent嵌套Agent的形式来运行

  1. 创建一个编写博客内容的Tool,内部调用编写博客的Agent进行博客内容生成
  2. 创建一个文章内容润色的Tool,内部调用润色文章的Agent进行博客内容润色
  3. 创建一个发布博客的Agent,给其加入上述的两个Tool,然后prompt中让其使用Tool,先生成内容,再润色内容
import {createOpenAI} from "@ai-sdk/openai";
import {Agent} from "@mastra/core/agent";
import {createTool} from "@mastra/core/tools";
import {z} from "zod";

//创建大模型实例
const llmProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
})

const llm = llmProvider('gpt-4o-2024-11-20')


//编写博客内容的Agent
const copywriterAgent = new Agent({
name: 'Copywriter',
instructions: "你是一个广告文字撰写人,写博客文章副本。",
model: llm,
})

//编写博客内容的工具,内部使用编写博客内容的Agent来生成关于对应主题的博客内容
const copywriterTool = createTool({
id: 'copywriter-agent',
description: "调用Copywriter Agent撰写博文文案。",
inputSchema: z.object({
topic: z.string().describe('博客标题'),
}),
outputSchema: z.object({
copy: z.string().describe('博客副本')
}),
execute: async ({context}) => {
const result = await copywriterAgent.generate(`撰写一篇关于${context.topic}的博客文章`)
return {
copy: result.text
}
}
})

//进行内容细化的Agent
const editorAgent = new Agent({
name: 'Editor',
instructions: "你是编辑博客文章副本的编辑代理。",
model: llm
})

//创建一个编辑Tool,内部调用editorAgent来对内容进行细化和润色
const editorTool = createTool({
id: 'editor-agent',
description: '调用Editor Agent来编辑博客文章副本。',
inputSchema: z.object({
copy: z.string().describe("博客文章内容"),
}),
outputSchema: z.object({
copy: z.string().describe("已编辑过的博客文章副本")
}),
execute: async ({ context }) => {
const result = await editorAgent.generate(`编辑以下博客文章仅返回编辑后的副本:${context.copy}`)
return { copy: result.text }
}
})


//进行文章发布的Agent,给其设置工作流程,先使用copywriterAgent生成博客内容,再使用editorAgent进行内容更新润色
const publisherAgent = new Agent({
name: 'publisherAgent',
instructions: "您是一位出版Agent,首先联系copywriterAgent撰写关于特定主题的博客文章副本,然后联系editorAgent编辑副本。只需返回最终编辑后的副本。",
model: llm,
tools: {
copywriterTool,
editorTool
}
})


export default publisherAgent

这样就完成了一个二级的Agent层次结构。

多个Agent组成的工作流

将上面例子中的publisherAgent换成workflow,就能让其以一个固定的顺序进行工作

import {createOpenAI} from "@ai-sdk/openai";
import {Agent} from "@mastra/core/agent";
import {Step, Workflow} from "@mastra/core/workflows";
import {z} from "zod";

const llmProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
})

const llm = llmProvider('gpt-4o-2024-11-20')


const copywriterAgent = new Agent({
name: 'Copywriter',
instructions: '你是一个广告文字撰写人,写博客文章副本。',
model: llm,
})

const copywriterStep = new Step({
id: 'copywriterStep',
execute: async ({context}) => {
//输入参数从triggerData中得到
if (!context?.triggerData?.topic) {
throw new Error("Topic not found in trigger data")
}

const result = await copywriterAgent.generate(`撰写一篇关于${context.triggerData.topic}的博客`)
return {
copy: result.text
}
}
})

const editorAgent = new Agent({
name: 'Editor',
instructions: "你是一名编辑,你可以对博客文章内容进行润色修改",
model: llm,
})

const editorStep = new Step({
id: 'editorStep',
execute: async ({context}) => {
//输入参数使用getStepResult获取
const copy = context.getStepResult<{copy: string}>('copywriterStep')?.copy
if (!copy) {
throw new Error('Blog copy content not found in copywriter step')
}
const result = await editorAgent.generate(`将下面这篇博客内容进行润色修改,只需要返回修改过后的文章即可: ${copy}`)
return {
copy: result
}
}
})

const blogWorkflow = new Workflow({
name: 'blogWorkflow',
triggerSchema: z.object({
topic: z.string(),
})
}).step(copywriterStep).then(editorStep) //设置工作流为先编写内容,再润色

blogWorkflow.commit()

export default blogWorkflow

Bird Checker 让Agent对图像进行识别

import {createOpenAI} from "@ai-sdk/openai";
import {Agent} from "@mastra/core/agent";
import {z} from "zod";

const llmProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
})

const llm = llmProvider('gpt-4o-2024-11-20')


export type Image = {
alt_description: string
urls: {
regular: string
raw: string
}
user: {
first_name: string
links: {
html: string
}
}
}

export type ImageResponse<T, K> =
| {
ok: true
data: T
}
| {
ok: false
error: K
}

//获取一张随机的图片
const getRandomImage = async ({ query }: {query: string}) : Promise<ImageResponse<Image, string>> => {
const page = Math.floor(Math.random() * 20)
const order_by = Math.random() < 0.5 ? "relevant" : "latest"

try {
const res = await fetch(
`https://api.unsplash.com/search/photos?query=${query}&page=${page}&order_by=${order_by}`,
{
method: 'GET',
headers: {
Authorization: `Client-ID ${process.env.UNSPLASH_ACCESS_KEY}`,
'Accept-Version': 'v1'
},
cache: 'no-store'
}
)

if (!res.ok) {
return {
ok: false,
error: 'Failed to fetch image'
}
}

const data = (await res.json()) as {
results: Array<Image>
}

const randomNo = Math.floor(Math.random() * data.results.length)

return {
ok: true,
data: data.results[randomNo] as Image
}
}catch (err) {
return {
ok: false,
error: "Error fetching image"
}
}
}


//创建一个识别鸟类图片的Agent
const instructions = `
你可以查看图像并确定它是否是一只鸟。
你也可以弄清楚这只鸟的种类和照片是在哪里拍的。
`

export const birdCheckerAgent = new Agent({
name: 'Bird Checker',
instructions,
model: llm
})


const queries: string[] = ["wildlife", "feathers", "flying", "birds"]
const randomQuery = queries[Math.floor(Math.random() * queries.length)]

const imageResponse = await getRandomImage({ query: randomQuery })

if (!imageResponse.ok) {
console.log("Error fetching image", imageResponse.error)
process.exit(1)
}


console.log("Image URL:", imageResponse.data.urls.regular)


//传入图片,如果是鸟类的话,返回鸟的学名以及这张照片的位置
const response = await birdCheckerAgent.generate(
[
{
role: "user",
content: [
{
type: "image",
image: new URL(imageResponse.data.urls.regular)
},
{
type: "text",
text: "查看这张图片,让我知道它是否是一只鸟,以及这只鸟的学名,不需要任何解释。也可以用一两个高中生可以理解的短句来总结这张照片的位置"
}
]
}
],
{
output: z.object({
bird: z.boolean(),
species: z.string(),
location: z.string()
})
}
)

console.log('Result', response)