{ "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", "\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 }