前言
如今的大模型应用架构设计基本都是一个主Agent携带多个子Agent。
主Agent负责调度其他垂类Agent,子Agent负责单一领域的角色,属于垂直域专家。
架构上比较类似这样:
┌─────────────────────────────────────────────────────────┐
│ 主 Agent(Orchestrator) │
│ 职责:理解用户意图、分解任务、协调子 Agent、聚合结果 │
└──────────────────────┬──────────────────────────────────┘
│
┌──────────────┼──────────────┬──────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│差旅Agent│ │日程Agent│ │支付Agent│ │通知Agent│
│(Travel)│ │(Calendar)│ │(Payment)│ │(Alert) │
└────────┘ └────────┘ └────────┘ └────────┘
│ │ │ │
└──────────────┴──────────────┴──────────────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
数据库 API 服务 外部服务
(DB) (Flights, (Payment,
Hotels, Email,
Trains) SMS)
那一个基本的LLM应用框架一般怎么设计?本文基于Midwayjs来解读分析。
Agent&提示词设计
基类Agent
所有Agent都集成于该类,核心触发如下能力。
- 上下文管理;
- 大模型调用;
- 提示词注入;
// src/agent/base-agent.ts
import { Logger } from "@midwayjs/core"
import { LLMService } from "@/service/llm.service"
interface Message {
role: "system" | "user" | "assistant"
content: string
}
interface ToolCall {
name: string
arguments: Record<string, any>
id?: string
}
/**
* Agent 基类
* 所有 Agent 都继承这个基类
*/
export abstract class BaseAgent {
@Logger()
logger: any
protected llmService: LLMService
protected conversationHistory: Message[] = []
constructor(llmService: LLMService) {
this.llmService = llmService
}
/**
* 初始化 Agent
* 1. 设置系统提示词
* 2. 注入工具定义
* 3. 初始化对话历史
*/
protected initializeAgent(
systemPrompt: string,
tools: any[]
): void {
this.logger.info(`[${this.getAgentName()}] 初始化 Agent`)
// Step 1: 清空历史对话
this.conversationHistory = []
// Step 2: 添加系统提示词
const enrichedSystemPrompt = this.enrichSystemPrompt(
systemPrompt,
tools
)
this.conversationHistory.push({
role: "system",
content: enrichedSystemPrompt,
})
this.logger.info(
`[${this.getAgentName()}] Agent 初始化完成,已注入 ${tools.length} 个工具`
)
}
/**
* 增强系统提示词(注入工具定义)
*/
private enrichSystemPrompt(systemPrompt: string, tools: any[]): string {
const toolDescriptions = tools
.map(
(tool) => `
### 工具:${tool.name}
描述:${tool.description}
参数:${JSON.stringify(tool.parameters, null, 2)}
`
)
.join("\n")
return `
${systemPrompt}
## 可用的工具
${toolDescriptions}
## 工具调用格式
当你需要使用工具时,请返回以下 JSON 格式:
\`\`\`json
{
"type": "tool_call",
"tool_name": "工具名称",
"arguments": {
"参数1": "值1",
"参数2": "值2"
}
}
\`\`\`
重要:
1. 每次只调用一个工具
2. 工具会返回结果,你会收到 "tool_result" 角色的消息
3. 根据工具结果继续推理和决策
4. 最终向用户返回友好的文字回复
`
}
/**
* 与大模型交互(核心方法)
*/
async callLLM(userMessage: string): Promise<string> {
this.logger.info(
`[${this.getAgentName()}] 用户消息: ${userMessage}`
)
// 1. 添加用户消息到历史
this.conversationHistory.push({
role: "user",
content: userMessage,
})
// 2. 调用大模型
let response = await this.llmService.call({
model: "gpt-4",
messages: this.conversationHistory,
temperature: 0.7,
maxTokens: 2000,
})
this.logger.info(
`[${this.getAgentName()}] 模型响应: ${response.content.substring(0, 100)}...`
)
// 3. 检查是否是工具调用
let finalResponse = response.content
let toolCalls = this.extractToolCalls(response.content)
// 4. 如果有工具调用,递归执行直到没有工具调用
while (toolCalls.length > 0) {
this.logger.info(
`[${this.getAgentName()}] 检测到工具调用: ${toolCalls.map((t) => t.name).join(", ")}`
)
// 添加助手的响应到历史
this.conversationHistory.push({
role: "assistant",
content: response.content,
})
// 执行所有工具调用
const toolResults = await Promise.all(
toolCalls.map((call) =>
this.executeTool(call.name, call.arguments)
)
)
// 5. 将工具结果添加到历史
const toolResultMessage = toolResults
.map(
(result, index) => `
[工具结果 ${index + 1}]
工具:${toolCalls[index].name}
参数:${JSON.stringify(toolCalls[index].arguments)}
结果:${JSON.stringify(result, null, 2)}
`
)
.join("\n")
this.conversationHistory.push({
role: "user",
content: `工具执行结果:\n${toolResultMessage}`,
})
this.logger.info(
`[${this.getAgentName()}] 工具执行完成,继续推理...`
)
// 6. 再次调用大模型,让它基于工具结果继续推理
response = await this.llmService.call({
model: "gpt-4",
messages: this.conversationHistory,
temperature: 0.7,
maxTokens: 2000,
})
this.logger.info(
`[${this.getAgentName()}] 后续模型响应: ${response.content.substring(0, 100)}...`
)
// 7. 再次检查是否有工具调用
toolCalls = this.extractToolCalls(response.content)
finalResponse = response.content
}
// 8. 添加最终回复到历史
this.conversationHistory.push({
role: "assistant",
content: finalResponse,
})
return finalResponse
}
/**
* 提取工具调用(从模型响应中)
*/
private extractToolCalls(content: string): ToolCall[] {
const toolCalls: ToolCall[] = []
// 匹配 JSON 格式的工具调用
const jsonMatches = content.match(/```json\n([\s\S]*?)\n```/g)
if (jsonMatches) {
jsonMatches.forEach((match) => {
try {
const json = match.replace(/```json\n/g, "").replace(/\n```/g, "")
const parsed = JSON.parse(json)
if (parsed.type === "tool_call") {
toolCalls.push({
name: parsed.tool_name,
arguments: parsed.arguments,
})
}
} catch (error) {
this.logger.warn(`[${this.getAgentName()}] 无法解析 JSON: ${match}`)
}
})
}
return toolCalls
}
/**
* 执行工具(由子类实现)
*/
protected abstract executeTool(
toolName: string,
arguments: Record<string, any>
): Promise<any>
/**
* 获取 Agent 名称
*/
protected abstract getAgentName(): string
}
工具定义&设计
工具定义核心是基于约定式的配置体,来提供给大模型。
这些工具可以是mcp,可以是function call,在工具中增加type即可扩展。
// src/tools/travel-tools.ts
/**
* 差旅工具定义
* 这些工具会被注入到 Agent 的提示词中
*/
export const TRAVEL_TOOLS = [
{
name: "search_flights",
description: "搜索机票,返回可用的航班列表",
parameters: {
type: "object",
properties: {
from: {
type: "string",
description: "出发城市(如:北京、上海)",
},
to: {
type: "string",
description: "目的城市",
},
date: {
type: "string",
description: "出发日期(格式:YYYY-MM-DD)",
},
return_date: {
type: "string",
description: "返回日期(可选,格式:YYYY-MM-DD)",
},
},
required: ["from", "to", "date"],
},
},
{
name: "search_hotels",
description: "搜索酒店,返回可用的酒店列表",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "目的城市",
},
check_in: {
type: "string",
description: "入住日期(格式:YYYY-MM-DD)",
},
check_out: {
type: "string",
description: "退房日期(格式:YYYY-MM-DD)",
},
max_price: {
type: "number",
description: "最高价格(可选,单位:元)",
},
},
required: ["city", "check_in", "check_out"],
},
},
{
name: "book_trip",
description: "预订机票和酒店,返回订单号",
parameters: {
type: "object",
properties: {
flight_id: {
type: "string",
description: "航班 ID",
},
hotel_id: {
type: "string",
description: "酒店 ID",
},
passengers: {
type: "number",
description: "乘客人数",
},
},
required: ["flight_id", "hotel_id"],
},
},
{
name: "get_trip_details",
description: "获取已预订差旅的详细信息",
parameters: {
type: "object",
properties: {
trip_id: {
type: "string",
description: "订单号",
},
},
required: ["trip_id"],
},
},
{
name: "cancel_trip",
description: "取消已预订的差旅",
parameters: {
type: "object",
properties: {
trip_id: {
type: "string",
description: "订单号",
},
reason: {
type: "string",
description: "取消原因(可选)",
},
},
required: ["trip_id"],
},
},
]
export const CALENDAR_TOOLS = [
{
name: "add_calendar_event",
description: "添加日历事件",
parameters: {
type: "object",
properties: {
title: {
type: "string",
description: "事件标题",
},
start_date: {
type: "string",
description: "开始时间(格式:YYYY-MM-DD HH:mm)",
},
end_date: {
type: "string",
description: "结束时间(格式:YYYY-MM-DD HH:mm)",
},
description: {
type: "string",
description: "事件描述",
},
},
required: ["title", "start_date", "end_date"],
},
},
{
name: "get_calendar_events",
description: "查询特定日期的日历事件",
parameters: {
type: "object",
properties: {
date: {
type: "string",
description: "查询日期(格式:YYYY-MM-DD)",
},
},
required: ["date"],
},
},
]
export const PAYMENT_TOOLS = [
{
name: "process_payment",
description: "处理支付请求",
parameters: {
type: "object",
properties: {
order_id: {
type: "string",
description: "订单号",
},
amount: {
type: "number",
description: "金额(单位:元)",
},
payment_method: {
type: "string",
enum: ["credit_card", "debit_card", "wechat", "alipay"],
description: "支付方式",
},
},
required: ["order_id", "amount", "payment_method"],
},
},
]
export const ALERT_TOOLS = [
{
name: "send_notification",
description: "发送通知给用户",
parameters: {
type: "object",
properties: {
title: {
type: "string",
description: "通知标题",
},
content: {
type: "string",
description: "通知内容",
},
channels: {
type: "array",
items: { type: "string", enum: ["email", "sms", "app"] },
description: "通知渠道",
},
},
required: ["title", "content", "channels"],
},
},
]
MCP设计
Agent基于多个Mcp能力的提供从而实现更垂直的领域能力。
因此Mcp也可以单独设计出来。
// src/mcp/types.ts
/**
* MCP 工具定义
*/
export interface MCPTool {
name: string
description: string
inputSchema: {
type: "object"
properties: Record<string, any>
required: string[]
}
}
/**
* MCP 资源定义
*/
export interface MCPResource {
uri: string
name: string
description: string
mimeType: string
contents: string
}
/**
* MCP 提示词定义
*/
export interface MCPPrompt {
name: string
description: string
arguments?: Array<{
name: string
description: string
required?: boolean
}>
}
/**
* MCP 工具调用请求
*/
export interface MCPToolCallRequest {
toolName: string
arguments: Record<string, any>
}
/**
* MCP 工具执行结果
*/
export interface MCPToolResult {
success: boolean
data?: any
error?: string
}
/**
* MCP 服务器接口
*/
export interface IMCPServer {
// 获取服务器信息
getServerInfo(): Promise<{
name: string
version: string
capabilities: string[]
}>
// 列出所有可用工具
listTools(): Promise<MCPTool[]>
// 执行工具
callTool(request: MCPToolCallRequest): Promise<MCPToolResult>
// 列出所有可用资源
listResources(): Promise<MCPResource[]>
// 获取资源内容
getResource(uri: string): Promise<MCPResource>
// 列出所有可用提示词
listPrompts(): Promise<MCPPrompt[]>
// 获取提示词内容
getPrompt(name: string, arguments?: Record<string, string>): Promise<string>
}
有了Agent、Mcp,本质上完整的一次自然语言对话 -> 反馈的系统流转图就很清晰了。
基于这套框架来扩展即可。
一次完整对话到反馈的时序图大概是这样:
用户 主Agent 子Agent MCP服务器 LLM模型 数据库
│ │ │ │ │ │
│ 用户请求: │ │ │ │ │
│ "帮我订一张 │ │ │ │ │
│ 明天北京到 │ │ │ │ │
│ 上海的机票 │ │ │ │ │
│ 和酒店" │ │ │ │ │
│──────────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ 1. 初始化对话 │ │ │ │
│ │ 构建系统提示词 │ │ │ │
│ │────────────────────────────────────>│ │ │
│ │ │ │ │ │
│ │ 2. 请求可用工具列表│ │ │ │
│ │──────────────────────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ 3. 返回工具列表 │ │ │ │
│ │<──────────────────────────────────────────────────────│ │
│ │ (search_flights, search_hotels, │ │ │
│ │ book_trip, etc.) │ │ │
│ │ │ │ │ │
│ │ 4. 获取提示词模板 │ │ │ │
│ │──────────────────────────────────────>│ │ │
│ │ │ │ │ │
│ │ 5. 返回提示词 │ │ │ │
│ │<──────────────────────────────────────│ │ │
│ │ (booking_recommendation等) │ │ │
│ │ │ │ │ │
│ │ 6. 构建系统消息 │ │ │ │
│ │ (系统提示词+工具定义+提示词) │ │ │
│ │ users消息="用户请求内容" │ │ │
│ │ │ │ │ │
│ │ 7. 调用 LLM │ │ │ │
│ │──────────────────────────────────────────────────────>│ │
│ │ │ │ │ 分析意图 │
│ │ │ │ │ (BOOK_TRIP) │
│ │ │ │ │ 提取参数 │
│ │ │ │ │ (from, to,date)│
│ │ │ │ │ 生成工具调用 │
│ │ │ │ │ │
│ │ 8. LLM 响应 │ │ │ │
│ │<──────────────────────────────────────────────────────│ │
│ │ { │ │ │
│ │ "type": "tool_call", │ │ │
│ │ "tool_name": "search_flights", │ │ │
│ │ "arguments": { │ │ │
│ │ "from": "北京", │ │ │
│ │ "to": "上海", │ │ │
│ │ "date": "明天" │ │ │
│ │ } │ │ │
│ │ } │ │ │
│ │ │ │ │ │
│ │ 9. 检测到工具调用, │ │ │ │
│ │ 路由到子Agent │ │ │ │
│ │────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ 10. 子Agent │ │ │
│ │ │ 处理工具调用 │ │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ 11. Travel MCP │ │ │
│ │ │ 执行 │ │ │
│ │ │ search_flights│ │ │
│ │ │ │ 查询数据库 │ │
│ │ │ │────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ 返回机票列表 │ │
│ │ │ │<────────────────────────────────│
│ │ │ │ │ │
│ │ 12. 返回工具结果 │ │ │ │
│ │<──────────────────── │ │ │
│ │ [ │ │ │ │ │ { │ │ │ │ │ "id": "CA123", │ │ │ │ │ "airline": "国航", │ │ │ │ │ "departure": "10:00", │ │ │ │ │ "price": 1200 │ │ │ │ │ }, │ │ │ │ │ ... │ │ │ │ │ ] │ │ │
│ │ │ │ │ │
│ │ 13. 添加工具结果 │ │ │ │
│ │ 到对话历史 │ │ │ │
│ │ 再次调用 LLM │ │ │ │
│ │──────────────────────────────────────────────────────>│ │
│ │ │ │ │ 分析机票 │
│ │ │ │ │ 生成下一个工具│
│ │ │ │ │ 调用: │
│ │ │ │ │ search_hotels │
│ │ │ │ │ │
│ │ 14. LLM 响应(第2次)│ │ │ │
│ │<──────────────────────────────────────────────────────│ │
│ │ { │ │ │
│ │ "type": "tool_call", │ │ │
│ │ "tool_name": "search_hotels", │ │ │
│ │ "arguments": { │ │ │
│ │ "city": "上海", │ │ │
│ │ "check_in": "明天", │ │ │
│ │ "check_out": "后天" │ │ │
│ │ } │ │ │
│ │ } │ │ │
│ │ │ │ │ │
│ │ 15. 再次路由到子Agent│ │ │ │
│ │────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ 16. 执行 │ │ │
│ │ │ search_hotels│ │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ 查询酒店 │
│ │ │ │────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ 返回酒店列表 │ │
│ │ │ │<────────────────────────────────│
│ │ │ │ │ │
│ │ 17. 返回酒店结果 │ │ │ │
│ │<──────────────────── │ │ │
│ │ │ │ │ │
│ │ 18. 再次调用 LLM │ │ │ │
│ │ (决定下一步) │ │ │ │
│ │──────────────────────────────────────────────────────>│ │
│ │ │ │ │ 分析酒店 │
│ │ │ │ │ 推荐最佳套餐 │
│ │ │ │ │ 生成工具调用: │
│ │ │ │ │ book_trip │
│ │ │ │ │ │
│ │ 19. LLM 响应(第3次)│ │ │ │
│ │<──────────────────────────────────────────────────────│ │
│ │ { │ │ │
│ │ "type": "tool_call", │ │ │
│ │ "tool_name": "book_trip", │ │ │
│ │ "arguments": { │ │ │
│ │ "flight_id": "CA123", │ │ │
│ │ "hotel_id": "SH001" │ │ │
│ │ } │ │ │
│ │ } │ │ │
│ │ │ │ │ │
│ │ 20. 路由到子Agent │ │ │ │
│ │ (预订差旅) │ │ │ │
│ │────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ 21. 执行book_trip│ │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ 创建订单 │
│ │ │ │────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ 返回订单号 │ │
│ │ │ │<────────────────────────────────│
│ │ │ │ │ │
│ │ 22. 返回预订结果 │ │ │ │
│ │<──────────────────── │ │ │
│ │ { │ │ │
│ │ "trip_id": "TRIP_001", │ │ │
│ │ "status": "confirmed", │ │ │
│ │ "total_cost": 3000 │ │ │
│ │ } │ │ │
│ │ │ │ │ │
│ │ 23. 调用Calendar MCP│ │ │ │
│ │ 添加日程 │ │ │ │
│ │────────────────────────────────────────────────────────>│ │
│ │ │ │ │ 添加日历事件 │
│ │ │ │────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ 返回事件ID │ │
│ │ │ │<────────────────────────────────│
│ │ │ │ │ │
│ │ 24. 调用Payment MCP│ │ │ │
│ │ 处理支付 │ │ │ │
│ │────────────────────────────────────────────────────────>│ │
│ │ │ │ │ 创建支付单 │
│ │ │ │────────────────────────────────>│
│ │ │ │ │ │
│ │ │ │ 返回交易ID │ │
│ │ │ │<────────────────────────────────│
│ │ │ │ │ │
│ │ 25. 调用Alert MCP │ │ │ │
│ │ 发送通知 │ │ │ │
│ │────────────────────────────────────────────────────────>│ │
│ │ │ │ │ 记录通知 │
│ │ │ │────────────────────────────────>│
│ │ │ │ │ │
│ │ 26. 最后调用 LLM │ │ │ │
│ │ 生成友好回复 │ │ │ │
│ │──────────────────────────────────────────────────────>│ │
│ │ │ │ │ 总结整个过程 │
│ │ │ │ │ 生成用户友好 │
│ │ │ │ │ 的文字回复 │
│ │ │ │ │ │
│ │ 27. LLM 最终响应 │ │ │ │
│ │<──────────────────────────────────────────────────────│ │
│ │ "好的,已为您预订了从北京 │ │ │
│ │ 到上海的差旅。您的订单号是 │ │ │
│ │ TRIP_001,总费用3000元。 │ │ │
│ │ 已添加到您的日程,并发送
本质上一句话总结:对话发起后,主Agent构建基础提示词进行首轮行为分析后,然后按需注入子Agent来递归/循环完成一轮对话。
结尾
如上就非常简单直观的结合代码,讲解了现在LLM大模型应用的核心架构和角色拆解。
希望对大家有所帮助。