LLM与工具的结合:从基础到Agent
在大型语言模型(LLM)的时代,我们常常看到LLM被用来处理文本生成、问答等任务,但当我们为其配备工具时,它们的潜力才真正被释放。想象一下,LLM不仅仅是聊天机器人,还能执行实际操作,比如读取文件、写入数据、列出目录或执行命令。这些工具包括read、write、listDir和exec等基本功能,将LLM与这些工具结合,就形成了Agent——一个能“干活”的智能系统。
这种结合带来的甜头显而易见:LLM不再局限于静态响应,而是能根据用户需求调用工具完成复杂任务。例如,在一个开发环境中,Agent可以通过工具查询数据库、处理文件或调用外部API。这让LLM从“会说话”变成“会做事”,极大提升了实用性。然而,现有的工具集成方式往往局限于单一进程或语言,难以扩展到更广阔的场景。这时候,我们需要一种更先进的机制来放大这种优势。
MCP的核心在于约定:所有工具按统一协议开发和暴露,从而让LLM在处理繁杂任务时更高效。无论是本地子进程、跨语言进程还是远程进程,MCP都能统一管理。通信部分包括stdio(标准输入输出)用于本地命令行,以及http用于远程调用。这使得LLM能执行更强大的任务,而开发者只需遵循规范提供工具和资源。
MCP协议的核心特点:跨进程调用与规范集成
MCP最大的特点是支持跨进程调用工具。这解决了传统工具集成的痛点:在复杂环境中,工具可能分布在不同进程、语言或部门,甚至远程服务器上。通过MCP,我们可以用Node的child-process创建子进程,调用Java或Rust工具,或通过网络访问远程资源。这样的规范让提供工具变得简单高效。
例如,MCP协议包括工具结果(tool result)和工具消息上下文(ToolMessage Context),确保LLM能正确处理工具输出。开源SDK如@modelcontextprotocol/sdk提供了实现基础,帮助开发者快速上手。为什么需要MCP配置?因为像Cursor或Trae这样的工具可以转变为支持MCP的Agent客户端,通过读取mcp.json文件加载需要的工具。这让集成变得即插即用。
在编写满足MCP规范的工具时,我们采用Client/Server架构。在现有工具基础上添加MCP规范,然后用服务器容器(如@modelcontextprotocol/mcp/server)托管。关键步骤包括registerTool(注册工具)和connect transport(连接传输方式)。这确保工具能在MCP生态中运行。
MCP的三者关系与工作流程
理解MCP需要把握三者关系:
- MCP Hosts:如Cursor或Vite这样的Agent宿主,负责运行整体系统。
- MCP Clients:遵循MCP规范的一系列工具,提供具体功能。
- MCP Server:工具运行的服务器容器,管理工具的注册和执行。
工作流程如下:
- MCP Hosts通过配置文件初始化。
- 发送初始化请求,获取MCP Server提供的工具列表和详情。
- Hosts接收用户prompt任务。
- 检索MCP配置文件,确定通信方式。
- Client与工具通信,MCP Server执行并返回结果。
这种流程让MCP工具可拔插,直接集成到Agent程序中。开发者可以轻松扩展系统,而无需重写底层逻辑。
MCP开发流程:从创建到连接
开发MCP工具的流程清晰明了。首先,创建MCP Server实例:
JavaScript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
然后,注册工具、资源或提示模板。工具包括名字、描述和执行函数。资源可以是文档或数据,提供给LLM作为上下文。提示模板则规范LLM的输入输出。
例如,注册一个查询用户信息的工具:
JavaScript
import { z } from 'zod';
server.registerTool('query-user', {
description: '查询数据库中的用户信息,输入用户ID,返回该用户的详细信息(姓名、邮箱、角色)。',
inputSchema: {
userId: z.string().describe("用户ID,例如:001, 002, 003")
}
}, async ({ userId }) => {
const user = database.users[userId];
if (!user) {
return {
content: {
type: 'text',
text: `用户ID ${userId} 不存在。可用的ID: 001, 002, 003`
}
}
} else {
return {
content: [
{
type: 'text',
text: `用户信息:\n- ID: ${user.id}\n- 姓名: ${user.name}\n- 邮箱: ${user.email}\n- 角色: ${user.role}`
}
]
}
}
});
这里使用了Zod库定义输入 schema,确保输入验证。数据库是模拟的内存对象,实际中可替换为真实数据库。
同样,注册资源:
JavaScript
server.registerResource('使用指南', 'docs://guide', {
description: 'MCP Server 使用文档',
mimeType: 'text/plain',
}, async () => {
return {
contents: [
{
uri: 'docs://guide',
mimeType: 'text/plain',
text: `MCP Server 使用指南
功能:提供用户查询等工具。
使用:在 Cursor 等 MCP Client 中通过自然语言对话,Cursor 会自动调用相应工具。
`,
}
]
}
});
这提供了上下文,帮助LLM更好地使用工具。
接下来,选择通信方式,如StdioServerTransport用于本地:
JavaScript
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const transport = new StdioServerTransport();
await server.connect(transport);
对于远程,可用HttpServerTransport。Hosts端则配置mcp.json加载这些工具。
实际代码示例:构建MCP Server和Client
以下是完整MCP Server代码示例,用于托管工具和资源:
JavaScript
// my-mcp-server.mjs
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const database = {
users: {
"001": { id: "001", name: "张三", email: "zhangsan@example.com", role: "admin" },
"002": { id: "002", name: "李四", email: "lisi@example.com", role: "user" },
"003": { id: "003", name: "王五", email: "wangwu@example.com", role: "user" },
}
};
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
server.registerTool('query-user', {
description: '查询数据库中的用户信息,输入用户ID,返回该用户的详细信息(姓名、邮箱、角色)。',
inputSchema: {
userId: z.string().describe("用户ID,例如:001, 002, 003")
}
}, async ({ userId }) => {
const user = database.users[userId];
if (!user) {
return {
content: {
type: 'text',
text: `用户ID ${userId} 不存在。可用的ID: 001, 002, 003`
}
}
} else {
return {
content: [
{
type: 'text',
text: `用户信息:\n- ID: ${user.id}\n- 姓名: ${user.name}\n- 邮箱: ${user.email}\n- 角色: ${user.role}`
}
]
}
}
});
server.registerResource('使用指南', 'docs://guide', {
description: 'MCP Server 使用文档',
mimeType: 'text/plain',
}, async () => {
return {
contents: [
{
uri: 'docs://guide',
mimeType: 'text/plain',
text: `MCP Server 使用指南
功能:提供用户查询等工具。
使用:在 Cursor 等 MCP Client 中通过自然语言对话,Cursor 会自动调用相应工具。
`,
}
]
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
这个Server通过stdio通信,适合本地跨进程调用。注意,代码中使用了内存数据库作为示例,实际应用中应考虑安全性和持久化。
现在,看Client端如何集成并使用这些工具。这里使用LangChain的MCP适配器构建Agent:
JavaScript
import 'dotenv/config';
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, ToolMessage } from '@langchain/core/messages';
import chalk from 'chalk';
const mcpClient = new MultiServerMCPClient({
mcpServers: {
'my-mcp-server': {
command: 'node',
args: ['./my-mcp-server.mjs'],
},
},
});
const model = new ChatOpenAI({
modelName: process.env.MODEL_NAME,
apiKey: process.env.OPENAI_API_KEY,
configuration: {
baseURL: process.env.OPENAI_API_BASE_URL,
}
});
const tools = await mcpClient.getTools();
const modelWithTools = model.bindTools(tools);
async function runAgentWithTools(query, maxIterations = 30) {
const messages = [new HumanMessage(query)];
for (let i = 0; i < maxIterations; i++) {
console.log(chalk.bgGreen('⏳正在等待AI思考...'));
const response = await modelWithTools.invoke(messages);
messages.push(response);
if (!response.tool_calls || response.tool_calls.length === 0) {
console.log(`\n AI 最终回复:\n ${response.content}\n`);
return response.content;
}
console.log(chalk.bgBlue(`🔍 检测到 ${response.tool_calls.length} 个工具调用`));
console.log(chalk.bgBlue(`🔍 工具调用: ${response.tool_calls.map(t => t.name).join(', ')}`));
for (const toolCall of response.tool_calls) {
const foundTool = tools.find(t => t.name === toolCall.name);
if (foundTool) {
const toolResult = await foundTool.invoke(toolCall.args);
messages.push(new ToolMessage({
content: toolResult,
tool_call_id: toolCall.id
}));
}
}
}
return messages[messages.length - 1].content;
}
const result = await runAgentWithTools("查一下用户 002 的信息");
console.log(result);
await mcpClient.close();
这个Client启动MCP Server,绑定工具到LLM模型,然后通过循环处理tool calls。示例查询“查一下用户 002 的信息”会调用query-user工具,返回用户信息。
MCP的扩展与未来展望
通过MCP,我们可以轻松集成第三方服务,将MCP视为工具的统一入口。这不仅适用于本地开发,还能扩展到分布式系统。例如,大厂的服务可以通过http暴露为MCP工具,让Agent跨部门协作。
MCP的开源性质鼓励社区贡献更多SDK和示例,推动LLM生态发展。开发者可以从简单工具入手,逐步构建复杂Agent。未来,随着更多hosts支持MCP,LLM将真正融入日常工作流。
总之,MCP协议为LLM工具集成提供了规范高效的路径。通过以上开发流程和代码示例,你可以快速上手,构建自己的Agent系统。欢迎在评论区分享你的实践经验!