Skip to main content

增强推理

autogen.OpenAIWrapper 提供了 openai>=1 的增强 LLM 推理功能。 autogen.Completionopenai<1openai.Completionopenai.ChatCompletion 的替代品,用于使用 openai<1 进行增强 LLM 推理。 使用 autogen 进行推理有许多好处:性能调优、API 统一、缓存、错误处理、多配置推理、结果过滤、模板化等等。

调整推理参数(适用于 openai<1)

在此页面中找到一些示例:调整推理参数示例

优化选择

使用基础模型进行文本生成的成本通常以输入和输出中的标记数量来衡量。对于使用基础模型的应用程序构建者来说,使用案例是在推理预算约束下最大化生成文本的效用(例如,通过解决编码问题所需的平均成本)。这可以通过优化推理的超参数来实现,这些超参数可以显著影响生成文本的效用和成本。

可调整的超参数包括:

  1. model - 这是一个必需的输入,指定要使用的模型 ID。
  2. prompt/messages - 提供文本生成任务的上下文的输入提示/消息。
  3. max_tokens - 输出中要生成的标记(单词或单词片段)的最大数量。
  4. temperature - 介于 0 和 1 之间的值,用于控制生成文本的随机性。较高的温度会导致更随机和多样化的文本,而较低的温度会导致更可预测的文本。
  5. top_p - 介于 0 和 1 之间的值,用于控制每个标记生成的采样概率质量。较低的 top_p 值会使生成的文本更有可能基于最可能的标记生成,而较高的值则允许模型探索更广泛的可能标记。
  6. n - 为给定的提示生成的响应数量。生成多个响应可以提供更多样化且可能更有用的输出,但也会增加请求的成本。
  7. stop - 一个字符串列表,在生成的文本中遇到这些字符串时,将导致生成停止。这可以用于控制输出的长度或有效性。
  8. presence_penalty, frequency_penalty - 控制生成的文本中特定单词或短语的存在和频率的相对重要性的值。
  9. best_of - 在为给定的提示选择“最佳”(每个标记的对数概率最高)响应时,在服务器端生成的响应数量。

文本生成的成本和效用与这些超参数的联合效应紧密相关。 超参数的子集之间也存在复杂的相互作用。例如, 温度和top_p不建议同时更改其默认值,因为它们都控制生成文本的随机性,同时更改可能会产生冲突的效果;n和best_of很少一起调整,因为如果应用程序可以处理多个输出,在服务器端进行过滤会导致不必要的信息丢失;n和max_tokens都会影响生成的总令牌数,进而影响请求的成本。 这些相互作用和权衡使得手动确定给定文本生成任务的最佳超参数设置变得困难。

选择重要吗?请查看这篇博文,了解有关gpt-3.5-turbo和gpt-4的示例调整结果。

使用AutoGen,可以使用以下信息进行调整:

  1. 验证数据。
  2. 评估函数。
  3. 优化的度量标准。
  4. 搜索空间。
  5. 预算:推理和优化分别。

验证数据

收集一组多样化的实例。它们可以存储在一个字典的可迭代对象中。例如,每个实例字典可以包含"problem"作为键,数学问题的描述字符串作为值;"solution"作为键,解决方案字符串作为值。

评估函数

评估函数应该接受一个响应列表和其他与每个验证数据实例中的键对应的关键字参数作为输入,并输出一个度量标准字典。例如,

def eval_math_responses(responses: List[str], solution: str, **args) -> Dict:
# 从响应列表中选择一个响应
answer = voted_answer(responses)
# 检查答案是否正确
return {"success": is_equivalent(answer, solution)}

autogen.code_utilsautogen.math_utils提供了一些用于代码生成和数学问题求解的示例评估函数。

优化的度量标准

优化的度量标准通常是对所有调整数据实例进行聚合的度量标准。例如,用户可以将"success"指定为度量标准,将"max"指定为优化模式。默认情况下,聚合函数取平均值。如果需要,用户可以提供自定义的聚合函数。

搜索空间

用户可以为每个超参数指定(可选的)搜索范围。

  1. model。可以是常量字符串,也可以是由flaml.tune.choice指定的多个选择。
  2. prompt/messages。prompt可以是字符串或字符串列表,是提示模板。messages是字典列表或列表列表,是消息模板。 每个prompt/message模板将使用每个数据实例进行格式化。例如,prompt模板可以是: "{problem} 仔细解决问题。尽量简化你的答案。将最终答案放在\boxed{{}}中。" 其中{problem}将被替换为每个数据实例的"problem"字段。
  3. max_tokensnbest_of。它们可以是常数,也可以由 flaml.tune.randintflaml.tune.qrandintflaml.tune.lograndintflaml.qlograndint 指定。默认情况下,max_tokens 在 [50, 1000) 范围内搜索;n 在 [1, 100) 范围内搜索;best_of 固定为 1。
  4. stop。它可以是字符串或字符串列表,也可以是字符串列表的列表或 None。默认值为 None。
  5. temperaturetop_p。其中一个可以通过 flaml.tune.uniformflaml.tune.loguniform 等指定为常数。请不要同时提供两者。默认情况下,每个配置将在 [0, 1] 范围内均匀选择温度或 top_p。
  6. presence_penaltyfrequency_penalty。它们可以是常数,也可以由 flaml.tune.uniform 等指定。默认情况下不进行调整。

预算

可以指定推理预算和优化预算。 推理预算是每个数据实例的平均推理成本。 优化预算是调整过程中允许的总预算。两者都以美元计算,并遵循每 1000 个标记的价格。

执行调整

现在,您可以使用 autogen.Completion.tune 进行调整。例如,

import autogen

config, analysis = autogen.Completion.tune(
data=tune_data,
metric="success",
mode="max",
eval_func=eval_func,
inference_budget=0.05,
optimization_budget=3,
num_samples=-1,
)

num_samples 是要采样的配置数量。-1 表示无限制(直到优化预算用尽)。 返回的 config 包含优化的配置,analysis 包含一个 ExperimentAnalysis 对象,其中包含所有尝试的配置和结果。

调整后的配置可用于执行推理。

API 统一化

autogen.OpenAIWrapper.create() 可用于为聊天模型和非聊天模型创建补全,以及 OpenAI API 和 Azure OpenAI API。

from autogen import OpenAIWrapper
# OpenAI 端点
client = OpenAIWrapper()
# ChatCompletion
response = client.create(messages=[{"role": "user", "content": "2+2="}], model="gpt-3.5-turbo")
# 提取响应文本
print(client.extract_text_or_completion_object(response))
# 获取此补全的成本
print(response.cost)
# Azure OpenAI 端点
client = OpenAIWrapper(api_key=..., base_url=..., api_version=..., api_type="azure")
# Completion
response = client.create(prompt="2+2=", model="gpt-3.5-turbo-instruct")
# 提取响应文本
print(client.extract_text_or_completion_object(response))

对于本地 LLM,可以使用类似 FastChat 的软件包启动一个端点,然后使用相同的 API 发送请求。有关如何使用本地 LLM 进行推理的示例,请参见此处

对于自定义模型客户端,可以使用 autogen.OpenAIWrapper.register_model_client 注册客户端,然后使用相同的 API 发送请求。有关如何使用自定义模型客户端进行推理的示例,请参见此处

使用摘要

autogen 中的 OpenAIWrapper 跟踪您的 API 调用的令牌计数和成本。使用 create() 方法发起请求,并使用 print_usage_summary() 获取详细的使用报告,包括缓存和实际请求的总成本和令牌使用情况。

  • mode=["actual", "total"](默认):打印所有完成和非缓存完成的使用摘要。
  • mode='actual':仅打印非缓存使用情况。
  • mode='total':仅打印所有使用情况(包括缓存)。

需要时,使用 clear_usage_summary() 重置会话的使用数据。查看笔记本

示例用法:

from autogen import OpenAIWrapper

client = OpenAIWrapper()
client.create(messages=[{"role": "user", "content": "Python 学习技巧。"}], model="gpt-3.5-turbo")
client.print_usage_summary() # 显示使用情况
client.clear_usage_summary() # 重置使用数据

示例输出:

不包括缓存使用情况的使用摘要:
总成本:0.00015
* 模型 'gpt-3.5-turbo':成本:0.00015,提示令牌:25,完成令牌:58,总令牌:83

包括缓存使用情况的使用摘要:
总成本:0.00027
* 模型 'gpt-3.5-turbo':成本:0.00027,提示令牌:50,完成令牌:100,总令牌:150

注意:如果使用自定义模型客户端(详见此处),并且如果未实现使用摘要,则使用摘要将不可用。

缓存

已移至此处

错误处理

运行时错误

可以传递一个包含不同模型/端点配置的列表来减轻速率限制和其他运行时错误。例如,

client = OpenAIWrapper(
config_list=[
{
"model": "gpt-4",
"api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
"api_type": "azure",
"base_url": os.environ.get("AZURE_OPENAI_API_BASE"),
"api_version": "2024-02-01",
},
{
"model": "gpt-3.5-turbo",
"api_key": os.environ.get("OPENAI_API_KEY"),
"base_url": "https://api.openai.com/v1",
},
{
"model": "llama2-chat-7B",
"base_url": "http://127.0.0.1:8080",
},
{
"model": "microsoft/phi-2",
"model_client_cls": "CustomModelClient"
}
],
)

client.create() 将依次尝试查询 Azure OpenAI gpt-4、OpenAI gpt-3.5-turbo、本地托管的 llama2-chat-7B 和使用名为 CustomModelClient 的自定义模型客户端类的 phi-2,直到返回有效结果。这可以加快开发过程,其中速率限制是瓶颈。如果最后一个选择失败,将引发错误。因此,请确保列表中的最后一个选择具有最佳的可用性。

为方便起见,我们提供了一些实用函数来加载配置列表。

  • get_config_list: 从提供的 API 密钥生成 API 调用的配置。
  • config_list_openai_aoai: 使用 Azure OpenAI 和 OpenAI 端点构建配置列表,从环境变量或本地文件中获取 API 密钥。
  • config_list_from_json: 从 JSON 结构中加载配置,可以从环境变量或本地 JSON 文件中加载,并根据给定的条件灵活过滤配置。
  • config_list_from_models: 根据提供的模型列表创建配置,可用于针对特定模型而无需手动指定每个配置。
  • config_list_from_dotenv: 从 .env 文件构建配置列表,提供了一种从单个文件中管理多个 API 配置和密钥的集中方式。

我们建议您查看这个 notebook 来获取完整的代码示例,了解配置模型端点的不同方法。

逻辑错误

另一种错误类型是返回的响应不满足要求。例如,如果要求响应是有效的 JSON 字符串,那么希望过滤掉不是 JSON 的响应。可以通过提供配置列表和过滤函数来实现。例如,

def valid_json_filter(response, **_):
for text in OpenAIWrapper.extract_text_or_completion_object(response):
try:
json.loads(text)
return True
except ValueError:
pass
return False

client = OpenAIWrapper(
config_list=[{"model": "text-ada-001"}, {"model": "gpt-3.5-turbo-instruct"}, {"model": "text-davinci-003"}],
)
response = client.create(
prompt="如何构造一个 JSON 请求到必应 API 来搜索“最新的人工智能新闻”?返回 JSON 请求。",
filter_func=valid_json_filter,
)

上面的示例将尝试迭代使用 text-ada-001、gpt-3.5-turbo-instruct 和 text-davinci-003,直到返回一个有效的 JSON 字符串或使用最后一个配置。还可以在列表中多次重复相同的模型(使用不同的种子)来多次尝试一个模型,以增加最终响应的鲁棒性。

高级用例:查看这篇 博文 了解如何将 GPT-4 的编码性能从 68% 提高到 90%,同时降低推理成本。

模板化

如果提供的提示或消息是一个模板,它将自动使用给定的上下文进行实例化。例如,

response = client.create(
context={"problem": "不超过 100 的正整数中,有多少个是 2 或 3 的倍数但不是 4 的倍数?"},
prompt="{problem} 仔细解决这个问题。",
allow_format_str_template=True,
**config
)

模板可以是格式化字符串,如上面的示例,也可以是一个函数,该函数从多个输入字段生成一个字符串,如下面的示例。

def content(turn, context):
return "\n".join(
[
context[f"user_message_{turn}"],
context[f"external_info_{turn}"]
]
)

messages = [
{
"role": "system",
"content": "你是一名数学助教。",
},
{
"role": "user",
"content": partial(content, turn=0),
},
]
context = {
"user_message_0": "你能解释一下问题1的解法吗?",
"external_info_0": "问题1:...",
}

response = client.create(context=context, messages=messages, **config)
messages.append(
{
"role": "assistant",
"content": client.extract_text(response)[0]
}
)
messages.append(
{
"role": "user",
"content": partial(content, turn=1),
},
)
context.append(
{
"user_message_1": "为什么我们不能将定理1应用于方程(2)?",
"external_info_1": "定理1:...",
}
)
response = client.create(context=context, messages=messages, **config)

日志记录

在调试或诊断基于LLM的系统时,通常可以通过记录API调用并对其进行分析来方便地进行操作。

对于openai >= 1

日志记录示例:查看笔记本

开始记录日志:

import autogen.runtime_logging

autogen.runtime_logging.start(logger_type="sqlite", config={"dbname": "YOUR_DB_NAME"})

logger_typeconfig 都是可选的。默认的日志记录器类型是SQLite记录器,这是目前在autogen中唯一可用的记录器。如果您想自定义数据库名称,可以通过config传递,默认为logs.db

停止记录日志:

autogen.runtime_logging.stop()

LLM 运行

AutoGen 日志记录支持OpenAI的llm消息模式。每个LLM运行都保存在chat_completions表中,包括:

  • session_id:用于日志记录会话的唯一标识符
  • invocation_id:用于日志记录记录的唯一标识符
  • client_id:用于Azure OpenAI/OpenAI客户端的唯一标识符
  • request:详细的llm请求,下面是一个示例
  • response:详细的llm响应,下面是一个示例
  • cost:请求和响应的总成本
  • start_time
  • end_time
示例请求
{
"messages":[
{
"content":"system_message_1",
"role":"system"
},
{
"content":"user_message_1",
"role":"user"
}
],
"model":"gpt-4",
"temperature": 0.9
}
示例响应
{
"id": "id_1",
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"message": {
"content": "assistant_message_1",
"role": "assistant",
"function_call": null,
"tool_calls": null
}
}
],
"created": "<timestamp>",
"model": "gpt-4",
"object": "chat.completion",
"system_fingerprint": null,
"usage": {
"completion_tokens": 155,
"prompt_tokens": 53,
"total_tokens": 208
}
}

了解更多关于请求和响应格式

对于openai < 1

autogen.Completionautogen.ChatCompletion 提供了一种简单的方式来收集API调用历史记录。例如,要记录聊天历史记录,只需运行:

autogen.ChatCompletion.start_logging()

此后进行的API调用将自动记录。可以随时通过以下方式检索它们:

autogen.ChatCompletion.logged_history

有一个函数可以用来打印使用摘要(总成本和每个模型的令牌计数使用):

autogen.ChatCompletion.print_usage_summary()

要停止记录日志,请使用

autogen.ChatCompletion.stop_logging()

如果想将历史记录追加到现有字典中,请传递该字典,例如:

autogen.ChatCompletion.start_logging(history_dict=existing_history_dict)

默认情况下,API调用计数器将在start_logging()时重置。如果不想重置,请将reset_counter=False

有两种日志格式:紧凑日志和单独的API调用日志。默认格式是紧凑的。在start_logging()中设置compact=False以切换格式。

  • 紧凑日志的历史字典示例。
{
"""
[
{
'role': 'system',
'content': system_message,
},
{
'role': 'user',
'content': user_message_1,
},
{
'role': 'assistant',
'content': assistant_message_1,
},
{
'role': 'user',
'content': user_message_2,
},
{
'role': 'assistant',
'content': assistant_message_2,
},
]""": {
"created_at": [0, 1],
"cost": [0.1, 0.2],
}
}
  • 单独的API调用日志的历史字典示例。
{
0: {
"request": {
"messages": [
{
"role": "system",
"content": system_message,
},
{
"role": "user",
"content": user_message_1,
}
],
... # 请求中的其他参数
},
"response": {
"choices": [
"messages": {
"role": "assistant",
"content": assistant_message_1,
},
],
... # 响应中的其他字段
}
},
1: {
"request": {
"messages": [
{
"role": "system",
"content": system_message,
},
{
"role": "user",
"content": user_message_1,
},
{
"role": "assistant",
"content": assistant_message_1,
},
{
"role": "user",
"content": user_message_2,
},
],
... # 请求中的其他参数
},
"response": {
"choices": [
"messages": {
"role": "assistant",
"content": assistant_message_2,
},
],
... # 响应中的其他字段
}
},
}
  • 用于使用摘要的打印示例
总成本: <cost>
模型 <model> 的令牌计数摘要:提示令牌:<count 1>,完成令牌:<count 2>,总令牌:<count 3>

可以看出,单独的API调用历史包含了对话的冗余信息。对于长对话,冗余程度较高。紧凑的历史更高效,而单独的API调用历史包含更多细节。