封装应用程序的原生 API
UFO 基于 UI 控件对应用程序执行操作,但为其工具箱提供原生 API 可以提高操作的效率和准确性。本文档提供了如何将应用程序的原生 API 封装到 UFO 工具箱中的指导。
如何封装应用程序的原生 API?
在开发原生 API 封装器之前,我们强烈建议您阅读 Automator 的设计。
步骤 1:为原生 API 创建一个接收器
Receiver
是一个接收 AppAgent
发来的原生 API 调用并执行它们的类。要封装应用程序的原生 API,您需要创建一个 Receiver
类,其中包含执行原生 API 调用的方法。
要创建 Receiver
类,请遵循以下步骤:
1. 为您的应用程序创建一个文件夹
- 导航到
ufo/automator/app_api/
目录。 - 创建一个以您的应用程序命名的文件夹。
2. 创建一个 Python 文件
- 在您刚创建的文件夹中,添加一个以您的应用程序命名的 Python 文件,例如
{your_application}_client.py
。
3. 定义接收器类
- 在 Python 文件中,定义一个名为
{Your_Receiver}
的类,继承自ufo/automator/basic.py
中的ReceiverBasic
类。 - 使用执行原生 API 调用的对象初始化
Your_Receiver
类。例如,如果您的 API 基于com
对象,请在Your_Receiver
类的__init__
方法中初始化com
对象。
WinCOMReceiverBasic
类的示例
class WinCOMReceiverBasic(ReceiverBasic):
"""
The base class for Windows COM client.
"""
_command_registry: Dict[str, Type[CommandBasic]] = {}
def __init__(self, app_root_name: str, process_name: str, clsid: str) -> None:
"""
Initialize the Windows COM client.
:param app_root_name: The app root name.
:param process_name: The process name.
:param clsid: The CLSID of the COM object.
"""
self.app_root_name = app_root_name
self.process_name = process_name
self.clsid = clsid
self.client = win32com.client.Dispatch(self.clsid)
self.com_object = self.get_object_from_process_name()
4. 定义执行原生 API 调用的方法
- 在
Your_Receiver
类中定义方法以执行原生 API 调用。
ExcelWinCOMReceiver
类的示例
def table2markdown(self, sheet_name: str) -> str:
"""
Convert the table in the sheet to a markdown table string.
:param sheet_name: The sheet name.
:return: The markdown table string.
"""
sheet = self.com_object.Sheets(sheet_name)
data = sheet.UsedRange()
df = pd.DataFrame(data[1:], columns=data[0])
df = df.dropna(axis=0, how="all")
df = df.applymap(self.format_value)
return df.to_markdown(index=False)
5. 创建一个工厂类
- 创建您的 Factory 类,继承自
APIReceiverFactory
类,以管理共享相同 API 类型的多个Receiver
类。 - 在
ReceiverFactory
类中实现create_receiver
和name
方法。create_receiver
方法应返回Receiver
类。 - 默认情况下,
create_receiver
将app_root_name
和process_name
作为参数,并返回Receiver
类。 - 使用装饰器
@ReceiverManager.register
注册ReceiverFactory
类。
COMReceiverFactory
类的示例
from ufo.automator.puppeteer import ReceiverManager
@ReceiverManager.register
class COMReceiverFactory(APIReceiverFactory):
"""
The factory class for the COM receiver.
"""
def create_receiver(self, app_root_name: str, process_name: str) -> WinCOMReceiverBasic:
"""
Create the wincom receiver.
:param app_root_name: The app root name.
:param process_name: The process name.
:return: The receiver.
"""
com_receiver = self.__com_client_mapper(app_root_name)
clsid = self.__app_root_mappping(app_root_name)
if clsid is None or com_receiver is None:
# print_with_color(f"Warning: Win32COM API is not supported for {process_name}.", "yellow")
return None
return com_receiver(app_root_name, process_name, clsid)
@classmethod
def name(cls) -> str:
"""
Get the name of the receiver factory.
:return: The name of the receiver factory.
"""
return "COM"
注意
如果不支持该应用程序,create_receiver
方法应返回 None
。
注意
您必须使用装饰器 @ReceiverManager.register
注册您的 ReceiverFactory
,以便 ReceiverManager
管理 ReceiverFactory
。
Receiver
类现在已准备好接收来自 AppAgent
的原生 API 调用。
步骤 2:为原生 API 创建一个命令
命令是 AppAgent
可以在应用程序上执行的操作。要为原生 API 创建命令,您需要创建一个 Command
类,其中包含执行原生 API 调用的方法。
1. 创建一个命令类
- 在
Receiver
类所在的同一个 Python 文件中创建一个Command
类。Command
类应继承自ufo/automator/basic.py
中的CommandBasic
类。
示例
class WinCOMCommand(CommandBasic):
"""
The abstract command interface.
"""
def __init__(self, receiver: WinCOMReceiverBasic, params=None) -> None:
"""
Initialize the command.
:param receiver: The receiver of the command.
"""
self.receiver = receiver
self.params = params if params is not None else {}
@abstractmethod
def execute(self):
pass
@classmethod
def name(cls) -> str:
"""
Get the name of the command.
:return: The name of the command.
"""
return cls.__name__
2. 定义执行方法
- 在
Command
类中定义execute
方法,以调用接收器执行原生 API 调用。
示例
def execute(self):
"""
Execute the command to insert a table.
:return: The inserted table.
"""
return self.receiver.insert_excel_table(
sheet_name=self.params.get("sheet_name", 1),
table=self.params.get("table"),
start_row=self.params.get("start_row", 1),
start_col=self.params.get("start_col", 1),
)
3. 注册命令类
- 使用
@your_receiver.register
装饰器在相应的Receiver
类中注册Command
类。
示例
@ExcelWinCOMReceiver.register
class InsertExcelTable(WinCOMCommand):
...
Command
类现在已在 Receiver
类中注册,可供 AppAgent
执行原生 API 调用。
步骤 3:为原生 API 提供提示描述
为了让 AppAgent
知道原生 API 调用的用法,您需要提供提示描述。
1. 创建一个 api.yaml 文件
- Create an `api.yaml` file in the `ufo/prompts/apps/{your_app_name}` directory.
2. 定义提示描述
- 在
api.yaml
文件中定义原生 API 调用的提示描述。
示例
table2markdown:
summary: |-
"table2markdown" is to get the table content in a sheet of the Excel app and convert it to markdown format.
class_name: |-
GetSheetContent
usage: |-
[1] API call: table2markdown(sheet_name: str)
[2] Args:
- sheet_name: The name of the sheet in the Excel app.
[3] Example: table2markdown(sheet_name="Sheet1")
[4] Available control item: Any control item in the Excel app.
[5] Return: the markdown format string of the table content of the sheet.
注意
table2markdown
是原生 API 调用的名称。它 必须
与相应的 Command
类中定义的 name()
匹配!
3. 在 config_dev.yaml
中注册提示地址
- 通过将应用程序程序名称作为键,提示文件地址作为值,将其添加到
config_dev.yaml
文件的APP_API_PROMPT_ADDRESS
字段中来注册提示地址。
示例
APP_API_PROMPT_ADDRESS: {
"WINWORD.EXE": "ufo/prompts/apps/word/api.yaml",
"EXCEL.EXE": "ufo/prompts/apps/excel/api.yaml",
"msedge.exe": "ufo/prompts/apps/web/api.yaml",
"chrome.exe": "ufo/prompts/apps/web/api.yaml"
"your_application_program_name": "YOUR_APPLICATION_API_PROMPT"
}
注意
your_application_program_name
必须与应用程序程序名称匹配。
AppAgent
现在可以使用提示描述来理解原生 API 调用的用法。
通过遵循这些步骤,您将成功将应用程序的原生 API 封装到 UFO 的工具箱中,从而允许 AppAgent
在应用程序上执行原生 API 调用!