兰 亭 墨 苑
期货 · 量化 · AI · 终身学习
首页
归档
编辑文章
标题 *
URL 别名 *
内容 *
(支持 Markdown 格式)
这是一个非常棒的例子,它完美地展示了现代大语言模型(LLM)最强大的能力之一:**函数调用(Function Calling)或工具使用(Tool Use)**。 我们来详细拆解一下这个过程,特别是它如何以及为何会涉及**多次**对 Gemini API 的请求。 ### 核心思想:从“聊天”到“智能代理” 首先要理解,这段代码构建的不仅仅是一个简单的问答机器人。它构建了一个**智能代理(Intelligent Agent)**。你可以把它想象成一个工作流程: * **你 (User):** 提出一个复杂的需求。 * **你的代码 (Manager):** 扮演“项目经理”的角色。它不直接干活,但它知道手下有哪些专家(工具)。 * **Gemini AI (Assistant):** 扮演一个“聪明的助理”。它能理解你的需求,但它自己不会查价格或画图。当它发现需要这些专业技能时,它会向“项目经理”提出请求。 * **`get_price`, `get_news`, `draw_chart` (Tools):** 扮演“领域专家”。它们是真正执行具体任务的函数。 这个流程的核心就是**“对话”**——不仅是用户和AI的对话,更是**你的代码和AI之间的多轮对话**。 --- ### API 请求次数分析 你的代码与 Gemini API 的交互次数取决于用户问题的复杂性。 #### 场景一:简单问题(仅需 1 次 API 请求) 假设用户问了一个不需要使用任何工具的简单问题: > **用户:** "你好,你叫什么名字?" 流程如下: 1. `getGeminiChatAnswer` 函数被调用,`question` 是 "你好,你叫什么名字?"。 2. 代码构建 `contents`,并将问题和 `tools`(工具菜单)一起打包。 3. **第1次 API 请求:** 代码向 Gemini 发送请求,问:“对于这个问题‘你好,你叫什么名字?’,结合你拥有的这些工具,你应该如何回答?” 4. **API 响应:** Gemini 分析后发现,回答这个问题根本不需要任何工具。于是它直接生成文本回复,例如 `"我是一个由 Google 训练的大型语言模型。"` 5. 代码收到响应,检查 `part.text` 字段,发现里面有文本。 6. 代码直接 `return part.text;`,函数执行结束。 **总计:1 次 API 请求。** --- #### 场景二:复杂问题(涉及多次 API 请求) 现在,我们来看一个更复杂的、需要调用工具的问题,这将触发多次API请求。 > **用户:** "帮我查一下螺纹钢的价格,并画出它的日K线图。" 这个需求需要调用两个工具:`get_price` 和 `draw_chart`。 **整个工作流程(代码与AI的“对话”)如下:** **第1轮:用户向AI提问** 1. **第1次 API 请求 (代码 -> Gemini):** * 你的代码将用户的请求("查螺纹钢价格并画日K线图")和它拥有的工具列表(`get_price`, `get_news`, `draw_chart`)一起发送给 Gemini。 * 这相当于在问AI:“嘿,用户有这个需求,看看你的工具箱,你觉得第一步该做什么?” 2. **第1次 API 响应 (Gemini -> 代码):** * Gemini 分析后,决定第一步是获取价格。它**不会返回文本**,而是返回一个 `functionCall` 对象,类似这样: ```json { "functionCall": { "name": "get_price", "args": { "symbol": "rb" } } } ``` * 这相当于AI在对你的代码说:“好的,经理。请帮我调用 `get_price` 工具,参数是 `symbol: 'rb'`。” **第2轮:代码执行工具,并向AI汇报结果** 3. **本地函数执行 (在你的服务器上):** * 你的代码接收到这个 `functionCall` 请求。 * 它在 `if (part.functionCall)` 分支里,调用本地的 `get_price("rb")` 函数。 * 假设函数执行后返回了结果:`"螺纹钢最新价是3650元,今日涨跌幅为-0.5%..."`。 4. **第2次 API 请求 (代码 -> Gemini):** * 你的代码**不会**把这个结果直接给用户。它需要回去向AI“汇报”。 * 它将上一步AI的调用请求 (`functionCall`) 和刚刚得到的函数结果 (`functionResponse`) 一起打包,再次发送给 Gemini。 * 这相当于在对AI说:“助理,你要的价格数据我拿到了,给你:‘螺纹钢最新价是3650元...’。现在,基于这个新信息和用户的原始问题,下一步你打算做什么?” 5. **第2次 API 响应 (Gemini -> 代码):** * Gemini 收到价格信息后,回顾用户的原始问题,发现还有一个任务没完成:“画出它的日K线图”。 * 于是,它再次返回一个 `functionCall` 对象: ```json { "functionCall": { "name": "draw_chart", "args": { "symbol": "rb", "period": "daily" } } } ``` * 这相当于AI说:“谢谢经理。现在请再帮我调用 `draw_chart` 工具,参数是 `symbol: 'rb'` 和 `period: 'daily'`。” **第3轮:代码执行第二个工具,并最终形成答案** 6. **本地函数执行 (在你的服务器上):** * 你的代码再次进入 `if (part.functionCall)` 分支,调用本地的 `draw_chart(env, "rb", "daily")` 函数。 * 假设函数返回一个图表图片的URL:`"https://charts.example.com/rb_daily.png"`。 7. **第3次 API 请求 (代码 -> Gemini):** * 代码再次向AI“汇报”,把第二个工具的执行结果发给 Gemini。 * 这相当于说:“助理,你要的K线图也准备好了,链接是 `https://...`。现在所有信息都齐了,请你总结一下,给用户一个最终的、完整的答复。” 8. **第3次 API 响应 (Gemini -> 代码):** * 此时,Gemini 拥有了所有它需要的信息(价格文本和图表链接)。 * 它不再需要调用任何工具,于是生成最终的、用户友好的文本回复。这次响应的 `part` 里将包含 `text` 字段: ```json { "text": "好的,螺纹钢(rb)的最新价格是3650元,今日涨跌幅为-0.5%。这是它的日K线图:https://charts.example.com/rb_daily.png" } ``` 9. **流程结束:** * 你的代码收到这个包含 `text` 的响应,进入 `else if (part.text)` 分支。 * 它将这段文本返回给最终用户,`while` 循环结束。 **总计:3 次 API 请求。** ### 总结 * **`while` 循环** 是实现这个多轮“代码-AI”对话的关键。它允许AI一步一步地思考和收集信息,直到它认为信息足够了,才生成最终答案。 * **`contents` 数组** 就像是这次任务的“会议纪要”,它记录了整个对话历史,包括用户的原始问题、AI的每次工具调用请求以及每次工具调用的结果。这保证了AI在后续步骤中不会“忘记”之前发生了什么。 * **API请求次数 = 1 (初始问题) + N (N次工具调用)**。每次AI需要一个工具时,就会增加一次API往返。
配图 (可多选)
选择新图片文件或拖拽到此处
标签
更新文章
删除文章