是时候转向 Python 了。JavaScript 虽然功能强大,但在本书的 GitHub 代码仓库中,我们也为部分示例提供了 JavaScript 版本供您使用。不过,Python 拥有能够处理复杂任务的各类库和工具。它无疑是人工智能开发的首选语言。基于此原因,从现在开始,本书的所有示例都将使用 Python 编写。
别担心!如果您不是 Python 开发者,我们会循序渐进地引导您入门。我们会详细解释那些具有“Python 风格”的独特语法。只要您按照书中后面的示例进行学习,就能打下扎实的 Python 基础。
我们首先用 Python 重新实现第 2 章中的示例。该章的代码可以在 GitHub 仓库的 part1/getting_started_python 文件夹中找到。
让我们开始用 Python 编程吧!
与第 2 章类似,你将构建一个简单的 REST 服务。当该服务被调用时,它会返回 LLM 的生成结果。你的 REST 服务将结合使用 FastAPI 和 Ollama SDK 来实现这一功能。你已经安装了 Ollama 并下载了 Llama 3.2 模型,只需确保该模型仍在运行即可。
如果您还没有安装 Python,请从 https://www.python.org/downloads/下载并安装。(本书中的所有示例均使用 Python 3.14.3 进行测试。)
你可以在终端中使用以下命令来验证安装是否成功:
~/ai-for-devs % python --version如果安装成功,将显示以下版本信息:
Python 3.14.3请注意,您的版本号可能有所不同。
在 Mac 或 Linux 系统上,或者如果您使用的是特定版本的 Python,可能需要将 “python” 命令替换为 “python3”:
~/ai-for-devs % python3 --version在运行本书中的示例时,请确保使用适合您安装环境的正确 Python 命令。
接下来,你需要安装那些能让 API 快速启动和运行的 Python 库。以下是你将用到的 Python 库:
在终端中运行以下安装命令:
~/ai-for-devs % python -m pip install "fastapi[standard]"==0.119.0 \ollama==0.6.0Python 中的虚拟环境
为便于操作,这些依赖项已全局安装。通常建议使用虚拟环境来将 Python 项目的依赖项隔离开来。虚拟环境本质上就是一个用于安装项目所需特定依赖项的目录,这样一来,不同项目就可以使用不同版本的库,而不会相互影响。
不过,Python 开发者们都是独立思考的人,实现方式也多种多样。这方面的讨论超出了本书的范围,因此关于虚拟环境设置的详细信息,我们已放在本书 GitHub 仓库的 README 文件中。
现在你将重新创建第 2 章中的 REST 服务。该服务在被调用时,会利用 FastAPI 和 Ollama SDK 来返回大语言模型的生成结果。你已在前一章的 JavaScript 示例中学习了相关概念,因此可以直接开始查看 Python 代码!
为你的项目创建一个名为“getting_started_python”的新目录。在该项目目录下,创建一个名为“main.py”的文件。用文本编辑器或你喜欢的集成开发环境(如 VS Code)打开该文件。
我们稍后会详细说明,但首先请在 main.py 文件中添加以下代码行:
from fastapi import FastAPIfrom fastapi.responses import StreamingResponsefrom fastapi.middleware.cors import CORSMiddlewarefrom ollama import Clientfrom pydantic import BaseModelapp = FastAPI()app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],)client = Client()class ChatRequest(BaseModel): question: strdef generate_stream(question: str): result = client.generate(stream=True, model="llama3.2", prompt=question) for chunk in result: if chunk.response: yield chunk.response@app.post("/")def chat(chatRequest: ChatRequest): return StreamingResponse( generate_stream(chatRequest.question), media_type="text/plain" )这是一个简单的 FastAPI 服务器。它会监听 8000 端口(FastAPI 的默认端口),接收所有的 HTTP POST 请求。现在我们来仔细看看这段代码。
让我们从用于创建你将要使用的库组件的代码开始吧:
--snip--➊ from fastapi import FastAPI from fastapi.responses import StreamingResponse from fastapi.middleware.cors import CORSMiddleware from ollama import Client➋ app = FastAPI()➌ app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )➍ client = Client() --snip--前四行是从你安装的 Python 库中导入的内容➊。这些导入语句定义了你在各个库中将要使用的类。
首先,你需要创建一个 FastAPI 服务器实例➋。app 变量被初始化为对该服务器的引用,这样你就可以配置服务器并设置服务 API 了。
与前面的示例相同,您在这里配置中间件来处理 CORS 相关的要求(关于 CORS 的更多信息,请参见第 2 章)。您调用 app.add_middleware 方法来添加之前导入的 CORSMiddleware 组件。这样一来,浏览器客户端就能向服务器发送请求了。
请注意,您需要同时提供 CORSMiddleware 类以及一些配置参数。这些参数都是通过名称来定义的(例如,allow_origins=["*"])。这是很常见的做法,尤其是在 Python 中处理可选的函数和方法参数时。这种配置实际上相当于将服务器开放给所有浏览器客户端访问。
现在已创建了一个 Ollama 客户端实例➍。由于默认设置已适用于本示例,因此无需更改任何配置选项。
现在您已经拥有了所需的各个组件,可以定义您的 REST API 了:
--snip--➊ from pydantic import BaseModel➋ class ChatRequest(BaseModel): question: str➌ @app.post("/")➍ def chat(chatRequest: ChatRequest): --snip--请注意,您是从 pydantic 模块中导入了 BaseModel 类➊。由于 Pydantic 是 FastAPI 的组成部分,因此您无需单独安装该库。在本书中,您会随处看到 Pydantic 的使用。
Pydantic 使得创建用于定义 REST API 请求和响应中 JSON 格式数据的类(即模型)变得非常简单。ChatRequest 类定义了我们所期望的 JSON 请求体应具有的格式➋。该类继承自 Pydantic BaseModel(括号中的内容是 Python 中的继承语法)。该类只有一个名为 question 的属性,其数据类型为字符串(str)。
Pydantic BaseModel 类为 ChatRequest 类添加了若干方法,使得验证数据以及将相应的 JSON 格式数据转换为该类的实例变得十分简单。
{ "question": "Tell me about the Beatles."}现在请看聊天功能的签名部分➍。在 Python 中,使用 def 关键字来开始定义方法或函数。函数上方的@app.post("/")注解用于配置该聊天功能以处理 REST API 请求➌。该注解是一种配置元素,它告诉 FastAPI:该聊天功能将响应如下格式的 HTTP POST 请求:
POST https://localhost:8000/{"question": "Who are the Beatles?"}这听起来可能有点神奇,但在本书的各个代码示例中,你都会看到注释。在 Python 中,注释提供了一种便捷的方式,通过内联配置将代码与各种库或框架进行集成。
FastAPI 认为,任何发送到“/”的 POST 请求——其中“/”是正在监听的端口的基准 URL——都应该调用你的聊天功能。同时,它会检查 chatRequest 参数,并利用 Pydantic 模型来解析 JSON 请求体应有的格式。
FastAPI 会根据我们定义的 ChatRequest 模型来验证所有请求中的 JSON 数据。如果验证失败,系统会自动返回错误响应。在第 6 章中,当我们使用 Pydantic 模式来整理大语言模型的输出时,你将更深入地了解这一机制的运作方式。
现在我们来看看 generate_stream 函数:
--snip--def generate_stream(question: str):➊ result = client.generate(model="llama3.2", stream=True, prompt=question)➋ for chunk in result: ➌ if chunk.response: ➍ yield chunk.response--snip--该函数首先调用 Ollama 客户端的 generate 方法 ➊。该方法会向 Ollama 发送请求,以便根据给定的提示词生成 LLM 响应。请注意,此处再次使用了命名参数来指定下载的模型(model="llama3.2"),同时指定了希望以流式方式接收响应(stream=True)。
返回的结果是一个可迭代流。for 循环会随着数据的生成而逐个处理这些数据块➋。在输出文本之前,需要先确认已获取到响应数据➌。
在 Python 中,yield 关键字可以将普通函数转换为生成器函数➍。调用生成器函数时,它不会立即执行。相反,它会返回一个生成器对象,该对象其实是一个迭代器。这对于流式处理场景非常合适,因为你需要等待从 Ollama 客户端传来的数据块。
现在你可以将整个聊天功能整合起来,并将回复内容进行流式传输:
--snip--def generate_stream(question: str): result = client.generate(stream=True, model="llama3.2", prompt=question) for chunk in result: if chunk.response: yield chunk.response@app.post("/")def chat(chatRequest: ChatRequest):➊ return StreamingResponse( generate_stream(chatRequest.question), media_type="text/plain")请注意,您使用的是之前导入的 FastAPI StreamingResponse 组件,该组件让流式传输变得非常简单➊。您只需用 generate_stream 函数生成的生成器来创建该组件的实例,并指定要返回的媒体类型。在本例中,您返回的是由大语言模型生成的纯文本。
备注
如果你来自 JavaScript、Java 或 C#等语言环境,一定要特别注意 Python 中的缩进规则。在那些语言中,大括号({})用于标识代码块,而缩进主要是为了提高代码的可读性。但在 Python 中,缩进本身就构成了代码块的结构。每行代码开头的空格数量决定了该代码属于哪个 if、for 或 def 语句。如果缩进不一致(或者同时使用了制表符和空格),代码可能会出错;更糟糕的是,虽然代码能运行,但行为会异常。请保持一致的缩进风格(通常为两个或四个空格),并让编辑器帮助你保持代码的整洁。
现在我们来试试吧!
回到终端后,运行以下命令:
~/ai-for-devs/getting_started_python % fastapi dev main.py如果服务器成功启动,您将看到类似以下的输出:
--snip-- server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use: fastapi run Logs: INFO Will watch for changes in these directories: ['/home/hewenxiang/workspace/ai/The-Developers-Guide-to-AI/examples/ai-for-devs'] INFO Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO Started reloader process [33076] using WatchFiles INFO Started server process [33078] INFO Waiting for application startup. INFO Application startup complete.要测试您的服务器,请先运行第 2 章中使用的相同 curl 命令。在终端中输入以下命令:
~ % curl -N -X POST -H "Content-Type: application/json" -d \'{"question": "Can you tell me about the Beatles in 500 words or less?"}' http://localhost:8000
与之前一样,您会看到大语言模型的响应结果实时显示在控制台上。
你现在可以使用第 2 章中提到的同一用户界面客户端来向服务器发送查询并接收响应数据。该客户端的源代码位于 part1/client 文件夹中。
README 文件提供了运行该示例的说明。一旦运行成功,你即可在浏览器中看到大语言模型的实时运行效果。
你现在已经把第 2 章中的 JavaScript 示例移植到了 Python 中。你使用了本书中会反复用到的几个重要 Python 库:FastAPI 和 Ollama SDK。
对于那些不熟悉 Python 的人来说,你们刚刚初次接触了这门语言(希望你们喜欢它的语法)。在整本书中,我们将继续介绍 Python 的语法及编程规范。
关于大语言模型的介绍就到此为止吧。现在是时候探讨提示工程了!
与第 2 章类似,试着修改示例代码,以便在数据流式传输的过程中打印出各个数据块。发起请求后,观察 Ollama 返回的数据以及每个数据块的内容。这些数据属于 GenerateResponse 类的属性。随着阅读的深入,这些数据的用途会变得清晰起来。
请在第二部分中使用这个示例,来体验我们将要展示的各种提示词。实际上,下次有疑问时,可以试着结合 ChatGPT、Gemini 或 Claude 来使用这个示例。看看这些工具与大型语言模型结合后所带来的差异以及各种额外功能。这将有助于你思考:我们在书中教授的各种技术,如何应用到你自己的实际场景中。