本文手把手教你如何用 Ollama 部署轻量级模型 qwen2.5:0.5b-instruct,并通过 MCP(Model Context Protocol)协议让模型调用你自己开发的 API 接口,实现自然语言查询数据。
引言
大模型的能力边界往往受限于训练数据,想要让模型获取实时数据、操作外部系统,就需要让它具备"调用工具"的能力。传统做法是使用 function calling,但各模型实现方式不统一,维护成本高。而 MCP(Model Context Protocol) 提供了一套标准化的协议,让 AI 应用可以统一地接入各种外部工具和数据源。
本文将展示一个完整的本地闭环:
- 用 Ollama 运行轻量级模型 qwen2.5:0.5b-instruct
- 开发一个简单的 Python API(模拟你自己的软件接口)
- 编写 MCP Server 将 API 包装成标准工具
- 让模型通过 MCP 调用该工具,实现自然语言查询
环境准备
1. 安装 Ollama
Ollama 是一个开源的本地大模型运行框架,支持 macOS、Linux 和 Windows。
bash
# macOS/Linux
curl -fsSL https://ollama.com/install.sh | sh
# 或直接下载安装包 https://ollama.com/download
安装完成后,验证:
bash
ollama --version
2. 下载 qwen2.5:0.5b-instruct
这是阿里巴巴通义千问系列中非常轻量的指令微调模型,仅有 0.5B 参数,在普通 CPU 上也能流畅运行。
bash
ollama pull qwen2.5:0.5b-instruct
测试模型是否可用:
bash
ollama run qwen2.5:0.5b-instruct "你好,请介绍一下自己"
如果看到回复,说明模型部署成功。
开发自定义 API
为了演示,我们开发一个简单的 Flask API,提供用户数据查询功能(你可以替换成自己的业务 API)。
创建文件 my_api.py:
python
from flask import Flask, request, jsonify
app = Flask(__name__)
# 模拟数据库
users = {
1: {"name": "张三", "age": 28, "city": "北京"},
2: {"name": "李四", "age": 35, "city": "上海"},
3: {"name": "王五", "age": 22, "city": "深圳"},
}
@app.route('/api/user/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = users.get(user_id)
if user:
return jsonify(user)
else:
return jsonify({"error": "用户不存在"}), 404
@app.route('/api/search', methods=['GET'])
def search_users():
name = request.args.get('name')
result = [u for u in users.values() if name in u['name']] if name else []
return jsonify(result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
启动 API:
bash
python my_api.py
测试接口:
bash
curl http://localhost:5000/api/user/1
# 返回 {"age":28,"city":"北京","name":"张三"}
curl "http://localhost:5000/api/search?name=李"
# 返回 [{"age":35,"city":"上海","name":"李四"}]
MCP 协议简介
MCP(Model Context Protocol)是 Anthropic 推出的开源协议,旨在让 AI 模型能够安全、标准地访问外部工具和数据源。其核心概念:
- MCP Server:暴露工具(Tools)和资源(Resources)的服务端
- MCP Client:与 Server 通信,并将结果提供给 AI 模型
- 工具(Tool):可被模型调用的函数,有明确的输入输出 schema
MCP 使用 JSON-RPC 2.0 进行通信,支持多种传输方式(stdio, SSE 等)。
实现 MCP Server 包装 API
我们将使用 Python 的 mcp 官方 SDK 来编写 Server。首先安装依赖:
bash
pip install mcp flask requests
创建 mcp_server.py:
python
import asyncio
import json
import requests
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import mcp.types as types
# 创建 MCP Server 实例
server = Server("my-api-server")
# 定义工具列表
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
return [
types.Tool(
name="get_user_by_id",
description="根据用户ID查询用户信息",
inputSchema={
"type": "object",
"properties": {
"user_id": {
"type": "integer",
"description": "用户ID,例如 1,2,3"
}
},
"required": ["user_id"]
}
),
types.Tool(
name="search_users_by_name",
description="根据姓名关键词搜索用户",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "姓名关键词"
}
},
"required": ["name"]
}
)
]
# 实现工具的调用逻辑
@server.call_tool()
async def handle_call_tool(
name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
if not arguments:
arguments = {}
if name == "get_user_by_id":
user_id = arguments.get("user_id")
if not user_id:
return [types.TextContent(type="text", text="错误:缺少 user_id")]
try:
# 调用自定义 API
resp = requests.get(f"http://localhost:5000/api/user/{user_id}")
if resp.status_code == 200:
data = resp.json()
text = f"用户信息:{json.dumps(data, ensure_ascii=False)}"
else:
text = f"查询失败:{resp.status_code} - {resp.text}"
return [types.TextContent(type="text", text=text)]
except Exception as e:
return [types.TextContent(type="text", text=f"调用API出错:{str(e)}")]
elif name == "search_users_by_name":
name_keyword = arguments.get("name")
if not name_keyword:
return [types.TextContent(type="text", text="错误:缺少 name")]
try:
resp = requests.get(f"http://localhost:5000/api/search", params={"name": name_keyword})
if resp.status_code == 200:
data = resp.json()
if data:
text = f"搜索结果:{json.dumps(data, ensure_ascii=False)}"
else:
text = "未找到匹配的用户"
else:
text = f"搜索失败:{resp.status_code} - {resp.text}"
return [types.TextContent(type="text", text=text)]
except Exception as e:
return [types.TextContent(type="text", text=f"调用API出错:{str(e)}")]
else:
raise ValueError(f"未知工具: {name}")
async def main():
# 使用 stdio 传输运行 server
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="my-api-server",
server_version="1.0.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
if __name__ == "__main__":
asyncio.run(main())
这个 MCP Server 暴露了两个工具,分别对应我们 API 的两个端点。当模型需要查询用户时,会调用相应的工具,Server 会转发请求到 Flask API 并返回结果。
集成模型与 MCP
现在我们要让模型能够识别用户意图并调用这些工具。有多种方式实现:
- 使用 MCP Client 并配合模型 :例如使用
mcp-client库,将工具描述嵌入到系统提示中,让模型决定调用。 - 利用 Ollama 的 function calling 支持:qwen2.5:0.5b-instruct 不支持原生 function calling,但我们可以手动构造 prompt。
由于 qwen2.5:0.5b 没有内置 function calling,我们采用一种简单的"意图识别 + 工具调用"流程:
- 用户输入 → 模型判断是否需要调用工具 → 如果需要,让模型输出结构化的调用指令 → 解析指令,调用 MCP Server → 将结果返回给模型 → 模型生成最终回答。
我们用一个 Python 脚本整合所有环节。安装额外依赖:
bash
pip install ollama
创建 main.py:
python
import json
import subprocess
import sys
import ollama
import requests
# MCP Server 通信(这里简化:直接通过 HTTP 调用 MCP Server?不,我们上面 MCP Server 是用 stdio 的)
# 为了简化演示,我们不使用 MCP Client 库,而是直接让模型输出结构化指令,然后手动调用 API。
# 这样虽然不符合 MCP 标准流程,但能让你看到核心思想。你也可以使用 MCP 的 Python 客户端库来完整实现。
# 定义工具描述(给模型看的)
TOOLS_DESCRIPTION = """
你可以使用以下工具来帮助用户:
1. get_user_by_id: 根据用户ID查询用户信息。参数 user_id (整数)
2. search_users_by_name: 根据姓名关键词搜索用户。参数 name (字符串)
当用户询问关于用户信息时,你需要决定是否使用工具。如果需要使用,请以 JSON 格式输出你的调用,格式为:
{"tool": "工具名称", "arguments": {"参数名": 值}}
然后我会执行工具并返回结果。如果不需要工具,直接回答即可。
"""
def call_mcp_tool(tool_name, arguments):
"""模拟 MCP 调用:这里直接调用我们的 Flask API"""
if tool_name == "get_user_by_id":
user_id = arguments.get("user_id")
resp = requests.get(f"http://localhost:5000/api/user/{user_id}")
if resp.status_code == 200:
return resp.json()
else:
return {"error": resp.text}
elif tool_name == "search_users_by_name":
name = arguments.get("name")
resp = requests.get(f"http://localhost:5000/api/search", params={"name": name})
if resp.status_code == 200:
return resp.json()
else:
return {"error": resp.text}
else:
return {"error": f"未知工具: {tool_name}"}
def chat_with_model(user_input):
messages = [
{"role": "system", "content": TOOLS_DESCRIPTION},
{"role": "user", "content": user_input}
]
# 调用 Ollama 生成
response = ollama.chat(model='qwen2.5:0.5b-instruct', messages=messages)
assistant_reply = response['message']['content']
print("模型原始回复:", assistant_reply)
# 尝试解析 JSON 调用
try:
# 查找 JSON 块
import re
json_match = re.search(r'\{.*?\}', assistant_reply, re.DOTALL)
if json_match:
tool_call = json.loads(json_match.group())
if "tool" in tool_call and "arguments" in tool_call:
tool_name = tool_call["tool"]
args = tool_call["arguments"]
print(f"调用工具 {tool_name},参数 {args}")
tool_result = call_mcp_tool(tool_name, args)
# 将工具结果返回给模型生成最终答案
messages.append({"role": "assistant", "content": assistant_reply})
messages.append({"role": "user", "content": f"工具返回结果:{json.dumps(tool_result, ensure_ascii=False)},请根据这个结果回答用户问题。"})
final_response = ollama.chat(model='qwen2.5:0.5b-instruct', messages=messages)
return final_response['message']['content']
except Exception as e:
print("解析工具调用失败:", e)
return assistant_reply
if __name__ == "__main__":
print("AI 助手已启动,输入 quit 退出")
while True:
user_input = input("你: ")
if user_input.lower() == 'quit':
break
response = chat_with_model(user_input)
print("AI:", response)
这个脚本做了:
- 将工具描述写入系统提示
- 让模型输出结构化 JSON 调用(如果它认为需要)
- 解析 JSON,调用对应 API
- 将结果再次发给模型,让模型生成自然语言回答
注意:这只是一个演示级实现,生产环境建议使用官方 MCP Client 库来规范通信。
运行与测试
-
启动 Flask API:
bashpython my_api.py -
启动 MCP Server(可选,我们上面直接调用了 API,但为了演示 MCP 概念,你也可以尝试使用 MCP Client 来连接 Server,这里简化了):
bashpython mcp_server.py但我们的
main.py并未与 MCP Server 通信,而是直接调 API。如果你想体验完整的 MCP 流程,可以使用mcp命令行工具或官方 Python 客户端,这里不再展开。 -
运行主程序:
bashpython main.py -
测试对话:
vbnet你: 帮我查一下用户ID为2的信息 AI: 模型原始回复:{"tool": "get_user_by_id", "arguments": {"user_id": 2}} 调用工具 get_user_by_id,参数 {'user_id': 2} AI: 用户ID为2的用户是李四,年龄35岁,来自上海。css你: 搜索名字中包含"王"的用户 AI: 模型原始回复:{"tool": "search_users_by_name", "arguments": {"name": "王"}} 调用工具 search_users_by_name,参数 {'name': '王'} AI: 找到了用户王五,年龄22岁,在深圳。makefile你: 你好,请介绍一下自己 AI: 模型原始回复:你好!我是阿里云研发的超大规模语言模型,我叫通义千问。 AI: 你好!我是阿里云研发的超大规模语言模型,我叫通义千问。
可以看到,模型在需要查询数据时,正确地输出了工具调用指令,我们通过解析并调用真实 API 后,得到了最终回答。
总结
本文实现了一个本地大模型调用自定义 API 的完整流程:
- 使用 Ollama 快速部署 qwen2.5:0.5b-instruct 轻量模型
- 开发了一个简单的 Flask API 作为业务接口
- 介绍了 MCP 协议的基本概念,并编写了一个 MCP Server 来包装 API(虽然最终示例中未强制使用 MCP Client,但展示了 MCP Server 的写法)
- 通过 prompt engineering 让模型输出结构化指令,完成工具调用
这种架构可以轻松扩展到更多工具和更复杂的业务场景。你可以将 MCP Server 部署为独立服务,让任意支持 MCP 的客户端(如 Claude Desktop)也能调用你的私有 API,实现跨应用的智能交互。
如果你希望完整使用 MCP 协议,推荐参考官方 Python SDK 文档,实现 MCP Client 与 Server 的通信,让模型与工具的交互更加标准化。
源码仓库 :你可以在 GitHub 示例 中找到本文全部代码。
参考资料:
希望这篇文章对你有所帮助,欢迎留言交流!