拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Generative AI for Beginners:第 11 课实战解析——用 Azure OpenAI Function Calling 为教育推荐聊天机器人接入外部课程数据

Generative AI for Beginners第 11 课实战解析——用 Azure OpenAI Function Calling 为教育推荐聊天机器人接入外部课程数据【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners本篇文章以开源课程仓库 generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI第 11 课《与函数调用集成Integrating with function calling》为基础系统讲解 Azure OpenAI Function Calling函数调用的动机、原理与端到端集成方法。你将掌握函数调用如何解决 LLM 输出格式不稳定与无法访问外部数据两大痛点、如何用 JSON Schema 声明函数并以auto模式让模型自主选择函数与参数、以及如何在真实应用中通过第二次补全请求把外部 API 结果转成自然语言推荐。文中所有示例均可结合本仓库的 Notebook、Python、JavaScript 与 TypeScript 配套代码动手运行。课程场景让教育类聊天机器人具备找课程能力第 11 课面向课程中反复出现的一家教育初创公司目标是为用户提供一个聊天机器人它能根据用户的技能水平skill level、**当前角色current role与感兴趣的技术technology of interest**推荐合适的 IT 课程。为实现这一场景该课组合使用了三类组件Azure OpenAI为用户提供对话体验Microsoft Learn Catalog API根据用户请求帮助其查找课程训练模块Function Calling把用户查询转化为函数调用参数由函数代替用户发起真实的外部 API 请求。要理解为什么要引入函数调用必须先看没有它时的两个根本性限制。为什么需要函数调用一致格式与外部数据在函数调用能力出现之前LLM 存在两个明显短板响应非结构化且不稳定开发者必须编写复杂的校验代码才能兜住模型输出的每一种格式变体模型知识受训练时间点限制模型无法回答斯德哥尔摩现在天气如何这类需要实时数据的问题因为其内部参数只固化到训练数据截止的某一时刻。Function Calling 正是 Azure OpenAI 服务为克服上述限制提供的能力它带来两个核心收益一致的响应格式Consistent response format能更好地控制输出结构从而把模型响应更平滑地集成进下游系统外部数据接入External data可以在对话上下文中使用应用其他来源的数据。需要特别强调函数调用并不是让 LLM 自己去调用或执行某个函数而是为模型的输出定义一套结构约束应用拿到这份结构化输出后才知道该调用自己的哪个函数真正执行仍然发生在你的程序里。用学生信息抽取场景看懂非结构化输出问题原文建议直接使用配套 Notebook 运行下面的场景见 翻译版配套 Notebook 或主仓库 python/aoai-assignment.ipynb也可跟随阅读。假设我们要建立一张学生数据表以便推荐合适课程先准备两个信息高度相似的学生描述1. 建立 Azure OpenAI 连接import os import json from openai import AzureOpenAI from dotenv import load_dotenv load_dotenv() client AzureOpenAI( api_keyos.environ[AZURE_OPENAI_API_KEY], # 亦为默认值可省略 api_version 2023-07-01-preview ) deployment os.environ[AZURE_OPENAI_DEPLOYMENT]AZURE_OPENAI_API_KEY、AZURE_OPENAI_DEPLOYMENT等密钥信息通常放在.env文件中通过load_dotenv()载入。注意本课翻译版示例运行在 Chat Completions 风格的旧接口之上主仓库英文版已迁移到 Azure OpenAI Responses API 的 v1 端点base_url f{endpoint.rstrip(/)}/openai/v1/两种风格的差异与迁移将在下文API 风格演进小节展开。2. 构造两条相似的学生描述student_1_description Emily Johnson is a sophomore majoring in computer science at Duke University. She has a 3.7 GPA. Emily is an active member of the universitys Chess Club and Debate Team. She hopes to pursue a career in software engineering after graduating. student_2_description Michael Lee is a sophomore majoring in computer science at Stanford University. He has a 3.8 GPA. Michael is known for his programming skills and is an active member of the universitys Robotics Club. He hopes to pursue a career in artificial intelligence after finishing his studies.我们希望把这两段文本交给 LLM 解析把结果存入数据库或继续传给下游 API。3. 构造两条内容完全一致的抽取 Promptprompt1 f Please extract the following information from the given text and return it as a JSON object: name major school grades club This is the body of text to extract the information from: {student_1_description} prompt2 f Please extract the following information from the given text and return it as a JSON object: name major school grades club This is the body of text to extract the information from: {student_2_description} Prompt 明确要求模型抽取指定字段并以 JSON 对象返回。4. 发送请求并读取响应把 Prompt 放入messages、角色设为user以模拟用户在聊天机器人中输入的文本# response from prompt one openai_response1 client.chat.completions.create( modeldeployment, messages [{role: user, content: prompt1}] ) openai_response1.choices[0].message.content # response from prompt two openai_response2 client.chat.completions.create( modeldeployment, messages [{role: user, content: prompt2}] ) openai_response2.choices[0].message.content通过openai_response1[choices][0][message][content]即可查看模型原始输出。5. 用 json.loads 解析响应# Loading the response as a JSON object json_response1 json.loads(openai_response1.choices[0].message.content) json_response1响应 1{ name: Emily Johnson, major: computer science, school: Duke University, grades: 3.7, club: Chess Club }响应 2{ name: Michael Lee, major: computer science, school: Stanford University, grades: 3.8 GPA, club: Robotics Club }问题暴露尽管两条 Prompt 相同、描述文本也高度相似grades字段的取值却出现3.7与3.8 GPA两种格式。原因在于 LLM 接收的是提示词这类非结构化输入返回的同样是非结构化数据一旦需要落库或交给下游程序这种不确定性就无法接受。函数调用如何解决上述问题函数调用通过为响应提供结构约束来解决问题。流程可概括为下图所示的闭环应用拿到函数返回结果后再把它回传给 LLMLLM 最终用自然语言回应用户查询——这正是下一节要构建的完整集成。函数调用的典型使用场景除本课的课程搜索外函数调用可用于显著增强应用能力的多种场景调用外部工具Calling External Tools聊天机器人擅长答疑但借助函数调用可将用户消息转化为具体动作。例如学生说给我的导师发封邮件说这个知识点我需要更多帮助即可触发一次send_email(to: string, body: string)调用。生成 API 或数据库查询Create API or Database Queries把自然语言转换为格式化的查询或 API 请求。例如教师提问哪些学生完成了上次作业可映射为函数get_completed(student_name: string, assignment: int, current_status: string)。构造结构化数据Creating Structured Data用户丢入一段文本或 CSV让 LLM 抽取重要信息。例如把关于和平协议的维基百科文章转换成 AI 闪卡可定义函数get_important_facts(agreement_name: string, date_signed: string, parties_involved: list)。创建第一个函数调用三大步骤一次完整的函数调用包含三个主要步骤调用Calling携带函数列表与用户消息调用 Chat Completions API读取Reading读取模型响应以决定执行哪个动作即执行某个函数或发起一次 API 请求回传Making把函数执行结果再次提交给 Chat Completions API让模型据此生成面向用户的最终回答。这三步循环的静态结构用户消息、LLM 引擎、函数声明与参数可参看下图步骤 1创建消息第一步是构造一条用户消息。可以从文本框动态取值也可以直接硬编码。首次使用 Chat Completions API 时需要明确每条消息的role与content。role可以是system设定规则、assistant代表模型或user最终用户。函数调用场景下用户问题按如下方式放入messages [ {role: user, content: Find me a good course for a beginner student to learn Azure.} ]为不同消息赋予不同角色能让 LLM 分清哪句是系统说的、哪句是用户说的从而构建起可供模型续写的对话历史。步骤 2创建函数声明 参数接下来定义函数及其参数。本课只使用一个search_courses函数实际可以声明多个。重要提示这些函数声明会被放入发送给 LLM 的系统消息中因此会占用你可用的 token 额度。函数越多、参数描述越长消耗的输入 token 越多。函数以数组形式组织每个元素代表一个函数包含name、description与parameters三个主要属性functions [ { name:search_courses, description:Retrieves courses from the search index based on the parameters provided, parameters:{ type:object, properties:{ role:{ type:string, description:The role of the learner (i.e. developer, data scientist, student, etc.) }, product:{ type:string, description:The product that the lesson is covering (i.e. Azure, Power BI, etc.) }, level:{ type:string, description:The level of experience the learner has prior to taking the course (i.e. beginner, intermediate, advanced) } }, required:[ role ] } } ]逐项拆解函数结构name希望模型去调用的函数名将在响应中回传description函数行为说明描述越具体、越清晰模型选择与填参越准确parameters希望模型在响应中产出的字段与格式由以下子属性构成type参数对象的整体数据类型此处为objectproperties模型在输出中会使用的具体字段清单每个字段又包含字段键名如product模型在格式化响应中使用的属性名type字段数据类型如stringdescription对该字段取值的解释另有可选的required数组声明哪些字段是完成函数调用所必需的。步骤 3发起带函数声明的请求让模型自主选择定义好函数后把它通过functions参数挂到请求上同时设置function_callauto。auto意味着不再由我们硬性指定函数而是由 LLM 依据用户消息自行判断该调用哪个函数response client.chat.completions.create( modeldeployment, messagesmessages, functionsfunctions, function_callauto) print(response.choices[0].message)此时模型返回的结构大致如下{ role: assistant, function_call: { name: search_courses, arguments: {\n \role\: \student\,\n \product\: \Azure\,\n \level\: \beginner\\n} } }可以看到模型选择了search_courses并在arguments中给出了该函数的实参。回看用户消息messages [ {role: user, content: Find me a good course for a beginner student to learn Azure.} ]student、Azure、beginner正是从这条消息中抽取出来并填入函数参数的。这种用法既能从提示词中提取结构化信息也为 LLM 提供了可复用的功能边界。把函数调用集成进真实应用验证完模型的结构化响应后把它接入应用。核心是管理好整个调用流flow。第 1 步保存模型响应消息response_message response.choices[0].message第 2 步实现与声明对应的真实 Python 函数接下来编写真正会执行外部 API 请求的 Python 函数。注意函数名必须与functions变量中声明的名字一一对应import requests def search_courses(role, product, level): url https://learn.microsoft.com/api/catalog/ params { role: role, product: product, level: level } response requests.get(url, paramsparams) modules response.json()[modules] results [] for module in modules[:5]: title module[title] url module[url] results.append({title: title, url: url}) return str(results)这里对 Microsoft Learn Catalog API 发起真实请求、搜索培训模块并把前 5 条结果标题 链接拼成字符串返回。第 3 步检查响应、映射函数并执行函数声明functions与 Python 实现函数是两个东西需要一种映射把它们关联起来。做法是检查 LLM 响应里是否带有function_call若有则从函数名 → Python 函数的映射字典中取出对应实现并调用# Check if the model wants to call a function if response_message.function_call.name: print(Recommended Function call:) print(response_message.function_call.name) print() # Call the function. function_name response_message.function_call.name available_functions { search_courses: search_courses, } function_to_call available_functions[function_name] function_args json.loads(response_message.function_call.arguments) function_response function_to_call(**function_args) print(Output of function call:) print(function_response) print(type(function_response)) # Add the assistant response and function response to the messages messages.append( # adding assistant response to messages { role: response_message.role, function_call: { name: function_name, arguments: response_message.function_call.arguments, }, content: None } ) messages.append( # adding function response to messages { role: function, name: function_name, content: function_response, } )其中最关键的三行完成了取函数名 → 解析参数 → 执行调用function_to_call available_functions[function_name] function_args json.loads(response_message.function_call.arguments) function_response function_to_call(**function_args)随后把助手侧的函数调用声明role: assistantfunction_call与函数执行结果role: function都追加进messages形成完整的多轮上下文。这样第二次请求时模型才能看到函数返回的数据。本课 Python 实现的执行输出大致如下示例Recommended Function call: { name: search_courses, arguments: {\n \role\: \student\,\n \product\: \Azure\,\n \level\: \beginner\\n} } Output of function call: [{title: Describe concepts of cryptography, url: https://learn.microsoft.com/training/modules/describe-concepts-of-cryptography/}, ...] class str第 4 步第二次请求把函数结果变成自然语言推荐最后把更新后的messages再次发给模型让它把结构化的课程数据组织成用户可读的自然语言回答print(Messages in next request:) print(messages) print() second_response client.chat.completions.create( messagesmessages, modeldeployment, function_callauto, functionsfunctions, temperature0 ) # get a new response from GPT where it can see the function response print(second_response.choices[0].message)输出节选{ role: assistant, content: I found some good courses for beginner students to learn Azure:\n\n1. Describe concepts of cryptography\n2. Introduction to audio classification with TensorFlow\n3. ...\n5. Set up the Rust development environment\n\nYou can click on the links to access the courses. }这里把temperature0是为了降低二次生成的随机性让回复更聚焦于函数返回的事实。至此用户 → 模型选函数 → 应用执行外部 API → 结果回传 → 模型自然语言作答的完整闭环就打通了。仓库配套实现JavaScript 与 TypeScript 样本中的工程化细节除本课主示例外仓库 11-integrating-with-function-calling 还提供了多语言实现可印证同一套思路在实际工程中的落地方式与安全要求js-githubmodels/app.js基于 Azure AI Inference SDKazure-rest/ai-inference调用/chat/completions声明getFlightInfo、getHotelInfo两个查航班/查酒店工具默认模型gpt-4o-mini。它重点演示了两类工程细节一是通过finish_reason tool_calls判断模型是否请求工具并把结果以role: tool与tool_call_id回填上下文二是安全实践——调用前先用Object.prototype.hasOwnProperty.call(namesToFunctions, functionName)校验函数名是否在白名单内防止模型幻觉出未注册函数同时用try/catch包裹JSON.parse以避免解析异常直接中断程序。typescript/function-app/src/main.ts使用 OpenAI SDK 的client.responses.createResponses API声明扁平化的getCurrentWeatherTool{ type, name, description, parameters }在item.type function_call时解析参数并调用findWeather去访问 Bing Maps 地理编码 API。它还展示了面向外部 API 的更完整防护校验端点必须为 HTTPS、用URLSearchParams对全部查询参数编码以防注入、为请求设置 10 秒超时、解析失败或缺少必填参数时安全跳过。这些样本与本课主流程殊途同归但揭示了函数名白名单校验、参数解析容错、外部请求超时与编码这类在把函数调用推向生产时不可或缺的护栏。API 风格演进functions 时代与 Responses API 时代的字段对照需要说明版本适用前提本课关联的西班牙语翻译版教材及其 Notebooktranslations/es撰写于较早时期使用的是 Chat Completions 风格的functions/function_call参数而主仓库的英文版教程11-integrating-with-function-calling/README.md、python 目录下的英文 Notebook 以及上述 TypeScript 样本均已迁移到Responses API / Tools 扁平格式。两者的核心思想一致仅字段命名变化对照如下旧式 Chat Completionsfunctions新式 Responses APItools说明client.chat.completions.create(...)client.responses.create(...)入口方法functionsfunctionstoolsfunctions携带函数/工具声明function_callautotool_choiceauto让模型自主决定是否、调用哪个函数response_message.function_call.name/.argumentsresponse.output中typefunction_call项name/arguments/call_id读取模型建议的函数与参数messages.append({role:assistant,function_call:{...}})messages.append(tool_call)回填助手侧函数调用项messages.append({role:function,name:...,content:...})messages.append({type:function_call_output,call_id:...,output:...})回填函数执行结果无论使用哪一代接口范式始终不变模型只负责决定并产出结构化调用参数真正的函数执行与外部数据访问永远发生在应用侧。课后任务Assignment原文在结尾布置了三项进阶练习用于加深对 Azure OpenAI Function Calling 的理解为函数增加更多参数帮助学习者找到更多合适的课程新建一个函数调用纳入更多学生侧信息例如其母语 native language为函数调用或 API 调用没有返回任何合适课程的情况编写错误处理逻辑。提示可参考 Learn Catalog API 的官方开发参考文档确认上述数据在接口中的位置与提供方式。小结与下一步至此你已经掌握了函数调用的完整知识闭环先用学生信息抽取实验直观看到非结构化输出的不可靠性再理解函数调用通过 JSON Schema 约束输出、以auto模式让模型自主选择函数最后通过声明函数 → 映射实现 → 回填上下文 → 二次请求四步把它落地为真实可用的教育课程推荐机器人。本仓库配套 Notebook、Python、JavaScript、TypeScript 多语言样本11-integrating-with-function-calling可用于对照运行。完成本课后可继续学习课程第 12 课 为 AI 应用设计用户体验探讨如何把这类具备工具调用能力的 AI 应用做成用户真正愿意使用、可解释、可反馈的产品。【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门