rootdriver 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rootdriver-0.2.0/rootdriver.egg-info → rootdriver-0.3.0}/PKG-INFO +25 -1
- rootdriver-0.2.0/PKG-INFO → rootdriver-0.3.0/README.md +122 -111
- {rootdriver-0.2.0 → rootdriver-0.3.0}/pyproject.toml +4 -1
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/__init__.py +1 -1
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/agent.py +13 -2
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/engine.py +70 -15
- rootdriver-0.2.0/README.md → rootdriver-0.3.0/rootdriver.egg-info/PKG-INFO +135 -98
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver.egg-info/SOURCES.txt +2 -1
- rootdriver-0.3.0/tests/test_tool_async.py +60 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/constants.py +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/conversation.py +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/exception.py +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver/state.py +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver.egg-info/dependency_links.txt +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver.egg-info/requires.txt +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/rootdriver.egg-info/top_level.txt +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/setup.cfg +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/tests/test_base_tool.py +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/tests/test_conversation.py +0 -0
- {rootdriver-0.2.0 → rootdriver-0.3.0}/tests/test_tool.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootdriver
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Rooted in Origin, Driving All Things
|
|
5
5
|
Author-email: zimvir <zimvir@qq.com>
|
|
6
6
|
License: MIT
|
|
@@ -24,6 +24,7 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
|
24
24
|
- **工具调用**:支持 function calling,自动执行工具并返回结果
|
|
25
25
|
- **状态管理**:支持内存和数据库持久化检查点
|
|
26
26
|
- **异常体系**:完整的异常类层次结构
|
|
27
|
+
- **异步支持**:全面异步支持,并发多 Agent / 工具调用
|
|
27
28
|
|
|
28
29
|
## 安装
|
|
29
30
|
|
|
@@ -74,6 +75,29 @@ response = agent.react("帮我查下上海天气")
|
|
|
74
75
|
print(response)
|
|
75
76
|
```
|
|
76
77
|
|
|
78
|
+
### 异步用法
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import asyncio
|
|
82
|
+
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
83
|
+
|
|
84
|
+
async def main():
|
|
85
|
+
agent = Agent(agent_llm=AgentLLM(...))
|
|
86
|
+
|
|
87
|
+
# 单次异步对话
|
|
88
|
+
response = await agent.atalk("你好")
|
|
89
|
+
print(response)
|
|
90
|
+
|
|
91
|
+
# 并发多个 Agent
|
|
92
|
+
results = await asyncio.gather(
|
|
93
|
+
agent.areact("问题1"),
|
|
94
|
+
agent.areact("问题2"),
|
|
95
|
+
agent.areact("问题3"),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
asyncio.run(main())
|
|
99
|
+
```
|
|
100
|
+
|
|
77
101
|
## 核心组件
|
|
78
102
|
|
|
79
103
|
| 组件 | 说明 |
|
|
@@ -1,111 +1,122 @@
|
|
|
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
|
-
|
|
1
|
+
# RootDriver
|
|
2
|
+
|
|
3
|
+
根源出发,驱动万物
|
|
4
|
+
|
|
5
|
+
一个轻量级的 Python AI Agent 开发框架,支持单智能体和多智能体应用。
|
|
6
|
+
|
|
7
|
+
## 特性
|
|
8
|
+
|
|
9
|
+
- **简洁易用**:装饰器方式定义工具,快速构建 Agent
|
|
10
|
+
- **模块化设计**:LLM 适配器、工具系统、会话管理解耦
|
|
11
|
+
- **工具调用**:支持 function calling,自动执行工具并返回结果
|
|
12
|
+
- **状态管理**:支持内存和数据库持久化检查点
|
|
13
|
+
- **异常体系**:完整的异常类层次结构
|
|
14
|
+
- **异步支持**:全面异步支持,并发多 Agent / 工具调用
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install rootdriver
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 快速开始
|
|
23
|
+
|
|
24
|
+
### 定义工具
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from rootdriver import tool
|
|
28
|
+
|
|
29
|
+
@tool
|
|
30
|
+
def get_weather(city: str) -> str:
|
|
31
|
+
"""获取城市天气"""
|
|
32
|
+
return f"{city} 晴天"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 创建 Agent
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
39
|
+
|
|
40
|
+
agent = Agent(
|
|
41
|
+
agent_llm=AgentLLM(
|
|
42
|
+
adapter=OpenAIAdapter(
|
|
43
|
+
api_key="YOUR_API_KEY",
|
|
44
|
+
base_url="BASE_URL"
|
|
45
|
+
),
|
|
46
|
+
model="gpt-4",
|
|
47
|
+
),
|
|
48
|
+
tools=[get_weather],
|
|
49
|
+
system_prompt="你是一个有用的助手",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# 单次对话
|
|
53
|
+
response = agent.talk("北京天气怎么样?")
|
|
54
|
+
print(response)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 使用工具
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# 完整对话循环(包含工具调用)
|
|
61
|
+
response = agent.react("帮我查下上海天气")
|
|
62
|
+
print(response)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 异步用法
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import asyncio
|
|
69
|
+
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
70
|
+
|
|
71
|
+
async def main():
|
|
72
|
+
agent = Agent(agent_llm=AgentLLM(...))
|
|
73
|
+
|
|
74
|
+
# 单次异步对话
|
|
75
|
+
response = await agent.atalk("你好")
|
|
76
|
+
print(response)
|
|
77
|
+
|
|
78
|
+
# 并发多个 Agent
|
|
79
|
+
results = await asyncio.gather(
|
|
80
|
+
agent.areact("问题1"),
|
|
81
|
+
agent.areact("问题2"),
|
|
82
|
+
agent.areact("问题3"),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
asyncio.run(main())
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 核心组件
|
|
89
|
+
|
|
90
|
+
| 组件 | 说明 |
|
|
91
|
+
|------|------|
|
|
92
|
+
| `Agent` | 智能体入口,整合 LLM、工具、会话 |
|
|
93
|
+
| `Engine` | 核心引擎,处理对话循环和工具调用 |
|
|
94
|
+
| `Conversation` | 会话管理,维护消息历史 |
|
|
95
|
+
| `LLM` | LLM 调用封装 |
|
|
96
|
+
| `Tool` | 工具集合,管理所有可调用工具 |
|
|
97
|
+
| `State` | 状态管理,支持检查点和持久化 |
|
|
98
|
+
|
|
99
|
+
## 项目结构
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
rootdriver/
|
|
103
|
+
├── agent.py # Agent 智能体
|
|
104
|
+
├── engine.py # 引擎核心
|
|
105
|
+
├── conversation.py # 对话管理
|
|
106
|
+
├── state.py # 状态管理
|
|
107
|
+
├── exception.py # 异常定义
|
|
108
|
+
├── llm/
|
|
109
|
+
│ ├── llm.py # LLM 封装
|
|
110
|
+
│ ├── base_adapter.py # 适配器基类
|
|
111
|
+
│ └── adapter/
|
|
112
|
+
│ └── openai_adapter.py # OpenAI 适配器
|
|
113
|
+
├── tool/
|
|
114
|
+
│ ├── base_tool.py # 工具基类
|
|
115
|
+
│ └── tools.py # 工具集
|
|
116
|
+
├── types/ # 类型定义
|
|
117
|
+
└── utils/ # 工具函数
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rootdriver"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Rooted in Origin, Driving All Things"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -22,5 +22,8 @@ dev = [
|
|
|
22
22
|
"pytest>=7.0.0",
|
|
23
23
|
]
|
|
24
24
|
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
asyncio_mode = "auto"
|
|
27
|
+
|
|
25
28
|
[tool.setuptools]
|
|
26
29
|
packages = ["rootdriver"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from uuid import uuid4
|
|
3
|
-
|
|
3
|
+
import asyncio
|
|
4
4
|
from .engine import Engine
|
|
5
5
|
from .llm import LLM
|
|
6
6
|
from .conversation import Conversation
|
|
@@ -19,7 +19,7 @@ class Agent:
|
|
|
19
19
|
system_prompt: str | None = None,
|
|
20
20
|
|
|
21
21
|
db_path: str| None = None,
|
|
22
|
-
#
|
|
22
|
+
# llm_retry: int = 3,
|
|
23
23
|
# timeout: float | None = None,
|
|
24
24
|
):
|
|
25
25
|
self.id = id if id else uuid4().hex
|
|
@@ -47,4 +47,15 @@ class Agent:
|
|
|
47
47
|
return self.engine.invoke(build_user_message(input_prompt)).content
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
"""==========异步部分=========="""
|
|
51
|
+
async def areact(self, input_prompt:str) -> "str":
|
|
52
|
+
"""一次 react 异步循环"""
|
|
53
|
+
response = await self.engine.arun(build_user_message(input_prompt))
|
|
54
|
+
return response.content
|
|
55
|
+
|
|
56
|
+
async def atalk(self, input_prompt:str) -> "str":
|
|
57
|
+
"""一次 agent 异步调用"""
|
|
58
|
+
return await self.engine.ainvoke(build_user_message(input_prompt)).content
|
|
59
|
+
|
|
60
|
+
|
|
50
61
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import asyncio
|
|
2
2
|
|
|
3
3
|
from .llm import LLM
|
|
4
4
|
from .conversation import Conversation
|
|
@@ -14,18 +14,16 @@ class Engine:
|
|
|
14
14
|
self.model = model
|
|
15
15
|
|
|
16
16
|
def invoke(self, input_message:Message) -> Message:
|
|
17
|
+
"""但系调用llm,并存入记忆,无tool调用"""
|
|
17
18
|
self.conversation.append(input_message)
|
|
18
19
|
message = self.chat()
|
|
19
20
|
return message
|
|
20
21
|
|
|
21
22
|
def chat(self) -> Message:
|
|
22
23
|
"""
|
|
23
|
-
|
|
24
24
|
1. 读会话消息
|
|
25
25
|
2. 大模型交流
|
|
26
26
|
3. 追加会话消息
|
|
27
|
-
4. 判断 是否调用工具
|
|
28
|
-
5. 是: tool 返回结果并追加,否: 追加并返回
|
|
29
27
|
"""
|
|
30
28
|
# 1. 读会话消息
|
|
31
29
|
message = self.conversation.get_messages()
|
|
@@ -41,17 +39,9 @@ class Engine:
|
|
|
41
39
|
|
|
42
40
|
def deal_tool_or_output(self, response_message:Message) -> Message|None:
|
|
43
41
|
"""
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
4. 追加会话消息
|
|
48
|
-
5. 判断 是否调用工具
|
|
49
|
-
6. 是: tool 返回结果并追加,否: 追加并返回
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
|
|
54
|
-
"""
|
|
42
|
+
5. 判断 是否调用工具
|
|
43
|
+
6. 是: tool 返回结果并追加,否: 追加并返回
|
|
44
|
+
"""
|
|
55
45
|
|
|
56
46
|
# 5. 判断 是否调用工具
|
|
57
47
|
if response_message.tool_calls:
|
|
@@ -106,3 +96,68 @@ class Engine:
|
|
|
106
96
|
"""把 LLM 响应转成 Message 。"""
|
|
107
97
|
return response.message
|
|
108
98
|
|
|
99
|
+
"""==========异步部分=========="""
|
|
100
|
+
async def ainvoke(self, input_message:Message) -> Message:
|
|
101
|
+
"""但系调用llm,并存入记忆,无tool调用"""
|
|
102
|
+
self.conversation.append(input_message)
|
|
103
|
+
message = await self.achat()
|
|
104
|
+
return message
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def achat(self) -> Message:
|
|
108
|
+
"""
|
|
109
|
+
1. 读会话消息
|
|
110
|
+
2. 大模型交流
|
|
111
|
+
3. 追加会话消息
|
|
112
|
+
"""
|
|
113
|
+
# 1. 读会话消息
|
|
114
|
+
message = self.conversation.get_messages()
|
|
115
|
+
|
|
116
|
+
# 2. llm交互
|
|
117
|
+
response = await self.llm.ainvoke(self.messages_to_llm_request(message))
|
|
118
|
+
|
|
119
|
+
# 3. 追加会话消息
|
|
120
|
+
response_message = self.llm_response_to_message(response)
|
|
121
|
+
self.conversation.append(response_message)
|
|
122
|
+
|
|
123
|
+
return response_message
|
|
124
|
+
|
|
125
|
+
async def adeal_tool_or_output(self, response_message:Message) -> Message|None:
|
|
126
|
+
"""
|
|
127
|
+
5. 判断 是否调用工具
|
|
128
|
+
6. 是: tool 返回结果并追加,否: 追加并返回
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
# 5. 判断 是否调用工具
|
|
132
|
+
if response_message.tool_calls:
|
|
133
|
+
# 6. 是: tool 返回结果追加
|
|
134
|
+
tool_results = await self.tool.ainvoke_many(response_message.tool_calls)
|
|
135
|
+
self.conversation.append_many([build_tool_message(tool_result.tool_call_id, tool_result.content) for tool_result in tool_results])
|
|
136
|
+
return None
|
|
137
|
+
else:
|
|
138
|
+
# 6. 否: 追加并返回
|
|
139
|
+
return response_message
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async def arun(self, input_message:Message) ->Message:
|
|
143
|
+
"""
|
|
144
|
+
1. 追加输入
|
|
145
|
+
2. 读会话消息
|
|
146
|
+
3. 大模型交流
|
|
147
|
+
4. 追加会话消息
|
|
148
|
+
5. 判断 是否调用工具
|
|
149
|
+
6. 是: tool 返回结果并追加,否: 追加并返回
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
self.conversation.append(input_message)
|
|
156
|
+
while True:
|
|
157
|
+
|
|
158
|
+
response_message = await self.achat()
|
|
159
|
+
result = await self.adeal_tool_or_output(response_message)
|
|
160
|
+
if result is None:
|
|
161
|
+
continue
|
|
162
|
+
elif isinstance(result, Message):
|
|
163
|
+
return result
|
|
@@ -1,98 +1,135 @@
|
|
|
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
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rootdriver
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Rooted in Origin, Driving All Things
|
|
5
|
+
Author-email: zimvir <zimvir@qq.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: openai>=1.0.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
13
|
+
|
|
14
|
+
# RootDriver
|
|
15
|
+
|
|
16
|
+
根源出发,驱动万物
|
|
17
|
+
|
|
18
|
+
一个轻量级的 Python AI Agent 开发框架,支持单智能体和多智能体应用。
|
|
19
|
+
|
|
20
|
+
## 特性
|
|
21
|
+
|
|
22
|
+
- **简洁易用**:装饰器方式定义工具,快速构建 Agent
|
|
23
|
+
- **模块化设计**:LLM 适配器、工具系统、会话管理解耦
|
|
24
|
+
- **工具调用**:支持 function calling,自动执行工具并返回结果
|
|
25
|
+
- **状态管理**:支持内存和数据库持久化检查点
|
|
26
|
+
- **异常体系**:完整的异常类层次结构
|
|
27
|
+
- **异步支持**:全面异步支持,并发多 Agent / 工具调用
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install rootdriver
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 快速开始
|
|
36
|
+
|
|
37
|
+
### 定义工具
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from rootdriver import tool
|
|
41
|
+
|
|
42
|
+
@tool
|
|
43
|
+
def get_weather(city: str) -> str:
|
|
44
|
+
"""获取城市天气"""
|
|
45
|
+
return f"{city} 晴天"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 创建 Agent
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
52
|
+
|
|
53
|
+
agent = Agent(
|
|
54
|
+
agent_llm=AgentLLM(
|
|
55
|
+
adapter=OpenAIAdapter(
|
|
56
|
+
api_key="YOUR_API_KEY",
|
|
57
|
+
base_url="BASE_URL"
|
|
58
|
+
),
|
|
59
|
+
model="gpt-4",
|
|
60
|
+
),
|
|
61
|
+
tools=[get_weather],
|
|
62
|
+
system_prompt="你是一个有用的助手",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# 单次对话
|
|
66
|
+
response = agent.talk("北京天气怎么样?")
|
|
67
|
+
print(response)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 使用工具
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# 完整对话循环(包含工具调用)
|
|
74
|
+
response = agent.react("帮我查下上海天气")
|
|
75
|
+
print(response)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 异步用法
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import asyncio
|
|
82
|
+
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
83
|
+
|
|
84
|
+
async def main():
|
|
85
|
+
agent = Agent(agent_llm=AgentLLM(...))
|
|
86
|
+
|
|
87
|
+
# 单次异步对话
|
|
88
|
+
response = await agent.atalk("你好")
|
|
89
|
+
print(response)
|
|
90
|
+
|
|
91
|
+
# 并发多个 Agent
|
|
92
|
+
results = await asyncio.gather(
|
|
93
|
+
agent.areact("问题1"),
|
|
94
|
+
agent.areact("问题2"),
|
|
95
|
+
agent.areact("问题3"),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
asyncio.run(main())
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 核心组件
|
|
102
|
+
|
|
103
|
+
| 组件 | 说明 |
|
|
104
|
+
|------|------|
|
|
105
|
+
| `Agent` | 智能体入口,整合 LLM、工具、会话 |
|
|
106
|
+
| `Engine` | 核心引擎,处理对话循环和工具调用 |
|
|
107
|
+
| `Conversation` | 会话管理,维护消息历史 |
|
|
108
|
+
| `LLM` | LLM 调用封装 |
|
|
109
|
+
| `Tool` | 工具集合,管理所有可调用工具 |
|
|
110
|
+
| `State` | 状态管理,支持检查点和持久化 |
|
|
111
|
+
|
|
112
|
+
## 项目结构
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
rootdriver/
|
|
116
|
+
├── agent.py # Agent 智能体
|
|
117
|
+
├── engine.py # 引擎核心
|
|
118
|
+
├── conversation.py # 对话管理
|
|
119
|
+
├── state.py # 状态管理
|
|
120
|
+
├── exception.py # 异常定义
|
|
121
|
+
├── llm/
|
|
122
|
+
│ ├── llm.py # LLM 封装
|
|
123
|
+
│ ├── base_adapter.py # 适配器基类
|
|
124
|
+
│ └── adapter/
|
|
125
|
+
│ └── openai_adapter.py # OpenAI 适配器
|
|
126
|
+
├── tool/
|
|
127
|
+
│ ├── base_tool.py # 工具基类
|
|
128
|
+
│ └── tools.py # 工具集
|
|
129
|
+
├── types/ # 类型定义
|
|
130
|
+
└── utils/ # 工具函数
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
import sys
|
|
4
|
+
sys.path.insert(0, "..")
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from rootdriver.tool import BaseTool, tool
|
|
8
|
+
|
|
9
|
+
@tool
|
|
10
|
+
def sync_weather(city: str) -> str:
|
|
11
|
+
"""同步查天气"""
|
|
12
|
+
time.sleep(0.5)
|
|
13
|
+
return f"{city}: 晴天"
|
|
14
|
+
|
|
15
|
+
@tool
|
|
16
|
+
async def async_weather(city: str) -> str:
|
|
17
|
+
"""异步查天气"""
|
|
18
|
+
await asyncio.sleep(0.5)
|
|
19
|
+
return f"{city}: 多云"
|
|
20
|
+
|
|
21
|
+
@pytest.mark.asyncio
|
|
22
|
+
async def test_base_tool_ainvoke():
|
|
23
|
+
"""测试 BaseTool.ainvoke"""
|
|
24
|
+
result = await sync_weather.ainvoke({"city": "北京"})
|
|
25
|
+
assert "北京" in result and "晴天" in result
|
|
26
|
+
|
|
27
|
+
result = await async_weather.ainvoke({"city": "北京"})
|
|
28
|
+
assert "北京" in result and "多云" in result
|
|
29
|
+
|
|
30
|
+
@pytest.mark.asyncio
|
|
31
|
+
async def test_concurrent():
|
|
32
|
+
"""测试并发执行"""
|
|
33
|
+
start = time.time()
|
|
34
|
+
results = await asyncio.gather(
|
|
35
|
+
sync_weather.ainvoke({"city": "北京"}),
|
|
36
|
+
sync_weather.ainvoke({"city": "上海"}),
|
|
37
|
+
async_weather.ainvoke({"city": "广州"}),
|
|
38
|
+
)
|
|
39
|
+
elapsed = time.time() - start
|
|
40
|
+
|
|
41
|
+
assert elapsed < 0.8, f"应该并发执行,但耗时 {elapsed} 秒"
|
|
42
|
+
assert len(results) == 3
|
|
43
|
+
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_ainvoke_many():
|
|
46
|
+
"""测试批量异步调用"""
|
|
47
|
+
from rootdriver.types.tool import ToolCall
|
|
48
|
+
from rootdriver.tool import Tool
|
|
49
|
+
|
|
50
|
+
tool_calls = [
|
|
51
|
+
ToolCall(id="1", name="sync_weather", arguments={"city": "北京"}),
|
|
52
|
+
ToolCall(id="2", name="async_weather", arguments={"city": "上海"}),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
tool_set = Tool([sync_weather, async_weather])
|
|
56
|
+
results = await tool_set.ainvoke_many(tool_calls)
|
|
57
|
+
|
|
58
|
+
assert len(results) == 2
|
|
59
|
+
assert "北京" in results[0].content
|
|
60
|
+
assert "上海" in results[1].content
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|