Skip to content

Commit 78bf6d7

Browse files
authored
Merge pull request #90 from Tylerking1/feat_live_prompts
Feat live prompts
2 parents baecec8 + f7423fa commit 78bf6d7

File tree

15 files changed

+3825
-1
lines changed

15 files changed

+3825
-1
lines changed

docs/docs_zh/1_5_live_prompts.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# 如何使用动态提示词(Live Prompts)
2+
3+
OxyGent 提供了动态提示词功能,允许您在运行时动态加载和更新智能体的提示词,无需重启应用程序。这个功能特别适用于需要频繁调整提示词或在生产环境中进行 A/B 测试的场景。
4+
5+
## 什么是动态提示词
6+
7+
动态提示词(Live Prompts)是一个存储在数据库中的提示词管理系统,支持:
8+
- **实时更新**:在运行时修改提示词,无需重启应用
9+
- **版本管理**:保存提示词的历史版本,支持回滚
10+
- **热重载**:提示词修改后立即生效
11+
- **备用机制**:当动态提示词不可用时,自动使用默认提示词
12+
13+
## 基本用法
14+
15+
### 1. 导入动态提示词功能
16+
17+
```python
18+
from oxygent.live_prompt import get_live_prompts
19+
```
20+
21+
### 2. 在智能体中使用动态提示词
22+
23+
```python
24+
oxy.ReActAgent(
25+
name="time_agent",
26+
desc="A tool that can query the time",
27+
prompt=get_live_prompts(
28+
"time_agent_prompt", # 提示词键名
29+
"You are a time management assistant. Help users with time-related queries." # 默认提示词
30+
),
31+
tools=["time_tools"],
32+
)
33+
```
34+
35+
## 函数参数说明
36+
37+
`get_live_prompts(prompt_key: str, default_prompt: Optional[str] = None) -> str`
38+
39+
**参数:**
40+
- `prompt_key` (str): 提示词的唯一标识符,用于从存储中检索提示词
41+
- `default_prompt` (Optional[str]): 默认提示词,当动态提示词不存在或不可用时使用
42+
43+
**工作逻辑:**
44+
1. 首先尝试从存储系统中使用 `prompt_key` 解析提示词
45+
2. 如果未找到且提供了 `default_prompt`,则使用默认提示词
46+
3. 如果未找到且 `default_prompt` 为 None 或空,则返回空字符串
47+
48+
## 完整示例
49+
50+
以下是一个完整的使用动态提示词的示例:
51+
52+
```python
53+
import asyncio
54+
import os
55+
56+
from oxygent import MAS, Config, oxy, preset_tools
57+
from oxygent.live_prompt import get_live_prompts
58+
59+
Config.set_agent_llm_model("default_llm")
60+
61+
oxy_space = [
62+
oxy.HttpLLM(
63+
name="default_llm",
64+
api_key=os.getenv("DEFAULT_LLM_API_KEY"),
65+
base_url=os.getenv("DEFAULT_LLM_BASE_URL"),
66+
model_name=os.getenv("DEFAULT_LLM_MODEL_NAME"),
67+
),
68+
preset_tools.time_tools,
69+
oxy.ReActAgent(
70+
name="time_agent",
71+
desc="A tool that can query the time",
72+
prompt=get_live_prompts(
73+
"time_agent_prompt",
74+
"You are a time management assistant. Help users with time-related queries."
75+
),
76+
tools=["time_tools"],
77+
),
78+
preset_tools.file_tools,
79+
oxy.ReActAgent(
80+
name="file_agent",
81+
desc="A tool that can operate the file system",
82+
tools=["file_tools"],
83+
prompt=get_live_prompts(
84+
"file_agent_prompt",
85+
"You are a file system assistant. Help users with file operations safely and efficiently."
86+
)
87+
),
88+
preset_tools.math_tools,
89+
oxy.ReActAgent(
90+
name="math_agent",
91+
desc="A tool that can perform mathematical calculations.",
92+
tools=["math_tools"],
93+
prompt=get_live_prompts(
94+
"math_agent_prompt" # 没有默认提示词,如果不存在将返回空字符串
95+
),
96+
),
97+
oxy.ReActAgent(
98+
is_master=True,
99+
name="master_agent",
100+
sub_agents=["time_agent", "file_agent", "math_agent"],
101+
prompt=get_live_prompts(
102+
"master_agent_prompt",
103+
"" # 空字符串作为默认值,将使用系统默认提示词
104+
),
105+
),
106+
]
107+
108+
async def main():
109+
async with MAS(oxy_space=oxy_space) as mas:
110+
await mas.start_web_service(
111+
first_query="What time is it now? Please save it into time.txt."
112+
)
113+
114+
if __name__ == "__main__":
115+
asyncio.run(main())
116+
```
117+
118+
## 使用场景
119+
120+
### 1. 生产环境优化
121+
在生产环境中,您可以通过 Web 界面实时调整提示词,优化智能体的表现,无需重启服务。
122+
123+
### 2. A/B 测试
124+
通过动态切换不同版本的提示词,可以快速进行 A/B 测试,比较不同提示词的效果。
125+
126+
### 3. 多语言支持
127+
根据用户的语言偏好,动态加载对应语言的提示词。
128+
129+
### 4. 渐进式优化
130+
在开发过程中,可以先使用默认提示词启动系统,然后逐步添加和优化动态提示词。
131+
132+
## 最佳实践
133+
134+
### 1. 提供有意义的默认提示词
135+
```python
136+
prompt=get_live_prompts(
137+
"customer_service_prompt",
138+
"You are a helpful customer service assistant. Be polite and professional."
139+
)
140+
```
141+
142+
### 2. 使用描述性的键名
143+
```python
144+
# 好的做法
145+
prompt=get_live_prompts("email_summarizer_prompt", default_prompt)
146+
147+
# 避免的做法
148+
prompt=get_live_prompts("prompt1", default_prompt)
149+
```
150+
151+
### 3. 处理空提示词情况
152+
```python
153+
# 对于可选的提示词,可以不提供默认值
154+
prompt=get_live_prompts("optional_enhancement_prompt")
155+
```
156+
157+
## 配置要求
158+
159+
动态提示词功能需要配置数据库连接:
160+
- 支持 Elasticsearch 作为主要存储
161+
- 当 ES 不可用时,自动回退到 LocalEs(本地存储)
162+
- 通过 `Config` 系统配置数据库连接参数
163+
164+
## 注意事项
165+
166+
1. **性能考虑**:动态提示词会在首次使用时从数据库加载,后续会使用缓存
167+
2. **错误处理**:当动态提示词系统不可用时,会自动使用默认提示词,确保系统稳定运行
168+
3. **版本管理**:系统会自动保存提示词的修改历史,支持版本回滚
169+
170+
[上一章:选择智能体种类](./1_4_select_agent.md)
171+
[下一章:注册单个智能体](./1_register_single_agent.md)
172+
[回到首页](./readme.md)

examples/live_prompts/demo.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import asyncio
2+
import os
3+
4+
from oxygent import MAS, Config, oxy, preset_tools
5+
from oxygent.live_prompt import get_live_prompts
6+
7+
Config.set_agent_llm_model("default_llm")
8+
9+
"""
10+
get_live_prompts(prompt_key: str, default_prompt: Optional[str] = None) -> str
11+
Logic:
12+
1. First try to resolve from storage using prompt_key
13+
2. If not found and default_prompt is provided, use default_prompt
14+
3. If not found and default_prompt is None/empty, return ""
15+
"""
16+
17+
oxy_space = [
18+
oxy.HttpLLM(
19+
name="default_llm",
20+
api_key=os.getenv("DEFAULT_LLM_API_KEY"),
21+
base_url=os.getenv("DEFAULT_LLM_BASE_URL"),
22+
model_name=os.getenv("DEFAULT_LLM_MODEL_NAME"),
23+
),
24+
preset_tools.time_tools,
25+
oxy.ReActAgent(
26+
name="time_agent",
27+
desc="A tool that can query the time",
28+
prompt=get_live_prompts(
29+
"time_agent_prompt",
30+
"You are a time management assistant. Help users with time-related queries." # default prompt
31+
),
32+
tools=["time_tools"],
33+
),
34+
preset_tools.file_tools,
35+
36+
oxy.ReActAgent(
37+
name="file_agent",
38+
desc="A tool that can operate the file system",
39+
tools=["file_tools"],
40+
prompt=get_live_prompts(
41+
"file_agent_prompt",
42+
"You are a file system assistant. Help users with file operations safely and efficiently." # default prompt
43+
)
44+
),
45+
preset_tools.math_tools,
46+
oxy.ReActAgent(
47+
name="math_agent",
48+
desc="A tool that can perform mathematical calculations.",
49+
tools=["math_tools"],
50+
prompt=get_live_prompts(
51+
"math_agent_prompt"
52+
),
53+
),
54+
oxy.ReActAgent(
55+
is_master=True,
56+
name="master_agent",
57+
sub_agents=["time_agent", "file_agent", "math_agent"],
58+
prompt=get_live_prompts(
59+
"master_agent_prompt", # prompt key
60+
"" # can be empty use system default
61+
),
62+
),
63+
]
64+
65+
66+
async def main():
67+
async with MAS(oxy_space=oxy_space) as mas:
68+
await mas.start_web_service(
69+
first_query="What time is it now? Please save it into time.txt."
70+
)
71+
72+
73+
if __name__ == "__main__":
74+
asyncio.run(main())

oxygent/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
from .oxy_factory import OxyFactory
77
from .schemas import OxyOutput, OxyRequest, OxyResponse, OxyState
88

9+
# Live prompt management module
10+
from .live_prompt import (
11+
get_prompt_manager,
12+
hot_reload_prompt,
13+
hot_reload_agent,
14+
hot_reload_all_prompts,
15+
)
16+
917
load_dotenv(".env")
1018

1119
__all__ = [
@@ -17,4 +25,9 @@
1725
"OxyResponse",
1826
"OxyFactory",
1927
"Config",
28+
# Prompt management
29+
"get_prompt_manager",
30+
"hot_reload_prompt",
31+
"hot_reload_agent",
32+
"hot_reload_all_prompts",
2033
]

oxygent/databases/db_es/jes_es.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ async def search(self, index_name, body):
8080
async def exists(self, index_name, doc_id):
8181
return await self.client.exists(index=index_name, id=doc_id)
8282

83+
async def delete(self, index_name, doc_id):
84+
return await self.client.delete(index=index_name, id=doc_id)
85+
8386
async def close(self):
8487
return await self.client.close()
8588

oxygent/databases/db_es/local_es.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,5 +328,33 @@ async def update_by_node_id(
328328

329329
return {"_id": target_doc_id, "result": "updated"}
330330

331+
async def delete(self, index_name: str, doc_id: str) -> dict[str, str]:
332+
"""Delete a document from the index.
333+
334+
Args:
335+
index_name: Name of the index
336+
doc_id: ID of the document to delete
337+
338+
Returns:
339+
Result of the delete operation
340+
"""
341+
data_path = self._index_path(index_name)
342+
backup_path = f"{data_path}.bak"
343+
344+
lock = self._locks.setdefault(index_name, asyncio.Lock())
345+
async with lock:
346+
data = await self._read_json_safe(data_path) or {}
347+
348+
if doc_id not in data:
349+
return {"_id": doc_id, "result": "not_found"}
350+
351+
del data[doc_id]
352+
353+
if await aiofiles.os.path.exists(data_path):
354+
await aiofiles.os.replace(data_path, backup_path)
355+
await self._write_json_atomic(data_path, data)
356+
357+
return {"_id": doc_id, "result": "deleted"}
358+
331359
async def close(self) -> bool: # noqa: D401 – nothing to clean
332360
return True

oxygent/live_prompt/__init__.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Live Prompt Management Module
3+
Provides real-time prompt management and hot-reload functionality for OxyGent agents
4+
"""
5+
6+
# Core ES-based prompt management
7+
from .manager import get_prompt_manager, get_dynamic_prompt, PromptManager
8+
9+
# Main feature: Live prompts for agent registration
10+
from .hot_prompts import get_live_prompts
11+
12+
# Hot-reload functionality
13+
from .wrapper import (
14+
setup_dynamic_agents,
15+
hot_reload_prompt,
16+
hot_reload_agent,
17+
hot_reload_all_prompts,
18+
dynamic_agent_manager,
19+
)
20+
21+
__all__ = [
22+
# Core ES-based prompt management
23+
'get_prompt_manager',
24+
'get_dynamic_prompt',
25+
'PromptManager',
26+
27+
# Main feature: Live prompts for agent registration
28+
'get_live_prompts',
29+
30+
# Hot-reload functionality
31+
'setup_dynamic_agents',
32+
'hot_reload_prompt',
33+
'hot_reload_agent',
34+
'hot_reload_all_prompts',
35+
'dynamic_agent_manager',
36+
]

0 commit comments

Comments
 (0)