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