使用 OpenAI SDK进行实时化学信息分子分析
借助OpenAI库构建动态查询化学信息的功能
前言
本文属于搬运内容分享,文章末尾附有原文链接。 本文提供了一种使用OpenAI库的思路将化学信息领域的工作流集成,实现客制化操作。其中封装一定的工作流函数以便利化学信息方向的工作需求。更多是提供一种思路,现在主流本地运行大模型框架如 Ollama,vLLM均支持 openai库,所以大家可以尝试更多的开源模型构建属于自己的工作流。
原作者:Abhik Seal
概览流程图

使用 OpenAI 的函数调用
函数调用旨在让模型(例如 OpenAI 提供的模型)根据用户查询动态执行特定函数。这种方法有几个显著优势:
- 实时数据访问:该功能克服了知识截止的限制,实现实时动态数据检索和处理,确保 AI 的响应是最新且相关的。
- 增强的 API 集成:它支持与内部和外部 API 的交互,实现跨不同软件系统的无缝信息交换和功能集成。
- 个性化互动:函数调用可以通过访问和分析用户数据、偏好和历史记录,提供个性化的响应,带来更具针对性和意义的互动。
- 高级任务自动化:它可以自动执行复杂或重复性任务,使 AI 成为优化工作流和操作的宝贵工具。
- 逻辑封装:函数封装特定逻辑和操作,使代码更具模块化、易维护性和可复用性。
接下来我们来看如何在构建分子对话系统时实现这一点。
我定义了两个函数 get_smiles_from_common_name 和 compute_physchem_properties。实现的目标是输入药物或化合物的通用名称,从 PubChem API 获取其 SMILES 表示式,然后计算某些性质并输出结果给用户。这里不要求 OpenAI 进行计算,而是系统在本地动态完成,这样便可以编写一组函数库以利用其他函数。
import warnings
import json
import requests
from rdkit import Chem
from rdkit.Chem import Descriptors
from openai import OpenAI
# 忽略所有警告
warnings.filterwarnings("ignore")
client = openai.OpenAI(api_key=key)
def get_smiles_from_common_name(common_name):
"""使用通用名称从 PubChem 获取 SMILES"""
base_url = f"https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/{common_name}/property/CanonicalSMILES/JSON"
response = requests.get(base_url)
if response.status_code == 200:
data = response.json()
smiles = data['PropertyTable']['Properties'][0]['CanonicalSMILES']
return json.dumps({"smiles": smiles})
else:
return json.dumps({"error": "SMILES not found"})
def compute_physchem_properties(smiles):
"""使用 RDKit 计算基本物理化学性质"""
mol = Chem.MolFromSmiles(smiles)
properties = {
"MolecularWeight": Descriptors.MolWt(mol),
"LogP": Descriptors.MolLogP(mol),
"NumHDonors": Descriptors.NumHDonors(mol),
"NumHAcceptors": Descriptors.NumHAcceptors(mol)
}
return json.dumps(properties)
def compare_properties(properties_list):
"""比较多个分子的性质"""
comparison = {}
for prop in ["MolecularWeight", "LogP", "NumHDonors", "NumHAcceptors"]:
comparison[prop] = {item["compound"]: item["properties"][prop] for item in properties_list}
return json.dumps(comparison, indent=2)
核心代码部分
下面是代码的核心部分。我们定义了 run_conversation 函数,脚本定义了 get_smiles_from_common_name 和 compute_physchem_properties 函数,并指定为模型可调用的工具。run_conversation 函数设置了模型使用的消息和工具。OpenAI API 调用使用 client.chat.completions.create 并启用函数调用。模型根据对话动态决定何时调用特定函数(如 get_smiles_from_common_name 和 compute_physchem_properties)。响应处理动态处理函数调用并处理其输出。这是一个相当酷的实用功能。
def run_conversation(content):
messages = [{"role": "user", "content": content}]
tools = [
{
"name": "get_smiles_from_common_name",
"description": "使用通用名称从 PubChem 获取 SMILES",
"parameters": {
"type": "object",
"properties": {
"common_name": {
"type": "string",
"description": "分子的通用名称",
}
},
"required": ["common_name"],
},
},
{
"name": "compute_physchem_properties",
"description": "从 SMILES 计算基本物理化学性质",
"parameters": {
"type": "object",
"properties": {
"smiles": {
"type": "string",
"description": "分子的 SMILES 表示式",
}
},
"required": ["smiles"],
},
},
{
"name": "compare_properties",
"description": "比较多个分子的性质",
"parameters": {
"type": "object",
"properties": {
"properties_list": {
"type": "array",
"items": {
"type": "object",
"properties": {
"compound": {"type": "string"},
"properties": {"type": "object"}
}
}
}
},
"required": ["properties_list"],
},
}
]
response = client.chat.completions.create(
model=config['openai']['model'],
messages=messages,
functions=tools,
function_call="auto",
temperature=0.1
)
response_message = response.choices[0].message
if hasattr(response_message, 'function_call') and response_message.function_call:
tool_call = response_message.function_call
messages.append(response_message)
available_functions = {
"get_smiles_from_common_name": get_smiles_from_common_name,
"compute_physchem_properties": compute_physchem_properties,
"compare_properties": compare_properties
}
function_name = tool_call.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.arguments)
function_response = function_to_call(**function_args)
messages.append({
"role": "assistant",
"name": function_name,
"content": function_response,
})
if function_name == "get_smiles_from_common_name":
response_dict = json.loads(function_response)
if "smiles" in response_dict:
smiles = response_dict["smiles"]
properties = compute_physchem_properties(smiles)
messages.append({
"role": "assistant",
"name": "compute_physchem_properties",
"content": json.dumps(properties),
})
second_response = client.chat.completions.create(
model=config['openai']['model'],
messages=messages,
stream=True,
temperature=config['openai']['temperature']
)
return second_response
if __name__ == "__main__":
while True:
next_question = input("输入你的问题(或键入 'exit' 退出):")
if next_question.lower() == 'exit':
break
response = run_conversation(next_question)
for chunk in response:
if hasattr(chunk.choices[0], 'delta'):
content = getattr(chunk.choices[0].delta, 'content', '')
if content:
print(content, end='', flush=True)
第二个响应使 AI 提供更详细和上下文相关的响应,增强用户互动体验。此代码将函数调用结果集成到最终用户响应中,确保任何通过函数获取或计算的数据都包含在回复中。该代码可用于单个分子、多个分子及其性质比较。下面是一些示例问题和对话。
- 计算 Navitoclax、Venetoclax 和 Aspirin 的性质。

- 比较 Navitoclax 和 Venetoclax 的差异。
