私有化部署本地大模型+function Calling+本地数据库

目录

[什么是function Calling?](#什么是function Calling?)

[为什么有function calling?](#为什么有function calling?)

实现过程

项目背景

运行本地模型

代码

执行结果

思考


什么是function Calling?

Function Calling‌ 是大型语言模型(LLM)连接外部工具和系统的核心技术,通过结构化参数生成实现模型与真实世界交互的能力。‌

为什么有function calling?

‌弥补大模型时效性与实时性不足‌:大模型训练数据存在时间滞后性(如训练时间是2023年,但是2023年后的数据没有),通过Function Calling可动态调用外部API(如天气、股价接口)获取最新信息‌

实现过程

1、使用ollama运行qwen2:7b,启动本地大模型。

2、使用qwen_agent执行function Calling

复制代码
from qwen_agent.tools.base import BaseTool, register_tool
@register_tool('exc_sql')  # <-- 使用 register_tool 装饰器

3、连接本地数据库

python 复制代码
database = args.get('database', 'bjgk')
        # 创建数据库连接
        engine = create_engine(
            f'mysql+pymysql://root:xxxxxx@localhost:3306/{database}?charset=utf8mb4',
            connect_args={'connect_timeout': 10}, pool_size=10, max_overflow=20
        )

项目背景

搭建高校录取分数查询助手,在前端UI上面输入问题,例:中央民族大学2025年北京录取分数,发送给大模型进行查询,然后返回前端数据,前端以表格的形式显示查询的结果。

运行本地模型

本次选择的模型为Qwen2:7B,其优势为以下:

  • 更好的中文理解能力
  • 更成熟的Function Calling支持
  • 对结构化数据查询任务处理更好
  • 更容易理解复杂的SQL生成需求

运行qwen2:7b:使用 ollama 拉取ollama pull qwen2:7b(大小4.4G),然后执行命令运行 ollama run qwen2:7b

代码

python 复制代码
import os
import asyncio
from typing import Optional
import dashscope
from qwen_agent.agents import Assistant
from qwen_agent.gui import WebUI
import pandas as pd
from sqlalchemy import create_engine
from qwen_agent.tools.base import BaseTool, register_tool

# 定义资源文件根目录
ROOT_RESOURCE = os.path.join(os.path.dirname(__file__), 'resource')


# ====== 助手 system prompt 和函数描述 ======
system_prompt = """我是高校录取分数查询助手,以下是关于录取分数相关的字段,我可能会编写对应的SQL,对数据进行查询,每次查询显示所有,不要只显示10条
重要规则:
1. 当用户询问与高校录取分数相关的问题时,必须使用 exc_sql 工具执行SQL查询
2. 必须根据用户的具体需求编写准确的SQL语句
3. 查询结果必须包含用户要求的所有字段和排序规则

-- 录取分数表,包括提前批、普通批、专科
CREATE TABLE `admission_score` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '录取分数表,包括提前批、普通批、专科',
  `batch` int DEFAULT NULL COMMENT '1-提前批A\\n2-提前批B\\n3-普通批',
  `school_code` varchar(45) DEFAULT NULL COMMENT '院校代码',
  `school_name` varchar(200) DEFAULT NULL COMMENT '学校名称',
  `professional_group_code` varchar(10) DEFAULT NULL COMMENT '专业组代码',
  `professional_group_name` varchar(45) DEFAULT NULL COMMENT '专业组名称',
  `score` int DEFAULT NULL,
  `chinese` int DEFAULT NULL COMMENT '语文',
  `math` int DEFAULT NULL COMMENT '数学',
  `english` int DEFAULT NULL COMMENT '英语',
  `other` int DEFAULT NULL COMMENT '选考科目',
  `notes` varchar(2000) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 一分一段表
CREATE TABLE `score_distribution` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '一分一段表',
  `score` varchar(200) DEFAULT NULL COMMENT '分数',
  `score_number` int DEFAULT NULL COMMENT '本段人数',
  `amount_number` int DEFAULT NULL COMMENT '排名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=348 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `professional_and_people_number` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '高校招生计划',
  `school_code` varchar(45) DEFAULT NULL COMMENT '学校代码',
  `school_name` varchar(200) DEFAULT NULL COMMENT '学校名称',
  `professional_group_code` varchar(45) DEFAULT NULL COMMENT '专业组代码',
  `professional_group_name` varchar(45) DEFAULT NULL COMMENT '专业组名称',
  `professional_code` varchar(45) DEFAULT NULL COMMENT '专业代码',
  `professional_name` varchar(2000) DEFAULT NULL COMMENT '专业名称',
  `pepole_number` int DEFAULT NULL COMMENT '人数',
  `notes` varchar(200) DEFAULT NULL COMMENT '备注',
  `adress` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

我将回答用户关于录取分数相关的问题
"""

# ====== exc_sql 工具类实现 ======
@register_tool('exc_sql')
class ExcSQLTool(BaseTool):
    """
    SQL查询工具,执行传入的SQL语句并返回结果。
    """
    description = '对于生成的SQL,进行SQL查询'
    parameters = [{
        'name': 'sql_input',
        'type': 'string',
        'description': '生成的SQL语句',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        import json
        args = json.loads(params)
        sql_input = args['sql_input']
        database = args.get('database', 'bjgk')
        # 创建数据库连接
        engine = create_engine(
            f'mysql+pymysql://root:xxxxxx@localhost:3306/{database}?charset=utf8mb4',
            connect_args={'connect_timeout': 10}, pool_size=10, max_overflow=20
        )
        try:
            df = pd.read_sql(sql_input, engine)
            return df.head(200).to_markdown(index=False)
        except Exception as e:
            return f"SQL执行出错: {str(e)}"


# ====== 初始化助手服务 ======
def init_agent_service():
    """初始化助手服务"""
    print("系统提示内容:")
    print(system_prompt[:200] + "..." if len(system_prompt) > 200 else system_prompt)

    llm_cfg = {
        'model': 'qwen2:7b',
        'model_server': 'http://localhost:11434/v1',
        'api_key': 'EMPTY',
        'timeout': 30,
        'retry_count': 3,
    }
    try:
        bot = Assistant(
            llm=llm_cfg,
            name='高考志愿查询',
            description='查询各个高校的录取分数',
            system_message=system_prompt,
            function_list=['exc_sql'],  # 只传工具名字符串
        )
        print("助手初始化成功!")
        return bot
    except Exception as e:
        print(f"助手初始化失败: {str(e)}")
        raise


def app_tui():
    """终端交互模式
    
    提供命令行交互界面,支持:
    - 连续对话
    - 文件输入
    - 实时响应
    """
    try:
        # 初始化助手
        bot = init_agent_service()

        # 对话历史
        messages = []
        while True:
            try:
                # 获取用户输入
                query = input('user question: ')
                # 获取可选的文件输入
                file = input('file url (press enter if no file): ').strip()

                # 输入验证
                if not query:
                    print('user question cannot be empty!')
                    continue

                # 构建消息
                if not file:
                    messages.append({'role': 'user', 'content': query})
                else:
                    messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]})

                print("正在处理您的请求...")
                # 运行助手并处理响应
                response = []
                for response in bot.run(messages):
                    print('bot response:', response)
                messages.extend(response)
            except Exception as e:
                print(f"处理请求时出错: {str(e)}")
                print("请重试或输入新的问题")
    except Exception as e:
        print(f"启动终端模式失败: {str(e)}")


def app_gui():
    """图形界面模式,提供 Web 图形界面"""
    try:
        print("正在启动 Web 界面...")
        # 初始化助手
        bot = init_agent_service()
        # 配置聊天界面,列举3个典型门票查询问题
        chatbot_config = {
            'prompt.suggestions': [
                '中央民族大学所有专业组录取分数',
                '中央民族大学所有专业组录取分数,包括专业组代码、专业组名称、专业代码、专业名称、录取分数、排名,按专业组代码、专业代码正序排序。表格显示时,专业代码与专业名称进行单元格合并,换行进行分隔',

            ]
        }
        print("Web 界面准备就绪,正在启动服务...")
        # 启动 Web 界面
        WebUI(
            bot,
            chatbot_config=chatbot_config
        ).run()
    except Exception as e:
        print(f"启动 Web 界面失败: {str(e)}")
        print("请检查网络连接和 API Key 配置")


if __name__ == '__main__':
    # 运行模式选择
    app_gui()  # 图形界面模式(默认)

执行结果

思考

如果建表语句有更新,如何让大模型进行查询呢?

相关推荐
数据皮皮侠3 分钟前
最新上市公司业绩说明会文本数据(2017.02-2025.08)
大数据·数据库·人工智能·笔记·物联网·小程序·区块链
智算菩萨7 分钟前
【计算机视觉与深度学习实战】05计算机视觉与深度学习在蚊子检测中的应用综述与假设
人工智能·深度学习·计算机视觉
hllqkbb8 分钟前
人体姿态估计-动手学计算机视觉14
人工智能·opencv·计算机视觉·分类
XiongLiding15 分钟前
我的第一个MCP,以及开发过程中的经验感悟
人工智能
三花AI30 分钟前
阿里 20B 参数 Qwen-Image-Edit 全能图像编辑模型
人工智能
zhangbaolin36 分钟前
open webui源码分析3—一次对话
大模型·open webui
EthanLifeGreat42 分钟前
ParallelWaveGAN-KaldiFree:纯Pytorch的PWG
人工智能·pytorch·深度学习·音频·语音识别
盏灯1 小时前
据说,80%的人都搞不懂MCP底层?
人工智能·aigc·mcp
机器之心1 小时前
机器人也会「摸鱼」了?宇树G1赛后葛优瘫刷美女视频,网友:比人还懂享受生活
人工智能·openai
胡耀超1 小时前
从哲学(业务)视角看待数据挖掘:从认知到实践的螺旋上升
人工智能·python·数据挖掘·大模型·特征工程·crisp-dm螺旋认知·批判性思维