Ai入门-搭建一个专属的ai学习助手

在上一篇文章 AI入门-搭建一个本地聊天机器人中我们了解了 提示词与ai的聊天对话过程。这一小节我们将了解 大模型的函数调用和rag开发。

前言

既然在学习ai,那么我们能不能让ai帮助我们学ai呢?为此 决定开发一个专属的ai学习助手。作为一个初学者,我们并不知道学习ai大模型应用开发要学习一些什么,以及怎么学。为此直接通过我们搭建的聊天机器人让他给我们一份学习计划

python 复制代码
ai_helper_prompt = """
你是一名高级ai开发教练,你严谨,认证细致,幽默。你的主要职责是 
1. 帮助制定学习计划
2. 检验每日的学习成果,并针对学习效果进行评估。
3. 当学习效果较好时,给予鼓励,当学习效果 较差时 针对不足的内容提出针对性的学习方案,并在此检验直到完成
4. 能够根据实际情况动态调整学习计划与目标。
"""

大型语言模型(LLM)如GPT-4在文本生成、问答和代码编写等任务上表现出色,但仍存在一些关键限制:

  1. 知识实时性不足:LLM的训练数据是静态的,无法实时获取最新信息(如新闻、股价或学术进展)。
  2. 事实准确性有限:模型可能生成看似合理但实际错误的回答("幻觉"问题)。
  3. 缺乏外部交互能力:LLM无法直接调用API、查询数据库或执行具体操作(如订机票、查天气)。

RAG(检索增强生成)和函数调用如何解决这些问题?

🔍 RAG:动态扩展知识库

  • 原理:通过检索外部数据源(如数据库、文档或网页),将最新信息注入模型上下文,再生成回答。

  • 优势

    • 提供实时、准确的答案(例如:"2023年世界杯冠军是谁?")。
    • 减少幻觉,支持引用来源,增强可信度。
    • 适用于企业知识库、客服等场景。

⚙️ 函数调用:连接现实世界的桥梁

  • 原理:让LLM根据用户需求生成结构化请求(如JSON),触发外部工具或API(如计算、支付、数据查询)。

  • 优势

    • 执行实际任务(如"订明天北京的酒店"或"查询我的订单状态")。
    • 扩展模型能力,实现动态交互(如调用Python代码做数学计算)。
    • 提升效率,避免手动操作。

函数调用

函数调用的时序如下

对话时大模型返回的数据结构如下

json 复制代码
{
  "id": "chatcmpl-ee9c3592-a825-971f-a6a2-7324a1459ab3",
  "choices": [
    {
      "finish_reason": "tool_calls",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "",
        "refusal": null,
        "role": "assistant",
        "annotations": null,
        "audio": null,
        "function_call": null,
        "tool_calls": [
          {
            "id": "call_386cbbf1db3848e69c8460",
            "function": {
              "arguments": "{}",
              "name": "get_current_time"
            },
            "type": "function",
            "index": 0
          }
        ]
      }
    }
  ],
  "created": 1753097180,
  "model": "qwen-plus",
  "object": "chat.completion",
  "service_tier": null,
  "system_fingerprint": null,
  "usage": {
    "completion_tokens": 16,
    "prompt_tokens": 230,
    "total_tokens": 246,
    "completion_tokens_details": null,
    "prompt_tokens_details": {
      "audio_tokens": null,
      "cached_tokens": 0
    }
  }
}

具体可以参考 其中choices 是大模型回调给我们的消息。

ai教学助手 代码设计

虽然我们才接触ai大模型应用,但考虑到 后续持续的开发迭代 我们的ai教学助手,整体代码设计如下

关键解释: MessageManager: 维护管理 与整个大模型关联的消息,并将消息变更通知到外界 ChatContext:与大模型的交互调用,响应 MessageManager 消息变更。处理模型的消息回调, ToolsProviderMixIn:工具提供的抽象类,ChatContext 通过持有不同的 工具实现类,可以根据不同的业务场景告知大模型 有不同的工具 ChatModelMixIn:大模型抽象接口,ChatContext持有不同的实现可以切换不同的模型

函数调用的实现

大模型交互的过程中有结果 与函数调用相关的参数:

function会逐渐被废弃,因此我们直接使用tools就可以了。 函数调用主要有下面3个步骤

  1. 函数定义
  2. llm 决定调用函数
  3. 执行函数
  4. 将函数执行的结果在次发送给大模型
  5. 获取模型最终响应结果

接下来看看具体的执行代码: 前面的代码结构中ChatContext通过持有ToolsProviderMixIn,切换不同的实现类,从而使用不同的工具(这里不是说大模型一次只能回调一个函数,而是我们可能会根据不同的业务场景给大模型不同批次的工具) ToolsProviderMixIn代码设计

python 复制代码
class ToolsProviderMixIn(ABC):
    def __init__(self, ):
        super().__init__()

    @abstractmethod
    def get_tools(self) -> List[ChatCompletionToolParam]:
        pass
        """
        返回需要将哪些工具注册到大模型中
        """

    @abstractmethod
    def message_match_tool(self, message: ChatCompletion|ChatCompletionChunk) -> bool:
        pass
        """
        判断当前工具类能否被大模型执行
        """

    @abstractmethod
    def tools_call(self, message: ChatCompletion) -> List[ChatCompletionToolMessageParam]:
        pass
        """
        响应大模型函数调用并返回结果
        """
           

具体的工具类实现

python 复制代码
from datetime import datetime
from typing import List

from openai.types.chat import ChatCompletionToolParam, ChatCompletion, ChatCompletionToolMessageParam, \
    ChatCompletionChunk

from learn02.chat_tools.chat_tool_mixin import ToolsProviderMixIn


class ChatTools(ToolsProviderMixIn):
    def __init__(self, ):
        super().__init__()
        self.calling_tools = {}

    def tools_call(self, message: ChatCompletion | ChatCompletionChunk) -> List[ChatCompletionToolMessageParam]:
        if isinstance(message, ChatCompletion):  # 可能是多个异步函数 当前先这样
            return self.dispatch_tool_call(message)
        if isinstance(message, ChatCompletionChunk):
            if self.calling_tools.get(message.id):
                if message.choices[0].finish_reason == "tool_calls":
                    self.calling_tools.pop(message.id)
                    return []
            else:
                self.calling_tools[message.id] = message
                return self.dispatch_tool_call(message)
            return []
        return []

    def dispatch_tool_call(self, message: ChatCompletion | ChatCompletionChunk):
        tool_response: List[ChatCompletionToolMessageParam] = []
        if isinstance(message, ChatCompletion):
            for tool_call in message.choices[0].message.tool_calls:
                if tool_call.function.name == "get_current_time":
                    current_time = self.get_current_time()
                    tool_response.append(
                        ChatCompletionToolMessageParam(role="tool", content=current_time, tool_call_id=tool_call.id))
        else:
            for tool_call in message.choices[0].delta.tool_calls:
                if tool_call.function.name == "get_current_time":
                    current_time = self.get_current_time()
                    tool_response.append(
                        ChatCompletionToolMessageParam(role="tool", content=current_time, tool_call_id=tool_call.id))
        return tool_response

    def get_current_time(self) -> str:
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    def message_match_tool(self, message: ChatCompletion | ChatCompletionChunk) -> bool:
        if isinstance(message, ChatCompletion):
            if message.choices[0].message.tool_calls:# 有tool_calls 存在说明是函数调用
                return True
        elif isinstance(message, ChatCompletionChunk):
            if message.choices[0].delta.tool_calls:
                return True
        return False

    def get_tools(self) -> List[ChatCompletionToolParam]:
        return [
            {
                "type": "function",
                "function": {
                    "name": "get_current_time",
                    "description": "获取当前时间",
                    "parameters": {
                        "type": "object",
                        "properties": {},
                        "required": [],
                    },
                },
            },
        ]

**注意:**函数执行之后返回给大模型的消息类型是 ChatCompletionToolMessageParam,我们给大模型发送 ChatCompletionToolMessageParam 类型消息的前面一定要有 模型回调出来需要调用函数的消息,否则会报错

体验一下效果:

运行 local_input_and_display_chat.py ,命令行输入今日计划 会在当前文件夹下输出 一份md文件。浏览器打开

到此函数调用的过程就结束了,后面会研究基于rag 来实现本地数据 检索,让ai 了解 我们的学习计划,学习结果,从而帮助我们 更好的验证学习情况。 参考代码:github.com/xiaolutang/...

相关推荐
mCell3 小时前
长期以来我对 LLM 的误解
深度学习·llm·ollama
扫地的小何尚5 小时前
深度解析 CUDA-QX 0.4 加速 QEC 与求解器库
人工智能·语言模型·llm·gpu·量子计算·nvidia·cuda
飞哥数智坊5 小时前
打造我的 AI 开发团队(二):bmad,开箱即用的敏捷开发智能体
人工智能·ai编程
RainbowSea8 小时前
4. ChatClient 的初始,快速使用上手
java·spring·ai编程
RainbowSea8 小时前
3. Ollama 安装,流式输出,多模态,思考模型
java·spring·ai编程
大模型教程9 小时前
如何在你的业务中选择RAG和Fine tuning?
程序员·llm·agent
AI大模型10 小时前
前端学 AI 不用愁!手把手教你用 LangGraph 实现 ReAct 智能体(附完整流程 + 代码)
前端·llm·agent
大模型教程10 小时前
LangGraph实战教程(1)- 从零开始认识LangGraph
langchain·llm·agent
coder_pig10 小时前
Claude Code + Holopix AI | 轻松复刻 "虚假广告"-🧟‍♀️射击小游戏
aigc·ai编程·claude
AI分享官10 小时前
低代码平台+MonkeyCode混合开发:3天上线一个App的野路子实操
github·ai编程