{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "8064927d", "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", "ds_api_key = os.getenv('OPENAI_API_KEY')\n", "\n", "# 创建 DeepSeek 聊天模型实例\n", "# base_url 指向 DeepSeek 的兼容端点,⽽⾮ OpenAI 官⽅地址\n", "llm = ChatOpenAI(\n", " model_name = \"deepseek-v4-flash\", # DeepSeek 的对话模型\n", " api_key = ds_api_key, # 在 platform.deepseek.com 获取\n", " base_url = \"https://api.deepseek.com\" # DeepSeek API 地址\n", ")\n", "\n", "# invoke 是 LangChain 统⼀的调⽤⽅法,返回 AIMessage 对象\n", "response = llm.invoke(\"⽤⼀句话解释什么是微积分\")\n", "print(response.content) # .content 拿到纯⽂本" ] }, { "cell_type": "code", "execution_count": null, "id": "886b38e4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Jupyter 是一个开源的交互式计算环境,允许用户创建和共享包含代码、文本、可视化和公式的文档,主要用于数据分析和科学计算。\n" ] } ], "source": [ "response = llm.invoke('用一句话解释什么是 jupyter')\n", "print(response.content) # .content 拿到纯⽂本" ] }, { "cell_type": "code", "execution_count": 3, "id": "22a2a229", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "装饰器(Decorator)是 Python 里一种非常实用的“魔法工具”。简单说,**装饰器就像一个“函数快递员”**,它不改变你原本写好的函数代码,但可以在你调用函数的前后,自动帮你做一些额外的事情。\n", "\n", "### 🎁 用大白话比喻:给函数“穿衣服”\n", "\n", "想象你有一件白 T 恤(这就是你的函数)。你想让它在不同场合变得更有用: \n", "- 去上班时,给它加上“工牌”(打印日志) \n", "- 去约会时,给它加上“香水”(计算耗时) \n", "- 去开会时,给它加上“领带”(检查权限)\n", "\n", "你当然可以每次把 T 恤拿出来,自己动手缝上工牌、香水、领带——但这就**修改了原 T 恤**,而且下次要去不同场合又得拆掉重做,很麻烦。\n", "\n", "装饰器做的就是在你**穿这件 T 恤出门之前**,自动帮你套上一件“智能外套”。这件外套保留 T 恤的所有功能(穿它),还额外帮你加了工牌、香水。并且当你不想要这些附加功能时,只需要**脱掉外套**(不用装饰器),原 T 恤还是原来那件。\n", "\n", "### 🔧 Python 里怎么实现?\n", "\n", "装饰器本质上是一个**函数(或类)**,它接收一个函数作为参数,返回一个新的增强版函数。语法上用 `@` 符号贴在函数定义上面。\n", "\n", "```python\n", "# 这是一个简单的装饰器,打印函数执行前后的提示\n", "def my_decorator(func):\n", " def wrapper():\n", " print(\"⏰ 函数开始前,先打个卡\")\n", " func() # 调用原来的函数\n", " print(\"🏁 函数结束后,打个总结\")\n", " return wrapper\n", "\n", "# 用 @ 语法给函数“穿上”装饰器\n", "@my_decorator\n", "def say_hello():\n", " print(\"Hello!\")\n", "\n", "# 调用时,实际执行的是 wrapper,而不是原来的 say_hello\n", "say_hello()\n", "```\n", "\n", "输出:\n", "```\n", "⏰ 函数开始前,先打个卡\n", "Hello!\n", "🏁 函数结束后,打个总结\n", "```\n", "\n", "看到了吗?你没改动 `say_hello` 里面的一行代码,但它却自动多了“开始前”和“结束后”的打印。这就是装饰器的威力。\n", "\n", "### 🎯 装饰器的常见用途\n", "\n", "- **日志记录**:自动记录每个函数什么时候被调用、参数是什么。 \n", "- **性能测试**:计算函数执行耗时。 \n", "- **权限校验**:检查当前用户是否有权限执行此函数。 \n", "- **缓存结果**:对重复计算的结果进行缓存,提高效率。 \n", "- **输入验证**:自动检查参数合法性。 \n", "\n", "### 🤔 为什么需要装饰器?\n", "\n", "如果没有装饰器,你想给多个函数加同样的功能,比如每个函数之前都要打印日志,你就得在每个函数里手动复制粘贴同样的日志代码,既臃肿又难维护。装饰器让你**一次定义,到处复用**,而且代码干干净净,原函数专注做自己的事。\n", "\n", "### 💡 总结\n", "\n", "装饰器 = **一种不修改原函数代码,却能给函数附加新功能的技术**。 \n", "理解它,记住两句话: \n", "1. 装饰器就是“给函数穿衣服”,衣服可以随时穿脱(可插拔)。 \n", "2. `@装饰器` 相当于 `原函数 = 装饰器(原函数)`。\n", "\n", "> 进阶提示:装饰器还可以多重嵌套、带参数、基于类实现,但核心思想不变——**在原函数外包裹一层增强逻辑**。\n" ] } ], "source": [ "from langchain_core.messages import SystemMessage, HumanMessage\n", "# SystemMessage:设定模型的\"⼈设\",相当于给它⼀个⻆⾊卡\n", "# HumanMessage:⽤户说的话\n", "# AIMessage:模型之前的回答(⽤于多轮对话)\n", "messages = [\n", " SystemMessage(content=\"你是⼀个资深的 Python 技术博主,擅⻓⽤⼤⽩话解释概念\")\n", ",\n", " HumanMessage(content=\"解释⼀下什么是装饰器?\"),\n", "]\n", "response = llm.invoke(messages)\n", "print(response.content)" ] }, { "cell_type": "code", "execution_count": 6, "id": "6cca8d85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "⽚名:星际穿越(Interstellar)\n", "年份:2014\n", "导演:克里斯托弗·诺兰\n", "评分:9.3\n" ] } ], "source": [ "from pydantic import BaseModel, Field\n", "from langchain.chat_models import init_chat_model\n", "\n", "# 定义你期望的输出结构(Pydantic 模型)\n", "class MovieInfo(BaseModel):\n", " \"\"\"电影信息\"\"\"\n", " title: str = Field(description=\"电影名称\")\n", " year: int = Field(description=\"上映年份\")\n", " director: str = Field(description=\"导演\")\n", " rating: float = Field(description=\"评分(10分制)\")\n", "\n", "llm = init_chat_model(\n", " model=\"qwen-plus\",\n", " model_provider=\"openai\",\n", " api_key=os.getenv('QWEN_API_KEY'),\n", " base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n", ")\n", "\n", "# .with_structured_output() 会⾃动把 Schema 注⼊ prompt,\n", "# 并在底层做 JSON 解析和类型校验\n", "structured_llm = llm.with_structured_output(MovieInfo)\n", "result = structured_llm.invoke(\"介绍⼀下电影《星际穿越》\")\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": 7, "id": "1575f61b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "流浪地球\n" ] } ], "source": [ "from typing_extensions import TypedDict\n", "# ⽅式⼆:TypedDict(更轻量,⽆运⾏时校验)\n", "class MovieTypedDict(TypedDict):\n", " title: str\n", " year: int\n", " director: str\n", " rating: float\n", " \n", "structured_llm = llm.with_structured_output(MovieTypedDict)\n", "result = structured_llm.invoke(\"介绍⼀下电影《流浪地球》\")\n", "# 返回的是普通 dict\n", "print(result[\"title\"])" ] }, { "cell_type": "code", "execution_count": 8, "id": "c5588de6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'director': '饺子', 'rating': 8.4, 'title': '哪吒之魔童降世', 'year': 2019}\n" ] } ], "source": [ "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", "structured_llm = llm.with_structured_output(json_schema)\n", "result = structured_llm.invoke(\"介绍⼀下电影《哪吒之魔童降世》\")\n", "print(result)" ] }, { "cell_type": "code", "execution_count": 9, "id": "01e865f0", "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", "# {name} 和 {question} 是占位符,运⾏时会被替换\n", "chat_template = ChatPromptTemplate.from_messages([\n", " (\"system\", \"你是⼀个叫 {name} 的 AI 助⼿,说话⻛格简洁专业\"),\n", " (\"human\", \"{question}\")\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": null, "id": "ccb30c69", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "机器学习是人工智能的一个分支,指让计算机系统从数据中自动学习规律,并基于这些规律对新数据做出预测或决策,而无需显式编程。核心思想是通过算法拟合数据中的模式,常见类型包括监督学习、无监督学习和强化学习。\n" ] } ], "source": [ "response = llm.invoke(messages)\n", "print(response.content)" ] }, { "cell_type": "code", "execution_count": 11, "id": "eab4f668", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "给我讲⼀个关于程序员的笑话\n", "请⽤幽默的⻛格,写⼀篇关于加班的短⽂,字数不超过100字\n" ] } ], "source": [ "from langchain_core.prompts import PromptTemplate\n", "# 单变量模板\n", "prompt = PromptTemplate.from_template(\"给我讲⼀个关于{topic}的笑话\")\n", "print(prompt.format(topic=\"程序员\"))\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": 12, "id": "c05b27cc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "请分析成都在2026年06/26/2026, 22:08:48的天⽓趋势\n" ] } ], "source": [ "from langchain_core.prompts import PromptTemplate\n", "from datetime import datetime\n", "# ⼀个需要三个变量的模板\n", "prompt = PromptTemplate(\n", " template=\"请分析{city}在{year}年{date}的天⽓趋势\",\n", " input_variables=[\"city\", \"year\", \"date\"]\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", "# 只需填剩余的变量(这⾥已经全部填完了)\n", "print(partial_prompt.format())" ] }, { "cell_type": "code", "execution_count": 13, "id": "997ab072", "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", "examples = [\n", " {\"input\": \"YYDS\", \"output\": \"永远的神(极度赞美)\"},\n", " {\"input\": \"绝绝⼦\", \"output\": \"太棒了(强烈赞叹)\"},\n", " {\"input\": \"躺平\", \"output\": \"⼼态平和、不再内卷(不争不抢的⽣活态度)\"},\n", "]\n", "# 第⼆步:定义每个示例的展示格式\n", "example_prompt = PromptTemplate(\n", " input_variables=[\"input\", \"output\"],\n", " template=\"⿊话: {input}\\n翻译: {output}\"\n", ")\n", "# 第三步:组装完整的 Few-Shot Prompt\n", "prompt = FewShotPromptTemplate(\n", " examples=examples, # 示例列表\n", " example_prompt=example_prompt, # 每个示例的格式\n", " prefix=\"你是⼀个精通⽹络流⾏语的翻译官。请把⿊话翻译成正式的职场语⾔。\\n\", # 前缀(指令)\n", " suffix=\"⿊话: {input}\\n翻译:\", \n", " # 后缀(⽤户问题)\n", " input_variables=[\"input\"]\n", ")\n", "# 第四步:⽣成最终 prompt\n", "final_prompt = prompt.format(input=\"下头\")\n", "print(final_prompt)" ] }, { "cell_type": "code", "execution_count": null, "id": "db9385b2", "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", "# chains -> Linux 管道命令 => cat xxx.txt | grep Error\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": "d6057a92", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "翻译:令人失望、扫兴或产生负面情绪(指因他人言行或情境而感到不适、反感或兴致骤降)\n" ] } ], "source": [ "# 把 Few-Shot Prompt 丢给模型\n", "response = llm.invoke(final_prompt)\n", "print(response.content)" ] }, { "cell_type": "code", "execution_count": null, "id": "4f782c47", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "秦始皇统一六国的顺序为:**韩、赵、魏、楚、燕、齐**,即按“先近后远、弱优先强”的战略,从公元前230年到前221年依次灭亡。\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", "# 三段式链:Prompt → LLM → Parser\n", "chain = prompt | llm | parser\n", "result = chain.invoke({\"input\": \"秦始皇统⼀六国的顺序是什么?\"})\n", "print(result) # 直接是字符串,不需要 .content" ] }, { "cell_type": "code", "execution_count": 4, "id": "a473f272", "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", "parser = CommaSeparatedListOutputParser()\n", "# get_format_instructions() 会⽣成⼀段\"输出格式要求\"的⽂字\n", "# ⽐如 \"Your response should be a list of comma separated values...\"\n", "format_instructions = parser.get_format_instructions()\n", "prompt = ChatPromptTemplate.from_messages([\n", " (\"system\", f\"你是⼀个旅游顾问。{{format_instructions}}\"),\n", " (\"human\", \"推荐{city}的{count}个必去景点\")\n", "])\n", "\n", "chain = prompt | llm | parser\n", "result = chain.invoke({\n", " \"city\": \"成都\",\n", " \"count\": 3,\n", " \"format_instructions\": format_instructions\n", "})\n", "print(result) # ['武侯祠', '锦⾥', '⼤熊猫繁育研究基地'] ← 直接是 list" ] }, { "cell_type": "code", "execution_count": null, "id": "1ce84f47", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "书名:朝花夕拾\n", "作者:鲁迅\n", "体裁:['散文']\n", "\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", "\n", "# 创建解析器,它会⾃动⽣成 JSON Schema 格式要求\n", "parser = PydanticOutputParser(pydantic_object=BookInfo)\n", "prompt = ChatPromptTemplate.from_messages([\n", " (\"system\", \"你是⼀个图书管理员。请按格式要求输出,使⽤中⽂。\\n{format_instructions}\"),\n", " (\"human\", \"从以下简介中提取书籍信息:\\n{introduction}\")\n", "])\n", "\n", "chain = prompt | llm | parser\n", "introduction = \"\"\"\n", "《朝花夕拾》原名《旧事重提》,是鲁迅的散⽂集,收录1926年创作的10篇回忆性散⽂。\n", "⽂集以记事为主,饱含抒情⽓息,反映了作者⻘少年时期的⽣活。\n", "\"\"\"\n", "result = chain.invoke({\n", " \"introduction\": introduction,\n", " \"format_instructions\": parser.get_format_instructions()\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": 6, "id": "9eb783b1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "冷笑话:程序员冷笑话: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", "parser = StrOutputParser()\n", "\n", "# 定义两条独⽴的链\n", "joke_prompt = ChatPromptTemplate.from_template(\"讲⼀个关于{topic}的冷笑话,越短越好\")\n", "poem_prompt = ChatPromptTemplate.from_template(\"写⼀⾸关于{topic}的五⾔绝句\")\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']}\")\n", "# 两条链是并⾏执⾏的,总耗时约等于较慢的那条,⽽⾮两者之和" ] }, { "cell_type": "code", "execution_count": null, "id": "ccaf6436", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "你好,小明!很高兴认识你,一位程序员同行。👋\n", "\n", "不管你是做前端、后端、移动端,还是正在探索AI、数据科学,这里都有很多可以聊的。有什么编程问题、项目思路,或者想聊聊技术圈的最新动态,我都在线。需要帮忙写段代码、调个bug,还是单纯想吐槽一下需求变更?尽管说!😄\n", "哈哈,这个问题有点难倒我啦!作为一个AI,我没有办法知道你的名字哦~除非你告诉我,不然我只能猜个像“小可爱”、“神秘用户”之类的通用称呼啦!😄 你愿意告诉我你的名字吗?\n" ] } ], "source": [ "# 第⼀轮\n", "r1 = llm.invoke(\"我叫⼩明,我是⼀名程序员\")\n", "print(r1.content) # \"你好,⼩明!程序员是个很棒的职业...\"\n", "\n", "# 第⼆轮:问它记不记得\n", "r2 = llm.invoke(\"我叫什么名字?\")\n", "print(r2.content) # \"抱歉,我⽆法知道你的名字...\" ← 完全忘了" ] }, { "cell_type": "code", "execution_count": 8, "id": "ca3c9a6a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'history': [HumanMessage(content='我叫⼩明,是⼀名 Python 开发者', additional_kwargs={}, response_metadata={}), AIMessage(content='你好⼩明!Python 开发者很厉害呢', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[])]}\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/1r/_xp9zgm56dqbl9ytglx51ccw0000gn/T/ipykernel_43666/3430713032.py:4: 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" ] } ], "source": [ "from langchain_classic.memory import ConversationBufferMemory\n", "# 创建记忆实例\n", "# return_messages=True 表示返回 Message 对象⽽⾮纯⽂本\n", "memory = ConversationBufferMemory(return_messages=True)\n", "# ⼿动添加对话记录\n", "memory.save_context(\n", " {\"input\": \"我叫⼩明,是⼀名 Python 开发者\"}, # ⽤户输⼊\n", " {\"output\": \"你好⼩明!Python 开发者很厉害呢\"} # AI 回复\n", ")\n", "# 查看存储的历史\n", "print(memory.load_memory_variables({}))\n", "# {'history': [HumanMessage(content='我叫⼩明...'), AIMessage(content='你好⼩明...')]}" ] }, { "cell_type": "code", "execution_count": 9, "id": "7b87c884", "metadata": {}, "outputs": [ { "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", "# 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", "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": 10, "id": "ce36c4e8", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/1r/_xp9zgm56dqbl9ytglx51ccw0000gn/T/ipykernel_43666/350106213.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": [ "你好,小明!很高兴认识你,程序员朋友!👋 编程的世界可真是奇妙又充满挑战,对吧?既然你是程序员,那我猜你可能对算法、框架、或者调试那些“神秘”的bug感兴趣?或者最近在忙什么项目呢?比如用Python写个爬虫,用Java搭个后端,还是在前端用React画点酷炫的UI? \n", "\n", "说到程序员日常,我最近刚好读到一些有趣的数据——有调查说,程序员平均每天花42%的时间在“读代码”上(比写代码还多!),而最经典的“咖啡因依赖”梗也真是写实。你是属于键盘敲得飞起的“夜猫子”型,还是喜欢晨间高效debug的“早鸟”型? \n", "\n", "对了,如果你需要技术建议、代码优化思路,或者纯粹想聊聊编程梗(比如“为什么程序员总分不清万圣节和圣诞节?——因为 Oct 31 == Dec 25!”😂),我这儿随时恭候。另外,悄悄说一句:作为AI,我虽然不会写生产级代码,但我能帮你快速找文档、解算法题,甚至生成注释哦~ 所以,有什么想聊的或者需要帮忙的吗?\n", "哈哈,小明,你是在考我是不是认真听了你刚才的介绍吧?😄 根据我们之前的对话,你可是亲口说过:“我叫小明,我是程序员”呀!所以,你的工作就是——程序员!每天和代码、bug、需求文档斗智斗勇的那位大神~ \n", "\n", "需要我帮你回忆一下你具体做什么方向的吗?比如后端、前端、全栈、算法、运维……还是你更想聊聊最近在写的某个功能?或者吐槽一下某个难以复现的“幽灵bug”?👻\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\"]) # \"你是程序员呀!\" ← ⾃动记住" ] }, { "cell_type": "code", "execution_count": 11, "id": "ae691146", "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", "# ⾃定义 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", "r = chain.invoke({\"input\": \"今天⼼情不好\"})\n", "print(r[\"response\"]) # 会带着 emoji 回复你" ] }, { "cell_type": "code", "execution_count": 13, "id": "ad088103", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/leon/workspace/ai-agent/01_langchain/.venv/lib/python3.11/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.496亿公里**,天文学上常将这个距离定义为 **1个天文单位(AU)**。具体精确值为 **149,597,870.7公里**。由于地球轨道是椭圆形,在近日点(约1月初)距离约1.471亿公里,远日点(约7月初)距离约1.521亿公里。\n", "地球到月球的平均距离约为 **38.4万公里**(精确值为 **384,400公里**)。由于月球轨道也是椭圆形,近地点约 **363,300公里**,远地点约 **405,500公里**。这个距离大约是地球到太阳距离的 **1/390**。\n", "当然是**月球**更近。\n", "\n", "地球到月球的距离大约是 **38.4万公里**,而地球到太阳的距离大约是 **1.496亿公里**。月球距离我们只有太阳距离的 **大约 1/390**(即约0.26%)。\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", "\n", "# 定义 prompt(带历史占位符)\n", "prompt = ChatPromptTemplate.from_messages([\n", " (\"system\", \"你是⼀个擅⻓物理的助⼿\"),\n", " MessagesPlaceholder(variable_name=\"history\"),\n", " (\"human\", \"{question}\")\n", "])\n", "chain = prompt | llm | StrOutputParser()\n", "\n", "# MySQL 连接串(换成你⾃⼰的)\n", "mysql_url = \"mysql+pymysql://root:admin@localhost:3306/mydb\"\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_string=mysql_url,\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", "r1 = chain_with_history.invoke({\"question\": \"地球到太阳有多远?\"}, config=config)\n", "print(r1)\n", "r2 = chain_with_history.invoke({\"question\": \"到⽉球呢?\"}, config=config)\n", "print(r2)\n", "r3 = chain_with_history.invoke({\"question\": \"哪个更近?\"}, config=config)\n", "print(r3) # \"⽉球更近\" ← 能正确理解\"哪个\"指的是前两轮的内容" ] }, { "cell_type": "code", "execution_count": 14, "id": "24de915a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sleep\n", "wolfram-alpha\n", "google-search\n", "google-search-results-json\n", "searx-search-results-json\n", "bing-search\n", "metaphor-search\n", "ddg-search\n", "google-books\n", "google-lens\n", "google-serper\n", "google-scholar\n", "google-finance\n", "google-trends\n", "google-jobs\n", "google-serper-results-json\n", "searchapi\n", "searchapi-results-json\n", "serpapi\n", "dalle-image-generator\n", "twilio\n", "searx-search\n", "merriam-webster\n", "wikipedia\n", "arxiv\n", "golden-query\n", "pubmed\n", "human\n", "awslambda\n", "stackexchange\n", "sceneXplain\n", "graphql\n", "openweathermap-api\n", "dataforseo-api-search\n", "dataforseo-api-search-json\n", "eleven_labs_text2speech\n", "google_cloud_texttospeech\n", "read_file\n", "reddit_search\n", "news-api\n", "tmdb-api\n", "podcast-api\n", "memorize\n", "llm-math\n", "open-meteo-api\n", "requests\n", "requests_get\n", "requests_post\n", "requests_patch\n", "requests_put\n", "requests_delete\n", "terminal\n" ] } ], "source": [ "from langchain_classic.agents import get_all_tool_names\n", "# 获取所有可⽤⼯具的名称\n", "tool_names = get_all_tool_names()\n", "for name in tool_names:\n", " print(name)" ] }, { "cell_type": "code", "execution_count": 16, "id": "6263971b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "wikipedia A wrapper around Wikipedia. Useful for when you need to answer general questions about people, places, companies, facts, historical events, or other subjects. Input should be a search query. {'query': {'description': 'query to look up on wikipedia', 'title': 'Query', 'type': 'string'}}\n" ] }, { "data": { "text/plain": [ "'Page: Emperor Ai of Han\\nSummary: Emperor Ai of Han, personal name Liu Xin (劉欣; 25 BC – 15 August 1 B'" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#WikipediaQueryRun为例⼦\n", "#WikipediaQueryRun:⽤于向维基百科API发送查询并获取数据\n", "# pip install wikipedia -i https://pypi.tuna.tsinghua.edu.cn/simple\n", "from langchain_community.tools import WikipediaQueryRun\n", "from langchain_community.utilities import WikipediaAPIWrapper\n", "\n", "# top_k_results 收索结果的数量\n", "# doc_content_chars_max 单个Document的内容⻓度\n", "api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)\n", "tool = WikipediaQueryRun(api_wrapper=api_wrapper)\n", "\n", "print(tool.name, tool.description, tool.args)\n", "\n", "tool.run({\"query\": \"AI之⽗\"})\n" ] }, { "cell_type": "code", "execution_count": 17, "id": "1a60d605", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "get_weather\n", "查询指定城市的实时天⽓信息\n", "\n", " Args:\n", " location: 城市名称,如\"北京\"、\"成都\"\n", "{'location': {'title': 'Location', 'type': 'string'}}\n", "成都的天⽓:Mist,温度:24°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", " current = data['current_condition'][0]\n", " weather_desc = current['weatherDesc'][0]['value']\n", " temp = current['temp_C']\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) # \"成都的天⽓:Sunny,温度:28°C\"" ] }, { "cell_type": "code", "execution_count": 18, "id": "355fad5b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{'name': 'get_weather', 'args': {'location': '成都'}, 'id': 'call_00_ZqvbJi0z3lvlUqvPvLrM4387', 'type': 'tool_call'}]\n" ] } ], "source": [ "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", "response = llm_with_tools.invoke(messages)\n", "# LLM的输出不再是⼀段⾃然语⾔⽂本,⽽是⼀个结构化的JSON对象(tool_calls)。\n", "#这个对象包含了要调⽤的函数名和所需的参数。\n", "print(response.tool_calls)\n", "# [{'name': 'get_weather', 'args': {'location': '成都'}, 'id': 'call_xxx', 'type': 'tool_call'}]" ] }, { "cell_type": "code", "execution_count": 19, "id": "6217b1d6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "成都今天(现在)的天气情况如下:\n", "\n", "🌤 **天气状况:** 薄雾(Mist)\n", "🌡 **温度:** 24°C\n", "\n", "整体来说是一个比较舒适的温度,不冷不热,但由于有雾,能见度可能会受到一些影响。如果需要外出,建议注意行车安全,适当增减衣物。希望这个信息对你有帮助!😊\n" ] } ], "source": [ "from langchain_core.messages import ToolMessage\n", "# 假设 response 是上⾯ bind_tools 后调⽤的结果\n", "if response.tool_calls:\n", " tool_results = []\n", " for call in response.tool_calls:\n", " # 根据⼯具名找到对应的函数并执⾏\n", " if call[\"name\"] == \"get_weather\":\n", " result = get_weather.invoke(call[\"args\"])\n", " # 把执⾏结果封装成 ToolMessage\n", " tool_results.append(ToolMessage(\n", " content=result,\n", " tool_call_id=call[\"id\"] # 关联到对应的 tool_call\n", " ))\n", " \n", " # 把⼯具结果追加到消息列表,再发⼀次请求\n", " messages.extend([response] + tool_results)\n", " final_response = llm_with_tools.invoke(messages)\n", " print(final_response.content)\n", " # \"成都今天天⽓晴朗,温度28°C,很适合出⻔~\"" ] }, { "cell_type": "code", "execution_count": 20, "id": "f3bb285e", "metadata": {}, "outputs": [], "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", "@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", "tools = [get_weather, multiply]\n", "\n", "# ─── 第三步:创建 Agent ───\n", "# create_agent 把模型、⼯具、prompt 组装成 Agent\n", "agent = create_agent(\n", " model=llm,\n", " tools=tools,\n", " system_prompt=\"你是⼀名智能助⼿,可以调⽤⼯具帮助⽤户解决问题。\"\n", ")" ] }, { "cell_type": "code", "execution_count": 21, "id": "b7267213", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "成都目前的天气情况如下:\n", "\n", "- 🌫️ **天气状况**:薄雾(Mist)\n", "- 🌡️ **温度**:24°C\n", "\n", "总的来说,成都现在有点雾蒙蒙的,温度比较舒适,不算太热。出门的话建议注意能见度,开车或骑行多加小心哦!\n", "{'messages': [HumanMessage(content='成都的天⽓怎么样?', additional_kwargs={}, response_metadata={}, id='9a3574ae-9b5e-41a1-9d73-5f1fcbd8c49f'), AIMessage(content='好的,我来帮你查询成都的天气情况。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 74, 'prompt_tokens': 358, 'total_tokens': 432, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 20, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 358}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '1ca6b6f5-0259-4948-90c2-6718f4f79a56', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019f0950-7dad-70e1-9963-179ea59d47a5-0', tool_calls=[{'name': 'get_weather', 'args': {'location': '成都'}, 'id': 'call_00_vi6NTwWj7lVTuzJTmUWR1224', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 358, 'output_tokens': 74, 'total_tokens': 432, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 20}}), ToolMessage(content='成都:Mist,24°C', name='get_weather', id='7decfa77-93d1-47ce-8305-b567bad6bc76', tool_call_id='call_00_vi6NTwWj7lVTuzJTmUWR1224'), AIMessage(content='成都目前的天气情况如下:\\n\\n- 🌫️ **天气状况**:薄雾(Mist)\\n- 🌡️ **温度**:24°C\\n\\n总的来说,成都现在有点雾蒙蒙的,温度比较舒适,不算太热。出门的话建议注意能见度,开车或骑行多加小心哦!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 86, 'prompt_tokens': 452, 'total_tokens': 538, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 20, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 384}, 'prompt_cache_hit_tokens': 384, 'prompt_cache_miss_tokens': 68}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '1043316b-2fec-493c-8ec5-6a5db21a8a89', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019f0950-8752-78c0-88f2-7e1e53bc757f-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 452, 'output_tokens': 86, 'total_tokens': 538, 'input_token_details': {'cache_read': 384}, 'output_token_details': {'reasoning': 20}})]}\n" ] } ], "source": [ "# ─── 第四步:使⽤ ───\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": 22, "id": "0f07a37c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5乘以6等于 **30** ✅\n", "{'messages': [HumanMessage(content='5乘以6等于多少?', additional_kwargs={}, response_metadata={}, id='8bbbbc1b-7981-4f26-b2ac-1fdc8429a0e7'), AIMessage(content='5乘以6的结果是:', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 89, 'prompt_tokens': 358, 'total_tokens': 447, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 24, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 256}, 'prompt_cache_hit_tokens': 256, 'prompt_cache_miss_tokens': 102}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '340d18f1-635a-439a-be8e-fb7d04e2b2e5', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019f0951-0afc-7b91-932e-8d424fef6ee6-0', tool_calls=[{'name': 'multiply', 'args': {'a': 5, 'b': 6}, 'id': 'call_00_uJzWNyll5fFr23s0v17t2721', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 358, 'output_tokens': 89, 'total_tokens': 447, 'input_token_details': {'cache_read': 256}, 'output_token_details': {'reasoning': 24}}), ToolMessage(content='30', name='multiply', id='d2f6f329-63ce-44ab-9f72-3090aa317c3a', tool_call_id='call_00_uJzWNyll5fFr23s0v17t2721'), AIMessage(content='5乘以6等于 **30** ✅', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 460, 'total_tokens': 475, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 6, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 384}, 'prompt_cache_hit_tokens': 384, 'prompt_cache_miss_tokens': 76}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': 'c311a36f-2b70-4cbe-aa68-183a57a7d0da', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019f0951-1068-73a2-bede-2b7aef42157f-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 460, 'output_tokens': 15, 'total_tokens': 475, 'input_token_details': {'cache_read': 384}, 'output_token_details': {'reasoning': 6}})]}\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.x)", "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.undefined" } }, "nbformat": 4, "nbformat_minor": 5 }