File size: 11,966 Bytes
f8d0193 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# 动作
动作,也被称为工具,提供了一套LLM驱动的智能体用来与真实世界交互并执行复杂任务的函数。
## 基本概念
### 工具 & 工具包
有两种类型的工具:
- 简单工具: 只提供一个API接口供调用。
- 工具包: 实现多个API接口,承担不同的子任务。
### 工具描述
在Lagent中,工具描述是一个刻画工具调用方式的字典,能够被LLM观察并用于决策。
对于简单工具,描述可按如下格式声明:
```python
TOOL_DESCRIPTION = {
'name': 'bold', # 工具名称
'description': 'a function used to make text bold', # 介绍工具的功能
'parameters': [ # 这个工具所需要的参数列表
{
'name': 'text', 'type': 'STRING', 'description': 'input content'
}
],
'required': ['text'], # 指定必需的参数名
}
```
在某些情况下,可能还包含 `return_data`,`parameter_description` 字段,分别描述返回内容及参数传递格式。
```{attention}
`parameter_description` 通常被动作的解析器自动插入到工具描述中,这部分将在[接口设计](#id6)中进行介绍。
```
对于工具包,描述非常相似,但嵌套了子方法
```python
TOOL_DESCRIPTION = {
'name': 'PhraseEmphasis', # 工具包的名字
'description': 'a toolkit which provides different styles of text emphasis', # 介绍工具包的功能
'api_list': [
{
'name': 'bold',
'description': 'make text bold',
'parameters': [
{
'name': 'text', 'type': 'STRING', 'description': 'input content'
}
],
'required': ['text']
},
{
'name': 'italic',
'description': 'make text italic',
'parameters': [
{
'name': 'text', 'type': 'STRING', 'description': 'input content'
}
],
'required': ['text']
}
]
}
```
## 将函数转换为工具
对于已定义好的函数,无需人工添加额外的描述。在 Lagent 中,我们提供了一个修饰器 `tool_api`,它可以通过自动解析函数的类型提示和文档字符串来生成描述字典,并将其绑定到属性 `api_description`。
```python
from lagent import tool_api
@tool_api
def bold(text: str) -> str:
"""make text bold
Args:
text (str): input text
Returns:
str: bold text
"""
return '**' + text + '**'
bold.api_description
```
```python
{'name': 'bold',
'description': 'make text bold',
'parameters': [{'name': 'text',
'type': 'STRING',
'description': 'input text'}],
'required': ['text']}
```
一旦启用 `returns_named_value`,您应当声明返回值的名称,这将被处理成一个新的字段 `return_data`:
```python
@tool_api(returns_named_value=True)
def bold(text: str) -> str:
"""make text bold
Args:
text (str): input text
Returns:
bold_text (str): bold text
"""
return '**' + text + '**'
bold.api_description
```
```python
{'name': 'bold',
'description': 'make text bold',
'parameters': [{'name': 'text',
'type': 'STRING',
'description': 'input text'}],
'required': ['text'],
'return_data': [{'name': 'bold_text',
'description': 'bold text',
'type': 'STRING'}]}
```
有时工具可能返回一个 `dict` 或 `tuple`,如果你想在 `return_data` 中详细说明每个成员的含义而不是把它们当作一个整体,设置 `explode_return=True` 并在文档字符串的 Returns 部分中罗列它们。
```python
@tool_api(explode_return=True)
def list_args(a: str, b: int, c: float = 0.0) -> dict:
"""Return arguments in dict format
Args:
a (str): a
b (int): b
c (float): c
Returns:
dict: input arguments
- a (str): a
- b (int): b
- c: c
"""
return {'a': a, 'b': b, 'c': c}
```
```python
{'name': 'list_args',
'description': 'Return arguments in dict format',
'parameters': [{'name': 'a', 'type': 'STRING', 'description': 'a'},
{'name': 'b', 'type': 'NUMBER', 'description': 'b'},
{'name': 'c', 'type': 'FLOAT', 'description': 'c'}],
'required': ['a', 'b'],
'return_data': [{'name': 'a', 'description': 'a', 'type': 'STRING'},
{'name': 'b', 'description': 'b', 'type': 'NUMBER'},
{'name': 'c', 'description': 'c'}]}
```
```{warning}
目前仅支持 Google 格式的 Python 文档字符串。
```
## 接口设计
`BaseAction(description=None, parser=JsonParser, enable=True)` 是所有动作应该继承的基类,它接收三个初始化参数:
- **description**:一个工具描述的字典,用于设置实例属性 `description`。通常不需要显式地传递这个参数,因为 `BaseAction` 的元类将查找被 `tool_api` 装饰的方法,并组装它们的 `api_description` 构造一个类属性 `__tool_description__`,如果实例化时 `description` 为空,那么该实例属性将置为 `__tool_description__`。
- **parser**:`BaseParser` 类,用于实例化一个动作解析器校验 `description` 所描述的工具的参数。例如,`JsonParser` 会要求模型在调用工具时传入一个 JSON 格式字符串或者 Python 字典,为了让 LLM 感知到该指令,它会在 `description` 中插入一个 `parameter_description` 字段。
```python
from lagent import BaseAction
action = BaseAction(
{
'name': 'bold',
'description': 'a function used to make text bold',
'parameters': [
{
'name': 'text', 'type': 'STRING', 'description': 'input content'
}
],
'required': ['text']
}
)
action.description
```
```python
{'name': 'bold',
'description': 'a function used to make text bold',
'parameters': [{'name': 'text',
'type': 'STRING',
'description': 'input content'}],
'required': ['text'],
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}
```
- **enable**: 指明该动作是否生效。
### 自定义动作
一个简单工具必须实现 `run` 方法,而工具包则应当避免将各子API名称定义为该保留字段。
```{tip}
对于非工具包的 Action,`run` 允许不被 `tool_api` 装饰,除非你想提示返回信息。
```
```python
class Bold(BaseAction):
def run(self, text: str):
"""make text bold
Args:
text (str): input text
Returns:
str: bold text
"""
return '**' + text + '**'
class PhraseEmphasis(BaseAction):
"""a toolkit which provides different styles of text emphasis"""
@tool_api
def bold(self, text):
"""make text bold
Args:
text (str): input text
Returns:
str: bold text
"""
return '**' + text + '**'
@tool_api
def italic(self, text):
"""make text italic
Args:
text (str): input text
Returns:
str: italic text
"""
return '*' + text + '*'
# 查看默认工具描述
# Bold.__tool_description__, PhraseEmphasis.__tool_description__
```
### 自动注册
任何 `BaseAction` 的子类都会自动被注册。你可以使用 `list_tools()` 和 `get_tool()` 来查看所有工具类并通过工具名进行初始化。
```python
from lagent import list_tools, get_tool
list_tools()
```
```python
['BaseAction',
'InvalidAction',
'NoAction',
'FinishAction',
'ArxivSearch',
'BINGMap',
'GoogleScholar',
'GoogleSearch',
'IPythonInterpreter',
'PPT',
'PythonInterpreter',
'Bold',
'PhraseEmphasis']
```
创建一个 `PhraseEmphasis` 对象。
```python
action = get_tool('PhraseEmphasis')
action.description
```
```python
{'name': 'PhraseEmphasis',
'description': 'a toolkit which provides different styles of text emphasis',
'api_list': [{'name': 'bold',
'description': 'make text bold',
'parameters': [{'name': 'text',
'type': 'STRING',
'description': 'input text'}],
'required': ['text'],
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'},
{'name': 'italic',
'description': 'make text italic',
'parameters': [{'name': 'text',
'type': 'STRING',
'description': 'input text'}],
'required': ['text'],
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}]}
```
## 工具调用
### 执行工具
`Action` 的 `__call__` 方法需要传入两个参数
- `inputs`: 其类型与动作绑定的 `BaseParser` 相关,通常是由大语言模型生成的字符串。
- `JsonParser`: 允许传入 JSON 格式字符串或 Python 字典。
- `TupleParser`: 允许传入字面量为元组的字符串或 Python 元组。
- `name`: 调用哪个 API,默认为 `run`。
工具会返回一个封装了调用细节的 `ActionReturn` 对象。
- `args`: 一个字典,表示该动作的入参。
- `type`: 动作名称。
- `result`: 以字典为成员的列表,每个字典包含两个键——'type' 和 'content',发生异常时该字段为 `None`。
- `errmsg`: 错误信息,默认为 `None`。
以下是一个例子:
```python
from lagent import IPythonInterpreter, TupleParser
action1 = IPythonInterpreter()
ret = action1('{"command": "import math;math.sqrt(100)"}')
print(ret.result)
ret = action1({'command': 'import math;math.sqrt(100)'})
print(ret.result)
action2 = IPythonInterpreter(parser=TupleParser)
ret = action2('("import math;math.sqrt(100)", )')
print(ret.result)
ret = action2(('import math;math.sqrt(100)',))
print(ret.result)
```
```python
[{'type': 'text', 'content': '10.0'}]
[{'type': 'text', 'content': '10.0'}]
[{'type': 'text', 'content': '10.0'}]
[{'type': 'text', 'content': '10.0'}]
```
### 动态触发
Lagent 提供 `ActionExecutor` 接口管理多个工具,它会将工具包的 `api_list` 平展并将各 API 更名为 `{tool_name}.{api_name}`。
```python
from lagent import ActionExecutor, ArxivSearch, IPythonInterpreter
executor = ActionExecutor(actions=[ArxivSearch(), IPythonInterpreter()])
executor.get_actions_info() # 该结果会作为LLM系统提示词的一部分
```
```python
[{'name': 'ArxivSearch.get_arxiv_article_information',
'description': 'Run Arxiv search and get the article meta information.',
'parameters': [{'name': 'query',
'type': 'STRING',
'description': 'the content of search query'}],
'required': ['query'],
'return_data': [{'name': 'content',
'description': 'a list of 3 arxiv search papers',
'type': 'STRING'}],
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'},
{'name': 'IPythonInterpreter',
'description': "When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 60.0 seconds. The drive at '/mnt/data' can be used to save and persist user files. Internet access for this session is disabled. Do not make external web requests or API calls as they will fail.",
'parameters': [{'name': 'command',
'type': 'STRING',
'description': 'Python code'},
{'name': 'timeout',
'type': 'NUMBER',
'description': 'Upper bound of waiting time for Python script execution.'}],
'required': ['command'],
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}]
```
通过动作执行器来触发一个工具
```python
ret = executor('IPythonInterpreter', '{"command": "import math;math.sqrt(100)"}')
ret.result
```
```python
[{'type': 'text', 'content': '10.0'}]
```
|