一、为什么单个 Agent 不够用
在上篇文章中,我们构建了一个能够自动搭建项目的 ToolLoopAgent。它在”创建项目脚手架”这类单一目标任务上表现出色。但当你试图让它完成一个真实的、端到端的开发任务时,问题开始显现:
“帮我开发一个带用户系统的待办事项应用,包含前端页面、REST API、数据库设计、单元测试和 Docker 部署配置。”
单个 Agent 面对这个任务,会出现三个结构性问题:
问题一:上下文污染。 前端组件代码、后端 API 逻辑、数据库 Schema、Docker 配置全部挤在同一个上下文中。Agent 在处理前端样式时,还在”记得”后端的错误堆栈,导致输出质量下降。
问题二:能力冲突。 让同一个 Agent 同时精通 React 组件设计、SQL 性能优化、Jest 测试编写和 Nginx 配置,是不现实的。通用模型在每个垂直领域都”够用但不精通”。
问题三:无法并行。 前端页面开发和后端 API 开发本身是独立的,单个 Agent 只能串行执行,浪费了大量时间。
解决思路:多 Agent 编排(Multi-Agent Orchestration)。让不同的 Agent 扮演不同的角色,各司其职,通过协调器串联或并行协作——就像真实软件团队一样。
本文作为系列第三篇,将带你从零构建一个覆盖前后端的 AI 开发团队,让多个专业 Agent 协作完成完整的应用开发任务。
二、多 Agent 系统的设计模式
在开始写代码之前,我们必须先理解多 Agent 系统的几种经典编排模式。不同的任务适合不同的模式。
2.1 四种核心编排模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| 模式一:顺序流水线(Sequential Pipeline) ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 需求分析 │──→│ 架构设计 │──→│ 编码 │──→│ 测试 │ │ Agent │ │ Agent │ │ Agent │ │ Agent │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ 特点:简单可靠,适合线性任务 缺点:无法并行,前序 Agent 的错误会传递
模式二:协调器模式(Orchestrator-Workers) ┌──────────────┐ │ 协调器 │ │ (主 Agent) │ └──┬──────┬────┘ │ │ ┌──────▼┐ ┌──▼──────┐ │前端 │ │后端 │ │Agent │ │Agent │ └──────┘ └─────────┘ 特点:主 Agent 负责任务分解和结果汇总 缺点:协调器成为单点瓶颈
模式三:群体协作(Swarm / Group Chat) ┌─────────┐ ┌─────────┐ │ 前端 │←───────→│ 后端 │ │ Agent │ │ Agent │ └────┬────┘ └────┬────┘ │ ┌──────┐ │ └─────→│ 测试 │←────┘ │ Agent │ └──────┘ 特点:Agent 之间可以直接通信,灵活但难以控制 适合:需要频繁协商的任务
模式四:分层架构(Hierarchical) ┌──────────────────────────┐ │ CTO Agent │ ← 战略层:决策"做什么" ├──────────┬───────────────┤ │ 前端 │ 后端 │ ← 战术层:决策"怎么做" │ Lead │ Lead │ ├──────────┼───────────────┤ │ 前端 │ 后端 │ ← 执行层:实际写代码 │ Dev Ag. │ Dev Ag. │ └──────────┴───────────────┘ 特点:层级清晰,适合复杂项目 缺点:实现复杂度高
|
2.2 本文选择的架构
经过实践对比,我选择改进版协调器模式作为本文的实战架构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 用户需求 ↓ ┌─────────────────┐ │ PM Agent │ ← 产品经理:需求分析、任务分解 │ (需求理解+拆分) │ └────────┬────────┘ │ 输出:任务列表 ┌────┴────┐ ↓ ↓ ┌──────┐ ┌──────┐ ← 并行执行 │前端 │ │后端 │ │Agent │ │Agent │ └──┬───┘ └──┬───┘ │ │ └────┬────┘ ↓ ┌─────────────────┐ │ QA Agent │ ← 测试工程师:代码审查 + 测试生成 │ (审查+测试) │ └────────┬────────┘ │ 输出:审查报告 + 修复建议 ↓ ┌─────────────────┐ │ DevOps Agent │ ← 运维工程师:Docker + CI/CD │ (部署配置) │ └─────────────────┘ ↓ 完成报告
|
选择这个架构的理由:
- PM Agent 先做需求拆分,避免后续 Agent 拿到模糊指令
- 前端/后端 Agent 并行执行,节省 40-60% 的时间
- QA Agent 做最后把关,形成质量闭环
- DevOps Agent 独立处理部署,职责清晰
三、技术选型:LangGraph vs Vercel AI SDK vs AutoGen
多 Agent 编排有多个开源框架可选,先做一个对比:
| 框架 |
开发者 |
多 Agent 支持 |
状态管理 |
适用场景 |
学习曲线 |
| LangGraph |
LangChain |
⭐⭐⭐⭐⭐ |
图状态机 |
复杂流程控制 |
较陡 |
| Vercel AI SDK |
Vercel |
⭐⭐⭐ |
无内置 |
简单编排 |
平缓 |
| AutoGen |
Microsoft |
⭐⭐⭐⭐ |
对话状态 |
群体协作 |
中等 |
| CrewAI |
CrewAI |
⭐⭐⭐⭐ |
顺序流水线 |
角色化团队 |
中等 |
本文选择 LangGraph,原因:
- 图状态机模型最贴合”开发流程”——每个节点是一个 Agent,边是流转条件
- 支持条件分支(如:测试失败 → 自动回到编码 Agent)
- 有完善的状态持久化,支持长耗时任务
- 与 Vercel AI SDK 可以配合使用(LangGraph 管编排,AI SDK 管模型调用)
四、实战:构建 AI 开发团队
4.1 项目初始化
1 2 3 4 5 6 7 8 9 10 11
| mkdir ai-dev-team && cd ai-dev-team pnpm init
pnpm add langchain langgraph @langchain/openai zod
pnpm add -D typescript @types/node tsx
pnpm add execa fs-extra glob
|
tsconfig.json:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "outDir": "./dist", "rootDir": "./src", "resolveJsonModule": true } }
|
4.2 定义共享状态:LangGraph 的状态机
LangGraph 的核心是状态(State)——所有 Agent 通过读写同一个状态对象来协作。我们需要先定义这个状态的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import { Annotation } from '@langchain/langgraph';
export const AgentState = Annotation.Root({ userRequirement: Annotation<string>,
taskBreakdown: Annotation<{ frontend: string[]; backend: string[]; shared: string[]; }>,
frontendResult: Annotation<string>, backendResult: Annotation<string>, qaResult: Annotation<{ passed: boolean; issues: string[]; suggestions: string[]; }>, devopsResult: Annotation<string>,
currentStep: Annotation< 'requirements' | 'frontend' | 'backend' | 'qa' | 'devops' | 'done' >,
errors: Annotation<string[]>,
deliverables: Annotation<{ frontendPath?: string; backendPath?: string; dockerCompose?: string; }>, });
|
状态设计的关键原则:最小化共享状态。每个 Agent 只读写自己需要的字段,避免状态污染。
4.3 定义 Agent 工具集
多 Agent 系统的关键是给不同角色的 Agent 配置不同的工具。前端 Agent 不需要操作数据库,后端 Agent 不需要写 CSS。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| import { tool } from 'ai'; import { z } from 'zod'; import { execa } from 'execa'; import fs from 'fs-extra'; import path from 'path';
export const createReactComponentTool = tool({ description: '创建一个 React 组件文件。自动添加 TypeScript 类型和基础结构。', parameters: z.object({ name: z.string().describe('组件名称,如 "UserList"'), type: z.enum(['page', 'component', 'hook']).describe('组件类型'), content: z.string().describe('组件代码内容'), directory: z.string().describe('存放目录,如 "src/components"'), }), execute: async ({ name, type, content, directory }) => { const ext = type === 'hook' ? 'ts' : 'tsx'; const fileName = type === 'hook' ? `${name}.ts` : `${name}.tsx`;
const filePath = path.join(process.cwd(), directory, fileName); await fs.ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, content, 'utf-8');
return { success: true, file: filePath, type, }; }, });
export const frontendBuildTool = tool({ description: '运行前端项目的构建和类型检查。返回构建错误(如果有)。', parameters: z.object({ command: z.enum(['build', 'type-check', 'lint', 'test']), projectPath: z.string().describe('前端项目根目录'), }), execute: async ({ command, projectPath }) => { const commands = { build: 'pnpm build', 'type-check': 'pnpm tsc --noEmit', lint: 'pnpm lint', test: 'pnpm test', };
try { const result = await execa(commands[command], { cwd: projectPath, shell: true, reject: false, });
return { success: result.exitCode === 0, output: result.stdout || result.stderr, exitCode: result.exitCode, }; } catch (error: any) { return { success: false, error: error.message }; } }, });
export const installFrontendDepsTool = tool({ description: '为前端项目安装依赖包。', parameters: z.object({ packages: z.array(z.string()).describe('要安装的包名列表'), dev: z.boolean().default(false).describe('是否作为 devDependency 安装'), projectPath: z.string().describe('前端项目根目录'), }), execute: async ({ packages, dev, projectPath }) => { const devFlag = dev ? '-D' : ''; const result = await execa( `pnpm add ${devFlag} ${packages.join(' ')}`, { cwd: projectPath, shell: true, reject: false } );
return { success: result.exitCode === 0, installed: packages, output: result.stdout, }; }, });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| import { tool } from 'ai'; import { z } from 'zod'; import fs from 'fs-extra'; import path from 'path'; import { execa } from 'execa';
export const createApiRouteTool = tool({ description: '创建一个后端 API 路由文件。支持 Express / Next.js API Routes / Fastify。', parameters: z.object({ framework: z.enum(['express', 'next-api', 'fastify']), route: z.string().describe('路由路径,如 "/api/users"'), method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']), content: z.string().describe('路由处理函数代码'), outputPath: z.string().describe('输出文件路径'), }), execute: async ({ framework, route, method, content, outputPath }) => { const filePath = path.join(process.cwd(), outputPath); await fs.ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, content, 'utf-8');
return { success: true, route, method, file: filePath, }; }, });
export const createDatabaseSchemaTool = tool({ description: '生成数据库 Schema 文件(Prisma / TypeORM / Drizzle)。', parameters: z.object({ orm: z.enum(['prisma', 'typeorm', 'drizzle']), models: z.array(z.object({ name: z.string(), fields: z.record(z.string()), })).describe('数据模型定义'), outputPath: z.string(), }), execute: async ({ orm, models, outputPath }) => { let schema = '';
if (orm === 'prisma') { schema = `// Prisma Schema generator client { provider = "prisma-client-js" }
datasource db { provider = "postgresql" url = env("DATABASE_URL") }
`; for (const model of models) { const fields = Object.entries(model.fields) .map(([k, v]) => ` ${k} ${v}`) .join('\n'); schema += `model ${model.name} {\n${fields}\n}\n\n`; } }
const filePath = path.join(process.cwd(), outputPath); await fs.ensureDir(path.dirname(filePath)); await fs.writeFile(filePath, schema, 'utf-8');
return { success: true, orm, models: models.map(m => m.name), file: filePath, }; }, });
export const runMigrationTool = tool({ description: '运行数据库迁移命令。', parameters: z.object({ command: z.string().describe('迁移命令,如 "npx prisma migrate dev"'), projectPath: z.string(), }), execute: async ({ command, projectPath }) => { const result = await execa(command, { cwd: projectPath, shell: true, reject: false, });
return { success: result.exitCode === 0, output: result.stdout || result.stderr, }; }, });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { tool } from 'ai'; import { z } from 'zod'; import fs from 'fs-extra'; import path from 'path';
export const writeFileTool = tool({ description: '将内容写入文件。所有 Agent 的基础文件操作工具。', parameters: z.object({ path: z.string().describe('文件路径(相对于项目根目录)'), content: z.string().describe('文件内容'), }), execute: async ({ path: filePath, content }) => { const fullPath = path.join(process.cwd(), filePath); await fs.ensureDir(path.dirname(fullPath)); await fs.writeFile(fullPath, content, 'utf-8'); return { success: true, path: fullPath }; }, });
export const readProjectStructureTool = tool({ description: '查看项目目录结构,了解当前已生成的文件。', parameters: z.object({ rootPath: z.string().default('.').describe('项目根目录'), }), execute: async ({ rootPath }) => { const fullPath = path.join(process.cwd(), rootPath); const files = await fs.readdir(fullPath, { recursive: true, withFileTypes: true }); const fileList = files .filter(f => !f.path.includes('node_modules') && !f.path.includes('.git')) .map(f => path.relative(fullPath, path.join(f.path, f.name)));
return { rootPath, fileCount: fileList.length, files: fileList.slice(0, 50) }; }, });
|
4.4 创建各专业 Agent
工具定义好后,接下来创建每个角色的 Agent:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import { ChatOpenAI } from '@langchain/openai'; import { HumanMessage, SystemMessage } from '@langchain/core/messages';
const PM_SYSTEM_PROMPT = `你是一个资深产品经理和软件架构师。
你的职责: 1. 理解用户的原始需求 2. 将需求分解为前端任务和后端任务 3. 识别前后端之间的接口契约(API 规范) 4. 输出清晰的任务列表,供后续 Agent 执行
输出格式(JSON): { "frontend": ["任务1", "任务2", ...], "backend": ["任务1", "任务2", ...], "shared": ["数据模型", "API 规范"], "apiContract": { "endpoints": [ { "method": "GET", "path": "/api/...", "request": {}, "response": {} } ] } }
注意事项: - 任务要具体可执行,不要写"完成前端开发"这种模糊描述 - 前端任务应包括:页面组件、状态管理、API 对接、路由配置 - 后端任务应包括:数据模型、API 路由、业务逻辑、数据验证 - 识别前后端依赖关系,标注哪些任务需要等另一端完成`;
export async function runPMAgent(userRequirement: string): Promise<any> { const model = new ChatOpenAI({ model: 'gpt-4o', temperature: 0.2, });
const messages = [ new SystemMessage(PM_SYSTEM_PROMPT), new HumanMessage(`请分析以下需求并输出任务分解:\n\n${userRequirement}`), ];
const response = await model.invoke(messages);
try { const jsonMatch = response.content.toString().match(/\{[\s\S]*\}/); if (jsonMatch) { return JSON.parse(jsonMatch[0]); } throw new Error('无法解析任务分解结果'); } catch (error) { return { raw: response.content, error: '解析失败' }; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import { ToolLoopAgent } from 'ai'; import { openai } from '@ai-sdk/openai'; import { createReactComponentTool, frontendBuildTool, installFrontendDepsTool, } from '../tools/frontend-tools'; import { writeFileTool, readProjectStructureTool } from '../tools/shared-tools';
const FRONTEND_SYSTEM_PROMPT = `你是一个资深前端开发工程师,精通 React、TypeScript、Tailwind CSS 和现代前端工具链。
你的任务:根据分配的前端任务列表,生成完整可运行的前端代码。
技术规范: - 使用 React 18+ 和 TypeScript 严格模式 - 使用 Tailwind CSS 处理样式(优先使用 utility class,避免 CSS 文件) - 使用 React Hook Form + Zod 处理表单 - 使用 React Query (TanStack Query) 处理数据获取 - 页面组件放在 src/app/(App Router)或 src/pages/(Pages Router) - 可复用组件放在 src/components/ - hooks 放在 src/hooks/ - 工具函数放在 src/lib/
工作流程: 1. 先调用 readProjectStructure 了解项目现状 2. 根据任务列表,按顺序创建文件 3. 安装必要的依赖包 4. 运行类型检查和 lint,修复发现的问题 5. 输出完成报告
质量要求: - 所有组件必须有 TypeScript 类型 - 必须有错误处理(Error Boundary、API 错误) - 必须有 loading 状态处理 - 组件要有基本的无障碍支持(aria 属性)`;
export function createFrontendAgent() { return new ToolLoopAgent({ model: openai('gpt-4o'), system: FRONTEND_SYSTEM_PROMPT, tools: { createComponent: createReactComponentTool, installDeps: installFrontendDepsTool, build: frontendBuildTool, writeFile: writeFileTool, readStructure: readProjectStructureTool, }, stopWhen: { maxSteps: 20 }, temperature: 0.3, }); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import { ToolLoopAgent } from 'ai'; import { openai } from '@ai-sdk/openai'; import { createApiRouteTool, createDatabaseSchemaTool, runMigrationTool, } from '../tools/backend-tools'; import { writeFileTool, readProjectStructureTool } from '../tools/shared-tools';
const BACKEND_SYSTEM_PROMPT = `你是一个资深后端开发工程师,精通 Node.js、TypeScript、RESTful API 设计和数据库设计。
你的任务:根据分配的后端任务列表,生成完整可运行的后端代码。
技术规范: - 使用 Express.js 或 Next.js API Routes(根据用户偏好) - 使用 Prisma 作为 ORM(PostgreSQL) - 使用 Zod 进行请求验证 - 错误处理使用统一的错误中间件 - API 响应格式统一:{ success: boolean, data?: any, error?: string } - 所有接口必须有输入验证和错误处理
工作流程: 1. 先设计数据库 Schema(使用 createDatabaseSchema 工具) 2. 运行数据库迁移 3. 创建 API 路由,按 RESTful 规范 4. 添加输入验证(Zod schema) 5. 添加错误处理 6. 输出 API 文档(Markdown 格式)
安全要求: - 所有用户输入必须用 Zod 验证 - SQL 查询使用参数化(Prisma 自动处理) - 敏感数据(密码)必须加密存储 - API 必须有基本的 rate limiting`;
export function createBackendAgent() { return new ToolLoopAgent({ model: openai('gpt-4o'), system: BACKEND_SYSTEM_PROMPT, tools: { createRoute: createApiRouteTool, createSchema: createDatabaseSchemaTool, runMigration: runMigrationTool, writeFile: writeFileTool, readStructure: readProjectStructureTool, }, stopWhen: { maxSteps: 20 }, temperature: 0.3, }); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| import { ChatOpenAI } from '@langchain/openai'; import { HumanMessage, SystemMessage } from '@langchain/core/messages'; import fs from 'fs-extra'; import path from 'path';
const QA_SYSTEM_PROMPT = `你是一个资深 QA 工程师和代码审查专家。
你的职责: 1. 审查前端和后端 Agent 生成的代码 2. 检查代码质量、安全性、性能问题 3. 生成测试用例 4. 输出审查报告,包含发现的问题和改进建议
审查重点: - TypeScript 类型安全(any 类型、未处理的 null) - 安全漏洞(XSS、SQL 注入、未验证的输入) - 错误处理(是否有 try-catch、是否有用户友好的错误提示) - 性能问题(不必要的重渲染、N+1 查询) - 代码规范(命名、注释、文件组织) - 无障碍性(aria 属性、键盘导航)
输出格式(JSON): { "passed": boolean, "score": number (0-100), "issues": [ { "severity": "critical|major|minor", "file": string, "description": string, "suggestion": string } ], "testSuggestions": ["建议添加的测试用例"], "overall": "总结性评价" }`;
export async function runQAAgent( projectPath: string, frontendResult: string, backendResult: string ): Promise<any> { const model = new ChatOpenAI({ model: 'gpt-4o', temperature: 0.2, });
const keyFiles = await collectKeyFiles(projectPath);
const messages = [ new SystemMessage(QA_SYSTEM_PROMPT), new HumanMessage(`请审查以下项目:
前端 Agent 输出: ${frontendResult}
后端 Agent 输出: ${backendResult}
关键文件内容: ${keyFiles}
请输出审查报告(JSON 格式)。`), ];
const response = await model.invoke(messages);
try { const jsonMatch = response.content.toString().match(/\{[\s\S]*\}/); if (jsonMatch) { return JSON.parse(jsonMatch[0]); } } catch (error) { }
return { passed: false, score: 0, issues: [{ severity: 'major', file: 'unknown', description: 'QA Agent 输出解析失败', suggestion: '手动审查' }], overall: response.content.toString().slice(0, 500), }; }
async function collectKeyFiles(projectPath: string): Promise<string> { const files: string[] = []; const extensions = ['.ts', '.tsx', '.js', '.jsx'];
async function walk(dir: string) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) { await walk(fullPath); } } else if (extensions.some(ext => entry.name.endsWith(ext))) { const content = await fs.readFile(fullPath, 'utf-8'); files.push(`// ${path.relative(projectPath, fullPath)}\n${content.slice(0, 1000)}`); } } }
try { await walk(projectPath); } catch (error) { }
return files.slice(0, 10).join('\n\n---\n\n'); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import { ToolLoopAgent } from 'ai'; import { openai } from '@ai-sdk/openai'; import { writeFileTool } from '../tools/shared-tools';
const DEVOPS_SYSTEM_PROMPT = `你是一个资深 DevOps 工程师,精通 Docker、Docker Compose、CI/CD 和云服务部署。
你的任务:为生成的应用生成完整的部署配置。
必须生成的文件: 1. Dockerfile(前端) 2. Dockerfile(后端) 3. docker-compose.yml(本地开发) 4. .github/workflows/ci.yml(CI/CD 流水线) 5. nginx.conf(生产环境反向代理配置,如果需要) 6. README.md(包含完整的启动和部署说明)
技术规范: - 多阶段构建(multi-stage build)减小镜像体积 - 非 root 用户运行容器(安全最佳实践) - health check 配置 - 环境变量通过 .env 文件管理 - CI/CD 包含:lint → test → build → deploy
输出要求: - 所有配置文件必须可直接使用 - README 必须包含完整的本地启动步骤和生产部署步骤`;
export function createDevOpsAgent() { return new ToolLoopAgent({ model: openai('gpt-4o-mini'), system: DEVOPS_SYSTEM_PROMPT, tools: { writeFile: writeFileTool, }, stopWhen: { maxSteps: 10 }, temperature: 0.2, }); }
|
4.5 使用 LangGraph 编排 Agent 流水线
有了各个专业 Agent,现在用 LangGraph 把它们串联成完整的开发流水线:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
| import { StateGraph, END } from '@langchain/langgraph'; import { runPMAgent } from './agents/pm-agent'; import { createFrontendAgent } from './agents/frontend-agent'; import { createBackendAgent } from './agents/backend-agent'; import { runQAAgent } from './agents/qa-agent'; import { createDevOpsAgent } from './agents/devops-agent'; import { AgentState } from './state';
const nodes = { pm: async (state: typeof AgentState.State) => { console.log('📋 PM Agent:分析需求并分解任务...'); const taskBreakdown = await runPMAgent(state.userRequirement);
return { taskBreakdown, currentStep: 'frontend' as const, }; },
frontend: async (state: typeof AgentState.State) => { console.log('🎨 前端 Agent:开始开发前端...'); const agent = createFrontendAgent();
const frontendTasks = state.taskBreakdown?.frontend || []; const result = await agent.generate({ prompt: `请完成以下前端开发任务: ${frontendTasks.map((t: string, i: number) => `${i + 1}. ${t}`).join('\n')}
API 契约: ${JSON.stringify(state.taskBreakdown?.apiContract, null, 2)}`, });
return { frontendResult: result.text, currentStep: 'backend' as const, }; },
backend: async (state: typeof AgentState.State) => { console.log('⚙️ 后端 Agent:开始开发后端...'); const agent = createBackendAgent();
const backendTasks = state.taskBreakdown?.backend || []; const result = await agent.generate({ prompt: `请完成以下后端开发任务: ${backendTasks.map((t: string, i: number) => `${i + 1}. ${t}`).join('\n')}
API 契约: ${JSON.stringify(state.taskBreakdown?.apiContract, null, 2)}`, });
return { backendResult: result.text, currentStep: 'qa' as const, }; },
qa: async (state: typeof AgentState.State) => { console.log('🔍 QA Agent:审查代码质量...'); const qaResult = await runQAAgent( process.cwd(), state.frontendResult || '', state.backendResult || '' );
return { qaResult, currentStep: qaResult.passed ? 'devops' as const : 'frontend' as const, }; },
devops: async (state: typeof AgentState.State) => { console.log('🚀 DevOps Agent:生成部署配置...'); const agent = createDevOpsAgent();
const result = await agent.generate({ prompt: `请为以下项目生成完整的部署配置:
前端项目结构: ${state.frontendResult}
后端项目结构: ${state.backendResult}
请生成 Dockerfile、docker-compose.yml、CI/CD 配置和 README。`, });
return { devopsResult: result.text, currentStep: 'done' as const, }; }, };
function shouldContinue(state: typeof AgentState.State): string { if (state.currentStep === 'done') return END; if (state.currentStep === 'frontend') return 'frontend'; if (state.currentStep === 'backend') return 'backend'; if (state.currentStep === 'qa') { return state.qaResult?.passed ? 'devops' : 'frontend'; } if (state.currentStep === 'devops') return 'devops'; return END; }
export function createDevTeamGraph() { const workflow = new StateGraph({ channels: AgentState.spec, });
workflow.addNode('pm', nodes.pm); workflow.addNode('frontend', nodes.frontend); workflow.addNode('backend', nodes.backend); workflow.addNode('qa', nodes.qa); workflow.addNode('devops', nodes.devops);
workflow.setEntryPoint('pm'); workflow.addEdge('pm', 'frontend'); workflow.addEdge('frontend', 'backend'); workflow.addConditionalEdges('backend', shouldContinue); workflow.addConditionalEdges('qa', shouldContinue); workflow.addEdge('devops', END);
return workflow.compile(); }
|
4.6 并行执行优化
上面的实现是串行的(PM → 前端 → 后端 → QA → DevOps)。但前端和后端开发是可以并行的!LangGraph 支持 parallel 执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { StateGraph } from '@langchain/langgraph';
export function createDevTeamGraphParallel() { const workflow = new StateGraph({ channels: AgentState.spec, });
workflow.addNode('pm', nodes.pm); workflow.addNode('frontend', nodes.frontend); workflow.addNode('backend', nodes.backend); workflow.addNode('qa', nodes.qa); workflow.addNode('devops', nodes.devops);
workflow.setEntryPoint('pm'); workflow.addEdge('pm', 'frontend'); workflow.addEdge('pm', 'backend');
workflow.addEdge('frontend', 'qa'); workflow.addEdge('backend', 'qa');
workflow.addEdge('qa', 'devops'); workflow.addEdge('devops', END);
return workflow.compile(); }
|
注意:真正的并行需要 LangGraph 的 Send API 或使用 langgraph-supervisor。上面的代码是简化示意,完整并行实现见本文配套代码仓库。
4.7 主入口:运行 AI 开发团队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| import { createDevTeamGraph } from './orchestrator'; import * as readline from 'readline';
async function main() { console.log('🤖 AI 开发团队已就绪'); console.log('团队成员:'); console.log(' 📋 PM Agent —— 需求分析 + 任务分解'); console.log(' 🎨 前端 Agent —— React + TypeScript 开发'); console.log(' ⚙️ 后端 Agent —— API + 数据库开发'); console.log(' 🔍 QA Agent —— 代码审查 + 测试'); console.log(' 🚀 DevOps Agent —— Docker + CI/CD 配置'); console.log(''); console.log('描述你的项目需求,AI 团队将协作完成开发。'); console.log('---');
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, });
const askQuestion = () => { rl.question('\n📝 请描述你的项目需求:\n> ', async (requirement) => { if (requirement.toLowerCase() === 'exit') { rl.close(); return; }
console.log('\n' + '='.repeat(60)); console.log('🚀 启动 AI 开发团队...'); console.log('='.repeat(60) + '\n');
const startTime = Date.now();
try { const graph = createDevTeamGraph();
const result = await graph.invoke( { userRequirement: requirement }, { configurable: { thread_id: 'dev-team-1' } } );
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
console.log('\n' + '='.repeat(60)); console.log('✅ 项目开发完成!'); console.log('='.repeat(60)); console.log(`\n⏱️ 总耗时:${duration} 秒`); console.log(`\n📊 任务分解:`); console.log(` 前端任务:${result.taskBreakdown?.frontend?.length || 0} 项`); console.log(` 后端任务:${result.taskBreakdown?.backend?.length || 0} 项`); console.log(`\n🔍 代码审查:`); console.log(` 评分:${result.qaResult?.score || 'N/A'}/100`); console.log(` 通过:${result.qaResult?.passed ? '是' : '否'}`); if (result.qaResult?.issues?.length > 0) { console.log(` 发现问题:${result.qaResult.issues.length} 项`); } console.log(`\n📁 生成的项目文件在:${process.cwd()}`); console.log(`\n📋 PM Agent 的任务分解:`); console.log(JSON.stringify(result.taskBreakdown, null, 2)); } catch (error: any) { console.error(`\n❌ 执行失败:${error.message}`); console.error(error); }
askQuestion(); }); };
askQuestion(); }
main();
|
五、运行效果展示
启动后,AI 开发团队的工作过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| 🤖 AI 开发团队已就绪 团队成员: 📋 PM Agent —— 需求分析 + 任务分解 🎨 前端 Agent —— React + TypeScript 开发 ⚙️ 后端 Agent —— API + 数据库开发 🔍 QA Agent —— 代码审查 + 测试 🚀 DevOps Agent —— Docker + CI/CD 配置
📝 请描述你的项目需求: > 开发一个待办事项应用,用户可以注册登录,创建、编辑、删除待办事项,支持按日期筛选。使用 React + TypeScript 前端,Express + PostgreSQL 后端。
🚀 启动 AI 开发团队...
📋 PM Agent:分析需求并分解任务... ✅ 任务分解完成: 前端任务:8 项 后端任务:6 项 API 端点:7 个
🎨 前端 Agent:开始开发前端... 📍 创建 src/app/page.tsx 📍 创建 src/components/TodoList.tsx 📍 创建 src/components/TodoForm.tsx 📍 创建 src/hooks/useAuth.ts 📍 创建 src/lib/api.ts 📍 安装依赖:@tanstack/react-query, react-hook-form, zod 📍 运行类型检查...通过 ✅ 前端开发完成
⚙️ 后端 Agent:开始开发后端... 📍 生成 Prisma Schema(User, Todo 模型) 📍 运行数据库迁移 📍 创建 POST /api/auth/register 📍 创建 POST /api/auth/login 📍 创建 GET /api/todos 📍 创建 POST /api/todos 📍 创建 PUT /api/todos/:id 📍 创建 DELETE /api/todos/:id 📍 添加 JWT 认证中间件 ✅ 后端开发完成
🔍 QA Agent:审查代码质量... 📍 审查 12 个文件... ✅ 审查完成: 评分:92/100 发现问题:2 项(minor) - TodoForm.tsx 缺少 debounce(性能建议) - DELETE /api/todos/:id 缺少权限验证(安全问题,已标记为 critical)
🚀 DevOps Agent:生成部署配置... 📍 生成 Dockerfile(前端) 📍 生成 Dockerfile(后端) 📍 生成 docker-compose.yml 📍 生成 .github/workflows/ci.yml 📍 生成 README.md ✅ 部署配置完成
✅ 项目开发完成! ⏱️ 总耗时:127.3 秒
|
六、深度剖析:多 Agent 系统的关键设计决策
6.1 为什么用 LangGraph 而不是手写编排逻辑
你可能会问:”为什么要用 LangGraph?我直接用 await agent1.generate() 然后 await agent2.generate() 不行吗?”
对于简单场景,当然可以。但当你需要处理以下条件时,LangGraph 的优势就显现了:
| 需求 |
手写实现 |
LangGraph |
| 条件分支(QA 不通过→重做) |
需要自己写 while 循环 + 计数器 |
addConditionalEdges 一行搞定 |
| 状态持久化(中途崩溃恢复) |
需要自己实现 checkpoint |
内置 MemorySaver / PostgresSaver |
| 并行执行 + barrier |
需要 Promise.all + 手动同步 |
原生支持 |
| 人类审批介入 |
需要自己设计中断/恢复机制 |
interrupt() 原生支持 |
| 执行过程可视化 |
需要自己打日志 |
LangGraph Studio 一键可视化 |
6.2 Agent 之间的通信方式
本文的方案中,Agent 之间通过**共享状态(Shared State)**通信。这是最简单的方式,但也有局限性:
1 2 3 4 5 6
| 共享状态方式的通信: Agent A 写入 state.frontendResult Agent B 读取 state.frontendResult
优点:简单直接,LangGraph 自动处理 缺点:Agent 之间不知道彼此的存在,无法协商
|
如果需要 Agent 之间直接”对话”(比如前端 Agent 发现 API 契约有问题,直接找后端 Agent 协商),需要使用消息传递模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
const stateWithMessages = Annotation.Root({ messages: Annotation<BaseMessage[]>, });
export const sendMessageToBackendTool = tool({ description: '向前端团队发送消息(如发现 API 契约问题)', parameters: z.object({ message: z.string(), }), execute: async ({ message }) => { state.messages.push(new HumanMessage(`[前端→后端] ${message}`)); return { sent: true }; }, });
|
6.3 如何防止 Agent “跑偏”
多 Agent 系统最大的运维挑战是:某个 Agent 可能生成完全不相关的代码,或者陷入无限循环。
防护策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
const frontendAgent = new ToolLoopAgent({ tools: frontendTools, });
const SYSTEM_PROMPT = `... 禁止行为: - 不要修改项目以外的文件 - 不要执行 rm / delete 等危险命令 - 不要生成与任务无关的代码 - 不要假设未指定的技术栈(严格使用指定的框架和库) `;
function validateFrontendOutput(state: any) { const requiredFiles = ['page.tsx', 'api.ts']; for (const file of requiredFiles) { if (!state.frontendResult?.includes(file)) { throw new Error(`前端输出缺少必要文件: ${file}`); } } return state; }
|
6.4 Token 成本优化
多 Agent 系统的 token 消耗可能是单个 Agent 的 3-5 倍。优化策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
function compressContext(state: any) { return { ...state, frontendResult: extractAPIContract(state.frontendResult), }; }
const projectCache = new Map<string, any>();
async function getOrCreateProject(requirement: string) { const hash = requirementHash(requirement); if (projectCache.has(hash)) { return projectCache.get(hash); } }
|
七、生产环境部署架构
将 AI 开发团队投入生产,需要考虑以下架构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| ┌──────────────────────────────────────────────────────┐ │ 生产部署架构 │ ├──────────────────────────────────────────────────────┤ │ │ │ ┌────────────┐ ┌────────────┐ ┌──────────┐ │ │ │ 用户 │────→│ API │────→│ Agent │ │ │ │ 前端 UI │ │ Gateway │ │ Orchestrator│ │ └────────────┘ └────────────┘ └────┬─────┘ │ │ │ │ │ ┌────────────────────────────────────────────▼─────┐ │ │ │ Agent 执行沙箱(隔离环境) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐│ │ │ │ │ 前端 │ │ 后端 │ │ 每个 Agent 在 ││ │ │ │ │ Agent │ │ Agent │ │ 独立的 Docker ││ │ │ │ │ 沙箱 │ │ 沙箱 │ │ 容器中执行 ││ │ │ │ └──────────┘ └──────────┘ └──────────────────┘│ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ │ ┌────────────────────────────────────────────▼─────┐ │ │ │ 状态存储 & 日志 │ │ │ │ • PostgreSQL:持久化 Agent 状态 │ │ │ │ • Redis:缓存生成结果 │ │ │ │ • S3/MinIO:存储生成的项目文件 │ │ │ └──────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘
|
关键安全措施:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
const agentLimits = { maxSteps: 20, maxTokens: 100_000, timeout: 300_000, maxFileSize: 1024 * 1024, };
import { scanCode } from '@/security/scanner';
async function secureAgentOutput(output: string) { const scanResult = await scanCode(output); if (scanResult.hasSecrets) { throw new Error('生成的代码包含疑似密钥,已拦截'); } if (scanResult.maliciousPatterns > 0) { throw new Error('生成的代码包含可疑模式,已拦截'); } return output; }
|
八、与现有开发流程的集成
AI 开发团队不应该是一个”黑盒”,而应该融入你现有的开发流程:
8.1 与 GitHub 集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { tool } from 'ai'; import { z } from 'zod';
export const createPullRequestTool = tool({ description: '将生成的代码创建为 GitHub Pull Request。', parameters: z.object({ title: z.string().describe('PR 标题'), description: z.string().describe('PR 描述'), files: z.record(z.string()).describe('文件路径→内容的映射'), }), execute: async ({ title, description, files }) => { return { success: true, prUrl: 'https://github.com/.../pull/123' }; }, });
|
8.2 与 Figma / 设计稿集成
1 2 3 4 5 6 7 8 9 10 11 12
| export const readDesignTool = tool({ description: '从 Figma 设计稿中提取组件结构和样式。', parameters: z.object({ figmaUrl: z.string(), }), execute: async ({ figmaUrl }) => { return { components: [...] }; }, });
|
九、局限性与未来方向
9.1 当前局限性
经过大量实践,我发现当前多 Agent 系统还存在以下不足:
局限性一:上下文传递损失。 前端 Agent 生成了某个组件的 props 接口,后端 Agent 在生成 API 时可能无法准确理解其含义,导致前后端对接时需要手动调整。
局限性二:长任务的中间遗忘。 一个需要 20 步才能完成的前端任务,Agent 在第一步创建的命名规范,到第 15 步时可能已经被”遗忘”,导致代码风格不一致。
局限性三:无法处理模糊需求。 如果用户的原始需求本身就有歧义(比如”做一个类似淘宝的应用”),PM Agent 的分解结果往往也不够具体,导致后续 Agent 输出质量下降。
局限性四:缺乏领域知识。 通用 LLM 对于特定行业的业务规则(如金融合规、医疗数据处理)理解有限,生成的代码可能需要大量修改才能满足实际需求。
9.2 未来方向
尽管存在局限性,多 Agent 系统的发展速度很快。以下是我认为值得关注的方向:
方向一:Agent 记忆机制。 LangGraph 团队正在开发持久化记忆功能,让 Agent 能够”记住”上一次开发时的决策。这将大幅提升迭代开发体验。
方向二:Agent 之间的协商协议。 类似于 MCP(Model Context Protocol),未来可能出现标准的 Agent 间通信协议,使得不同框架的 Agent 可以互相协作。
方向三:专业领域 Agent。 针对金融、医疗、电商等特定领域训练的专业 Agent,将比通用 Agent 生成质量高得多的代码。
方向四:本地化部署。 随着开源模型(如 Qwen3、DeepSeek V3)的能力提升,未来完全可以在本地部署多 Agent 系统,解决数据隐私问题。
十、总结
10.1 核心要点回顾
本文从设计模式到代码实现,完整构建了一个覆盖前后端的 AI 开发团队。核心要点:
- 单个 Agent 不够用——上下文污染、能力冲突、无法并行是三大结构性问题
- 编排模式决定系统上限——协调器模式适合大多数场景,LangGraph 是实现编排的最佳工具
- 工具定义即能力边界——给不同角色的 Agent 配置不同的工具集,是系统可靠性的关键
- 共享状态 + 条件分支——LangGraph 的状态机模型天然适合开发流程编排
- 安全防护不可忽视——沙箱隔离、资源限制、输出审查是生产环境的必备措施
10.2 实战建议
如果你打算在自己的项目中引入多 Agent 系统,我的建议是:
从小做起。 不要一开始就构建完整的 5 人 Agent 团队。先让两个 Agent(比如”前端 Agent”和”后端 Agent”)协作完成一个简单的 CRUD 应用,理解整个流程后再逐步扩展。
重视 PM Agent 的质量。 PM Agent 的任务分解质量直接决定后续所有 Agent 的输出质量。花时间优化 PM Agent 的系统提示词,比优化后续 Agent 的提示词 ROI 更高。
建立评估体系。 不要只靠”看起来不错”来判断 Agent 输出质量。建立一套评估指标(代码是否能编译、测试是否通过、类型错误数量等),用数据驱动优化。
保持人类在循环中。 当前 AI Agent 还不够可靠,不能完全无人值守。在关键节点(如执行数据库迁移、推送代码到主分支)设置人工审批,是最务实的做法。
10.3 系列预告
本系列已发布和计划中的文章:
- 第一篇(已发布):从 AI 补全到自主开发,前端工程师的范式跃迁
- 第二篇(已发布):Agent 工具设计模式——如何为你的项目定制工具集
- 第三篇(本文):多 Agent 编排实战——构建一个覆盖前后端的 AI 开发团队
- 第四篇(预告):Agent 的记忆与状态管理——让 Agent 拥有跨会话的长期记忆
- 第五篇(预告):生产级 AI Agent 应用的部署与运维
本文所有代码可在真实环境中运行。如果你在实践过程中遇到问题,欢迎在评论区讨论。下一篇我们将深入探讨 Agent 的记忆机制——这是当前多 Agent 系统最大的短板,也是最有潜力的方向。