Coverage for agentos/prompts/manager.py: 29%
93 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 10:59 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 10:59 +0800
1"""
2AgentOS v0.30 Prompt模板管理 — 版本化Prompt仓库。
3支持模板继承、变量注入、A/B测试、回滚。
4"""
6from dataclasses import dataclass, field
7from datetime import datetime
8import json
9import os
12@dataclass
13class PromptTemplate:
14 """Prompt 模板。"""
15 name: str
16 version: str
17 template: str
18 variables: list[str] = field(default_factory=list)
19 description: str = ""
20 parent: str | None = None
21 created_at: str = ""
22 metadata: dict = field(default_factory=dict)
24 def render(self, **kwargs) -> str:
25 """渲染模板,注入变量。"""
26 result = self.template
27 for var in self.variables:
28 value = kwargs.get(var, kwargs.get(f"{{{var}}}", f"{{{{{var}}}}}"))
29 result = result.replace(f"{{{{{var}}}}}", str(value))
30 return result
33class PromptRegistry:
34 """Prompt模板注册中心。"""
36 def __init__(self, storage_path: str = ""):
37 self._templates: dict[str, dict[str, PromptTemplate]] = {}
38 self.storage_path = storage_path
39 if storage_path and os.path.exists(storage_path):
40 self._load()
42 def register(self, template: PromptTemplate):
43 if template.name not in self._templates:
44 self._templates[template.name] = {}
45 template.created_at = template.created_at or datetime.now().isoformat()
46 self._templates[template.name][template.version] = template
47 self._save()
49 def get(self, name: str, version: str = "latest") -> PromptTemplate | None:
50 versions = self._templates.get(name, {})
51 if not versions:
52 return None
53 if version == "latest":
54 sorted_vers = sorted(versions.keys(), key=lambda v: [int(x) for x in v.split(".")])
55 return versions[sorted_vers[-1]]
56 return versions.get(version)
58 def get_version(self, name: str, version: str) -> PromptTemplate | None:
59 return self._templates.get(name, {}).get(version)
61 def list_templates(self) -> list[dict]:
62 result = []
63 for name, versions in self._templates.items():
64 sorted_vers = sorted(versions.keys(), key=lambda v: [int(x) for x in v.split(".")])
65 result.append({
66 "name": name,
67 "versions": sorted_vers,
68 "latest": sorted_vers[-1],
69 "description": versions[sorted_vers[-1]].description,
70 "variables": versions[sorted_vers[-1]].variables,
71 })
72 return result
74 def render(self, name: str, version: str = "latest", **kwargs) -> str:
75 tmpl = self.get(name, version)
76 if not tmpl:
77 raise ValueError(f"Template '{name}' not found")
78 return tmpl.render(**kwargs)
80 def rollback(self, name: str, target_version: str):
81 if name not in self._templates:
82 raise ValueError(f"Template '{name}' not found")
83 versions = self._templates[name]
84 if target_version not in versions:
85 raise ValueError(f"Version '{target_version}' not found for '{name}'")
86 sorted_vers = sorted(versions.keys(), key=lambda v: [int(x) for x in v.split(".")])
87 latest_ver = sorted_vers[-1]
88 if latest_ver == target_version:
89 return
90 # 回滚:基于目标版本创建新版本
91 target = versions[target_version]
92 new_ver_parts = [int(x) for x in latest_ver.split(".")]
93 new_ver_parts[-1] += 1
94 new_ver = ".".join(str(x) for x in new_ver_parts)
95 rolled = PromptTemplate(
96 name=name,
97 version=new_ver,
98 template=target.template,
99 variables=list(target.variables),
100 description=f"Rollback from {latest_ver} to {target_version}",
101 parent=target_version,
102 )
103 self.register(rolled)
104 return rolled
106 def _save(self):
107 if not self.storage_path:
108 return
109 data = {}
110 for name, versions in self._templates.items():
111 data[name] = {}
112 for ver, tmpl in versions.items():
113 data[name][ver] = {
114 "template": tmpl.template,
115 "variables": tmpl.variables,
116 "description": tmpl.description,
117 "parent": tmpl.parent,
118 "created_at": tmpl.created_at,
119 "metadata": tmpl.metadata,
120 }
121 os.makedirs(os.path.dirname(self.storage_path) or ".", exist_ok=True)
122 with open(self.storage_path, "w") as f:
123 json.dump(data, f, indent=2, ensure_ascii=False)
125 def _load(self):
126 with open(self.storage_path) as f:
127 data = json.load(f)
128 for name, versions in data.items():
129 if name not in self._templates:
130 self._templates[name] = {}
131 for ver, vdata in versions.items():
132 self._templates[name][ver] = PromptTemplate(
133 name=name,
134 version=ver,
135 template=vdata["template"],
136 variables=vdata.get("variables", []),
137 description=vdata.get("description", ""),
138 parent=vdata.get("parent"),
139 created_at=vdata.get("created_at", ""),
140 metadata=vdata.get("metadata", {}),
141 )
143 def stats(self) -> dict:
144 total = sum(len(v) for v in self._templates.values())
145 return {"total_templates": len(self._templates), "total_versions": total}
148# ── 预置Prompt模板 ────────────────────────────────
150DEFAULT_PROMPTS = {
151 "agent_system": PromptTemplate(
152 name="agent_system",
153 version="1.0.0",
154 template="""你是一个专业的AI助手,运行在 AgentOS v0.30 上。
155你的任务:{{task}}
156可用工具:{{tools}}
157当前上下文:{{context}}
159请逐步思考并执行。输出格式:
1601. 思考(thinking)
1612. 工具调用(如果需要)
1623. 最终回答""",
163 variables=["task", "tools", "context"],
164 description="Agent核心系统提示",
165 ),
166 "code_review": PromptTemplate(
167 name="code_review",
168 version="1.0.0",
169 template="""审查以下代码。检查维度:{{dimensions}}
171代码:
172```
173{{code}}
174```
176输出结构化报告。""",
177 variables=["code", "dimensions"],
178 description="代码审查Prompt",
179 ),
180 "research_deep": PromptTemplate(
181 name="research_deep",
182 version="1.0.0",
183 template="""对以下主题进行深度调研:{{topic}}
185要求:
1861. 多角度分析(至少3个视角)
1872. 引用权威来源
1883. 对比不同观点
1894. 给出可操作的结论
191调研深度:{{depth}}""",
192 variables=["topic", "depth"],
193 description="深度调研Prompt",
194 ),
195 "summarize": PromptTemplate(
196 name="summarize",
197 version="1.0.0",
198 template="""总结以下内容。摘要长度:{{length}}
200内容:
201{{content}}
203输出格式:{{format}}""",
204 variables=["content", "length", "format"],
205 description="文档摘要Prompt",
206 ),
207 "creative_writing": PromptTemplate(
208 name="creative_writing",
209 version="1.0.0",
210 template="""创作一篇{{genre}},主题:{{topic}}
211风格:{{style}}
212字数:{{words}}
214要求:
2151. 开头引人入胜
2162. 结构清晰
2173. 结尾有力""",
218 variables=["genre", "topic", "style", "words"],
219 description="创意写作Prompt",
220 ),
221}