{ "cells": [ { "cell_type": "code", "execution_count": 9, "id": "03d8e409", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "微积分是研究函数的变化率(微分)与累积量(积分)及其相互关系的数学分支。\n" ] } ], "source": [ "from langchain_openai import ChatOpenAI\n", "from dotenv import load_dotenv\n", "import os\n", "\n", "api_key = os.getenv('OPENAI_API_KEY')\n", "\n", "# 创建 DeepSeek 聊天模型实例\n", "# base_url 指向 DeepSeek 的兼容端点,而非 OpenAI 官方地址\n", "load_dotenv()\n", "llm = ChatOpenAI(\n", " model_name=\"deepseek-v4-flash\", # DeepSeek 的对话模型\n", " api_key=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": 3, "id": "366dc781", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "装饰器(Decorator)在你看来可能挺唬人,但说白了它就是“给函数穿衣服”——在不改变函数本身代码的前提下,给函数加一些额外的功能,比如记录日志、计算运行时间、校验权限等。\n", "\n", "想象一下:你有一个非常简单的函数,它只是说“你好”。如果每次调用这个函数之前,你都想先确认一下对方有没有权限(比如是否登录),你不想改动函数内部的代码(因为可能有很多地方都用它),那就可以写一个“包装器”函数,把这个“你好”函数像礼物一样包起来,在这个包装器里先做权限检查,再调用原函数。这个包装器就是**装饰器**。\n", "\n", "Python 里的装饰器用 `@` 符号放在函数定义上面,像这样:\n", "\n", "```python\n", "def decorator(func):\n", " def wrapper():\n", " print(\"检查权限...\") # 额外功能\n", " func() # 调用原函数\n", " print(\"记录日志...\") # 额外功能\n", " return wrapper\n", "\n", "@decorator\n", "def say_hello():\n", " print(\"你好\")\n", "```\n", "\n", "当你调用 `say_hello()` 时,实际上执行的是装饰器内部的 `wrapper` 函数,它会先打印“检查权限…”,然后打印“你好”,最后打印“记录日志…”。而 `say_hello` 本身的代码完全没有被改动过。\n", "\n", "所以,装饰器就是一个**高阶函数**(接收函数作为参数),它内部定义一个新函数(包装函数),在新函数中加入额外操作,然后返回这个新函数。Python 的 `@` 语法糖只是让你写起来更方便——本质上就是 `say_hello = decorator(say_hello)` 这一步的简写。\n", "\n", "**一句话总结**:装饰器就是给函数“套个壳”,在不改原函数代码的前提下,让函数拥有新的技能。\n" ] } ], "source": [ "from langchain_core.messages import 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)" ] }, { "cell_type": "code", "execution_count": 10, "id": "3ef122de", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "片名:星际穿越(Interstellar)\n", "年份:2014\n", "导演:克里斯托弗·诺兰\n", "评分:8.6\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", "\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": 12, "id": "5ddf6b79", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "流浪地球\n" ] } ], "source": [ "from typing_extensions import TypedDict\n", "\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": 13, "id": "2b6e9160", "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": 14, "id": "cf4a8346", "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": 15, "id": "dfd55f3a", "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": 16, "id": "86f7eb00", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "请分析成都在2026年06/26/2026, 16:36:19的天气趋势\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", "# 只需填剩余的变量(这里已经全部填完了)\n", "print(partial_prompt.format())" ] }, { "cell_type": "code", "execution_count": 17, "id": "74918bf5", "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)\n", "# 输出效果:\n", "# 你是一个精通网络流行语的翻译官。请把黑话翻译成正式的职场语言。\n", "#\n", "# 黑话: YYDS\n", "# 翻译: 永远的神(极度赞美)\n", "#\n", "# 黑话: 绝绝子\n", "# 翻译: 太棒了(强烈赞叹)\n", "#\n", "# 黑话: 躺平\n", "# 翻译: 心态平和、不再内卷(不争不抢的生活态度)\n", "#\n", "# 黑话: 下头\n", "# 翻译:" ] }, { "cell_type": "code", "execution_count": 18, "id": "9c999e85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "量子计算是一种利用量子力学中的叠加和纠缠等特性,通过量子比特并行处理信息,从而在特定问题上实现远超经典计算机性能的新型计算方式。\n" ] } ], "source": [ "from langchain_core.prompts import PromptTemplate\n", "from langchain_openai import ChatOpenAI\n", "\n", "llm = ChatOpenAI(\n", " model_name=\"deepseek-v4-flash\",\n", " api_key=api_key,\n", " base_url=\"https://api.deepseek.com\"\n", ")\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": 19, "id": "da3321b8", "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", "from langchain_openai import ChatOpenAI\n", "\n", "llm = ChatOpenAI(\n", " model_name=\"deepseek-v4-flash\",\n", " api_key=api_key,\n", " base_url=\"https://api.deepseek.com\"\n", ")\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": 20, "id": "3d1fab11", "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": 22, "id": "cd62de23", "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", "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": 23, "id": "9f0e16c5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "嗨,小明!很高兴认识你,作为一名程序员同行,咱们可是“自己人”啦!👨‍💻 不知道你平时主要写什么方向的代码?是前端、后端、全栈,还是对某个领域(比如AI、游戏、嵌入式)特别感兴趣?或者最近在忙什么有趣的项目吗?\n", "\n", "如果有任何技术问题、职业发展困惑,或者想聊聊编程的趣事,随时都可以问我~ 当然,如果你只是想打个招呼,我也很乐意陪你闲聊!😄\n", "你的名字我目前还不知道呢!😊 我们的对话刚刚开始,你还没有告诉我你的名字。如果你愿意的话,可以告诉我,这样我就能用你的名字来称呼你啦!或者你希望我继续用“你”来称呼也行。\n" ] } ], "source": [ "\n", "# 第一轮\n", "r1 = llm.invoke(\"我叫小明,我是一名程序员\")\n", "print(r1.content) # \"你好,小明!程序员是个很棒的职业...\"\n", "\n", "# 第二轮:问它记不记得\n", "r2 = llm.invoke(\"我叫什么名字?\")\n", "print(r2.content) # \"抱歉,我无法知道你的名字...\" ← 完全忘了" ] }, { "cell_type": "code", "execution_count": 1, "id": "5758f6ef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "get_weather\n", "查询指定城市的实时天气信息\n", "\n", " Args:\n", " location: 城市名称,如\"北京\"、\"成都\"\n", "{'location': {'title': 'Location', 'type': 'string'}}\n", "成都的天气:Partly cloudy,温度:27°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) # \"成都的天气:Sunny,温度:28°C\"" ] } ], "metadata": { "kernelspec": { "display_name": "02_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.15" } }, "nbformat": 4, "nbformat_minor": 5 }