Skip to content

Commit 768c6e4

Browse files
feat(prompt): support cozeloop prompt manager (#380)
* feat: support cozeloop prompt manager * fix typos * fix override
1 parent 04f4241 commit 768c6e4

File tree

6 files changed

+158
-11
lines changed

6 files changed

+158
-11
lines changed

docs/docs/agent/prompt.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Prompt 管理
2+
3+
在实际生产过程中,Prompt 需要进行版本管理与动态下发,VeADK 提供了 Prompt 管理模块,用户可以通过 Prompt 管理功能来进行 Prompt 的模板化与热更新,在运行时动态切换 Prompt 版本。
4+
5+
## CozeLoop 提示词管理
6+
7+
在使用 CozeLoop 进行提示词管理前,您需要安装 Python 版本的 `cozeloop` SDK:
8+
9+
```bash
10+
pip install cozeloop
11+
```
12+
13+
您可以通过 CozeLoop 云端提示词管理功能来对接您的 Agent 系统提示词。例如:
14+
15+
```python title="agent.py" linenums="1" hl_lines="4 6-11 13"
16+
import asyncio
17+
18+
from veadk import Agent, Runner
19+
from veadk.prompts.prompt_manager import CozeloopPromptManager
20+
21+
prompt_manager = CozeloopPromptManager(
22+
cozeloop_workspace_id="", # CozeLoop workspace ID
23+
cozeloop_token="", # CozeLoop token
24+
prompt_key="", # CozeLoop 中创建的 Prompt Key
25+
label="production", # CozeLoop 中创建的 Prompt 标签
26+
)
27+
28+
agent = Agent(name="test_prompt_mgr", prompt_manager=prompt_manager)
29+
30+
runner = Runner(agent=agent)
31+
32+
response = asyncio.run(runner.run(messages="你的任务是什么?"))
33+
print(response)
34+
```
35+
36+
!!! note "提示"
37+
CozeLoop 会在本地进行 Prompt 缓存,**更新时间为 1 分钟**。当获取 Prompt 失败时,会返回 VeADK 默认的 Agent 系统提示词。
38+
39+
您可以在日志中看到,每次处理用户请求之前,都会执行 `CozeloopPromptManager` 中的 `get_prompt` 方法,来获取最新的 Prompt 模板内容。效果如下:
40+
41+
![img](../assets/images/agents/cozeloop_prompt_mgr.png)
42+
43+
更加详细的说明,请参考 [CozeLoop 提示词管理](https://loop.coze.cn/open/docs/cozeloop/what-is-prompt)
44+
45+
## 实现您自己的 Prompt Manager
46+
47+
若您想实现更高阶的 Prompt 模板或变量组合,您可以继承 `PromptManager` 类,实现自定义的 Prompt 管理逻辑。
48+
49+
在实现自定义 Prompt Manager 时,您需要实现 `get_prompt` 方法,该方法会在每次处理用户请求之前被调用,用于获取最新的 Prompt 模板内容。
232 KB
Loading

docs/docs/optimization.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ veadk prompt
1515
```
1616

1717
选项包括:
18+
1819
```shell
1920
--path:指定要优化的 Agent 文件路径,默认值为当前目录下 agent.py。注意,必须将定义的智能体作为全局变量导出
2021
--feedback:指定优化后的提示词反馈,用于优化模型
@@ -45,20 +46,22 @@ VeADK 与方舟平台 Agent RL 集成,用户使用 VeADK 提供的脚手架,
4546

4647
在你的终端中运行以下命令,初始化一个强化学习项目:
4748

48-
```shell
49+
```bash
4950
veadk rl init --platform ark --workspace veadk_rl_ark_project
5051
```
52+
5153
该命令会在当前目录下创建一个名为 `veadk_rl_ark_project` 的文件夹,其中包含了一个基本的强化学习项目结构。
5254
然后在终端中运行以下命令,提交任务到方舟平台:
5355

54-
```shell
56+
```bash
5557
cd veadk_rl_ark_project
5658
veadk rl submit --platform ark
5759
```
5860

5961
#### 原理说明
6062

6163
生成后的项目结构如下,其中核心文件包括:
64+
6265
- 数据集: `data/*.jsonl`
6366
- `/plugins`文件夹下的rollout和reward:
6467
- rollout :用以规定agent的工作流,`raw_async_veadk_rollout.py`提供了使用在方舟rl中使用veadk agent的示例,
@@ -83,6 +86,7 @@ veadk_rl_ark_project
8386
```
8487

8588
#### 最佳实践案例
89+
8690
1. 脚手架中,基于 VeADK 的天气查询 Agent 进行强化学习优化
8791
2. 提交任务 (veadk rl submit --platform ark)
8892

@@ -104,20 +108,21 @@ VeADK 与 Agent Lightning 集成,用户使用 VeADK 提供的脚手架,可
104108

105109
在你的终端中运行以下命令,初始化一个 Agent Lightning 项目:
106110

107-
```shell
111+
```bash
108112
veadk rl init --platform lightning --workspace veadk_rl_lightning_project
109113
```
114+
110115
该命令会在当前目录下创建一个名为 `veadk_rl_lightning_project` 的文件夹,其中包含了一个基本的基于 VeADK 和 Agent Lightning 的强化学习项目结构。
111116
然后在终端1中运行以下命令,启动 client:
112117

113-
```shell
118+
```bash
114119
cd veadk_rl_lightning_project
115120
veadk rl run --platform lightning --client
116121
```
117122

118123
然后在终端2中运行以下命令,启动 server:
119124

120-
```shell
125+
```bash
121126
cd veadk_rl_lightning_project
122127
veadk rl run --platform lightning --server
123128
```
@@ -160,5 +165,3 @@ veadk_rl_lightning_project
160165
![启动client](./assets/images/optimization/lightning_client.png)
161166

162167
![启动server](./assets/images/optimization/lightning_training_server.png)
163-
164-

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ nav:
4545
- 构建智能体: agent/agent.md
4646
- 多智能体交互: agent/agents.md
4747
- A2A 协议: agent/agent-to-agent.md
48+
- Prompt 管理: agent/prompt.md
4849
- 执行引擎:
4950
- Runner: runner.md
5051
- 个性化引擎——记忆:

veadk/agent.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@
4444
from veadk.memory.long_term_memory import LongTermMemory
4545
from veadk.memory.short_term_memory import ShortTermMemory
4646
from veadk.processors import BaseRunProcessor, NoOpRunProcessor
47-
from veadk.prompts.agent_default_prompt import DEFAULT_DESCRIPTION, DEFAULT_INSTRUCTION
47+
from veadk.prompts.agent_default_prompt import (
48+
DEFAULT_DESCRIPTION,
49+
DEFAULT_INSTRUCTION,
50+
)
51+
from veadk.prompts.prompt_manager import BasePromptManager
4852
from veadk.tracing.base_tracer import BaseTracer
4953
from veadk.utils.logger import get_logger
5054
from veadk.utils.patches import patch_asyncio, patch_tracer
@@ -96,6 +100,8 @@ class Agent(LlmAgent):
96100

97101
sub_agents: list[BaseAgent] = Field(default_factory=list, exclude=True)
98102

103+
prompt_manager: Optional[BasePromptManager] = None
104+
99105
knowledgebase: Optional[KnowledgeBase] = None
100106

101107
short_term_memory: Optional[ShortTermMemory] = None
@@ -202,6 +208,9 @@ def model_post_init(self, __context: Any) -> None:
202208
else:
203209
self.before_agent_callback = check_agent_authorization
204210

211+
if self.prompt_manager:
212+
self.instruction = self.prompt_manager.get_prompt
213+
205214
logger.info(f"VeADK version: {VERSION}")
206215

207216
logger.info(f"{self.__class__.__name__} `{self.name}` init done.")
@@ -274,14 +283,20 @@ def _prepare_tracers(self):
274283
return
275284

276285
if not self.tracers:
277-
from veadk.tracing.telemetry.opentelemetry_tracer import OpentelemetryTracer
286+
from veadk.tracing.telemetry.opentelemetry_tracer import (
287+
OpentelemetryTracer,
288+
)
278289

279290
self.tracers.append(OpentelemetryTracer())
280291

281292
exporters = self.tracers[0].exporters # type: ignore
282293

283-
from veadk.tracing.telemetry.exporters.apmplus_exporter import APMPlusExporter
284-
from veadk.tracing.telemetry.exporters.cozeloop_exporter import CozeloopExporter
294+
from veadk.tracing.telemetry.exporters.apmplus_exporter import (
295+
APMPlusExporter,
296+
)
297+
from veadk.tracing.telemetry.exporters.cozeloop_exporter import (
298+
CozeloopExporter,
299+
)
285300
from veadk.tracing.telemetry.exporters.tls_exporter import TLSExporter
286301

287302
if enable_apmplus_tracer and not any(

veadk/prompts/prompt_manager.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from abc import ABC, abstractmethod
16+
17+
from google.adk.agents.readonly_context import ReadonlyContext
18+
from typing_extensions import override
19+
20+
from veadk.prompts.agent_default_prompt import DEFAULT_INSTRUCTION
21+
from veadk.utils.logger import get_logger
22+
23+
logger = get_logger(__name__)
24+
25+
26+
class BasePromptManager(ABC):
27+
def __init__(self) -> None: ...
28+
29+
@abstractmethod
30+
def get_prompt(self, context: ReadonlyContext, **kwargs) -> str: ...
31+
32+
33+
class CozeloopPromptManager(BasePromptManager):
34+
def __init__(
35+
self,
36+
cozeloop_workspace_id: str,
37+
cozeloop_token: str,
38+
prompt_key: str,
39+
version: str = "",
40+
label: str = "",
41+
) -> None:
42+
import cozeloop
43+
44+
self.cozeloop_workspace_id = cozeloop_workspace_id
45+
self.cozeloop_token = cozeloop_token
46+
47+
self.prompt_key = prompt_key
48+
self.version = version
49+
self.label = label
50+
51+
self.client = cozeloop.new_client(
52+
workspace_id=self.cozeloop_workspace_id,
53+
api_token=self.cozeloop_token,
54+
)
55+
56+
super().__init__()
57+
58+
@override
59+
def get_prompt(self, context: ReadonlyContext, **kwargs) -> str:
60+
logger.info(f"Get prompt for agent {context.agent_name} from CozeLoop.")
61+
62+
prompt = self.client.get_prompt(
63+
prompt_key=self.prompt_key,
64+
version=self.version,
65+
label=self.label,
66+
)
67+
if (
68+
prompt
69+
and prompt.prompt_template
70+
and prompt.prompt_template.messages
71+
and prompt.prompt_template.messages[0].content
72+
):
73+
return prompt.prompt_template.messages[0].content
74+
75+
logger.warning(
76+
f"Prompt {self.prompt_key} version {self.version} label {self.label} not found, get prompt result is {prompt}"
77+
f"return default instruction"
78+
)
79+
return DEFAULT_INSTRUCTION

0 commit comments

Comments
 (0)