原文地址:https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql 了解数据工程师如何使用 PL/SQL 利用 AI 实现 Oracle 自动化。探索开源 UC AI SDK 的结构化输出、文件分析、推理和工具调用功能。 * 95% 的人工智能试点项目都失败了,但后台自动化却取得了成功——这对数据工程师来说是一个巨大的机遇。 * 无需学习新语言——如果您掌握了 PL/SQL/SQL,AI 只是在其上叠加的一层薄薄的底层。 * 学习实用技巧:结构化输出、文件分析、推理和工具调用 * 开源 UC AI SDK 提供对多个 AI 提供商(OpenAI、Claude、Gemini 等)的统一访问。 这篇文章主要是我上周网络研讨会“ [United Codes, Live! - Practical AI in PL/SQL Made Easy”](https://youtube.com/live/k4Vt7AKXCnM) 的文字稿。在研讨会上,我更深入地探讨了相关概念。如果您想在阅读本文时获得更多背景信息,可以观看视频中对应的章节。 ## [**人工智能和预言机**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#ai-and-oracle) 人工智能热潮正席卷全球。自然,所有利益相关者都已开始思考它在 Oracle 领域中的地位。 ### [**为什么 Oracle 要关注人工智能?**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#why-care-about-ai-in-oracle) 现在几乎每个人都在开发聊天功能。如果你已经拥有一个成熟且运行良好的用户界面 ,不妨问问你的用户是否真的能从中受益。我非常喜欢我的银行网站,它仍然保持着原来的界面,我可以立即查看余额,并且无需与机器人聊天就能完成交易。 相反,如果你是一名数据工程师,你应该从不同的角度来看待这个问题。不要只是在用户界面中添加逻辑层模型(LLM);要思考你的数据在数据库中是如何被处理的。人工智能在三个关键领域表现出色: 1. 数据分析 :汇总、分类、归类、聚类和检测异常(比人类更快,有时甚至会产生幻觉!) 2. 非结构化数据到结构化数据 :将文本或文档块转换为关系数据 3. 流程自动化 :实现智能工作流程和自动化决策 ### [**现有解决方案的问题**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#the-problem-with-existing-solutions) 在我看来,Oracle 目前的解决方案在关键领域存在不足,不足以与其他编程环境竞争。 Oracle 23ai SELECT AI * 仅限 23ai :此功能仅在 Oracle 23ai 中可用,目前尚未推出本地部署版本。因此,对大多数客户而言意义不大。 * 功能不足 :该方案的重点在于将自然语言转换为 SQL 语句。我认为这些应用场景比较小众。我在本文中介绍的大多数其他技术都不受支持。 Oracle APEX AI 套件 * 供应商有限 :目前支持的 AI 供应商仅限于 Cohere、OpenAI 和一些过时的 OCI 服务。像 Google、Anthropic、xAI 和开源模型等大型供应商尚未获得原生支持。 * 功能不足 :与 SELECT AI 一样,仅支持基本文本生成,缺乏更深入的技术。 当然,这些解决方案会随着时间推移而改进。人工智能领域发展日新月异,它们很可能很快又会过时。目前, APEX World 已经等待 25.1 版本近四分之三的时间了。而且,用户也无法自行增强这些解决方案。 我感到很沮丧,于是决定自己开发解决方案。 ## [**隆重推出 UC AI:一款颠覆性的 PL/SQL SDK**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#introducing-uc-ai-a-game-changing-plsql-sdk) 由于我想亲自尝试数据库中的高级人工智能功能,所以我编写了 PL/SQL 包来调用它们的 API。这促使我萌生了将其开源的想法。 ### [**主要特点**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#key-features) * 免费开源 :可在 [GitHub](https://github.com/United-Codes/uc_ai) 上获取 * 广泛兼容性 :可与旧版 Oracle 数据库配合使用(可能为 12.2 版本,已在 18c 版本上测试)。 * 统一接口 :一个 API 可供多个 AI 提供商使用 * 提供商独立性 :轻松切换模型和提供商。目前支持 5 个提供商:OpenAI GPT、Anthropologie Claude、Google Gemini、Ollama(离线模型)和 Oracle 云基础设施 (OCI)(Llama、Cohere、xAI Grok、Google Gemini)。 * 高级功能 :工具、推理、文件、结构化输出 * 文档 :总有改进的空间,但我非常重视文档质量(如果您发现任何改进空间,请提交 issue 或 PR)。 ### [**统一方法**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#unified-approach) 不同的 AI 提供商通常拥有不同的 API。UC AI 消除了这些差异,使您能够: * 无需更改代码即可切换供应商 * 轻松比较模型性能 * 避免供应商锁定 * 专注于业务逻辑而非 API 实现 ## [**多家人工智能提供商**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#multiple-ai-providers) 人工智能提供商领域非常多元化,而且发展迅速。 ### [**为什么选择模式和提供商很重要**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#why-model-and-provider-choice-matters) 不同型号的模型各有优势: * 智力 * 速度 * 成本 遗憾的是,选择\_最佳\_模型并非易事。智能模型速度较慢且价格昂贵。选择模型还需了解任务的具体内容。例如,概括中等长度的文本并不需要昂贵且智能的模型。 尽管 OpenAI 最初取得了巨大成功,ChatGPT 也是使用最广泛的平台,但仅仅关注它们就大错特错了。不同的模型各有特色,擅长的领域也各不相同。例如,就我的经验而言,Claude 在编写 PL/SQL 代码和调用工具方面无疑是最佳选择。但最近,只有 Gemini 能够正确回答我提出的一个特定问题。我的建议是:动手实践,尝试用不同的模型完成相同的任务,并比较结果。正因如此,我使用 [t3.chat](https://t3.chat/) 而不是 ChatGPT 或 Claude,因为 t3.chat 可以运行不同提供商的提示(我与他们没有任何关联)。 ![](https://cn-oracle-apex.oss-cn-shanghai.aliyuncs.com/file_storage/20251115_zz2lnhb8.png) 此外,像 [Artificial Analysis](https://artificialanalysis.ai/) 这样的平台提供了优秀的基准测试来比较不同的模型。这张图展示了他们其中一个基准测试随时间的变化,可以很好地展现人工智能的发展历程和竞争态势。OpenRouter [会](https://openrouter.ai/rankings)分享哪些模型使用最广泛以及在哪些行业中最常用的排名。 ### [**支持的提供商**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#supported-providers) 更多详情请查看[各供应商的文档](https://www.united-codes.com/products/uc-ai/docs/providers/anthropic/) 。 1. OpenAI (GPT 模型) 2. 人格 (克劳德模型) 3. 谷歌 (Gemini 模型) 4. Ollama (本地/离线模型:Meta Llama、Google Gemma、阿里巴巴 Qwen、DeepSeek、GPT-OSS) 5. Oracle 云基础设施 (Cohere、Meta Llama、xAI Grok、Google Gemini) ### [**线下模式:隐私与控制**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#offline-models-privacy-and-control) 也不要低估离线模型。虽然前沿模型并非离线模型,但它们仍然能提供卓越的性能,中国公司更是每月都在突破极限。更多信息请阅读我的[上一篇博文](https://hartenfeller.dev/blog/open-and-local-AI-models-in-oracle) ,但显而易见的优势是: * 完全隐私 :数据不会离开您的网络 * 成本效益 :初始硬件投资之后 UC AI 通过 Ollama 支持离线模型。您可以[在他们的网站上](https://ollama.com/search)浏览可用的模型。United Codes 最近升级了一台专用于离线 AI 使用的本地 AI 服务器。 ## [**生成文本:人工智能入门指南**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#generate-text-your-first-steps-with-ai) LLM 的核心功能是生成文本。以下是使用 UC AI 实现此功能的方法: ```sql declare l_result json_object_t; begin l_result := uc_ai.generate_text( p_user_prompt => 'What is the most common insurance claim type?', p_provider => uc_ai.c_provider_openai, p_model => uc_ai_openai.c_model_gpt_5 ); dbms_output.put_line(l_result.get_clob('final_message')); end; ``` ### [**系统提示与用户提示**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#system-vs-user-prompts) UC AI 支持用户提示和系统提示: * 用户提示 :实际的问题或请求 * 系统提示 :限制模型行为和角色的指令 例如,ChatGPT(指的是应用本身,而非模型)使用了一个不受限制的系统提示,因为它可以用于多种用途。另一方面,在商业应用中,“仅回答 X 相关问题”则是一个非常有用的功能,可以让汽车经销商的销售助理拒绝与顾客讨论《搏击俱乐部》的哲学思想。 您只需在调用中添加 `**p_system_prompt**` 参数即可: ```sql declare l_result json_object_t; begin l_result := uc_ai.generate_text( p_user_prompt => 'Explain the philosophy of fight club', p_system_prompt => 'You are an assistant to help users ask question about the inventory of our car dealership. Only ask questions about the dealership and its cars.' p_provider => uc_ai.c_provider_openai, p_model => uc_ai_openai.c_model_gpt_5 ); dbms_output.put_line(l_result.get_clob('final_message')); end; ``` ### [**转换模式和提供商**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#switching-model-and-provider) 只需更改 `**p_model**` 和 `**p_provider**` 参数: ```sql declare l_result json_object_t; begin l_result := uc_ai.generate_text( p_user_prompt => 'Explain the philosophy of fight club', p_provider => uc_ai.c_provider_anthropic, -- or uc_ai.c_provider_google p_model => uc_ai_anthropic.c_model_claude_4_sonnet -- or uc_ai_google.c_model_gemini_2_5_flash ); dbms_output.put_line(l_result.get_clob('final_message')); end; ``` ### [**实际案例:保险理赔函**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#practical-example-insurance-settlement-letters) 我选择保险公司作为演示的主题。我们处理用户关于其财产(例如车辆)损失的索赔。 我们无需手动撰写结算函,可以使用 `**uc_ai.generate_text**` 函数自动生成。我们使用以下提示: * 系统 :请为以下案例生成一份保险理赔函。本案例仅用于演示,您可以自行添加细节使其更贴近实际情况。保险公司名称为:位于红木海岸的 APEX 保险公司。发件人为 L. Ellison。请仅返回理赔函内容,无需其他任何信息。请使用 Markdown 格式,但请勿将响应内容包裹在 Markdown 代码块中。 * 用户 :#CLAIM\_JSON#(将被替换为实际的 JSON 数据) 用户提示符中仅包含一个占位符,我们将用此查询的结果替换它: ```sql SELECT JSON_OBJECT( 'settlementId' VALUE s.settlement_id, -- 101 'claimNumber' VALUE s.claim_number, -- CL2025-001-AUTO 'policyNumber' VALUE s.policy_number, -- POL-AX789 'policyType' VALUE s.policy_type, -- Auto 'incidentDate' VALUE TO_CHAR(s.incident_date, 'YYYY-MM-DD'), -- 2025-01-15 'incidentDescription' VALUE s.incident_description, -- Minor fender bender on Main Street 'claimantFirstName' VALUE s.claimant_first_name, -- Alice 'claimantLastName' VALUE s.claimant_last_name, -- Smith 'claimantEmail' VALUE s.claimant_email, -- alice.smith@example.com 'claimantPhone' VALUE s.claimant_phone, -- +33612345678 'insuredFirstName' VALUE s.insured_first_name, -- Bob 'insuredLastName' VALUE s.insured_last_name, -- Smith 'settlementDate' VALUE TO_CHAR(s.settlement_date, 'YYYY-MM-DD'), -- 2025-02-01 'settlementAmount' VALUE s.settlement_amount, -- 1500 'currencyCode' VALUE s.currency_code, -- EUR 'settlementStatus' VALUE s.settlement_status, -- Proposed 'releaseSignedDate' VALUE TO_CHAR(s.release_signed_date, 'YYYY-MM-DD'), -- 2025-03-02 'paymentDueDate' VALUE TO_CHAR(s.payment_due_date, 'YYYY-MM-DD'), -- 2025-06-01 'paymentPaidDate' VALUE TO_CHAR(s.payment_paid_date, 'YYYY-MM-DD'), -- 2025-04-21 'negotiatorAgentName' VALUE s.negotiator_agent_name, -- Jean Dupont 'notes' VALUE s.notes -- Beneficiary confirmed receipt of funds. ) AS settlement_json INTO :P1_SETTLEMENT_DATA FROM pame_settlement_demo s WHERE s.settlement_id = :P1_SETTLEMENT_ID; ``` **人工智能响应** > APEX Insurance 123 Coastal Way Redwood Shores, CA 94065 > > February 1, 2025 > > Alice Smith \[Claimant's Address - Fictional, as not provided in JSON\] 101 Oak Avenue Anytown, France 12345 > > Subject: Settlement Confirmation for Claim CL2025-001-AUTO – Policy POL-AX789 – Insured: Bob Smith > > Dear Ms. Smith, > > This letter serves to formally confirm the settlement of your auto insurance claim, CL2025-001-AUTO, related to the incident on January 15, 2025, involving our policyholder, Mr. Bob Smith, under policy number POL-AX789. > > We understand this claim resulted from a minor fender bender on Main Street, and we appreciate your cooperation throughout the adjustment process. Our Claims Negotiator, Jean Dupont, has worked diligently to facilitate a fair resolution. > > We are pleased to inform you that APEX Insurance has processed the final settlement for your claim in the amount of €1,500.00 EUR (One Thousand Five Hundred Euros). > > We confirm that the Release of Liability form, which you signed on March 2, 2025, was duly received. The payment of €1,500.00 EUR was processed and disbursed on April 21, 2025. Our records indicate that you have confirmed receipt of these funds. > > We trust that this settlement brings a satisfactory close to this matter. Should you have any further questions, please do not hesitate to contact our claims department at the number below. > > Sincerely, > > L. Ellison Claims Adjuster APEX Insurance Phone: +1-800-555-APEX Email: [claims@apexinsurance.com](https://hartenfeller.dev/blog/xinsurance.com) 我使用的是 Gemini Flash 2.5,它甚至智能地指出我们还没有提供索赔人的地址。 ## [**结构化输出:从文本到 JSON**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#structured-output-from-text-to-json) 语言学习模型(LLM)非常擅长快速处理和输出文本。我们的数据库以结构化的方式存储数据,即带有类型化列的表格。人工智能可以帮助我们从非结构化文本(例如电子邮件)中提取重要信息,并以预定义的结构返回。 ### [**JSON 模式**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#json-schemas) JSON Schema 是一种用于描述 JSON 内部 JSON 结构的标准。您可以定义对象具有哪些属性,包括类型和必需信息: ```js { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Response with confidence score", "properties": { "response": { "type": "string", "description": "The generated response text" }, "confidence": { "type": "number", "description": "Confidence score between 0 and 1", "minimum": 0, "maximum": 1 } }, "required": ["response", "confidence"] } // Valid example: // { // "response": "The capital of France is Paris.", // "confidence": 1 //} ``` ### [**实际案例:电子邮件处理**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#real-world-example-email-processing) 为了方便客户提交理赔申请,我们允许他们仅通过电子邮件发送详细信息。我们希望在理赔表中创建一条包含这些信息的记录。与其让员工手动输入,不如让人工智能提取这些信息。 这次我们将使用更大的 JSON 模式,其中包含嵌套对象和许多详细信息,例如理赔编号、损失日期和个人信息。我们还要求 AI 从文本中提取损坏类型数组,使其成为某种自动标记系统。 JSON 模式 ```js { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "claim_number": { "type": "string", "description": "The unique identifier for the insurance claim." }, "date_of_loss": { "type": "string", "description": "The date when the loss or damage occurred." }, "time_of_loss": { "type": "string", "description": "The approximate time when the loss or damage occurred." }, "loss_location": { "type": "object", "properties": { "street": { "type": "string", "description": "The street address of the loss location." }, "city": { "type": "string", "description": "The city of the loss location." }, "province_state": { "type": "string", "description": "The province or state of the loss location." }, "postal_code": { "type": "string", "description": "The postal code of the loss location." }, "country": { "type": "string", "description": "The country of the loss location." } }, "required": [ "street", "city", "province_state", "postal_code", "country" ] }, "damage_types": { "type": "array", "description": "An array of types of damage reported.", "items": { "type": "string" } }, "damaged_vehicles": { "type": "array", "description": "An array of details for damaged vehicles.", "items": { "type": "object", "properties": { "car": { "type": "string", "description": "Car brand and model" }, "license_plate": { "type": "string", "description": "License plate of the damaged car" } } } }, "estimated_total_damage_eur": { "type": "number", "description": "The total estimated cost of all damages in Euros." }, "police_report_id": { "type": "string", "description": "The ID of the police report filed, if any." }, "contact_info": { "type": "object", "properties": { "phone_number": { "type": "string", "description": "The claimant's phone number." }, "email": { "type": "string", "description": "The claimant's email address." } }, "required": [ "phone_number", "email" ] } }, "required": [ "claim_number", "date_of_loss", "time_of_loss", "loss_location", "damage_types", "estimated_total_damage_eur", "contact_info" ] } ``` 例如,我们使用包含所有必要信息的自然文本作为电子邮件示例。 财产损失示例文本 > Subject: Urgent: Claim #20240902-A1B2C3 - Property Damage - 123 Maple Street > > Dear APEX Insurance, > > This letter serves as a formal notification of a property damage claim. On August 28, 2024, at approximately 14:30 GMT+2, a severe hailstorm struck my residence located at 123 Maple Street, Anytown, AB 12345, Canada. > > The primary damage incurred includes significant roof damage (multiple shattered tiles, leaks), extensive damage to the two vehicles parked in the driveway (a 2020 Honda Civic with license plate XYZ-789 and a 2022 Tesla Model 3 with license plate ABC-456), and several broken windows on the north side of the house. I have attached photographs documenting the damage, including timestamps. > > The estimated cost of repairs for the roof alone is approximately €15,000. For the vehicles, I've received preliminary estimates of €3,500 for the Honda Civic and €7,000 for the Tesla Model 3. Total estimated damages are around €25,500. > > I have already contacted a local roofing contractor, 'RoofMasters Inc.', and an auto repair shop, 'Apex Auto Solutions', for detailed assessments.  > > I've also filed a police report (Report ID: PR20240902-001) for the widespread storm damage in the area. > > Please advise on the next steps for inspection and processing this claim. My contact number is +1-555-123-4567 and email is Tyler.Durden@example.com. > > Sincerely, > > Tyler 现在我们可以使用带有附加参数 `**p_response_json_schema**` 的 UC AI。 ```sql declare l_result json_object_t; l_schema json_object_t; begin l_schema := json_object_t('{"$schema": "http://json ...'); l_result := uc_ai.generate_text( p_system_prompt => 'Please extract the information from this text into the given JSON schema.' p_user_prompt => 'Subject: Urgent: Claim #20240902-A1B2C3...', p_provider => uc_ai.c_provider_openai, p_model => uc_ai_openai.c_model_gpt_5, p_response_json_schema => l_schema ); end; ``` 作为响应,我们将以我们定义的 JSON 格式收到提取的数据。 ```js { "claim_number": "20240902-A1B2C3", "date_of_loss": "2024-08-28", "time_of_loss": "14:30 GMT+2", "loss_location": { "street": "123 Maple Street", "city": "Anytown", "province_state": "AB", "postal_code": "12345", "country": "Canada" }, "damage_types": ["roof damage", "vehicle damage", "broken windows"], "damaged_vehicles": [ { "car": "2020 Honda Civic", "license_plate": "XYZ-789" }, { "car": "2022 Tesla Model 3", "license_plate": "ABC-456" } ], "estimated_total_damage_eur": 25500, "police_report_id": "PR20240902-001", "contact_info": { "phone_number": "+1-555-123-4567", "email": "Tyler.Durden@example.com" } } ``` 如果缺少信息或存在疑问,您可以让员工查看电子邮件,或者让 AI 回答客户的问题。 ### [**构建 JSON 模式**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#building-json-schemas) 构建大型 JSON 模式可能很繁琐。为了简化这一过程,您可以让 AI 根据您的表结构自动生成一个模式,并使用 [UC AI 文档中的 JSON 模式构建器](https://www.united-codes.com/products/uc-ai/docs/other/json-schema/) 。它提供了一种更直观的体验,方便您创建新模式或修改现有模式。 ![](https://cn-oracle-apex.oss-cn-shanghai.aliyuncs.com/file_storage/20251115_MN1n0yqJ.png) ![](https://hartenfeller.dev/static/df6545d85fc975941ecfe580798c635d/baac7/uc-ai-json-schema-builder.png) ## [**文件分析:超越文本处理**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#file-analysis-beyond-text-processing) 现代人工智能模型实际上是多模态的。这意味着它们可以处理文本和文件。也就是说,您无需运行单独的 OCR 服务;只需将文件通过 UC AI 发送即可。 以保险为例,我们可以帮助人工智能分析信件扫描件或事故照片。 * PDF 处理 :从扫描文档中提取文本 * 图像识别 :分析照片和图表 ### [**分析车祸照片**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#analyzing-car-accident-photos) 我们以这张车祸照片为例。我们想知道照片中的汽车是什么型号,哪些部件损坏了,以及预计的维修费用是多少(图片来源: [Wikimedia 上的 Thue](https://commons.wikimedia.org/wiki/File:Car_crash_1.jpg) )。 ![](https://cn-oracle-apex.oss-cn-shanghai.aliyuncs.com/file_storage/20251115_gLsRs5T7.png) 正如我们在上一章中学到的,以结构化的方式获取信息是明智之举。因此,让我们再次在请求中发送一个 JSON 模式。 用于汽车损坏分析的 JSON Schema ```js { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Car crash", "properties": { "car": { "type": "object", "description": "Car information", "properties": { "brand": { "type": "string", "description": "brand of the car" }, "model": { "type": "string", "description": "car model" }, "confidence": { "type": "number", "description": "confidence score of detected model and brand between 1 and 0", "minimum": 0, "maximum": 1 } } }, "broken_parts": { "type": "array", "description": "List protential broken car parts", "items": { "type": "string" } }, "damage_estimation_eur": { "type": "number", "description": "Estimate the car damage in euro" }, "write_off_estimation": { "type": "number", "description": "Estimate between 1 and 0 how likely it is that repairs would be more expensive then the car value", "minimum": 0, "maximum": 1 } }, "required": [ "car" ] } ``` UC AI 调用看起来比以前稍微复杂一些。原因是我们需要同时发送文本消息和图像。在这种情况下,我们需要使用 `**uc_ai_message_api**` 包中的辅助函数,将两者都封装在一个内容数组中。 ```sql declare l_result json_object_t; l_schema json_object_t; l_messages json_array_t := json_array_t(); l_content json_array_t := json_array_t(); l_file_record pame_files%rowtype; begin -- load image file row select * into l_file_record from pame_files where file_name = :P20_FILE; -- create file content metadata l_content.append(uc_ai_message_api.create_file_content( p_media_type => l_file_record.mime_type, p_data_blob => l_file_record.file_content, p_filename => l_file_record.file_name )); -- add text content with our prmompt l_content.append(uc_ai_message_api.create_text_content( 'Please extract the info for the car crash. Just do your best estimations.' )); -- combine image and message into a user mesage l_messages.append(uc_ai_message_api.create_user_message(l_content)); -- define structured output schema l_schema := json_object_t('{ "$schema": ...'); l_result := uc_ai.generate_text( p_messages => l_messages, p_provider => uc_ai.c_provider_google, p_model => uc_ai_google.c_model_gemini_2_5_flash, p_response_json_schema => l_schema ); :P20_OUTPUT := l_result.get_clob('final_message'); :P20_FULL_RESPONSE := l_result.to_clob; end; ``` ### [**回复**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#response) Gemini Flash 2.5 显示这辆车是雷诺梅甘娜风景(Renault Megane Scenic)。我不是汽车专家,但它肯定是雷诺,而且残骸看起来也和梅甘娜很像。有趣的是,它竟然能正确列出很多损坏的部件。这辆车目前的状态可能更容易列出未损坏的部件,但其他车型在这方面就做得更差了。 ```plaintext { "car": { "brand": "Renault", "confidence": 0.8, "model": "Megane Scenic" }, "broken_parts": [ "front bumper", "hood", "windshield", "front headlights", "engine compartment components", "right front fender", "roof", "front chassis", "right front wheel suspension", "passenger side door" ], "damage_estimation_eur": 10000, "write_off_estimation": 0.95 } ``` ## [**推理与思考:更智能的人工智能响应**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#reasoning-and-thinking-smarter-ai-responses) 推理(也称“思考”)代表了人工智能能力的重大进步,它使模型能够在内部处理信息,然后再利用这些思考结果形成最终答案。这与我们建议其他人三思而后行的原则相同。 ### [**收益与权衡**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#benefits-and-trade-offs) 更智能的人工智能听起来很棒。在基准测试中,推理模型比传统模型具有明显的优势。但要注意,它们也有缺点。 优势 : * 在复杂任务上具有更高的准确率 * 分析问题表现更佳 * 更全面地考虑极端情况 * 深入了解 LLM 如何得出结论(但有些提供者不分享思考步骤) 成本 : * 代币使用量/成本显著增加(你需要为思考代币付费) * 响应时间更长 * 更高的计算需求 因此,只有在真正需要的时候才使用推理是合理的。只需测试一下非推理模型是否能以类似的结果完成任务即可。 ### [**实际案例:数据异常检测**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#practical-example-data-anomaly-detection) 为了测试这项功能,我们将让 LLM 分析一批十份索赔。我们只会提出“注意到什么了吗?”的提示,然后观察他们的回答。 如果我们使用像 Qwen3 这样拥有 17 亿参数的小型模型,区别就非常明显了。在未启用推理功能的情况下,它只会从上到下对文本进行概括。感觉就像它读一部分,然后概括这部分,再读下一部分,如此往复。但启用推理功能后,它似乎能够将整个输入置于上下文中,并输出统计信息,例如大多数索赔都与车辆损坏有关,而德克萨斯州的案例数量似乎最多。 借助更智能的模型 `**GPT-4o**` 即使不启用推理功能,它也能更好地概括整个文档。但最有趣的是,如果我们使用 OpenAI 的 `**o4-mini**` 模型启用推理功能,它还能检测数据中的异常情况 : ```plaintext I spotted a couple of internal inconsistencies in the data—specifically, the narrative dates don’t line up with the “incident_date” fields in two of the claims: • CLM-2024-001923 (Home – Storm Damage) – incident_date: 2024-07-22 – report_text: “Severe thunderstorm … struck the property on July 24th, 2024” • CLM-2024-002789 (Auto – Hit and Run) – incident_date: 2024-08-28 – report_text: “While stopped … at 5:45 PM on August 30th, 2024” In both cases the text describes the event happening on a different date than what’s recorded in the incident_date field (and, in the second, the police-report ID suggests August 28th, not the 30th mentioned). You may want to correct those date mismatches. ``` 虽然我的提示语故意含糊不清,但这恰恰说明了推理模型是如何自然而然地变得更加分析性和全面性的。如果我明确要求模型寻找矛盾之处, `**GPT-4o**` 也能找到。 ### [**推理 API**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#reasoning-api) 启用推理功能时请务必谨慎,因为并非所有模型(主要是较旧的模型)都支持此功能。此外,UC AI 在这方面也无法做到完全统一,因为不同提供商的设置各不相同。例如,Ollama 仅支持启用或禁用推理功能。Anthropic 和 Google 则允许您微调用于推理的标记数量,而 OpenAI 则提供了 `**low**` 、 `**medium**` 、 `**high**` 三种设置。请阅读[推理文档](https://www.united-codes.com/products/uc-ai/docs/guides/reasoning/)以了解更多详情和最佳实践。 ```sql uc_ai.g_enable_reasoning := true; uc_ai_openai.g_reasoning_effort := 'medium'; uc_ai_anthropic.g_reasoning_budget_tokens := 512; uc_ai_google.g_reasoning_budget := 512; l_result := uc_ai.generate_text( p_user_prompt => :P30_USER_PROMPT, p_system_prompt => :P30_SYSTEM_PROMPT, p_provider => :P0_PROVIDER, p_model => :P0_MODEL ); ``` ## [**工具和函数调用:允许人工智能执行操作**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#tools-and-function-calling-allowing-ai-to-execute-actions) 工具(也称为“函数调用”)是 UC AI 最强大的功能,它允许 AI 模型直接执行 PL/SQL 函数。 ### [**概念:赋予人工智能能力**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#the-concept-giving-ai-capabilities) 人工智能不仅可以生成文本,还可以借助工具与数据库进行交互。这对于以下两类功能非常有用: * 条件上下文 (SQL):不要向系统提示中添加太多数据;让 AI 根据用户提示在需要时动态运行查询。 * 操作 (插入、更新、删除、调用 API、作业等):让 AI 根据提示执行业务逻辑 ### [**工作原理**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#how-it-works) 一个简单的工具示例是让模型访问天气 API。由于模型没有实时数据,该工具可用于让 LLM 使用当前天气信息。 这是从人工智能的角度来看的: 1. 用户注册工具时需定义参数和描述(例如,getWeather 返回给定城市的当前天气信息)。 2. 根据用户提示,人工智能决定请求执行工具是否合理(“给我讲个笑话”→否,“我在罗马需要雨衣吗?”→是) 3. 它不会返回答案,而是返回一个工具调用请求。它期望我们运行该工具并返回其输出结果。(“罗马天气晴朗,气温26摄氏度。”) 4. 人工智能会评估结果,可以调用另一个工具,或者决定回答(“不,你不需要雨衣,因为现在天气温暖晴朗”)。 关键在于,人工智能本身并不执行该函数,而是请求执行该函数。然后,UC AI PL/SQL 包会自动运行该工具并返回结果。 UC AI 知道该怎么做,因为您必须使用您创建的 PL/SQL 函数名称向 UC AI 注册工具。 ### [**安全与控制**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#safety-and-control) [与 Oracle 的 SQLcl MCP 服务器方法(允许执行任意 SQL)不同](https://hartenfeller.dev/blog/demystifying-sqlcl-mcp-server#security-concerns) ,UC AI 工具: * 仅执行预定义的 PL/SQL 函数 * 需要显式函数注册 * 允许参数验证 * 保持对开发者的完全控制 ### [**真实案例:保险理赔处理**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#real-world-example-insurance-claim-processing) 现在我们可以进一步实现保险理赔录入的自动化。当收到新邮件时,我们会让人工智能在数据库中创建一条相关记录。 ### [**工具注册**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#tool-registration) 要注册一个工具,我们需要为参数(如果有)创建一个 JSON 模式。然后,我们调用 `**uc_ai_tools_api.create_tool_from_schema**` 并传入工具代码、描述、PL/SQL 函数调用、模式和标签。请注意我们在工具调用中使用了 `**:parameters**` 吗?UC AI 会将其替换为 LLM 提供的 JSON 参数。仔细描述参数和工具本身非常重要,因为这为 AI 提供了宝贵的上下文信息。 ```sql declare l_schema json_object_t; l_tool_id uc_ai_tools.id%type; begin l_schema := json_object_t.parse('{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Create Settlement Request", "description": "Create a new settlement in the database", "properties": { "incident_date": { "type": "string", "description": "Date of the incident in YYYY-MM-DD format" }, "claimant_first_name": { "type": "string", "description": "First name of the person making the claim" }, }, {...} "required": [ "incident_date", "claimant_first_name", "claimant_last_name" ] }'); -- Create the tool l_tool_id := uc_ai_tools_api.create_tool_from_schema( p_tool_code => 'PAME_CREATE_SETTLEMENT', p_description => 'Create a new insurance settlement from initial provided data', p_function_call => 'return pame_pkg.create_new_settlement(:parameters);', p_json_schema => l_schema, p_tags => apex_t_varchar2('pame', 'pame_create_settlement') ); commit; end; / ``` 这是底层函数 `**pame_pkg.create_new_settlement**` 。我们接收一个参数并返回一个结果,两者都以 `**CLOB**` 存储。然后,我们需要检查 JSON 数据的结构是否符合预期。我们不会直接抛出异常,而是会返回错误信息。这样,AI 就能根据上下文在下次尝试中修正输入。如果插入成功,我们会返回成功消息。 **获取用户信息功能** ```sql function create_new_settlement(p_settlement_data in clob) return clob as l_settlement_data json_object_t; l_settlement_id number; l_claim_number varchar2(50 char); l_policy_number varchar2(50 char); l_policy_type varchar2(100 char); l_incident_date date; l_incident_desc varchar2(500 char); l_claimant_fname varchar2(100 char); l_claimant_lname varchar2(100 char); l_claimant_email varchar2(255 char); l_claimant_phone varchar2(20 char); l_insured_fname varchar2(100 char); l_insured_lname varchar2(100 char); l_settlement_amount number(18, 2); l_currency_code varchar2(3 char); l_notes varchar2(1000 char); l_result clob; begin BEGIN l_settlement_data := json_object_t.parse(p_settlement_data); EXCEPTION when others then return '{"status": "error", "message": "Invalid JSON input: ' || sqlerrm || '"}'; END; -- Validate required fields and extract from JSON if not l_settlement_data.has('incident_date') then return '{"status": "error", "message": "Missing required field: incident_date"}'; end if; if not l_settlement_data.has('claimant_first_name') then return '{"status": "error", "message": "Missing required field: claimant_first_name"}'; end if; if not l_settlement_data.has('claimant_last_name') then return '{"status": "error", "message": "Missing required field: claimant_last_name"}'; end if; -- Extract required fields -- generate pattern like this: CL2025-009-ACC l_claim_number := 'CL' || to_char(sysdate, 'YYYY') || '-' || round(sys.dbms_random.value(100, 999999)) || '-AI'; l_policy_number := 'POL' || to_char(sysdate, 'YYYY') || '-' || round(sys.dbms_random.value(100, 999999)) || '-AI'; l_incident_date := to_date(l_settlement_data.get_String('incident_date'), 'FXYYYY-MM-DD'); l_claimant_fname := l_settlement_data.get_String('claimant_first_name'); l_claimant_lname := l_settlement_data.get_String('claimant_last_name'); -- Extract optional fields l_policy_type := case when l_settlement_data.has('policy_type') then l_settlement_data.get_String('policy_type') else null end; l_incident_desc := case when l_settlement_data.has('incident_description') then l_settlement_data.get_String('incident_description') else null end; l_claimant_email := case when l_settlement_data.has('claimant_email') then l_settlement_data.get_String('claimant_email') else null end; l_claimant_phone := case when l_settlement_data.has('claimant_phone') then l_settlement_data.get_String('claimant_phone') else null end; l_insured_fname := case when l_settlement_data.has('insured_first_name') then l_settlement_data.get_String('insured_first_name') else null end; l_insured_lname := case when l_settlement_data.has('insured_last_name') then l_settlement_data.get_String('insured_last_name') else null end; l_settlement_amount := case when l_settlement_data.has('settlement_amount') then l_settlement_data.get_Number('settlement_amount') else 0 end; l_currency_code := case when l_settlement_data.has('currency_code') then l_settlement_data.get_String('currency_code') else 'EUR' end; l_notes := case when l_settlement_data.has('notes') then l_settlement_data.get_String('notes') else null end; -- Generate new settlement ID select nvl(max(settlement_id), 0) + 1 into l_settlement_id from pame_settlement_demo; -- Insert new settlement record insert into pame_settlement_demo ( settlement_id, claim_number, policy_number, policy_type, incident_date, incident_description, claimant_first_name, claimant_last_name, claimant_email, claimant_phone, insured_first_name, insured_last_name, settlement_date, settlement_amount, currency_code, settlement_status, notes ) values ( l_settlement_id, l_claim_number, l_policy_number, l_policy_type, l_incident_date, l_incident_desc, l_claimant_fname, l_claimant_lname, l_claimant_email, l_claimant_phone, l_insured_fname, l_insured_lname, sysdate, -- settlement_date defaults to current date l_settlement_amount, l_currency_code, 'Proposed', -- initial status is always 'Proposed' l_notes ); l_result := '{"status": "success", "message": "Settlement created successfully", "settlement_id": ' || l_settlement_id || ', "claim_number": "' || l_claim_number || '"}'; return l_result; exception when dup_val_on_index then return '{"status": "error", "message": "Claim number already exists: ' || l_claim_number || '"}'; when others then return '{"status": "error", "message": "Database error: ' || sqlerrm || '", "backtrace": "' || sys.dbms_utility.format_error_backtrace || '"}'; end create_new_settlement; ``` 我们的插件确实需要用户的姓名信息。由于用户可能忘记在电子邮件中填写这些信息,我们添加了另一个工具,可以根据提供的电子邮件地址获取用户信息: ```sql declare l_schema json_object_t; l_tool_id uc_ai_tools.id%type; begin l_schema := json_object_t.parse('{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Get User Info", "description": "Retrieve user information (name, phone, etc.) by email address (insensitive). The parameter email is required.", "properties": { "email": { "type": "string", "description": "Email address of the user to lookup" } }, "required": [ "email" ] }'); -- Create the tool l_tool_id := uc_ai_tools_api.create_tool_from_schema( p_tool_code => 'PAME_GET_USER_INFO', p_description => 'Get user information (name, phone, etc.) by email address (insensitive)', p_function_call => 'return pame_pkg.get_user_info(:parameters);', p_json_schema => l_schema, p_tags => apex_t_varchar2('pame', 'pame_get_user_info') ); commit; end; / ``` 底层功能比较简单;我们需要检查的输入较少,只需构建一个包含客户数据的 JSON 对象并返回即可。 **创建新的结算功能** ```sql function get_user_info(p_email_data in clob) return clob as l_email_data json_object_t; l_email varchar2(255 char); l_user_record pame_users%rowtype; l_result clob; begin BEGIN l_email_data := json_object_t.parse(p_email_data); EXCEPTION when others then return '{"status": "error", "message": "Invalid JSON input: ' || sqlerrm || '", "backtrace": "' || sys.dbms_utility.format_error_backtrace || '"}'; END; -- Validate required field if not l_email_data.has('email') then return '{"status": "error", "message": "Missing required field: email"}'; end if; -- Extract email from JSON l_email := l_email_data.get_String('email'); -- Validate email format (basic check) if l_email is null or length(trim(l_email)) = 0 then return '{"status": "error", "message": "Email cannot be empty"}'; end if; if instr(l_email, '@') = 0 then return '{"status": "error", "message": "Invalid email format"}'; end if; -- Query user by email begin select user_id, first_name, last_name, email, phone, created_at, updated_at into l_user_record.user_id, l_user_record.first_name, l_user_record.last_name, l_user_record.email, l_user_record.phone, l_user_record.created_at, l_user_record.updated_at from pame_users where lower(email) = lower(l_email); -- Build success response with user data l_result := '{"status": "success", "user": {' || '"user_id": "' || l_user_record.user_id || '",' || '"first_name": "' || l_user_record.first_name || '",' || '"last_name": "' || l_user_record.last_name || '",' || '"email": "' || l_user_record.email || '",' || '"phone": "' || nvl(l_user_record.phone, 'null') || '",' || '"created_at": "' || to_char(l_user_record.created_at, 'YYYY-MM-DD"T"HH24:MI:SS') || '",' || '"updated_at": "' || to_char(l_user_record.updated_at, 'YYYY-MM-DD"T"HH24:MI:SS') || '"' || '}}'; exception when no_data_found then l_result := '{"status": "error", "message": "No user found with email: ' || l_email || '"}'; when too_many_rows then l_result := '{"status": "error", "message": "Multiple users found with email: ' || l_email || ' (data integrity issue)"}'; when others then l_result := '{"status": "error", "message": "Database error in user lookup: ' || sqlerrm || '", "backtrace": "' || sys.dbms_utility.format_error_backtrace || '"}'; end; return l_result; exception when others then return '{"status": "error", "message": "Database error: ' || sqlerrm || '", "backtrace": "' || sys.dbms_utility.format_error_backtrace || '"}'; end get_user_info; ``` **现在,为了测试一下,我们将使用这封故意不包含任何客户姓名的电子邮件。** ```plaintext Subject: Car accident Sender: apex_fan@example.com I hope this email finds you well. I’m writing to provide details regarding the settlement for the bicycle accident I was involved in. The accident occurred on 2025-08-22. I was cycling home when a pedestrian unexpectedly stepped into the bike lane, causing me to swerve and fall. Thankfully, I only sustained minor scrapes and bruises, but there was damage to the front wheel and handlebars of my bicycle. I’d like to proceed with the settlement process. The estimated repair costs came to around €350, which I believe should be covered under my policy. Please let me know if you need any additional information from my end. Thank you for your assistance. ``` 当我使用 `**GPT-4o**` 并仅输入系统提示“请根据提供的用户输入创建结算方案”来调用它时,观察其运行过程很有意思。以下 JSON 显示了对话消息,这些消息也包含在 `**generate_text**` 方法的 `**messages**` 键中。 首先,该工具会立即尝试调用这两个工具,但没有任何输入。不知何故,OpenAI 有时会忽略我们已定义的必需参数。因此,它会收到关于缺少参数的错误信息。然后,它再次尝试调用这些工具,这次正确地为客户信息工具传递了电子邮件地址。有了这些信息,它就能正确调用创建事件工具,从而在数据库中创建一条记录。 **GPT-4o 消息链及其工具调用** ```js [ { "role": "system", "content": "Please create a settlement from the provided user input." }, { "role": "user", "content": [ { "type": "text", "text": "Subject: Car accident\r\nSender: apex_fan@example.com\r\n\r\nI hope this email finds you well. I’m writing to provide details regarding the settlement for the bicycle accident I was involved in.\r\n\r\nThe accident occurred on 2025-08-22. I was cycling home when a pedestrian unexpectedly stepped into the bike lane, causing me to swerve and fall. Thankfully, I only sustained minor scrapes and bruises, but there was damage to the front wheel and handlebars of my bicycle.\r\n\r\nI’d like to proceed with the settlement process. The estimated repair costs came to around €350, which I believe should be covered under my policy. Please let me know if you need any additional information from my end.\r\n\r\nThank you for your assistance." } ] }, { "role": "assistant", "content": [ { "type": "tool_call", "toolCallId": "call_ry3uRa8PaFdq3xjLKmjWtzPF", "toolName": "PAME_GET_USER_INFO", "args": "{}" }, { "type": "tool_call", "toolCallId": "call_x6zibhtANPRw5s5OQLbZK1Sg", "toolName": "PAME_CREATE_SETTLEMENT", "args": "{}" } ] }, { "role": "tool", "content": [ { "type": "tool_result", "toolCallId": "call_ry3uRa8PaFdq3xjLKmjWtzPF", "toolName": "PAME_GET_USER_INFO", "result": "{\"status\": \"error\", \"message\": \"Missing required field: email\"}" }, { "type": "tool_result", "toolCallId": "call_x6zibhtANPRw5s5OQLbZK1Sg", "toolName": "PAME_CREATE_SETTLEMENT", "result": "{\"status\": \"error\", \"message\": \"Missing required field: incident_date\"}" } ] }, { "role": "assistant", "content": [ { "type": "tool_call", "toolCallId": "call_p4VnV3jzeUsZeqOdX6J6W4mC", "toolName": "PAME_GET_USER_INFO", "args": "{\"email\": \"apex_fan@example.com\"}" }, { "type": "tool_call", "toolCallId": "call_q2rHAEDiMrffuDylnEyGZTVc", "toolName": "PAME_CREATE_SETTLEMENT", "args": "{\"incident_date\": \"2025-08-22\"}" } ] }, { "role": "tool", "content": [ { "type": "tool_result", "toolCallId": "call_p4VnV3jzeUsZeqOdX6J6W4mC", "toolName": "PAME_GET_USER_INFO", "result": "{\"status\": \"success\", \"user\": {\"user_id\": \"user2\",\"first_name\": \"Bob\",\"last_name\": \"Johnson\",\"email\": \"apex_fan@example.com\",\"phone\": \"234-567-8901\",\"created_at\": \"2025-09-05T15:47:33\",\"updated_at\": \"2025-09-05T15:47:33\"}}" }, { "type": "tool_result", "toolCallId": "call_q2rHAEDiMrffuDylnEyGZTVc", "toolName": "PAME_CREATE_SETTLEMENT", "result": "{\"status\": \"error\", \"message\": \"Missing required field: claimant_first_name\"}" } ] }, { "role": "assistant", "content": [ { "type": "tool_call", "toolCallId": "call_8kV8NV6mHCktYgDhIKOZoEgT", "toolName": "PAME_CREATE_SETTLEMENT", "args": "{\"claimant_first_name\":\"Bob\",\"claimant_last_name\":\"Johnson\",\"incident_date\":\"2025-08-22\"}" } ] }, { "role": "tool", "content": [ { "type": "tool_result", "toolCallId": "call_8kV8NV6mHCktYgDhIKOZoEgT", "toolName": "PAME_CREATE_SETTLEMENT", "result": "{\"status\": \"success\", \"message\": \"Settlement created successfully\", \"settlement_id\": 112, \"claim_number\": \"CL2025-714988-AI\"}" } ] }, { "role": "assistant", "content": [ { "type": "text", "text": "The settlement for the bicycle accident on 2025-08-22 has been created successfully. Here are the details:\n\n- **Claimant Name**: Bob Johnson\n- **Claim Number**: CL2025-714988-AI\n- **Settlement ID**: 112\n\nPlease let me know if there is anything else you need assistance with, or if there are any further details required for the process.", "providerOptions": { "refusal": null, "annotations": [] } } ] } ] ``` 但请注意,它实际上只填充了必填字段。即使输入内容包含更多细节,它也不会在参数中填充这些细节。我们可以通过提供更清晰的提示来优化它,使其更仔细地规划步骤,并确保提供尽可能多的信息。此外,使用推理模型也有助于提升性能。 像 `**GPT-5**` 这样的更现代的模型无需改进就能做得更好,但有趣的是,Anthropic 最小的上一代模型 `**Haiku 3.5**` 甚至超越了 GPT-5,因为它始终采用最直接的方案,即先获取用户信息,然后再进行插入。 **Haiku 3.5 消息链与工具调用** ```js [ { "role": "system", "content": "Please create a settlement from the provided user input." }, { "role": "user", "content": [ { "type": "text", "text": "Subject: Car accident\r\nSender: apex_fan@example.com\r\n\r\nI hope this email finds you well. I’m writing to provide details regarding the settlement for the bicycle accident I was involved in.\r\n\r\nThe accident occurred on 2025-08-22. I was cycling home when a pedestrian unexpectedly stepped into the bike lane, causing me to swerve and fall. Thankfully, I only sustained minor scrapes and bruises, but there was damage to the front wheel and handlebars of my bicycle.\r\n\r\nI’d like to proceed with the settlement process. The estimated repair costs came to around €350, which I believe should be covered under my policy. Please let me know if you need any additional information from my end.\r\n\r\nThank you for your assistance." } ] }, { "role": "assistant", "content": [ { "type": "text", "text": "I'll help you create a settlement for this bicycle accident. First, I'll retrieve the user's information using their email address, and then I'll create the settlement.", "providerOptions": {} }, { "type": "tool_call", "toolCallId": "toolu_017mN3JWFaGBYuWZmJgyLSK2", "toolName": "PAME_GET_USER_INFO", "args": "{\"email\":\"apex_fan@example.com\"}" } ] }, { "role": "tool", "content": [ { "type": "tool_result", "toolCallId": "toolu_017mN3JWFaGBYuWZmJgyLSK2", "toolName": "PAME_GET_USER_INFO", "result": "{\"status\": \"success\", \"user\": {\"user_id\": \"user2\",\"first_name\": \"Bob\",\"last_name\": \"Johnson\",\"email\": \"apex_fan@example.com\",\"phone\": \"234-567-8901\",\"created_at\": \"2025-09-05T15:47:33\",\"updated_at\": \"2025-09-05T15:47:33\"}}" } ] }, { "role": "assistant", "content": [ { "type": "text", "text": "Now, I'll create the settlement using the retrieved information and the details from the email:", "providerOptions": {} }, { "type": "tool_call", "toolCallId": "toolu_01LK98bf8BG97NVWzP29KLZD", "toolName": "PAME_CREATE_SETTLEMENT", "args": "{\"claimant_first_name\":\"Bob\",\"claimant_last_name\":\"Johnson\",\"claimant_email\":\"apex_fan@example.com\",\"claimant_phone\":\"234-567-8901\",\"incident_date\":\"2025-08-22\",\"incident_description\":\"Cyclist swerved and fell after a pedestrian unexpectedly stepped into the bike lane, resulting in damage to bicycle front wheel and handlebars\",\"policy_type\":\"Personal Liability\",\"settlement_amount\":350,\"currency_code\":\"EUR\"}" } ] }, { "role": "tool", "content": [ { "type": "tool_result", "toolCallId": "toolu_01LK98bf8BG97NVWzP29KLZD", "toolName": "PAME_CREATE_SETTLEMENT", "result": "{\"status\": \"success\", \"message\": \"Settlement created successfully\", \"settlement_id\": 113, \"claim_number\": \"CL2025-356222-AI\"}" } ] }, { "role": "assistant", "content": [ { "type": "text", "text": "I've processed the settlement claim with the following details:\n- Claimant: Bob Johnson\n- Email: apex_fan@example.com\n- Phone: 234-567-8901\n- Incident Date: 2025-08-22\n- Incident Description: Cyclist swerved and fell after a pedestrian unexpectedly stepped into the bike lane, resulting in damage to bicycle front wheel and handlebars\n- Policy Type: Personal Liability\n- Settlement Amount: €350\n- Claim Number: CL2025-356222-AI\n\nThe settlement has been created successfully in the system. The claimant can expect further communication regarding the claim processing and reimbursement.", "providerOptions": {} } ] } ] ``` ## [**Oracle 的人工智能成功案例**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#successful-ai-in-oracle) 仅仅使用人工智能并不能带来成功。 ### [**现状与挑战**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#current-state-and-challenges) 尽管人工智能备受追捧, [但麻省理工学院的研究表明,企业中 95%的生成式人工智能试点项目都以失败告终](https://www.forbes.com/sites/jasonsnyder/2025/08/26/mit-finds-95-of-genai-pilots-fail-because-companies-avoid-friction/) 。主要原因如下: 1. 缺乏清晰的使用案例:不要构建花哨的演示(就像我在这里做的那样);解决实际问题。 2. 集成度差:过于关注面向客户的功能,而忽视了后台自动化。 3. 缺乏变革:项目面临阻力,未能进行必要的流程重新设计。 至关重要的是,我们认识到后台自动化对我们数据工程师的影响最大。现在,我们可以构建自动化流程,推动您的业务迈入人工智能时代 。我认为,有了这里阐述的原则,您就可以很好地利用现有的 SQL 和 PL/SQL 技能,并在此基础上学习一些 LLM 知识。 ### [**自动化**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#automations) 因此,请运用上述原则构建能够自主帮助您管理数据的工作流程。为此,您可以使用 1. DBMS\_SCHEDULER 用于自动化作业 2. 用于视觉 AI 流程设计的 APEX 流程或 APEX 工作流 但也要密切关注接下来会发生什么。 ### [**UC AI 路线图**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#uc-ai-roadmap) 计划中的改进包括: * 用于自主多步骤工作流程的智能体人工智能 * 语音转文本和文本转语音功能 * 图像生成集成 * 支持向量嵌入 (需要 23ai) * 实时应用的流式响应 * 为您的应用程序提供用于拖放式 AI 集成的 APEX 插件 我认为,人工智能数据工程学习链条的下一步是智能体人工智能。我目前正在深入研究如何在加州大学人工智能中实现它。智能体可以完成更复杂的任务(例如编写智能体代码或进行深度研究),但也需要更优秀的提示和微调技能。 ## [**UC AI 入门**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#getting-started-with-uc-ai) 如果您也想开始使用 UC AI,请按照[安装指南](https://www.united-codes.com/products/uc-ai/docs/guides/installation/)进行操作。 请记住,它是[免费开源软件](https://github.com/United-Codes/uc_ai) 。这个项目很大程度上源于我的个人热情。如果您使用愉快,请考虑与我分享您的使用体验,或者至少在 [GitHub](https://github.com/United-Codes/uc_ai) 上给我们点个赞。我也非常欢迎任何人为本项目做出贡献。即使是改进文档、报告 bug 或提出新想法,都能帮助项目保持发展势头。 UC AI 之所以能够实现,是因为我在 [United Codes](https://www.united-codes.com/) 有时间进行开发。如果您想以其他方式支持这个项目,请考虑使用我们的产品,例如 AOP,或者与我们进行咨询合作。我们也可以协助您制定 AI 战略。 ## [**结论**](https://hartenfeller.dev/blog/real-ai-solutions-oracle-plsql#conclusion) 人工智能的热潮不仅仅体现在聊天机器人和面向客户的界面上,它更关乎我们处理和管理数据方式的变革。作为 Oracle 的数据工程师,你们拥有得天独厚的优势,能够充分把握这一机遇。 本文探讨了如何利用您现有的 PL/SQL 技能来实现人工智能。从生成结构化输出和分析文件,到实现推理和工具调用,这些技术代表了能够立即带来业务价值的实用自动化方法。 需要记住的关键要点: * 重点关注人工智能优势明显且阻力较小的后台自动化领域。 * 充分利用你现有的技能——如果你掌握了 SQL 和 PL/SQL,你就已经成功了 90%。 * 从清晰的使用案例入手——解决实际问题,而不是炫目的演示。 * 迭代改进——人工智能是一种需要精心实施才能不断完善的工具。 UC AI SDK 为您奠定了基础,让您立即开始实验。无论您是想实现文档处理自动化、启用智能数据分析,还是构建复杂的工作流程,所需的工具都已准备就绪,现在正是好时机。