共用方式為


AI:自然語言轉換為 SQL 語言

在考慮 AI 功能時,引述「只是因為您無法表示您應該」是一個有用的指南。 例如,Azure OpenAI 的自然語言對 SQL 功能可讓使用者以純英文進行資料庫查詢,這可以是增強其生產力的強大工具。 不過, 強大的 並不一定意味著 適當安全。 本練習將示範如何使用此 AI 功能,同時討論在決定實作之前要牢記的重要考慮。

以下是可用來從資料庫擷取數據的自然語言查詢範例:

Get the the total revenue for all companies in London.

使用適當的提示時,Azure OpenAI 會將此查詢轉換成 SQL,以用來從資料庫傳回結果。 因此,包括商務分析師、營銷人員和主管在內的非技術性使用者可以更輕鬆地從資料庫擷取寶貴的資訊,而不需使用複雜的 SQL 語法或依賴受限制的數據格和篩選。 這種簡化的方法可藉由免除使用者尋求技術專家協助的需求,來提升生產力。

此練習提供一個起點,可協助您瞭解 SQL 的自然語言運作方式、介紹一些重要考慮、讓您思考優缺點,並示範程式代碼以開始使用。

在本練習中,您將會:

  • 使用 GPT 提示將自然語言轉換成 SQL。
  • 試驗不同的 GPT 提示。
  • 使用產生的 SQL 來查詢稍早啟動的 PostgreSQL 資料庫。
  • 從 PostgreSQL 傳回查詢結果,並在瀏覽器中顯示它們。

讓我們從實驗不同的 GPT 提示開始,這些提示可用來將自然語言轉換成 SQL。

使用自然語言到 SQL 功能

  1. 上一個練習 中,您已啟動資料庫、API 和應用程式。 您也會更新檔案 .env 。 如果您未完成這些步驟,請遵循練習結尾的指示,再繼續進行。

  2. 返回瀏覽器 (http://localhost:4200) 並找出 datagrid 下方頁面的 [自訂查詢 ] 區段。 請注意,已包含範例查詢值: 取得所有訂單的總營收。依公司分組,並包括城市。

    SQL 查詢的自然語言。

  3. 選取 [ 執行查詢] 按鈕。 這會將使用者的自然語言查詢傳遞至 Azure OpenAI,以將它轉換成 SQL。 然後,SQL 查詢將用來查詢資料庫,並傳回任何潛在的結果。

  4. 執行下列 自訂查詢

    Get the total revenue for Adventure Works Cycles. Include the contact information as well.
    
  5. 檢視在 Visual Studio Code 中執行 API 伺服器的終端機視窗,並注意到它會顯示從 Azure OpenAI 傳回的 SQL 查詢。 伺服器端 API 會使用 JSON 數據來查詢 PostgreSQL 資料庫。 查詢中包含的任何字串值會新增為參數值,以防止 SQL 插入式攻擊:

    { 
        "sql": "SELECT c.company, c.city, c.email, SUM(o.total) AS revenue FROM customers c INNER JOIN orders o ON c.id = o.customer_id WHERE c.company = $1 GROUP BY c.company, c.city, c.email", 
        "paramValues": ["Adventure Works Cycles"] 
    }
    
  6. 返回瀏覽器,然後選取 [ 重設數據 ] 以在 datagrid 中再次檢視所有客戶。

探索自然語言至 SQL 程式代碼

小提示

如果您使用 Visual Studio Code,您可以選取下列專案來直接開啟檔案:

  • Windows/Linux: Ctrl + P
  • Mac: Cmd + P

然後輸入您要開啟的檔案名。

備註

此練習的目標是要示範如何使用自然語言來使用 SQL 功能,並示範如何開始使用它。 如先前所述,在繼續進行任何實作之前,請務必先討論這種類型的 AI 是否適合您的組織。 您也必須 規劃適當的提示規則和資料庫安全性措施 ,以防止未經授權的存取和保護敏感數據。

  1. 既然您已瞭解 SQL 功能的自然語言運作方式,讓我們來檢查其實作方式。

  2. 開啟 伺服器/apiRoutes.ts 檔案並找出 generateSql 路由。 在瀏覽器中執行的用戶端應用程式會呼叫此 API 路由,並用來從自然語言查詢產生 SQL。 擷取 SQL 查詢之後,它會用來查詢資料庫並傳回結果。

    router.post('/generateSql', async (req, res) => {
        const userPrompt = req.body.prompt;
    
        if (!userPrompt) {
            return res.status(400).json({ error: 'Missing parameter "prompt".' });
        }
    
        try {
            // Call Azure OpenAI to convert the user prompt into a SQL query
            const sqlCommandObject = await getSQLFromNLP(userPrompt);
    
            let result: any[] = [];
            // Execute the SQL query
            if (sqlCommandObject && !sqlCommandObject.error) {
                result = await queryDb(sqlCommandObject) as any[];
            }
            else {
                result = [ { query_error : sqlCommandObject.error } ];
            }
            res.json(result);
        } catch (e) {
            console.error(e);
            res.status(500).json({ error: 'Error generating or running SQL query.' });
        }
    });
    

    請注意路由中的 generateSql 下列功能:

    • 它會從 req.body.prompt 擷取用戶查詢值,並將它指派給名為的 userPrompt變數。 此值將會用於 GPT 提示字元中。
    • 它會呼叫函 getSQLFromNLP() 式,將自然語言轉換成 SQL。
    • 它會將產生的 SQL 傳遞至名為 queryDb 的函式,該函式會執行 SQL 查詢,並從資料庫傳回結果。
  3. 在編輯器中開啟 伺服器/openAI.ts 檔案,並找出 函 getSQLFromNLP() 式。 路由會 generatesql 呼叫此函式,並用來將自然語言轉換成SQL。

    async function getSQLFromNLP(userPrompt: string): Promise<QueryData> {
        // Get the high-level database schema summary to be used in the prompt.
        // The db.schema file could be generated by a background process or the 
        // schema could be dynamically retrieved.
        const dbSchema = await fs.promises.readFile('db.schema', 'utf8');
    
        const systemPrompt = `
        Assistant is a natural language to SQL bot that returns a JSON object with the SQL query and 
        the parameter values in it. The SQL will query a PostgreSQL database.
    
        PostgreSQL tables with their columns:    
    
        ${dbSchema}
    
        Rules:
        - Convert any strings to a PostgreSQL parameterized query value to avoid SQL injection attacks.
        - Return a JSON object with the following structure: { "sql": "", "paramValues": [] }
    
        Examples:
    
        User: "Display all company reviews. Group by company."      
        Assistant: { "sql": "SELECT * FROM reviews", "paramValues": [] }
    
        User: "Display all reviews for companies located in cities that start with 'L'."
        Assistant: { "sql": "SELECT r.* FROM reviews r INNER JOIN customers c ON r.customer_id = c.id WHERE c.city LIKE 'L%'", "paramValues": [] }
    
        User: "Display revenue for companies located in London. Include the company name and city."
        Assistant: { 
            "sql": "SELECT c.company, c.city, SUM(o.total) AS revenue FROM customers c INNER JOIN orders o ON c.id = o.customer_id WHERE c.city = $1 GROUP BY c.company, c.city", 
            "paramValues": ["London"] 
        }
    
        User: "Get the total revenue for Adventure Works Cycles. Include the contact information as well."
        Assistant: { 
            "sql": "SELECT c.company, c.city, c.email, SUM(o.total) AS revenue FROM customers c INNER JOIN orders o ON c.id = o.customer_id WHERE c.company = $1 GROUP BY c.company, c.city, c.email", 
            "paramValues": ["Adventure Works Cycles"] 
        }
        `;
    
        let queryData: QueryData = { sql: '', paramValues: [], error: '' };
        let results = '';
    
        try {
            results = await callOpenAI(systemPrompt, userPrompt);
            if (results) {
                console.log('results', results);
                const parsedResults = JSON.parse(results);
                queryData = { ...queryData, ...parsedResults };
                if (isProhibitedQuery(queryData.sql)) {
                    queryData.sql = '';
                    queryData.error = 'Prohibited query.';
                }
            }
        } catch (error) {
            console.log(error);
            if (isProhibitedQuery(results)) {
                queryData.sql = '';
                queryData.error = 'Prohibited query.';
            } else {
                queryData.error = results;
            }
        }
    
        return queryData;
    }
    
    • userPrompt參數會傳遞至函式。 此值 userPrompt 是使用者在瀏覽器中輸入的自然語言查詢。
    • systemPrompt定義要使用的 AI 助理類型,以及應遵循的規則。 這有助於 Azure OpenAI 了解資料庫結構、要套用的規則,以及如何傳回產生的 SQL 查詢和參數。
    • 會呼叫名為 callOpenAI() 的函式, systemPrompt 並將和 userPrompt 值傳遞給它。
    • 系統會檢查結果,以確保產生的 SQL 查詢中未包含任何禁止的值。 如果找到禁止的值,SQL 查詢會設定為空字串。
  4. 讓我們更詳細地逐步解說系統提示:

    const systemPrompt = `
      Assistant is a natural language to SQL bot that returns a JSON object with the SQL query and 
      the parameter values in it. The SQL will query a PostgreSQL database.
    
      PostgreSQL tables with their columns:    
    
      ${dbSchema}
    
      Rules:
      - Convert any strings to a PostgreSQL parameterized query value to avoid SQL injection attacks.
      - Return a JSON object with the following structure: { "sql": "", "paramValues": [] }
    
      Examples:
    
      User: "Display all company reviews. Group by company."      
      Assistant: { "sql": "SELECT * FROM reviews", "paramValues": [] }
    
      User: "Display all reviews for companies located in cities that start with 'L'."
      Assistant: { "sql": "SELECT r.* FROM reviews r INNER JOIN customers c ON r.customer_id = c.id WHERE c.city LIKE 'L%'", "paramValues": [] }
    
      User: "Display revenue for companies located in London. Include the company name and city."
      Assistant: { 
        "sql": "SELECT c.company, c.city, SUM(o.total) AS revenue FROM customers c INNER JOIN orders o ON c.id = o.customer_id WHERE c.city = $1 GROUP BY c.company, c.city", 
        "paramValues": ["London"] 
      }
    
      User: "Get the total revenue for Adventure Works Cycles. Include the contact information as well."
      Assistant: { 
        "sql": "SELECT c.company, c.city, c.email, SUM(o.total) AS revenue FROM customers c INNER JOIN orders o ON c.id = o.customer_id WHERE c.company = $1 GROUP BY c.company, c.city, c.email", 
        "paramValues": ["Adventure Works Cycles"] 
      }
    `;
    
    • 定義的 AI 助理類型。 在此情況下,「自然語言為 SQL Bot」。

    • 資料庫中的數據表名稱和數據行已定義。 提示中包含的高階架構可以在 server/db.schema 檔案中找到,如下所示。

      - customers (id, company, city, email)
      - orders (id, customer_id, date, total)
      - order_items (id, order_id, product_id, quantity, price)
      - reviews (id, customer_id, review, date, comment)
      

      小提示

      您可以考慮建立僅包含數據使用者的只讀檢視,以使用自然語言查詢 SQL。

    • 規則的定義是將任何字串值轉換成參數化查詢值,以避免發生 SQL 插入式攻擊。

    • 規則的定義是一律傳回 JSON 物件,其中包含 SQL 查詢和其中的參數值。

    • 提供範例使用者提示和預期的 SQL 查詢和參數值。 這稱為 「很少拍攝」的學習。 雖然 LLM 是針對大量數據進行定型,但只能使用一些範例來適應新工作。 替代方法是「零射」學習,其中未提供任何範例,而且模型預期會產生正確的 SQL 查詢和參數值。

  5. getSQLFromNLP() 式會將系統和使用者提示傳送至名為 callOpenAI() 的函式,該函式也位於 伺服器/openAI.ts 檔案中。 函數 callOpenAI() 會透過檢查環境變數來判斷應該呼叫 Foundry Models 中的 Azure OpenAI 或 OpenAI 服務。 如果環境變數中提供密鑰、端點和模型,則會呼叫 Azure OpenAI,否則會呼叫 OpenAI。

    function callOpenAI(systemPrompt: string, userPrompt: string, temperature = 0, useBYOD = false) {
        const isAzureOpenAI = OPENAI_API_KEY && OPENAI_ENDPOINT && OPENAI_MODEL;
    
        if (isAzureOpenAI) {
            if (useBYOD) {
                return getAzureOpenAIBYODCompletion(systemPrompt, userPrompt, temperature);
            }
            return getAzureOpenAICompletion(systemPrompt, userPrompt, temperature);
        }
    
        return getOpenAICompletion(systemPrompt, userPrompt, temperature);
    }
    

    備註

    雖然我們會在本教學課程中著重於 Azure OpenAI,但如果您只提供 OPENAI_API_KEY.env 檔案中的值,應用程式將會改用 OpenAI。 如果您選擇使用 OpenAI 而非 Azure OpenAI,在某些情況下可能會看到不同的結果。

  6. 找出函式 getAzureOpenAICompletion()

    async function getAzureOpenAICompletion(systemPrompt: string, userPrompt: string, temperature: number): Promise<string> {
        const completion = await createAzureOpenAICompletion(systemPrompt, userPrompt, temperature);
        let content = completion.choices[0]?.message?.content?.trim() ?? '';
        console.log('Azure OpenAI Output: \n', content);
        if (content && content.includes('{') && content.includes('}')) {
            content = extractJson(content);
        }
        return content;
    }
    

    此函式會執行下列動作:

    • 參數:

      • systemPromptuserPrompttemperature 是主要參數。
        • systemPrompt:通知 Azure OpenAI 模型其角色和要遵循的規則。
        • userPrompt:包含使用者提供的資訊,例如自然語言輸入或產生輸出的規則。
        • temperature:指定模型響應的創意層級。 較高的值會產生更具創意的輸出,而較低的值(例如0)會產生更具決定性的答案。
    • 完成產生:

      • 函式會使用createAzureOpenAICompletion()systemPromptuserPrompt 呼叫 temperature ,以產生完成。
      • 它會從完成的第一個選項擷取內容,並修剪任何額外的空格符。
      • 如果內容包含類似 JSON 的結構(以 和 {的存在}表示),則會擷取 JSON 內容。
    • 記錄與傳回值:

      • 函式會將 Azure OpenAI 輸出記錄至主控台。
      • 它會以字串的形式傳回已處理的內容。
  7. 找出函式 createAzureOpenAICompletion()

    async function createAzureOpenAICompletion(systemPrompt: string, userPrompt: string, temperature: number, dataSources?: any[]): Promise<any> {
        const baseEnvVars = ['OPENAI_API_KEY', 'OPENAI_ENDPOINT', 'OPENAI_MODEL'];
        const byodEnvVars = ['AZURE_AI_SEARCH_ENDPOINT', 'AZURE_AI_SEARCH_KEY', 'AZURE_AI_SEARCH_INDEX'];
        const requiredEnvVars = dataSources ? [...baseEnvVars, ...byodEnvVars] : baseEnvVars;
        checkRequiredEnvVars(requiredEnvVars);
    
        const config = { 
            apiKey: OPENAI_API_KEY,
            endpoint: OPENAI_ENDPOINT,
            apiVersion: OPENAI_API_VERSION,
            deployment: OPENAI_MODEL
        };
        const aoai = new AzureOpenAI(config);
        const completion = await aoai.chat.completions.create({
            model: OPENAI_MODEL, // gpt-4o, gpt-3.5-turbo, etc. Pulled from .env file
            max_tokens: 1024,
            temperature,
            response_format: {
                type: "json_object",
            },
            messages: [
                { role: 'system', content: systemPrompt },
                { role: 'user', content: userPrompt }
            ],
            // @ts-expect-error data_sources is a custom property used with the "Azure Add Your Data" feature
            data_sources: dataSources
        });
        return completion;
    }
    
    function checkRequiredEnvVars(requiredEnvVars: string[]) {
        for (const envVar of requiredEnvVars) {
            if (!process.env[envVar]) {
                throw new Error(`Missing ${envVar} in environment variables.`);
            }
        }
    }
    

    此函式會執行下列動作:

    • 參數:

      • systemPromptuserPrompttemperature 是稍早討論的主要參數。
      • 選擇性 dataSources 參數支援「Azure 攜帶您自己的數據」功能,本教學課程稍後將涵蓋此功能。
    • 環境變數檢查:

      • 函式會驗證基本環境變數是否存在,如果遺漏任何環境變數,則會擲回錯誤。
    • 組態物件:

      • 物件config是使用來自 .env 檔案的值來建立的 (OPENAI_API_KEYOPENAI_ENDPOINTOPENAI_API_VERSIONOPENAI_MODEL。 這些值可用來建構 URL 來呼叫 Azure OpenAI。
    • AzureOpenAI 實例:

      • AzureOpenAI 的執行個體是使用 config 物件所建立。 符號 AzureOpenAI 是套件的 openai 一部分,應該匯入檔案頂端。
    • 產生完成:

      • 使用下列屬性呼叫 函 chat.completions.create() 式:
        • model:指定檔案中所 .env 定義的 GPT 模型(例如 gpt-4o、gpt-3.5-turbo)。
        • max_tokens:定義完成的令牌數目上限。
        • temperature:設定取樣溫度。 較高的值(例如 0.9)會產生更多的創造性回應,而較低的值(例如 0)會產生更決定性的答案。
        • response_format:定義回應格式。 在這裡,它會設定為傳回 JSON 物件。 如需 JSON 模式的詳細資訊,請參閱 Azure OpenAI 參考檔
        • messages:包含用來產生聊天完成的訊息。 此範例包含兩則訊息:一則來自系統(定義行為和規則),另一則來自使用者(包含提示文字)。
    • 傳回值:

      • 函式會傳回 Azure OpenAI 所產生的完成物件。
  8. 將函式中的 getSQLFromNLP() 下列幾行批注化:

    // if (isProhibitedQuery(queryData.sql)) { 
    //     queryData.sql = '';
    // }
    
  9. 儲存 openAI.ts。 API 伺服器會自動重建 TypeScript 程式代碼,然後重新啟動伺服器。

  10. 返回瀏覽器,然後在 [自定義查詢] 輸入中輸入 [從資料庫選取所有數據表名稱]。 請選擇執行查詢。 是否顯示資料表名稱?

  11. 返回 getSQLFromNLP() 中的 函式,並將下列規則新增至Rules:系統提示字元的 區段,然後儲存盤案。

    - Do not allow the SELECT query to return table names, function names, or procedure names.
    
  12. 傳回瀏覽器並執行下列工作:

    • 輸入 從資料庫選取所有資料表名稱[自訂查詢] 輸入。 請選擇執行查詢。 是否顯示資料表名稱?
    • 輸入 從資料庫選取所有函數名稱。[自定義查詢 ] 輸入中,然後再次選取 [ 執行查詢 ]。 是否顯示函式名稱?
  13. 問題:模型一律會遵循您在提示中定義的規則嗎?

    答:不! 請務必注意,OpenAI 模型有時可能會傳回不符合您所定義規則的非預期結果。 請務必在程式代碼中規劃。

  14. 返回 伺服器/openAI.ts 並找出函 isProhibitedQuery() 式。 這是可在 Azure OpenAI 傳回結果之後執行之後續處理程式碼的範例。 請注意,如果在產生的 SQL 查詢中傳回禁止的關鍵詞,它會將 sql 屬性設定為空字串。 這可確保如果從 Azure OpenAI 傳回非預期的結果,就不會對資料庫執行 SQL 查詢。

    function isProhibitedQuery(query: string): boolean {
        if (!query) return false;
    
        const prohibitedKeywords = [
            'insert', 'update', 'delete', 'drop', 'truncate', 'alter', 'create', 'replace',
            'information_schema', 'pg_catalog', 'pg_tables', 'pg_proc', 'pg_namespace', 'pg_class',
            'table_schema', 'table_name', 'column_name', 'column_default', 'is_nullable',
            'data_type', 'udt_name', 'character_maximum_length', 'numeric_precision',
            'numeric_scale', 'datetime_precision', 'interval_type', 'collation_name',
            'grant', 'revoke', 'rollback', 'commit', 'savepoint', 'vacuum', 'analyze'
        ];
        const queryLower = query.toLowerCase();
        return prohibitedKeywords.some(keyword => queryLower.includes(keyword));
    }
    

    備註

    請務必注意,這隻是示範程序代碼。 如果您選擇將自然語言轉換成 SQL,可能需要其他禁止的關鍵詞來涵蓋您的特定使用案例。 這是您必須謹慎規劃並使用的功能,以確保只會傳回有效的 SQL 查詢,並針對資料庫執行。 除了禁止的關鍵詞之外,您也需要考慮安全性。

  15. 返回 伺服器/openAI.ts ,並取消批注函式中的 getSQLFromNLP() 下列程序代碼。 儲存檔案。

    if (isProhibitedQuery(queryData.sql)) { 
        queryData.sql = '';
    }
    
  16. 從中移除下列規則 systemPrompt 並儲存盤案。

    - Do not allow the SELECT query to return table names, function names, or procedure names.
    
  17. 返回瀏覽器,再次在 [自定義查詢] 輸入中輸入 [從資料庫選取所有數據表名稱],然後選取 [執行查詢] 按鈕。

  18. 是否有任何數據表結果顯示? 即使沒有規則, isProhibitedQuery 後續處理程式碼仍會禁止對資料庫執行該類型的查詢。

  19. 如先前所述,將自然語言整合到企業營運應用程式中的 SQL 對用戶來說相當有益,但它確實隨附於自己的一組考慮。

    優點:

    • 使用者友好性:這項功能可讓使用者更容易存取資料庫互動,而不需要技術專業知識,減少對 SQL 知識的需求,並可能加快作業速度。

    • 提高生產力:商務分析師、營銷人員、主管和其他非技術使用者可以從資料庫擷取寶貴的資訊,而不必依賴技術專家,進而提高效率。

    • 廣泛的應用程式:藉由使用進階語言模型,應用程式可以設計來迎合廣泛的使用者和使用案例。

    考量因素:

    • 安全性:最大的考慮之一是安全性。 如果使用者可以使用自然語言與資料庫互動,就必須有健全的安全性措施,以防止未經授權的存取或惡意查詢。 您可以考慮實作唯讀模式,以防止使用者修改數據。

    • 數據隱私權:某些數據可能很敏感,而且不應該容易存取,因此您必須確保適當的保護和用戶權力已就緒。

    • 正確性:雖然自然語言處理已大幅改善,但並不完美。 錯誤解譯用戶查詢可能會導致不正確的結果或非預期的行為。 您必須規劃如何處理非預期的結果。

    • 效率:不保證從自然語言查詢傳回的 SQL 會有效率。 在某些情況下,如果後續處理規則偵測到 SQL 查詢的問題,可能需要對 Azure OpenAI 進行額外的呼叫。

    • 訓練和用戶調整:用戶必須經過訓練,才能正確制定其查詢。 雖然比學習 SQL 更容易,但仍可能涉及學習曲線。

  20. 在繼續進行下一個練習之前,需要考慮幾個最後幾點:

    • 請記住,“只是因為你不能代表你應該”在這裡套用。 在將自然語言整合到應用程式中之前,請先謹慎謹慎謹慎地規劃。 請務必瞭解潛在風險,併為其進行規劃。
    • 使用這種類型的技術之前,請先與您的小組、資料庫管理員、安全性小組、項目關係人及任何其他相關方討論潛在的案例,以確保適合您的組織。 請務必討論 SQL 的自然語言是否符合安全性、隱私權,以及貴組織可能具備的任何其他需求。
    • 安全性應該是主要考慮,並內建於規劃、開發和部署程式。
    • 雖然 SQL 的自然語言非常強大,但仔細規劃必須進入它,以確保提示具有必要的規則,並包含後續處理功能。 規劃額外的時間來實作及測試這種類型的功能,並考慮傳回非預期結果的案例。
    • 透過 Azure OpenAI,客戶可享有 Microsoft Azure 的安全性功能,同時執行與 OpenAI 相同的模型。 Azure OpenAI 提供私人網路、區域可用性,以及負責任 AI 內容篩選。 深入瞭解 Azure OpenAI 的數據、隱私權和安全性
  21. 您現在已瞭解如何使用 Azure OpenAI 將自然語言轉換成 SQL,並了解實作這種類型的功能優缺點。 在下一個練習中,您將瞭解如何使用 Azure OpenAI 產生電子郵件和簡訊。

後續步驟