专栏:Python 从真零基础到纯文本 LLM 全栈实战・第 4 篇 | 字数:14026 字 | 零基础友好 | LLM 场景深度绑定 | 代码可运行
开篇:函数 = LLM 逻辑的「积木块」
对零基础开发者来说,Python 函数是封装 LLM 通用逻辑的「积木块」------ 它能让你:
- 避免重复代码:将 LLM API 调用、Prompt 生成等重复逻辑封装成函数
- 提高代码可读性:用函数名清晰表达逻辑功能
- 简化调试:单个函数独立测试,定位错误更快
- 实现复用:在多个 LLM 项目中复用同一函数
本文将从零基础视角 讲解 Python 函数的核心语法,并结合 LLM 开发的真实场景,教你如何封装 LLM 的通用逻辑并实现复用。
一、函数的「核心概念」(零基础入门)
1.1 什么是函数?
函数是一段可以重复使用的代码块,用于实现一个单一的、明确的功能。
1.2 函数的基本结构
def 函数名(参数1, 参数2, ...):
"""
函数文档字符串:描述函数的功能、参数、返回值
"""
# 函数体:实现功能的代码
return 返回值 # 可选,返回函数执行结果
1.3 函数的调用
# 定义函数
def add(a, b):
"""计算两个数的和"""
return a + b
# 调用函数
result = add(1, 2) # 传递参数1和2,接收返回值
print(result) # 输出:3
二、函数的核心语法(零基础必学)
2.1 参数类型
Python 函数支持多种参数类型,用于满足不同的 LLM 开发需求。
2.1.1 位置参数
位置参数是最基本的参数类型,参数的顺序必须与函数定义的顺序一致。
# LLM场景:生成Prompt模板
def generate_prompt(product, question):
"""生成LLM的Prompt模板"""
return f"请回答关于{product}的问题:{question}"
# 调用函数(位置参数必须按顺序传递)
prompt = generate_prompt("苹果15手机壳", "发货时间")
print(prompt) # 输出:请回答关于苹果15手机壳的问题:发货时间
2.1.2 关键字参数
关键字参数是通过参数名指定的参数,参数的顺序可以任意。
# 调用函数(关键字参数)
prompt = generate_prompt(question="发货时间", product="苹果15手机壳")
print(prompt) # 输出:请回答关于苹果15手机壳的问题:发货时间
2.1.3 默认参数
默认参数是在函数定义时指定默认值的参数,调用函数时可以省略。
# LLM场景:生成LLM API请求参数
def generate_api_params(prompt, model="gpt-3.5-turbo", temperature=0.7):
"""生成LLM API请求参数"""
return {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": temperature
}
# 调用函数(使用默认参数)
params1 = generate_api_params("什么是LLM?")
print(params1) # 输出:{"model":"gpt-3.5-turbo", "messages":..., "temperature":0.7}
# 调用函数(覆盖默认参数)
params2 = generate_api_params("什么是LLM?", model="gpt-4", temperature=1.0)
print(params2) # 输出:{"model":"gpt-4", "messages":..., "temperature":1.0}
2.1.4 可变参数
可变参数是可以接收任意数量参数的参数,分为两种:
-
*args:接收任意数量的位置参数,返回元组 -
**kwargs:接收任意数量的关键字参数,返回字典*args示例
def send_emails(*emails):
"""发送邮件给多个收件人"""
for email in emails:
print(f"Sent email to: {email}")send_emails("a@example.com", "b@example.com", "c@example.com")
**kwargs示例
def generate_user(**user_info):
"""生成用户信息"""
return user_infouser = generate_user(name="Tom", age=20, email="tom@example.com")
print(user) # 输出:{"name":"Tom", "age":20, "email":"tom@example.com"}LLM场景:接收任意数量的Prompt模板参数
def generate_llm_prompt(*args, **kwargs):
"""生成LLM的Prompt模板"""
prompt = ""
for arg in args:
prompt += arg + "\n"
for key, value in kwargs.items():
prompt += f"{key}: {value}\n"
return prompt.strip()prompt = generate_llm_prompt("你是电商客服", "仅回答商品相关问题", product="苹果15手机壳", question="发货时间")
print(prompt) # 输出:你是电商客服\n仅回答商品相关问题\nproduct: 苹果15手机壳\nquestion: 发货时间
2.2 返回值
函数的返回值是函数执行的结果 ,用return关键字返回。
2.2.1 单个返回值
def calculate_area(radius):
"""计算圆的面积"""
import math
return math.pi * radius ** 2
area = calculate_area(5)
print(area) # 输出:78.53981633974483
2.2.2 多个返回值
Python 函数可以返回多个值,用逗号分隔,返回的是元组。
# LLM场景:解析LLM API的响应
def parse_llm_response(response):
"""解析LLM API的响应"""
if "choices" in response:
answer = response["choices"][0]["message"]["content"]
token_count = response["usage"]["total_tokens"]
return True, answer, token_count # 返回三个值
else:
return False, None, 0 # 返回三个值
# 调用函数
response = {
"choices": [{"message": {"content": "LLM是大语言模型"}}],
"usage": {"total_tokens": 10}
}
success, answer, token_count = parse_llm_response(response)
print(f"Success: {success}, Answer: {answer}, Token Count: {token_count}")
# 输出:Success: True, Answer: LLM是大语言模型, Token Count: 10
2.3 函数的文档字符串
函数的文档字符串是用于描述函数功能、参数、返回值的字符串 ,用"""包裹,位于函数体的第一行。
def call_llm_api(prompt, model="gpt-3.5-turbo", temperature=0.7):
"""
调用LLM API生成回答
参数:
prompt (str): 输入给LLM的Prompt
model (str): LLM模型名称,默认gpt-3.5-turbo
temperature (float): LLM回答的随机性,0-2,默认0.7
返回值:
tuple: (success, answer, token_count),分别为是否成功、回答内容、Token数量
"""
# 函数体
pass
三、LLM 通用逻辑的「封装技巧」
在 LLM 开发中,以下逻辑可以封装为通用函数:
- Prompt 生成
- API 调用
- 响应解析
- 语料清洗
- Token 计数
- 限流处理
3.1 封装 LLM Prompt 生成逻辑
def generate_llm_prompt(system_prompt, user_prompt, **context):
"""
生成标准的LLM Prompt格式
参数:
system_prompt (str): LLM的系统提示词
user_prompt (str): 用户的提问
**context (dict): 上下文信息(可选)
返回值:
str: 标准的LLM Prompt
"""
# 构建系统提示
prompt = f"系统提示:{system_prompt}\n"
# 构建上下文信息
if context:
for key, value in context.items():
prompt += f"{key}:{value}\n"
# 构建用户提问
prompt += f"用户:{user_prompt}\nLLM:"
return prompt
# 使用示例
system_prompt = "你是电商客服,仅回答商品相关问题"
user_prompt = "苹果15手机壳的发货时间"
context = {"商品名称": "苹果15手机壳", "价格": "29.9元"}
prompt = generate_llm_prompt(system_prompt, user_prompt, **context)
print(prompt)
# 输出:
# 系统提示:你是电商客服,仅回答商品相关问题
# 商品名称:苹果15手机壳
# 价格:29.9元
# 用户:苹果15手机壳的发货时间
# LLM:
3.2 封装 LLM API 调用逻辑
import openai
import time
from dotenv import load_dotenv
import os
# 加载API密钥
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
def call_openai_api(prompt, model="gpt-3.5-turbo", max_retries=3, wait_time=2):
"""
调用OpenAI API生成回答,带重试机制
参数:
prompt (str): 输入给LLM的Prompt
model (str): LLM模型名称,默认gpt-3.5-turbo
max_retries (int): 最大重试次数,默认3
wait_time (int): 重试间隔时间,默认2s
返回值:
tuple: (success, answer, token_count),分别为是否成功、回答内容、Token数量
"""
retry_count = 0
while retry_count < max_retries:
try:
# 调用API
response = openai.ChatCompletion.create(
model=model,
messages=[{"role": "system", "content": prompt}]
)
# 解析响应
answer = response["choices"][0]["message"]["content"]
token_count = response["usage"]["total_tokens"]
return True, answer, token_count
except Exception as e:
retry_count += 1
print(f"API调用失败:{e},第{retry_count}次重试...")
time.sleep(wait_time * retry_count) # 指数退避
return False, None, 0
# 使用示例
prompt = "请解释什么是LLM?"
success, answer, token_count = call_openai_api(prompt)
if success:
print(f"LLM回答:{answer}")
print(f"Token数量:{token_count}")
else:
print("API调用失败")
安装依赖 :pip install openai python-dotenv
3.3 封装 LLM 响应解析逻辑
def parse_llm_response(response):
"""
解析不同LLM平台的响应
参数:
response (dict): LLM API的响应
返回值:
tuple: (success, answer, token_count),分别为是否成功、回答内容、Token数量
"""
try:
# 解析OpenAI响应
if "choices" in response:
answer = response["choices"][0]["message"]["content"]
token_count = response["usage"]["total_tokens"] if "usage" in response else 0
return True, answer, token_count
# 解析文心一言响应
elif "result" in response:
answer = response["result"]
token_count = response["usage"]["total_tokens"] if "usage" in response else 0
return True, answer, token_count
# 解析混元响应
elif "output" in response:
answer = response["output"]
token_count = response["token_usage"]["total"] if "token_usage" in response else 0
return True, answer, token_count
else:
return False, None, 0
except Exception as e:
print(f"响应解析失败:{e}")
return False, None, 0
3.4 封装 LLM Token 计数逻辑
from transformers import AutoTokenizer
# 加载中文Tokenizer
tokenizer = AutoTokenizer.from_pretrained("hfl/chinese-roberta-wwm-ext")
def count_llm_tokens(text):
"""
计算文本的Token数量
参数:
text (str): 输入文本
返回值:
int: Token数量
"""
return len(tokenizer.tokenize(text))
# 使用示例
prompt = "请解释什么是LLM?"
token_count = count_llm_tokens(prompt)
print(f"Prompt的Token数量:{token_count}") # 输出:7
安装依赖 :pip install transformers
四、LLM 函数的「复用方案」
封装好的 LLM 函数可以在多个项目中复用,常用的复用方案有两种:
- 模块复用:将函数放在.py 文件中,导入使用
- 包复用:将函数放在包中,安装使用
4.1 模块复用
模块复用是将函数放在.py 文件中,在其他文件中导入使用的方法。
4.1.1 创建模块文件(llm_utils.py)
# llm_utils.py
def generate_llm_prompt(system_prompt, user_prompt, **context):
# 函数实现(省略)
pass
def call_openai_api(prompt, model="gpt-3.5-turbo"):
# 函数实现(省略)
pass
def parse_llm_response(response):
# 函数实现(省略)
pass
4.1.2 在其他文件中导入使用
# main.py
import llm_utils
# 导入特定函数
# from llm_utils import generate_llm_prompt, call_openai_api
# 使用函数
system_prompt = "你是电商客服"
user_prompt = "苹果15手机壳的发货时间"
prompt = llm_utils.generate_llm_prompt(system_prompt, user_prompt)
success, answer, token_count = llm_utils.call_openai_api(prompt)
4.2 包复用
包复用是将函数放在包中,安装后在多个项目中使用的方法。
4.2.1 创建包结构
llm_utils/
├── __init__.py
├── prompt.py # Prompt生成函数
├── api.py # API调用函数
├── parse.py # 响应解析函数
└── token.py # Token计数函数
4.2.2 编写__init__.py 文件
# __init__.py
from .prompt import generate_llm_prompt
from .api import call_openai_api, call_wenxin_api
from .parse import parse_llm_response
from .token import count_llm_tokens
__version__ = "1.0.0"
4.2.3 安装包
# 安装到系统Python
pip install .
# 安装为可编辑模式
pip install -e .
4.2.4 在其他项目中使用
import llm_utils
prompt = llm_utils.generate_llm_prompt("你是客服", "苹果15手机壳的发货时间")
success, answer, token_count = llm_utils.call_openai_api(prompt)
五、LLM 函数的「进阶应用」
5.1 装饰器:增强 LLM 函数的功能
装饰器是在不修改函数代码的情况下增强函数功能 的方法,常用于 LLM 函数的日志记录、鉴权、缓存等。
5.1.1 日志装饰器
import time
def log_decorator(func):
"""日志装饰器:记录函数的调用时间和参数"""
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数名:{func.__name__},参数:args={args}, kwargs={kwargs},耗时:{end_time - start_time:.2f}s")
return result
return wrapper
# 使用装饰器
@log_decorator
def call_llm_api(prompt):
time.sleep(0.5) # 模拟API调用耗时
return "LLM回答:什么是LLM?"
# 调用函数
result = call_llm_api("什么是LLM?")
# 输出:函数名:call_llm_api,参数:args=('什么是LLM?',), kwargs={},耗时:0.50s
5.1.2 缓存装饰器
from functools import lru_cache
@lru_cache(maxsize=100) # 缓存最大100条记录
def call_llm_api(prompt):
time.sleep(0.5) # 模拟API调用耗时
return f"LLM回答:{prompt}"
# 第一次调用:耗时0.5s
result1 = call_llm_api("什么是LLM?")
print(result1)
# 第二次调用:从缓存读取,耗时0s
result2 = call_llm_api("什么是LLM?")
print(result2)
5.2 高阶函数:将函数作为参数或返回值
高阶函数是将函数作为参数或返回值 的函数,常用于 LLM 函数的流程控制、动态生成等。
5.2.1 将函数作为参数
# LLM场景:批量处理语料,使用不同的LLM API
def batch_process_corpus(corpus, llm_func):
"""
批量处理LLM语料
参数:
corpus (list): LLM语料列表
llm_func (callable): LLM API调用函数
返回值:
list: LLM回答列表
"""
results = []
for text in corpus:
success, answer, token_count = llm_func(text)
if success:
results.append(answer)
else:
results.append(None)
return results
# 使用示例
corpus = ["什么是LLM?", "LLM的应用场景有哪些?"]
results = batch_process_corpus(corpus, call_openai_api)
print(results) # 输出:["LLM是大语言模型", "LLM的应用场景包括..."]
5.2.2 将函数作为返回值
# LLM场景:动态生成不同的LLM API调用函数
def create_llm_api_func(model="gpt-3.5-turbo", temperature=0.7):
"""
动态生成LLM API调用函数
参数:
model (str): LLM模型名称
temperature (float): LLM回答的随机性
返回值:
callable: LLM API调用函数
"""
def llm_api_func(prompt):
return call_openai_api(prompt, model=model, temperature=temperature)
return llm_api_func
# 使用示例
# 生成严谨的LLM API调用函数
strict_llm_func = create_llm_api_func(temperature=0.0)
# 生成随机的LLM API调用函数
random_llm_func = create_llm_api_func(temperature=2.0)
# 调用不同的LLM函数
result1 = strict_llm_func("什么是LLM?")
result2 = random_llm_func("什么是LLM?")
六、LLM 函数的「最佳实践」
6.1 单一职责原则
一个函数只负责一个单一的功能 ------ 如generate_prompt只负责生成 Prompt,call_api只负责调用 API。
6.2 明确的函数名和文档字符串
函数名要清晰表达函数的功能 ------ 如count_llm_tokens比count_tokens更清晰。函数要包含文档字符串------ 描述函数的功能、参数、返回值。
6.3 处理异常情况
LLM 函数要处理异常情况------ 如 API 调用失败、响应解析失败等,返回明确的错误信息。
6.4 避免硬编码
LLM 函数要避免硬编码------ 如 API 密钥、模型名称等,使用配置文件或环境变量。
6.5 测试函数
LLM 函数要进行测试------ 确保函数的功能正确,参数处理正确。
七、LLM 函数封装的「完整实战」
7.1 实战需求
封装 LLM 的通用函数,实现:
- Prompt 生成
- API 调用
- 响应解析
- Token 计数
- 批量处理
7.2 实战完整代码
import openai
import time
from dotenv import load_dotenv
import os
from transformers import AutoTokenizer
# 加载配置
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
# 初始化Tokenizer
tokenizer = AutoTokenizer.from_pretrained("hfl/chinese-roberta-wwm-ext")
# -------------------------- LLM通用函数 --------------------------
def generate_llm_prompt(system_prompt, user_prompt, **context):
"""
生成标准的LLM Prompt格式
参数:
system_prompt (str): LLM的系统提示词
user_prompt (str): 用户的提问
**context (dict): 上下文信息(可选)
返回值:
str: 标准的LLM Prompt
"""
prompt = f"系统提示:{system_prompt}\n"
if context:
for key, value in context.items():
prompt += f"{key}:{value}\n"
prompt += f"用户:{user_prompt}\nLLM:"
return prompt
def call_openai_api(prompt, model="gpt-3.5-turbo", max_retries=3, wait_time=2):
"""
调用OpenAI API生成回答,带重试机制
参数:
prompt (str): 输入给LLM的Prompt
model (str): LLM模型名称,默认gpt-3.5-turbo
max_retries (int): 最大重试次数,默认3
wait_time (int): 重试间隔时间,默认2s
返回值:
tuple: (success, answer, token_count),分别为是否成功、回答内容、Token数量
"""
retry_count = 0
while retry_count < max_retries:
try:
response = openai.ChatCompletion.create(
model=model,
messages=[{"role": "system", "content": prompt}]
)
answer = response["choices"][0]["message"]["content"]
token_count = response["usage"]["total_tokens"] if "usage" in response else 0
return True, answer, token_count
except Exception as e:
retry_count += 1
print(f"API调用失败:{e},第{retry_count}次重试...")
time.sleep(wait_time * retry_count)
return False, None, 0
def parse_llm_response(response):
"""
解析不同LLM平台的响应
参数:
response (dict): LLM API的响应
返回值:
tuple: (success, answer, token_count),分别为是否成功、回答内容、Token数量
"""
try:
if "choices" in response:
answer = response["choices"][0]["message"]["content"]
token_count = response["usage"]["total_tokens"] if "usage" in response else 0
return True, answer, token_count
elif "result" in response:
answer = response["result"]
token_count = response["usage"]["total_tokens"] if "usage" in response else 0
return True, answer, token_count
elif "output" in response:
answer = response["output"]
token_count = response["token_usage"]["total"] if "token_usage" in response else 0
return True, answer, token_count
else:
return False, None, 0
except Exception as e:
print(f"响应解析失败:{e}")
return False, None, 0
def count_llm_tokens(text):
"""
计算文本的Token数量
参数:
text (str): 输入文本
返回值:
int: Token数量
"""
return len(tokenizer.tokenize(text))
def batch_process_corpus(corpus, system_prompt, model="gpt-3.5-turbo"):
"""
批量处理LLM语料
参数:
corpus (list): LLM语料列表
system_prompt (str): LLM的系统提示词
model (str): LLM模型名称,默认gpt-3.5-turbo
返回值:
list: LLM回答列表,每个元素为(timestamp, prompt, answer, token_count)
"""
import datetime
results = []
for text in corpus:
prompt = generate_llm_prompt(system_prompt, text)
token_count_prompt = count_llm_tokens(prompt)
success, answer, token_count_response = call_openai_api(prompt, model=model)
if success:
results.append({
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"prompt": prompt,
"answer": answer,
"token_count_prompt": token_count_prompt,
"token_count_response": token_count_response,
"token_count_total": token_count_prompt + token_count_response
})
return results
# -------------------------- 实战测试 --------------------------
if __name__ == "__main__":
# 测试Prompt生成
print("【测试Prompt生成】")
system_prompt = "你是电商客服,仅回答商品相关问题"
user_prompt = "苹果15手机壳的发货时间"
context = {"商品名称": "苹果15手机壳", "价格": "29.9元"}
prompt = generate_llm_prompt(system_prompt, user_prompt, **context)
print(prompt)
# 测试Token计数
print("\n【测试Token计数】")
token_count = count_llm_tokens(prompt)
print(f"Prompt的Token数量:{token_count}")
# 测试批量处理
print("\n【测试批量处理】")
corpus = ["苹果15手机壳的发货时间", "苹果15手机壳的材质", "苹果15手机壳的价格"]
results = batch_process_corpus(corpus, system_prompt)
for result in results:
print(f"时间:{result['timestamp']},回答:{result['answer']},总Token:{result['token_count_total']}")
八、零基础避坑指南
8.1 函数的缩进错误
Python 函数的函数体必须缩进,缩进错误会导致语法错误:
# 错误:函数体没有缩进
def add(a, b):
return a + b
# 正确:函数体缩进4个空格或1个Tab
def add(a, b):
return a + b
8.2 函数的参数数量错误
调用函数时参数数量必须与函数定义的数量一致,否则会导致 TypeError:
def add(a, b):
return a + b
# 错误:参数数量不足
add(1) # TypeError: add() missing 1 required positional argument: 'b'
# 错误:参数数量过多
add(1, 2, 3) # TypeError: add() takes 2 positional arguments but 3 were given
8.3 函数的默认参数必须是不可变类型
函数的默认参数必须是不可变类型(如字符串、数字、元组),否则会出现预料之外的结果:
# 错误:默认参数是可变类型
def bad_func(items=[]):
items.append(1)
return items
print(bad_func()) # 输出:[1]
print(bad_func()) # 输出:[1, 2](不是预期的[1])
# 正确:默认参数是不可变类型
def good_func(items=None):
if items is None:
items = []
items.append(1)
return items
print(good_func()) # 输出:[1]
print(good_func()) # 输出:[1]
8.4 函数的返回值类型不一致
函数的返回值类型应该保持一致,否则会导致调用函数时出现错误:
# 错误:返回值类型不一致
def bad_func(x):
if x > 0:
return "Positive"
# 没有返回值,默认返回None
# 正确:返回值类型一致
def good_func(x):
if x > 0:
return "Positive"
else:
return "Non-positive"
九、总结:Python 函数与 LLM 开发的「对应关系」
| 函数类型 | 核心功能 | LLM 应用场景 |
|---|---|---|
| 基础函数 | 封装单一功能 | Prompt 生成、API 调用、响应解析 |
| 可变参数函数 | 接收任意数量参数 | 动态生成 Prompt、批量处理语料 |
| 装饰器函数 | 增强函数功能 | 日志记录、鉴权、缓存 |
| 高阶函数 | 函数作为参数 / 返回值 | 动态生成 LLM 函数、流程控制 |
| 模块 / 包 | 复用函数 | 多个 LLM 项目中复用同一函数 |
Python 函数是LLM 开发的基础,掌握函数的封装与复用技巧,你将能够:
- 减少重复代码,提高开发效率
- 提高代码的可读性和可维护性
- 实现 LLM 逻辑的模块化和复用
- 为后续的 LLM 项目开发打下坚实的基础
下一篇我们将学习《Python 模块与包:LLM 依赖库的管理与隔离》,讲解如何管理 LLM 开发的依赖库。