外观
文件管理 Agent
本教程将带你构建一个文件管理 Agent,支持 LLM 驱动的工具调用。
你将学到
- 定义多个 Skill(工具)
- LLM 自动选择工具
- 工具执行结果处理
最终效果
用户: 读取 package.json 文件
助手: [调用 readFile 工具] 文件内容如下...
用户: 列出当前目录的文件
助手: [调用 listFiles 工具] 找到 5 个文件...第一步:创建项目
bash
mkdir tool-agent && cd tool-agent
bun init -y
bun add @multi-agent/a2a zod @langchain/openai第二步:定义工具 Schema
创建 server.ts,定义文件操作工具:
typescript
import { z } from 'zod'
// 读取文件
const readFileSchema = z.object({
path: z.string().describe('文件路径'),
})
// 写入文件
const writeFileSchema = z.object({
path: z.string().describe('文件路径'),
content: z.string().describe('文件内容'),
})
// 列出文件
const listFilesSchema = z.object({
path: z.string().optional().describe('目录路径,默认当前目录'),
})typescript
// server.ts
import { z } from 'zod'
// 读取文件
const readFileSchema = z.object({
path: z.string().describe('文件路径'),
})
// 写入文件
const writeFileSchema = z.object({
path: z.string().describe('文件路径'),
content: z.string().describe('文件内容'),
})
// 列出文件
const listFilesSchema = z.object({
path: z.string().optional().describe('目录路径,默认当前目录'),
}) 第三步:实现工具 Handler
typescript
import { readFile, writeFile, readdir } from 'fs/promises'
import { type Context } from '@multi-agent/a2a'
// 读取文件
async function readFileHandler(params: { path: string }, ctx: Context) {
ctx.stream.send({ type: 'progress', text: `读取文件: ${params.path}` })
const content = await readFile(params.path, 'utf-8')
ctx.stream.send({
type: 'done',
text: '读取完成',
data: { path: params.path, content, size: content.length },
})
}
// 写入文件
async function writeFileHandler(params: { path: string; content: string }, ctx: Context) {
ctx.stream.send({ type: 'progress', text: `写入文件: ${params.path}` })
await writeFile(params.path, params.content, 'utf-8')
ctx.stream.send({
type: 'done',
text: '写入完成',
data: { path: params.path, size: params.content.length },
})
}
// 列出文件
async function listFilesHandler(params: { path?: string }, ctx: Context) {
const dir = params.path || '.'
ctx.stream.send({ type: 'progress', text: `列出目录: ${dir}` })
const entries = await readdir(dir, { withFileTypes: true })
const files = entries.map(e => ({
name: e.name,
isDirectory: e.isDirectory(),
}))
ctx.stream.send({
type: 'done',
text: `找到 ${files.length} 个文件`,
data: { path: dir, files },
})
}typescript
// server.ts
import { z } from 'zod'
import { readFile, writeFile, readdir } from 'fs/promises'
import { type Context } from '@multi-agent/a2a'
const readFileSchema = z.object({
path: z.string().describe('文件路径'),
})
const writeFileSchema = z.object({
path: z.string().describe('文件路径'),
content: z.string().describe('文件内容'),
})
const listFilesSchema = z.object({
path: z.string().optional().describe('目录路径,默认当前目录'),
})
// 读取文件
async function readFileHandler(params: { path: string }, ctx: Context) {
ctx.stream.send({ type: 'progress', text: `读取文件: ${params.path}` })
const content = await readFile(params.path, 'utf-8')
ctx.stream.send({
type: 'done',
text: '读取完成',
data: { path: params.path, content, size: content.length },
})
}
// 写入文件
async function writeFileHandler(params: { path: string; content: string }, ctx: Context) {
ctx.stream.send({ type: 'progress', text: `写入文件: ${params.path}` })
await writeFile(params.path, params.content, 'utf-8')
ctx.stream.send({
type: 'done',
text: '写入完成',
data: { path: params.path, size: params.content.length },
})
}
// 列出文件
async function listFilesHandler(params: { path?: string }, ctx: Context) {
const dir = params.path || '.'
ctx.stream.send({ type: 'progress', text: `列出目录: ${dir}` })
const entries = await readdir(dir, { withFileTypes: true })
const files = entries.map(e => ({
name: e.name,
isDirectory: e.isDirectory(),
}))
ctx.stream.send({
type: 'done',
text: `找到 ${files.length} 个文件`,
data: { path: dir, files },
})
} 第四步:配置 Agent Server
typescript
import { createAgentServer, type AgentConfig } from '@multi-agent/a2a'
const config: AgentConfig = {
agentId: 'tool-agent',
name: 'Tool Agent',
version: '1.0.0',
description: '文件管理 Agent',
address: 'a2a://0.0.0.0:50055',
skills: [
{
name: 'readFile',
description: '读取文件内容',
handler: readFileHandler,
inputSchema: z.toJSONSchema(readFileSchema),
},
{
name: 'writeFile',
description: '写入文件内容',
handler: writeFileHandler,
inputSchema: z.toJSONSchema(writeFileSchema),
},
{
name: 'listFiles',
description: '列出目录中的文件',
handler: listFilesHandler,
inputSchema: z.toJSONSchema(listFilesSchema),
},
],
}
const server = createAgentServer(config)
await server.start()
console.log('Tool Agent 已启动: localhost:50055')typescript
// server.ts
import { z } from 'zod'
import { readFile, writeFile, readdir } from 'fs/promises'
import { createAgentServer, type AgentConfig, type Context } from '@multi-agent/a2a'
const readFileSchema = z.object({
path: z.string().describe('文件路径'),
})
const writeFileSchema = z.object({
path: z.string().describe('文件路径'),
content: z.string().describe('文件内容'),
})
const listFilesSchema = z.object({
path: z.string().optional().describe('目录路径,默认当前目录'),
})
async function readFileHandler(params: { path: string }, ctx: Context) {
ctx.stream.send({ type: 'progress', text: `读取文件: ${params.path}` })
const content = await readFile(params.path, 'utf-8')
ctx.stream.send({
type: 'done',
text: '读取完成',
data: { path: params.path, content, size: content.length },
})
}
async function writeFileHandler(params: { path: string; content: string }, ctx: Context) {
ctx.stream.send({ type: 'progress', text: `写入文件: ${params.path}` })
await writeFile(params.path, params.content, 'utf-8')
ctx.stream.send({
type: 'done',
text: '写入完成',
data: { path: params.path, size: params.content.length },
})
}
async function listFilesHandler(params: { path?: string }, ctx: Context) {
const dir = params.path || '.'
ctx.stream.send({ type: 'progress', text: `列出目录: ${dir}` })
const entries = await readdir(dir, { withFileTypes: true })
const files = entries.map(e => ({
name: e.name,
isDirectory: e.isDirectory(),
}))
ctx.stream.send({
type: 'done',
text: `找到 ${files.length} 个文件`,
data: { path: dir, files },
})
}
const config: AgentConfig = {
agentId: 'tool-agent',
name: 'Tool Agent',
version: '1.0.0',
description: '文件管理 Agent',
address: 'a2a://0.0.0.0:50055',
skills: [
{
name: 'readFile',
description: '读取文件内容',
handler: readFileHandler,
inputSchema: z.toJSONSchema(readFileSchema),
},
{
name: 'writeFile',
description: '写入文件内容',
handler: writeFileHandler,
inputSchema: z.toJSONSchema(writeFileSchema),
},
{
name: 'listFiles',
description: '列出目录中的文件',
handler: listFilesHandler,
inputSchema: z.toJSONSchema(listFilesSchema),
},
],
}
const server = createAgentServer(config)
await server.start()
console.log('Tool Agent 已启动: localhost:50055') 第五步:创建测试客户端
创建 client.ts:
typescript
// client.ts
import { createAgentClient } from '@multi-agent/a2a'
// 1. 创建客户端
const client = createAgentClient({
agentId: 'tool-agent',
address: 'a2a://localhost:50055',
})
// 2. 列出文件
console.log('--- 列出文件 ---')
const listStream = await client.call('listFiles', { path: '.' })
for await (const msg of listStream) {
if (msg.type === 'progress') console.log(msg.text)
if (msg.type === 'done') console.log('文件列表:', msg.data.files)
}
// 3. 读取文件
console.log('\n--- 读取文件 ---')
const readStream = await client.call('readFile', { path: 'package.json' })
for await (const msg of readStream) {
if (msg.type === 'progress') console.log(msg.text)
if (msg.type === 'done') console.log('文件大小:', msg.data.size, '字节')
}
// 4. 写入文件
console.log('\n--- 写入文件 ---')
const writeStream = await client.call('writeFile', {
path: 'test.txt',
content: 'Hello from Tool Agent!',
})
for await (const msg of writeStream) {
if (msg.type === 'progress') console.log(msg.text)
if (msg.type === 'done') console.log('写入完成')
}
// 5. 关闭连接
await client.close()第六步:运行
bash
# 终端 1
bun run server.ts
# 终端 2
bun run client.ts输出:
--- 列出文件 ---
列出目录: .
文件列表: [ { name: 'package.json', isDirectory: false }, ... ]
--- 读取文件 ---
读取文件: package.json
文件大小: 234 字节
--- 写入文件 ---
写入文件: test.txt
写入完成进阶:LLM 驱动工具选择
让 LLM 根据用户请求自动选择工具:
typescript
import { createAgentClient, buildTools } from '@multi-agent/a2a'
import { ChatOpenAI } from '@langchain/openai'
const client = createAgentClient({
agentId: 'tool-agent',
address: 'a2a://localhost:50055',
})
// 1. 获取 Agent 能力,构建 LangChain Tools
const agentCard = await client.getAgentCard()
const tools = buildTools(client, agentCard)
// 2. 绑定工具到 LLM
const llm = new ChatOpenAI({ model: 'gpt-4o-mini' })
const llmWithTools = llm.bindTools(tools)
// 3. 用户请求
const response = await llmWithTools.invoke('帮我看看当前目录有哪些文件')
// 4. 执行工具调用
if (response.tool_calls?.length) {
for (const toolCall of response.tool_calls) {
console.log(`调用工具: ${toolCall.name}`)
const stream = await client.call(toolCall.name, toolCall.args)
for await (const msg of stream) {
if (msg.type === 'done') console.log('结果:', msg.data)
}
}
}
await client.close()