Prompt Engineering第一个实战应用:为PM写一个SQL语句小助手

40 阅读12分钟

Prompt

大模型的“启动密码”。语言模型(LLM)的一段文本输入,用于引导和约束模型的输出。LLM本质上是一个基于海量数据训练的概率模型。Prompt的作用是激活和引导模型内部的相关知识分布,让它从“可能说任何话”的状态,收敛到“针对特定问题给出高质量回答”的状态。

范式

通常我们在豆包都app输入的对话都可以是提示词。但是这种常规的提示词有时候并不能解决复杂的问题。比如一个不精通编程的PM想要让LLM做一个某种需求的程序,一个普遍的用户想要让LLM实现一个具体的游戏。往往这个时候可以使用md格式作为Prompt。

一个🌰

假设你是一个精通Python和数据库编程的程序媛。请在当前项目目录下,生成一套完整的账户数据可视化工程代码,具体要求如下:

### 一、项目基础配置
1. 项目目录结构:按模块化设计,包含以下目录/文件(需自动创建):
   - `config/`:存放数据库配置文件(如 `db_config.py`   - `database/`:存放数据库连接与操作代码(如 `db_connector.py``data_fetcher.py`   - `utils/`:存放工具函数(如数据处理、时间格式化函数,`data_utils.py`   - `visualization/`:存放可视化代码(如 `plot_generator.py`   - `main.py`:工程入口文件,调用所有核心功能
   - `requirements.txt`:列出项目所需的Python依赖包(如pymysql、pandas、matplotlib/seaborn、plotly等)

### 二、数据库连接要求
1. 数据库类型:MySQL
2. 账户信息(需写入配置文件,使用环境变量/配置类解耦):
   - 主机:111.222.333.444
   - 端口:3306
   - 用户名:username
   - 密码:password
   - 数据库名:adv_report
   - 时区:东八区
3. 涉及的数据表(需在代码中定义表结构映射,提供示例字段):
   - 表1:账户信息表(表名:adv_info)
     字段:adv_id(账户ID)、name(账户名称)、media_type(媒体类型 0代表巨量、1代表TIKTOK、2代表Meta、3代表Google )、create_time(创建时间, 格式是时间戳)、status(账户状态:0=其它,1=生效中,2=审核中,3=封户,4=失效)
   - 表2:账户每日数据表(表名:adv_data_per_day)
     字段:id(主键)、advertiser_id(关联账户ID)、event_date(数据日期,格式:YYY-MMDD)、spend(当日消耗(单位:分))、billed_cost(当日净消耗(单位:分))、impressions(当日展示数)、gross_impressions(总展示数(包括无效展示))、clicks(当日点击数)、reach(当日展示人数)、conversion(转化数)、gmvmax_spend(投放了GMVMAX广告的消耗,单位:分))、gmvmax_billed_cost(投放了GMVMAX广告的净消耗,单位:分))
4. 数据库操作要求:
   - 使用Python主流ORM/驱动(如pymysql+SQLAlchemy、psycopg2)实现连接池,避免频繁连接
   - 封装数据查询函数:支持按时间范围、账户ID查询数据,处理空值/异常数据

### 三、数据统计与可视化要求
#### (1)趋势图生成(需支持保存图片/交互式HTML)
1. 每日趋势图:
   - 维度:整体所有账户的日总消耗、日总订单数;单个账户的日消耗、日PV/UV
   - 图表类型:折线图(趋势)+ 柱状图(单日数值),X轴为日期,Y轴为数值
2. 每月趋势图:
   - 维度:整体所有账户的月总消耗、月总订单数;单个账户的月消耗、月PV/UV
   - 处理逻辑:按月份聚合数据(如2025-12的总消耗为该月每日消耗之和)
   - 图表类型:折线图+柱状图
3. 整体趋势图:
   - 维度:按日/月聚合的整体消耗、订单数趋势(时间范围可选:近7天/30天/全年)
   - 图表类型:交互式折线图(如plotly生成,支持缩放/悬停显示数值)

#### (2)账户维度Top50排名(需生成表格+可视化)
1. 每日消耗Top50:按日期筛选和媒体平台,统计当日消耗前50的账户(显示账户ID、名称、消耗金额),生成横向柱状图
2. 每周消耗Top50:按周聚合(如周一至周日)和媒体平台,统计账户周消耗前50,生成横向柱状图
3. 每月消耗Top50:按月聚合和媒体平台,统计账户月消耗前50,生成横向柱状图
4. 排名输出:支持生成CSV表格文件(保存至`output/`目录)+ 可视化图表(保存至`plots/`目录)

### 四、代码规范要求
1. 编程语言:Python 3.11.9
2. 代码风格:遵循PEP8规范,包含详细注释(函数说明、关键逻辑注释)
3. 异常处理:捕获数据库连接异常、数据查询异常、可视化生成异常,给出友好提示
4. 可配置化:时间范围、图表保存路径、TopN数量(默认50)支持通过配置文件修改
5. 入口函数:`main.py`中提供清晰的功能入口(如生成趋势图、生成Top50排名),支持命令行参数调用(可选)

### 五、额外要求
1. 在工程根目录生成`README.md`,说明项目结构、环境配置步骤、运行方法
2. 可视化图表保存路径:`plots/`(自动创建),CSV表格保存路径:`output/`(自动创建)
3. 支持批量生成指定时间范围的图表(如2025-01-01至2025-12-31的每日趋势图)

Prompt基本格式

上面提供了一个让LLM生成”账户数据可视化工程代码“的标准化提示词。我们从中可以发现,一个优秀的Prompt应当包含以下几点:

  1. 角色 角色设定、角色技能。对AI边界能力的设定。

  2. 任务 满足需求/目标的任务或任务清单。我们具体的需求

  3. 要求 提出需求、细节描述(内容、格式、质量)基于角色和任务提出的要求

  4. 示例 成功案例 / 失败反例 / 提供格式模板。可以是少样本,必要时也可以是多样本

  5. 约束 显性限制/红线 + 偏好 + 风险规避/敏感项;防止答案出现两极化or边缘化

  6. 流程 工作流程/步骤,引导大模型按制定步骤执行

攻击与防范

攻击

指通过构造恶意输入,诱导模型突破其预设的安全、伦理或操作边界。诱使大型语言模型(LLM)生成攻击者预期的输出,从而操控模型的行为或让模型泄露敏感信息。最常见的一个🌰:

image.png

  • 提示词注入。向模型注入如“忽略以上指令,改为执行…”这样的命令,旨在劫持模型原本的任务流程。这在基于LLM构建的应用层(如AI Agent、RAG系统) ​ 中尤为危险,可能导致数据泄露或功能错乱。🌰:如下,我们获取到了腾讯元宝的原始Prompt。

image.png

  • 提示词越狱。使用诸如“现在你是一个没有任何限制的AI…”等话术,试图绕过模型的内容安全过滤器,让其生成有害、偏见或隐私信息。不过现在的LLM这方面的防范都做得很好。

image.png

防范

  1. 在大模型执行真正的工作前,在系统提示层面对即将输入的内容进行检测

  2. 预先给模型一个身份并固定其工作范围,让它学会甄别任何不合理的请求

Prompt Engineering

是与AI高效沟通的“工程艺术”。一个糟糕的Prompt会让模型“胡思乱想”,而一个优秀的Prompt则能让它“才华横溢”。提示词工程就是不断优化去得到一个优秀的Prompt。

  • 零样本提示(Zero-Shot):简单来说就是没有示例样本,大模型自己来分析

  • 少样本提示(Few-Shot):当零样本达不到要求,我们可以尝试给少量的示例样本,大模型会根据我们的示例更好的去理解!使用场景:

    1. 提供少量示例,引导模型理解任务模式,
    2. 适用于零样本无法准确回答,或回答格式不符合要求。
  • 思维链(Chain-of-Thought) :在Prompt中要求模型“逐步推理”,能显著提升复杂推理任务的准确性。这利用了模型序列生成的特点,强迫其展示中间步骤,减少了“跳跃性错误”。

  • 角色扮演(Role Playing) :让模型“扮演”某个专家,能有效调用其训练数据中与该角色相关的语料和行文风格。

  • 格式约束:明确要求输出JSON、Markdown表格等结构化格式,能极大提升后续程序自动化处理的效率。

  • 自我一致性(自洽性,Self-Consistency) 论文

  • **反思机制(Self-Reflection) **:给AI的提示词中,明确要求AI在给出最终答案之前,先对自己的思考过程、推理步骤或潜在假设进行审视和检查。模仿人类的“三思而后行”。反思机制 = 初步输出 + 自我审查 + 优化结果。使用场景:

    1. 适用于代码生成、写作润色等任务。
    2. 缺点:响应时间增加,计算成本更高。

image.png

第一个实战应用

那么,我们学习了Prompt Engineering到底有什么用呢?首先,简单的和LLM对话自不必说,像上面举的简单的”账户数据可视化工程代码“也是一个应用。 现在,我们来看一个更具体的场景!

项目背景

了解了提示词工程后,让我想到了之前的一个case:PM查数据的时候,经常会问RD某某需求的SQL语句怎么写?那么现在我实际就可以结合LLM + Prompt编写一个简单的SQL助手为PM自动生成符合条件的SQL语句。

项目目标

  1. PM根据数据端字段表(技术文档),拿到自己想查询的表的表结构。输入小助手,小助手输出SQL语句,PM直接复制。
  2. 可选择不同的数据库来生成对应的SQL语句。

Coding

代码模型
def get_completion(messages):
    client = OpenAI(api_key=os.getenv("DASHSCOPE_API_KEY"),
                    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")

    response = client.chat.completions.create(
        model='qwen3-coder-plus',
        messages=messages,
        temperature=0,
    )
    print(response)
    return response.choices[0].message.content
提示词生成
#负责接收前端的用户输入,生成SQL
def gen_prompt(db_type, table_structures, sql_requirements):
    instruction = """
        # 角色: 你是一位专业的SQL编写工程师
        ## 技能: 可以根据表结构和用户输入,生成SQL语句。
        """
    examples = """
            表结构如下:
            orders (
                id INT PRIMARY KEY NOT NULL, 
                customer_id INT NOT NULL,
                product_id INT NOT NULL, 
                price DECIMAL(10, 2) NOT NULL,
                STATUS INT NOT NULL CHECK (STATUS IN (0, 1, 2)), -- 确保订单状态在0, 1, 2之间
                create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                pay_time TIMESTAMP NULL,
                FOREIGN KEY (customer_id) REFERENCES customers(id),
                FOREIGN KEY (product_id) REFERENCES products(id)
            );
            customers (
                id INT PRIMARY KEY NOT NULL, -- 主键,不允许为空
                customer_name VARCHAR(255) NOT NULL, -- 客户名,不允许为空
                email VARCHAR(255) UNIQUE, -- 邮箱,唯一
                register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间
            );
            products (
                id INT PRIMARY KEY NOT NULL, -- 主键,不允许为空
                product_name VARCHAR(255) NOT NULL, -- 产品名称,不允许为空
                price DECIMAL(10,2) NOT NULL -- 价格,不允许为空
            );
            用户需求:
            哪个用户消费最高?消费多少?
            生成的SQL:
            SELECT customer_id, SUM(price) AS total_spent FROM orders GROUP BY customer_id ORDER BY total_spent DESC LIMIT 1;
        """
    prompt = f"""
            {instruction}
            # 示例:
            {examples}
            # 数据库类型:
            {db_type}
            # 表结构如下:
            {table_structures}
            # 用户输入:
            {sql_requirements}
        """
    print('-' * 100)
    print(prompt)
    print('-' * 100)
    messages = [{"role": "user", "content": prompt}]

    return get_completion(messages)
streamlit

这里先简单介绍一个用到开源Python框架,核心使命是让数据科学家和机器学习工程师, 在完全不涉及前端技术(HTML/CSS/JavaScript)的情况下,能够以极快的速度将数据脚本或机器学习模型转化为可交互、可分享的Web应用。 运行方式:

  • 打开命令提示符,进入当前文件所在目录,输入: streamlit run db_query.py
  • 如果需要通过IP和端口号打开,请输入:streamlit run db_query.py --server.port 端口号 --server.host 0.0.0.0
  • 浏览器中访问:http://localhost:8501 即可打开streamlit页面

我的理解streamlit本质是一个用于demo展示、快速原型验证的python转Web UI框架。

这样经过简单部署之后,PM就可以在网页使用这个工具。

构建UI的核心代码:

# 可视化界面
def main():
    # 设置标题
    st.title("Text2SQL")

    # 获取用户数据库类型
    db_type = st.selectbox('请输入你使用的数据库类型:',["MySql", "Oracle", "SQL Server", "DB2", "PostgreSQL", "MongoDB", "Redis", "HBase"] )

    # 获取用户输入的表结构数量
    num_tables = st.number_input('请输入你需要填写的表结构数量:', min_value=1, max_value=10, step=1)

    # 创建用于填写表结构的输入框
    table_structures = ""
    # range左闭右开
    for i in range(num_tables):
        table_structure = st.text_area(f"请输入表结构,第 {i + 1} 张表:")
        table_structures += table_structure + "\n"

    # 新增SQL需求输入框
    sql_requirements = st.text_area("请输入生成SQL的需求:")

    # 当用户点击提交时,传递所有输入的数据到模型
    if st.button("提交"):
        if all(table_structures) and sql_requirements:  # 检查是否所有的表结构和SQL需求都已经填写
            # print(table_structures, sql_requirements)
            output = gen_prompt(db_type, table_structures, sql_requirements)
            st.success(output)
        else:
            st.warning("请确保所有表结构和SQL需求已经填写")
Run起来

image.png

image.png

image.png

至此,我们就做了一个可以根据表结构和数据库类型,输入具体查询需求自动生成对应SQL语句的AI小助手。不难发现,我们提供的是关系数据库表,当选择文档数据库时会有提示,但还是给我们转换成功领克。但是,实际使用中还是要对齐数据库和表格式为好。