掘金 人工智能 04月27日 11:22
Tiny Agents:用50行代码打造基于MCP的AI代理
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了模型上下文协议(MCP),一个用于连接大语言模型(LLM)与工具集的标准API。通过扩展推理客户端,可以轻松构建MCP客户端,将MCP服务器中的工具接入LLM推理。文章以TypeScript示例展示了如何实现这一过程,并阐述了MCP如何简化Agentic AI的开发。核心在于,现代LLM已支持函数调用,开发者只需执行工具并将结果反馈给LLM即可。最终,文章展示了如何通过MCP和简单的循环构建AI代理。

💡 MCP的核心在于其作为标准API,能够将LLM与工具集连接起来,简化了Agentic AI的开发流程。

🔑 通过扩展推理客户端,可以轻松创建MCP客户端,从而将MCP服务器中的工具集成到LLM推理中。

⚙️ 现代LLM原生支持函数调用,开发者只需定义工具的名称、描述和参数,并执行工具、反馈结果给LLM。

💻 AI代理本质上是一个简单的while循环,循环中代理与LLM交替进行工具调用和结果反馈,直到满足特定条件。

🚀 Tiny Agents展示了如何通过MCP客户端和简单的while循环,用不到50行代码构建AI代理,简化了Agentic AI的开发。

引言

过去几周,我深入研究了MCP(Model Context Protocol,模型上下文协议),试图理解它为何备受关注。我的总结是:MCP简单却强大,它是一个标准API,用于暴露可以连接到大语言模型(LLM)的工具集。

通过扩展推理客户端(Hugging Face提供了两个官方SDK:JavaScript的@huggingface/inference和Python的huggingface_hub),可以轻松将其变为MCP客户端,将MCP服务器中的工具接入LLM推理。

在实现过程中,我有了第二个发现:一旦拥有MCP客户端,AI代理本质上只是一个简单的循环。本文将通过TypeScript(JavaScript)示例,展示如何实现这一过程,介绍如何采用MCP,并探讨它如何让Agentic AI开发变得更简单。


如何运行完整示例

如果你有NodeJS(支持pnpm或npm),只需在终端运行以下命令:

bash

npx @huggingface/mcp-client

或使用pnpm:

bash

pnpx @huggingface/mcp-client

这会将我的软件包安装到一个临时文件夹并执行其命令。你将看到一个简单的代理连接到两个本地运行的MCP服务器,加载它们的工具,然后提示你进行对话。

默认MCP服务器

默认情况下,示例代理会连接以下两个MCP服务器:

注意:目前所有MCP服务器都是本地进程,但远程服务器支持即将来临。

示例输入

我们测试了以下两个输入:

    文件操作:
    “写一首关于Hugging Face社区的俳句,并将其写入桌面上的hf.txt文件。”网页浏览:
    “在Brave Search上搜索HF推理提供商,并打开前三个结果。”

默认模型和提供商

示例代理默认使用:

这些参数可通过环境变量配置:

javascript

const agent = new Agent({    provider: process.env.PROVIDER ?? "nebius",    model: process.env.MODEL_ID ?? "Qwen/Qwen2.5-72B-Instruct",    apiKey: process.env.HF_TOKEN,    servers: SERVERS,});

代码存储位置

Tiny Agent的代码位于huggingface.js mono-repo的mcp-client子包中,这是Hugging Face所有JavaScript库的GitHub仓库。

代码地址:
github.com/huggingface…

代码使用了现代JavaScript特性(尤其是异步生成器),这让异步事件(如LLM响应)的实现更加简单。如果你不熟悉这些特性,可以咨询LLM以获取更多信息。

基础:LLM的原生工具调用支持

本文的核心在于,当前主流LLM(无论是闭源还是开源)都已训练支持函数调用(即工具使用)。工具通过以下方式定义:

这是一种不透明的函数接口表示,LLM并不关心函数的具体实现。例如:

javascript

const weatherTool = {    type: "function",    function: {        name: "get_weather",        description: "Get current temperature for a given location.",        parameters: {            type: "object",            properties: {                location: {                    type: "string",                    description: "City and country e.g. Bogotá, Colombia",                },            },        },    },};

参考文档:OpenAI的函数调用文档(链接)。是的,OpenAI几乎为整个社区定义了LLM标准

推理引擎允许在调用LLM时传入工具列表,LLM可以选择调用零个、一个或多个工具。开发者需要执行这些工具并将结果反馈给LLM以继续生成。

在后端,工具以特殊格式的chat_template传递给模型,然后通过模型特定的特殊标记从响应中解析为工具调用。


在InferenceClient上实现MCP客户端

了解了LLM中的工具概念后,我们来实现MCP客户端。

官方文档(modelcontextprotocol.io/quickstart/…)写得很清晰。你只需将其中提到的Anthropic客户端SDK替换为任何兼容OpenAI的客户端SDK即可。我们使用Hugging Face的InferenceClient作为推理客户端。

完整代码:McpClient.ts(链接 (#))。

McpClient类结构

McpClient类包含:

javascript

export class McpClient {    protected client: InferenceClient;    protected provider: string;    protected model: string;    private clients: Map<ToolName, Client> = new Map();    public readonly availableTools: ChatCompletionInputTool[] = [];    constructor({ provider, model, apiKey }) {        this.client = new InferenceClient(apiKey);        this.provider = provider;        this.model = model;    }}

连接MCP服务器

MCP官方SDK(@modelcontextprotocol/sdk/client)提供了Client类,其中的listTools()方法可以列出服务器的工具:

javascript

async addMcpServer(server: StdioServerParameters): Promise<void> {    const transport = new StdioClientTransport({        ...server,        env: { ...server.env, PATH: process.env.PATH ?? "" },    });    const mcp = new Client({ name: "@huggingface/mcp-client", version: packageVersion });    await mcp.connect(transport);    const toolsResult = await mcp.listTools();    debug(        "Connected to server with tools:",        toolsResult.tools.map(({ name }) => name)    );    for (const tool of toolsResult.tools) {        this.clients.set(tool.name, mcp);    }    this.availableTools.push(        ...toolsResult.tools.map((tool) => ({            type: "function",            function: {                name: tool.name,                description: tool.description,                parameters: tool.inputSchema,            },        }))    );}

StdioServerParameters是MCP SDK提供的接口,用于启动本地进程(目前MCP服务器均为本地进程)。


如何使用工具

使用工具很简单,只需将this.availableTools传递给LLM的chatCompletion接口,与消息数组一起提交:

javascript

const stream = this.client.chatCompletionStream({    provider: this.provider,    model: this.model,    messages,    tools: this.availableTools,    tool_choice: "auto",});

tool_choice: "auto"允许LLM生成零个、一个或多个工具调用。

在解析或流式处理输出时,LLM会生成工具调用(包括函数名称和JSON编码的参数)。开发者需要执行这些工具,MCP客户端SDK提供了client.callTool()方法:

javascript

const toolName = toolCall.function.name;const toolArgs = JSON.parse(toolCall.function.arguments);const toolMessage: ChatCompletionInputMessageTool = {    role: "tool",    tool_call_id: toolCall.id,    content: "",    name: toolName,};const client = this.clients.get(toolName);if (client) {    const result = await client.callTool({ name: toolName, arguments: toolArgs });    toolMessage.content = result.content[0].text;} else {    toolMessage.content = `Error: No session found for tool: ${toolName}`;}

最后,将工具执行结果添加到消息数组中并反馈给LLM。


AI代理的本质:一个简单的循环

有了支持工具的推理客户端后,AI代理本质上只是其上的一个while循环。

具体来说,AI代理包括:

完整代码:Agent.ts(链接 (#))。

Agent类

Agent类继承自McpClient:

javascript

export class Agent extends McpClient {    private readonly servers: StdioServerParameters[];    protected messages: ChatCompletionInputMessage[];    constructor({ provider, model, apiKey, servers, prompt }) {        super({ provider, model, apiKey });        this.servers = servers;        this.messages = [            {                role: "system",                content: prompt ?? DEFAULT_SYSTEM_PROMPT,            },        ];    }}

默认使用一个简单的系统提示词(参考GPT-4.1提示指南)。OpenAI建议开发者直接使用tools字段传递工具,而无需手动将工具描述注入提示词中。

加载工具

加载工具只需并行连接到所需的MCP服务器:

javascript

async loadTools(): Promise<void> {    await Promise.all(this.servers.map((s) => this.addMcpServer(s)));}

此外,我们添加了两个额外的控制流工具:

javascript

const taskCompletionTool: ChatCompletionInputTool = {    type: "function",    function: {        name: "task_complete",        description: "Call this tool when the task given by the user is complete",        parameters: { type: "object", properties: {} },    },};const askQuestionTool: ChatCompletionInputTool = {    type: "function",    function: {        name: "ask_question",        description: "Ask a question to the user to get more info required to solve or clarify their problem.",        parameters: { type: "object", properties: {} },    },};const exitLoopTools = [taskCompletionTool, askQuestionTool];

完整的While循环

以下是代理的核心while循环:

javascript

let numOfTurns = 0;let nextTurnShouldCallTools = true;while (true) {    try {        yield* this.processSingleTurnWithTools(this.messages, {            exitLoopTools,            exitIfFirstChunkNoTool: numOfTurns > 0 && nextTurnShouldCallTools,            abortSignal: opts.abortSignal,        });    } catch (err) {        if (err instanceof Error && err.message === "AbortError") {            return;        }        throw err;    }    numOfTurns++;    const currentLast = this.messages.at(-1)!;    if (        currentLast.role === "tool" &&        currentLast.name &&        exitLoopTools.map((t) => t.function.name).includes(currentLast.name)    ) {        return;    }    if (currentLast.role !== "tool" && numOfTurns > MAX_NUM_TURNS) {        return;    }    if (currentLast.role !== "tool" && nextTurnShouldCallTools) {        return;    }    if (currentLast.role === "tool") {        nextTurnShouldCallTools = false;    } else {        nextTurnShouldCallTools = true;    }}

循环的核心逻辑是:代理与LLM交替进行工具调用和结果反馈,直到LLM连续生成两个非工具消息,或者调用控制流工具(task_complete或ask_question)。


下一步

有了运行的MCP客户端和简单的代理构建方法后,可以探索更多可能性:

项目完全开源,欢迎提交PR和贡献!


结论

通过MCP和现代LLM的工具调用支持,构建AI代理变得异常简单。Tiny Agents展示了一个不到50行代码的实现,结合MCP客户端和一个简单的while循环即可完成。未来,MCP的扩展(如远程服务器支持)将进一步简化Agentic AI的开发流程。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

MCP AI代理 LLM 工具调用 Agentic AI
相关文章