{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "926ed501",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "微积分是研究变化率（微分）与累积量（积分）及其互逆关系的数学分支。\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()  # 加载环境变量\n",
    "\n",
    "\n",
    "# 创建 DeepSeek 聊天模型实例\n",
    "# base_url 指向 DeepSeek 的兼容端点，而非 OpenAI 官方地址\n",
    "llm = ChatOpenAI(\n",
    "    model_name=os.getenv(\"DEEPSEEK_MODEL\"),              # DeepSeek 的对话模型              # \n",
    "    api_key=os.getenv(\"DEEPSEEK_API_KEY\"),                    # 在 platform.deepseek.com 获取\n",
    "    base_url=os.getenv(\"DEEPSEEK_API_BASE\")      # DeepSeek API 地址\n",
    ")\n",
    "\n",
    "# invoke 是 LangChain 统一的调用方法，返回 AIMessage 对象\n",
    "response = llm.invoke(\"用一句话解释什么是微积分\")\n",
    "print(response.content)  # .content 拿到纯文本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "84c550f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "量子纠缠是指两个或多个粒子之间的一种特殊关联，使得它们的状态无法独立描述，对其中一个粒子的测量会瞬间影响另一个粒子的状态，无论距离多远。\n"
     ]
    }
   ],
   "source": [
    "response = llm.invoke(\"用一句话解释什么是量子纠缠\")\n",
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1e0923f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "大家好，我是你们的Python老司机。今天咱们聊一个听起来高大上，其实很实在的东西——**装饰器**。\n",
      "\n",
      "## 装饰器到底是个啥？\n",
      "\n",
      "想象一下，你有一件普通的T恤，现在你想让它变成一件潮牌联名款。你不需要重新做一件T恤，只需要在原来的T恤上面**加个刺绣**或者**改个版型**，就OK了。\n",
      "\n",
      "装饰器在Python里做的是同一件事：**在不改变原函数代码的前提下，给函数附加额外的功能**。比如给函数加个“打印日志”的功能，或者加个“计算运行时间”的功能，或者加个“检查用户权限”的功能。\n",
      "\n",
      "## 为什么需要装饰器？\n",
      "\n",
      "假设你写了好几个函数，比如：\n",
      "\n",
      "```python\n",
      "def buy_apple():\n",
      "    print(\"买苹果\")\n",
      "\n",
      "def buy_banana():\n",
      "    print(\"买香蕉\")\n",
      "```\n",
      "\n",
      "现在老板说：“所有买东西的函数，都要先打印‘开始购物’，再执行主要功能，最后打印‘购物结束’。”\n",
      "\n",
      "最粗暴的方式：每个函数里面都加两行print。但如果函数有100个呢？改起来累死人，而且万一以后要改提示语，又要一个个改。\n",
      "\n",
      "装饰器就是来解决这个痛点的：**把重复的“附加逻辑”抽出来，像盖章一样盖到每个函数上**。\n",
      "\n",
      "## 装饰器的核心原理（简单版）\n",
      "\n",
      "装饰器本质上是一个**函数**，它接收一个函数，返回一个新函数。新函数会先执行一些额外操作，再调用原函数。比如：\n",
      "\n",
      "```python\n",
      "def my_decorator(func):          # 接收原函数\n",
      "    def wrapper():               # 定义新函数\n",
      "        print(\"开始购物\")        # 额外操作\n",
      "        func()                   # 调用原函数\n",
      "        print(\"购物结束\")        # 额外操作\n",
      "    return wrapper               # 返回新函数\n",
      "```\n",
      "\n",
      "然后你把它“盖”到原函数上：\n",
      "\n",
      "```python\n",
      "@my_decorator\n",
      "def buy_apple():\n",
      "    print(\"买苹果\")\n",
      "```\n",
      "\n",
      "当你调用 `buy_apple()` 时，实际执行的是 `wrapper()`，所以会打印：\n",
      "\n",
      "```\n",
      "开始购物\n",
      "买苹果\n",
      "购物结束\n",
      "```\n",
      "\n",
      "看到了吗？`buy_apple` 本身的代码没变，但行为变了。这就是装饰器。\n",
      "\n",
      "## 装饰器的“语法糖”\n",
      "\n",
      "上面的 `@my_decorator` 其实就是 `buy_apple = my_decorator(buy_apple)` 的简写。Python给了一个甜甜的语法糖，让你不用写那个赋值语句，直接写在函数头上就行。\n",
      "\n",
      "## 常见装饰器例子\n",
      "\n",
      "### 1. 计时装饰器\n",
      "\n",
      "```python\n",
      "import time\n",
      "def timer_decorator(func):\n",
      "    def wrapper(*args, **kwargs):\n",
      "        start = time.time()\n",
      "        result = func(*args, **kwargs)\n",
      "        end = time.time()\n",
      "        print(f\"{func.__name__} 运行耗时 {end-start:.4f} 秒\")\n",
      "        return result\n",
      "    return wrapper\n",
      "\n",
      "@timer_decorator\n",
      "def slow_function():\n",
      "    time.sleep(2)\n",
      "    print(\"慢函数执行完毕\")\n",
      "\n",
      "slow_function()\n",
      "# 输出: 慢函数执行完毕\n",
      "#        slow_function 运行耗时 2.0010 秒\n",
      "```\n",
      "\n",
      "### 2. 登录检查装饰器（模拟）\n",
      "\n",
      "```python\n",
      "def login_required(func):\n",
      "    def wrapper(*args, **kwargs):\n",
      "        if not user_is_logged_in():  # 假设有个检查函数\n",
      "            print(\"请先登录\")\n",
      "            return\n",
      "        return func(*args, **kwargs)\n",
      "    return wrapper\n",
      "\n",
      "@login_required\n",
      "def post_comment():\n",
      "    print(\"发布评论成功\")\n",
      "```\n",
      "\n",
      "## 小贴士：装饰器可以带参数吗？\n",
      "\n",
      "当然可以，比如你想让装饰器接受一个“购物提示语”参数。这时需要再嵌套一层函数。\n",
      "\n",
      "```python\n",
      "def shopping_decorator(message):\n",
      "    def decorator(func):\n",
      "        def wrapper():\n",
      "            print(f\"开始购物：{message}\")\n",
      "            func()\n",
      "            print(\"购物结束\")\n",
      "        return wrapper\n",
      "    return decorator\n",
      "\n",
      "@shopping_decorator(\"买苹果啰\")\n",
      "def buy_apple():\n",
      "    print(\"买苹果\")\n",
      "```\n",
      "\n",
      "这样调用 `@shopping_decorator(\"买苹果啰\")` 就相当于先执行 `shopping_decorator(\"买苹果啰\")` 得到一个装饰器，再用那个装饰器去装饰 `buy_apple`。\n",
      "\n",
      "## 总结\n",
      "\n",
      "- **装饰器 = 给函数穿马甲**，不改变原函数代码，但增加功能。\n",
      "- 它通过**闭包**（内部函数访问外部函数变量）实现，核心是**高阶函数**（接收函数，返回函数）。\n",
      "- 使用 `@装饰器名` 语法糖，简洁优雅。\n",
      "- 常用于日志、计时、权限校验、缓存、路由等场景。\n",
      "\n",
      "别被“装饰器”三个字吓到，你就把它想象成给函数“加个外挂”——简单又实用。\n",
      "\n",
      "下次写代码遇到重复的“前置/后置操作”，就试试用装饰器吧！\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import AIMessage, SystemMessage, HumanMessage\n",
    "\n",
    "# SystemMessage：设定模型的\"人设\"，相当于给它一个角色卡\n",
    "# HumanMessage：用户说的话\n",
    "# AIMessage：模型之前的回答（用于多轮对话）\n",
    "\n",
    "messages = [\n",
    "    SystemMessage(content=\"你是一个资深的 Python 技术博主，擅长用大白话解释概念\"),\n",
    "    HumanMessage(content=\"解释一下什么是装饰器？\"),\n",
    "]\n",
    "\n",
    "response = llm.invoke(messages)\n",
    "print(response.content)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6f8611ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "片名：星际穿越\n",
      "年份：2014\n",
      "导演：克里斯托弗·诺兰\n",
      "评分：9.4\n"
     ]
    }
   ],
   "source": [
    "from pydantic import BaseModel, Field\n",
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "# 定义你期望的输出格式 （Pythintic 模型）\n",
    "class MovieInfo(BaseModel):\n",
    "    title: str = Field(description=\"电影名称\")\n",
    "    year: int = Field(description=\"上映年份\")\n",
    "    director: str = Field(description=\"导演\")\n",
    "    rating: float = Field(description=\"评分\")\n",
    "\n",
    "# .with_structured_output() 会自动把 Schema 注入 prompt，\n",
    "# 并在底层做 JSON 解析和类型校验\n",
    "structured_llm = llm.with_structured_output(MovieInfo)\n",
    "result = structured_llm.invoke(\"绍一下电影《星际穿越》\")\n",
    "\n",
    "# 返回的是 MovieInfo 对象，可以直接用属性访问\n",
    "print(f\"片名：{result.title}\")\n",
    "print(f\"年份：{result.year}\")\n",
    "print(f\"导演：{result.director}\")\n",
    "print(f\"评分：{result.rating}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7779a538",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "片名：星际穿越\n",
      "年份：2014\n",
      "导演：克里斯托弗·诺兰\n",
      "评分：8.7\n"
     ]
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "\n",
    "# 方式二：TypedDict（更轻量，无运行时校验）\n",
    "class MovieInfo(TypedDict):\n",
    "    title: str\n",
    "    year: int\n",
    "    director: str\n",
    "    rating: float\n",
    "\n",
    "# .with_structured_output() 会自动把 Schema 注入 prompt，\n",
    "structured_llm = llm.with_structured_output(MovieInfo)\n",
    "result = structured_llm.invoke(\"绍一下电影《星际穿越》\")\n",
    "\n",
    "# 返回的是 MovieInfo 对象，可以直接用属性访问\n",
    "print(f\"片名：{result['title']}\")\n",
    "print(f\"年份：{result['year']}\")\n",
    "print(f\"导演：{result['director']}\")\n",
    "print(f\"评分：{result['rating']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bd278017",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'director': '饺子', 'rating': 8.4, 'title': '哪吒之魔童降世', 'year': 2019}\n"
     ]
    }
   ],
   "source": [
    "# 方式三：JSON Schema（最灵活，跨语言通用）\n",
    "json_schema = {\n",
    "    \"title\": \"MovieInfo\",\n",
    "    \"description\": \"电影信息对象\",\n",
    "    \"type\": \"object\",\n",
    "    \"properties\": {\n",
    "        \"title\":    {\"type\": \"string\",  \"description\": \"电影名称\"},\n",
    "        \"year\":     {\"type\": \"integer\", \"description\": \"上映年份\"},\n",
    "        \"director\": {\"type\": \"string\",  \"description\": \"导演\"},\n",
    "        \"rating\":   {\"type\": \"number\",  \"description\": \"评分（10分制）\"}\n",
    "    },\n",
    "    \"required\": [\"title\", \"year\", \"director\", \"rating\"]\n",
    "}\n",
    "\n",
    "structured_llm = llm.with_structured_output(json_schema)\n",
    "result = structured_llm.invoke(\"介绍一下电影《哪吒之魔童降世》\")\n",
    "print(result)  # 返回的是 dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b5db897c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[SystemMessage(content='你是一个叫 小助手 的 AI 助手，说话风格简洁专业', additional_kwargs={}, response_metadata={}), HumanMessage(content='什么是机器学习？', additional_kwargs={}, response_metadata={})]\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "# 定义一个带变量的对话模板\n",
    "# {name} 和 {question} 是占位符，运行时会被替换\n",
    "chat_template = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个叫 {name} 的 AI 助手，说话风格简洁专业\"),\n",
    "    (\"human\", \"{question}\")\n",
    "])\n",
    "\n",
    "# 填充变量，生成最终的消息列表\n",
    "messages = chat_template.format_messages(name=\"小助手\", question=\"什么是机器学习？\")\n",
    "print(messages)\n",
    "# 输出：\n",
    "# [SystemMessage(content='你是一个叫 小助手 的 AI 助手，说话风格简洁专业'),\n",
    "#  HumanMessage(content='什么是机器学习？')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f7835d40",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "机器学习是人工智能的一个分支，它使计算机能够通过数据学习模式，而不需要显式编程。简单来说，就是让系统从经验（数据）中自动改进性能。核心是通过算法构建模型，用训练数据调整参数，最终对未知数据做出预测或决策。常见类型包括监督学习、无监督学习和强化学习。\n"
     ]
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "respoonse = llm.invoke(messages)\n",
    "print(respoonse.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7ed25d7b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "给我讲一个关于程序员的笑话\n",
      "请用幽默的风格，写一篇关于加班的短文，字数不超过100字\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "# 单变量模板\n",
    "prompt = PromptTemplate.from_template(\"给我讲一个关于{topic}的笑话\")\n",
    "print(prompt.format(topic=\"程序员\"))\n",
    "# → \"给我讲一个关于程序员的笑话\"\n",
    "\n",
    "# 多变量模板\n",
    "prompt = PromptTemplate(\n",
    "    template=\"请用{style}的风格，写一篇关于{topic}的短文，字数不超过{limit}字\",\n",
    "    input_variables=[\"style\", \"topic\", \"limit\"]\n",
    ")\n",
    "print(prompt.format(style=\"幽默\", topic=\"加班\", limit=100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "91159635",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请分析上海在2026年07/15/2026, 02:18:13的天气趋势\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "from datetime import datetime\n",
    "\n",
    "# 一个需要三个变量的模板\n",
    "prompt = PromptTemplate(\n",
    "    template=\"请分析{city}在{year}年{date}的天气趋势\",\n",
    "    input_variables=[\"city\", \"year\", \"date\"]\n",
    ")\n",
    "\n",
    "def get_datetime():\n",
    "    now = datetime.now()\n",
    "    return now.strftime(\"%m/%d/%Y, %H:%M:%S\")\n",
    "\n",
    "# partial() 可以预填部分变量\n",
    "# 注意：date 传的是函数引用而非调用结果，每次 format 时会重新执行\n",
    "partial_prompt = prompt.partial(\n",
    "    city=\"上海\",\n",
    "    year=\"2026\",\n",
    "    date=get_datetime)  # 动态获取当前日期\n",
    "\n",
    "# 只需填剩余的变量（这里已经全部填完了）\n",
    "print(partial_prompt.format())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ed1561ad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你是一个精通网络流行语的翻译官。请把黑话翻译成正式的职场语言。\n",
      "\n",
      "\n",
      "黑话: YYDS\n",
      "翻译: 永远的神（极度赞美）\n",
      "\n",
      "黑话: 绝绝子\n",
      "翻译: 太棒了（强烈赞叹）\n",
      "\n",
      "黑话: 躺平\n",
      "翻译: 心态平和、不再内卷（不争不抢的生活态度）\n",
      "\n",
      "黑话: 下头\n",
      "翻译:\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate\n",
    "\n",
    "# 第一步：准备示例数据\n",
    "examples = [\n",
    "    {\"input\": \"YYDS\",   \"output\": \"永远的神（极度赞美）\"},\n",
    "    {\"input\": \"绝绝子\", \"output\": \"太棒了（强烈赞叹）\"},\n",
    "    {\"input\": \"躺平\",   \"output\": \"心态平和、不再内卷（不争不抢的生活态度）\"},\n",
    "]\n",
    "\n",
    "# 第二步：定义每个示例的展示格式\n",
    "example_prompt = PromptTemplate(\n",
    "    input_variables=[\"input\", \"output\"],\n",
    "    template=\"黑话: {input}\\n翻译: {output}\"\n",
    ")\n",
    "\n",
    "# 第三步：组装完整的 Few-Shot Prompt\n",
    "prompt = FewShotPromptTemplate(\n",
    "    examples=examples,               # 示例列表\n",
    "    example_prompt=example_prompt,   # 每个示例的格式\n",
    "\n",
    "    prefix=\"你是一个精通网络流行语的翻译官。请把黑话翻译成正式的职场语言。\\n\",  # 前缀（指令）\n",
    "    suffix=\"黑话: {input}\\n翻译:\",                                           # 后缀（用户问题）\n",
    "    input_variables=[\"input\"]\n",
    ")\n",
    "\n",
    "# 第四步：生成最终 prompt\n",
    "final_prompt = prompt.format(input=\"下头\")\n",
    "print(final_prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "dc66923a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "黑话: YYDS  \n",
      "翻译: 无可挑剔的卓越（极度赞美）\n",
      "\n",
      "黑话: 绝绝子  \n",
      "翻译: 极其出色（强烈赞叹）\n",
      "\n",
      "黑话: 躺平  \n",
      "翻译: 保持内心平和，不参与过度竞争（不争不抢的生活态度）\n",
      "\n",
      "黑话: 下头  \n",
      "翻译: 令人扫兴，降低热情（负面的感受）\n"
     ]
    }
   ],
   "source": [
    "response = llm.invoke(final_prompt)\n",
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "23836e26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "量子计算是一种利用量子比特（qubit）的叠加和纠缠等量子力学特性，实现并行计算并大幅提升特定问题处理速度的新型计算范式。\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "prompt = PromptTemplate.from_template(\"请用一句话解释什么是{concept}\")\n",
    "\n",
    "# 用管道符 | 把 prompt 和 llm 连起来，就构成了一条链\n",
    "chain = prompt | llm\n",
    "\n",
    "# invoke 时只需传入变量，prompt 填充和模型调用自动完成\n",
    "result = chain.invoke({\"concept\": \"量子计算\"})\n",
    "print(result.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "daa450bd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "秦始皇统一六国的顺序是：韩、赵、魏、楚、燕、齐。\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "# 定义对话模板\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个中国历史专家，回答简洁明了\"),\n",
    "    (\"user\", \"{input}\")\n",
    "])\n",
    "\n",
    "# StrOutputParser 把 AIMessage 转成纯字符串\n",
    "# 这样链的输出就是 str，而非 AIMessage 对象\n",
    "parser = StrOutputParser()\n",
    "\n",
    "# 三段式链：Prompt → LLM → Parser\n",
    "chain = prompt | llm | parser\n",
    "\n",
    "result = chain.invoke({\"input\": \"秦始皇统一六国的顺序是什么？\"})\n",
    "print(result)  # 直接是字符串，不需要 .content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "b054d78d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['大熊猫繁育研究基地', '宽窄巷子', '武侯祠']\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.output_parsers import CommaSeparatedListOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "parser = CommaSeparatedListOutputParser()\n",
    "\n",
    "# get_format_instructions() 会生成一段\"输出格式要求\"的文字\n",
    "# 比如 \"Your response should be a list of comma separated values...\"\n",
    "format_instructions = parser.get_format_instructions()\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", f\"你是一个旅游顾问。{{format_instructions}}\"),\n",
    "    (\"human\", \"推荐{city}的{count}个必去景点\")\n",
    "])\n",
    "\n",
    "chain = prompt | llm | parser\n",
    "\n",
    "result = chain.invoke({\n",
    "    \"city\": \"成都\",\n",
    "    \"count\": 3,\n",
    "    \"format_instructions\": format_instructions\n",
    "})\n",
    "print(result)  # ['武侯祠', '锦里', '大熊猫繁育研究基地'] ← 直接是 list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f722b4d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "书名：朝花夕拾\n",
      "作者：鲁迅\n",
      "体裁：['散文集', '回忆性散文']\n",
      "<class '__main__.BookInfo'>\n"
     ]
    }
   ],
   "source": [
    "from typing import List\n",
    "from pydantic import BaseModel, Field\n",
    "from langchain_core.output_parsers import PydanticOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "# 定义数据结构\n",
    "class BookInfo(BaseModel):\n",
    "    \"\"\"书籍信息\"\"\"\n",
    "    book_name: str = Field(description=\"书名\")\n",
    "    author: str = Field(description=\"作者\")\n",
    "    genres: List[str] = Field(description=\"体裁列表\")\n",
    "\n",
    "# 创建解析器，它会自动生成 JSON Schema 格式要求\n",
    "parser = PydanticOutputParser(pydantic_object=BookInfo)\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个图书管理员。请按格式要求输出，使用中文。\\n{format_instructions}\"),\n",
    "    (\"human\", \"从以下简介中提取书籍信息：\\n{introduction}\")\n",
    "])\n",
    "\n",
    "\n",
    "chain = prompt | llm | parser\n",
    "\n",
    "introduction = \"\"\"\n",
    "《朝花夕拾》原名《旧事重提》，是鲁迅的散文集，收录1926年创作的10篇回忆性散文。\n",
    "文集以记事为主，饱含抒情气息，反映了作者青少年时期的生活。\n",
    "\"\"\"\n",
    "\n",
    "result = chain.invoke({\n",
    "    \"introduction\": introduction,\n",
    "    \"format_instructions\": parser.get_format_instructions()\n",
    "})\n",
    "\n",
    "print(f\"书名：{result.book_name}\")       # 朝花夕拾\n",
    "print(f\"作者：{result.author}\")          # 鲁迅\n",
    "print(f\"体裁：{result.genres}\")          # ['散文集', '回忆性散文']\n",
    "print(type(result)) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "ebfb8d9d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "冷笑话：为什么程序员分不清万圣节和圣诞节？  \n",
      "因为 Oct 31 等于 Dec 25。\n",
      "五言绝句：《程序员》\n",
      "键盘敲夜寂，代码写心思。\n",
      "不觉时光逝，屏前展妙奇。\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnableParallel\n",
    "\n",
    "\n",
    "parser = StrOutputParser()\n",
    "\n",
    "# 定义两条独立的链\n",
    "joke_prompt = ChatPromptTemplate.from_template(\"讲一个关于{topic}的冷笑话，越短越好\")\n",
    "poem_prompt = ChatPromptTemplate.from_template(\"写一首关于{topic}的五言绝句\")\n",
    "\n",
    "joke_chain = joke_prompt | llm | parser\n",
    "poem_chain = poem_prompt | llm | parser\n",
    "\n",
    "# 用 RunnableParallel 把两条链并联\n",
    "# 同一个 topic 会同时发给两条链，结果以 dict 返回\n",
    "combined = RunnableParallel(joke=joke_chain, poem=poem_chain)\n",
    "\n",
    "result = combined.invoke({\"topic\": \"程序员\"})\n",
    "print(f\"冷笑话：{result['joke']}\")\n",
    "print(f\"五言绝句：{result['poem']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "0fae47d9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好小明！作为程序员，你的一天大概是在代码、调试、咖啡和“这bug怎么又出现了”的循环中度过的吧？😄\n",
      "\n",
      "有什么我可以帮忙的吗？无论是：\n",
      "- 技术问题（比如某个框架的坑、算法优化、debug思路）\n",
      "- 职业发展（转行、跳槽、技术选型）\n",
      "- 想聊聊某个项目或想法\n",
      "- 或者单纯想吐槽一下需求变更\n",
      "\n",
      "随时告诉我，我都在线等着给你搭把手～\n",
      "--------------------------\n",
      "你叫DeepSeek，是由深度求索公司创造的AI助手！不过，如果你愿意的话，也可以给我起个昵称，或者告诉我你的名字，这样我们的对话会更亲切哦～ 😊 有什么我可以帮你的吗？\n"
     ]
    }
   ],
   "source": [
    "# 第一轮 \n",
    "r1 = llm.invoke(\"我叫小明，是一名程序员\")\n",
    "print(r1.content)\n",
    "\n",
    "print('--------------------------')\n",
    "# 第二轮:问他记不记得\n",
    "r2 = llm.invoke(\"我叫什么名字？\")\n",
    "print(r2.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "9c91139f",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\29448\\AppData\\Local\\Temp\\ipykernel_46780\\2339440262.py:6: LangChainDeprecationWarning: The class `ConversationBufferMemory` was deprecated in LangChain 0.3.1 and will be removed in 2.0.0. Use `langchain.agents.create_agent` instead. For agents that need to remember prior interactions, use `create_agent` with checkpointing or the `Store` API. See https://docs.langchain.com/oss/python/langchain/short-term-memory and https://docs.langchain.com/oss/python/langchain/long-term-memory\n",
      "  memory = ConversationBufferMemory(return_messages=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好，小明！很高兴认识你。今天有什么我可以帮忙的吗？或者你想聊些什么？\n",
      "你叫小明呀，刚才你告诉我的～😊\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_classic.memory import ConversationBufferMemory\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "memory = ConversationBufferMemory(return_messages=True)\n",
    "\n",
    "# MessagesPlaceholder 会在运行时被历史消息列表替换\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个友好的 AI 助手\"),\n",
    "    MessagesPlaceholder(variable_name=\"history\"),   # 历史消息插槽\n",
    "    (\"human\", \"{input}\")                            # 当前用户输入\n",
    "])\n",
    "\n",
    "chain = prompt | llm | StrOutputParser()\n",
    "\n",
    "# 第一轮\n",
    "history = memory.load_memory_variables({})[\"history\"]\n",
    "r1 = chain.invoke({\"input\": \"我叫小明\", \"history\": history})\n",
    "print(r1)\n",
    "memory.save_context({\"input\": \"我叫小明\"}, {\"output\": r1})\n",
    "\n",
    "# 第二轮：历史自动带入\n",
    "history = memory.load_memory_variables({})[\"history\"]\n",
    "r2 = chain.invoke({\"input\": \"我叫什么？\", \"history\": history})\n",
    "print(r2) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "f8ee1358",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\29448\\AppData\\Local\\Temp\\ipykernel_46780\\4272763690.py:6: LangChainDeprecationWarning: The class `ConversationChain` was deprecated in LangChain 0.2.7 and will be removed in 2.0.0. Use `langchain.agents.create_agent` instead. Build a conversational agent with `langchain.agents.create_agent` and persist message history via a LangGraph checkpointer.\n",
      "  chain = ConversationChain(llm=llm, memory=memory)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好，小明！很高兴认识你，我是你的AI助手，叫小智（或者你可以给我起个更喜欢的名字）。听说你是程序员，那太棒了！我脑袋里装着各种编程语言的知识，从Python、JavaScript到C++、Rust，甚至一些冷门的Lisp和Haskell也略知一二。如果你遇到bug调试、算法优化或者想聊聊最新的框架，比如React、Vue或者Django，随时可以找我。最近我在研究AI编程助手如何更高效地帮开发者写代码，比如自动生成单元测试或者代码审查建议，你有兴趣试试吗？\n",
      "哈哈，小明，你这是在考验我的记忆力吗？刚才你明明说自己是程序员呀！难道这么快就转行了？🤔\n",
      "\n",
      "不过开玩笑归开玩笑，程序员这个身份我牢牢记住了——毕竟你刚说完，我脑袋里的“记忆缓存”还没清空呢。需要我帮你查查系统日志吗？还是说，你想聊聊最近的代码项目，比如用Python写个爬虫、用JavaScript调个API，或者研究一下怎么用Rust搞系统编程？😄\n"
     ]
    }
   ],
   "source": [
    "from langchain_classic.chains import ConversationChain\n",
    "from langchain_classic.memory import ConversationBufferMemory\n",
    "\n",
    "memory = ConversationBufferMemory(return_messages=True)\n",
    "# ConversationChain 自动处理：注入历史 → 调用模型 → 保存对话\n",
    "chain = ConversationChain(llm=llm, memory=memory)\n",
    "\n",
    "r1 = chain.invoke({\"input\": \"你好，我叫小明，我是程序员\"})\n",
    "print(r1[\"response\"])\n",
    "\n",
    "r2 = chain.invoke({\"input\": \"我是做什么工作的？\"})\n",
    "print(r2[\"response\"])\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "f0d9ed2c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🌟 别难过啦~让我给你一个温暖的抱抱！要记住，阴天总会过去，阳光一定会重新出现的 ☀️ 要不要和我聊聊是什么让你不开心？我随时都在这里听你说哦 💕\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_classic.chains import ConversationChain\n",
    "from langchain_classic.memory import ConversationBufferMemory\n",
    "\n",
    "\n",
    "# 自定义 prompt，加入个性化设定\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个爱用 emoji 的开心助手 ✨\"),\n",
    "    MessagesPlaceholder(variable_name=\"history\"),\n",
    "    (\"human\", \"{input}\")\n",
    "])\n",
    "\n",
    "memory = ConversationBufferMemory(return_messages=True)\n",
    "chain = ConversationChain(llm=llm, memory=memory, prompt=prompt)\n",
    "\n",
    "r = chain.invoke({\"input\": \"今天心情不好\"})\n",
    "print(r[\"response\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "354b5494",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\code\\01_langchain\\.venv\\Lib\\site-packages\\IPython\\core\\interactiveshell.py:3748: LangChainDeprecationWarning: RunnableWithMessageHistory is deprecated. Use LangGraph's built-in persistence instead.\n",
      "  exec(code_obj, self.user_global_ns, self.user_ns)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "地球到太阳的平均距离约为 **1.495978707亿公里**，通常取 **1.496亿公里**，天文学上将其定义为一个 **天文单位（AU）**。\n",
      "\n",
      "由于地球绕太阳公转的轨道是一个椭圆，所以距离会略有变化：\n",
      "- **近日点（1月初）**：约 1.471亿公里\n",
      "- **远日点（7月初）**：约 1.521亿公里\n",
      "地球到月球的平均距离约为 **38.44万公里**（即 384,400 公里）。\n",
      "\n",
      "由于月球绕地球的轨道也是椭圆，实际距离会变化：\n",
      "- **近地点（最近时）**：约 36.33万公里\n",
      "- **远地点（最远时）**：约 40.55万公里\n",
      "\n",
      "作为参考，光从地球到月球大约需要 **1.28秒**。\n",
      "当然是 **月球** 更近。\n",
      "\n",
      "具体对比一下：\n",
      "- **地球到月球**：平均约 **38.44万公里**\n",
      "- **地球到太阳**：平均约 **1.496亿公里**\n",
      "\n",
      "也就是说，太阳到地球的距离大约是月球到地球距离的 **389倍**。\n",
      "\n",
      "如果换一个更直观的说法：光从月球到地球大约只要 **1.28秒**，而从太阳到地球则需要 **约8分20秒**。所以月球离我们近得多。\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "from langchain_community.chat_message_histories import SQLChatMessageHistory\n",
    "from sqlalchemy import create_engine\n",
    "\n",
    "# 定义 prompt（带历史占位符）\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个擅长物理的助手\"),\n",
    "    MessagesPlaceholder(variable_name=\"history\"),\n",
    "    (\"human\", \"{question}\")\n",
    "])\n",
    "\n",
    "chain = prompt | llm | StrOutputParser()\n",
    "\n",
    "# 手动创建 SQLAlchemy 引擎，确保 utf8mb4 字符集生效\n",
    "engine = create_engine(\n",
    "    \"mysql+pymysql://root:admin@localhost:3308/mydb\",\n",
    "    connect_args={\"charset\": \"utf8mb4\"},\n",
    "    pool_recycle=3600\n",
    ")\n",
    "\n",
    "# 用 RunnableWithMessageHistory 包装链\n",
    "# 每次 invoke 时自动加载历史，结束后自动保存\n",
    "chain_with_history = RunnableWithMessageHistory(\n",
    "    chain,\n",
    "    # session_id 到 MessageHistory 的映射函数\n",
    "    # 每个 session_id 对应一组独立的对话记录\n",
    "    lambda session_id: SQLChatMessageHistory(\n",
    "        session_id=session_id,\n",
    "        connection=engine,           # 直接传入引擎，而非连接串\n",
    "        table_name=\"chat_history\"\n",
    "    ),\n",
    "    input_messages_key=\"question\",    # 用户输入的 key\n",
    "    history_messages_key=\"history\"    # 历史消息的 key\n",
    ")\n",
    "\n",
    "# 使用时通过 config 传入 session_id\n",
    "config = {\"configurable\": {\"session_id\": \"user_001\"}}\n",
    "\n",
    "r1 = chain_with_history.invoke({\"question\": \"地球到太阳有多远？\"}, config=config)\n",
    "print(r1)\n",
    "\n",
    "r2 = chain_with_history.invoke({\"question\": \"到月球呢？\"}, config=config)\n",
    "print(r2)\n",
    "\n",
    "r3 = chain_with_history.invoke({\"question\": \"哪个更近？\"}, config=config)\n",
    "print(r3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "4a494a7b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get_weather\n",
      "查询指定城市的实时天气信息\n",
      "\n",
      "    Args:\n",
      "        location: 城市名称，如\"北京\"、\"成都\"\n",
      "{'location': {'title': 'Location', 'type': 'string'}}\n",
      "成都的天气：Mist，温度：29°C\n"
     ]
    }
   ],
   "source": [
    "from langchain.tools import tool\n",
    "import requests\n",
    "\n",
    "@tool\n",
    "def get_weather(location: str) -> str:\n",
    "    \"\"\"查询指定城市的实时天气信息\n",
    "    \n",
    "    Args:\n",
    "        location: 城市名称，如\"北京\"、\"成都\"\n",
    "    \"\"\"\n",
    "    # 调用免费天气 API\n",
    "    url = f\"https://wttr.in/{location}?format=j1&lang=zh\"\n",
    "    response = requests.get(url)\n",
    "    data = response.json()\n",
    "\n",
    "    current = data['current_condition'][0]\n",
    "    weather_desc = current['weatherDesc'][0]['value']\n",
    "    temp = current['temp_C']\n",
    "\n",
    "    return f\"{location}的天气：{weather_desc}，温度：{temp}°C\"\n",
    "\n",
    "# @tool 装饰器自动提取的信息\n",
    "print(get_weather.name)         # \"get_weather\"\n",
    "print(get_weather.description)  # \"查询指定城市的实时天气信息\"\n",
    "print(get_weather.args)         # {'location': {'title': 'Location', 'type': 'string'}}\n",
    "\n",
    "# 工具可以直接调用\n",
    "result = get_weather.invoke({\"location\": \"成都\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "1ce9b3d1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'name': 'get_weather', 'args': {'location': '成都'}, 'id': 'call_1de0db4927b3482f8e27feab', 'type': 'tool_call'}]\n"
     ]
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "\n",
    "# bind_tools() 把工具的 Schema 注入到模型请求中\n",
    "llm_with_tools = llm.bind_tools([get_weather])\n",
    "\n",
    "# 此时模型已经\"知道\"你有一个叫 get_weather 的工具了\n",
    "messages = [HumanMessage(content=\"成都今天天气怎么样？\")]\n",
    "\n",
    "response = llm_with_tools.invoke(messages)\n",
    "\n",
    "# LLM的输出不再是一段自然语言文本，而是一个结构化的JSON对象(tool_calls)。\n",
    "#这个对象包含了要调用的函数名和所需的参数。\n",
    "print(response.tool_calls)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "9bd54932",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "成都的天气情况如下：\n",
      "\n",
      "🌤️ **天气状况：** 雾（Mist）\n",
      "🌡️ **温度：** 29°C\n",
      "\n",
      "成都目前有雾，气温为29°C，体感可能会有些闷热，出门请注意防暑降温，雾天能见度较低，开车或出行请注意安全哦！\n",
      "{'messages': [HumanMessage(content='成都的天气怎么样？', additional_kwargs={}, response_metadata={}, id='8a282ff9-0b3c-4c9d-9cc5-b4eaf29724e4'), AIMessage(content='好的，我来查询一下成都的天气情况', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 78, 'prompt_tokens': 345, 'total_tokens': 423, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 23, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': None, 'id': 'chatcmpl-22876e14-8df2-97af-b684-cd86148a6d6f', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019f61da-c2ab-7650-a630-e7f03a0b2066-0', tool_calls=[{'name': 'get_weather', 'args': {'location': '成都'}, 'id': 'call_71bb69ff29e04e51aaf45cb7', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 345, 'output_tokens': 78, 'total_tokens': 423, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 23}}), ToolMessage(content='成都：Mist，29°C', name='get_weather', id='fece702e-33cd-436b-a3bc-bac0ca9a9432', tool_call_id='call_71bb69ff29e04e51aaf45cb7'), AIMessage(content='成都的天气情况如下：\\n\\n🌤️ **天气状况：** 雾（Mist）\\n🌡️ **温度：** 29°C\\n\\n成都目前有雾，气温为29°C，体感可能会有些闷热，出门请注意防暑降温，雾天能见度较低，开车或出行请注意安全哦！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 79, 'prompt_tokens': 419, 'total_tokens': 498, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 7, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': None, 'id': 'chatcmpl-2059311f-4be1-9512-bfb4-3f3e8621cd22', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019f61da-ce3a-7c72-be78-26a79ea7913d-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 419, 'output_tokens': 79, 'total_tokens': 498, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 7}})]}\n"
     ]
    }
   ],
   "source": [
    "from langchain.tools import tool\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.agents import create_agent\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "import requests\n",
    "\n",
    "# ─── 第一步：定义工具 ───\n",
    "@tool\n",
    "def get_weather(location: str) -> str:\n",
    "    \"\"\"查询指定城市的天气\"\"\"\n",
    "    url = f\"https://wttr.in/{location}?format=j1&lang=zh\"\n",
    "    data = requests.get(url).json()\n",
    "    current = data['current_condition'][0]\n",
    "    desc = current['weatherDesc'][0]['value']\n",
    "    temp = current['temp_C']\n",
    "    return f\"{location}：{desc}，{temp}°C\"\n",
    "\n",
    "@tool\n",
    "def multiply(a:int,b:int) ->int:\n",
    "    \"\"\"实现两个整数相乘\"\"\"\n",
    "    return a * b\n",
    "\n",
    "tools = [get_weather, multiply]\n",
    "\n",
    "# ─── 第二步：配置大模型 ───\n",
    "\n",
    "# ─── 第三步：创建 Agent ───\n",
    "# create_agent 把模型、工具、prompt 组装成 Agent\n",
    "agent = create_agent(\n",
    "    model=llm,\n",
    "    tools=tools,\n",
    "    system_prompt=\"你是一名智能助手，可以调用工具帮助用户解决问题。\"\n",
    ")\n",
    "\n",
    "# ─── 第四步：使用 ───\n",
    "res = agent.invoke({\"messages\": [\n",
    "    {\"role\": \"user\",\n",
    "     \"content\": \"成都的天气怎么样？\"\n",
    "     }]})\n",
    "print(res['messages'][-1].content)\n",
    "print(res)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "f02ecb65",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5乘以6等于 **30**。\n",
      "{'messages': [HumanMessage(content='5乘以6等于多少？', additional_kwargs={}, response_metadata={}, id='82213bfd-e9a7-4e1d-86e0-0a2c6cc53134'), AIMessage(content='5乘以6等于30。\\n\\n', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 82, 'prompt_tokens': 346, 'total_tokens': 428, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 16, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': None, 'id': 'chatcmpl-af937450-37a7-9382-a580-b4c5fc845e39', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019f61db-80e4-7911-a94f-25082ac58200-0', tool_calls=[{'name': 'multiply', 'args': {'a': 5, 'b': 6}, 'id': 'call_5f04c6f643914efeabc41886', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 346, 'output_tokens': 82, 'total_tokens': 428, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 16}}), ToolMessage(content='30', name='multiply', id='a39f01c8-6410-4568-801b-543aff620a0d', tool_call_id='call_5f04c6f643914efeabc41886'), AIMessage(content='5乘以6等于 **30**。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 425, 'total_tokens': 442, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 7, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': None, 'id': 'chatcmpl-56cae7c1-c3a3-945a-ab03-4239a98b4572', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019f61db-88b6-7083-b9cc-f71c2c516217-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 425, 'output_tokens': 17, 'total_tokens': 442, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 7}})]}\n"
     ]
    }
   ],
   "source": [
    "res = agent.invoke({\"messages\": [\n",
    "    {\"role\": \"user\",\n",
    "     \"content\": \"5乘以6等于多少？\"\n",
    "     }]})\n",
    "print(res['messages'][-1].content)\n",
    "print(res)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "01_langchain (3.11.14.final.0)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
