在当今这个数据驱动的时代,企业要取得成功,关键在于从海量数据集中提取有价值的洞察。然而,要做到这一点,往往需要一项专业技能:编写复杂的 SQL 查询。这构成了一个瓶颈,因为许多业务用户虽然是各自领域的专家,但缺乏直接与数据库交互的技术能力。而 大模型 (LLM) 的出现,特别是像 Ollama 结合 Granite 这样的模型,正在彻底改变非技术专业人员查询和理解数据的方式。

大模型赋能:自然语言到SQL的桥梁

想象一下,您可以毫不费力地将您的自然语言问题(例如“上个季度的销售额是多少?”或“哪些员工在市场部工作?”)转化为精确的 SQL 命令,而无需编写任何代码。大模型 正在弥合这一差距,充当智能翻译器,将人类语言转换为可执行的数据库查询。这不仅实现了数据访问的民主化,还使业务用户能够独立检索所需的信息,从而加速决策并培养更敏捷、更具数据素养的环境。例如,一位市场经理无需依赖IT部门,即可通过简单的“找出过去一个月社交媒体广告点击率最高的三个活动”这样的自然语言描述,快速生成相应的SQL查询,并获得活动数据,从而及时调整营销策略。这种效率的提升,直接转化为更快的市场响应速度和更高的营销ROI。

Ollama和Granite:强大的本地化大模型解决方案

Ollama 提供了一个方便的平台,可以在本地运行 大模型,而无需依赖云服务,从而保护了数据的安全性并降低了延迟。Granite,作为IBM开发的一系列高性能大模型,专为企业级应用设计,拥有卓越的性能和效率。将 Ollama 和 Granite 结合使用,企业可以在内部构建强大的 自然语言到SQL (Text-to-SQL) 解决方案,满足特定的业务需求。本文中的示例正是利用了Ollama本地运行Granite模型,将自然语言转化成SQL语句,然后执行查询。这意味着即使在没有网络连接的情况下,用户也可以进行数据查询和分析,这对一些安全性要求高的企业或需要离线操作的场景非常重要。

实例演示:使用Ollama和Granite构建数据库查询应用

为了简化演示,文章使用两个 SQLite 数据库来探索这个概念。一个数据库包含个人的个人信息,例如他们的名字、姓氏和出生日期;第二个数据库包含特定于员工的详细信息,包括工资、职位和部门。这两个数据库通过一个通用的唯一标识符链接,这使得我们可以通过一个简单的查询来组合来自两个来源的信息。

文章中给出了创建这两个数据库的Python代码(create-db.py):

# create-db.py
import sqlite3
import os

def create_personal_data_db(db_name="personal_data.db"):
    """
    Creates an SQLite database for personal employee data and populates it.
    """
    conn = None
    try:
        conn = sqlite3.connect(db_name)
        cursor = conn.cursor()
        cursor.execute("DROP TABLE IF EXISTS employees_personal")
        cursor.execute("""
            CREATE TABLE employees_personal (
                firstName TEXT NOT NULL,
                lastName TEXT NOT NULL,
                dateOfBirth TEXT NOT NULL,
                uniqueID TEXT PRIMARY KEY
            )
        """)
        sample_data = [
            ("Alice", "Smith", "1990-05-15", "EMP001"),
            ("Bob", "Johnson", "1985-11-22", "EMP002"),
            ("Charlie", "Brown", "1992-03-01", "EMP003")
        ]
        cursor.executemany("INSERT INTO employees_personal (firstName, lastName, dateOfBirth, uniqueID) VALUES (?, ?, ?, ?)", sample_data)
        conn.commit()

    except sqlite3.Error as e:
        print(f"An SQLite error occurred: {e}")
    finally:
        if conn:
            conn.close()


def create_employment_data_db(db_name="employment_data.db"):
    """
    Creates an SQLite database for employment data and populates it.
    """
    conn = None
    try:
        conn = sqlite3.connect(db_name)
        cursor = conn.cursor()
        cursor.execute("DROP TABLE IF EXISTS employees_employment")
        cursor.execute("""
            CREATE TABLE employees_employment (
                uniqueID TEXT PRIMARY KEY,
                salary REAL NOT NULL,
                jobTitle TEXT NOT NULL,
                departmentId INTEGER NOT NULL
            )
        """)
        sample_data = [
            ("EMP001", 60000.00, "Software Engineer", 101),
            ("EMP002", 75000.00, "Project Manager", 102),
            ("EMP003", 50000.00, "HR Specialist", 103)
        ]
        cursor.executemany("INSERT INTO employees_employment (uniqueID, salary, jobTitle, departmentId) VALUES (?, ?, ?, ?)", sample_data)
        conn.commit()

    except sqlite3.Error as e:
        print(f"An SQLite error occurred: {e}")
    finally:
        if conn:
            conn.close()

if __name__ == "__main__":
    create_personal_data_db()
    create_employment_data_db()
    print("\nApplication finished.")

以及用于读取数据库信息的代码(display-db.py):

# display-db.py
import sqlite3
import os

def read_sqlite_database(db_name):
    """
    Connects to an SQLite database, lists all tables, and then retrieves
    and prints all rows from each table.
    """
    if not os.path.exists(db_name):
        print(f"Error: Database file '{db_name}' not found.")
        return

    conn = None
    try:
        conn = sqlite3.connect(db_name)
        cursor = conn.cursor()

        cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
        tables = cursor.fetchall()

        if not tables:
            print("No tables found in this database.")
            return

        for table_name_tuple in tables:
            table_name = table_name_tuple[0]

            cursor.execute(f"PRAGMA table_info({table_name});")
            columns_info = cursor.fetchall()
            column_names = [col[1] for col in columns_info]

            cursor.execute(f"SELECT * FROM {table_name}")
            rows = cursor.fetchall()

            if not rows:
                print(f"  No data in table '{table_name}'.")
            else:
                for row in rows:
                    print(f"  {row}")


    except sqlite3.Error as e:
        print(f"An SQLite error occurred: {e}")
    finally:
        if conn:
            conn.close()

if __name__ == "__main__":
    db_file_input = input("Please enter the name of the SQLite database file (e.g., personal_data.db): ")
    read_sqlite_database(db_file_input)

接下来,文章重点介绍了如何使用 大模型 生成 SQL 查询。 核心代码 ( text2sql.py ) 通过 Ollama 接口调用 Granite 模型,接收用户输入的 employee uniqueID 作为输入,并显示员工的雇佣信息以及生成的 SQL 查询。

# text2sql.py
import sqlite3
import os
import requests
import json

# Configuration for Ollama
OLLAMA_API_URL = "http://localhost:11434/api/chat"
OLLAMA_MODEL = "granite3.3:latest" # Ensure this model is pulled in your Ollama instance

# Database file names
PERSONAL_DB = "personal_data.db"
EMPLOYMENT_DB = "employment_data.db"

def get_employee_details(unique_id):
    """
    Connects to both personal and employment databases, performs a join,
    and retrieves employee details for a given uniqueID.
    Displays the SQL query being executed.
    """
    if not os.path.exists(PERSONAL_DB):
        print(f"Error: Personal data database '{PERSONAL_DB}' not found.")
        return None
    if not os.path.exists(EMPLOYMENT_DB):
        print(f"Error: Employment data database '{EMPLOYMENT_DB}' not found.")
        return None

    conn = None
    try:
        # Connect to the personal data database
        conn = sqlite3.connect(PERSONAL_DB)
        cursor = conn.cursor()

        # Attach the employment data database
        cursor.execute(f"ATTACH DATABASE '{EMPLOYMENT_DB}' AS employment_db;")

        # Perform a JOIN query
        query = f"""
            SELECT
                p.firstName,
                p.lastName,
                e.jobTitle
            FROM
                employees_personal AS p
            JOIN
                employment_db.employees_employment AS e
            ON
                p.uniqueID = e.uniqueID
            WHERE
                p.uniqueID = '{unique_id}'; -- Displaying the query with the ID for clarity
        """
        print(f"\n--- Executing SQL Query ---")
        print(query)
        print(f"---------------------------\n")
        cursor.execute(query) # Execute the query

        result = cursor.fetchone() # Fetch one matching record
        return result

    except sqlite3.Error as e:
        print(f"An SQLite error occurred during lookup: {e}")
        return None
    finally:
        if conn:
            # Detach the database before closing connection
            try:
                cursor.execute("DETACH DATABASE employment_db;")
            except sqlite3.Error as e:
                print(f"Warning: Could not detach employment_db: {e}")
            conn.close()

def send_to_ollama(prompt):
    """
    Sends a prompt to the local Ollama API and returns the LLM's response.
    """
    messages = [
        {"role": "system", "content": "You are a friendly assistant providing employee information. Respond concisely and helpfully."},
        {"role": "user", "content": prompt}
    ]

    payload = {
        "model": OLLAMA_MODEL,
        "messages": messages,
        "stream": False # We want the full response at once
    }

    headers = {"Content-Type": "application/json"}

    try:
        response = requests.post(OLLAMA_API_URL, headers=headers, data=json.dumps(payload))
        response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)
        response_data = response.json()

        if 'message' in response_data and 'content' in response_data['message']:
            return response_data['message']['content']
        else:
            return "Ollama: Unexpected response format."

    except requests.exceptions.ConnectionError:
        return "Ollama: Could not connect to the Ollama server. Please ensure it's running."
    except requests.exceptions.RequestException as e:
        return f"Ollama: An error occurred during the API call: {e}"
    except json.JSONDecodeError:
        return "Ollama: Failed to decode JSON response from Ollama."


def run_chat_app():
    """
    Runs the main chat application loop.
    """
    print(f"Welcome to the Employee Lookup Chatbot! (Using Ollama with {OLLAMA_MODEL})")
    print("Type 'exit' or 'quit' to end the session.")

    while True:
        user_input = input("\nEnter employee Unique ID (e.g., EMP001): ").strip().upper()
        if user_input in ["EXIT", "QUIT"]:
            print("Goodbye!")
            break

        employee_data = get_employee_details(user_input)

        if employee_data:
            first_name, last_name, job_title = employee_data
            llm_prompt = (
                f"I found an employee with Unique ID {user_input}. "
                f"Their first name is {first_name}, last name is {last_name}, "
                f"and their job title is {job_title}. Please tell me this information "
                f"in a friendly and concise way, confirming the ID."
            )
        else:
            llm_prompt = (
                f"I looked for an employee with Unique ID {user_input}, but I couldn't find them in the database. "
                f"Please respond to the user that the employee was not found and suggest they try another ID."
            )

        llm_response = send_to_ollama(llm_prompt)
        print(f"\nChatbot: {llm_response}")


if __name__ == "__main__":
    run_chat_app()

此代码展示了如何将用户的输入传递给 Ollama 运行的 Granite 模型,并根据返回的数据构建一个友好的回复。值得注意的是,该代码示例展示的是直接执行SQL查询,而不是让 大模型 生成SQL。 虽然这是一个简化,但它仍然演示了如何使用 LLM 来处理和呈现数据库信息。 实际应用中,可以将用户输入转换为更复杂的自然语言描述,再由 大模型 生成 SQL 查询,从而支持更灵活的查询方式。

大模型选型:Granite的优势

文章选择 Granite 作为 大模型,是因为它在处理企业级数据查询方面具有优势。 Granite 模型经过专门训练,能够理解和生成准确的 SQL 查询,这对于确保数据分析的准确性和可靠性至关重要。 此外,Granite 还具有良好的可扩展性和安全性,可以满足企业对数据处理的各种需求。 当然,在实际应用中,企业可以根据自身的需求选择其他 大模型,例如 Llama 2、GPT-4 等。

结论:LLM赋能,数据驱动的未来

尽管本文提供的步骤非常简单,但它们展示了使用 大模型 构建强大应用程序以简化企业数据库中结构化信息的使用的潜力,使其触手可及。 通过将 Ollama 和 Granite 等 大模型 结合使用,企业可以构建 自然语言到SQL 解决方案,从而使业务用户能够更自主地完成日常任务,而无需了解数据库或复杂系统的使用。这种 大模型 赋能的方式,将加速企业的数字化转型,并最终实现数据驱动的未来。例如,在金融行业,分析师可以使用自然语言查询快速获取市场数据,从而更好地评估投资风险和机会。 在医疗保健领域,医生可以使用 大模型 驱动的应用程序,从电子病历中提取关键信息,从而更快速、更准确地做出诊断。 这些应用场景都表明, 大模型 将在各个行业发挥越来越重要的作用。