{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "3a522fca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你问的这句话“chromaDB返回相似度分数的计算机制”，意思是：**ChromaDB（一个向量数据库）在返回查询结果时，里面附带的那个“相似度分数”到底是怎么算出来的？** 换句话说，是问背后的数学原理或算法。\n",
      "\n",
      "### 它在做什么？\n",
      "\n",
      "让我拆开来说：\n",
      "\n",
      "1. **ChromaDB 是什么？**  \n",
      "   它是一个专门存储和搜索“向量”的数据库。向量可以理解为一段数字列表（比如 `[0.1, 0.5, -0.2]`），用来表示文字、图片、音频等内容的“语义特征”。\n",
      "\n",
      "2. **返回相似度分数**  \n",
      "   当你向 ChromaDB 提交一个查询（比如一段文本或一个向量），它会在库中找出最接近的几个向量，并给每个结果附上一个分数，比如 `0.92`。这个分数表示查询向量和库中某个向量有多“像”。\n",
      "\n",
      "3. **计算机制**  \n",
      "   就是指这个分数是通过**哪种距离或相似度度量**算出来的。常用的方法有：\n",
      "   - **余弦相似度**：看两个向量的方向是否一致（取值范围 -1 ~ 1，越大越相似）。  \n",
      "   - **欧几里得距离**：看两个向量在多维空间中的直线距离（越小越相似）。  \n",
      "   - **点积**：简单的内积（适用于某些已归一化的场景）。\n",
      "\n",
      "   ChromaDB 默认使用 **余弦相似度**，但你也可以手动指定其他距离函数。\n",
      "\n",
      "### 总结一句话\n",
      "\n",
      "这句话是在询问：**当你用 ChromaDB 做相似度检索时，它内部用的是什么公式/算法，来计算那个“相似度分数”的？** 换句话说，就是搞清楚结果里的数字（比如 0.95）到底代表什么物理意义（是角度相似还是距离远近）。\n"
     ]
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "# 创建 DeepSeek 聊天模型实例\n",
    "# base_url 指向 DeepSeek 的兼容端点，而非 OpenAI 官方地址\n",
    "llm = ChatOpenAI(\n",
    "    model_name=\"deepseek-v4-flash\",              # DeepSeek 的对话模型\n",
    "    api_key=\"sk-8fd808112bf7488ab27dacf0cad09315\",                  # 在 platform.deepseek.com 获取\n",
    "    base_url=\"https://api.deepseek.com\"      # DeepSeek API 地址\n",
    ")\n",
    "\n",
    "query_stm = \"\"\"\n",
    "chromaDB返回相似度分数的计算机制\n",
    "\"\"\"\n",
    "# invoke 是 LangChain 统一的调用方法，返回 AIMessage 对象\n",
    "response = llm.invoke(f\"解释{query_stm}是什么意思，在做什么？\")\n",
    "print(response.content)  # .content 拿到纯文本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f77c487",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "237fc3ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "片名：绿皮书\n",
      "年份：2018\n",
      "导演：彼得·法雷里\n",
      "评分：8.2\n",
      "主角名字：托尼·利普\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",
    "    character_name: str = Field(description=\"主角名字\")\n",
    "\n",
    "llm = init_chat_model(\n",
    "    model=\"qwen-plus\",\n",
    "    model_provider=\"openai\",\n",
    "    api_key=\"sk-1283efb80348448180e87b3c56fb6f3d\",\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}\")\n",
    "print(f\"主角名字：{result.character_name}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "cc05cc04",
   "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": 6,
   "id": "3af58865",
   "metadata": {
    "vscode": {
     "languageId": "javascript"
    }
   },
   "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",
    "\n",
    "structured_llm = llm.with_structured_output(json_schema)\n",
    "result = structured_llm.invoke(\"介绍一下电影《哪吒之魔童降世》\")\n",
    "print(result)  # 返回的是 dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09fdc151",
   "metadata": {
    "vscode": {
     "languageId": "javascript"
    }
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "01_langchain (3.14.3)",
   "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.14.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
