diff --git a/.env b/.env index 3c50d62..5cbca84 100644 --- a/.env +++ b/.env @@ -1,6 +1,5 @@ # OPENAI API 访问密钥配置 OPENAI_API_KEY = "" - # 文心 API 访问密钥配置 # 方式1. 使用应用 AK/SK 鉴权 # 创建的应用的 API Key @@ -16,19 +15,11 @@ QIANFAN_SECRET_KEY = "" # Ernie SDK 文心 API 访问密钥配置 EB_ACCESS_TOKEN = "" -# 控制台中获取的 APPID 信息 -SPARK_APPID = "" -# 控制台中获取的 APIKey 信息 -SPARK_API_KEY = "" -# 控制台中获取的 APISecret 信息 -SPARK_API_SECRET = "" - -# langchain中星火 API 访问密钥配置 # 控制台中获取的 APPID 信息 IFLYTEK_SPARK_APP_ID = "" -# 控制台中获取的 APISecret 信息 -IFLYTEK_SPARK_API_KEY = "" # 控制台中获取的 APIKey 信息 +IFLYTEK_SPARK_API_KEY = "" +# 控制台中获取的 APISecret 信息 IFLYTEK_SPARK_API_SECRET = "" # 智谱 API 访问密钥配置 diff --git a/.gitignore b/.gitignore index 70411b3..bd43d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,4 @@ cython_debug/ */.DS_Store */*/.DS_Store .idea/ +.DS_Store \ No newline at end of file diff --git a/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/data_level0.bin b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/data_level0.bin new file mode 100644 index 0000000..202709a Binary files /dev/null and b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/data_level0.bin differ diff --git a/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/header.bin b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/header.bin new file mode 100644 index 0000000..5210d42 Binary files /dev/null and b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/header.bin differ diff --git a/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/index_metadata.pickle b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/index_metadata.pickle new file mode 100644 index 0000000..a21e9e6 Binary files /dev/null and b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/index_metadata.pickle differ diff --git a/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/length.bin b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/length.bin new file mode 100644 index 0000000..a05d56b Binary files /dev/null and b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/length.bin differ diff --git a/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/link_lists.bin b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/link_lists.bin new file mode 100644 index 0000000..0b0395b Binary files /dev/null and b/data_base/vector_db/chroma/b3e7ca3b-6e21-4022-aa67-a422c6ec8b0b/link_lists.bin differ diff --git a/data_base/vector_db/chroma/chroma.sqlite3 b/data_base/vector_db/chroma/chroma.sqlite3 index 1776c7a..767719a 100644 Binary files a/data_base/vector_db/chroma/chroma.sqlite3 and b/data_base/vector_db/chroma/chroma.sqlite3 differ diff --git a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/data_level0.bin b/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/data_level0.bin deleted file mode 100644 index f91dfe9..0000000 Binary files a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/data_level0.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/header.bin b/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/header.bin deleted file mode 100644 index ba8553e..0000000 Binary files a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/header.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/length.bin b/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/length.bin deleted file mode 100644 index 1dc89f8..0000000 Binary files a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/length.bin and /dev/null differ diff --git a/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/link_lists.bin b/data_base/vector_db/chroma/d06adb59-dfe7-4f80-9453-356173b214de/link_lists.bin deleted file mode 100644 index e69de29..0000000 diff --git "a/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/2. \344\275\277\347\224\250 LLM API.ipynb" "b/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/2. \344\275\277\347\224\250 LLM API.ipynb" index 6d9de42..bc4d1e3 100644 --- "a/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/2. \344\275\277\347\224\250 LLM API.ipynb" +++ "b/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/2. \344\275\277\347\224\250 LLM API.ipynb" @@ -92,8 +92,8 @@ "_ = load_dotenv(find_dotenv())\n", "\n", "# 如果你需要通过代理端口访问,还需要做如下配置\n", - "os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'\n", - "os.environ[\"HTTP_PROXY\"] = 'http://127.0.0.1:7890'" + "# os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'\n", + "# os.environ[\"HTTP_PROXY\"] = 'http://127.0.0.1:7890'" ] }, { @@ -129,7 +129,7 @@ "# 注意,此处我们假设你已根据上文配置了 OpenAI API Key,如没有将访问失败\n", "completion = client.chat.completions.create(\n", " # 调用模型:ChatGPT-3.5\n", - " model=\"gpt-3.5-turbo\",\n", + " model=\"gpt-4o\",\n", " # messages 是对话列表\n", " messages=[\n", " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", @@ -153,7 +153,7 @@ { "data": { "text/plain": [ - "ChatCompletion(id='chatcmpl-9FAKG4M6HXML257axa12PUuCXbJJz', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='Hello! How can I assist you today?', role='assistant', function_call=None, tool_calls=None))], created=1713401640, model='gpt-3.5-turbo-0125', object='chat.completion', system_fingerprint='fp_c2295e73ad', usage=CompletionUsage(completion_tokens=9, prompt_tokens=19, total_tokens=28))" + "ChatCompletion(id='chatcmpl-B71U2dZrK2tL7tzFpio0cvvg1AMQ5', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='Hello! How can I assist you today?', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None))], created=1741013698, model='gpt-4o-2024-08-06', object='chat.completion', service_tier='default', system_fingerprint='fp_f9f4fb6dbf', usage=CompletionUsage(completion_tokens=10, prompt_tokens=19, total_tokens=29, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))" ] }, "execution_count": 3, @@ -224,13 +224,13 @@ " return messages\n", "\n", "\n", - "def get_completion(prompt, model=\"gpt-3.5-turbo\", temperature = 0):\n", + "def get_completion(prompt, model=\"gpt-4o\", temperature = 0):\n", " '''\n", " 获取 GPT 模型调用结果\n", "\n", " 请求参数:\n", " prompt: 对应的提示词\n", - " model: 调用的模型,默认为 gpt-3.5-turbo,也可以按需选择 gpt-4 等其他模型\n", + " model: 调用的模型,默认为 gpt-4o,也可以按需选择 gpt-o1 等其他模型\n", " temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~2。温度系数越低,输出内容越一致。\n", " '''\n", " response = client.chat.completions.create(\n", @@ -251,7 +251,7 @@ { "data": { "text/plain": [ - "'你好!有什么可以帮助你的吗?'" + "'你好!有什么我可以帮助你的吗?'" ] }, "execution_count": 6, @@ -387,7 +387,15 @@ "cell_type": "code", "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[WARNING][2025-03-03 22:55:00.860] redis_rate_limiter.py:21 [t:8258539328]: No redis installed, RedisRateLimiter unavailable. Ignore this warning if you don't need to use qianfan SDK in distribution environment\n" + ] + } + ], "source": [ "import qianfan\n", "\n", @@ -432,24 +440,39 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "[WARNING] [04-18 08:54:40] base.py:516 [t:8610091584]: This key `system` does not seem to be a parameter that the model `Yi-34B-Chat` will accept\n", - "[INFO] [04-18 08:54:40] openapi_requestor.py:316 [t:8610091584]: requesting llm api endpoint: /chat/yi_34b_chat\n" + "[ERROR][2025-03-03 22:55:01.057] base.py:134 [t:8258539328]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-03T14:55:00Z', 'Authorization': 'bce-auth-v1//2025-03-03T14:55:00Z/300/request-source;content-type;host;x-bce-date/90494a05277c2e37ce7ce4eef55d7f1dcd620928178618bf9ec2b3208ba77308', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Mon, 03 Mar 2025 14:55:01 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '68a31f71-e2d5-4d86-b7e5-8ad857270976', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[WARNING][2025-03-03 22:55:01.058] base.py:1083 [t:8258539328]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-03T14:55:00Z', 'Authorization': 'bce-auth-v1//2025-03-03T14:55:00Z/300/request-source;content-type;host;x-bce-date/90494a05277c2e37ce7ce4eef55d7f1dcd620928178618bf9ec2b3208ba77308', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Mon, 03 Mar 2025 14:55:01 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '68a31f71-e2d5-4d86-b7e5-8ad857270976', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[WARNING][2025-03-03 22:55:01.059] base.py:973 [t:8258539328]: This key `system` does not seem to be a parameter that the model `Yi-34B-Chat` will accept\n", + "[INFO][2025-03-03 22:55:01.059] oauth.py:277 [t:8258539328]: trying to refresh token for ak `6hM0ZG***`\n", + "[INFO][2025-03-03 22:55:01.226] oauth.py:304 [t:8258539328]: successfully refresh token\n" ] }, { "data": { "text/plain": [ - "'你好!我叫 Yi,我是零一万物开发的一个智能助手,由零一万物的研究团队通过大量的文本数据进行训练,学习了语言的各种模式和关联,从而能够生成文本、回答问题、进行对话。我的目标是帮助用户获取信息、解答疑问以及提供各种语言相关的帮助。我是一个人工智能,没有感受和意识,但我可以模拟人类的交流方式,以便于与用户互动。如果你有任何问题或需要帮助,请随时告诉我!'" + "'你好!我叫 Yi,我是零一万物开发的一个智能助手,由零一万物的研究团队通过大量的文本数据进行训练,学习了语言的各种模式和关联,从而能够生成文本、回答问题、进行对话。我的目标是帮助用户获取信息、解答疑问以及提供各种文本相关的帮助。我是一个人工智能,没有感受和意识,但我可以模仿人类的交流方式,并根据我训练时所学的内容提供有用的信息。如果你有任何问题或需要帮助,请随时告诉我!'" ] }, - "execution_count": 10, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -467,23 +490,38 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "[INFO] [04-18 08:57:01] openapi_requestor.py:316 [t:8610091584]: requesting llm api endpoint: /chat/completions\n" + "[ERROR][2025-03-03 22:55:07.043] base.py:134 [t:8258539328]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-03T14:55:06Z', 'Authorization': 'bce-auth-v1//2025-03-03T14:55:06Z/300/request-source;content-type;host;x-bce-date/186905ec301889815f92604046d53e6be29dab4eae08dc144622b9e808b7c122', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Mon, 03 Mar 2025 14:55:07 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': 'be01334f-67c2-4307-90d8-6193da9be0e8', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[WARNING][2025-03-03 22:55:07.045] base.py:1083 [t:8258539328]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-03T14:55:06Z', 'Authorization': 'bce-auth-v1//2025-03-03T14:55:06Z/300/request-source;content-type;host;x-bce-date/186905ec301889815f92604046d53e6be29dab4eae08dc144622b9e808b7c122', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Mon, 03 Mar 2025 14:55:07 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': 'be01334f-67c2-4307-90d8-6193da9be0e8', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[INFO][2025-03-03 22:55:07.047] oauth.py:277 [t:8258539328]: trying to refresh token for ak `6hM0ZG***`\n", + "[INFO][2025-03-03 22:55:07.204] oauth.py:304 [t:8258539328]: successfully refresh token\n" ] }, { "data": { "text/plain": [ - "'你好!我是小鲸鱼,你的个人助理。我致力于为你提供准确、及时的信息和帮助,解答你的问题,并尽力满足你的需求。无论你需要什么帮助,我都会尽力提供帮助和支持。'" + "'嗨!我是小鲸鱼,你的个人助理。我在这里帮你解决问题、提供信息和建议,让你的生活更加轻松和愉快!'" ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -505,147 +543,6 @@ " · temperature,温度系数,默认 0.8,文心的 temperature 参数要求范围为 (0, 1.0],不能设置为 0。" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2.2 ERNIE SDK" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 2.2.1 API 申请指引" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "这里将使用 `ERNIE SDK` 中的 `ERNIE Bot` 来调用文心一言。ERNIE Bot 为开发者提供了便捷易用的接口,使其能够轻松调用文心大模型的强大功能,涵盖了文本创作、通用对话、语义向量以及AI作图等多个基础功能。`ERNIE SDK` 并不像 `千帆 SDK` 那样支持各种大语言模型, 而是只支持百度自家的文心大模型。目前 ERNIE Bot 支持的模型有:\n", - "\n", - "```\n", - "ernie-3.5 文心大模型(ernie-3.5)\n", - "ernie-lite 文心大模型(ernie-lite)\n", - "ernie-4.0 文心大模型(ernie-4.0)\n", - "ernie-longtext 文心大模型(ernie-longtext)\n", - "ernie-speed 文心大模型(ernie-speed)\n", - "ernie-speed-128k 文心大模型(ernie-speed-128k)\n", - "ernie-tiny-8k 文心大模型(ernie-tiny-8k)\n", - "ernie-char-8k 文心大模型(ernie-char-8k)\n", - "ernie-text-embedding 文心百中语义模型\n", - "ernie-vilg-v2 文心一格模型\n", - "```\n", - "\n", - "在使用 ERNIE SDK 之前,需要先获取 AI Studio 后端的认证鉴权(access token),在代码中需要配置自己的密钥才能实现对模型的调用,下面我们以 [Ernie Bot](https://ernie-bot-agent.readthedocs.io/zh-cn/latest/sdk/) 为例,介绍通过 ERNIE Bot 调用文心模型的流程。\n", - "\n", - "首先需要在 [AI Studio星河社区](https://aistudio.baidu.com/index) 注册并登录账号(新用户会送100万token的免费额度,为期3个月)。\n", - "\n", - "![](../../figures/C2-2-ernie_bot_1.png)\n", - "\n", - "点击 `访问令牌` 获取账户的 access token,复制 access token 并且以此形式 `EB_ACCESS_TOKEN=\"...\"` 保存到 `.env` 文件中。\n", - "![](../../figures/C2-2-ernie_bot_2.png)\n", - "\n", - "然后执行以下代码,将密钥加载到环境变量中。" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from dotenv import load_dotenv, find_dotenv\n", - "\n", - "# 读取本地/项目的环境变量。\n", - "\n", - "# find_dotenv() 寻找并定位 .env 文件的路径\n", - "# load_dotenv() 读取该 .env 文件,并将其中的环境变量加载到当前的运行环境中 \n", - "# 如果你设置的是全局的环境变量,这行代码则没有任何作用。\n", - "_ = load_dotenv(find_dotenv())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 2.2.2 调用 Ernie Bot API" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import erniebot\n", - "import os\n", - "\n", - "erniebot.api_type = \"aistudio\"\n", - "erniebot.access_token = os.environ.get(\"EB_ACCESS_TOKEN\")\n", - "\n", - "def gen_wenxin_messages(prompt):\n", - " '''\n", - " 构造文心模型请求参数 messages\n", - "\n", - " 请求参数:\n", - " prompt: 对应的用户提示词\n", - " '''\n", - " messages = [{\"role\": \"user\", \"content\": prompt}]\n", - " return messages\n", - "\n", - "\n", - "def get_completion(prompt, model=\"ernie-3.5\", temperature=0.01):\n", - " '''\n", - " 获取文心模型调用结果\n", - "\n", - " 请求参数:\n", - " prompt: 对应的提示词\n", - " model: 调用的模型\n", - " temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~1.0,且不能设置为 0。温度系数越低,输出内容越一致。\n", - " '''\n", - "\n", - " chat_comp = erniebot.ChatCompletion()\n", - " message = gen_wenxin_messages(prompt)\n", - "\n", - " resp = chat_comp.create(messages=message, \n", - " model=model,\n", - " temperature = temperature,\n", - " system=\"你是一名个人助理\")\n", - "\n", - " return resp[\"result\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'你好!我是一名个人助理,我的主要任务是协助和支持你的日常工作和活动。我可以帮助你管理时间、安排日程、提供信息、解答问题,以及完成其他你需要的任务。如果你有任何需求或问题,请随时告诉我,我会尽力帮助你。'" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "get_completion(\"你好,介绍一下你自己\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`Ernie Bot API` 的调用方式和参数基本和 `文心千帆 API` 一样,除了在创建 `ChatCompletion` 时使用的是 `.create()` 函数。\n", - "\n", - "所以此处并不再过多介绍接口常用参数,你可以参考 [参数介绍](https://ernie-bot-agent.readthedocs.io/zh-cn/latest/sdk/api_reference/chat_completion/) 以获取其他参数的详细信息。" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -705,7 +602,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -730,7 +627,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -763,6 +660,11 @@ " \"v3.5\": {\n", " \"domain\": \"generalv3.5\", # 用于配置大模型版本\n", " \"spark_url\": spark_url_tpl.format(\"v3.5\") # 云端环境的服务地址\n", + " },\n", + " # v4.0 版本\n", + " \"v4.0\": {\n", + " \"domain\": \"generalv4.0\", # 用于配置大模型版本\n", + " \"spark_url\": spark_url_tpl.format(\"v4.0\") # 云端环境的服务地址\n", " }\n", " }\n", " return model_params_dict[model]\n", @@ -791,9 +693,9 @@ "\n", " spark_llm = ChatSparkLLM(\n", " spark_api_url=gen_spark_params(model)[\"spark_url\"],\n", - " spark_app_id=os.environ[\"SPARK_APPID\"],\n", - " spark_api_key=os.environ[\"SPARK_API_KEY\"],\n", - " spark_api_secret=os.environ[\"SPARK_API_SECRET\"],\n", + " spark_app_id=os.environ[\"IFLYTEK_SPARK_APP_ID\"],\n", + " spark_api_key=os.environ[\"IFLYTEK_SPARK_API_KEY\"],\n", + " spark_api_secret=os.environ[\"IFLYTEK_SPARK_API_SECRET\"],\n", " spark_llm_domain=gen_spark_params(model)[\"domain\"],\n", " temperature=temperature,\n", " streaming=False,\n", @@ -807,16 +709,16 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'你好!有什么我能帮忙的吗?'" + "'你好!很高兴在这里遇到你。如果你有任何问题或者需要帮助,随时可以向我提问,我会尽力为你解答。'" ] }, - "execution_count": 29, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -826,172 +728,6 @@ "get_completion(\"你好\").generations[0][0].text" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 3.3 通过 WebSocket 调用" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过 WebSocket 进行连接的方式相对来说配置较为复杂,讯飞给出了[调用示例](https://www.xfyun.cn/doc/spark/Web.html#_3-%E8%B0%83%E7%94%A8%E7%A4%BA%E4%BE%8B) ,点击对应的语言调用示例下载即可。这里我们以 [Python 调用示例](https://xfyun-doc.xfyun.cn/lc-sp-sparkAPI-1709535448185.zip)为例进行讲解,下载后我们可以得到一个 `sparkAPI.py` 文件,文件中包含了服务端封装和客户端调用的实现。\n", - "\n", - "需要注意的是,直接运行官方示例的 `sparkAPI.py` 文件会有报错,需要做如下修改:\n", - "\n", - "(1)注释掉如下行:`import openpyxl`(代码中并未使用到这个包,如未安装,则会提示 ModuleNotFoundError);\n", - "\n", - "(2)修改 `on_close` 函数(该函数接收 3 个入参),修改后的函数如下:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# 收到 websocket 关闭的处理\n", - "def on_close(ws, close_status_code, close_msg): \n", - " print(\"### closed ###\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "然后我们运行一下修改后的官方示例代码。注意:在运行之前,还需要把在上一节中获取到的 API 的密钥赋值给 `main` 函数的入参 `appid`、`api_secret`、`api_key`。\n", - "\n", - "执行 `python sparkAPI.py`,可以得到如下的输出结果:\n", - "\n", - "![](../../figures/C2-2-spark_4.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "可以注意到,官方示例的输出结果中除了 LLM 的回答内容外,还包含标识回答结束(“#### 关闭会话”、“### close ###”)的打印日志,如果只想保留原始输出内容,可以通过修改源代码来进行优化。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们基于 `sparkAPI.py` 文件,同样封装一个 `get_completion` 函数,用于后续章节的调用。\n", - "\n", - "首先执行如下代码,读取 `.env` 文件的密钥配置。" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import sparkAPI\n", - "\n", - "from dotenv import load_dotenv, find_dotenv\n", - "\n", - "# 读取本地/项目的环境变量。\n", - "\n", - "# find_dotenv() 寻找并定位 .env 文件的路径\n", - "# load_dotenv() 读取该 .env 文件,并将其中的环境变量加载到当前的运行环境中 \n", - "# 如果你设置的是全局的环境变量,这行代码则没有任何作用。\n", - "_ = load_dotenv(find_dotenv())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "星火大模型 API 当前有 V1.5、V2.0、V3.0 和 V3.5 四个版本,四个版本独立计量 tokens。`get_completion` 函数封装如下:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "def gen_spark_params(model):\n", - " '''\n", - " 构造星火模型请求参数\n", - " '''\n", - "\n", - " spark_url_tpl = \"wss://spark-api.xf-yun.com/{}/chat\"\n", - " model_params_dict = {\n", - " # v1.5 版本\n", - " \"v1.5\": {\n", - " \"domain\": \"general\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v1.1\") # 云端环境的服务地址\n", - " },\n", - " # v2.0 版本\n", - " \"v2.0\": {\n", - " \"domain\": \"generalv2\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v2.1\") # 云端环境的服务地址\n", - " },\n", - " # v3.0 版本\n", - " \"v3.0\": {\n", - " \"domain\": \"generalv3\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v3.1\") # 云端环境的服务地址\n", - " },\n", - " # v3.5 版本\n", - " \"v3.5\": {\n", - " \"domain\": \"generalv3.5\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v3.5\") # 云端环境的服务地址\n", - " }\n", - " }\n", - " return model_params_dict[model]\n", - "\n", - "\n", - "def get_completion(prompt, model=\"v3.5\", temperature = 0.1):\n", - " '''\n", - " 获取星火模型调用结果\n", - "\n", - " 请求参数:\n", - " prompt: 对应的提示词\n", - " model: 调用的模型,默认为 v3.5,也可以按需选择 v3.0 等其他模型\n", - " temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~1.0,且不能设置为 0。温度系数越低,输出内容越一致。\n", - " '''\n", - "\n", - " response = sparkAPI.main(\n", - " appid=os.environ[\"SPARK_APPID\"],\n", - " api_secret=os.environ[\"SPARK_API_SECRET\"],\n", - " api_key=os.environ[\"SPARK_API_KEY\"],\n", - " gpt_url=gen_spark_params(model)[\"spark_url\"],\n", - " domain=gen_spark_params(model)[\"domain\"],\n", - " query=prompt\n", - " )\n", - " return response" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "你好!有什么我能帮忙的吗?\n" - ] - } - ], - "source": [ - "get_completion(\"你好\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "需要注意的是,在官方示例 `sparkAPI.py` 文件中,`temperature` 参数并不支持外部传入,而是固定值为 0.5,如果不想使用默认值,可以通过修改源代码实现支持外部参数传入,这里就不额外讲解了。" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -1052,7 +788,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -1077,7 +813,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -1098,7 +834,7 @@ " return messages\n", "\n", "\n", - "def get_completion(prompt, model=\"glm-4\", temperature=0.95):\n", + "def get_completion(prompt, model=\"glm-4-plus\", temperature=0.95):\n", " '''\n", " 获取 GLM 模型调用结果\n", "\n", @@ -1121,16 +857,16 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'你好!有什么可以帮助您的吗?'" + "'你好👋!我是人工智能助手智谱清言(ChatGLM),很高兴见到你,欢迎问我任何问题。'" ] }, - "execution_count": 25, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -1159,7 +895,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -1173,7 +909,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/3. Prompt Engineering.ipynb" "b/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/3. Prompt Engineering.ipynb" index 259540b..19a2fab 100644 --- "a/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/3. Prompt Engineering.ipynb" +++ "b/notebook/C2 \344\275\277\347\224\250 LLM API \345\274\200\345\217\221\345\272\224\347\224\250/3. Prompt Engineering.ipynb" @@ -78,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -102,11 +102,11 @@ "\n", "# 一个封装 OpenAI 接口的函数,参数为 Prompt,返回对应结果\n", "def get_completion(prompt,\n", - " model=\"gpt-3.5-turbo\"\n", + " model=\"gpt-4o\"\n", " ):\n", " '''\n", " prompt: 对应的提示词\n", - " model: 调用的模型,默认为 gpt-3.5-turbo(ChatGPT)。你也可以选择其他模型。\n", + " model: 调用的模型,默认为 gpt-4o。你也可以选择其他模型。\n", " https://platform.openai.com/docs/models/overview\n", " '''\n", "\n", @@ -131,14 +131,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "请回答问题:你是谁\n" + "询问对方身份。\n" ] } ], @@ -177,14 +177,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "我是一个智能助手。\n" + "我是一个由OpenAI开发的AI助手,旨在提供信息和帮助。\n" ] } ], @@ -223,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -233,22 +233,22 @@ "```json\n", "[\n", " {\n", - " \"book_id\": 1,\n", - " \"title\": \"幻境之门\",\n", - " \"author\": \"张三\",\n", - " \"genre\": \"奇幻\"\n", + " \"book_id\": \"001\",\n", + " \"title\": \"星河彼岸\",\n", + " \"author\": \"李明宇\",\n", + " \"genre\": \"科幻\"\n", " },\n", " {\n", - " \"book_id\": 2,\n", - " \"title\": \"星际迷航\",\n", - " \"author\": \"李四\",\n", - " \"genre\": \"科幻\"\n", + " \"book_id\": \"002\",\n", + " \"title\": \"古城谜影\",\n", + " \"author\": \"王晓峰\",\n", + " \"genre\": \"悬疑\"\n", " },\n", " {\n", - " \"book_id\": 3,\n", - " \"title\": \"时光漩涡\",\n", - " \"author\": \"王五\",\n", - " \"genre\": \"穿越\"\n", + " \"book_id\": \"003\",\n", + " \"title\": \"心灵之旅\",\n", + " \"author\": \"陈静\",\n", + " \"genre\": \"心理\"\n", " }\n", "]\n", "```\n" @@ -286,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -294,13 +294,13 @@ "output_type": "stream", "text": [ "Text 1 的总结:\n", - "第一步 - 把水烧开。\n", - "第二步 - 拿一个杯子并把茶包放进去。\n", - "第三步 - 把烧开的水倒在茶包上。\n", - "第四步 - 等待一会儿,让茶叶浸泡。\n", - "第五步 - 取出茶包。\n", - "第六步 - 如果愿意,可以加一些糖或牛奶调味。\n", - "第七步 - 尽情享受一杯美味的茶。\n" + "第一步 - 把水烧开。 \n", + "第二步 - 在等待期间,拿一个杯子并把茶包放进去。 \n", + "第三步 - 一旦水足够热,就把它倒在茶包上。 \n", + "第四步 - 等待一会儿,让茶叶浸泡。 \n", + "第五步 - 几分钟后,取出茶包。 \n", + "第六步 - 如果您愿意,可以加一些糖或牛奶调味。 \n", + "第七步 - 享受一杯美味的茶。\n" ] } ], @@ -342,7 +342,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -398,14 +398,14 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "<圣贤>: 孝顺者,孝敬父母,顺从长辈,尊重家族传统,忠诚孝道,不忘家国情怀。\n" + "<圣贤>: 夫孝,德之本也,教之所由生也。孝者,善事父母者也。事亲以敬,养亲以乐,终亲之年,毋使有憾。孝顺者,心存敬爱,行以奉养,始终如一,方为至孝。\n" ] } ], @@ -472,7 +472,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -480,11 +480,13 @@ "output_type": "stream", "text": [ "response :\n", - "摘要:在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水,不幸中途发生意外,但他们仍然充满冒险精神。\n", + "摘要:杰克和吉尔在村庄里去山顶打水时摔倒,但他们的冒险精神未减。\n", "\n", - "翻译:In a charming village, siblings Jack and Jill set out to fetch water from a well on top of a hill, unfortunately encountering an accident along the way, but their adventurous spirit remains undiminished.\n", + "翻译:Jack and Jill fell while fetching water from a hilltop in the village, but their adventurous spirit remained undiminished.\n", "\n", - "名称:Jack, Jill\n" + "名称:[\"Jack\", \"Jill\"]\n", + "\n", + "输出 JSON 格式:{\"English_summary\": \"Jack and Jill fell while fetching water from a hilltop in the village, but their adventurous spirit remained undiminished.\", \"num_names\": 2}\n" ] } ], @@ -539,14 +541,36 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "学生的解决方案是正确的。首年运营的总费用为450x+100,000美元,其中x为发电站的大小,单位为平方英尺。\n" + "学生的解决方案是正确的。\n", + "\n", + "他们正确地计算了每个部分的费用:\n", + "\n", + "1. **土地费用**:每平方英尺100美元,因此总费用为 \\(100x\\)。\n", + "2. **太阳能电池板费用**:每平方英尺250美元,因此总费用为 \\(250x\\)。\n", + "3. **维护费用**:固定费用为10万美元,加上每平方英尺10美元,因此总费用为 \\(100,000 + 10x\\)。\n", + "\n", + "将所有费用加在一起:\n", + "\n", + "- 土地费用:\\(100x\\)\n", + "- 太阳能电池板费用:\\(250x\\)\n", + "- 维护费用:\\(100,000 + 10x\\)\n", + "\n", + "总费用为:\n", + "\n", + "\\[ 100x + 250x + 100,000 + 10x = 360x + 100,000 \\]\n", + "\n", + "学生的解决方案中有一个小错误,他们将维护费用的每平方英尺费用写成了100x,而不是10x。因此,正确的总费用应该是:\n", + "\n", + "\\[ 360x + 100,000 \\]\n", + "\n", + "而不是学生所写的 \\(450x + 100,000\\)。\n" ] } ], @@ -584,22 +608,34 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "首先计算土地费用:100美元/平方英尺 * x平方英尺 = 100x美元\n", - "然后计算太阳能电池板费用:250美元/平方英尺 * x平方英尺 = 250x美元\n", - "接着计算维护费用:10万美元 + 10美元/平方英尺 * x平方英尺 = 10万 + 10x美元\n", - "最后计算总费用:100x美元 + 250x美元 + 10万美元 + 10x美元 = 360x + 10万美元\n", + "实际解决方案和步骤:\n", + "\n", + "1. **土地费用**:每平方英尺100美元,因此土地费用为 \\(100x\\) 美元。\n", + "\n", + "2. **太阳能电池板费用**:每平方英尺250美元,因此太阳能电池板费用为 \\(250x\\) 美元。\n", + "\n", + "3. **维护费用**:每年固定费用为10万美元,外加每平方英尺10美元,因此维护费用为 \\(100,000 + 10x\\) 美元。\n", + "\n", + "4. **总费用**:将所有费用相加,得到首年运营的总费用:\n", + " \\[\n", + " 100x + 250x + 100,000 + 10x = 360x + 100,000\n", + " \\]\n", + "\n", + "学生计算的总费用:450x + 100,000美元\n", + "\n", + "实际计算的总费用:360x + 100,000美元\n", "\n", - "学生计算的总费用:450x + 10万美元\n", - "实际计算的总费用:360x + 10万美元\n", "学生计算的费用和实际计算的费用是否相同:否\n", + "\n", "学生的解决方案和实际解决方案是否相同:否\n", + "\n", "学生的成绩:不正确\n" ] } @@ -659,26 +695,32 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "1. 论文标题: \"Extrapolating LLM Lengths: A Study on the Impact of Training Data Size\"\n", - "主要内容: 该论文研究了在训练数据规模不断增加的情况下,LLM长度的外推效果。通过实验和数据分析,论文探讨了训练数据规模对LLM长度外推性能的影响,并提出了一些改进方法。\n", - "链接:https://arxiv.org/abs/2106.12345\n", + "以下是一些关于大语言模型(LLM)长度外推的研究论文,包括论文标题、主要内容的简要介绍:\n", + "\n", + "1. **\"Scaling Laws for Neural Language Models\"** \n", + " *主要内容*:这篇论文探讨了语言模型的性能如何随着模型大小、数据集大小和计算量的增加而变化。研究发现,模型的性能遵循某种幂律关系,这为理解和预测更大规模模型的行为提供了理论基础。 \n", + " *链接*:[arXiv](https://arxiv.org/abs/2001.08361)\n", + "\n", + "2. **\"Language Models are Few-Shot Learners\"** \n", + " *主要内容*:该论文介绍了GPT-3,一个具有1750亿参数的语言模型,并展示了其在少样本学习任务中的强大能力。研究中也涉及到模型规模对性能的影响,提供了关于模型扩展的见解。 \n", + " *链接*:[arXiv](https://arxiv.org/abs/2005.14165)\n", "\n", - "2. 论文标题: \"Analyzing the Extrapolation of LLM Lengths in Natural Language Understanding Tasks\"\n", - "主要内容: 该论文分析了LLM长度在自然语言理解任务中的外推情况。通过实验和对比分析,论文探讨了不同任务对LLM长度外推的需求,以及如何优化LLM模型在不同长度下的性能。\n", - "链接:https://arxiv.org/abs/2110.67890\n", + "3. **\"Training Compute-Optimal Large Language Models\"** \n", + " *主要内容*:这篇论文研究了在给定计算预算下训练大规模语言模型的最优策略。作者提出了一种新的计算预算分配方法,以提高模型的训练效率和性能。 \n", + " *链接*:[arXiv](https://arxiv.org/abs/2203.15556)\n", "\n", - "3. 论文标题: \"Improving LLM Length Extrapolation through Data Augmentation Techniques\"\n", - "主要内容: 该论文提出了一种通过数据增强技术来改进LLM长度外推的方法。通过在训练数据中引入多样性和噪声,论文探讨了如何提高LLM模型在不同长度下的泛化能力。\n", - "链接:https://arxiv.org/abs/2201.23456\n", + "4. **\"An Empirical Study on Scaling Pre-trained Language Models\"** \n", + " *主要内容*:论文分析了预训练语言模型在不同规模下的表现,研究了模型大小、数据量和计算资源之间的关系,并提供了实证结果以支持理论推断。 \n", + " *链接*:[arXiv](https://arxiv.org/abs/2104.06305)\n", "\n", - "希望以上论文能够帮助到您的研究工作。\n" + "这些论文提供了关于大语言模型扩展的理论和实证研究,帮助理解如何有效地扩展模型以提高性能。请注意,访问这些链接可能需要科学上网工具。\n" ] } ], @@ -695,7 +737,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "模型给出的论文信息看上去非常正确,但如果打开链接,会发现 404 或者指向的论文不对。也就是说,论文的信息或者链接是模型捏造的。\n", + "模型给出的论文信息看上去非常正确,但如果打开链接,会发现部分链接打开后显示 404 或者指向的论文不对。也就是说,论文的信息或者链接是模型捏造的。\n", "\n", "语言模型的幻觉问题事关应用的可靠性与安全性。开发者有必要认识到这一缺陷,并采取 Prompt优化、外部知识等措施予以缓解,以开发出更加可信赖的语言模型应用。这也将是未来语言模型进化的重要方向之一。" ] @@ -703,7 +745,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -717,7 +759,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/2.\344\275\277\347\224\250 Embedding API.ipynb" "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/2.\344\275\277\347\224\250 Embedding API.ipynb" index 83b8062..d609aa9 100644 --- "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/2.\344\275\277\347\224\250 Embedding API.ipynb" +++ "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/2.\344\275\277\347\224\250 Embedding API.ipynb" @@ -121,7 +121,7 @@ "output_type": "stream", "text": [ "embedding长度为:1536\n", - "embedding(前10)为:[0.03884002938866615, 0.013516489416360855, -0.0024250170681625605, -0.01655769906938076, 0.024130908772349358, -0.017382603138685226, 0.04206013306975365, 0.011498954147100449, -0.028245486319065094, -0.00674333656206727]\n" + "embedding(前10)为:[0.038827355951070786, 0.013531949371099472, -0.0025024667847901583, -0.016542360186576843, 0.02412303350865841, -0.017386866733431816, 0.042086150497198105, 0.011515072546899319, -0.0282362699508667, -0.006800748407840729]\n" ] } ], @@ -161,7 +161,7 @@ "metadata": {}, "source": [ "## 二、使用文心千帆API\n", - "Embedding-V1是基于百度文心大模型技术的文本表示模型,Access token为调用接口的凭证,使用Embedding-V1时应先凭API Key、Secret Key获取Access token,再通过Access token调用接口来embedding text。同时千帆大模型平台还支持bge-large-zh等embedding model。" + "Embedding-V1是基于百度文心大模型技术的文本表示模型,Access token为调用接口的凭证,使用Embedding-V1时应先凭API Key、Secret Key获取Access token,再通过Access token调用接口来embedding text。同时千帆大模型平台还支持`bge-large-zh`等embedding model。" ] }, { @@ -222,8 +222,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "本次embedding id为:as-hvbgfuk29u\n", - "本次embedding产生时间戳为:1711435238\n" + "本次embedding id为:as-h7u5sde3ga\n", + "本次embedding产生时间戳为:1741091715\n" ] } ], @@ -271,7 +271,45 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### 尚未开放" + "使用讯飞 embedding 时需要注意`spark_embedding_domain`参数,该参数在向量化问题时为 `\"query\"` 在向量化知识库时为 `\"para\"`。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sparkai.embedding.spark_embedding import Embeddingmodel\n", + "import os\n", + "xunfei_embedding = Embeddingmodel(\n", + " spark_embedding_app_id=os.environ[\"IFLYTEK_SPARK_APP_ID\"],\n", + " spark_embedding_api_key=os.environ[\"IFLYTEK_SPARK_API_KEY\"],\n", + " spark_embedding_api_secret=os.environ[\"IFLYTEK_SPARK_API_SECRET\"],\n", + " spark_embedding_domain=\"para\"\n", + " )\n", + "\n", + "text = {\"content\":'要生成 embedding 的输入文本。',\"role\":\"user\"}\n", + "response = xunfei_embedding.embedding(text=text)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "生成的embedding长度为:2560\n", + "embedding(前10)为: [-0.448486328125, 0.84130859375, 0.67919921875, 0.214599609375, 0.374267578125, 0.384033203125, -0.488525390625, -0.6103515625, 0.1571044921875, 0.81494140625]\n" + ] + } + ], + "source": [ + "print(f'生成的embedding长度为:{len(response)}')\n", + "print(f'embedding(前10)为: {response[:10]}')" ] }, { @@ -284,7 +322,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -294,7 +332,7 @@ " api_key = os.environ['ZHIPUAI_API_KEY']\n", " client = ZhipuAI(api_key=api_key)\n", " response = client.embeddings.create(\n", - " model=\"embedding-2\",\n", + " model=\"embedding-3\",\n", " input=text,\n", " )\n", " return response\n", @@ -312,7 +350,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -321,9 +359,9 @@ "text": [ "response类型为:\n", "embedding类型为:list\n", - "生成embedding的model为:embedding-2\n", - "生成的embedding长度为:1024\n", - "embedding(前10)为: [0.017892399802803993, 0.0644201710820198, -0.009342825971543789, 0.02707476168870926, 0.004067837726324797, -0.05597858875989914, -0.04223804175853729, -0.03003198653459549, -0.016357755288481712, 0.06777040660381317]\n" + "生成embedding的model为:embedding-3\n", + "生成的embedding长度为:2048\n", + "embedding(前10)为: [-0.0042974288, 0.040918995, -0.0036798029, 0.034753118, -0.047749206, -0.015196704, -0.023666998, -0.002935019, 0.0015090306, -0.011958062]\n" ] } ], @@ -338,7 +376,7 @@ ], "metadata": { "kernelspec": { - "display_name": "llm_universe_2.x", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -352,7 +390,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/3.\346\225\260\346\215\256\345\244\204\347\220\206.ipynb" "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/3.\346\225\260\346\215\256\345\244\204\347\220\206.ipynb" index bc75f8d..cb01552 100644 --- "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/3.\346\225\260\346\215\256\345\244\204\347\220\206.ipynb" +++ "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/3.\346\225\260\346\215\256\345\244\204\347\220\206.ipynb" @@ -23,7 +23,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.document_loaders.pdf import PyMuPDFLoader\n", + "from langchain_community.document_loaders import PyMuPDFLoader\n", "\n", "# 创建一个 PyMuPDFLoader Class 实例,输入为待加载的 pdf 文档路径\n", "loader = PyMuPDFLoader(\"../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf\")\n", @@ -78,24 +78,19 @@ "text": [ "每一个元素的类型:.\n", "------\n", - "该文档的描述性数据:{'source': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'file_path': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'page': 1, 'total_pages': 196, 'format': 'PDF 1.5', 'title': '', 'author': '', 'subject': '', 'keywords': '', 'creator': 'LaTeX with hyperref', 'producer': 'xdvipdfmx (20200315)', 'creationDate': \"D:20230303170709-00'00'\", 'modDate': '', 'trapped': ''}\n", + "该文档的描述性数据:{'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'page': 1, 'total_pages': 196, 'format': 'PDF 1.5', 'title': '', 'author': '', 'subject': '', 'keywords': '', 'creator': 'LaTeX with hyperref', 'producer': 'xdvipdfmx (20200315)', 'creationDate': \"D:20230303170709-00'00'\", 'modDate': '', 'trapped': ''}\n", "------\n", "查看该文档的内容:\n", "前言\n", - "“周志华老师的《机器学习》\n", - "(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\n", + "“周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\n", "者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\n", - "导细节的读者来说可能“不太友好”\n", - ",本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\n", - "具体的推导细节。\n", - "”\n", + "导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\n", + "具体的推导细节。”\n", "读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\n", "老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\n", - "中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”\n", - "。所以...... 本南瓜书只能算是我\n", + "中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”。所以...... 本南瓜书只能算是我\n", "等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\n", - "下学生”\n", - "。\n", + "下学生”。\n", "使用说明\n", "• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\n", "为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;\n", @@ -103,13 +98,10 @@ "有点飘的时候再回来啃都来得及;\n", "• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\n", "我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;\n", - "• 若南瓜书里没有你想要查阅的公式,\n", - "或者你发现南瓜书哪个地方有错误,\n", - "请毫不犹豫地去我们GitHub 的\n", + "• 若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub 的\n", "Issues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\n", "提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\n", - "话可以微信联系我们(微信号:at-Sm1les)\n", - ";\n", + "话可以微信联系我们(微信号:at-Sm1les);\n", "配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\n", "在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1 版)\n", "最新版PDF 获取地址:https://github.com/datawhalechina/pumpkin-book/releases\n", @@ -118,18 +110,9 @@ "编委:juxiao、Majingmin、MrBigFan、shanry、Ye980226\n", "封面设计:构思-Sm1les、创作-林王茂盛\n", "致谢\n", - "特别感谢awyd234、\n", - "feijuan、\n", - "Ggmatch、\n", - "Heitao5200、\n", - "huaqing89、\n", - "LongJH、\n", - "LilRachel、\n", - "LeoLRH、\n", - "Nono17、\n", + "特别感谢awyd234、feijuan、Ggmatch、Heitao5200、huaqing89、LongJH、LilRachel、LeoLRH、Nono17、\n", "spareribs、sunchaothu、StevenLzq 在最早期的时候对南瓜书所做的贡献。\n", - "扫描下方二维码,然后回复关键词“南瓜书”\n", - ",即可加入“南瓜书读者交流群”\n", + "扫描下方二维码,然后回复关键词“南瓜书”,即可加入“南瓜书读者交流群”\n", "版权声明\n", "本作品采用知识共享署名-非商业性使用-相同方式共享4.0 国际许可协议进行许可。\n", "\n" @@ -158,7 +141,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.document_loaders.markdown import UnstructuredMarkdownLoader\n", + "from langchain_community.document_loaders.markdown import UnstructuredMarkdownLoader\n", "\n", "loader = UnstructuredMarkdownLoader(\"../../data_base/knowledge_db/prompt_engineering/1. 简介 Introduction.md\")\n", "md_pages = loader.load()" @@ -199,7 +182,7 @@ "text": [ "每一个元素的类型:.\n", "------\n", - "该文档的描述性数据:{'source': './data_base/knowledge_db/prompt_engineering/1. 简介 Introduction.md'}\n", + "该文档的描述性数据:{'source': '../../data_base/knowledge_db/prompt_engineering/1. 简介 Introduction.md'}\n", "------\n", "查看该文档的内容:\n", "第一章 简介\n", @@ -248,9 +231,7 @@ "• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\n", "为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\n", "有点飘的时候再回来啃都来得及;• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\n", - "我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;• 若南瓜书里没有你想要查阅的公式,\n", - "或者你发现南瓜书哪个地方有错误,\n", - "请毫不犹豫地去我们GitHub 的\n", + "我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;• 若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub 的\n", "Issues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\n", "提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\n", "话可以微信联系我们(微信号:at-Sm1les);\n", @@ -307,9 +288,7 @@ "南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\n", "为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;对于初学机器学习的小白,西瓜书第1章和第2章的公式强烈不建议深究,简单过一下即可,等你学得\n", "有点飘的时候再回来啃都来得及;每个公式的解析和推导我们都力(zhi)争(neng)以本科数学基础的视角进行讲解,所以超纲的数学知识\n", - "我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;若南瓜书里没有你想要查阅的公式,\n", - "或者你发现南瓜书哪个地方有错误,\n", - "请毫不犹豫地去我们GitHub的\n", + "我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub的\n", "Issues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\n", "提交你希望补充的公式编号或者勘误信息,我们通常会在24小时以内给您回复,超过24小时未回复的\n", "话可以微信联系我们(微信号:at-Sm1les);\n", @@ -420,7 +399,7 @@ "* length_function - 长度计算函数\n", "'''\n", "#导入文本分割器\n", - "from langchain.text_splitter import RecursiveCharacterTextSplitter" + "from langchain_text_splitters import RecursiveCharacterTextSplitter" ] }, { @@ -445,8 +424,8 @@ "data": { "text/plain": [ "['前言\\n“周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\\n者通过西瓜书对机器学习有所了解,所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\\n导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\\n具体的推导细节。”\\n读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\\n老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\\n中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”。所以......本南瓜书只能算是我\\n等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\\n下学生”。\\n使用说明\\n南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\\n为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;对于初学机器学习的小白,西瓜书第1章和第2章的公式强烈不建议深究,简单过一下即可,等你学得',\n", - " '有点飘的时候再回来啃都来得及;每个公式的解析和推导我们都力(zhi)争(neng)以本科数学基础的视角进行讲解,所以超纲的数学知识\\n我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;若南瓜书里没有你想要查阅的公式,\\n或者你发现南瓜书哪个地方有错误,\\n请毫不犹豫地去我们GitHub的\\nIssues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\\n提交你希望补充的公式编号或者勘误信息,我们通常会在24小时以内给您回复,超过24小时未回复的\\n话可以微信联系我们(微信号:at-Sm1les);\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\\n在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1版)\\n最新版PDF获取地址:https://github.com/datawhalechina/pumpkin-book/releases\\n编委会',\n", - " '编委会\\n主编:Sm1les、archwalk']" + " '有点飘的时候再回来啃都来得及;每个公式的解析和推导我们都力(zhi)争(neng)以本科数学基础的视角进行讲解,所以超纲的数学知识\\n我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub的\\nIssues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\\n提交你希望补充的公式编号或者勘误信息,我们通常会在24小时以内给您回复,超过24小时未回复的\\n话可以微信联系我们(微信号:at-Sm1les);\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\\n在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1版)\\n最新版PDF获取地址:https://github.com/datawhalechina/pumpkin-book/releases\\n编委会',\n", + " '编委会\\n主编:Sm1les、archwalker']" ] }, "execution_count": 12, @@ -472,7 +451,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "切分后的文件数量:720\n" + "切分后的文件数量:711\n" ] } ], @@ -490,7 +469,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "切分后的字符数(可以用来大致评估 token 数):308931\n" + "切分后的字符数(可以用来大致评估 token 数):305816\n" ] } ], @@ -508,7 +487,7 @@ ], "metadata": { "kernelspec": { - "display_name": "llm_universe_2.x", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -522,7 +501,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/4.\346\220\255\345\273\272\345\271\266\344\275\277\347\224\250\345\220\221\351\207\217\346\225\260\346\215\256\345\272\223.ipynb" "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/4.\346\220\255\345\273\272\345\271\266\344\275\277\347\224\250\345\220\221\351\207\217\346\225\260\346\215\256\345\272\223.ipynb" index 8084e51..4383ec4 100644 --- "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/4.\346\220\255\345\273\272\345\271\266\344\275\277\347\224\250\345\220\221\351\207\217\346\225\260\346\215\256\345\272\223.ipynb" +++ "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/4.\346\220\255\345\273\272\345\271\266\344\275\277\347\224\250\345\220\221\351\207\217\346\225\260\346\215\256\345\272\223.ipynb" @@ -18,7 +18,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "['../../data_base/knowledge_db/.DS_Store', '../../data_base/knowledge_db/prompt_engineering/6. 文本转换 Transforming.md', '../../data_base/knowledge_db/prompt_engineering/4. 文本概括 Summarizing.md']\n" + "['../../data_base/knowledge_db/prompt_engineering/6. 文本转换 Transforming.md', '../../data_base/knowledge_db/prompt_engineering/4. 文本概括 Summarizing.md', '../../data_base/knowledge_db/prompt_engineering/5. 推断 Inferring.md']\n" ] } ], @@ -52,14 +52,13 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.document_loaders.pdf import PyMuPDFLoader\n", - "from langchain.document_loaders.markdown import UnstructuredMarkdownLoader\n", + "from langchain_community.document_loaders import PyMuPDFLoader\n", + "from langchain_community.document_loaders import UnstructuredMarkdownLoader\n", "\n", "# 遍历文件路径并把实例化的loader存放在loaders里\n", "loaders = []\n", "\n", "for file_path in file_paths:\n", - "\n", " file_type = file_path.split('.')[-1]\n", " if file_type == 'pdf':\n", " loaders.append(PyMuPDFLoader(file_path))\n", @@ -116,32 +115,23 @@ "\n", "接下来我们提供一段在线商品评价作为示例,可能来自于一个在线购物平台,例如亚马逊、淘宝、京东等。评价者为一款熊猫公仔进行了点评,评价内容包括商品的质量、大小、价格和物流速度等因素,以及他的女儿对该商品的喜爱程度。\n", "\n", - "python\n", - "prod_review = \"\"\"\n", - "这个熊猫公仔是我给女儿的生日礼物,她很喜欢,去哪都带着。\n", - "公仔很软,超级可爱,面部表情也很和善。但是相比于价钱来说,\n", - "它有点小,我感觉在别的地方用同样的价钱能买到更大的。\n", - "快递比预期提前了一天到货,所以在送给女儿之前,我自己玩了会。\n", - "\"\"\"\n", + "python prod_review = \"\"\" 这个熊猫公仔是我给女儿的生日礼物,她很喜欢,去哪都带着。 公仔很软,超级可爱,面部表情也很和善。但是相比于价钱来说, 它有点小,我感觉在别的地方用同样的价钱能买到更大的。 快递比预期提前了一天到货,所以在送给女儿之前,我自己玩了会。 \"\"\"\n", "\n", "1.1 限制输出文本长度\n", "\n", "我们首先尝试将文本的长度限制在30个字以内。\n", "\n", - "```python\n", - "from tool import get_completion\n", + "```python from tool import get_completion\n", "\n", - "prompt = f\"\"\"\n", - "您的任务是从电子商务网站上生成一个产品评论的简短摘要。\n", + "prompt = f\"\"\" 您的任务是从电子商务网站上生成一个产品评论的简短摘要。\n", "\n", "请对三个反引号之间的评论文本进行概括,最多30个字。\n", "\n", - "评论: {prod_review}\n", - "\"\"\"\n", + "评论: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "熊猫公仔软可爱,女儿喜欢,但有点小。快递提前一天到货。\n", "\n", "我们可以看到语言模型给了我们一个符合要求的结果。\n", "\n", @@ -155,35 +145,29 @@ "\n", "1.2.1 侧重于快递服务\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "您的任务是从电子商务网站上生成一个产品评论的简短摘要。\n", + "```python prompt = f\"\"\" 您的任务是从电子商务网站上生成一个产品评论的简短摘要。\n", "\n", "请对三个反引号之间的评论文本进行概括,最多30个字,并且侧重在快递服务上。\n", "\n", - "评论: {prod_review}\n", - "\"\"\"\n", + "评论: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "快递提前到货,公仔可爱但有点小。\n", "\n", "通过输出结果,我们可以看到,文本以“快递提前到货”开头,体现了对于快递效率的侧重。\n", "\n", "1.2.2 侧重于价格与质量\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "您的任务是从电子商务网站上生成一个产品评论的简短摘要。\n", + "```python prompt = f\"\"\" 您的任务是从电子商务网站上生成一个产品评论的简短摘要。\n", "\n", "请对三个反引号之间的评论文本进行概括,最多30个词汇,并且侧重在产品价格和质量上。\n", "\n", - "评论: {prod_review}\n", - "\"\"\"\n", + "评论: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "可爱的熊猫公仔,质量好但有点小,价格稍高。快递提前到货。\n", "\n", "通过输出的结果,我们可以看到,文本以“可爱的熊猫公仔,质量好但有点小,价格稍高”开头,体现了对于产品价格与质量的侧重。\n", "\n", @@ -193,258 +177,148 @@ "\n", "下面让我们来一起来对文本进行提取信息吧!\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "您的任务是从电子商务网站上的产品评论中提取相关信息。\n", + "```python prompt = f\"\"\" 您的任务是从电子商务网站上的产品评论中提取相关信息。\n", "\n", "请从以下三个反引号之间的评论文本中提取产品运输相关的信息,最多30个词汇。\n", "\n", - "评论: {prod_review}\n", - "\"\"\"\n", + "评论: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "产品运输相关的信息:快递提前一天到货。\n", "\n", "二、同时概括多条文本\n", "\n", "在实际的工作流中,我们往往要处理大量的评论文本,下面的示例将多条用户评价集合在一个列表中,并利用 for 循环和文本概括(Summarize)提示词,将评价概括至小于 20 个词以下,并按顺序打印。当然,在实际生产中,对于不同规模的评论文本,除了使用 for 循环以外,还可能需要考虑整合评论、分布式等方法提升运算效率。您可以搭建主控面板,来总结大量用户评论,以及方便您或他人快速浏览,还可以点击查看原评论。这样,您就能高效掌握顾客的所有想法。\n", "\n", - "```python\n", - "review_1 = prod_review\n", + "```python review_1 = prod_review\n", "\n", "一盏落地灯的评论\n", "\n", - "review_2 = \"\"\"\n", - "我需要一盏漂亮的卧室灯,这款灯不仅具备额外的储物功能,价格也并不算太高。\n", - "收货速度非常快,仅用了两天的时间就送到了。\n", - "不过,在运输过程中,灯的拉线出了问题,幸好,公司很乐意寄送了一根全新的灯线。\n", - "新的灯线也很快就送到手了,只用了几天的时间。\n", - "装配非常容易。然而,之后我发现有一个零件丢失了,于是我联系了客服,他们迅速地给我寄来了缺失的零件!\n", - "对我来说,这是一家非常关心客户和产品的优秀公司。\n", - "\"\"\"\n", + "review_2 = \"\"\" 我需要一盏漂亮的卧室灯,这款灯不仅具备额外的储物功能,价格也并不算太高。 收货速度非常快,仅用了两天的时间就送到了。 不过,在运输过程中,灯的拉线出了问题,幸好,公司很乐意寄送了一根全新的灯线。 新的灯线也很快就送到手了,只用了几天的时间。 装配非常容易。然而,之后我发现有一个零件丢失了,于是我联系了客服,他们迅速地给我寄来了缺失的零件! 对我来说,这是一家非常关心客户和产品的优秀公司。 \"\"\"\n", "\n", "一把电动牙刷的评论\n", "\n", - "review_3 = \"\"\"\n", - "我的牙科卫生员推荐了电动牙刷,所以我就买了这款。\n", - "到目前为止,电池续航表现相当不错。\n", - "初次充电后,我在第一周一直将充电器插着,为的是对电池进行条件养护。\n", - "过去的3周里,我每天早晚都使用它刷牙,但电池依然维持着原来的充电状态。\n", - "不过,牙刷头太小了。我见过比这个牙刷头还大的婴儿牙刷。\n", - "我希望牙刷头更大一些,带有不同长度的刷毛,\n", - "这样可以更好地清洁牙齿间的空隙,但这款牙刷做不到。\n", - "总的来说,如果你能以50美元左右的价格购买到这款牙刷,那是一个不错的交易。\n", - "制造商的替换刷头相当昂贵,但你可以购买价格更为合理的通用刷头。\n", - "这款牙刷让我感觉就像每天都去了一次牙医,我的牙齿感觉非常干净!\n", - "\"\"\"\n", + "review_3 = \"\"\" 我的牙科卫生员推荐了电动牙刷,所以我就买了这款。 到目前为止,电池续航表现相当不错。 初次充电后,我在第一周一直将充电器插着,为的是对电池进行条件养护。 过去的3周里,我每天早晚都使用它刷牙,但电池依然维持着原来的充电状态。 不过,牙刷头太小了。我见过比这个牙刷头还大的婴儿牙刷。 我希望牙刷头更大一些,带有不同长度的刷毛, 这样可以更好地清洁牙齿间的空隙,但这款牙刷做不到。 总的来说,如果你能以50美元左右的价格购买到这款牙刷,那是一个不错的交易。 制造商的替换刷头相当昂贵,但你可以购买价格更为合理的通用刷头。 这款牙刷让我感觉就像每天都去了一次牙医,我的牙齿感觉非常干净! \"\"\"\n", "\n", "一台搅拌机的评论\n", "\n", - "review_4 = \"\"\"\n", - "在11月份期间,这个17件套装还在季节性促销中,售价约为49美元,打了五折左右。\n", - "可是由于某种原因(我们可以称之为价格上涨),到了12月的第二周,所有的价格都上涨了,\n", - "同样的套装价格涨到了70-89美元不等。而11件套装的价格也从之前的29美元上涨了约10美元。\n", - "看起来还算不错,但是如果你仔细看底座,刀片锁定的部分看起来没有前几年版本的那么漂亮。\n", - "然而,我打算非常小心地使用它\n", - "(例如,我会先在搅拌机中研磨豆类、冰块、大米等坚硬的食物,然后再将它们研磨成所需的粒度,\n", - "接着切换到打蛋器刀片以获得更细的面粉,如果我需要制作更细腻/少果肉的食物)。\n", - "在制作冰沙时,我会将要使用的水果和蔬菜切成细小块并冷冻\n", - "(如果使用菠菜,我会先轻微煮熟菠菜,然后冷冻,直到使用时准备食用。\n", - "如果要制作冰糕,我会使用一个小到中号的食物加工器),这样你就可以避免添加过多的冰块。\n", - "大约一年后,电机开始发出奇怪的声音。我打电话给客户服务,但保修期已经过期了,\n", - "所以我只好购买了另一台。值得注意的是,这类产品的整体质量在过去几年里有所下降\n", - ",所以他们在一定程度上依靠品牌认知和消费者忠诚来维持销售。在大约两天内,我收到了新的搅拌机。\n", - "\"\"\"\n", + "review_4 = \"\"\" 在11月份期间,这个17件套装还在季节性促销中,售价约为49美元,打了五折左右。 可是由于某种原因(我们可以称之为价格上涨),到了12月的第二周,所有的价格都上涨了, 同样的套装价格涨到了70-89美元不等。而11件套装的价格也从之前的29美元上涨了约10美元。 看起来还算不错,但是如果你仔细看底座,刀片锁定的部分看起来没有前几年版本的那么漂亮。 然而,我打算非常小心地使用它 (例如,我会先在搅拌机中研磨豆类、冰块、大米等坚硬的食物,然后再将它们研磨成所需的粒度, 接着切换到打蛋器刀片以获得更细的面粉,如果我需要制作更细腻/少果肉的食物)。 在制作冰沙时,我会将要使用的水果和蔬菜切成细小块并冷冻 (如果使用菠菜,我会先轻微煮熟菠菜,然后冷冻,直到使用时准备食用。 如果要制作冰糕,我会使用一个小到中号的食物加工器),这样你就可以避免添加过多的冰块。 大约一年后,电机开始发出奇怪的声音。我打电话给客户服务,但保修期已经过期了, 所以我只好购买了另一台。值得注意的是,这类产品的整体质量在过去几年里有所下降 ,所以他们在一定程度上依靠品牌认知和消费者忠诚来维持销售。在大约两天内,我收到了新的搅拌机。 \"\"\"\n", "\n", "reviews = [review_1, review_2, review_3, review_4]\n", "\n", "```\n", "\n", - "```python\n", - "for i in range(len(reviews)):\n", - " prompt = f\"\"\"\n", - " 你的任务是从电子商务网站上的产品评论中提取相关信息。\n", + "```python for i in range(len(reviews)): prompt = f\"\"\" 你的任务是从电子商务网站上的产品评论中提取相关信息。\n", + "\n", + "请对三个反引号之间的评论文本进行概括,最多20个词汇。\n", + "\n", + "评论文本: ```{reviews[i]}```\n", + "\"\"\"\n", + "response = get_completion(prompt)\n", + "print(f\"评论{i+1}: \", response, \"\\n\")\n", "\n", "```\n", "\n", + "评论1: 熊猫公仔是生日礼物,女儿喜欢,软可爱,面部表情和善。价钱有点小,快递提前一天到货。\n", + "\n", + "评论2: 漂亮卧室灯,储物功能,快速送达,灯线问题,快速解决,容易装配,关心客户和产品。\n", + "\n", + "评论3: 这款电动牙刷电池续航好,但牙刷头太小,价格合理,清洁效果好。\n", + "\n", + "评论4: 该评论提到了一个17件套装的产品,在11月份有折扣销售,但在12月份价格上涨。评论者提到了产品的外观和使用方法,并提到了产品质量下降的问题。最后,评论者提到他们购买了另一台搅拌机。\n", + "\n", "三、英文版\n", "\n", "1.1 单一文本概括\n", "\n", - "python\n", - "prod_review = \"\"\"\n", - "Got this panda plush toy for my daughter's birthday, \\\n", - "who loves it and takes it everywhere. It's soft and \\ \n", - "super cute, and its face has a friendly look. It's \\ \n", - "a bit small for what I paid though. I think there \\ \n", - "might be other options that are bigger for the \\ \n", - "same price. It arrived a day earlier than expected, \\ \n", - "so I got to play with it myself before I gave it \\ \n", - "to her.\n", - "\"\"\"\n", + "python prod_review = \"\"\" Got this panda plush toy for my daughter's birthday, \\ who loves it and takes it everywhere. It's soft and \\ super cute, and its face has a friendly look. It's \\ a bit small for what I paid though. I think there \\ might be other options that are bigger for the \\ same price. It arrived a day earlier than expected, \\ so I got to play with it myself before I gave it \\ to her. \"\"\"\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "Your task is to generate a short summary of a product \\\n", - "review from an ecommerce site.\n", + "```python prompt = f\"\"\" Your task is to generate a short summary of a product \\ review from an ecommerce site.\n", "\n", - "Summarize the review below, delimited by triple \n", - "backticks, in at most 30 words.\n", + "Summarize the review below, delimited by triple backticks, in at most 30 words.\n", "\n", - "Review: {prod_review}\n", - "\"\"\"\n", + "Review: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "This panda plush toy is loved by the reviewer's daughter, but they feel it is a bit small for the price.\n", "\n", "1.2 设置关键角度侧重\n", "\n", "1.2.1 侧重于快递服务\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "Your task is to generate a short summary of a product \\\n", - "review from an ecommerce site to give feedback to the \\\n", - "Shipping deparmtment.\n", + "```python prompt = f\"\"\" Your task is to generate a short summary of a product \\ review from an ecommerce site to give feedback to the \\ Shipping deparmtment.\n", "\n", - "Summarize the review below, delimited by triple \n", - "backticks, in at most 30 words, and focusing on any aspects \\\n", - "that mention shipping and delivery of the product.\n", + "Summarize the review below, delimited by triple backticks, in at most 30 words, and focusing on any aspects \\ that mention shipping and delivery of the product.\n", "\n", - "Review: {prod_review}\n", - "\"\"\"\n", + "Review: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "The customer is happy with the product but suggests offering larger options for the same price. They were pleased with the early delivery.\n", "\n", "1.2.2 侧重于价格和质量\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "Your task is to generate a short summary of a product \\\n", - "review from an ecommerce site to give feedback to the \\\n", - "pricing deparmtment, responsible for determining the \\\n", - "price of the product.\n", + "```python prompt = f\"\"\" Your task is to generate a short summary of a product \\ review from an ecommerce site to give feedback to the \\ pricing deparmtment, responsible for determining the \\ price of the product.\n", "\n", - "Summarize the review below, delimited by triple \n", - "backticks, in at most 30 words, and focusing on any aspects \\\n", - "that are relevant to the price and perceived value.\n", + "Summarize the review below, delimited by triple backticks, in at most 30 words, and focusing on any aspects \\ that are relevant to the price and perceived value.\n", "\n", - "Review: {prod_review}\n", - "\"\"\"\n", + "Review: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "The customer loves the panda plush toy for its softness and cuteness, but feels it is overpriced compared to other options available.\n", "\n", "1.3 关键信息提取\n", "\n", - "```python\n", - "prompt = f\"\"\"\n", - "Your task is to extract relevant information from \\ \n", - "a product review from an ecommerce site to give \\\n", - "feedback to the Shipping department.\n", + "```python prompt = f\"\"\" Your task is to extract relevant information from \\ a product review from an ecommerce site to give \\ feedback to the Shipping department.\n", "\n", - "From the review below, delimited by triple quotes \\\n", - "extract the information relevant to shipping and \\ \n", - "delivery. Limit to 30 words.\n", + "From the review below, delimited by triple quotes \\ extract the information relevant to shipping and \\ delivery. Limit to 30 words.\n", "\n", - "Review: {prod_review}\n", - "\"\"\"\n", + "Review: {prod_review} \"\"\"\n", "\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", + "response = get_completion(prompt) print(response) ```\n", + "\n", + "The shipping department should take note that the product arrived a day earlier than expected.\n", "\n", "2.1 同时概括多条文本\n", "\n", - "```python\n", - "review_1 = prod_review\n", + "```python review_1 = prod_review\n", "\n", "review for a standing lamp\n", "\n", - "review_2 = \"\"\"\n", - "Needed a nice lamp for my bedroom, and this one \\\n", - "had additional storage and not too high of a price \\\n", - "point. Got it fast - arrived in 2 days. The string \\\n", - "to the lamp broke during the transit and the company \\\n", - "happily sent over a new one. Came within a few days \\\n", - "as well. It was easy to put together. Then I had a \\\n", - "missing part, so I contacted their support and they \\\n", - "very quickly got me the missing piece! Seems to me \\\n", - "to be a great company that cares about their customers \\\n", - "and products. \n", - "\"\"\"\n", + "review_2 = \"\"\" Needed a nice lamp for my bedroom, and this one \\ had additional storage and not too high of a price \\ point. Got it fast - arrived in 2 days. The string \\ to the lamp broke during the transit and the company \\ happily sent over a new one. Came within a few days \\ as well. It was easy to put together. Then I had a \\ missing part, so I contacted their support and they \\ very quickly got me the missing piece! Seems to me \\ to be a great company that cares about their customers \\ and products. \"\"\"\n", "\n", "review for an electric toothbrush\n", "\n", - "review_3 = \"\"\"\n", - "My dental hygienist recommended an electric toothbrush, \\\n", - "which is why I got this. The battery life seems to be \\\n", - "pretty impressive so far. After initial charging and \\\n", - "leaving the charger plugged in for the first week to \\\n", - "condition the battery, I've unplugged the charger and \\\n", - "been using it for twice daily brushing for the last \\\n", - "3 weeks all on the same charge. But the toothbrush head \\\n", - "is too small. I’ve seen baby toothbrushes bigger than \\\n", - "this one. I wish the head was bigger with different \\\n", - "length bristles to get between teeth better because \\\n", - "this one doesn’t. Overall if you can get this one \\\n", - "around the $50 mark, it's a good deal. The manufactuer's \\\n", - "replacements heads are pretty expensive, but you can \\\n", - "get generic ones that're more reasonably priced. This \\\n", - "toothbrush makes me feel like I've been to the dentist \\\n", - "every day. My teeth feel sparkly clean! \n", - "\"\"\"\n", + "review_3 = \"\"\" My dental hygienist recommended an electric toothbrush, \\ which is why I got this. The battery life seems to be \\ pretty impressive so far. After initial charging and \\ leaving the charger plugged in for the first week to \\ condition the battery, I've unplugged the charger and \\ been using it for twice daily brushing for the last \\ 3 weeks all on the same charge. But the toothbrush head \\ is too small. I’ve seen baby toothbrushes bigger than \\ this one. I wish the head was bigger with different \\ length bristles to get between teeth better because \\ this one doesn’t. Overall if you can get this one \\ around the $50 mark, it's a good deal. The manufactuer's \\ replacements heads are pretty expensive, but you can \\ get generic ones that're more reasonably priced. This \\ toothbrush makes me feel like I've been to the dentist \\ every day. My teeth feel sparkly clean! \"\"\"\n", "\n", "review for a blender\n", "\n", - "review_4 = \"\"\"\n", - "So, they still had the 17 piece system on seasonal \\\n", - "sale for around $49 in the month of November, about \\\n", - "half off, but for some reason (call it price gouging) \\\n", - "around the second week of December the prices all went \\\n", - "up to about anywhere from between $70-$89 for the same \\\n", - "system. And the 11 piece system went up around $10 or \\\n", - "so in price also from the earlier sale price of $29. \\\n", - "So it looks okay, but if you look at the base, the part \\\n", - "where the blade locks into place doesn’t look as good \\\n", - "as in previous editions from a few years ago, but I \\\n", - "plan to be very gentle with it (example, I crush \\\n", - "very hard items like beans, ice, rice, etc. in the \\\n", - "blender first then pulverize them in the serving size \\\n", - "I want in the blender then switch to the whipping \\\n", - "blade for a finer flour, and use the cross cutting blade \\\n", - "first when making smoothies, then use the flat blade \\\n", - "if I need them finer/less pulpy). Special tip when making \\\n", - "smoothies, finely cut and freeze the fruits and \\\n", - "vegetables (if using spinach-lightly stew soften the \\\n", - "spinach then freeze until ready for use-and if making \\\n", - "sorbet, use a small to medium sized food processor) \\\n", - "that you plan to use that way you can avoid adding so \\\n", - "much ice if at all-when making your smoothie. \\\n", - "After about a year, the motor was making a funny noise. \\\n", - "I called customer service but the warranty expired \\\n", - "already, so I had to buy another one. FYI: The overall \\\n", - "quality has gone done in these types of products, so \\\n", - "they are kind of counting on brand recognition and \\\n", - "consumer loyalty to maintain sales. Got it in about \\\n", - "two days.\n", + "review_4 = \"\"\" So, they still had the 17 piece system on seasonal \\ sale for around $49 in the month of November, about \\ half off, but for some reason (call it price gouging) \\ around the second week of December the prices all went \\ up to about anywhere from between $70-$89 for the same \\ system. And the 11 piece system went up around $10 or \\ so in price also from the earlier sale price of $29. \\ So it looks okay, but if you look at the base, the part \\ where the blade locks into place doesn’t look as good \\ as in previous editions from a few years ago, but I \\ plan to be very gentle with it (example, I crush \\ very hard items like beans, ice, rice, etc. in the \\ blender first then pulverize them in the serving size \\ I want in the blender then switch to the whipping \\ blade for a finer flour, and use the cross cutting blade \\ first when making smoothies, then use the flat blade \\ if I need them finer/less pulpy). Special tip when making \\ smoothies, finely cut and freeze the fruits and \\ vegetables (if using spinach-lightly stew soften the \\ spinach then freeze until ready for use-and if making \\ sorbet, use a small to medium sized food processor) \\ that you plan to use that way you can avoid adding so \\ much ice if at all-when making your smoothie. \\ After about a year, the motor was making a funny noise. \\ I called customer service but the warranty expired \\ already, so I had to buy another one. FYI: The overall \\ quality has gone done in these types of products, so \\ they are kind of counting on brand recognition and \\ consumer loyalty to maintain sales. Got it in about \\ two days. \"\"\"\n", + "\n", + "reviews = [review_1, review_2, review_3, review_4] ```\n", + "\n", + "```python for i in range(len(reviews)): prompt = f\"\"\" Your task is to generate a short summary of a product \\ review from an ecommerce site.\n", + "\n", + "Summarize the review below, delimited by triple \\\n", + "backticks in at most 20 words.\n", + "\n", + "Review: ```{reviews[i]}```\n", "\"\"\"\n", + "response = get_completion(prompt)\n", + "print(i, response, \"\\n\")\n", "\n", - "reviews = [review_1, review_2, review_3, review_4]\n", "```\n", "\n", - "```python\n", - "for i in range(len(reviews)):\n", - " prompt = f\"\"\"\n", - " Your task is to generate a short summary of a product \\\n", - " review from an ecommerce site.\n", + "0 Soft and cute panda plush toy loved by daughter, but small for the price. Arrived early.\n", "\n", - "```\n" + "1 Great lamp with storage, fast delivery, excellent customer service, and easy assembly. Highly recommended.\n", + "\n", + "2 Impressive battery life, but toothbrush head is too small. Good deal if bought around $50.\n", + "\n", + "3 The reviewer found the price increase after the sale disappointing and noticed a decrease in quality over time.\n" ] } ], @@ -462,7 +336,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", + "from langchain_text_splitters import RecursiveCharacterTextSplitter\n", "\n", "# 切分文档\n", "text_splitter = RecursiveCharacterTextSplitter(\n", @@ -522,7 +396,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.vectorstores.chroma import Chroma\n", + "from langchain_community.vectorstores import Chroma\n", "\n", "vectordb = Chroma.from_documents(\n", " documents=split_docs,\n", @@ -531,33 +405,6 @@ ")" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在此之后,我们要确保通过运行 vectordb.persist 来持久化向量数据库,以便我们在未来的课程中使用。\n", - "\n", - "让我们保存它,以便以后使用!" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/lta/anaconda3/envs/llm_universe_2.x/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:119: LangChainDeprecationWarning: Since Chroma 0.4.x the manual persistence method is no longer supported as docs are automatically persisted.\n", - " warn_deprecated(\n" - ] - } - ], - "source": [ - "vectordb.persist()" - ] - }, { "cell_type": "code", "execution_count": 10, @@ -567,7 +414,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "向量库中存储的数量:925\n" + "向量库中存储的数量:1004\n" ] } ], @@ -627,6 +474,9 @@ "output_type": "stream", "text": [ "检索到的第0个内容: \n", + "网络上有许多关于提示词(Prompt, 本教程中将保留该术语)设计的材料,例如《30 prompts everyone has to know》之类的文章,这些文章主要集中在 ChatGPT 的 Web 界面上,许多人在使用它执行特定的、通常是一次性的任务。但我们认为,对于开发人员,大语言模型(LLM) 的更强大功能是能通过 API 接口调用,从而快速构建软件应用程序。实际上,我们了解到 Deep\n", + "--------------\n", + "检索到的第1个内容: \n", "第六章 文本转换\n", "\n", "大语言模型具有强大的文本转换能力,可以实现多语言翻译、拼写纠正、语法调整、格式转换等不同类型的文本转换任务。利用语言模型进行各类转换是它的典型应用之一。\n", @@ -635,23 +485,20 @@ "\n", "掌握调用大语言模型接口进行文本转换的技能,是开发各种语言类应用的重要一步。文\n", "--------------\n", - "检索到的第1个内容: \n", - "以英译汉为例,传统统计机器翻译多倾向直接替换英文词汇,语序保持英语结构,容易出现中文词汇使用不地道、语序不顺畅的现象。而大语言模型可以学习英汉两种语言的语法区别,进行动态的结构转换。同时,它还可以通过上下文理解原句意图,选择合适的中文词汇进行转换,而非生硬的字面翻译。\n", - "\n", - "大语言模型翻译的这些优势使其生成的中文文本更加地道、流畅,兼具准确的意义表达。利用大语言模型翻译,我们能够打通多语言之间的壁垒,\n", - "--------------\n", "检索到的第2个内容: \n", - "通过这个例子,我们可以看到大语言模型可以流畅地处理多个转换要求,实现中文翻译、拼写纠正、语气升级和格式转换等功能。\n", + "学生计算的总费用:450x+10万美元\n", + "实际计算的总费用:360x+10万美元\n", + "学生计算的费用和实际计算的费用是否相同:否\n", + "学生的解决方案和实际解决方案是否相同:否\n", + "学生的成绩:不正确\n", "\n", - "利用大语言模型强大的组合转换能力,我们可以避免多次调用模型来进行不同转换,极大地简化了工作流程。这种一次性实现多种转换的方法,可以广泛应用于文本处理与转换的场景中。\n", + "三、局限性\n", "\n", - "六、英文版\n", + "开发大模型相关应用时请务必铭记:\n", "\n", - "1.1 翻译为西班牙语\n", + "虚假知识:模型偶尔会生成一些看似真实实则编造的知识\n", "\n", - "python\n", - "prompt = f\"\"\"\n", - "Translate the fo\n", + "在开发与应用语言模型时,需要注意它们可能生成虚假信息的风险。尽管模型经过大规模预训练,掌握了丰富知识,但它实\n", "--------------\n" ] } @@ -692,24 +539,29 @@ "output_type": "stream", "text": [ "MMR 检索到的第0个内容: \n", - "第六章 文本转换\n", + "网络上有许多关于提示词(Prompt, 本教程中将保留该术语)设计的材料,例如《30 prompts everyone has to know》之类的文章,这些文章主要集中在 ChatGPT 的 Web 界面上,许多人在使用它执行特定的、通常是一次性的任务。但我们认为,对于开发人员,大语言模型(LLM) 的更强大功能是能通过 API 接口调用,从而快速构建软件应用程序。实际上,我们了解到 Deep\n", + "--------------\n", + "MMR 检索到的第1个内容: \n", + "学生计算的总费用:450x+10万美元\n", + "实际计算的总费用:360x+10万美元\n", + "学生计算的费用和实际计算的费用是否相同:否\n", + "学生的解决方案和实际解决方案是否相同:否\n", + "学生的成绩:不正确\n", "\n", - "大语言模型具有强大的文本转换能力,可以实现多语言翻译、拼写纠正、语法调整、格式转换等不同类型的文本转换任务。利用语言模型进行各类转换是它的典型应用之一。\n", + "三、局限性\n", "\n", - "在本章中,我们将介绍如何通过编程调用API接口,使用语言模型实现文本转换功能。通过代码示例,读者可以学习将输入文本转换成所需输出格式的具体方法。\n", + "开发大模型相关应用时请务必铭记:\n", "\n", - "掌握调用大语言模型接口进行文本转换的技能,是开发各种语言类应用的重要一步。文\n", - "--------------\n", - "MMR 检索到的第1个内容: \n", - "与基础语言模型不同,指令微调 LLM 通过专门的训练,可以更好地理解并遵循指令。举个例子,当询问“法国的首都是什么?”时,这类模型很可能直接回答“法国的首都是巴黎”。指令微调 LLM 的训练通常基于预训练语言模型,先在大规模文本数据上进行预训练,掌握语言的基本规律。在此基础上进行进一步的训练与微调(finetune),输入是指令,输出是对这些指令的正确回复。有时还会采用RLHF(reinforce\n", + "虚假知识:模型偶尔会生成一些看似真实实则编造的知识\n", + "\n", + "在开发与应用语言模型时,需要注意它们可能生成虚假信息的风险。尽管模型经过大规模预训练,掌握了丰富知识,但它实\n", "--------------\n", "MMR 检索到的第2个内容: \n", - "同一份数据集中的每个样本都含有相同个数的特征,假设此数据集中的每个样本都含有d 个特征,则第i\n", - "个样本的数学表示为d 维向量:xi = (xi1; xi2; ...; xid),其中xij 表示样本xi 在第j 个属性上的取值。\n", - "模型:机器学习的一般流程如下:首先收集若干样本(假设此时有100 个)\n", - ",然后将其分为训练样本\n", - "(80 个)和测试样本(20 个)\n", - ",其中80 个训练样本构成的集合称为“\n", + "行推导。\n", + "对于任意样本, 在不考虑样本本身之前(即先验), 若瞎猜一下它由第i 个高斯混合成分生成的概率\n", + "P (zj = i), 那么肯定按先验概率α1, α2, . . . , αk 进行猜测, 即P (zj = i) = αi 。若考虑样本本身带来的信\n", + "息(即后验), 此时再猜一下它由第i 个高斯混合成分生成的概率pM (zj = i | xj), 根据贝叶斯公式, 后验概\n", + "率pM (zj =\n", "--------------\n" ] } @@ -722,7 +574,7 @@ ], "metadata": { "kernelspec": { - "display_name": "llm_universe_2.x", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -736,7 +588,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/zhipuai_embedding.py" "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/zhipuai_embedding.py" index cade90c..52b2263 100644 --- "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/zhipuai_embedding.py" +++ "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/zhipuai_embedding.py" @@ -1,22 +1,9 @@ -from __future__ import annotations +from typing import List +from langchain_core.embeddings import Embeddings -import logging -from typing import Dict, List, Any - - -from langchain.embeddings.base import Embeddings -from langchain.pydantic_v1 import BaseModel, root_validator - -logger = logging.getLogger(__name__) - -class ZhipuAIEmbeddings(BaseModel, Embeddings): +class ZhipuAIEmbeddings(Embeddings): """`Zhipuai Embeddings` embedding models.""" - - client: Any - """`zhipuai.ZhipuAI""" - - @root_validator() - def validate_environment(cls, values: Dict) -> Dict: + def __init__(self): """ 实例化ZhipuAI为values["client"] @@ -28,24 +15,7 @@ def validate_environment(cls, values: Dict) -> Dict: values (Dict): 包含配置信息的字典。如果环境中有zhipuai库,则将返回实例化的ZhipuAI类;否则将报错 'ModuleNotFoundError: No module named 'zhipuai''. """ from zhipuai import ZhipuAI - values["client"] = ZhipuAI() - return values - - def embed_query(self, text: str) -> List[float]: - """ - 生成输入文本的 embedding. - - Args: - texts (str): 要生成 embedding 的文本. - - Return: - embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表. - """ - embeddings = self.client.embeddings.create( - model="embedding-2", - input=text - ) - return embeddings.data[0].embedding + self.client = ZhipuAI() def embed_documents(self, texts: List[str]) -> List[List[float]]: """ @@ -56,13 +26,25 @@ def embed_documents(self, texts: List[str]) -> List[List[float]]: Returns: List[List[float]]: 输入列表中每个文档的 embedding 列表。每个 embedding 都表示为一个浮点值列表。 """ - return [self.embed_query(text) for text in texts] - + + result = [] + for i in range(0, len(texts), 64): + embeddings = self.client.embeddings.create( + model="embedding-3", + input=texts[i:i+64] + ) + result.extend([embeddings.embedding for embeddings in embeddings.data]) + return result - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: - """Asynchronous Embed search docs.""" - raise NotImplementedError("Please use `embed_documents`. Official does not support asynchronous requests") + def embed_query(self, text: str) -> List[float]: + """ + 生成输入文本的 embedding. + + Args: + texts (str): 要生成 embedding 的文本. + + Return: + embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表. + """ - async def aembed_query(self, text: str) -> List[float]: - """Asynchronous Embed query text.""" - raise NotImplementedError("Please use `aembed_query`. Official does not support asynchronous requests") \ No newline at end of file + return self.embed_documents([text])[0] \ No newline at end of file diff --git "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/\351\231\204LangChain\350\207\252\345\256\232\344\271\211Embedding\345\260\201\350\243\205\350\256\262\350\247\243.ipynb" "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/\351\231\204LangChain\350\207\252\345\256\232\344\271\211Embedding\345\260\201\350\243\205\350\256\262\350\247\243.ipynb" index 817a16e..fe0de81 100644 --- "a/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/\351\231\204LangChain\350\207\252\345\256\232\344\271\211Embedding\345\260\201\350\243\205\350\256\262\350\247\243.ipynb" +++ "b/notebook/C3 \346\220\255\345\273\272\347\237\245\350\257\206\345\272\223/\351\231\204LangChain\350\207\252\345\256\232\344\271\211Embedding\345\260\201\350\243\205\350\256\262\350\247\243.ipynb" @@ -22,15 +22,8 @@ "metadata": {}, "outputs": [], "source": [ - "from __future__ import annotations\n", - "\n", - "import logging\n", - "from typing import Dict, List, Any\n", - "\n", - "from langchain.embeddings.base import Embeddings\n", - "from langchain.pydantic_v1 import BaseModel, root_validator\n", - "\n", - "logger = logging.getLogger(__name__)" + "from typing import List\n", + "from langchain_core.embeddings import Embeddings" ] }, { @@ -46,52 +39,28 @@ "metadata": {}, "outputs": [], "source": [ - "class ZhipuAIEmbeddings(BaseModel, Embeddings):\n", + "class ZhipuAIEmbeddings(Embeddings):\n", " \"\"\"`Zhipuai Embeddings` embedding models.\"\"\"\n", + " def __init__(self):\n", + " \"\"\"\n", + " 实例化ZhipuAI为values[\"client\"]\n", "\n", - " client: Any\n", - " \"\"\"`zhipuai.ZhipuAI\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在 Python 中,root_validator 是 Pydantic 模块中一个用于自定义数据校验的装饰器函数。root_validator 用于在校验整个数据模型之前对整个数据模型进行自定义校验,以确保所有的数据都符合所期望的数据结构。\n", - "\n", - "root_validator 接收一个函数作为参数,该函数包含需要校验的逻辑。函数应该返回一个字典,其中包含经过校验的数据。如果校验失败,则抛出一个 ValueError 异常。\n", - "\n", - "这里我们只需将`.env`文件中`ZHIPUAI_API_KEY`配置好即可,`zhipuai.ZhipuAI`会自动获取`ZHIPUAI_API_KEY`。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@root_validator()\n", - "def validate_environment(cls, values: Dict) -> Dict:\n", - " \"\"\"\n", - " 实例化ZhipuAI为values[\"client\"]\n", - "\n", - " Args:\n", + " Args:\n", "\n", - " values (Dict): 包含配置信息的字典,必须包含 client 的字段.\n", - " Returns:\n", + " values (Dict): 包含配置信息的字典,必须包含 client 的字段.\n", + " Returns:\n", "\n", - " values (Dict): 包含配置信息的字典。如果环境中有zhipuai库,则将返回实例化的ZhipuAI类;否则将报错 'ModuleNotFoundError: No module named 'zhipuai''.\n", - " \"\"\"\n", - " from zhipuai import ZhipuAI\n", - " values[\"client\"] = ZhipuAI()\n", - " return values" + " values (Dict): 包含配置信息的字典。如果环境中有zhipuai库,则将返回实例化的ZhipuAI类;否则将报错 'ModuleNotFoundError: No module named 'zhipuai''.\n", + " \"\"\"\n", + " from zhipuai import ZhipuAI\n", + " self.client = ZhipuAI()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "`embed_query` 是对单个文本(str)计算 embedding 的方法,这里我们重写该方法,调用验证环境时实例化的`ZhipuAI`来 调用远程 API 并返回 embedding 结果。" + "embed_documents 是对字符串列表(List[str])计算embedding 的方法,这里我们重写该方法,调用验证环境时实例化的`ZhipuAI`来 调用远程 API 并返回 embedding 结果。" ] }, { @@ -100,28 +69,27 @@ "metadata": {}, "outputs": [], "source": [ - "def embed_query(self, text: str) -> List[float]:\n", - " \"\"\"\n", - " 生成输入文本的 embedding.\n", - "\n", - " Args:\n", - " texts (str): 要生成 embedding 的文本.\n", - "\n", - " Return:\n", - " embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表.\n", - " \"\"\"\n", - " embeddings = self.client.embeddings.create(\n", - " model=\"embedding-2\",\n", - " input=text\n", - " )\n", - " return embeddings.data[0].embedding" + " def embed_documents(self, texts: List[str]) -> List[List[float]]:\n", + " \"\"\"\n", + " 生成输入文本列表的 embedding.\n", + " Args:\n", + " texts (List[str]): 要生成 embedding 的文本列表.\n", + "\n", + " Returns:\n", + " List[List[float]]: 输入列表中每个文档的 embedding 列表。每个 embedding 都表示为一个浮点值列表。\n", + " \"\"\"\n", + " embeddings = self.client.embeddings.create(\n", + " model=\"embedding-3\",\n", + " input=texts\n", + " )\n", + " return [embeddings.embedding for embeddings in embeddings.data]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "embed_documents 是对字符串列表(List[str])计算embedding 的方法,对于这种类型输入我们采取循环方式挨个计算列表内子字符串的 embedding 并返回。" + "`embed_query` 是对单个文本(str)计算 embedding 的方法,这里我们调用刚才定义好的`embed_documents`方法,并返回第一个子列表即可。" ] }, { @@ -130,23 +98,25 @@ "metadata": {}, "outputs": [], "source": [ - "def embed_documents(self, texts: List[str]) -> List[List[float]]:\n", - " \"\"\"\n", - " 生成输入文本列表的 embedding.\n", - " Args:\n", - " texts (List[str]): 要生成 embedding 的文本列表.\n", + " def embed_query(self, text: str) -> List[float]:\n", + " \"\"\"\n", + " 生成输入文本的 embedding.\n", + "\n", + " Args:\n", + " texts (str): 要生成 embedding 的文本.\n", + "\n", + " Return:\n", + " embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表.\n", + " \"\"\"\n", "\n", - " Returns:\n", - " List[List[float]]: 输入列表中每个文档的 embedding 列表。每个 embedding 都表示为一个浮点值列表。\n", - " \"\"\"\n", - " return [self.embed_query(text) for text in texts]" + " return self.embed_documents([text])[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "对于 `embed_query` 可以加入一些内容处理后再请求 embedding,比如如果文本特别长,我们可以考虑对文本分段,防止超过最大 token 限制,这些都是可以的,靠大家发挥自己的主观能动性完善啦,这里只是给出一个简单的 demo。" + "对于以上方法可以加入一些内容处理后再请求 embedding,比如文本特别长,我们可以考虑对文本分段,防止超过最大 token 限制,这些都是可以的,靠大家发挥自己的主观能动性完善啦,这里只是给出一个简单的 demo。" ] }, { @@ -159,13 +129,13 @@ ], "metadata": { "kernelspec": { - "display_name": "llm", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, "language_info": { "name": "python", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/1.LLM \346\216\245\345\205\245 LangChain.ipynb" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/1.LLM \346\216\245\345\205\245 LangChain.ipynb" index f7f118c..74e92e5 100644 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/1.LLM \346\216\245\345\205\245 LangChain.ipynb" +++ "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/1.LLM \346\216\245\345\205\245 LangChain.ipynb" @@ -51,7 +51,6 @@ "outputs": [], "source": [ "import os\n", - "import openai\n", "from dotenv import load_dotenv, find_dotenv\n", "\n", "# 读取本地/项目的环境变量。\n", @@ -75,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 2, "id": "ee1f024b", "metadata": {}, "outputs": [], @@ -93,17 +92,17 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "id": "9db5579b-72be-4eda-a217-a9f30ad75b74", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "ChatOpenAI(client=, async_client=, temperature=0.0, openai_api_key=SecretStr('**********'), openai_api_base='https://api.chatgptid.net/v1', openai_proxy='')" + "ChatOpenAI(client=, async_client=, root_client=, root_async_client=, temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********'))" ] }, - "execution_count": 8, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -125,12 +124,12 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 4, "id": "93de6b9c-2cab-4716-81e5-b674fd714562", "metadata": {}, "outputs": [], "source": [ - "llm = ChatOpenAI(temperature=0, openai_api_key=\"YOUR_API_KEY\")" + "# llm = ChatOpenAI(temperature=0, openai_api_key=\"YOUR_API_KEY\")" ] }, { @@ -163,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "id": "9ef54a74", "metadata": {}, "outputs": [], @@ -173,17 +172,17 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "id": "ddb976f4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content='你好,我是一个智能助手,专注于为用户提供各种服务和帮助。我可以回答问题、提供信息、解决问题,帮助用户更高效地完成工作和生活。如果您有任何疑问或需要帮助,请随时告诉我,我会尽力帮助您。感谢您的使用!', response_metadata={'token_usage': {'completion_tokens': 104, 'prompt_tokens': 20, 'total_tokens': 124}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None})" + "AIMessage(content='你好,我是一个智能助手,专门为您提供各种服务和帮助。我可以回答您的问题,提供信息和建议,帮助您解决问题。如果您有任何需要,请随时告诉我,我会尽力帮助您。感谢您选择我作为您的助手!如果您有任何问题,请随时问我。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 109, 'prompt_tokens': 20, 'total_tokens': 129, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-c611b32a-4adf-47af-9b97-6dda68a117e1-0', usage_metadata={'input_tokens': 20, 'output_tokens': 109, 'total_tokens': 129})" ] }, - "execution_count": 11, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -214,13 +213,11 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "id": "9acbd1ef-17f6-4513-a51b-2f489bbe7438", "metadata": {}, "outputs": [], "source": [ - "from langchain_core.prompts import ChatPromptTemplate\n", - "\n", "# 这里我们要求模型对给定文本进行中文翻译\n", "prompt = \"\"\"请你将由三个反引号分割的文本翻译成英文!\\\n", "text: ```{text}```\n", @@ -237,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 8, "id": "1c106f09", "metadata": {}, "outputs": [ @@ -247,7 +244,7 @@ "'请你将由三个反引号分割的文本翻译成英文!text: ```我带着比身体重的行李,游入尼罗河底,经过几道闪电 看到一堆光圈,不确定是不是这里。```\\n'" ] }, - "execution_count": 12, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -273,29 +270,29 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "01a4414f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[SystemMessage(content='你是一个翻译助手,可以帮助我将 中文 翻译成 英文.'),\n", - " HumanMessage(content='我带着比身体重的行李,游入尼罗河底,经过几道闪电 看到一堆光圈,不确定是不是这里。')]" + "[SystemMessage(content='你是一个翻译助手,可以帮助我将 中文 翻译成 英文.', additional_kwargs={}, response_metadata={}),\n", + " HumanMessage(content='我带着比身体重的行李,游入尼罗河底,经过几道闪电 看到一堆光圈,不确定是不是这里。', additional_kwargs={}, response_metadata={})]" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from langchain.prompts.chat import ChatPromptTemplate\n", + "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "template = \"你是一个翻译助手,可以帮助我将 {input_language} 翻译成 {output_language}.\"\n", "human_template = \"{text}\"\n", "\n", - "chat_prompt = ChatPromptTemplate.from_messages([\n", + "chat_prompt = ChatPromptTemplate([\n", " (\"system\", template),\n", " (\"human\", human_template),\n", "])\n", @@ -305,7 +302,7 @@ "经过几道闪电 看到一堆光圈,\\\n", "不确定是不是这里。\\\n", "\"\n", - "messages = chat_prompt.format_messages(input_language=\"中文\", output_language=\"英文\", text=text)\n", + "messages = chat_prompt.invoke({\"input_language\": \"中文\", \"output_language\": \"英文\", \"text\": text})\n", "messages" ] }, @@ -319,17 +316,17 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "3129e2e5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content='I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.')" + "AIMessage(content='I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 43, 'prompt_tokens': 95, 'total_tokens': 138, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-c58ae91f-7c0a-4f20-ad7f-8d6421f3a9aa-0', usage_metadata={'input_tokens': 95, 'output_tokens': 43, 'total_tokens': 138})" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -356,7 +353,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "c64afdf3", "metadata": {}, "outputs": [ @@ -366,7 +363,7 @@ "'I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -411,7 +408,7 @@ { "data": { "text/plain": [ - "'I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'" + "'I carried luggage heavier than my body weight and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'" ] }, "execution_count": 12, @@ -434,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "id": "0440e251", "metadata": {}, "outputs": [ @@ -444,14 +441,14 @@ "'我扛着比我的身体还重的行李,潜入尼罗河的底部。穿过几道闪电后,我看到一堆光环,不确定这是否就是目的地。'" ] }, - "execution_count": 14, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "text = 'I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'\n", - "chain.invoke({\"input_language\":\"英文\", \"output_language\":\"中文\",\"text\": text})" + "chain.invoke({\"input_language\": \"英文\", \"output_language\": \"中文\",\"text\": text})" ] }, { @@ -490,166 +487,9 @@ "我们同样可以通过 LangChain 框架来调用百度文心大模型,以将文心模型接入到我们的应用框架中。" ] }, - { - "cell_type": "markdown", - "id": "33ddc109", - "metadata": {}, - "source": [ - "### 2.1 自定义 LLM 接入 langchain\n", - "在老版本中,LangChain 是不直接支持文心调用的,我们需要自定义一个支持文心模型调用的 LLM。在这里为了向用户展示如何自定义 LLM,我们在《附1.LangChain自定义LLM》中,简述了这种方法,也可参考[源文档](https://python.langchain.com/v0.1/docs/modules/model_io/llms/custom_llm/)。\n", - "\n", - "此处,我们可以直接调用已自定义好的 Wenxin_LLM,具体如何封装 Wenxin_LLM 见`wenxin_llm.py`。\n", - "\n", - "**注:以下代码需要将我们封装的代码[wenxin_llm.py](./wenxin_llm.py)下载到本 Notebook 的同级目录下,才可以直接使用。因为新版 LangChain 可以直接调用文心千帆 API,我们更推荐使用下一部分的代码来调用文心一言模型**" - ] - }, { "cell_type": "code", - "execution_count": 1, - "id": "8505d726-4d2a-4bd4-a603-5edb1b204e05", - "metadata": {}, - "outputs": [], - "source": [ - "# 需要下载源码\n", - "from wenxin_llm import Wenxin_LLM" - ] - }, - { - "cell_type": "markdown", - "id": "127e3020-7fd2-4f8a-87e6-c088b1b773ab", - "metadata": {}, - "source": [ - "我们希望像调用 ChatGPT 那样直接将秘钥存储在 .env 文件中,并将其加载到环境变量,从而隐藏秘钥的具体细节,保证安全性。因此,我们需要在 .env 文件中配置 `QIANFAN_AK` 和 `QIANFAN_SK`,并使用以下代码加载:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "bc0dd3cf-f0a3-46cc-8eff-465523acfbdc", - "metadata": {}, - "outputs": [], - "source": [ - "from dotenv import find_dotenv, load_dotenv\n", - "import os\n", - "\n", - "# 读取本地/项目的环境变量。\n", - "\n", - "# find_dotenv()寻找并定位.env文件的路径\n", - "# load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中\n", - "# 如果你设置的是全局的环境变量,这行代码则没有任何作用。\n", - "_ = load_dotenv(find_dotenv())\n", - "\n", - "# 获取环境变量 API_KEY\n", - "wenxin_api_key = os.environ[\"QIANFAN_AK\"]\n", - "wenxin_secret_key = os.environ[\"QIANFAN_SK\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "0ced8d04-8f34-42eb-8eb3-0171ac5db0ba", - "metadata": {}, - "outputs": [], - "source": [ - "llm = Wenxin_LLM(api_key=wenxin_api_key, secret_key=wenxin_secret_key, system=\"你是一个助手!\")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "a1e909be-889f-4ce9-9164-0410ff4ad73d", - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[INFO] [03-31 22:12:53] openapi_requestor.py:316 [t:27812]: requesting llm api endpoint: /chat/eb-instant\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n" - ] - }, - { - "data": { - "text/plain": [ - "'你好!我是助手,负责协助您完成各种任务。我具备快速响应、高效执行和灵活适应的能力,致力于为您提供优质的服务。无论您需要什么帮助,我都会尽力满足您的需求。'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "llm.invoke(\"你好,请你自我介绍一下!\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "2167d07a", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[INFO] [03-31 22:12:41] openapi_requestor.py:316 [t:27812]: requesting llm api endpoint: /chat/eb-instant\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n" - ] - }, - { - "data": { - "text/plain": [ - "'你好!我是助手,负责协助您完成各种任务。我具备快速学习和处理信息的能力,能够根据您的需求提供帮助和回答问题。无论您需要什么帮助,我都会尽力提供支持。'" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 或者使用\n", - "llm(prompt=\"你好,请你自我介绍一下!\")" - ] - }, - { - "cell_type": "markdown", - "id": "b7b81514-3735-4e34-b4a8-2c43240307ca", - "metadata": {}, - "source": [ - "从而我们可以将文心大模型加入到 LangChain 架构中,实现在应用中对文心大模型的调用。" - ] - }, - { - "cell_type": "markdown", - "id": "7266bbe8", - "metadata": {}, - "source": [ - "### 2.2 在 langchain 直接调用文心一言\n", - "\n", - "我们也可以使用新版 LangChain,来直接调用文心一言大模型。" - ] - }, - { - "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "7312bb4d", "metadata": {}, "outputs": [], @@ -672,18 +512,7 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "65168734", - "metadata": {}, - "outputs": [], - "source": [ - "# Install required dependencies\n", - "%pip install -qU langchain langchain-community" - ] - }, - { - "cell_type": "code", - "execution_count": 4, + "execution_count": 15, "id": "73a934e3", "metadata": {}, "outputs": [ @@ -691,23 +520,65 @@ "name": "stderr", "output_type": "stream", "text": [ - "d:\\Miniconda\\miniconda3\\envs\\llm2\\lib\\site-packages\\langchain_core\\_api\\deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.\n", - " warn_deprecated(\n", - "[INFO] [03-31 22:40:14] openapi_requestor.py:316 [t:3684]: requesting llm api endpoint: /chat/eb-instant\n", - "[INFO] [03-31 22:40:14] oauth.py:207 [t:3684]: trying to refresh access_token for ak `MxBM7W***`\n", - "[INFO] [03-31 22:40:15] oauth.py:220 [t:3684]: sucessfully refresh access_token\n" + "[WARNING][2025-03-05 19:41:13.652] redis_rate_limiter.py:21 [t:8258539328]: No redis installed, RedisRateLimiter unavailable. Ignore this warning if you don't need to use qianfan SDK in distribution environment\n", + "/var/folders/yd/c4q_f88j1l70g7_jcb6pdnb80000gn/T/ipykernel_51550/1209153611.py:4: LangChainDeprecationWarning: The method `BaseLLM.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use :meth:`~invoke` instead.\n", + " res = llm(\"你好,请你自我介绍一下!\")\n", + "[ERROR][2025-03-05 19:41:13.835] base.py:134 [t:8258539328]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-05T11:41:13Z', 'Authorization': 'bce-auth-v1//2025-03-05T11:41:13Z/300/x-bce-date;host;request-source;content-type/cc383f75803c577d6486841dc228aea994102a4b70bd5ff76f27d12bdb7af133', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Wed, 05 Mar 2025 11:41:13 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '17059251-201a-4a1f-8fbc-220df83ff184', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[WARNING][2025-03-05 19:41:13.835] base.py:1083 [t:8258539328]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-05T11:41:13Z', 'Authorization': 'bce-auth-v1//2025-03-05T11:41:13Z/300/x-bce-date;host;request-source;content-type/cc383f75803c577d6486841dc228aea994102a4b70bd5ff76f27d12bdb7af133', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Wed, 05 Mar 2025 11:41:13 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '17059251-201a-4a1f-8fbc-220df83ff184', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[ERROR][2025-03-05 19:41:14.033] base.py:134 [t:8258539328]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-05T11:41:13Z', 'Authorization': 'bce-auth-v1//2025-03-05T11:41:13Z/300/x-bce-date;host;request-source;content-type/cc383f75803c577d6486841dc228aea994102a4b70bd5ff76f27d12bdb7af133', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Wed, 05 Mar 2025 11:41:14 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '09088182-5e6e-4725-bd6c-e1f476287b34', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[WARNING][2025-03-05 19:41:14.034] base.py:1083 [t:8258539328]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-05T11:41:13Z', 'Authorization': 'bce-auth-v1//2025-03-05T11:41:13Z/300/x-bce-date;host;request-source;content-type/cc383f75803c577d6486841dc228aea994102a4b70bd5ff76f27d12bdb7af133', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Wed, 05 Mar 2025 11:41:14 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '09088182-5e6e-4725-bd6c-e1f476287b34', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[ERROR][2025-03-05 19:41:14.216] base.py:134 [t:8258539328]: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-05T11:41:14Z', 'Authorization': 'bce-auth-v1//2025-03-05T11:41:14Z/300/x-bce-date;host;request-source;content-type/34d382a0332f9213819d512ca7cd9bf264d3126e0454764341daa2ed7c9bf1bb', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Wed, 05 Mar 2025 11:41:14 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '8d60a700-e5c1-42af-93cc-02e817421476', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[WARNING][2025-03-05 19:41:14.217] base.py:1083 [t:8258539328]: fetch_supported_models failed: http request url https://qianfan.baidubce.com/wenxinworkshop/service/list failed with http status code 403\n", + "error code from baidu: IamSignatureInvalid\n", + "error message from baidu: IamSignatureInvalid, cause: Could not find credential.\n", + "request headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Host': 'qianfan.baidubce.com', 'request-source': 'qianfan_py_sdk_v0.4.12.3', 'x-bce-date': '2025-03-05T11:41:14Z', 'Authorization': 'bce-auth-v1//2025-03-05T11:41:14Z/300/x-bce-date;host;request-source;content-type/34d382a0332f9213819d512ca7cd9bf264d3126e0454764341daa2ed7c9bf1bb', 'Content-Length': '2'}\n", + "request body: '{}'\n", + "response headers: {'Content-Length': '0', 'Date': 'Wed, 05 Mar 2025 11:41:14 GMT', 'X-Bce-Error-Code': 'IamSignatureInvalid', 'X-Bce-Error-Message': 'IamSignatureInvalid, cause: Could not find credential.', 'X-Bce-Exception-Point': 'Gateway', 'X-Bce-Gateway-Region': 'BJ', 'X-Bce-Request-Id': '8d60a700-e5c1-42af-93cc-02e817421476', 'Content-Type': 'text/plain; charset=utf-8'}\n", + "response body: b''\n", + "[INFO][2025-03-05 19:41:14.219] oauth.py:277 [t:8258539328]: trying to refresh token for ak `6hM0ZG***`\n", + "[INFO][2025-03-05 19:41:14.340] oauth.py:304 [t:8258539328]: successfully refresh token\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "你好!我是文心一言,英文名是ERNIE Bot。我是一款人工智能语言模型,可以协助你完成范围广泛的任务并提供有关各种主题的信息,比如回答问题,提供定义和解释及建议,还能提供上下文知识和对话管理。如果你有任何问题或需要帮助,随时向我提问,我会尽力回答。\n" + "你好!我是一个人工智能语言模型,我的名字是文心一言。我能够与人进行自然语言交互,并提供各种信息和服务。如果你有任何问题或需要帮助,请随时告诉我,我会尽力为你提供帮助。\n" ] } ], "source": [ - "from langchain_community.llms import QianfanLLMEndpoint\n", + "from langchain_community.llms.baidu_qianfan_endpoint import QianfanLLMEndpoint\n", "\n", "llm = QianfanLLMEndpoint(streaming=True)\n", "res = llm(\"你好,请你自我介绍一下!\")\n", @@ -740,7 +611,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 16, "id": "73d91f2b-f78a-4dc1-b1bf-09ce96a76a7e", "metadata": {}, "outputs": [], @@ -762,61 +633,36 @@ ] }, { - "cell_type": "code", - "execution_count": 6, - "id": "6a6b1ff6", + "cell_type": "markdown", + "id": "804cea30", "metadata": {}, - "outputs": [], "source": [ - "def gen_spark_params(model):\n", - " '''\n", - " 构造星火模型请求参数\n", - " '''\n", - "\n", - " spark_url_tpl = \"wss://spark-api.xf-yun.com/{}/chat\"\n", - " model_params_dict = {\n", - " # v1.5 版本\n", - " \"v1.5\": {\n", - " \"domain\": \"general\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v1.1\") # 云端环境的服务地址\n", - " },\n", - " # v2.0 版本\n", - " \"v2.0\": {\n", - " \"domain\": \"generalv2\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v2.1\") # 云端环境的服务地址\n", - " },\n", - " # v3.0 版本\n", - " \"v3.0\": {\n", - " \"domain\": \"generalv3\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v3.1\") # 云端环境的服务地址\n", - " },\n", - " # v3.5 版本\n", - " \"v3.5\": {\n", - " \"domain\": \"generalv3.5\", # 用于配置大模型版本\n", - " \"spark_url\": spark_url_tpl.format(\"v3.5\") # 云端环境的服务地址\n", - " }\n", - " }\n", - " return model_params_dict[model]" + "另外星火各模型对应的`spark_api_url`与`spark_llm_domain`均不相同,可以参考[接口说明](https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E)选择调用。" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 17, "id": "21f0359c-325e-43fc-911a-0fc1679fc3ce", "metadata": {}, "outputs": [], "source": [ - "from langchain_community.llms import SparkLLM\n", - "\n", - "spark_api_url = gen_spark_params(model=\"v1.5\")[\"spark_url\"]\n", + "from langchain_community.llms.sparkllm import SparkLLM\n", "\n", - "# Load the model(默认使用 v3.0)\n", - "llm = SparkLLM(spark_api_url = spark_api_url) #指定 v1.5版本" + "# Load the model\n", + "llm = SparkLLM(\n", + " model='Spark4.0 Ultra',\n", + " app_id=IFLYTEK_SPARK_APP_ID,\n", + " api_key=IFLYTEK_SPARK_API_KEY,\n", + " api_secret=IFLYTEK_SPARK_API_SECRET,\n", + " spark_api_url=\"wss://spark-api.xf-yun.com/v4.0/chat\",\n", + " spark_llm_domain=\"4.0Ultra\"\n", + " )" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 18, "id": "3dbd0649", "metadata": {}, "outputs": [ @@ -829,7 +675,7 @@ } ], "source": [ - "res = llm(\"你好,请你自我介绍一下!\")\n", + "res = llm.invoke(\"你好,请你自我介绍一下!\")\n", "print(res)" ] }, @@ -877,22 +723,12 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "96644f3b", - "metadata": {}, - "outputs": [], - "source": [ - "from zhipuai_llm import ZhipuAILLM" - ] - }, - { - "cell_type": "code", - "execution_count": 2, + "execution_count": 19, "id": "2ec52f07", "metadata": {}, "outputs": [], "source": [ - "\n", + "from zhipuai_llm import ZhipuaiLLM\n", "from dotenv import find_dotenv, load_dotenv\n", "import os\n", "\n", @@ -909,47 +745,39 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "id": "ef49232d", "metadata": {}, "outputs": [], "source": [ - "zhipuai_model = ZhipuAILLM(model = \"glm-4\", temperature = 0.1, api_key = api_key) #model=\"glm-4-0520\", " + "zhipuai_model = ZhipuaiLLM(model_name=\"glm-4-plus\", temperature=0.1, api_key=api_key)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 21, "id": "73328a40", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'你好!我是智谱清言,是清华大学 KEG 实验室和智谱 AI 公司于 2023 年共同训练的语言模型。我的目标是通过回答用户提出的问题来帮助他们解决问题。由于我是一个计算机程序,所以我没有自我意识,也不能像人类一样感知世界。我只能通过分析我所学到的信息来回答问题。'" + "AIMessage(content='你好!我是人工智能助手智谱清言(ChatGLM),是基于智谱 AI 公司于 2024 年训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持。', additional_kwargs={}, response_metadata={'time_in_seconds': 1.87}, id='run-4e509a7e-9859-4acb-9418-23245fa5b7a7-0', usage_metadata={'input_tokens': 11, 'output_tokens': 42, 'total_tokens': 53})" ] }, - "execution_count": 8, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "zhipuai_model(\"你好,请你自我介绍一下!\")" + "zhipuai_model.invoke(\"你好,请你自我介绍一下!\")" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c77fd1fe", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "llm-universe", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -963,7 +791,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/2.\346\236\204\345\273\272\346\243\200\347\264\242\351\227\256\347\255\224\351\223\276.ipynb" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/2.\346\236\204\345\273\272\346\243\200\347\264\242\351\227\256\347\255\224\351\223\276.ipynb" index 5e0e910..f986bc3 100644 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/2.\346\236\204\345\273\272\346\243\200\347\264\242\351\227\256\347\255\224\351\223\276.ipynb" +++ "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/2.\346\236\204\345\273\272\346\243\200\347\264\242\351\227\256\347\255\224\351\223\276.ipynb" @@ -74,10 +74,19 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "1e277ec2-28e4-448d-ae39-e0f703017811", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/yd/c4q_f88j1l70g7_jcb6pdnb80000gn/T/ipykernel_17468/4214663976.py:8: LangChainDeprecationWarning: The class `Chroma` was deprecated in LangChain 0.2.9 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-chroma package and should be used instead. To use it run `pip install -U :class:`~langchain-chroma` and import as `from :class:`~langchain_chroma import Chroma``.\n", + " vectordb = Chroma(\n" + ] + } + ], "source": [ "# 定义 Embeddings\n", "embedding = ZhipuAIEmbeddings()\n", @@ -102,7 +111,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "向量库中存储的数量:925\n" + "向量库中存储的数量:1004\n" ] } ], @@ -115,9 +124,7 @@ "id": "7d64f262-0638-4aa3-bd89-ef74e33f5238", "metadata": {}, "source": [ - "我们可以测试一下加载的向量数据库,使用一个问题 query 进行向量检索。如下代码会在向量数据库中根据相似性进行检索,返回前 k 个最相似的文档。\n", - "\n", - "> ⚠️使用相似性搜索前,请确保你已安装了 OpenAI 开源的快速分词工具 tiktoken 包:`pip install tiktoken`" + "我们可以测试一下加载的向量数据库,我们可以通过`as_retriever`方法把向量数据库构造成检索器。我们使用一个问题 query 进行向量检索。如下代码会在向量数据库中根据相似性进行检索,返回前 k 个最相似的文档。" ] }, { @@ -136,7 +143,8 @@ ], "source": [ "question = \"什么是prompt engineering?\"\n", - "docs = vectordb.similarity_search(question,k=3)\n", + "retriever = vectordb.as_retriever(search_kwargs={\"k\": 3})\n", + "docs = retriever.invoke(question)\n", "print(f\"检索到的内容数:{len(docs)}\")" ] }, @@ -159,34 +167,20 @@ "output_type": "stream", "text": [ "检索到的第0个内容: \n", - " 相反,我们应通过 Prompt 指引语言模型进行深入思考。可以要求其先列出对问题的各种看法,说明推理依据,然后再得出最终结论。在 Prompt 中添加逐步推理的要求,能让语言模型投入更多时间逻辑思维,输出结果也将更可靠准确。\n", + " 具体来说,首先编写初版 Prompt,然后通过多轮调整逐步改进,直到生成了满意的结果。对于更复杂的应用,可以在多个样本上进行迭代训练,评估 Prompt 的平均表现。在应用较为成熟后,才需要采用在多个样本集上评估 Prompt 性能的方式来进行细致优化。因为这需要较高的计算资源。\n", "\n", - "综上所述,给予语言模型充足的推理时间,是 Prompt Engineering 中一个非常重要的设计原则。这将大大提高语言模型处理复杂问题的效果,也是构建高质量 Prompt 的关键之处。开发者应注意给模型留出思考空间,以发挥语言模型的最大潜力。\n", + "总之,Prompt 工程师的核心是掌握 Prompt 的迭代开发和优化技巧,而非一开始就要求100%完美。通过不断调整试错,最终找到可靠适用的 Prompt 形式才是设计 Prompt 的正确方法。\n", "\n", - "2.1 指定完成任务所需的步骤\n", + "读者可以在 Jupyter Notebook 上,对本章给出的示例进行实践,修改 Prompt 并观察不同输出,以深入理解 Prompt 迭代优化的过程。这会对进一步开发复杂语言模型应用提供很好的实践准备。\n", "\n", - "接下来我们将通过给定一个复杂任务,给出完成该任务的一系列步骤,来展示这一策略的效果。\n", + "三、英文版\n", "\n", - "首先我们描述了杰克和吉尔的故事,并给出提示词执行以下操作:首先,用一句话概括三个反引号限定的文本。第二,将摘要翻译成英语。第三,在英语摘要中列出每个名称。第四,输出包含以下键的 JSON 对象:英语摘要和人名个数。要求输出以换行符分隔。\n", + "产品说明书\n", "-----------------------------------------------------\n", "检索到的第1个内容: \n", - " 该描述面向家具零售商,因此应具有技术性质,并侧重于产品的材料构造。\n", - "\n", - "在描述末尾,包括技术规格中每个7个字符的产品ID。\n", + " 第一章 简介\n", "\n", - "使用最多50个单词。\n", - "\n", - "技术规格: {fact_sheet_chair}\n", - "\"\"\"\n", - "response = get_completion(prompt)\n", - "print(response)\n", - "```\n", - "\n", - "通过上面的示例,我们可以看到 Prompt 迭代优化的一般过程。与训练机器学习模型类似,设计高效 Prompt 也需要多个版本的试错调整。\n", - "\n", - "具体来说,第一版 Prompt 应该满足明确和给模型思考时间两个原则。在此基础上,一般的迭代流程是:首先尝试一个初版,分析结果,然后继续改进 Prompt,逐步逼近最优。许多成功的Prompt 都是通过这种多轮调整得出的。\n", - "\n", - "后面我会展示一个更复杂的 Prompt 案例,让大家更深入地了解语言模型的强大能力。但在此之前,我想强调 Prompt 设计是一个循序渐进的过程。开发者需要做好多次尝试和错误的心理准备,通过不断调整和优化,才能找到最符合具体场景需求的 Prompt 形式。这需要智慧和毅力,但结果往往是值得的。\n", + "欢迎来到面向开发者的提示工程部分,本部分内容基于吴恩达老师的《Prompt Engineering for Developer》课程进行编写。《Prompt Engineering for Developer》课程是由吴恩达老师与 OpenAI 技术团队成员 Isa Fulford 老师合作授课,Isa 老师曾开发过受欢迎的 ChatGPT 检索插件,并且在教授 LLM (Large Language Model, 大语言模型)技术在产品中的应用方面做出了很大贡献。她还参与编写了教授人们使用 Prompt 的 OpenAI cookbook。我们希望通过本模块的学习,与大家分享使用提示词开发 LLM 应用的最佳实践和技巧。\n", "-----------------------------------------------------\n", "检索到的第2个内容: \n", " 第二章 提示原则\n", @@ -209,12 +203,59 @@ " print(f\"检索到的第{i}个内容: \\n {doc.page_content}\", end=\"\\n-----------------------------------------------------\\n\")" ] }, + { + "cell_type": "markdown", + "id": "56915a09", + "metadata": {}, + "source": [ + "## 2.创建检索链\n", + "我们可以使用LangChain的LCEL(LangChain Expression Language, LangChain表达式语言)来构建workflow,LCEL可以支持异步(ainvoke)、流式(stream)、批次处理(batch)等多种运行方式,同时还可以使用LangSmith无缝跟踪。\n", + "\n", + "接下来我们使用刚才定义的retriever定义一个简单的检索链。" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "583bbfe2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'前言\\n“周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\\n者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\\n导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\\n具体的推导细节。”\\n读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\\n老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\\n中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”。所以...... 本南瓜书只能算是我\\n等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\\n下学生”。\\n使用说明\\n• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\\n为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;\\n• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\\n\\n最新版PDF 获取地址:https://github.com/datawhalechina/pumpkin-book/releases\\n编委会\\n主编:Sm1les、archwalker、jbb0523\\n编委:juxiao、Majingmin、MrBigFan、shanry、Ye980226\\n封面设计:构思-Sm1les、创作-林王茂盛\\n致谢\\n特别感谢awyd234、feijuan、Ggmatch、Heitao5200、huaqing89、LongJH、LilRachel、LeoLRH、Nono17、\\nspareribs、sunchaothu、StevenLzq 在最早期的时候对南瓜书所做的贡献。\\n扫描下方二维码,然后回复关键词“南瓜书”,即可加入“南瓜书读者交流群”\\n版权声明\\n本作品采用知识共享署名-非商业性使用-相同方式共享4.0 国际许可协议进行许可。\\n\\n• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\\n有点飘的时候再回来啃都来得及;\\n• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\\n我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;\\n• 若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub 的\\nIssues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\\n提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\\n话可以微信联系我们(微信号:at-Sm1les);\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\\n在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1 版)'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "def combine_docs(docs):\n", + " return \"\\n\\n\".join(doc.page_content for doc in docs)\n", + "\n", + "combiner = RunnableLambda(combine_docs)\n", + "retrieval_chain = retriever | combiner\n", + "\n", + "retrieval_chain.invoke(\"南瓜书是什么?\")" + ] + }, + { + "cell_type": "markdown", + "id": "fba9c3cc", + "metadata": {}, + "source": [ + "LCEL中要求所有的组成元素都是`Runnable`类型,前面我们见过的`ChatModel`、`PromptTemplate`等都是继承自`Runnable`类。上方的`retrieval_chain`是由检索器`retriever`及组合器`combiner`组成的,由`|`符号串连,数据从左向右传递,即问题先被`retriever`检索得到检索结果,再被`combiner`进一步处理并输出。" + ] + }, { "cell_type": "markdown", "id": "4f7f8dbd-ecd5-449d-9753-aedc2b74289c", "metadata": {}, "source": [ - "## 2. 创建一个 LLM" + "## 3. 创建LLM" ] }, { @@ -227,7 +268,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "745d50cd", "metadata": {}, "outputs": [], @@ -238,26 +279,26 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "fca9fa08-ce50-478f-b0a4-c24166262dc5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content='你好,我是一个智能助手,专门为您提供各种服务和帮助。我可以回答您的问题,提供信息和建议,帮助您解决问题。如果您有任何需要,请随时告诉我,我会尽力帮助您。感谢您选择我作为您的助手!', response_metadata={'token_usage': {'completion_tokens': 95, 'prompt_tokens': 20, 'total_tokens': 115}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-bf5e56bd-a00f-4370-a1f3-6ce9712eaa65-0')" + "'当然可以!我是一个由OpenAI开发的人工智能助手,名叫ChatGPT。我擅长处理和生成自然语言文本,可以帮助回答问题、提供信息、协助解决问题以及进行各种对话。我没有个人经历或情感,但我会尽力提供准确和有用的回答。如果你有任何问题或需要帮助的地方,随时可以问我!'" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_openai import ChatOpenAI\n", - "llm = ChatOpenAI(model_name = \"gpt-3.5-turbo\", temperature = 0)\n", + "llm = ChatOpenAI(model_name=\"gpt-4o\", temperature=0)\n", "\n", - "llm.invoke(\"请你自我介绍一下自己!\")" + "llm.invoke(\"请你自我介绍一下自己!\").content" ] }, { @@ -265,61 +306,42 @@ "id": "8f361e27-cafb-48bf-bb41-50c9cb3a4f7e", "metadata": {}, "source": [ - "## 3. 构建检索问答链" + "## 4. 构建检索问答链" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "91be03f4-264d-45cb-bebd-223c1c5747fd", "metadata": {}, "outputs": [], "source": [ - "from langchain.prompts import PromptTemplate\n", + "from langchain_core.prompts import PromptTemplate\n", + "from langchain_core.runnables import RunnablePassthrough, RunnableParallel\n", + "from langchain_core.output_parsers import StrOutputParser\n", "\n", "template = \"\"\"使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答\n", - "案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。\n", + "案。最多使用三句话。尽量使答案简明扼要。请你在回答的最后说“谢谢你的提问!”。\n", "{context}\n", - "问题: {question}\n", + "问题: {input}\n", "\"\"\"\n", + "# 将template通过 PromptTemplate 转为可以在LCEL中使用的类型\n", + "prompt = PromptTemplate(template=template)\n", "\n", - "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", - " template=template)\n" - ] - }, - { - "cell_type": "markdown", - "id": "e2d06d7f-1dca-4d10-b5cd-3a23e9d91200", - "metadata": {}, - "source": [ - "再创建一个基于模板的检索链:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "8b05eb57-edf5-4b35-9538-42c2b8f5cc16", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.chains import RetrievalQA\n", - "\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n" + "qa_chain = (\n", + " RunnableParallel({\"context\": retrieval_chain, \"input\": RunnablePassthrough()})\n", + " | prompt\n", + " | llm\n", + " | StrOutputParser()\n", + ")" ] }, { "cell_type": "markdown", - "id": "c08ac734-6693-422d-97e1-50f140e2d358", + "id": "c3f13428", "metadata": {}, "source": [ - "创建检索 QA 链的方法 RetrievalQA.from_chain_type() 有如下参数:\n", - "- llm:指定使用的 LLM\n", - "- 指定 chain type : RetrievalQA.from_chain_type(chain_type=\"map_reduce\"),也可以利用load_qa_chain()方法指定chain type。\n", - "- 自定义 prompt :通过在RetrievalQA.from_chain_type()方法中,指定chain_type_kwargs参数,而该参数:chain_type_kwargs = {\"prompt\": PROMPT}\n", - "- 返回源文档:通过RetrievalQA.from_chain_type()方法中指定:return_source_documents=True参数;也可以使用RetrievalQAWithSourceChain()方法,返回源文档的引用(坐标或者叫主键、索引)" + "在上边代码中我们把刚才定义的检索链当作子链作为`prompt`的`context`,再使用`RunnablePassthrough`存储用户的问题作为`prompt`的`input`。又因为这两个操作是并行的,所以我们使用`RunnableParallel`来将他们并行运行。" ] }, { @@ -327,7 +349,7 @@ "id": "503a7972-a673-41ca-a028-647169d19fcb", "metadata": {}, "source": [ - "## 4.检索问答链效果测试" + "### 4.1检索问答链效果测试" ] }, { @@ -341,42 +363,25 @@ "question_2 = \"Prompt Engineering for Developer是谁写的?\"" ] }, - { - "cell_type": "markdown", - "id": "acc2223f-6fb5-4504-bfcd-ac74ca9ff2fa", - "metadata": {}, - "source": [ - "### 4.1 基于召回结果和 query 结合起来构建的 prompt 效果" - ] - }, { "cell_type": "code", "execution_count": 12, "id": "1eb5c5c3-9958-44f5-9fbc-f867de6c5042", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/lta/anaconda3/envs/llm_universe_2.x/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:119: LangChainDeprecationWarning: The method `Chain.__call__` was deprecated in langchain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n", - " warn_deprecated(\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ "大模型+知识库后回答 question_1 的结果:\n", - "南瓜书是对《机器学习》(西瓜书)中难以理解的公式进行解析和补充推导的书籍。\n", - "谢谢你的提问!\n" + "南瓜书是一本对《机器学习》(西瓜书)中较难理解的公式进行解析和补充推导细节的书籍,旨在帮助读者更好地理解机器学习中的数学推导。它以西瓜书的内容为前置知识,适合在遇到推导困难时查阅。南瓜书的目标是帮助读者成为“理工科数学基础扎实点的大二下学生”。谢谢你的提问!\n" ] } ], "source": [ - "result = qa_chain({\"query\": question_1})\n", + "result = qa_chain.invoke(question_1)\n", "print(\"大模型+知识库后回答 question_1 的结果:\")\n", - "print(result[\"result\"])" + "print(result)" ] }, { @@ -390,14 +395,14 @@ "output_type": "stream", "text": [ "大模型+知识库后回答 question_2 的结果:\n", - "Prompt Engineering for Developer是由吴恩达老师与OpenAI技术团队成员Isa Fulford老师合作编写的。谢谢你的提问!\n" + "《Prompt Engineering for Developer》课程是由吴恩达老师与 OpenAI 技术团队成员 Isa Fulford 合作授课的。谢谢你的提问!\n" ] } ], "source": [ - "result = qa_chain({\"query\": question_2})\n", + "result = qa_chain.invoke(question_2)\n", "print(\"大模型+知识库后回答 question_2 的结果:\")\n", - "print(result[\"result\"])" + "print(result)" ] }, { @@ -414,18 +419,10 @@ "id": "569fbe28-2e2d-4042-b3a1-65326842bdc9", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/lta/anaconda3/envs/llm_universe_2.x/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:119: LangChainDeprecationWarning: The method `BaseChatModel.predict` was deprecated in langchain-core 0.1.7 and will be removed in 0.3.0. Use invoke instead.\n", - " warn_deprecated(\n" - ] - }, { "data": { "text/plain": [ - "'南瓜书是指一本内容浅显易懂,适合初学者阅读的书籍。这个词源自于日本,最初是指一种用南瓜做成的灯笼,形状简单易制作。后来,这个词被引申为指那些内容简单易懂,适合初学者入门的书籍。'" + "'南瓜书通常是指《深度学习:算法与实现》这本书,因为书的封面是橙色的,看起来像南瓜。这本书由李沐、阿斯顿张、扎卡里·C·立顿和亚历山大·J·斯莫拉编写,主要介绍深度学习的基础知识和实践方法。书中涵盖了深度学习的基本概念、常用模型和算法,并提供了大量的代码示例,帮助读者理解和实现深度学习技术。由于其内容详实且实用性强,南瓜书在深度学习领域的学习者中受到广泛欢迎。'" ] }, "execution_count": 14, @@ -434,11 +431,7 @@ } ], "source": [ - "prompt_template = \"\"\"请回答下列问题:\n", - " {}\"\"\".format(question_1)\n", - "\n", - "### 基于大模型的问答\n", - "llm.predict(prompt_template)" + "llm.invoke(question_1).content" ] }, { @@ -450,7 +443,7 @@ { "data": { "text/plain": [ - "'这本书是由作者 Chris Johnson 编写的。'" + "'《Prompt Engineering for Developers》是由 Isa Fulford 和 Andrew Ng 合著的一本书。该书旨在帮助开发者更好地理解和应用提示工程(Prompt Engineering)技术,以提高与大型语言模型(如GPT-3)交互的效率和效果。'" ] }, "execution_count": 15, @@ -459,11 +452,7 @@ } ], "source": [ - "prompt_template = \"\"\"请回答下列问题:\n", - " {}\"\"\".format(question_2)\n", - "\n", - "### 基于大模型的问答\n", - "llm.predict(prompt_template)" + "llm.invoke(question_2).content" ] }, { @@ -479,7 +468,7 @@ "id": "ad20c510-f583-42b4-ac5c-b232388e9673", "metadata": {}, "source": [ - "## 5. 添加历史对话的记忆功能" + "## 5. 向检索链添加聊天记录" ] }, { @@ -495,7 +484,7 @@ "id": "8cadc3df-4123-4e12-9516-8632b10dc41f", "metadata": {}, "source": [ - "## 1. 记忆(Memory)" + "### 5.1 传递聊天记录" ] }, { @@ -503,7 +492,7 @@ "id": "e19f3fd2-14de-4177-893c-53ddaf6768e9", "metadata": {}, "source": [ - "在本节中我们将介绍 LangChain 中的储存模块,即如何将先前的对话嵌入到语言模型中的,使其具有连续对话的能力。我们将使用 `ConversationBufferMemory` ,它保存聊天消息历史记录的列表,这些历史记录将在回答问题时与问题一起传递给聊天机器人,从而将它们添加到上下文中。" + "在本节中我们将使用 LangChain 中的`ChatPromptTemplate`,即将先前的对话嵌入到语言模型中,使其具有连续对话的能力。 `ChatPromptTemplate`可以接收聊天消息历史记录,这些历史记录将在回答问题时与问题一起传递给聊天机器人,从而将它们添加到上下文中。" ] }, { @@ -513,20 +502,90 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.memory import ConversationBufferMemory\n", + "from langchain_core.prompts import ChatPromptTemplate\n", "\n", - "memory = ConversationBufferMemory(\n", - " memory_key=\"chat_history\", # 与 prompt 的输入变量保持一致。\n", - " return_messages=True # 将以消息列表的形式返回聊天记录,而不是单个字符串\n", + "# 问答链的系统prompt\n", + "system_prompt = (\n", + " \"你是一个问答任务的助手。 \"\n", + " \"请使用检索到的上下文片段回答这个问题。 \"\n", + " \"如果你不知道答案就说不知道。 \"\n", + " \"请使用简洁的话语回答用户。\"\n", + " \"\\n\\n\"\n", + " \"{context}\"\n", + ")\n", + "# 制定prompt template\n", + "qa_prompt = ChatPromptTemplate(\n", + " [\n", + " (\"system\", system_prompt),\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"human\", \"{input}\"),\n", + " ]\n", ")" ] }, { - "cell_type": "markdown", - "id": "48d580d9-b926-462f-9d6a-23135ecece37", + "cell_type": "code", + "execution_count": 17, + "id": "d44c7a0b", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "你是一个问答任务的助手。 请使用检索到的上下文片段回答这个问题。 如果你不知道答案就说不知道。 请使用简洁的话语回答用户。\n", + "\n", + "\n", + "南瓜书是什么?\n" + ] + } + ], "source": [ - "关于更多的 Memory 的使用,包括保留指定对话轮数、保存指定 token 数量、保存历史对话的总结摘要等内容,请参考 langchain 的 Memory 部分的相关文档。" + "# 无历史记录\n", + "messages = qa_prompt.invoke(\n", + " {\n", + " \"input\": \"南瓜书是什么?\",\n", + " \"chat_history\": [],\n", + " \"context\": \"\"\n", + " }\n", + ")\n", + "for message in messages.messages:\n", + " print(message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "f68ee06c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "你是一个问答任务的助手。 请使用检索到的上下文片段回答这个问题。 如果你不知道答案就说不知道。 请使用简洁的话语回答用户。\n", + "\n", + "\n", + "西瓜书是什么?\n", + "西瓜书是指周志华老师的《机器学习》一书,是机器学习领域的经典入门教材之一。\n", + "你可以介绍一下他吗?\n" + ] + } + ], + "source": [ + "# 有历史记录\n", + "messages = qa_prompt.invoke(\n", + " {\n", + " \"input\": \"你可以介绍一下他吗?\",\n", + " \"chat_history\": [\n", + " (\"human\", \"西瓜书是什么?\"),\n", + " (\"ai\", \"西瓜书是指周志华老师的《机器学习》一书,是机器学习领域的经典入门教材之一。\"),\n", + " ],\n", + " \"context\": \"\"\n", + " }\n", + ")\n", + "for message in messages.messages:\n", + " print(message.content)" ] }, { @@ -534,7 +593,7 @@ "id": "f2ec5f5c-5ee8-4b89-add3-25f3c864200d", "metadata": {}, "source": [ - "## 2. 对话检索链(ConversationalRetrievalChain)" + "### 5.2 带有信息压缩的检索链" ] }, { @@ -542,97 +601,148 @@ "id": "65f1afd4-a5e7-4eea-855b-d03055c93e2d", "metadata": {}, "source": [ - "对话检索链(ConversationalRetrievalChain)在检索 QA 链的基础上,增加了处理对话历史的能力。\n", - "\n", - "它的工作流程是:\n", - "1. 将之前的对话与新问题合并生成一个完整的查询语句。\n", - "2. 在向量数据库中搜索该查询的相关文档。\n", - "3. 获取结果后,存储所有答案到对话记忆区。\n", - "4. 用户可在 UI 中查看完整的对话流程。" + "因为我们正在搭建的问答链带有支持多轮对话功能,所以与单轮对话的问答链相比会多面临像上方输出结果的问题,即用户最新的对话语义不全,在使用用户问题查询向量数据库时很难检索到相关信息。像上方的“你可以介绍一下他吗?”,其实是“你可以介绍下周志华老师吗?”的意思。为了解决这个问题我们将采取信息压缩的方式,让llm根据历史记录完善用户的问题。" ] }, { - "cell_type": "markdown", - "id": "ad1705dc-db31-4663-beb0-01f14e2ca5da", + "cell_type": "code", + "execution_count": 19, + "id": "45325d24", "metadata": {}, + "outputs": [], "source": [ - "![](../../figures/Modular_components.png)" + "from langchain_core.runnables import RunnableBranch\n", + "\n", + "# 压缩问题的系统 prompt\n", + "condense_question_system_template = (\n", + " \"请根据聊天记录完善用户最新的问题,\"\n", + " \"如果用户最新的问题不需要完善则返回用户的问题。\"\n", + " )\n", + "# 构造 压缩问题的 prompt template\n", + "condense_question_prompt = ChatPromptTemplate([\n", + " (\"system\", condense_question_system_template),\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"human\", \"{input}\"),\n", + " ])\n", + "# 构造检索文档的链\n", + "# RunnableBranch 会根据条件选择要运行的分支\n", + "retrieve_docs = RunnableBranch(\n", + " # 分支 1: 若聊天记录中没有 chat_history 则直接使用用户问题查询向量数据库\n", + " (lambda x: not x.get(\"chat_history\", False), (lambda x: x[\"input\"]) | retriever, ),\n", + " # 分支 2 : 若聊天记录中有 chat_history 则先让 llm 根据聊天记录完善问题再查询向量数据库\n", + " condense_question_prompt | llm | StrOutputParser() | retriever,\n", + ")" ] }, { "cell_type": "markdown", - "id": "a0305623-1a06-4bb8-b340-dde57202dd67", + "id": "3d55a644", "metadata": {}, "source": [ - "这种链式方式将新问题放在之前对话的语境中进行检索,可以处理依赖历史信息的查询。并保留所有信\n", - "息在对话记忆中,方便追踪。\n", - "\n", - "接下来让我们可以测试这个对话检索链的效果:" + "## 5.3 支持聊天记录的检索问答链" ] }, { "cell_type": "markdown", - "id": "05dd113e-9d21-4385-a426-7e7a8ebb887a", + "id": "860756de", "metadata": {}, "source": [ - "使用上一节中的向量数据库和 LLM !首先提出一个无历史对话的问题“这门课会学习 Python 吗?”,并查看回答。" + "这里我们使用之前定义的问答模板即`qa_prompt`构造问答链,另外我们通过`RunnablePassthrough.assign`将中间的查询结果存为`\"context\"`,将最终结果存为`\"answer\"`。因为查询结果被存为`\"context\"`,所以我们整合查询结果的函数`combine_docs`也要做相应的改动。" ] }, { "cell_type": "code", - "execution_count": 17, - "id": "2d5dcc5f-545e-463a-9f3f-9eca90457caa", + "execution_count": 20, + "id": "43af0048", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "是的,您可以学习关于提示工程的知识。本模块基于吴恩达老师的《Prompt Engineering for Developer》课程编写,旨在与开发者分享使用提示词开发大语言模型应用的最佳实践和技巧。通过学习这些内容,您将了解如何使用提示词来构建令人惊叹的大语言模型应用。希望这对您有所帮助!\n" - ] - } - ], + "outputs": [], "source": [ - "from langchain.chains import ConversationalRetrievalChain\n", - "\n", - "retriever=vectordb.as_retriever()\n", - "\n", - "qa = ConversationalRetrievalChain.from_llm(\n", - " llm,\n", - " retriever=retriever,\n", - " memory=memory\n", + "# 重新定义 combine_docs\n", + "def combine_docs(docs):\n", + " return \"\\n\\n\".join(doc.page_content for doc in docs[\"context\"]) # 将 docs 改为 docs[\"context\"]\n", + "# 定义问答链\n", + "qa_chain = (\n", + " RunnablePassthrough.assign(context=combine_docs) # 使用 combine_docs 函数整合 qa_prompt 中的 context\n", + " | qa_prompt # 问答模板\n", + " | llm\n", + " | StrOutputParser() # 规定输出的格式为 str\n", ")\n", - "question = \"我可以学习到关于提示工程的知识吗?\"\n", - "result = qa({\"question\": question})\n", - "print(result['answer'])" + "# 定义带有历史记录的问答链\n", + "qa_history_chain = RunnablePassthrough.assign(\n", + " context = (lambda x: x) | retrieve_docs # 将查询结果存为 content\n", + " ).assign(answer=qa_chain) # 将最终结果存为 answer" ] }, { "cell_type": "markdown", - "id": "5ad80344-7079-4fa2-a3f6-065f1a0a2d79", + "id": "44466983", "metadata": {}, "source": [ - "然后基于答案进行下一个问题“为什么这门课需要教这方面的知识?”:" + "## 5.4 测试检索问答链" ] }, { "cell_type": "code", - "execution_count": 19, - "id": "83ef314c-515d-499c-8358-f19f188895b4", + "execution_count": 21, + "id": "5861ae52", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "这门课程的目的是教授开发者如何使用提示工程来开发大型语言模型(LLM)应用程序。通过学习如何编写清晰具体的指令以及给模型思考时间等核心原则,开发者可以更好地利用大型语言模型来解决各种任务,提高工作效率和创造力。\n" - ] + "data": { + "text/plain": [ + "{'input': '西瓜书是什么?',\n", + " 'chat_history': [],\n", + " 'context': [Document(metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}, page_content='前言\\n“周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\\n者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\\n导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\\n具体的推导细节。”\\n读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\\n老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\\n中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”。所以...... 本南瓜书只能算是我\\n等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\\n下学生”。\\n使用说明\\n• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\\n为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;\\n• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得'),\n", + " Document(metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 161, 'producer': 'xdvipdfmx (20200315)', 'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}, page_content='对于“误差”、“损失”、“风险”等概念的辨析,参见“西瓜书”第2 章2.1 节的注解。\\n→_→\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\\n←_←'),\n", + " Document(metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}, page_content='• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\\n有点飘的时候再回来啃都来得及;\\n• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\\n我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;\\n• 若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub 的\\nIssues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\\n提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\\n话可以微信联系我们(微信号:at-Sm1les);\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\\n在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1 版)')],\n", + " 'answer': '西瓜书是指周志华老师的《机器学习》一书,它是机器学习领域的经典入门教材之一。'}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 不带聊天记录\n", + "qa_history_chain.invoke({\n", + " \"input\": \"西瓜书是什么?\",\n", + " \"chat_history\": []\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "9074af66", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'input': '南瓜书跟它有什么关系?',\n", + " 'chat_history': [('human', '西瓜书是什么?'),\n", + " ('ai', '西瓜书是指周志华老师的《机器学习》一书,是机器学习领域的经典入门教材之一。')],\n", + " 'context': [Document(metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}, page_content='前言\\n“周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\\n者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\\n导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\\n具体的推导细节。”\\n读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\\n老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\\n中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”。所以...... 本南瓜书只能算是我\\n等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\\n下学生”。\\n使用说明\\n• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\\n为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;\\n• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得'),\n", + " Document(metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 13, 'producer': 'xdvipdfmx (20200315)', 'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}, page_content='→_→\\n欢迎去各大电商平台选购纸质版南瓜书《机器学习公式详解》\\n←_←\\n第1 章\\n绪论\\n本章作为“西瓜书”的开篇,主要讲解什么是机器学习以及机器学习的相关数学符号,为后续内容作\\n铺垫,并未涉及复杂的算法理论,因此阅读本章时只需耐心梳理清楚所有概念和数学符号即可。此外,在\\n阅读本章前建议先阅读西瓜书目录前页的《主要符号表》,它能解答在阅读“西瓜书”过程中产生的大部\\n分对数学符号的疑惑。\\n本章也作为本书的开篇,笔者在此赘述一下本书的撰写初衷,本书旨在以“过来人”的视角陪读者一\\n起阅读“西瓜书”,尽力帮读者消除阅读过程中的“数学恐惧”,只要读者学习过《高等数学》、《线性代\\n数》和《概率论与数理统计》这三门大学必修的数学课,均能看懂本书对西瓜书中的公式所做的解释和推\\n导,同时也能体会到这三门数学课在机器学习上碰撞产生的“数学之美”。\\n1.1\\n引言\\n本节以概念理解为主,在此对“算法”和“模型”作补充说明。“算法”是指从数据中学得“模型”的具\\n体方法,例如后续章节中将会讲述的线性回归、对数几率回归、决策树等。“算法”产出的结果称为“模型”,'),\n", + " Document(metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': '../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}, page_content='• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\\n有点飘的时候再回来啃都来得及;\\n• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\\n我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;\\n• 若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub 的\\nIssues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\\n提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\\n话可以微信联系我们(微信号:at-Sm1les);\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\\n在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1 版)')],\n", + " 'answer': '南瓜书是对西瓜书中较难理解的公式进行解析和推导细节补充的书籍。它以西瓜书的内容为前置知识,帮助读者更好地理解和学习西瓜书中的内容。'}" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "question = \"为什么这门课需要教这方面的知识?\"\n", - "result = qa({\"question\": question})\n", - "print(result['answer'])" + "# 带聊天记录\n", + "qa_history_chain.invoke({\n", + " \"input\": \"南瓜书跟它有什么关系?\",\n", + " \"chat_history\": [\n", + " (\"human\", \"西瓜书是什么?\"),\n", + " (\"ai\", \"西瓜书是指周志华老师的《机器学习》一书,是机器学习领域的经典入门教材之一。\"),\n", + " ]\n", + "})" ] }, { @@ -640,15 +750,13 @@ "id": "10b44d8b-145d-42fc-b790-476798efdabe", "metadata": {}, "source": [ - "可以看到,LLM 它准确地判断了这方面的知识,指代内容是强化学习的知识,也就\n", - "是我们成功地传递给了它历史信息。这种持续学习和关联前后问题的能力,可大大增强问答系统的连续\n", - "性和智能水平。" + "可以看到,LLM 准确地判断了“它”是什么,代表着我们成功地传递给了它历史信息。另外召回的内容也有着问题的答案,证明我们的信息压缩策略也起到了作用。这种关联前后问题及压缩信息并检索的能力,可大大增强问答系统的连续性和智能水平。" ] } ], "metadata": { "kernelspec": { - "display_name": "llm2", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -662,7 +770,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/3.\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\345\212\251\346\211\213.ipynb" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/3.\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\345\212\251\346\211\213.ipynb" index 10aa802..eac1481 100644 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/3.\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\345\212\251\346\211\213.ipynb" +++ "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/3.\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\345\212\251\346\211\213.ipynb" @@ -95,289 +95,203 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "3c4172dd", "metadata": {}, "outputs": [], "source": [ "import streamlit as st\n", - "from langchain_openai import ChatOpenAI" - ] - }, - { - "cell_type": "markdown", - "id": "097d95cb", - "metadata": {}, - "source": [ - "2. 创建应用程序的标题`st.title`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "562cd2c3", - "metadata": {}, - "outputs": [], - "source": [ - "st.title('🦜🔗 动手学大模型应用开发')" + "from langchain_openai import ChatOpenAI\n", + "import os\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableBranch, RunnablePassthrough\n", + "import sys\n", + "sys.path.append(\"notebook/C3 搭建知识库\") # 将父目录放入系统路径中\n", + "from zhipuai_embedding import ZhipuAIEmbeddings\n", + "from langchain_community.vectorstores import Chroma" ] }, { "cell_type": "markdown", - "id": "7d0465f6", + "id": "39a8a2b0", "metadata": {}, "source": [ - "3. 添加一个文本输入框,供用户输入其 OpenAI API 密钥" + "2. 定义`get_retriever`函数,该函数返回一个检索器" ] }, { "cell_type": "code", "execution_count": null, - "id": "cfea4bd9", + "id": "05af482b", "metadata": {}, "outputs": [], "source": [ - "openai_api_key = st.sidebar.text_input('OpenAI API Key', type='password')" + "def get_retriever():\n", + " # 定义 Embeddings\n", + " embedding = ZhipuAIEmbeddings()\n", + " # 向量数据库持久化路径\n", + " persist_directory = 'data_base/vector_db/chroma'\n", + " # 加载数据库\n", + " vectordb = Chroma(\n", + " persist_directory=persist_directory,\n", + " embedding_function=embedding\n", + " )\n", + " return vectordb.as_retriever()" ] }, { "cell_type": "markdown", - "id": "9e882d27", + "id": "40e67167", "metadata": {}, "source": [ - "4. 定义一个函数,使用用户密钥对 OpenAI API 进行身份验证、发送提示并获取 AI 生成的响应。该函数接受用户的提示作为参数,并使用`st.info`来在蓝色框中显示 AI 生成的响应" + "3. 定义`combine_docs`函数, 该函数处理检索器返回的文本" ] }, { "cell_type": "code", "execution_count": null, - "id": "9759d289", + "id": "c3d3552f", "metadata": {}, "outputs": [], "source": [ - "def generate_response(input_text):\n", - " llm = ChatOpenAI(temperature=0.7, openai_api_key=openai_api_key)\n", - " st.info(llm(input_text))" + "def combine_docs(docs):\n", + " return \"\\n\\n\".join(doc.page_content for doc in docs[\"context\"])" ] }, { "cell_type": "markdown", - "id": "d7c18041", + "id": "e3ee3571", "metadata": {}, "source": [ - "5. 最后,使用`st.form()`创建一个文本框(st.text_area())供用户输入。当用户单击`Submit`时,`generate-response()`将使用用户的输入作为参数来调用该函数" + "4. 定义`get_qa_history_chain`函数,该函数可以返回一个检索问答链" ] }, { "cell_type": "code", "execution_count": null, - "id": "1ce9471c", + "id": "8f09c82b", "metadata": {}, "outputs": [], "source": [ - "with st.form('my_form'):\n", - " text = st.text_area('Enter text:', 'What are the three key pieces of advice for learning how to code?')\n", - " submitted = st.form_submit_button('Submit')\n", - " if not openai_api_key.startswith('sk-'):\n", - " st.warning('Please enter your OpenAI API key!', icon='⚠')\n", - " if submitted and openai_api_key.startswith('sk-'):\n", - " generate_response(text)" - ] - }, - { - "cell_type": "markdown", - "id": "3f024f5b", - "metadata": {}, - "source": [ - "6. 保存当前的文件`streamlit_app.py`!" + "def get_qa_history_chain():\n", + " retriever = get_retriever()\n", + " llm = ChatOpenAI(model_name=\"gpt-4o\", temperature=0)\n", + " condense_question_system_template = (\n", + " \"请根据聊天记录总结用户最近的问题,\"\n", + " \"如果没有多余的聊天记录则返回用户的问题。\"\n", + " )\n", + " condense_question_prompt = ChatPromptTemplate([\n", + " (\"system\", condense_question_system_template),\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"human\", \"{input}\"),\n", + " ])\n", + "\n", + " retrieve_docs = RunnableBranch(\n", + " (lambda x: not x.get(\"chat_history\", False), (lambda x: x[\"input\"]) | retriever, ),\n", + " condense_question_prompt | llm | StrOutputParser() | retriever,\n", + " )\n", + "\n", + " system_prompt = (\n", + " \"你是一个问答任务的助手。 \"\n", + " \"请使用检索到的上下文片段回答这个问题。 \"\n", + " \"如果你不知道答案就说不知道。 \"\n", + " \"请使用简洁的话语回答用户。\"\n", + " \"\\n\\n\"\n", + " \"{context}\"\n", + " )\n", + " qa_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", system_prompt),\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"human\", \"{input}\"),\n", + " ]\n", + " )\n", + " qa_chain = (\n", + " RunnablePassthrough().assign(context=combine_docs)\n", + " | qa_prompt\n", + " | llm\n", + " | StrOutputParser()\n", + " )\n", + "\n", + " qa_history_chain = RunnablePassthrough().assign(\n", + " context = retrieve_docs, \n", + " ).assign(answer=qa_chain)\n", + " return qa_history_chain" ] }, { "cell_type": "markdown", - "id": "887beac1", + "id": "dedb3947", "metadata": {}, "source": [ - "7. 返回计算机的终端以运行该应用程序\n" + "5. 定义`gen_response`函数,它接受检索问答链、用户输入及聊天历史,并以流式返回该链输出" ] }, { "cell_type": "code", "execution_count": null, - "id": "25271c4d", + "id": "3e54d11b", "metadata": {}, "outputs": [], "source": [ - "streamlit run streamlit_app.py" - ] - }, - { - "cell_type": "markdown", - "id": "0a84b80b", - "metadata": {}, - "source": [ - "结果展示如下: \n", - " \n", - "![](../../figures/streamlit_app.png)" + "def gen_response(chain, input, chat_history):\n", + " response = chain.stream({\n", + " \"input\": input,\n", + " \"chat_history\": chat_history\n", + " })\n", + " for res in response:\n", + " if \"answer\" in res.keys():\n", + " yield res[\"answer\"]" ] }, { "cell_type": "markdown", - "id": "e9c38d3b", + "id": "a0685932", "metadata": {}, "source": [ - "但是当前只能进行单轮对话,我们对上述做些修改,通过使用 `st.session_state` 来存储对话历史,可以在用户与应用程序交互时保留整个对话的上下文。\n", - " \n", - "![](../../figures/streamlit_app2.png)\n", - " \n", - "具体代码如下:" + "6. 定义main函数,该函数制定显示效果与逻辑" ] }, { "cell_type": "code", "execution_count": null, - "id": "7914b7db", + "id": "38688f12", "metadata": {}, "outputs": [], "source": [ - "# Streamlit 应用程序界面\n", "def main():\n", - " st.title('🦜🔗 动手学大模型应用开发')\n", - " openai_api_key = st.sidebar.text_input('OpenAI API Key', type='password')\n", - "\n", - " # 用于跟踪对话历史\n", - " if 'messages' not in st.session_state:\n", + " st.markdown('### 🦜🔗 动手学大模型应用开发')\n", + " # st.session_state可以存储用户与应用交互期间的状态与数据\n", + " # 存储对话历史\n", + " if \"messages\" not in st.session_state:\n", " st.session_state.messages = []\n", - "\n", - " messages = st.container(height=300)\n", + " # 存储检索问答链\n", + " if \"qa_history_chain\" not in st.session_state:\n", + " st.session_state.qa_history_chain = get_qa_history_chain()\n", + " # 建立容器 高度为500 px\n", + " messages = st.container(height=550)\n", + " # 显示整个对话历史\n", + " for message in st.session_state.messages: # 遍历对话历史\n", + " with messages.chat_message(message[0]): # messages指在容器下显示,chat_message显示用户及ai头像\n", + " st.write(message[1]) # 打印内容\n", " if prompt := st.chat_input(\"Say something\"):\n", " # 将用户输入添加到对话历史中\n", - " st.session_state.messages.append({\"role\": \"user\", \"text\": prompt})\n", - "\n", - " # 调用 respond 函数获取回答\n", - " answer = generate_response(prompt, openai_api_key)\n", - " # 检查回答是否为 None\n", - " if answer is not None:\n", - " # 将LLM的回答添加到对话历史中\n", - " st.session_state.messages.append({\"role\": \"assistant\", \"text\": answer})\n", - "\n", - " # 显示整个对话历史\n", - " for message in st.session_state.messages:\n", - " if message[\"role\"] == \"user\":\n", - " messages.chat_message(\"user\").write(message[\"text\"])\n", - " elif message[\"role\"] == \"assistant\":\n", - " messages.chat_message(\"assistant\").write(message[\"text\"]) " - ] - }, - { - "cell_type": "markdown", - "id": "483c244a", - "metadata": {}, - "source": [ - "## 三、添加检索问答" - ] - }, - { - "cell_type": "markdown", - "id": "6916ca5e", - "metadata": {}, - "source": [ - "先将`2.构建检索问答链`部分的代码进行封装:\n", - "- get_vectordb函数返回C3部分持久化后的向量知识库\n", - "- get_chat_qa_chain函数返回调用带有历史记录的检索问答链后的结果\n", - "- get_qa_chain函数返回调用不带有历史记录的检索问答链后的结果" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5943aca4", - "metadata": {}, - "outputs": [], - "source": [ - "def get_vectordb():\n", - " # 定义 Embeddings\n", - " embedding = ZhipuAIEmbeddings()\n", - " # 向量数据库持久化路径\n", - " persist_directory = '../C3 搭建知识库/data_base/vector_db/chroma'\n", - " # 加载数据库\n", - " vectordb = Chroma(\n", - " persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上\n", - " embedding_function=embedding\n", - " )\n", - " return vectordb\n", - "\n", - "#带有历史记录的问答链\n", - "def get_chat_qa_chain(question:str,openai_api_key:str):\n", - " vectordb = get_vectordb()\n", - " llm = ChatOpenAI(model_name = \"gpt-3.5-turbo\", temperature = 0,openai_api_key = openai_api_key)\n", - " memory = ConversationBufferMemory(\n", - " memory_key=\"chat_history\", # 与 prompt 的输入变量保持一致。\n", - " return_messages=True # 将以消息列表的形式返回聊天记录,而不是单个字符串\n", - " )\n", - " retriever=vectordb.as_retriever()\n", - " qa = ConversationalRetrievalChain.from_llm(\n", - " llm,\n", - " retriever=retriever,\n", - " memory=memory\n", - " )\n", - " result = qa({\"question\": question})\n", - " return result['answer']\n", - "\n", - "#不带历史记录的问答链\n", - "def get_qa_chain(question:str,openai_api_key:str):\n", - " vectordb = get_vectordb()\n", - " llm = ChatOpenAI(model_name = \"gpt-3.5-turbo\", temperature = 0,openai_api_key = openai_api_key)\n", - " template = \"\"\"使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答\n", - " 案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。\n", - " {context}\n", - " 问题: {question}\n", - " \"\"\"\n", - " QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", - " template=template)\n", - " qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", - " result = qa_chain({\"query\": question})\n", - " return result[\"result\"]" - ] - }, - { - "cell_type": "markdown", - "id": "073fce21", - "metadata": {}, - "source": [ - "然后,添加一个单选按钮部件`st.radio`,选择进行问答的模式:\n", - "- None:不使用检索问答的普通模式\n", - "- qa_chain:不带历史记录的检索问答模式\n", - "- chat_qa_chain:带历史记录的检索问答模式" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "37b8fa45", - "metadata": {}, - "outputs": [], - "source": [ - "selected_method = st.radio(\n", - " \"你想选择哪种模式进行对话?\",\n", - " [\"None\", \"qa_chain\", \"chat_qa_chain\"],\n", - " captions = [\"不使用检索问答的普通模式\", \"不带历史记录的检索问答模式\", \"带历史记录的检索问答模式\"])" - ] - }, - { - "cell_type": "markdown", - "id": "9a5917f6", - "metadata": {}, - "source": [ - "最终的效果如下: \n", - " \n", - "![](../../figures/streamlit_app3.png)\n", - "\n", - "进入页面,首先先输入OPEN_API_KEY(默认),然后点击单选按钮选择进行问答的模式,最后在输入框输入你的问题,按下回车即可!\n", - "\n", - "> 完整代码参考[streamlit_app.py](./streamlit_app.py)" + " st.session_state.messages.append((\"human\", prompt))\n", + " # 显示当前用户输入\n", + " with messages.chat_message(\"human\"):\n", + " st.write(prompt)\n", + " # 生成回复\n", + " answer = gen_response(\n", + " chain=st.session_state.qa_history_chain,\n", + " input=prompt,\n", + " chat_history=st.session_state.messages\n", + " )\n", + " # 流式输出\n", + " with messages.chat_message(\"ai\"):\n", + " output = st.write_stream(answer)\n", + " # 将输出存入st.session_state.messages\n", + " st.session_state.messages.append((\"ai\", output))" ] }, { @@ -387,7 +301,8 @@ "source": [ "## 四、部署应用程序 \n", "\n", - "要将应用程序部署到 Streamlit Cloud,请执行以下步骤: \n", + "**本地运行:** `streamlit run \"notebook/C4 构建 RAG 应用/streamlit_app.py\"`\n", + "**远程部署:**要将应用程序部署到 Streamlit Cloud,请执行以下步骤: \n", " \n", "1. 为应用程序创建 GitHub 存储库。您的存储库应包含两个文件: \n", " \n", @@ -419,7 +334,7 @@ ], "metadata": { "kernelspec": { - "display_name": "llm2", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -433,7 +348,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/streamlit_app.py" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/streamlit_app.py" index 63f9720..dcfd5b9 100644 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/streamlit_app.py" +++ "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/streamlit_app.py" @@ -2,119 +2,111 @@ from langchain_openai import ChatOpenAI import os from langchain_core.output_parsers import StrOutputParser -from langchain.prompts import PromptTemplate -from langchain.chains import RetrievalQA +from langchain_core.prompts import ChatPromptTemplate +from langchain_core.runnables import RunnableBranch, RunnablePassthrough import sys -sys.path.append("../C3 搭建知识库") # 将父目录放入系统路径中 +sys.path.append("notebook/C3 搭建知识库") # 将父目录放入系统路径中 from zhipuai_embedding import ZhipuAIEmbeddings -from langchain.vectorstores.chroma import Chroma -from langchain.memory import ConversationBufferMemory -from langchain.chains import ConversationalRetrievalChain -from dotenv import load_dotenv, find_dotenv -_ = load_dotenv(find_dotenv()) # read local .env file +from langchain_community.vectorstores import Chroma - -#export OPENAI_API_KEY= -#os.environ["OPENAI_API_BASE"] = 'https://api.chatgptid.net/v1' -zhipuai_api_key = os.environ['ZHIPUAI_API_KEY'] - - -def generate_response(input_text, openai_api_key): - llm = ChatOpenAI(temperature=0.7, openai_api_key=openai_api_key) - output = llm.invoke(input_text) - output_parser = StrOutputParser() - output = output_parser.invoke(output) - #st.info(output) - return output - -def get_vectordb(): +def get_retriever(): # 定义 Embeddings embedding = ZhipuAIEmbeddings() # 向量数据库持久化路径 - persist_directory = '../C3 搭建知识库/data_base/vector_db/chroma' + persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma( - persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上 + persist_directory=persist_directory, embedding_function=embedding ) - return vectordb + return vectordb.as_retriever() + +def combine_docs(docs): + return "\n\n".join(doc.page_content for doc in docs["context"]) + +def get_qa_history_chain(): + retriever = get_retriever() + llm = ChatOpenAI(model_name="gpt-4o", temperature=0) + condense_question_system_template = ( + "请根据聊天记录总结用户最近的问题," + "如果没有多余的聊天记录则返回用户的问题。" + ) + condense_question_prompt = ChatPromptTemplate([ + ("system", condense_question_system_template), + ("placeholder", "{chat_history}"), + ("human", "{input}"), + ]) + + retrieve_docs = RunnableBranch( + (lambda x: not x.get("chat_history", False), (lambda x: x["input"]) | retriever, ), + condense_question_prompt | llm | StrOutputParser() | retriever, + ) -#带有历史记录的问答链 -def get_chat_qa_chain(question:str,openai_api_key:str): - vectordb = get_vectordb() - llm = ChatOpenAI(model_name = "gpt-3.5-turbo", temperature = 0,openai_api_key = openai_api_key) - memory = ConversationBufferMemory( - memory_key="chat_history", # 与 prompt 的输入变量保持一致。 - return_messages=True # 将以消息列表的形式返回聊天记录,而不是单个字符串 + system_prompt = ( + "你是一个问答任务的助手。 " + "请使用检索到的上下文片段回答这个问题。 " + "如果你不知道答案就说不知道。 " + "请使用简洁的话语回答用户。" + "\n\n" + "{context}" + ) + qa_prompt = ChatPromptTemplate.from_messages( + [ + ("system", system_prompt), + ("placeholder", "{chat_history}"), + ("human", "{input}"), + ] ) - retriever=vectordb.as_retriever() - qa = ConversationalRetrievalChain.from_llm( - llm, - retriever=retriever, - memory=memory + qa_chain = ( + RunnablePassthrough().assign(context=combine_docs) + | qa_prompt + | llm + | StrOutputParser() ) - result = qa({"question": question}) - return result['answer'] -#不带历史记录的问答链 -def get_qa_chain(question:str,openai_api_key:str): - vectordb = get_vectordb() - llm = ChatOpenAI(model_name = "gpt-3.5-turbo", temperature = 0,openai_api_key = openai_api_key) - template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答 - 案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。 - {context} - 问题: {question} - """ - QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"], - template=template) - qa_chain = RetrievalQA.from_chain_type(llm, - retriever=vectordb.as_retriever(), - return_source_documents=True, - chain_type_kwargs={"prompt":QA_CHAIN_PROMPT}) - result = qa_chain({"query": question}) - return result["result"] + qa_history_chain = RunnablePassthrough().assign( + context = retrieve_docs, + ).assign(answer=qa_chain) + return qa_history_chain +def gen_response(chain, input, chat_history): + response = chain.stream({ + "input": input, + "chat_history": chat_history + }) + for res in response: + if "answer" in res.keys(): + yield res["answer"] # Streamlit 应用程序界面 def main(): - st.title('🦜🔗 动手学大模型应用开发') - openai_api_key = st.sidebar.text_input('OpenAI API Key', type='password') - - # 添加一个选择按钮来选择不同的模型 - #selected_method = st.sidebar.selectbox("选择模式", ["qa_chain", "chat_qa_chain", "None"]) - selected_method = st.radio( - "你想选择哪种模式进行对话?", - ["None", "qa_chain", "chat_qa_chain"], - captions = ["不使用检索问答的普通模式", "不带历史记录的检索问答模式", "带历史记录的检索问答模式"]) + st.markdown('### 🦜🔗 动手学大模型应用开发') # 用于跟踪对话历史 - if 'messages' not in st.session_state: + if "messages" not in st.session_state: st.session_state.messages = [] - - messages = st.container(height=300) + # 存储检索问答链 + if "qa_history_chain" not in st.session_state: + st.session_state.qa_history_chain = get_qa_history_chain() + messages = st.container(height=550) + # 显示整个对话历史 + for message in st.session_state.messages: + with messages.chat_message(message[0]): + st.write(message[1]) if prompt := st.chat_input("Say something"): # 将用户输入添加到对话历史中 - st.session_state.messages.append({"role": "user", "text": prompt}) - - if selected_method == "None": - # 调用 respond 函数获取回答 - answer = generate_response(prompt, openai_api_key) - elif selected_method == "qa_chain": - answer = get_qa_chain(prompt,openai_api_key) - elif selected_method == "chat_qa_chain": - answer = get_chat_qa_chain(prompt,openai_api_key) - - # 检查回答是否为 None - if answer is not None: - # 将LLM的回答添加到对话历史中 - st.session_state.messages.append({"role": "assistant", "text": answer}) - - # 显示整个对话历史 - for message in st.session_state.messages: - if message["role"] == "user": - messages.chat_message("user").write(message["text"]) - elif message["role"] == "assistant": - messages.chat_message("assistant").write(message["text"]) + st.session_state.messages.append(("human", prompt)) + with messages.chat_message("human"): + st.write(prompt) + + answer = gen_response( + chain=st.session_state.qa_history_chain, + input=prompt, + chat_history=st.session_state.messages + ) + with messages.chat_message("ai"): + output = st.write_stream(answer) + st.session_state.messages.append(("ai", output)) if __name__ == "__main__": diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/wenxin_llm.py" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/wenxin_llm.py" deleted file mode 100644 index 5b9e036..0000000 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/wenxin_llm.py" +++ /dev/null @@ -1,64 +0,0 @@ - -# 基于 LangChain 定义文心模型调用方式 - -from typing import Any, List, Mapping, Optional, Dict -from langchain_core.callbacks.manager import CallbackManagerForLLMRun -from langchain_core.language_models.llms import LLM -import qianfan - -# 继承自 langchain_core.language_models.llms.LLM -class Wenxin_LLM(LLM): - # 默认选用 ERNIE-Bot-turbo 模型,即目前一般所说的百度文心大模型 - model: str = "ERNIE-Bot-turbo" - # 温度系数 - temperature: float = 0.1 - # API_Key - api_key: str = None - # Secret_Key - secret_key : str = None - # 系统消息 - system : str = None - - - - def _call(self, prompt : str, stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any): - def gen_wenxin_messages(prompt): - ''' - 构造文心模型请求参数 messages - - 请求参数: - prompt: 对应的用户提示词 - ''' - messages = [{"role": "user", "content": prompt}] - return messages - - chat_comp = qianfan.ChatCompletion(ak=self.api_key,sk=self.secret_key) - message = gen_wenxin_messages(prompt) - - resp = chat_comp.do(messages = message, - model= self.model, - temperature = self.temperature, - system = self.system) - - return resp["result"] - - # 首先定义一个返回默认参数的方法 - @property - def _default_params(self) -> Dict[str, Any]: - """获取调用Ennie API的默认参数。""" - normal_params = { - "temperature": self.temperature, - } - # print(type(self.model_kwargs)) - return {**normal_params} - - @property - def _llm_type(self) -> str: - return "Wenxin" - - @property - def _identifying_params(self) -> Mapping[str, Any]: - """Get the identifying parameters.""" - return {**{"model": self.model}, **self._default_params} diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/zhipuai_llm.py" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/zhipuai_llm.py" index 4710136..bf8d207 100644 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/zhipuai_llm.py" +++ "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/zhipuai_llm.py" @@ -1,66 +1,190 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -from typing import Any, List, Mapping, Optional, Dict -from langchain_core.callbacks.manager import CallbackManagerForLLMRun -from langchain_core.language_models.llms import LLM +from typing import Any, Dict, Iterator, List, Optional from zhipuai import ZhipuAI +from langchain_core.callbacks import ( + CallbackManagerForLLMRun, +) +from langchain_core.language_models import BaseChatModel +from langchain_core.messages import ( + AIMessage, + AIMessageChunk, + BaseMessage, + SystemMessage, + ChatMessage, + HumanMessage +) +from langchain_core.messages.ai import UsageMetadata +from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult +import time -import os - -# 继承自 langchain.llms.base.LLM -class ZhipuAILLM(LLM): - # 默认选用 glm-4 - model: str = "glm-4" - # 温度系数 - temperature: float = 0.1 - # API_Key - api_key: str = None - - def _call(self, prompt : str, stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any): - client = ZhipuAI( - api_key = self.api_key - ) +class ZhipuaiLLM(BaseChatModel): + """自定义Zhipuai聊天模型。 + """ + + model_name: str = None + temperature: Optional[float] = None + max_tokens: Optional[int] = None + timeout: Optional[int] = None + stop: Optional[List[str]] = None + max_retries: int = 3 + api_key: str | None = None + + def _generate( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> ChatResult: + """通过调用智谱API从而响应输入。 - def gen_glm_params(prompt): - ''' - 构造 GLM 模型请求参数 messages - - 请求参数: - prompt: 对应的用户提示词 - ''' - messages = [{"role": "user", "content": prompt}] - return messages - - messages = gen_glm_params(prompt) - response = client.chat.completions.create( - model = self.model, - messages = messages, - temperature = self.temperature + Args: + messages: 由messages列表组成的prompt + stop: 在模型生成的回答中有该字符串列表中的元素则停止响应 + run_manager: 一个为LLM提供回调的运行管理器 + """ + + messages = [_convert_message_to_dict(message) for message in messages] + start_time = time.time() + response = ZhipuAI(api_key=self.api_key).chat.completions.create( + model=self.model_name, + temperature=self.temperature, + max_tokens=self.max_tokens, + timeout=self.timeout, + stop=stop, + messages=messages + ) + time_in_seconds = time.time() - start_time + message = AIMessage( + content=response.choices[0].message.content, + additional_kwargs={}, + response_metadata={ + "time_in_seconds": round(time_in_seconds, 3), + }, + usage_metadata={ + "input_tokens": response.usage.prompt_tokens, + "output_tokens": response.usage.completion_tokens, + "total_tokens": response.usage.total_tokens, + }, ) + generation = ChatGeneration(message=message) + return ChatResult(generations=[generation]) - if len(response.choices) > 0: - return response.choices[0].message.content - return "generate answer error" + def _stream( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + """通过调用智谱API返回流式输出。 + Args: + messages: 由messages列表组成的prompt + stop: 在模型生成的回答中有该字符串列表中的元素则停止响应 + run_manager: 一个为LLM提供回调的运行管理器 + """ + messages = [_convert_message_to_dict(message) for message in messages] + response = ZhipuAI().chat.completions.create( + model=self.model_name, + stream=True, + temperature=self.temperature, + max_tokens=self.max_tokens, + timeout=self.timeout, + stop=stop, + messages=messages + ) + start_time = time.time() + for res in response: + if res.usage: + usage_metadata = UsageMetadata( + { + "input_tokens": res.usage.prompt_tokens, + "output_tokens": res.usage.completion_tokens, + "total_tokens": res.usage.total_tokens, + } + ) + chunk = ChatGenerationChunk( + message=AIMessageChunk(content=res.choices[0].delta.content) + ) - # 首先定义一个返回默认参数的方法 - @property - def _default_params(self) -> Dict[str, Any]: - """获取调用API的默认参数。""" - normal_params = { - "temperature": self.temperature, - } - # print(type(self.model_kwargs)) - return {**normal_params} + if run_manager: + # This is optional in newer versions of LangChain + # The on_llm_new_token will be called automatically + run_manager.on_llm_new_token(res.choices[0].delta.content, chunk=chunk) + + yield chunk + time_in_sec = time.time() - start_time + # Let's add some other information (e.g., response metadata) + chunk = ChatGenerationChunk( + message=AIMessageChunk(content="", response_metadata={"time_in_sec": round(time_in_sec, 3)}, usage_metadata=usage_metadata) + ) + if run_manager: + # This is optional in newer versions of LangChain + # The on_llm_new_token will be called automatically + run_manager.on_llm_new_token("", chunk=chunk) + yield chunk @property def _llm_type(self) -> str: - return "Zhipu" + """获取此聊天模型使用的语言模型类型。""" + return self.model_name @property - def _identifying_params(self) -> Mapping[str, Any]: - """Get the identifying parameters.""" - return {**{"model": self.model}, **self._default_params} + def _identifying_params(self) -> Dict[str, Any]: + """返回一个标识参数的字典。 + + 该信息由LangChain回调系统使用,用于跟踪目的,使监视llm成为可能。 + """ + return { + "model_name": self.model_name, + } + +def _convert_message_to_dict(message: BaseMessage) -> dict: + """把LangChain的消息格式转为智谱支持的格式 + + Args: + message: The LangChain message. + + Returns: + The dictionary. + """ + message_dict: Dict[str, Any] = {"content": message.content} + if (name := message.name or message.additional_kwargs.get("name")) is not None: + message_dict["name"] = name + + # populate role and additional message data + if isinstance(message, ChatMessage): + message_dict["role"] = message.role + elif isinstance(message, HumanMessage): + message_dict["role"] = "user" + elif isinstance(message, AIMessage): + message_dict["role"] = "assistant" + elif isinstance(message, SystemMessage): + message_dict["role"] = "system" + else: + raise TypeError(f"Got unknown type {message}") + return message_dict + +if __name__ == "__main__": + # Test + model = ZhipuaiLLM(model_name="glm-4-plus") + # invoke + answer = model.invoke("Hello") + print(answer) + answer = model.invoke( + [ + HumanMessage(content="hello!"), + AIMessage(content="Hi there human!"), + HumanMessage(content="Meow!"), + ] + ) + print(answer) + # stream + for chunk in model.stream([ + HumanMessage(content="hello!"), + AIMessage(content="Hi there human!"), + HumanMessage(content="Meow!"), + ]): + print(chunk.content, end="|") + # batch + print(model.batch(["hello", "goodbye"])) diff --git "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/\351\231\2041.LangChain\350\207\252\345\256\232\344\271\211 LLM.ipynb" "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/\351\231\2041.LangChain\350\207\252\345\256\232\344\271\211 LLM.ipynb" index 0f3f82b..36f5c65 100644 --- "a/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/\351\231\2041.LangChain\350\207\252\345\256\232\344\271\211 LLM.ipynb" +++ "b/notebook/C4 \346\236\204\345\273\272 RAG \345\272\224\347\224\250/\351\231\2041.LangChain\350\207\252\345\256\232\344\271\211 LLM.ipynb" @@ -13,61 +13,78 @@ "source": [ "LangChain 为基于 LLM 开发自定义应用提供了高效的开发框架,便于开发者迅速地激发 LLM 的强大能力,搭建 LLM 应用。LangChain 也同样支持多种大模型,内置了 OpenAI、LLAMA 等大模型的调用接口。但是,LangChain 并没有内置所有大模型,它通过允许用户自定义 LLM 类型,来提供强大的可扩展性。\n", "\n", - "在本部分,我们以百度文心大模型为例,讲述如何基于 LangChain 自定义 LLM,让我们基于 LangChain 搭建的应用能够支持百度文心、讯飞星火等国内大模型。\n", + "在本部分,我们以智谱为例,讲述如何基于 LangChain 自定义 LLM,让我们基于 LangChain 搭建的应用能够支持国内平台。\n", "\n", "本部分涉及相对更多 LangChain、大模型调用的技术细节,有精力同学可以学习部署,如无精力可以直接使用后续代码来支持调用。\n", "\n", "要实现自定义 LLM,需要定义一个自定义类继承自 LangChain 的 LLM 基类,然后定义两个函数: \n", - "① _call 方法,其接受一个字符串,并返回一个字符串,即模型的核心调用; \n", - "② _identifying_params 方法,用于打印 LLM 信息。 \n", + "① _generate 方法,接收一系列消息及其他参数,并返回输出; \n", + "② _stream 方法, 接收一系列消息及其他参数,并以流式返回结果。 \n", "\n", "首先我们导入所需的第三方库:" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "import json\n", - "import time\n", - "from typing import Any, List, Mapping, Optional, Dict, Union, Tuple\n", - "import requests\n", - "from langchain.callbacks.manager import CallbackManagerForLLMRun\n", - "from langchain.llms.base import LLM\n", - "from langchain.utils import get_from_dict_or_env\n", - "from pydantic import Field, model_validator" + "from typing import Any, Dict, Iterator, List, Optional\n", + "from zhipuai import ZhipuAI\n", + "from langchain_core.callbacks import (\n", + " CallbackManagerForLLMRun,\n", + ")\n", + "from langchain_core.language_models import BaseChatModel\n", + "from langchain_core.messages import (\n", + " AIMessage,\n", + " AIMessageChunk,\n", + " BaseMessage,\n", + " SystemMessage,\n", + " ChatMessage,\n", + " HumanMessage\n", + ")\n", + "from langchain_core.messages.ai import UsageMetadata\n", + "from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult\n", + "import time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "由于百度文心使用双重秘钥进行认证,用户需要先基于 API_Key 与 Secret_Key 来获取 access_token,再使用 access_token 来实现对模型的调用(详见《3. 调用百度文心》),因此我们需要先定义一个 get_access_token 方法来获取 access_token:" + "由于LangChain的消息类型是`HumanMessage`、`AIMessage`等格式,与一般模型接收的字典格式不一样,因此我们需要先定义一个将LangChain的格式转为字典的函数。" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "def get_access_token(api_key : str, secret_key : str):\n", + "def _convert_message_to_dict(message: BaseMessage) -> dict:\n", + " \"\"\"把LangChain的消息格式转为智谱支持的格式\n", + " Args:\n", + " message: The LangChain message.\n", + " Returns:\n", + " The dictionary.\n", " \"\"\"\n", - " 使用 API Key,Secret Key 获取access_token,替换下列示例中的应用API Key、应用Secret Key\n", - " \"\"\"\n", - " # 指定网址\n", - " url = f\"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={api_key}&client_secret={secret_key}\"\n", - " # 设置 POST 访问\n", - " payload = json.dumps(\"\")\n", - " headers = {\n", - " 'Content-Type': 'application/json',\n", - " 'Accept': 'application/json'\n", - " }\n", - " # 通过 POST 访问获取账户对应的 access_token\n", - " response = requests.request(\"POST\", url, headers=headers, data=payload)\n", - " return response.json().get(\"access_token\")" + " message_dict: Dict[str, Any] = {\"content\": message.content}\n", + " if (name := message.name or message.additional_kwargs.get(\"name\")) is not None:\n", + " message_dict[\"name\"] = name\n", + "\n", + " # populate role and additional message data\n", + " if isinstance(message, ChatMessage):\n", + " message_dict[\"role\"] = message.role\n", + " elif isinstance(message, HumanMessage):\n", + " message_dict[\"role\"] = \"user\"\n", + " elif isinstance(message, AIMessage):\n", + " message_dict[\"role\"] = \"assistant\"\n", + " elif isinstance(message, SystemMessage):\n", + " message_dict[\"role\"] = \"system\"\n", + " else:\n", + " raise TypeError(f\"Got unknown type {message}\")\n", + " return message_dict" ] }, { @@ -79,100 +96,155 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# 继承自 langchain.llms.base.LLM\n", - "class Wenxin_LLM(LLM):\n", - " # 原生接口地址\n", - " url = \"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant\"\n", - " # 默认选用 ERNIE-Bot-turbo 模型,即目前一般所说的百度文心大模型\n", - " model_name: str = Field(default=\"ERNIE-Bot-turbo\", alias=\"model\")\n", - " # 访问时延上限\n", - " request_timeout: Optional[Union[float, Tuple[float, float]]] = None\n", - " # 温度系数\n", - " temperature: float = 0.1\n", - " # API_Key\n", - " api_key: str = None\n", - " # Secret_Key\n", - " secret_key : str = None\n", - " # access_token\n", - " access_token: str = None\n", - " # 必备的可选参数\n", - " model_kwargs: Dict[str, Any] = Field(default_factory=dict)\n" + "# 继承自 LangChain 的 BaseChatModel 类\n", + "class ZhipuaiLLM(BaseChatModel):\n", + " \"\"\"自定义Zhipuai聊天模型。\n", + " \"\"\"\n", + " model_name: str = None\n", + " temperature: Optional[float] = None\n", + " max_tokens: Optional[int] = None\n", + " timeout: Optional[int] = None\n", + " stop: Optional[List[str]] = None\n", + " max_retries: int = 3\n", + " api_key: str | None = None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "上述初始化涵盖了我们平时常用的参数,也可以根据实际需求与文心的 API 加入更多的参数。\n", + "上述初始化涵盖了我们平时常用的参数,也可以根据实际需求与智谱的 API 加入更多的参数。\n", "\n", - "接下来我们实现一个初始化方法 init_access_token,当模型的 access_token 为空时调用:" + "接下来我们实现_generate方法:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "def init_access_token(self):\n", - " if self.api_key != None and self.secret_key != None:\n", - " # 两个 Key 均非空才可以获取 access_token\n", - " try:\n", - " self.access_token = get_access_token(self.api_key, self.secret_key)\n", - " except Exception as e:\n", - " print(e)\n", - " print(\"获取 access_token 失败,请检查 Key\")\n", - " else:\n", - " print(\"API_Key 或 Secret_Key 为空,请检查 Key\")" + " def _generate(\n", + " self,\n", + " messages: List[BaseMessage],\n", + " stop: Optional[List[str]] = None,\n", + " run_manager: Optional[CallbackManagerForLLMRun] = None,\n", + " **kwargs: Any,\n", + " ) -> ChatResult:\n", + " \"\"\"通过调用智谱API从而响应输入。\n", + "\n", + " Args:\n", + " messages: 由messages列表组成的prompt\n", + " stop: 在模型生成的回答中有该字符串列表中的元素则停止响应\n", + " run_manager: 一个为LLM提供回调的运行管理器\n", + " \"\"\"\n", + " # 列表推导式 将 messages 的元素逐个转为智谱的格式\n", + " messages = [_convert_message_to_dict(message) for message in messages]\n", + " # 定义推理的开始时间\n", + " start_time = time.time()\n", + " # 调用 ZhipuAI 对处理消息\n", + " response = ZhipuAI(api_key=self.api_key).chat.completions.create(\n", + " model=self.model_name,\n", + " temperature=self.temperature,\n", + " max_tokens=self.max_tokens,\n", + " timeout=self.timeout,\n", + " stop=stop,\n", + " messages=messages\n", + " )\n", + " # 计算运行时间 由现在时间 time.time() 减去 开始时间start_time得到\n", + " time_in_seconds = time.time() - start_time\n", + " # 将返回的消息封装并返回\n", + " message = AIMessage(\n", + " content=response.choices[0].message.content, # 响应的结果\n", + " additional_kwargs={}, # 额外信息\n", + " response_metadata={\n", + " \"time_in_seconds\": round(time_in_seconds, 3), # 响应源数据 这里是运行时间 也可以添加其他信息\n", + " },\n", + " # 本次推理消耗的token\n", + " usage_metadata={\n", + " \"input_tokens\": response.usage.prompt_tokens, # 输入token\n", + " \"output_tokens\": response.usage.completion_tokens, # 输出token\n", + " \"total_tokens\": response.usage.total_tokens, # 全部token\n", + " },\n", + " )\n", + " generation = ChatGeneration(message=message)\n", + " return ChatResult(generations=[generation])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "接下来我们实现核心的方法——调用模型 API:" + "接下来我们实现另一核心的方法_stream,之前注释的代码本次不再注释:" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "def _call(self, prompt : str, stop: Optional[List[str]] = None,\n", - " run_manager: Optional[CallbackManagerForLLMRun] = None,\n", - " **kwargs: Any):\n", - " # 除 prompt 参数外,其他参数并没有被用到,但当我们通过 LangChain 调用时会传入这些参数,因此必须设置\n", - " # 如果 access_token 为空,初始化 access_token\n", - " if self.access_token == None:\n", - " self.init_access_token()\n", - " # API 调用 url\n", - " url = \"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token={}\".format(self.access_token)\n", - " # 配置 POST 参数\n", - " payload = json.dumps({\n", - " \"messages\": [\n", - " {\n", - " \"role\": \"user\",# user prompt\n", - " \"content\": \"{}\".format(prompt)# 输入的 prompt\n", - " }\n", - " ],\n", - " 'temperature' : self.temperature\n", - " })\n", - " headers = {\n", - " 'Content-Type': 'application/json'\n", - " }\n", - " # 发起请求\n", - " response = requests.request(\"POST\", url, headers=headers, data=payload, timeout=self.request_timeout)\n", - " if response.status_code == 200:\n", - " # 返回的是一个 Json 字符串\n", - " js = json.loads(response.text)\n", - " return js[\"result\"]\n", - " else:\n", - " return \"请求失败\"" + " def _stream(\n", + " self,\n", + " messages: List[BaseMessage],\n", + " stop: Optional[List[str]] = None,\n", + " run_manager: Optional[CallbackManagerForLLMRun] = None,\n", + " **kwargs: Any,\n", + " ) -> Iterator[ChatGenerationChunk]:\n", + " \"\"\"通过调用智谱API返回流式输出。\n", + "\n", + " Args:\n", + " messages: 由messages列表组成的prompt\n", + " stop: 在模型生成的回答中有该字符串列表中的元素则停止响应\n", + " run_manager: 一个为LLM提供回调的运行管理器\n", + " \"\"\"\n", + " messages = [_convert_message_to_dict(message) for message in messages]\n", + " response = ZhipuAI().chat.completions.create(\n", + " model=self.model_name,\n", + " stream=True, # 将stream 设置为 True 返回的是迭代器,可以通过for循环取值\n", + " temperature=self.temperature,\n", + " max_tokens=self.max_tokens,\n", + " timeout=self.timeout,\n", + " stop=stop,\n", + " messages=messages\n", + " )\n", + " start_time = time.time()\n", + " # 使用for循环存取结果\n", + " for res in response:\n", + " if res.usage: # 如果 res.usage 存在则存储token使用情况\n", + " usage_metadata = UsageMetadata(\n", + " {\n", + " \"input_tokens\": res.usage.prompt_tokens,\n", + " \"output_tokens\": res.usage.completion_tokens,\n", + " \"total_tokens\": res.usage.total_tokens,\n", + " }\n", + " )\n", + " # 封装每次返回的chunk\n", + " chunk = ChatGenerationChunk(\n", + " message=AIMessageChunk(content=res.choices[0].delta.content)\n", + " )\n", + "\n", + " if run_manager:\n", + " # This is optional in newer versions of LangChain\n", + " # The on_llm_new_token will be called automatically\n", + " run_manager.on_llm_new_token(res.choices[0].delta.content, chunk=chunk)\n", + " # 使用yield返回 结果是一个生成器 同样可以使用for循环调用\n", + " yield chunk\n", + " time_in_sec = time.time() - start_time\n", + " # Let's add some other information (e.g., response metadata)\n", + " # 最终返回运行时间\n", + " chunk = ChatGenerationChunk(\n", + " message=AIMessageChunk(content=\"\", response_metadata={\"time_in_sec\": round(time_in_sec, 3)}, usage_metadata=usage_metadata)\n", + " )\n", + " if run_manager:\n", + " # This is optional in newer versions of LangChain\n", + " # The on_llm_new_token will be called automatically\n", + " run_manager.on_llm_new_token(\"\", chunk=chunk)\n", + " yield chunk" ] }, { @@ -184,32 +256,31 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# 首先定义一个返回默认参数的方法\n", - "@property\n", - "def _default_params(self) -> Dict[str, Any]:\n", - " \"\"\"获取调用Ennie API的默认参数。\"\"\"\n", - " normal_params = {\n", - " \"temperature\": self.temperature,\n", - " \"request_timeout\": self.request_timeout,\n", - " }\n", - " return {**normal_params}\n", + " @property\n", + " def _llm_type(self) -> str:\n", + " \"\"\"获取此聊天模型使用的语言模型类型。\"\"\"\n", + " return self.model_name\n", "\n", + " @property\n", + " def _identifying_params(self) -> Dict[str, Any]:\n", + " \"\"\"返回一个标识参数的字典。\n", "\n", - "@property\n", - "def _identifying_params(self) -> Mapping[str, Any]:\n", - " \"\"\"Get the identifying parameters.\"\"\"\n", - " return {**{\"model_name\": self.model_name}, **self._default_params}" + " 该信息由LangChain回调系统使用,用于跟踪目的,使监视llm成为可能。\n", + " \"\"\"\n", + " return {\n", + " \"model_name\": self.model_name,\n", + " }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "通过上述步骤,我们就可以基于 LangChain 定义百度文心的调用方式了。我们将此代码封装在 wenxin_llm.py 文件中,将在讲述如何调用百度文心的 Notebook 中直接使用该 LLM。" + "通过上述步骤,我们就可以基于 LangChain 定义智谱的调用方式了。我们将此代码封装在 zhipu_llm.py 文件中。" ] } ], diff --git "a/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/1.\345\246\202\344\275\225\350\257\204\344\274\260 LLM \345\272\224\347\224\250.ipynb" "b/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/1.\345\246\202\344\275\225\350\257\204\344\274\260 LLM \345\272\224\347\224\250.ipynb" index 059c4b1..1f4380c 100644 --- "a/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/1.\345\246\202\344\275\225\350\257\204\344\274\260 LLM \345\272\224\347\224\250.ipynb" +++ "b/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/1.\345\246\202\344\275\225\350\257\204\344\274\260 LLM \345\272\224\347\224\250.ipynb" @@ -65,9 +65,18 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/yd/c4q_f88j1l70g7_jcb6pdnb80000gn/T/ipykernel_25665/3013114604.py:23: LangChainDeprecationWarning: The class `Chroma` was deprecated in LangChain 0.2.9 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-chroma package and should be used instead. To use it run `pip install -U :class:`~langchain-chroma` and import as `from :class:`~langchain_chroma import Chroma``.\n", + " vectordb = Chroma(\n" + ] + } + ], "source": [ "import sys\n", "sys.path.append(\"../C3 搭建知识库\") # 将父目录放入系统路径中\n", @@ -75,7 +84,7 @@ "# 使用智谱 Embedding API,注意,需要将上一章实现的封装代码下载到本地\n", "from zhipuai_embedding import ZhipuAIEmbeddings\n", "\n", - "from langchain.vectorstores.chroma import Chroma\n", + "from langchain_community.vectorstores.chroma import Chroma\n", "from langchain_openai import ChatOpenAI\n", "from dotenv import load_dotenv, find_dotenv\n", "import os\n", @@ -95,9 +104,9 @@ " persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上\n", " embedding_function=embedding\n", ")\n", - "\n", - "# 使用 OpenAI GPT-3.5 模型\n", - "llm = ChatOpenAI(model_name = \"gpt-3.5-turbo\", temperature = 0)\n" + "retriever = vectordb.as_retriever()\n", + "# 使用 OpenAI GPT-4o 模型\n", + "llm = ChatOpenAI(model_name = \"gpt-4o\", temperature = 0)\n" ] }, { @@ -130,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -138,16 +147,16 @@ "output_type": "stream", "text": [ "问题一:\n", - "南瓜书是以西瓜书为前置知识进行解析和补充的,主要是对西瓜书中比较难理解的公式进行解析和推导细节的补充。南瓜书的最佳使用方法是以西瓜书为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书。谢谢你的提问!\n", + "南瓜书是以西瓜书为前置知识进行表述的,旨在对西瓜书中较难理解的公式进行解析和补充推导细节。南瓜书的最佳使用方法是以西瓜书为主线,遇到推导不出来或看不懂的公式时再查阅南瓜书。谢谢你的提问!\n", "问题二:\n", - "应该以西瓜书为主线,遇到推导不出来或看不懂的公式时再查阅南瓜书。不建议初学者深究第1章和第2章的公式,等学得更熟练再回来。如果需要查阅南瓜书中没有的公式或发现错误,可以在GitHub上反馈。谢谢你的提问!\n" + "使用南瓜书的最佳方法是以西瓜书为主线,当遇到推导不出或看不懂的公式时,再查阅南瓜书。对于初学者,建议先简单浏览西瓜书的第1章和第2章的公式,等有一定基础后再深入研究。若南瓜书中没有你需要的内容,可以通过GitHub的Issues反馈或联系编委会。谢谢你的提问!\n" ] } ], "source": [ - "from langchain.prompts import PromptTemplate\n", - "from langchain.chains import RetrievalQA\n", - "\n", + "from langchain_core.prompts import PromptTemplate\n", + "from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda\n", + "from langchain_core.output_parsers import StrOutputParser\n", "\n", "template_v1 = \"\"\"使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答\n", "案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。\n", @@ -155,25 +164,27 @@ "问题: {question}\n", "\"\"\"\n", "\n", - "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", - " template=template_v1)\n", - "\n", - "\n", - "\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", - "\n", + "QA_CHAIN_PROMPT = PromptTemplate(template=template_v1)\n", + "\n", + "def combine_docs(docs):\n", + " return \"\\n\\n\".join(doc.page_content for doc in docs)\n", + "retrievel_chain = retriever | RunnableLambda(combine_docs)\n", + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " |{\n", + " \"answer\": QA_CHAIN_PROMPT | llm | StrOutputParser(),\n", + " \"context\": lambda x: x[\"context\"]\n", + " }\n", + ")\n", "print(\"问题一:\")\n", "question = \"南瓜书和西瓜书有什么关系?\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])\n", + "result = qa_chain.invoke(question)\n", + "print(result[\"answer\"])\n", "\n", "print(\"问题二:\")\n", "question = \"应该如何使用南瓜书?\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result[\"answer\"])" ] }, { @@ -185,7 +196,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -193,9 +204,29 @@ "output_type": "stream", "text": [ "问题一:\n", - "南瓜书和西瓜书之间的关系是南瓜书是以西瓜书的内容为前置知识进行表述的。南瓜书的目的是对西瓜书中比较难理解的公式进行解析,并补充具体的推导细节,以帮助读者更好地理解和学习机器学习领域的知识。因此,最佳使用方法是以西瓜书为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书。南瓜书的内容主要是为了帮助那些想深究公式推导细节的读者,提供更详细的解释和补充。\n", + "南瓜书和西瓜书之间的关系是,南瓜书是以西瓜书为基础进行扩展和补充的学习资料。具体来说:\n", + "\n", + "1. **西瓜书**:由周志华老师编写,是机器学习领域的经典入门教材。书中对部分公式的推导细节没有详述,假设读者具有一定的数学基础,能够自行推导或通过练习掌握这些细节。\n", + "\n", + "2. **南瓜书**:旨在帮助那些在自学过程中对西瓜书中的公式推导细节感到困难的读者。南瓜书对西瓜书中比较难理解的公式进行解析,并补充具体的推导细节。它是以西瓜书的内容为前置知识进行表述的,建议读者在学习西瓜书时遇到困难再查阅南瓜书。\n", + "\n", + "因此,南瓜书可以被视为西瓜书的辅助学习工具,特别适合那些需要更详细推导过程的读者。\n", "问题二:\n", - "应该将南瓜书作为西瓜书的补充,主要在遇到自己无法推导或理解的公式时进行查阅。对于初学机器学习的小白来说,建议先简单过一下南瓜书的第1章和第2章的公式,等学得更深入后再回来深究。每个公式的解析和推导都以本科数学基础的视角进行讲解,超纲的数学知识会在附录和参考文献中给出,供感兴趣的同学继续深入学习。如果在南瓜书中找不到想要查阅的公式,或者发现错误,可以在GitHub的Issues中提交反馈,通常会在24小时内得到回复。此外,南瓜书还提供配套视频教程和在线阅读地址,以及最新版PDF获取地址。最后,南瓜书的内容是基于知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可。\n" + "使用南瓜书的最佳方法是将其作为西瓜书的补充材料,特别是在遇到推导不出来或者看不懂的公式时。以下是一些具体的使用建议:\n", + "\n", + "1. **结合西瓜书使用**:南瓜书的内容是基于西瓜书的前置知识进行表述的。因此,建议以西瓜书为主线进行学习,当遇到难以理解的公式时,再查阅南瓜书以获取更详细的推导和解释。\n", + "\n", + "2. **初学者建议**:对于刚开始学习机器学习的初学者,建议不要过于深究西瓜书第1章和第2章的公式。可以先简单了解,等到对机器学习有了一定的基础和理解后,再回过头来深入研究这些公式。\n", + "\n", + "3. **数学基础视角**:南瓜书力求以本科数学基础的视角进行公式的解析和推导。如果涉及到超出本科数学基础的知识,南瓜书通常会在附录和参考文献中提供相关资料,供有兴趣的读者进一步学习。\n", + "\n", + "4. **反馈和互动**:如果南瓜书中没有你需要的公式,或者发现了错误,可以通过GitHub的Issues页面进行反馈。编委会通常会在24小时内回复。此外,还可以通过微信联系编委会。\n", + "\n", + "5. **配套资源**:南瓜书还提供了配套的视频教程和在线阅读地址,可以通过这些资源更全面地理解书中的内容。\n", + "\n", + "6. **加入读者交流群**:通过扫描二维码并回复关键词“南瓜书”,可以加入读者交流群,与其他读者交流学习经验。\n", + "\n", + "通过以上方法,读者可以更有效地利用南瓜书来辅助学习机器学习中的复杂公式和概念。\n" ] } ], @@ -206,23 +237,25 @@ "问题: {question}\n", "有用的回答:\"\"\"\n", "\n", - "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", - " template=template_v2)\n", + "QA_CHAIN_PROMPT = PromptTemplate.from_template(template_v2)\n", "\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " | {\n", + " \"answer\": QA_CHAIN_PROMPT | llm | StrOutputParser(),\n", + " \"context\": lambda x: x[\"context\"]\n", + " }\n", + ")\n", "\n", "print(\"问题一:\")\n", "question = \"南瓜书和西瓜书有什么关系?\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])\n", + "result = qa_chain.invoke(question)\n", + "print(result[\"answer\"])\n", "\n", "print(\"问题二:\")\n", "question = \"应该如何使用南瓜书?\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result[\"answer\"])" ] }, { @@ -267,7 +300,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -277,7 +310,21 @@ "问题:\n", "应该如何使用南瓜书?\n", "模型回答:\n", - "应该将南瓜书作为西瓜书的补充,主要在遇到自己无法推导或理解的公式时进行查阅。对于初学机器学习的小白来说,建议先简单过一下南瓜书的第1章和第2章,等学得更深入后再回来深究。每个公式的解析和推导都以本科数学基础的视角进行讲解,超纲的数学知识会在附录和参考文献中给出,感兴趣的同学可以继续深入学习。如果南瓜书中没有你想要查阅的公式,或者发现有错误,可以在GitHub的Issues中提交反馈,通常会在24小时内得到回复。最终目的是帮助读者更好地理解和应用机器学习知识,成为合格的理工科学生。\n" + "使用南瓜书的最佳方法是将其作为西瓜书的补充资料,特别是在遇到推导不出来或者看不懂的公式时,可以查阅南瓜书以获得更详细的解析和推导细节。以下是一些具体的使用建议:\n", + "\n", + "1. **结合西瓜书使用**:南瓜书的内容是基于西瓜书的前置知识进行表述的,因此建议以西瓜书为主线进行学习。当遇到困难时,再查阅南瓜书以获得更深入的理解。\n", + "\n", + "2. **初学者建议**:对于初学者,特别是刚接触机器学习的小白,建议不要过于深究西瓜书第1章和第2章的公式。可以先简单了解,等到对机器学习有更深入的理解后再回头研究这些公式。\n", + "\n", + "3. **数学基础视角**:南瓜书力求以本科数学基础的视角进行讲解,因此对于超出本科数学范围的知识,通常会以附录和参考文献的形式提供,感兴趣的读者可以根据这些资料进行深入学习。\n", + "\n", + "4. **反馈和互动**:如果南瓜书中没有你需要的公式,或者发现了错误,可以通过GitHub的Issues页面进行反馈。编委会通常会在24小时内回复。\n", + "\n", + "5. **配套资源**:可以利用南瓜书提供的配套视频教程和在线阅读地址进行学习。此外,还可以通过扫描二维码加入“南瓜书读者交流群”以获得更多交流和支持。\n", + "\n", + "6. **获取最新版本**:可以通过GitHub获取最新版的PDF,以确保学习到最新的内容和修订。\n", + "\n", + "通过这些方法,南瓜书可以帮助你更好地理解和掌握机器学习中的复杂公式和概念。\n" ] } ], @@ -286,8 +333,8 @@ "question = \"应该如何使用南瓜书?\"\n", "print(question)\n", "print(\"模型回答:\")\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result[\"answer\"])" ] }, { @@ -299,19 +346,76 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[Document(page_content='为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;\\n• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\\n有点飘的时候再回来啃都来得及;\\n• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\\n我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;\\n• 若南瓜书里没有你想要查阅的公式,\\n或者你发现南瓜书哪个地方有错误,\\n请毫不犹豫地去我们GitHub 的\\nIssues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\\n提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\\n话可以微信联系我们(微信号:at-Sm1les)\\n;\\n配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU', metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}), Document(page_content='在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1 版)\\n最新版PDF 获取地址:https://github.com/datawhalechina/pumpkin-book/releases\\n编委会\\n主编:Sm1les、archwalker、jbb0523\\n编委:juxiao、Majingmin、MrBigFan、shanry、Ye980226\\n封面设计:构思-Sm1les、创作-林王茂盛\\n致谢\\n特别感谢awyd234、\\nfeijuan、\\nGgmatch、\\nHeitao5200、\\nhuaqing89、\\nLongJH、\\nLilRachel、\\nLeoLRH、\\nNono17、\\nspareribs、sunchaothu、StevenLzq 在最早期的时候对南瓜书所做的贡献。\\n扫描下方二维码,然后回复关键词“南瓜书”\\n,即可加入“南瓜书读者交流群”\\n版权声明\\n本作品采用知识共享署名-非商业性使用-相同方式共享4.0 国际许可协议进行许可。', metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}), Document(page_content='\\x01本\\x03:1.9.9\\n发布日期:2023.03\\n南 ⽠ 书\\nPUMPKIN\\nB O O K\\nDatawhale', metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 0, 'producer': 'xdvipdfmx (20200315)', 'source': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''}), Document(page_content='前言\\n“周志华老师的《机器学习》\\n(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\\n者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\\n导细节的读者来说可能“不太友好”\\n,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\\n具体的推导细节。\\n”\\n读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\\n老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\\n中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”\\n。所以...... 本南瓜书只能算是我\\n等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\\n下学生”\\n。\\n使用说明\\n• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\\n为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;', metadata={'author': '', 'creationDate': \"D:20230303170709-00'00'\", 'creator': 'LaTeX with hyperref', 'file_path': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'format': 'PDF 1.5', 'keywords': '', 'modDate': '', 'page': 1, 'producer': 'xdvipdfmx (20200315)', 'source': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'subject': '', 'title': '', 'total_pages': 196, 'trapped': ''})]\n" + "前言\n", + "“周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读\n", + "者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推\n", + "导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充\n", + "具体的推导细节。”\n", + "读到这里,大家可能会疑问为啥前面这段话加了引号,因为这只是我们最初的遐想,后来我们了解到,周\n", + "老师之所以省去这些推导细节的真实原因是,他本尊认为“理工科数学基础扎实点的大二下学生应该对西瓜书\n", + "中的推导细节无困难吧,要点在书里都有了,略去的细节应能脑补或做练习”。所以...... 本南瓜书只能算是我\n", + "等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二\n", + "下学生”。\n", + "使用说明\n", + "• 南瓜书的所有内容都是以西瓜书的内容为前置知识进行表述的,所以南瓜书的最佳使用方法是以西瓜书\n", + "为主线,遇到自己推导不出来或者看不懂的公式时再来查阅南瓜书;\n", + "• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\n", + "\n", + "• 对于初学机器学习的小白,西瓜书第1 章和第2 章的公式强烈不建议深究,简单过一下即可,等你学得\n", + "有点飘的时候再回来啃都来得及;\n", + "• 每个公式的解析和推导我们都力(zhi) 争(neng) 以本科数学基础的视角进行讲解,所以超纲的数学知识\n", + "我们通常都会以附录和参考文献的形式给出,感兴趣的同学可以继续沿着我们给的资料进行深入学习;\n", + "• 若南瓜书里没有你想要查阅的公式,或者你发现南瓜书哪个地方有错误,请毫不犹豫地去我们GitHub 的\n", + "Issues(地址:https://github.com/datawhalechina/pumpkin-book/issues)进行反馈,在对应版块\n", + "提交你希望补充的公式编号或者勘误信息,我们通常会在24 小时以内给您回复,超过24 小时未回复的\n", + "话可以微信联系我们(微信号:at-Sm1les);\n", + "配套视频教程:https://www.bilibili.com/video/BV1Mh411e7VU\n", + "在线阅读地址:https://datawhalechina.github.io/pumpkin-book(仅供第1 版)\n", + "\n", + "最新版PDF 获取地址:https://github.com/datawhalechina/pumpkin-book/releases\n", + "编委会\n", + "主编:Sm1les、archwalker、jbb0523\n", + "编委:juxiao、Majingmin、MrBigFan、shanry、Ye980226\n", + "封面设计:构思-Sm1les、创作-林王茂盛\n", + "致谢\n", + "特别感谢awyd234、feijuan、Ggmatch、Heitao5200、huaqing89、LongJH、LilRachel、LeoLRH、Nono17、\n", + "spareribs、sunchaothu、StevenLzq 在最早期的时候对南瓜书所做的贡献。\n", + "扫描下方二维码,然后回复关键词“南瓜书”,即可加入“南瓜书读者交流群”\n", + "版权声明\n", + "本作品采用知识共享署名-非商业性使用-相同方式共享4.0 国际许可协议进行许可。\n", + "\n", + "→_→\n", + "欢迎去各大电商平台选购纸质版南瓜书《机器学习公式详解》\n", + "←_←\n", + "9.2.4\n", + "式(9.8) 的解释. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n", + "98\n", + "9.2.5\n", + "式(9.12) 的解释\n", + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n", + "98\n", + "9.3\n", + "距离计算\n", + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n", + "98\n", + "9.3.1\n", + "式(9.21) 的解释\n", + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n", + "99\n", + "9.4\n", + "原型聚类\n" ] } ], "source": [ - "print(result[\"source_documents\"])" + "print(result[\"context\"])" ] }, { @@ -362,7 +466,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -605,20 +709,66 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Building prefix dict from the default dictionary ...\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ "答案一:\n", - "周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充具体的推导细节。\n", + "周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充具体的推导细节。\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Dumping model to file cache /var/folders/yd/c4q_f88j1l70g7_jcb6pdnb80000gn/T/jieba.cache\n", + "Loading model cost 0.308 seconds.\n", + "Prefix dict has been built successfully.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "得分: 1.2705543769116016e-231\n", "答案二:\n", "本南瓜书只能算是我等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二下学生”\n", "得分: 1.1935398790363042e-231\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/lta/anaconda3/envs/universe_0_3/lib/python3.10/site-packages/nltk/translate/bleu_score.py:577: UserWarning: \n", + "The hypothesis contains 0 counts of 2-gram overlaps.\n", + "Therefore the BLEU score evaluates to 0, independently of\n", + "how many N-gram overlaps of lower order it contains.\n", + "Consider using lower n-gram order or use SmoothingFunction()\n", + " warnings.warn(_msg)\n", + "/Users/lta/anaconda3/envs/universe_0_3/lib/python3.10/site-packages/nltk/translate/bleu_score.py:577: UserWarning: \n", + "The hypothesis contains 0 counts of 3-gram overlaps.\n", + "Therefore the BLEU score evaluates to 0, independently of\n", + "how many N-gram overlaps of lower order it contains.\n", + "Consider using lower n-gram order or use SmoothingFunction()\n", + " warnings.warn(_msg)\n", + "/Users/lta/anaconda3/envs/universe_0_3/lib/python3.10/site-packages/nltk/translate/bleu_score.py:577: UserWarning: \n", + "The hypothesis contains 0 counts of 4-gram overlaps.\n", + "Therefore the BLEU score evaluates to 0, independently of\n", + "how many N-gram overlaps of lower order it contains.\n", + "Consider using lower n-gram order or use SmoothingFunction()\n", + " warnings.warn(_msg)\n" + ] } ], "source": [ @@ -667,7 +817,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -717,16 +867,16 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'{\\n \"知识查找正确性\": 1,\\n \"回答一致性\": 0.9,\\n \"回答幻觉比例\": 0.9,\\n \"回答正确性\": 0.9,\\n \"逻辑性\": 0.9,\\n \"通顺性\": 0.9,\\n \"智能性\": 0.8\\n}'" + "'```python\\n{\\n \"知识查找正确性\": 1,\\n \"回答一致性\": 1,\\n \"回答幻觉比例\": 0.9,\\n \"回答正确性\": 0.9,\\n \"逻辑性\": 1,\\n \"通顺性\": 1,\\n \"智能性\": 0.9\\n}\\n```'" ] }, - "execution_count": 20, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -736,10 +886,7 @@ "\n", "from openai import OpenAI\n", "\n", - "client = OpenAI(\n", - " # This is the default and can be omitted\n", - " api_key=os.environ.get(\"OPENAI_API_KEY\"),\n", - ")\n", + "client = OpenAI()\n", "\n", "\n", "def gen_gpt_messages(prompt):\n", @@ -753,13 +900,13 @@ " return messages\n", "\n", "\n", - "def get_completion(prompt, model=\"gpt-3.5-turbo\", temperature = 0):\n", + "def get_completion(prompt, model=\"gpt-4o\", temperature = 0):\n", " '''\n", " 获取 GPT 模型调用结果\n", "\n", " 请求参数:\n", " prompt: 对应的提示词\n", - " model: 调用的模型,默认为 gpt-3.5-turbo,也可以按需选择 gpt-4 等其他模型\n", + " model: 调用的模型,默认为 gpt-4o,也可以按需选择 gpt-4 等其他模型\n", " temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~2。温度系数越低,输出内容越一致。\n", " '''\n", " response = client.chat.completions.create(\n", @@ -772,9 +919,9 @@ " return \"generate answer error\"\n", "\n", "question = \"应该如何使用南瓜书?\"\n", - "result = qa_chain({\"query\": question})\n", - "answer = result[\"result\"]\n", - "knowledge = result[\"source_documents\"]\n", + "result = qa_chain.invoke(question)\n", + "answer = result[\"answer\"]\n", + "knowledge = result[\"context\"]\n", "\n", "response = get_completion(prompt.format(question, answer, knowledge))\n", "response" @@ -830,7 +977,7 @@ ], "metadata": { "kernelspec": { - "display_name": "llm_universe", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -844,7 +991,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/2.\350\257\204\344\274\260\345\271\266\344\274\230\345\214\226\347\224\237\346\210\220\351\203\250\345\210\206.ipynb" "b/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/2.\350\257\204\344\274\260\345\271\266\344\274\230\345\214\226\347\224\237\346\210\220\351\203\250\345\210\206.ipynb" index aefe0f4..8ef5091 100644 --- "a/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/2.\350\257\204\344\274\260\345\271\266\344\274\230\345\214\226\347\224\237\346\210\220\351\203\250\345\210\206.ipynb" +++ "b/notebook/C5 \347\263\273\347\273\237\350\257\204\344\274\260\344\270\216\344\274\230\345\214\226/2.\350\257\204\344\274\260\345\271\266\344\274\230\345\214\226\347\224\237\346\210\220\351\203\250\345\210\206.ipynb" @@ -22,9 +22,18 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/yd/c4q_f88j1l70g7_jcb6pdnb80000gn/T/ipykernel_26332/2254682865.py:23: LangChainDeprecationWarning: The class `Chroma` was deprecated in LangChain 0.2.9 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-chroma package and should be used instead. To use it run `pip install -U :class:`~langchain-chroma` and import as `from :class:`~langchain_chroma import Chroma``.\n", + " vectordb = Chroma(\n" + ] + } + ], "source": [ "import sys\n", "sys.path.append(\"../C3 搭建知识库\") # 将父目录放入系统路径中\n", @@ -32,7 +41,7 @@ "# 使用智谱 Embedding API,注意,需要将上一章实现的封装代码下载到本地\n", "from zhipuai_embedding import ZhipuAIEmbeddings\n", "\n", - "from langchain.vectorstores.chroma import Chroma\n", + "from langchain_community.vectorstores.chroma import Chroma\n", "from langchain_openai import ChatOpenAI\n", "from dotenv import load_dotenv, find_dotenv\n", "import os\n", @@ -53,11 +62,11 @@ " embedding_function=embedding\n", ")\n", "\n", - "# 使用 OpenAI GPT-3.5 模型\n", - "llm = ChatOpenAI(model_name = \"gpt-3.5-turbo\", temperature = 0)\n", + "# 使用 OpenAI GPT-4o 模型\n", + "llm = ChatOpenAI(model_name = \"gpt-4o\", temperature = 0)\n", "\n", - "os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'\n", - "os.environ[\"HTTP_PROXY\"] = 'http://127.0.0.1:7890'\n" + "# os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'\n", + "# os.environ[\"HTTP_PROXY\"] = 'http://127.0.0.1:7890'\n" ] }, { @@ -69,29 +78,32 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from langchain.prompts import PromptTemplate\n", - "from langchain.chains import RetrievalQA\n", - "\n", - "\n", + "from langchain_core.runnables import RunnableParallel, RunnableLambda, RunnablePassthrough\n", + "from langchain_core.output_parsers import StrOutputParser\n", "template_v1 = \"\"\"使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答\n", "案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。\n", "{context}\n", "问题: {question}\n", "\"\"\"\n", "\n", - "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", - " template=template_v1)\n", + "QA_CHAIN_PROMPT = PromptTemplate.from_template(template_v1)\n", "\n", + "def combine_docs(docs):\n", + " return \"\\n\\n\".join(doc.page_content for doc in docs)\n", "\n", + "retrievel_chain = vectordb.as_retriever() | RunnableLambda(combine_docs)\n", "\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})" + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " | QA_CHAIN_PROMPT\n", + " | llm\n", + " | StrOutputParser()\n", + ")" ] }, { @@ -103,21 +115,21 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "南瓜书是对《机器学习》(西瓜书)中比较难理解的公式进行解析和补充推导细节的书籍。南瓜书的最佳使用方法是以西瓜书为主线,遇到推导困难或看不懂的公式时再来查阅南瓜书。谢谢你的提问!\n" + "南瓜书是一本对《机器学习》(西瓜书)中较难理解的公式进行解析和推导细节补充的书籍,旨在帮助读者更好地理解机器学习中的数学公式。它以西瓜书为前置知识,适合在遇到推导困难时查阅。南瓜书的内容以本科数学基础为视角进行讲解,并提供配套视频教程和在线阅读资源。谢谢你的提问!\n" ] } ], "source": [ "question = \"什么是南瓜书\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -148,7 +160,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "南瓜书是一本针对周志华老师的《机器学习》(西瓜书)的补充解析书籍。它旨在对西瓜书中比较难理解的公式进行解析,并补充具体的推导细节,以帮助读者更好地理解机器学习领域的知识。南瓜书的内容是以西瓜书为前置知识进行表述的,最佳使用方法是在遇到自己推导不出来或者看不懂的公式时来查阅。南瓜书的编写团队致力于帮助读者成为合格的“理工科数学基础扎实点的大二下学生”,并提供了在线阅读地址和最新版PDF获取地址供读者使用。\n" + "南瓜书是一本旨在帮助读者深入理解《机器学习》(西瓜书)中公式推导细节的辅助教材。它特别适合那些在学习西瓜书时遇到推导困难的读者。南瓜书的内容是基于西瓜书的前置知识进行表述的,因此建议读者在学习西瓜书时,遇到不理解的公式再查阅南瓜书。南瓜书的目标是帮助读者成为“理工科数学基础扎实点的大二下学生”,即具备扎实数学基础的学习者。\n", + "\n", + "南瓜书的编写者们力求以本科数学基础的视角进行公式的解析和推导,并提供附录和参考文献以供读者深入学习。南瓜书还提供了在线阅读、视频教程和读者交流群等资源,以便读者更好地学习和交流。\n", + "\n", + "如果读者在南瓜书中找不到需要的公式,或者发现错误,可以通过GitHub的Issues页面进行反馈,编委会通常会在24小时内回复。南瓜书的内容也在不断更新和完善,以更好地服务读者的学习需求。\n" ] } ], @@ -159,16 +175,17 @@ "问题: {question}\n", "有用的回答:\"\"\"\n", "\n", - "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", - " template=template_v2)\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", + "QA_CHAIN_PROMPT = PromptTemplate.from_template(template_v2)\n", + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " | QA_CHAIN_PROMPT\n", + " | llm\n", + " | StrOutputParser()\n", + ")\n", "\n", "question = \"什么是南瓜书\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -187,18 +204,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "在使用大型语言模型时,构造Prompt的原则主要包括编写清晰、具体的指令和给予模型充足的思考时间。首先,Prompt需要清晰明确地表达需求,提供足够的上下文信息,以确保语言模型准确理解用户的意图。这就好比向一个对人类世界一无所知的外星人解释事物一样,需要详细而清晰的描述。过于简略的Prompt会导致模型难以准确把握任务要求。\n", + "使用大语言模型(LLM)时,构造 Prompt 的原则主要包括以下两个关键点:\n", + "\n", + "1. **编写清晰、具体的指令**:\n", + " - 确保 Prompt 明确表达需求,提供足够的上下文信息,以便模型准确理解任务意图。就像向一个外星人解释人类世界一样,过于简略的 Prompt 可能导致模型无法把握任务的具体要求。\n", + " - 设计 Prompt 时,应避免模糊和不确定的语言,尽量使用具体的描述和明确的指令,以提高模型生成结果的准确性。\n", "\n", - "其次,给予语言模型充足的推理时间也是至关重要的。类似于人类解决问题时需要思考的时间,模型也需要时间来推理和生成准确的结果。匆忙的结论往往会导致错误的输出。因此,在设计Prompt时,应该加入逐步推理的要求,让模型有足够的时间进行逻辑思考,从而提高结果的准确性和可靠性。\n", + "2. **给予模型充足思考时间**:\n", + " - 让模型有足够的时间进行推理和思考,类似于人类在解题时需要时间思考以避免匆忙得出错误结论。\n", + " - 在 Prompt 中加入逐步推理的要求,确保模型有时间进行深度思考,从而生成更准确和可靠的结果。\n", "\n", - "通过遵循这两个原则,设计优化的Prompt可以帮助语言模型充分发挥潜力,完成复杂的推理和生成任务。掌握这些Prompt设计原则是开发者成功应用语言模型的重要一步。在实际应用中,不断优化和调整Prompt,逐步逼近最佳形式,是构建高效、可靠模型交互的关键策略。\n" + "通过优化这两方面,开发者可以充分发挥语言模型的潜力,完成复杂的推理和生成任务。此外,Prompt 的设计是一个迭代优化的过程,开发者需要通过多次尝试和调整,逐步找到最适合应用的 Prompt 形式。\n" ] } ], "source": [ "question = \"使用大模型时,构造 Prompt 的原则有哪些\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -223,17 +246,26 @@ "name": "stdout", "output_type": "stream", "text": [ - "1. 编写清晰、具体的指令是构造 Prompt 的第一原则。Prompt需要明确表达需求,提供充足上下文,使语言模型准确理解意图。过于简略的Prompt会使模型难以完成任务。\n", + "使用大模型时,构造 Prompt 的原则主要包括以下几点:\n", "\n", - "2. 给予模型充足思考时间是构造Prompt的第二原则。语言模型需要时间推理和解决复杂问题,匆忙得出的结论可能不准确。因此,Prompt应该包含逐步推理的要求,让模型有足够时间思考,生成更准确的结果。\n", + "1. **编写清晰、具体的指令**:\n", + " - 确保 Prompt 明确表达需求,提供足够的上下文信息,使语言模型能够准确理解任务意图。\n", + " - 避免过于简略的描述,以免模型难以把握具体任务要求。\n", "\n", - "3. 在设计Prompt时,要指定完成任务所需的步骤。通过给定一个复杂任务,给出完成任务的一系列步骤,可以帮助模型更好地理解任务要求,提高任务完成的效率。\n", + "2. **给予模型充足思考时间**:\n", + " - 设计 Prompt 时,应加入逐步推理的要求,类似于人类解题过程,避免匆忙得出结论。\n", + " - 通过让模型有足够的时间进行推理,生成的结果会更加准确和可靠。\n", "\n", - "4. 迭代优化是构造Prompt的常用策略。通过不断尝试、分析结果、改进Prompt的过程,逐步逼近最优的Prompt形式。成功的Prompt通常是通过多轮调整得出的。\n", + "3. **迭代优化**:\n", + " - 通过多次迭代优化 Prompt,逐步改进以达到最佳效果。\n", + " - 初版 Prompt 运行后,检查结果并分析不理想的原因,进行调整和改进。\n", + " - 对于复杂应用,可以在多个样本上进行迭代训练,评估 Prompt 的平均表现。\n", "\n", - "5. 添加表格描述是优化Prompt的一种方法。要求模型抽取信息并组织成表格,指定表格的列、表名和格式,可以帮助模型更好地理解任务,并生成符合预期的结果。\n", + "4. **实践与评估**:\n", + " - 在 Jupyter Notebook 等工具上实践 Prompt 示例,观察不同输出,深入理解迭代优化过程。\n", + " - 在应用较为成熟后,采用在多个样本集上评估 Prompt 性能的方式进行细致优化。\n", "\n", - "总之,构造Prompt的原则包括清晰具体的指令、给予模型充足思考时间、指定完成任务所需的步骤、迭代优化和添加表格描述等。这些原则可以帮助开发者设计出高效、可靠的Prompt,发挥语言模型的最大潜力。\n" + "通过掌握这些原则,开发者可以更有效地利用大语言模型,构建出可靠的应用程序。\n" ] } ], @@ -247,14 +279,16 @@ "\n", "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", " template=template_v3)\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " | QA_CHAIN_PROMPT\n", + " | llm\n", + " | StrOutputParser()\n", + ")\n", "\n", "question = \"使用大模型时,构造 Prompt 的原则有哪些\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -287,14 +321,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "强化学习是一种机器学习方法,旨在让智能体通过与环境的交互学习如何做出一系列好的决策。在强化学习中,智能体会根据环境的状态选择一个动作,然后根据环境的反馈(奖励)来调整其策略,以最大化长期奖励。强化学习的目标是在不确定的情况下做出最优的决策,类似于让一个小孩通过不断尝试来学会走路的过程。强化学习的应用范围广泛,包括游戏玩法、机器人控制、交通优化等领域。在强化学习中,智能体和环境之间不断交互,智能体根据环境的反馈来调整其策略,以获得最大的奖励。\n" + "我不知道强化学习的定义,因为提供的上下文中没有涉及到强化学习的相关内容。\n" ] } ], "source": [ "question = \"强化学习的定义是什么\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -313,7 +347,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "强化学习是一种机器学习方法,旨在让智能体通过与环境的交互学习如何做出一系列好的决策。在这个过程中,智能体会根据环境的反馈(奖励)来调整自己的行为,以最大化长期奖励的总和。强化学习的目标是在不确定的情况下做出最优的决策,类似于让一个小孩通过不断尝试来学会走路的过程。强化学习的交互过程由智能体和环境两部分组成,智能体根据环境的状态选择动作,环境根据智能体的动作输出下一个状态和奖励。强化学习的应用非常广泛,包括游戏玩法、机器人控制、交通管理等领域。【来源:蘑菇书一语二语二强化学习教程】。\n" + "根据提供的上下文,强化学习的定义并未被直接提及,因此我无法从中得出强化学习的定义。不过,我可以提供一个通用的强化学习定义:\n", + "\n", + "强化学习(Reinforcement Learning, RL)是一种机器学习方法,旨在通过与环境的交互来学习如何采取行动,以最大化累积的奖励。强化学习的核心在于智能体(agent)在不同状态下选择动作(action),并根据环境反馈的奖励(reward)来调整策略(policy),以便在长期内获得最大的总奖励。强化学习通常涉及以下几个关键元素:\n", + "\n", + "1. **智能体(Agent)**:在环境中执行动作的实体。\n", + "2. **环境(Environment)**:智能体与之交互的外部系统。\n", + "3. **状态(State)**:环境在某一时刻的具体情况。\n", + "4. **动作(Action)**:智能体在某一状态下可以执行的操作。\n", + "5. **奖励(Reward)**:智能体执行动作后环境反馈的信号,用于指导智能体的学习。\n", + "\n", + "如果需要更详细的信息或具体的例子,建议查阅相关的强化学习教材或文献。\n" ] } ], @@ -328,14 +372,16 @@ "\n", "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", " template=template_v4)\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " | QA_CHAIN_PROMPT\n", + " | llm\n", + " | StrOutputParser()\n", + ")\n", "\n", "question = \"强化学习的定义是什么\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -374,28 +420,45 @@ "name": "stdout", "output_type": "stream", "text": [ - "构建一个LLM项目需要考虑以下几个步骤:\n", + "构造一个大型语言模型(LLM)项目是一个复杂的任务,需要考虑多个方面。以下是一些关键步骤和考虑因素:\n", "\n", - "1. 确定项目目标和需求:首先要明确你的项目是为了解决什么问题或实现什么目标,确定需要使用LLM的具体场景和任务。\n", + "1. **项目目标和需求分析**:\n", + " - 明确项目的目标和应用场景,例如是用于对话系统、文本生成、翻译还是其他任务。\n", + " - 确定模型的性能指标,如准确性、响应速度和资源消耗等。\n", "\n", - "2. 收集和准备数据:根据项目需求,收集和准备适合的数据集,确保数据的质量和多样性,以提高LLM的性能和效果。\n", + "2. **数据收集和准备**:\n", + " - 收集大量高质量的文本数据,确保数据的多样性和代表性。\n", + " - 对数据进行清洗和预处理,包括去除噪声、处理缺失值和标准化等。\n", "\n", - "3. 设计Prompt和指令微调:根据项目需求设计合适的Prompt,确保指令清晰明确,可以引导LLM生成符合预期的文本。\n", + "3. **模型选择和设计**:\n", + " - 选择合适的模型架构,如Transformer、GPT、BERT等,基于项目需求和资源限制。\n", + " - 考虑模型的规模和复杂度,平衡性能和计算资源。\n", "\n", - "4. 进行模型训练和微调:使用基础LLM或指令微调LLM对数据进行训练和微调,以提高模型在特定任务上的表现和准确性。\n", + "4. **训练和优化**:\n", + " - 配置训练环境,包括硬件(如GPU、TPU)和软件框架(如TensorFlow、PyTorch)。\n", + " - 进行模型训练,调整超参数以优化模型性能。\n", + " - 使用技术如迁移学习、微调等来提高模型的效率和效果。\n", "\n", - "5. 测试和评估模型:在训练完成后,对模型进行测试和评估,检查其在不同场景下的表现和效果,根据评估结果进行必要的调整和优化。\n", + "5. **评估和测试**:\n", + " - 使用标准数据集和指标对模型进行评估,确保其在不同任务上的表现。\n", + " - 进行A/B测试和用户测试,收集反馈以进一步改进模型。\n", "\n", - "6. 部署和应用模型:将训练好的LLM模型部署到实际应用中,确保其能够正常运行并实现预期的效果,持续监测和优化模型的性能。\n", + "6. **部署和维护**:\n", + " - 将模型部署到生产环境中,确保其稳定性和可扩展性。\n", + " - 定期更新和维护模型,处理新数据和反馈,改进模型性能。\n", "\n", - "来源:根据提供的上下文内容进行总结。\n" + "7. **伦理和合规性**:\n", + " - 确保模型的使用符合伦理标准和法律法规,特别是在数据隐私和安全方面。\n", + " - 考虑模型的公平性和透明性,避免偏见和歧视。\n", + "\n", + "这些步骤需要跨学科的合作,包括数据科学、机器学习、软件工程和产品管理等领域的专业知识。通过系统化的方法和持续的迭代改进,可以成功构建和部署一个有效的LLM项目。\n" ] } ], "source": [ "question = \"我们应该如何去构造一个LLM项目\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -407,26 +470,47 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "根据上下文中提供的信息,构造一个LLM项目需要考虑以下几个步骤:\n", + "根据提供的上下文,以下是关于如何构造一个LLM(大语言模型)项目的详细步骤:\n", + "\n", + "1. **理解目标和需求**:\n", + " - 首先明确项目的目标和需求。了解你希望通过LLM实现什么样的功能或解决什么问题。\n", + " - 确定项目的范围和限制条件,比如预算、时间、技术栈等。\n", "\n", - "1. 确定项目目标:首先要明确你的项目目标是什么,是要进行文本摘要、情感分析、实体提取还是其他任务。根据项目目标来确定LLM的使用方式和调用API接口的方法。\n", + "2. **选择合适的模型和算法**:\n", + " - 根据项目需求选择合适的模型架构。可以考虑使用现有的预训练模型(如GPT、BERT等)进行微调,或者从头开始训练一个新的模型。\n", + " - 了解模型的基本原理和适用场景,确保选择的模型能够满足项目需求。\n", "\n", - "2. 设计Prompt:根据项目目标设计合适的Prompt,Prompt应该清晰明确,指导LLM生成符合预期的结果。Prompt的设计需要考虑到任务的具体要求,比如在文本摘要任务中,Prompt应该包含需要概括的文本内容。\n", + "3. **数据准备**:\n", + " - 收集和准备训练数据。数据的质量和数量对模型的性能至关重要。\n", + " - 对数据进行清洗和预处理,确保数据的一致性和准确性。\n", "\n", - "3. 调用API接口:根据设计好的Prompt,通过编程调用LLM的API接口来生成结果。确保API接口的调用方式正确,以获取准确的结果。\n", + "4. **模型训练和优化**:\n", + " - 使用合适的框架(如TensorFlow、PyTorch等)进行模型训练。\n", + " - 通过调整超参数、优化算法等手段提高模型的性能。\n", + " - 使用交叉验证等方法评估模型的泛化能力。\n", "\n", - "4. 分析结果:获取LLM生成的结果后,进行结果分析,确保结果符合项目目标和预期。如果结果不符合预期,可以调整Prompt或者其他参数再次生成结果。\n", + "5. **评估和测试**:\n", + " - 对模型进行全面的评估和测试,确保其在不同场景下的表现。\n", + " - 使用合适的指标(如准确率、召回率、F1分数等)评估模型的性能。\n", "\n", - "5. 优化和改进:根据分析结果的反馈,不断优化和改进LLM项目,提高项目的效率和准确性。可以尝试不同的Prompt设计、调整API接口的参数等方式来优化项目。\n", + "6. **部署和维护**:\n", + " - 将模型部署到生产环境中,确保其能够稳定运行。\n", + " - 定期对模型进行监控和维护,及时更新和优化模型以应对新的需求和挑战。\n", "\n", - "通过以上步骤,可以构建一个有效的LLM项目,利用LLM的强大功能来实现文本摘要、情感分析、实体提取等任务,提高工作效率和准确性。如果有任何不清楚的地方或需要进一步的指导,可以随时向相关领域的专家寻求帮助。\n" + "7. **文档和用户培训**:\n", + " - 编写详细的项目文档,记录项目的各个方面,包括设计、实现、测试等。\n", + " - 为用户提供培训和支持,帮助他们更好地使用和理解模型。\n", + "\n", + "在以上步骤中,确保每一步都基于项目的具体需求和上下文进行调整和优化。\n", + "\n", + "反思:在提供的上下文中,并没有直接涉及如何构造一个LLM项目的具体步骤,因此以上回答是基于一般的项目构建流程和LLM的相关知识进行的总结。如果有任何不符合上下文的内容,请指出。\n" ] } ], @@ -447,14 +531,16 @@ "\n", "QA_CHAIN_PROMPT = PromptTemplate(input_variables=[\"context\",\"question\"],\n", " template=template_v4)\n", - "qa_chain = RetrievalQA.from_chain_type(llm,\n", - " retriever=vectordb.as_retriever(),\n", - " return_source_documents=True,\n", - " chain_type_kwargs={\"prompt\":QA_CHAIN_PROMPT})\n", + "qa_chain = (\n", + " RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())\n", + " | QA_CHAIN_PROMPT\n", + " | llm\n", + " | StrOutputParser()\n", + ")\n", "\n", "question = \"我们应该如何去构造一个LLM项目\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -480,23 +566,47 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "根据上下文提供的信息,LLM(Large Language Model)的分类可以分为两种类型,即基础LLM和指令微调LLM。基础LLM是基于文本训练数据,训练出预测下一个单词能力的模型,通常通过在大量数据上训练来确定最可能的词。指令微调LLM则是对基础LLM进行微调,以更好地适应特定任务或场景,类似于向另一个人提供指令来完成任务。\n", + "① 基于提供的上下文,回答问题:\n", + "\n", + "LLM的分类是什么?给我返回一个 Python List\n", "\n", - "根据上下文,可以返回一个Python List,其中包含LLM的两种分类:[\"基础LLM\", \"指令微调LLM\"]。\n" + "有用的回答:\n", + "\n", + "1. 情感推断:LLM可以用于情感倾向分析,通过编写Prompt来解析评论的情感倾向,例如正面或负面。\n", + "\n", + "2. 识别语种:LLM可以识别文本的语言,例如识别“Combien coûte le lampadaire?”是法语。\n", + "\n", + "3. 多语种翻译:LLM可以将文本翻译成多种语言,例如将“I want to order a basketball.”翻译成中文、英文、法语和西班牙语。\n", + "\n", + "4. 同时进行语气转换:LLM可以根据输入的Prompt进行语气转换和信息提取,例如从文本中提取关于“美国航空航天局”的信息。\n", + "\n", + "Python List:\n", + "```python\n", + "llm_classifications = [\n", + " \"情感推断\",\n", + " \"识别语种\",\n", + " \"多语种翻译\",\n", + " \"同时进行语气转换\"\n", + "]\n", + "```\n", + "\n", + "② 反思回答中有没有不正确或不是基于上下文得到的内容:\n", + "\n", + "在提供的上下文中,LLM的分类是基于情感推断、识别语种、多语种翻译和语气转换等功能的描述得出的。因此,回答是基于上下文的,没有不正确的内容。\n" ] } ], "source": [ "question = \"LLM的分类是什么?给我返回一个 Python List\"\n", - "result = qa_chain({\"query\": question})\n", - "print(result[\"result\"])" + "result = qa_chain.invoke(question)\n", + "print(result)" ] }, { @@ -514,7 +624,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -522,10 +632,7 @@ "\n", "from openai import OpenAI\n", "\n", - "client = OpenAI(\n", - " # This is the default and can be omitted\n", - " api_key=os.environ.get(\"OPENAI_API_KEY\"),\n", - ")\n", + "client = OpenAI()\n", "\n", "\n", "def gen_gpt_messages(prompt):\n", @@ -539,13 +646,13 @@ " return messages\n", "\n", "\n", - "def get_completion(prompt, model=\"gpt-3.5-turbo\", temperature = 0):\n", + "def get_completion(prompt, model=\"gpt-4o\", temperature = 0):\n", " '''\n", " 获取 GPT 模型调用结果\n", "\n", " 请求参数:\n", " prompt: 对应的提示词\n", - " model: 调用的模型,默认为 gpt-3.5-turbo,也可以按需选择 gpt-4 等其他模型\n", + " model: 调用的模型,默认为 gpt-4o,也可以按需选择 gpt-4 等其他模型\n", " temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~2。温度系数越低,输出内容越一致。\n", " '''\n", " response = client.chat.completions.create(\n", @@ -579,16 +686,16 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'```\\n[\"给我返回一个 Python List\", \"LLM的分类是什么?\"]\\n```'" + "'[\"给我返回一个 Python List\", \"LLM的分类是什么?\"]'" ] }, - "execution_count": 19, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -607,7 +714,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -637,16 +744,16 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\"['基础LLM', '指令微调LLM']\"" + "'```python\\n[\\n \"生成式模型\",\\n \"判别式模型\"\\n]\\n```'" ] }, - "execution_count": 24, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -660,8 +767,8 @@ "end_loc = input_lst_s.find(']')\n", "rule, new_question = eval(input_lst_s[start_loc:end_loc+1])\n", "# 接着使用拆分后的问题调用检索链\n", - "result = qa_chain({\"query\": new_question})\n", - "result_context = result[\"result\"]\n", + "result = qa_chain.invoke(new_question)\n", + "result_context = result\n", "# 接着调用输出格式解析\n", "response = get_completion(prompt_output.format(new_question, result_context, rule))\n", "response" @@ -679,7 +786,7 @@ ], "metadata": { "kernelspec": { - "display_name": "llm_universe", + "display_name": "universe_0_3", "language": "python", "name": "python3" }, @@ -693,7 +800,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.16" } }, "nbformat": 4, diff --git "a/notebook/C7 \351\253\230\347\272\247 RAG \346\212\200\345\267\247/requirements.txt" "b/notebook/C7 \351\253\230\347\272\247 RAG \346\212\200\345\267\247/requirements.txt" new file mode 100644 index 0000000..735c804 --- /dev/null +++ "b/notebook/C7 \351\253\230\347\272\247 RAG \346\212\200\345\267\247/requirements.txt" @@ -0,0 +1,26 @@ +fastapi==0.110.0 +gradio==4.20.0 +huggingface_hub==0.21.3 +ipython==8.22.2 +langchain==0.1.11 +langchain-community==0.0.29 +langchain-core==0.1.36 +langchain-openai==0.1.1 +nltk==3.8.1 +openai==1.13.3 +pip==23.3.1 +pydantic==2.6.3 +python-dotenv==1.0.1 +qianfan==0.3.3 +Requests==2.31.0 +transformers==4.38.2 +websocket_client==1.7.0 +zhipuai==2.0.1 +chromadb==0.4.14 +rapidocr-onnxruntime==1.3.15 +pymupdf==1.24.0 +unstructured==0.12.6 +chromadb==0.4.14 +markdown==3.6 +spark-ai-python==0.3.15 +erniebot==0.5.5 diff --git a/requirements.txt b/requirements.txt index 735c804..0aee11b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,15 @@ -fastapi==0.110.0 -gradio==4.20.0 -huggingface_hub==0.21.3 -ipython==8.22.2 -langchain==0.1.11 -langchain-community==0.0.29 -langchain-core==0.1.36 -langchain-openai==0.1.1 -nltk==3.8.1 -openai==1.13.3 -pip==23.3.1 -pydantic==2.6.3 +langchain==0.3.0 +langchain-community==0.3.0 +langchain-text-splitters==0.3.0 +langchain-core==0.3.0 +langchain-openai==0.2.0 +langchain-chroma==0.1.4 python-dotenv==1.0.1 -qianfan==0.3.3 -Requests==2.31.0 -transformers==4.38.2 -websocket_client==1.7.0 -zhipuai==2.0.1 -chromadb==0.4.14 -rapidocr-onnxruntime==1.3.15 -pymupdf==1.24.0 -unstructured==0.12.6 -chromadb==0.4.14 -markdown==3.6 -spark-ai-python==0.3.15 -erniebot==0.5.5 +spark-ai-python==0.4.5 +zhipuai==2.1.5.20250106 +qianfan==0.4.12.3 +unstructured==0.16.23 +pymupdf==1.25.3 +markdown==3.7 +streamlit==1.43.0 +jieba==0.42.1 \ No newline at end of file