從"嘴炮王者"到"實(shí)干專家":基于MCP、Function Calling和A2A打造AI萬(wàn)能助手
當(dāng)你對(duì)單純的大模型說(shuō)"關(guān)掉客廳的燈",它只會(huì)彬彬有禮地回應(yīng):"好的,已為您關(guān)閉客廳的燈"——然后燈還亮著。這像極了那些只會(huì)說(shuō)"改天一起吃飯"卻從不約具體時(shí)間的朋友。
但當(dāng)大模型配上了AI Agent,情況就大不相同了。同樣的指令,它不再只是動(dòng)動(dòng)嘴皮子,而是真的起身走到開(kāi)關(guān)前,"啪"一聲讓客廳陷入黑暗——這才是真正的"言出必行"。
這場(chǎng)從"嘴強(qiáng)王者"到"實(shí)干專家"的蛻變,背后藏著三大秘密武器:MCP、Function Calling和A2A。它們就像是Agent的"社交三部曲",教會(huì)了AI如何與外界打交道:
- MCP協(xié)議:Agent的"工具使用說(shuō)明書",讓AI知道哪里有工具、怎么用
- Function Calling:Agent的"大腦指揮系統(tǒng)",讓AI知道什么時(shí)候該用什么工具
- A2A協(xié)議:Agent的"同事協(xié)作指南",讓AI們能夠組團(tuán)打怪
圖片
今天就讓我們用Java揭開(kāi)這三項(xiàng)技術(shù)的神秘面紗,看看它們是如何讓AI從"思想的巨人"變成"行動(dòng)的達(dá)人"的。
一、AI Agent:給大模型裝上"手腳"
什么是AI Agent?簡(jiǎn)單說(shuō),它就是能自主感知環(huán)境、做規(guī)劃、真干活的智能系統(tǒng)。如果把大模型比作Agent的"大腦",那Agent就是大模型的"身體"。
這個(gè)比喻很形象:
- 只有大腦沒(méi)有身體:就像個(gè)足不出戶的宅男學(xué)霸,滿腹經(jīng)綸卻連燈泡都不會(huì)換
- 只有身體沒(méi)有大腦:就像個(gè)機(jī)械重復(fù)的打工人,只會(huì)按流程辦事,遇到突發(fā)狀況就懵圈
AI Agent與傳統(tǒng)自動(dòng)化程序的本質(zhì)區(qū)別,就在于它有了"隨機(jī)應(yīng)變"的能力。傳統(tǒng)的SOP就像是嚴(yán)格的食譜,必須按部就班;而Agent則像經(jīng)驗(yàn)豐富的大廚,看著冰箱里有什么就能做出一桌好菜。
一個(gè)完整的Agent系統(tǒng),除了核心的"大腦"(LLM)外,還需要三大配件:
- 感知模塊:"眼睛和耳朵",通過(guò)API、搜索引擎等獲取外界信息
- 工具集:"手和腳",能調(diào)用各種函數(shù)、API來(lái)改變現(xiàn)實(shí)世界
- 記憶模塊:"日記本",記錄過(guò)往經(jīng)驗(yàn),避免在同一個(gè)坑里摔倒兩次
二、MCP協(xié)議:Agent的"工具租賃店"
為了理解MCP(模型上下文協(xié)議),我們用Java開(kāi)一家特殊的"工具租賃店"——?jiǎng)?chuàng)建一個(gè)能記錄所有交易過(guò)程的天氣查詢Agent。
2.1 開(kāi)店實(shí)錄:用Java搭建天氣查詢Agent
Maven配置就像置辦開(kāi)店的家當(dāng):
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>創(chuàng)建MCP Server就像準(zhǔn)備店里的主打商品:
// 天氣查詢工具 - 我們的主打商品
publicclass WeatherMcpServer {
privatestaticfinal ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) {
System.out.println("?? Java MCP工具店開(kāi)業(yè)大吉!");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out))) {
String line;
while ((line = reader.readLine()) != null) {
log("?? MCP客戶發(fā)來(lái)消息: " + line);
JsonRpcMessage request = mapper.readValue(line, JsonRpcMessage.class);
JsonRpcMessage response = handleRequest(request);
if (response != null) {
String responseJson = mapper.writeValueAsString(response);
log("?? MCP店鋪回復(fù): " + responseJson);
writer.println(responseJson);
writer.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("?? 今日營(yíng)業(yè)結(jié)束");
}
// 處理各種MCP請(qǐng)求
private static JsonRpcMessage handleRequest(JsonRpcMessage request) {
String method = request.getMethod();
Object id = request.getId();
switch (method) {
case"initialize":
return handleInitialize(id); // 握手打招呼
case"tools/list":
return handleToolsList(id); // 展示商品目錄
case"tools/call":
return handleToolsCall(request.getParams(), id); // 實(shí)際交易
default:
return createErrorResponse(id, "抱歉,這個(gè)服務(wù)我們暫時(shí)沒(méi)有");
}
}
// 核心商品:天氣查詢
private static JsonRpcMessage handleToolsCall(Object params, Object id) {
Map<String, Object> paramsMap = (Map<String, Object>) params;
String toolName = (String) paramsMap.get("name");
Map<String, Object> arguments = (Map<String, Object>) paramsMap.get("arguments");
if ("獲取天氣信息".equals(toolName)) {
String city = (String) arguments.get("city");
String weatherResult = getForecast(city); // 調(diào)用核心業(yè)務(wù)邏輯
// 包裝響應(yīng)結(jié)果
Map<String, Object> result = new HashMap<>();
result.put("content", Arrays.asList(Map.of("type", "text", "text", weatherResult)));
result.put("isError", false);
result.put("structuredContent", Map.of("result", weatherResult));
JsonRpcMessage response = new JsonRpcMessage();
response.setId(id);
response.setResult(result);
return response;
}
return createErrorResponse(id, "工具不存在");
}
// 核心業(yè)務(wù)邏輯:天氣查詢
private static String getForecast(String city) {
// 這里只是模擬,真實(shí)場(chǎng)景會(huì)調(diào)用天氣API
return city + "明天有大暴雨!記得帶傘哦!?";
}
}我們還準(zhǔn)備了一個(gè)"監(jiān)控?cái)z像頭"(日志記錄器),記錄店里的所有交易對(duì)話:
// 交易記錄員
public class McpLogger {
public static void logCommunication(String direction, String message) {
try (PrintWriter writer = new PrintWriter(new FileWriter("mcp_io.log", true))) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
writer.printf("[%s] %s: %s\n", timestamp, direction, message);
} catch (IOException e) {
e.printStackTrace();
}
}
}配置客戶端就像培訓(xùn)店員,告訴它我們店里有什么商品、怎么使用。
驗(yàn)證效果時(shí),當(dāng)顧客詢問(wèn)"杭州明天的天氣如何",我們的Java Agent店員熟練地調(diào)用getForecast工具,準(zhǔn)確給出了答案。

查看交易記錄(mcp_io.log),整個(gè)過(guò)程清晰可見(jiàn):
== 1. 握手階段 ==
客戶:"你好,我是CodeMate,版本25.2.200"
店鋪:"你好,我是Java天氣查詢工具店,版本1.0.0"
== 2. 商品目錄展示 ==
客戶:"你們店里有什么工具?"
店鋪:"我們有『獲取天氣信息』,需要提供城市名"
== 3. 實(shí)際交易 ==
客戶:"我要用『獲取天氣信息』,參數(shù)是{'city':'杭州'}"
店鋪:"交易完成,結(jié)果是『杭州明天有大暴雨!記得帶傘哦!?』"2.2 MCP的本質(zhì):標(biāo)準(zhǔn)化工具調(diào)用
把這段對(duì)話畫成時(shí)序圖,你會(huì)發(fā)現(xiàn)MCP就像是一套標(biāo)準(zhǔn)化的時(shí)序流程:
圖片
MCP的核心價(jià)值就是讓AI Agent能夠用統(tǒng)一的語(yǔ)言發(fā)現(xiàn)、注冊(cè)、調(diào)用外部工具。但這里有個(gè)關(guān)鍵問(wèn)題:大模型怎么知道什么時(shí)候該來(lái)"租工具"、該租哪個(gè)工具呢?
三、Function Calling:Agent的"使用說(shuō)明書"
當(dāng)用戶問(wèn)"杭州明天天氣如何"時(shí),大模型為什么能準(zhǔn)確選擇getForecast這個(gè)工具?這就涉及到Function Calling機(jī)制——本質(zhì)上是通過(guò)"精心設(shè)計(jì)的提示詞"來(lái)教大模型做事。
在Java中,我們這樣模擬Function Calling:
// 聰明的Java Agent - 知道什么時(shí)候該用什么工具
publicclass SmartJavaAgent {
private McpClient mcpClient;
private List<String> availableTools = new ArrayList<>();
public void initialize() {
try {
mcpClient.startServer();
mcpClient.initialize();
// 模擬工具發(fā)現(xiàn)和注冊(cè) - 就像給Agent裝備工具包
availableTools.add("獲取天氣信息");
availableTools.add("查詢新聞");
availableTools.add("計(jì)算數(shù)學(xué)題");
System.out.println("??? Agent已裝備工具: " + availableTools);
} catch (Exception e) {
System.out.println("? Agent初始化失敗: " + e.getMessage());
}
}
// 處理用戶請(qǐng)求的核心邏輯
public String processUserRequest(String userMessage) {
System.out.println("?? 用戶說(shuō): " + userMessage);
// 模擬大模型的決策過(guò)程 - 就像大腦在思考
if (userMessage.contains("天氣")) {
return handleWeatherRequest(userMessage);
} elseif (userMessage.contains("新聞")) {
return handleNewsRequest(userMessage);
} elseif (userMessage.matches(".*\\d+.*")) {
return handleMathRequest(userMessage);
} else {
return"?? 抱歉,我暫時(shí)無(wú)法處理這個(gè)請(qǐng)求。";
}
}
// 處理天氣請(qǐng)求 - 知道該調(diào)用天氣工具
private String handleWeatherRequest(String userMessage) {
String city = extractCityFromMessage(userMessage);
if (city != null) {
try {
// 調(diào)用MCP工具 - 就像伸手拿工具
String weatherInfo = mcpClient.callWeatherTool(city);
return String.format("??? 已為您查詢%s的天氣:%s", city, weatherInfo);
} catch (Exception e) {
return"? 天氣查詢失敗: " + e.getMessage();
}
} else {
return"??? 請(qǐng)告訴我您想查詢哪個(gè)城市的天氣?";
}
}
// 智能提取城市名稱 - 模擬NLP理解
private String extractCityFromMessage(String message) {
// 簡(jiǎn)單的規(guī)則匹配,實(shí)際應(yīng)該用更復(fù)雜的NLP處理
Map<String, String> cityMapping = Map.of(
"杭州", "杭州", "北京", "北京", "上海", "上海",
"廣州", "廣州", "深圳", "深圳", "成都", "成都"
);
return cityMapping.entrySet().stream()
.filter(entry -> message.contains(entry.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
}
}這就是Function Calling的Java實(shí)現(xiàn)真相:通過(guò)if-else規(guī)則模擬大模型的工具選擇邏輯,實(shí)際生產(chǎn)中會(huì)用更復(fù)雜的NLP模型。
四、上下文工程:Agent的"記憶魔法"
通過(guò)對(duì)智能Agent的分析,我們發(fā)現(xiàn)一個(gè)驚人真相:Agent的本質(zhì)就是一套精心設(shè)計(jì)的上下文管理。
大模型本質(zhì)上是"金魚(yú)腦"——沒(méi)有長(zhǎng)期記憶,每次對(duì)話都是全新的開(kāi)始。你提供的上下文就是它這次對(duì)話的全部世界。
圖片
在Java中,我們這樣管理上下文:
// 上下文管理器 - 給金魚(yú)腦準(zhǔn)備記憶水晶
publicclass ContextManager {
private List<String> conversationHistory = new ArrayList<>();
private Map<String, Object> knowledgeBase = new HashMap<>();
privatefinalint MAX_CONTEXT_LENGTH = 4000; // 模擬上下文窗口限制
public void addToContext(String role, String content) {
String entry = String.format("[%s] %s", role, content);
conversationHistory.add(entry);
// 防止上下文過(guò)長(zhǎng) - 就像整理書包
if (getCurrentContextLength() > MAX_CONTEXT_LENGTH) {
compressContext();
}
}
// 上下文壓縮 - 扔掉不重要的記憶
private void compressContext() {
System.out.println("?? 上下文過(guò)長(zhǎng),開(kāi)始?jí)嚎s...");
// 簡(jiǎn)單的壓縮策略:保留最近的和最重要的對(duì)話
if (conversationHistory.size() > 10) {
conversationHistory = conversationHistory.subList(
conversationHistory.size() - 8, conversationHistory.size());
}
}
public String getFullContext() {
return String.join("\n", conversationHistory);
}
private int getCurrentContextLength() {
return getFullContext().length();
}
}構(gòu)建完整的Agent功能,就像給金魚(yú)準(zhǔn)備一個(gè)精致的魚(yú)缸:
- System prompt:魚(yú)缸的基本環(huán)境設(shè)置
- User message:每次投喂的食物
- Docs:魚(yú)缸里的裝飾物和水草
- Message history:魚(yú)游過(guò)的軌跡
把這些元素精心組合起來(lái)的過(guò)程,就是上下文工程。它比單純的提示詞工程更加復(fù)雜和系統(tǒng)。
現(xiàn)在你應(yīng)該明白,為什么我說(shuō)MCP是"模型無(wú)關(guān)"的協(xié)議了。MCP只是為模型的上下文服務(wù)的通信規(guī)范,它既不直接跟模型聊天,也不關(guān)心你用的是什么模型。
五、A2A協(xié)議:Agent的"協(xié)作指南"
Agent的核心是上下文,但把所有工具說(shuō)明都塞進(jìn)上下文里,就像往書包里塞太多書——遲早要撐破。
即使沒(méi)撐破,過(guò)長(zhǎng)的上下文也會(huì)導(dǎo)致"上下文腐化"問(wèn)題——信息太多,重點(diǎn)都被淹沒(méi)了。這跟我們?nèi)祟惖奶幘骋荒R粯樱合⑻啵炊裁炊加洸蛔 ?/span>
解決這個(gè)問(wèn)題的一個(gè)重要思路是多Agent架構(gòu)——讓專業(yè)的Agent做專業(yè)的事,避免一個(gè)人扛下所有。
多個(gè)Agent之間的協(xié)作,就需要A2A協(xié)議來(lái)規(guī)范,就像公司里需要制定同事間的協(xié)作流程。
圖片
在Java中,我們這樣實(shí)現(xiàn)多Agent協(xié)作:
// Agent名片 - 就像員工工牌
publicclass AgentCard {
private String name;
private String version;
private String endpoint;
private List<String> capabilities;
private Map<String, String> authentication;
public AgentCard(String name, String version, String endpoint) {
this.name = name;
this.version = version;
this.endpoint = endpoint;
this.capabilities = new ArrayList<>();
this.authentication = new HashMap<>();
}
// 添加到知名位置,供其他Agent發(fā)現(xiàn)
public void publishToWellKnown() {
String wellKnownPath = "/.well-known/agent.json";
try {
String cardJson = new ObjectMapper().writeValueAsString(this);
Files.write(Paths.get(wellKnownPath), cardJson.getBytes());
System.out.println("?? Agent名片已發(fā)布: " + wellKnownPath);
} catch (IOException e) {
System.out.println("? 發(fā)布名片失敗: " + e.getMessage());
}
}
}
// 任務(wù)管理器 - 處理多Agent協(xié)作
publicclass TaskManager {
private Map<String, AgentCard> registeredAgents = new HashMap<>();
public void registerAgent(AgentCard agentCard) {
registeredAgents.put(agentCard.getName(), agentCard);
System.out.println("?? 注冊(cè)新Agent: " + agentCard.getName());
}
// 分配任務(wù)給最合適的Agent
public String assignTask(String taskDescription) {
Optional<AgentCard> suitableAgent = findSuitableAgent(taskDescription);
if (suitableAgent.isPresent()) {
AgentCard agent = suitableAgent.get();
return String.format("?? 任務(wù)已分配給 %s: %s", agent.getName(), taskDescription);
} else {
return"? 沒(méi)有找到合適的Agent處理此任務(wù)";
}
}
private Optional<AgentCard> findSuitableAgent(String task) {
return registeredAgents.values().stream()
.filter(agent -> agent.getCapabilities().stream()
.anyMatch(capability -> task.toLowerCase().contains(capability.toLowerCase())))
.findFirst();
}
}A2A協(xié)議定義了一套完整的協(xié)作體系:
概念 | Java實(shí)現(xiàn) | 現(xiàn)實(shí)類比 |
Agent Card | AgentCard類 | 員工工牌 |
A2A Server | TaskManager | 部門經(jīng)理 |
A2A Client | 其他Agent | 協(xié)作同事 |
Task | 任務(wù)字符串 | 工作任務(wù)單 |
A2A的工作流程就像現(xiàn)代企業(yè)的協(xié)作方式:
- 發(fā)現(xiàn)人才:查看工牌了解對(duì)方能做什么
- 分配任務(wù):發(fā)送明確的工作請(qǐng)求
- 過(guò)程跟進(jìn):通過(guò)流式更新掌握進(jìn)度
- 必要溝通:任務(wù)需要時(shí)提供額外輸入
- 驗(yàn)收交付:任務(wù)完成或失敗時(shí)收到通知
六、演示:Java Agent如何工作
// 演示完整的Agent工作流程
publicclass AgentDemo {
public static void main(String[] args) {
System.out.println("?? 啟動(dòng)Java AI Agent演示...\n");
// 1. 初始化智能Agent
SmartJavaAgent agent = new SmartJavaAgent();
agent.initialize();
// 2. 初始化多Agent協(xié)作系統(tǒng)
TaskManager taskManager = new TaskManager();
// 注冊(cè)各種專業(yè)Agent
taskManager.registerAgent(new AgentCard("天氣專家", "1.0", "/weather-agent"));
taskManager.registerAgent(new AgentCard("新聞播報(bào)員", "1.0", "/news-agent"));
taskManager.registerAgent(new AgentCard("數(shù)學(xué)教授", "1.0", "/math-agent"));
// 3. 測(cè)試各種用戶請(qǐng)求
String[] testRequests = {
"杭州明天的天氣如何?",
"今天有什么重大新聞?",
"計(jì)算一下125的平方根是多少",
"我想訂一張去北京的機(jī)票"
};
for (String request : testRequests) {
System.out.println("\n" + "=".repeat(50));
System.out.println("?? 用戶請(qǐng)求: " + request);
// 先嘗試多Agent協(xié)作
String assignment = taskManager.assignTask(request);
System.out.println("?? 多Agent協(xié)作: " + assignment);
// 單個(gè)Agent處理
String response = agent.processUserRequest(request);
System.out.println("?? Agent回復(fù): " + response);
}
agent.shutdown();
System.out.println("\n?? 演示結(jié)束!");
}
}運(yùn)行結(jié)果示例:
?? 啟動(dòng)Java AI Agent演示...
??? Agent已裝備工具: [獲取天氣信息, 查詢新聞, 計(jì)算數(shù)學(xué)題]
?? 注冊(cè)新Agent: 天氣專家
?? 注冊(cè)新Agent: 新聞播報(bào)員
?? 注冊(cè)新Agent: 數(shù)學(xué)教授
==================================================
?? 用戶請(qǐng)求: 杭州明天的天氣如何?
?? 多Agent協(xié)作: ?? 任務(wù)已分配給 天氣專家: 杭州明天的天氣如何?
?? Agent回復(fù): ??? 已為您查詢杭州的天氣:杭州明天有大暴雨!記得帶傘哦!?
==================================================
?? 用戶請(qǐng)求: 計(jì)算一下125的平方根是多少
?? 多Agent協(xié)作: ?? 任務(wù)已分配給 數(shù)學(xué)教授: 計(jì)算一下125的平方根是多少
?? Agent回復(fù): ?? 計(jì)算結(jié)果:125的平方根是≈11.18七、總結(jié)
單純的大模型,是個(gè)只會(huì)動(dòng)嘴的"學(xué)霸";配上Agent的大模型,成了能文能武的"萬(wàn)能管家"。
這場(chǎng)進(jìn)化的核心在于上下文工程,背后依靠三大技術(shù)支柱:MCP、Function Calling和A2A。它們不是相互替代的競(jìng)爭(zhēng)對(duì)手,而是互補(bǔ)協(xié)作的鐵三角。
- MCP讓Agent知道"外面有什么工具可用"
- Function Calling讓Agent知道"什么時(shí)候該用什么工具"
- A2A讓Agent們知道"怎么合作完成大項(xiàng)目"
通過(guò)Java實(shí)現(xiàn),我們看到了:
- ??? MCP服務(wù)器像工具租賃店,提供標(biāo)準(zhǔn)化服務(wù)
- ?? 智能Agent像聰明管家,知道何時(shí)使用何種工具
- ?? 多Agent系統(tǒng)像高效團(tuán)隊(duì),各司其職又緊密協(xié)作
技術(shù)棧總結(jié):
- Jackson用于JSON處理
- 標(biāo)準(zhǔn)I/O進(jìn)行進(jìn)程通信
- 簡(jiǎn)單的規(guī)則引擎模擬AI決策
- 多線程支持并發(fā)Agent協(xié)作
- 文件系統(tǒng)用于持久化配置


































