rootdriver 0.1.0__tar.gz → 0.2.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.1.0 → rootdriver-0.2.0}/PKG-INFO +30 -21
- {rootdriver-0.1.0 → rootdriver-0.2.0}/README.md +29 -20
- {rootdriver-0.1.0 → rootdriver-0.2.0}/pyproject.toml +1 -1
- rootdriver-0.2.0/rootdriver/__init__.py +58 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver/agent.py +10 -8
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver/conversation.py +5 -2
- rootdriver-0.2.0/rootdriver/exception.py +119 -0
- rootdriver-0.2.0/rootdriver/state.py +103 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver.egg-info/PKG-INFO +30 -21
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver.egg-info/SOURCES.txt +1 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/tests/test_tool.py +5 -3
- rootdriver-0.1.0/rootdriver/__init__.py +0 -32
- rootdriver-0.1.0/rootdriver/exception.py +0 -1
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver/constants.py +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver/engine.py +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver.egg-info/dependency_links.txt +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver.egg-info/requires.txt +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/rootdriver.egg-info/top_level.txt +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/setup.cfg +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/tests/test_base_tool.py +0 -0
- {rootdriver-0.1.0 → rootdriver-0.2.0}/tests/test_conversation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootdriver
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Rooted in Origin, Driving All Things
|
|
5
5
|
Author-email: zimvir <zimvir@qq.com>
|
|
6
6
|
License: MIT
|
|
@@ -13,16 +13,17 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
|
13
13
|
|
|
14
14
|
# RootDriver
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
根源出发,驱动万物
|
|
17
17
|
|
|
18
|
-
一个轻量级的 Python AI Agent
|
|
18
|
+
一个轻量级的 Python AI Agent 开发框架,支持单智能体和多智能体应用。
|
|
19
19
|
|
|
20
20
|
## 特性
|
|
21
21
|
|
|
22
22
|
- **简洁易用**:装饰器方式定义工具,快速构建 Agent
|
|
23
23
|
- **模块化设计**:LLM 适配器、工具系统、会话管理解耦
|
|
24
24
|
- **工具调用**:支持 function calling,自动执行工具并返回结果
|
|
25
|
-
-
|
|
25
|
+
- **状态管理**:支持内存和数据库持久化检查点
|
|
26
|
+
- **异常体系**:完整的异常类层次结构
|
|
26
27
|
|
|
27
28
|
## 安装
|
|
28
29
|
|
|
@@ -47,16 +48,15 @@ def get_weather(city: str) -> str:
|
|
|
47
48
|
|
|
48
49
|
```python
|
|
49
50
|
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
50
|
-
|
|
51
|
-
adapter=OpenAIAdapter(
|
|
52
|
-
api_key="YOUR_API_KEY",
|
|
53
|
-
base_url="BASE_URL"
|
|
54
|
-
),
|
|
55
|
-
model="gpt-4",
|
|
56
|
-
|
|
57
|
-
),
|
|
51
|
+
|
|
58
52
|
agent = Agent(
|
|
59
|
-
agent_llm=
|
|
53
|
+
agent_llm=AgentLLM(
|
|
54
|
+
adapter=OpenAIAdapter(
|
|
55
|
+
api_key="YOUR_API_KEY",
|
|
56
|
+
base_url="BASE_URL"
|
|
57
|
+
),
|
|
58
|
+
model="gpt-4",
|
|
59
|
+
),
|
|
60
60
|
tools=[get_weather],
|
|
61
61
|
system_prompt="你是一个有用的助手",
|
|
62
62
|
)
|
|
@@ -70,7 +70,7 @@ print(response)
|
|
|
70
70
|
|
|
71
71
|
```python
|
|
72
72
|
# 完整对话循环(包含工具调用)
|
|
73
|
-
response =
|
|
73
|
+
response = agent.react("帮我查下上海天气")
|
|
74
74
|
print(response)
|
|
75
75
|
```
|
|
76
76
|
|
|
@@ -83,18 +83,27 @@ print(response)
|
|
|
83
83
|
| `Conversation` | 会话管理,维护消息历史 |
|
|
84
84
|
| `LLM` | LLM 调用封装 |
|
|
85
85
|
| `Tool` | 工具集合,管理所有可调用工具 |
|
|
86
|
+
| `State` | 状态管理,支持检查点和持久化 |
|
|
86
87
|
|
|
87
88
|
## 项目结构
|
|
88
89
|
|
|
89
90
|
```
|
|
90
91
|
rootdriver/
|
|
91
|
-
├── agent.py
|
|
92
|
-
├── engine.py
|
|
93
|
-
├── conversation.py
|
|
94
|
-
├──
|
|
95
|
-
├──
|
|
96
|
-
├──
|
|
97
|
-
|
|
92
|
+
├── agent.py # Agent 智能体
|
|
93
|
+
├── engine.py # 引擎核心
|
|
94
|
+
├── conversation.py # 对话管理
|
|
95
|
+
├── state.py # 状态管理
|
|
96
|
+
├── exception.py # 异常定义
|
|
97
|
+
├── llm/
|
|
98
|
+
│ ├── llm.py # LLM 封装
|
|
99
|
+
│ ├── base_adapter.py # 适配器基类
|
|
100
|
+
│ └── adapter/
|
|
101
|
+
│ └── openai_adapter.py # OpenAI 适配器
|
|
102
|
+
├── tool/
|
|
103
|
+
│ ├── base_tool.py # 工具基类
|
|
104
|
+
│ └── tools.py # 工具集
|
|
105
|
+
├── types/ # 类型定义
|
|
106
|
+
└── utils/ # 工具函数
|
|
98
107
|
```
|
|
99
108
|
|
|
100
109
|
## License
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# RootDriver
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
根源出发,驱动万物
|
|
4
4
|
|
|
5
|
-
一个轻量级的 Python AI Agent
|
|
5
|
+
一个轻量级的 Python AI Agent 开发框架,支持单智能体和多智能体应用。
|
|
6
6
|
|
|
7
7
|
## 特性
|
|
8
8
|
|
|
9
9
|
- **简洁易用**:装饰器方式定义工具,快速构建 Agent
|
|
10
10
|
- **模块化设计**:LLM 适配器、工具系统、会话管理解耦
|
|
11
11
|
- **工具调用**:支持 function calling,自动执行工具并返回结果
|
|
12
|
-
-
|
|
12
|
+
- **状态管理**:支持内存和数据库持久化检查点
|
|
13
|
+
- **异常体系**:完整的异常类层次结构
|
|
13
14
|
|
|
14
15
|
## 安装
|
|
15
16
|
|
|
@@ -34,16 +35,15 @@ def get_weather(city: str) -> str:
|
|
|
34
35
|
|
|
35
36
|
```python
|
|
36
37
|
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
37
|
-
|
|
38
|
-
adapter=OpenAIAdapter(
|
|
39
|
-
api_key="YOUR_API_KEY",
|
|
40
|
-
base_url="BASE_URL"
|
|
41
|
-
),
|
|
42
|
-
model="gpt-4",
|
|
43
|
-
|
|
44
|
-
),
|
|
38
|
+
|
|
45
39
|
agent = Agent(
|
|
46
|
-
agent_llm=
|
|
40
|
+
agent_llm=AgentLLM(
|
|
41
|
+
adapter=OpenAIAdapter(
|
|
42
|
+
api_key="YOUR_API_KEY",
|
|
43
|
+
base_url="BASE_URL"
|
|
44
|
+
),
|
|
45
|
+
model="gpt-4",
|
|
46
|
+
),
|
|
47
47
|
tools=[get_weather],
|
|
48
48
|
system_prompt="你是一个有用的助手",
|
|
49
49
|
)
|
|
@@ -57,7 +57,7 @@ print(response)
|
|
|
57
57
|
|
|
58
58
|
```python
|
|
59
59
|
# 完整对话循环(包含工具调用)
|
|
60
|
-
response =
|
|
60
|
+
response = agent.react("帮我查下上海天气")
|
|
61
61
|
print(response)
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -70,18 +70,27 @@ print(response)
|
|
|
70
70
|
| `Conversation` | 会话管理,维护消息历史 |
|
|
71
71
|
| `LLM` | LLM 调用封装 |
|
|
72
72
|
| `Tool` | 工具集合,管理所有可调用工具 |
|
|
73
|
+
| `State` | 状态管理,支持检查点和持久化 |
|
|
73
74
|
|
|
74
75
|
## 项目结构
|
|
75
76
|
|
|
76
77
|
```
|
|
77
78
|
rootdriver/
|
|
78
|
-
├── agent.py
|
|
79
|
-
├── engine.py
|
|
80
|
-
├── conversation.py
|
|
81
|
-
├──
|
|
82
|
-
├──
|
|
83
|
-
├──
|
|
84
|
-
|
|
79
|
+
├── agent.py # Agent 智能体
|
|
80
|
+
├── engine.py # 引擎核心
|
|
81
|
+
├── conversation.py # 对话管理
|
|
82
|
+
├── state.py # 状态管理
|
|
83
|
+
├── exception.py # 异常定义
|
|
84
|
+
├── llm/
|
|
85
|
+
│ ├── llm.py # LLM 封装
|
|
86
|
+
│ ├── base_adapter.py # 适配器基类
|
|
87
|
+
│ └── adapter/
|
|
88
|
+
│ └── openai_adapter.py # OpenAI 适配器
|
|
89
|
+
├── tool/
|
|
90
|
+
│ ├── base_tool.py # 工具基类
|
|
91
|
+
│ └── tools.py # 工具集
|
|
92
|
+
├── types/ # 类型定义
|
|
93
|
+
└── utils/ # 工具函数
|
|
85
94
|
```
|
|
86
95
|
|
|
87
96
|
## License
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
2
|
+
__author__ = "zimvir"
|
|
3
|
+
__email__ = "zimvir@qq.com"
|
|
4
|
+
|
|
5
|
+
from .agent import Agent
|
|
6
|
+
from .engine import Engine
|
|
7
|
+
from .conversation import Conversation
|
|
8
|
+
from .state import State
|
|
9
|
+
from .llm import LLM
|
|
10
|
+
from .llm.base_adapter import BaseAdapter
|
|
11
|
+
from .llm.adapter import OpenAIAdapter
|
|
12
|
+
from .tool import tool, Tool
|
|
13
|
+
from .types.agent import AgentLLM
|
|
14
|
+
from .types import Message, ToolDefinition, ToolCall
|
|
15
|
+
from .exception import (
|
|
16
|
+
LLMError,
|
|
17
|
+
LLMInvokeError,
|
|
18
|
+
LLMResponseError,
|
|
19
|
+
ToolError,
|
|
20
|
+
ToolNotFoundError,
|
|
21
|
+
ToolInvokeError,
|
|
22
|
+
ToolArgumentError,
|
|
23
|
+
AgentError,
|
|
24
|
+
ConversationError,
|
|
25
|
+
StateError,
|
|
26
|
+
CheckpointNotFoundError,
|
|
27
|
+
StateSaveError,
|
|
28
|
+
StateLoadError,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Agent",
|
|
33
|
+
"Engine",
|
|
34
|
+
"Conversation",
|
|
35
|
+
"State",
|
|
36
|
+
"LLM",
|
|
37
|
+
"BaseAdapter",
|
|
38
|
+
"OpenAIAdapter",
|
|
39
|
+
"tool",
|
|
40
|
+
"Tool",
|
|
41
|
+
"AgentLLM",
|
|
42
|
+
"Message",
|
|
43
|
+
"ToolDefinition",
|
|
44
|
+
"ToolCall",
|
|
45
|
+
"LLMError",
|
|
46
|
+
"LLMInvokeError",
|
|
47
|
+
"LLMResponseError",
|
|
48
|
+
"ToolError",
|
|
49
|
+
"ToolNotFoundError",
|
|
50
|
+
"ToolInvokeError",
|
|
51
|
+
"ToolArgumentError",
|
|
52
|
+
"AgentError",
|
|
53
|
+
"ConversationError",
|
|
54
|
+
"StateError",
|
|
55
|
+
"CheckpointNotFoundError",
|
|
56
|
+
"StateSaveError",
|
|
57
|
+
"StateLoadError",
|
|
58
|
+
]
|
|
@@ -6,7 +6,7 @@ from .llm import LLM
|
|
|
6
6
|
from .conversation import Conversation
|
|
7
7
|
from .tool import Tool, BaseTool
|
|
8
8
|
from .types.agent import AgentLLM
|
|
9
|
-
from .
|
|
9
|
+
from .state import State
|
|
10
10
|
from .utils import build_system_message, build_message, build_tool_message, build_user_message, build_assistant_message
|
|
11
11
|
|
|
12
12
|
class Agent:
|
|
@@ -14,18 +14,20 @@ class Agent:
|
|
|
14
14
|
def __init__(
|
|
15
15
|
self,
|
|
16
16
|
agent_llm: AgentLLM,
|
|
17
|
-
|
|
17
|
+
id: str | None = None,
|
|
18
18
|
tools: list[BaseTool] | None = None,
|
|
19
19
|
system_prompt: str | None = None,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
timeout: float | None = None,
|
|
20
|
+
|
|
21
|
+
db_path: str| None = None,
|
|
22
|
+
# retry: int = 3,
|
|
23
|
+
# timeout: float | None = None,
|
|
24
24
|
):
|
|
25
|
+
self.id = id if id else uuid4().hex
|
|
26
|
+
self.db_path = db_path
|
|
27
|
+
|
|
25
28
|
self.conversation = Conversation(system_prompt)
|
|
26
29
|
self.tool = Tool(tools if tools else [])
|
|
27
|
-
self.
|
|
28
|
-
# self.save_path = save_path
|
|
30
|
+
self.state = State(self, self.db_path)
|
|
29
31
|
|
|
30
32
|
self.engine = Engine(
|
|
31
33
|
model=agent_llm.model,
|
|
@@ -40,6 +40,9 @@ class Conversation:
|
|
|
40
40
|
def append_system(self, content: str) -> "Conversation":
|
|
41
41
|
self.messages.append(Message(role="system", content=content, created_at=get_iso_timestamp()))
|
|
42
42
|
return self
|
|
43
|
+
def update_message(self, messages: list[Message]) -> "Conversation":
|
|
44
|
+
self.messages = messages.copy()
|
|
45
|
+
return self
|
|
43
46
|
|
|
44
47
|
def delete(self, index: int=-1) -> "Conversation":
|
|
45
48
|
self.messages.pop(index)
|
|
@@ -53,18 +56,18 @@ class Conversation:
|
|
|
53
56
|
'''返回 字典加列表组成的message(可转成json用于网络、跨语言传输) 组成的列表'''
|
|
54
57
|
return [m.model_dump(exclude_none=True) for m in self.messages]
|
|
55
58
|
|
|
56
|
-
|
|
57
59
|
@classmethod
|
|
58
60
|
def from_dict_list(cls, messages: list[dict]) -> "Conversation":
|
|
59
61
|
conv = cls()
|
|
60
62
|
conv.messages = [Message.model_validate(m) for m in messages]
|
|
61
63
|
return conv
|
|
62
|
-
|
|
64
|
+
from_messages_to_conversation = from_dict_list
|
|
63
65
|
@classmethod
|
|
64
66
|
def from_list(cls, messages: list[Message]) -> "Conversation":
|
|
65
67
|
conv = cls()
|
|
66
68
|
conv.messages = list(messages)
|
|
67
69
|
return conv
|
|
70
|
+
from_messages_list_to_conversation = from_dict_list
|
|
68
71
|
|
|
69
72
|
def clear(self) -> "Conversation":
|
|
70
73
|
self.messages = []
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""RootDriver 异常体系。"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# ============== LLM 相关 ==============
|
|
5
|
+
|
|
6
|
+
class LLMError(Exception):
|
|
7
|
+
"""LLM 调用基类。"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str, model: str = None, **kwargs):
|
|
10
|
+
self.model = model
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LLMInvokeError(LLMError):
|
|
15
|
+
"""LLM 调用失败(网络错误、API 错误、超时等)。"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message: str, model: str = None, status_code: int = None, **kwargs):
|
|
18
|
+
self.status_code = status_code
|
|
19
|
+
super().__init__(message, model)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LLMResponseError(LLMError):
|
|
23
|
+
"""LLM 返回格式异常(解析失败、缺少字段等)。"""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AdapterError(Exception):
|
|
29
|
+
"""适配器基类。"""
|
|
30
|
+
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ============== 工具相关 ==============
|
|
35
|
+
|
|
36
|
+
class ToolError(Exception):
|
|
37
|
+
"""工具基类。"""
|
|
38
|
+
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ToolNotFoundError(ToolError):
|
|
43
|
+
"""工具不存在。"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, name: str, available: list = None):
|
|
46
|
+
self.name = name
|
|
47
|
+
self.available = available or []
|
|
48
|
+
msg = f"Tool '{name}' not found"
|
|
49
|
+
if available:
|
|
50
|
+
msg += f", available: {available}"
|
|
51
|
+
super().__init__(msg)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ToolInvokeError(ToolError):
|
|
55
|
+
"""工具执行失败。"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, name: str, reason: str = None):
|
|
58
|
+
self.name = name
|
|
59
|
+
self.reason = reason
|
|
60
|
+
msg = f"Tool '{name}' invoke failed"
|
|
61
|
+
if reason:
|
|
62
|
+
msg += f": {reason}"
|
|
63
|
+
super().__init__(msg)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ToolArgumentError(ToolError):
|
|
67
|
+
"""工具参数错误。"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, name: str, missing: list = None, invalid: dict = None):
|
|
70
|
+
self.name = name
|
|
71
|
+
self.missing = missing or []
|
|
72
|
+
self.invalid = invalid or {}
|
|
73
|
+
msg = f"Tool '{name}' argument error"
|
|
74
|
+
if missing:
|
|
75
|
+
msg += f", missing: {missing}"
|
|
76
|
+
super().__init__(msg)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ============== Agent / 会话相关 ==============
|
|
80
|
+
|
|
81
|
+
class AgentError(Exception):
|
|
82
|
+
"""Agent 基类。"""
|
|
83
|
+
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ConversationError(Exception):
|
|
88
|
+
"""会话操作错误。"""
|
|
89
|
+
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ============== State 相关 ==============
|
|
94
|
+
|
|
95
|
+
class StateError(Exception):
|
|
96
|
+
"""State 基类。"""
|
|
97
|
+
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class CheckpointNotFoundError(StateError):
|
|
102
|
+
"""检查点不存在。"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, name: str, source: str = "memory"):
|
|
105
|
+
self.name = name
|
|
106
|
+
self.source = source
|
|
107
|
+
super().__init__(f"Checkpoint '{name}' not found in {source}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class StateSaveError(StateError):
|
|
111
|
+
"""状态保存失败。"""
|
|
112
|
+
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class StateLoadError(StateError):
|
|
117
|
+
"""状态加载失败。"""
|
|
118
|
+
|
|
119
|
+
pass
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .conversation import Conversation
|
|
6
|
+
from .exception import CheckpointNotFoundError
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
class State:
|
|
10
|
+
def __init__(self, agent_obj:Agent, db_path):
|
|
11
|
+
self.agent = agent_obj
|
|
12
|
+
self.db_path = db_path
|
|
13
|
+
self.checkpoints:dict[str,Conversation] = {}
|
|
14
|
+
|
|
15
|
+
def checkpoint(self, name) -> "State":
|
|
16
|
+
self.checkpoints[name] = self.agent.conversation.copy()
|
|
17
|
+
return self
|
|
18
|
+
def save_messages(self, name:str, agent_id, messages:list[dict]) -> Agent:
|
|
19
|
+
"""
|
|
20
|
+
Args:
|
|
21
|
+
name: 检查点 的名称
|
|
22
|
+
agent_id: agent的id, 默认当前agent的id
|
|
23
|
+
"""
|
|
24
|
+
# 打开数据库
|
|
25
|
+
data = json.load(self.db_path)
|
|
26
|
+
# 确保有此agent
|
|
27
|
+
if data.get(agent_id) is None:
|
|
28
|
+
data[agent_id] = {}
|
|
29
|
+
data[agent_id][name] = messages
|
|
30
|
+
json.dump(data, self.db_path)
|
|
31
|
+
return self.agent
|
|
32
|
+
def save_from_now(self, name:str, agent_id:str|None=None) -> Agent:
|
|
33
|
+
"""
|
|
34
|
+
Args:
|
|
35
|
+
name: 检查点 的名称
|
|
36
|
+
agent_id: agent的id, 默认当前agent的id
|
|
37
|
+
"""
|
|
38
|
+
self.save_messages(name, agent_id, self.agent.conversation.get_messages_in_list())
|
|
39
|
+
return self.agent
|
|
40
|
+
def save_from_checkpoints(self, name:str,checkpoint_name:str, agent_id:str|None=None) -> Agent:
|
|
41
|
+
"""
|
|
42
|
+
Args:
|
|
43
|
+
name: 检查点 的名称
|
|
44
|
+
checkpoint_name : 再checkpoints中的name
|
|
45
|
+
agent_id: agent的id, 默认当前agent的id
|
|
46
|
+
"""
|
|
47
|
+
checkpoint = self.checkpoints.get(checkpoint_name)
|
|
48
|
+
if checkpoint is None:
|
|
49
|
+
raise CheckpointNotFoundError(checkpoint_name, "memory")
|
|
50
|
+
messages = checkpoint.get_messages_in_list()
|
|
51
|
+
self.save_messages(name, agent_id, messages)
|
|
52
|
+
return self.agent
|
|
53
|
+
|
|
54
|
+
def get_from_checkpoints(self, name) -> Conversation:
|
|
55
|
+
"""
|
|
56
|
+
Args:
|
|
57
|
+
name: 检查点 的名称
|
|
58
|
+
agent_id: agent的id, 默认当前agent的id
|
|
59
|
+
Return:
|
|
60
|
+
Conversation
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
checkpoint:Conversation = self.checkpoints.get(name, None)
|
|
64
|
+
if checkpoint is None:
|
|
65
|
+
raise CheckpointNotFoundError(name, "memory")
|
|
66
|
+
return checkpoint
|
|
67
|
+
def get_from_db(self, name, agent_id:str|None=None) -> Conversation:
|
|
68
|
+
"""
|
|
69
|
+
Args:
|
|
70
|
+
name: 检查点 的名称
|
|
71
|
+
agent_id: agent的id, 默认当前agent的id
|
|
72
|
+
Return:
|
|
73
|
+
Conversation
|
|
74
|
+
"""
|
|
75
|
+
if agent_id is None:
|
|
76
|
+
agent_id = self.agent.id
|
|
77
|
+
|
|
78
|
+
data = json.load(self.db_path)
|
|
79
|
+
|
|
80
|
+
if data.get(agent_id) is None:
|
|
81
|
+
raise CheckpointNotFoundError(name, f"db (unknown agent_id: {agent_id})")
|
|
82
|
+
|
|
83
|
+
messages = data[agent_id].get(name, None)
|
|
84
|
+
if messages is None:
|
|
85
|
+
raise CheckpointNotFoundError(name, f"db ({self.db_path})")
|
|
86
|
+
|
|
87
|
+
return Conversation.from_messages_list_to_conversation(messages)
|
|
88
|
+
|
|
89
|
+
def load_from_checkpoint(self, name:str|None=None) -> Agent:
|
|
90
|
+
messages = self.get_from_checkpoints(name).messages
|
|
91
|
+
self.agent.conversation.update_message(messages)
|
|
92
|
+
return self.agent
|
|
93
|
+
|
|
94
|
+
def load_from_db(self, name:str|None=None, agent_id:str|None =None ) -> Agent:
|
|
95
|
+
"""
|
|
96
|
+
Args:
|
|
97
|
+
name: 检查点 的名称
|
|
98
|
+
agent_id: agent的id, 默认当前agent的id
|
|
99
|
+
"""
|
|
100
|
+
agent_id = agent_id if agent_id is not None else self.agent.id
|
|
101
|
+
messages = self.get_from_db(name, agent_id).messages
|
|
102
|
+
self.agent.conversation.update_message(messages)
|
|
103
|
+
return self.agent
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootdriver
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Rooted in Origin, Driving All Things
|
|
5
5
|
Author-email: zimvir <zimvir@qq.com>
|
|
6
6
|
License: MIT
|
|
@@ -13,16 +13,17 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
|
13
13
|
|
|
14
14
|
# RootDriver
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
根源出发,驱动万物
|
|
17
17
|
|
|
18
|
-
一个轻量级的 Python AI Agent
|
|
18
|
+
一个轻量级的 Python AI Agent 开发框架,支持单智能体和多智能体应用。
|
|
19
19
|
|
|
20
20
|
## 特性
|
|
21
21
|
|
|
22
22
|
- **简洁易用**:装饰器方式定义工具,快速构建 Agent
|
|
23
23
|
- **模块化设计**:LLM 适配器、工具系统、会话管理解耦
|
|
24
24
|
- **工具调用**:支持 function calling,自动执行工具并返回结果
|
|
25
|
-
-
|
|
25
|
+
- **状态管理**:支持内存和数据库持久化检查点
|
|
26
|
+
- **异常体系**:完整的异常类层次结构
|
|
26
27
|
|
|
27
28
|
## 安装
|
|
28
29
|
|
|
@@ -47,16 +48,15 @@ def get_weather(city: str) -> str:
|
|
|
47
48
|
|
|
48
49
|
```python
|
|
49
50
|
from rootdriver import Agent, AgentLLM, OpenAIAdapter
|
|
50
|
-
|
|
51
|
-
adapter=OpenAIAdapter(
|
|
52
|
-
api_key="YOUR_API_KEY",
|
|
53
|
-
base_url="BASE_URL"
|
|
54
|
-
),
|
|
55
|
-
model="gpt-4",
|
|
56
|
-
|
|
57
|
-
),
|
|
51
|
+
|
|
58
52
|
agent = Agent(
|
|
59
|
-
agent_llm=
|
|
53
|
+
agent_llm=AgentLLM(
|
|
54
|
+
adapter=OpenAIAdapter(
|
|
55
|
+
api_key="YOUR_API_KEY",
|
|
56
|
+
base_url="BASE_URL"
|
|
57
|
+
),
|
|
58
|
+
model="gpt-4",
|
|
59
|
+
),
|
|
60
60
|
tools=[get_weather],
|
|
61
61
|
system_prompt="你是一个有用的助手",
|
|
62
62
|
)
|
|
@@ -70,7 +70,7 @@ print(response)
|
|
|
70
70
|
|
|
71
71
|
```python
|
|
72
72
|
# 完整对话循环(包含工具调用)
|
|
73
|
-
response =
|
|
73
|
+
response = agent.react("帮我查下上海天气")
|
|
74
74
|
print(response)
|
|
75
75
|
```
|
|
76
76
|
|
|
@@ -83,18 +83,27 @@ print(response)
|
|
|
83
83
|
| `Conversation` | 会话管理,维护消息历史 |
|
|
84
84
|
| `LLM` | LLM 调用封装 |
|
|
85
85
|
| `Tool` | 工具集合,管理所有可调用工具 |
|
|
86
|
+
| `State` | 状态管理,支持检查点和持久化 |
|
|
86
87
|
|
|
87
88
|
## 项目结构
|
|
88
89
|
|
|
89
90
|
```
|
|
90
91
|
rootdriver/
|
|
91
|
-
├── agent.py
|
|
92
|
-
├── engine.py
|
|
93
|
-
├── conversation.py
|
|
94
|
-
├──
|
|
95
|
-
├──
|
|
96
|
-
├──
|
|
97
|
-
|
|
92
|
+
├── agent.py # Agent 智能体
|
|
93
|
+
├── engine.py # 引擎核心
|
|
94
|
+
├── conversation.py # 对话管理
|
|
95
|
+
├── state.py # 状态管理
|
|
96
|
+
├── exception.py # 异常定义
|
|
97
|
+
├── llm/
|
|
98
|
+
│ ├── llm.py # LLM 封装
|
|
99
|
+
│ ├── base_adapter.py # 适配器基类
|
|
100
|
+
│ └── adapter/
|
|
101
|
+
│ └── openai_adapter.py # OpenAI 适配器
|
|
102
|
+
├── tool/
|
|
103
|
+
│ ├── base_tool.py # 工具基类
|
|
104
|
+
│ └── tools.py # 工具集
|
|
105
|
+
├── types/ # 类型定义
|
|
106
|
+
└── utils/ # 工具函数
|
|
98
107
|
```
|
|
99
108
|
|
|
100
109
|
## License
|
|
@@ -2,6 +2,7 @@ import pytest
|
|
|
2
2
|
from rootdriver.tool.base_tool import BaseTool
|
|
3
3
|
from rootdriver.tool.tools import Tool
|
|
4
4
|
from rootdriver.types.tool import ToolCall
|
|
5
|
+
from rootdriver.exception import ToolNotFoundError
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def get_weather(city: str) -> str:
|
|
@@ -52,9 +53,10 @@ def test_tool_invoke_success(tool_registry):
|
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
def test_tool_invoke_not_found(tool_registry):
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
assert
|
|
56
|
+
with pytest.raises(ToolNotFoundError) as exc_info:
|
|
57
|
+
tool_registry.invoke(ToolCall(id="2", name="not_exist", arguments={}))
|
|
58
|
+
assert exc_info.value.name == "not_exist"
|
|
59
|
+
assert "get_weather" in str(exc_info.value.available)
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
def test_tool_invoke_many(tool_registry):
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.0"
|
|
2
|
-
__author__ = "zimvir"
|
|
3
|
-
__email__ = "zimvir@qq.com"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from .agent import Agent
|
|
10
|
-
from .engine import Engine
|
|
11
|
-
from .conversation import Conversation
|
|
12
|
-
from .llm import LLM
|
|
13
|
-
from .llm.base_adapter import BaseAdapter
|
|
14
|
-
from .llm.adapter import OpenAIAdapter
|
|
15
|
-
from .tool import tool, Tool
|
|
16
|
-
from .types.agent import AgentLLM
|
|
17
|
-
from .types import Message, ToolDefinition, ToolCall
|
|
18
|
-
|
|
19
|
-
__all__ = [
|
|
20
|
-
"Agent",
|
|
21
|
-
"Engine",
|
|
22
|
-
"Conversation",
|
|
23
|
-
"LLM",
|
|
24
|
-
"BaseAdapter",
|
|
25
|
-
"OpenAIAdapter",
|
|
26
|
-
"tool",
|
|
27
|
-
"Tool",
|
|
28
|
-
"AgentLLM",
|
|
29
|
-
"Message",
|
|
30
|
-
"ToolDefinition",
|
|
31
|
-
"ToolCall",
|
|
32
|
-
]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|