|
@@ -0,0 +1,219 @@
|
|
|
|
|
+{
|
|
|
|
|
+ "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
|
|
|
|
|
+}
|