chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:19:22 +08:00
commit 6366859bb3
222 changed files with 47313 additions and 0 deletions

View File

@@ -0,0 +1,219 @@
"""生成时由服务端注入的规则,对用户不可见。"""
from __future__ import annotations
import re
from typing import Any
# 用户明确不要按截图还原时才退出忠实模式
_SCREENSHOT_OPT_OUT = (
"不要还原截图",
"不按截图",
"无需还原截图",
"忽略截图布局",
"自定义布局",
"改成通用后台",
"不要一模一样",
"重新设计界面",
"不要照搬截图",
)
_SCREENSHOT_OPT_IN = (
"还原截图",
"对齐截图",
"一模一样",
"截图还原",
"screenshot_faithful",
)
DEFAULT_ACTION_LABELS: dict[str, str] = {
"create": "新增",
"refresh": "刷新",
"import": "导入 Excel",
"export": "导出 Excel",
"edit": "编辑",
"delete": "删除",
"save": "保存",
"cancel": "取消",
"back": "返回",
"search": "查询",
}
# 较长别名优先匹配
_ACTION_ALIASES: list[tuple[str, str]] = [
("导入 excel", "import"),
("导出 excel", "export"),
("导入xlsx", "import"),
("导出xlsx", "export"),
("导入csv", "import"),
("导出csv", "export"),
("导入", "import"),
("导出", "export"),
("新建", "create"),
("新增", "create"),
("添加", "create"),
("创建", "create"),
("刷新", "refresh"),
("重载", "refresh"),
("编辑", "edit"),
("修改", "edit"),
("删除", "delete"),
("移除", "delete"),
("保存", "save"),
("取消", "cancel"),
("返回", "back"),
("查询", "search"),
("搜索", "search"),
("筛选", "search"),
]
SYSTEM_RULES = """
[系统规则 · 用户不可见]
你是业务前端生成器。输出 App Blueprintentities / apis / pages / widgets
适用于任意行业,禁止把所有应用生成成同一种通用绿主题 CRUD 壳。
还原原则(全局):
- 有截图/HTML默认一模一样meta.ui_preset=screenshot_faithfuldashboard layout.preset=screenshot_faithful
用户文字点名要改的才改;未提及的顶栏/导航/筛选/图表/表格/按键/配色保持与截图一致。
- 生成闭环(有截图时由服务端自动执行):视觉模型摘录 → 代码模型出蓝图 → **发布并截取 Web 真页面**
→ 视觉模型对照原图与真页面打分 → 按差异再改蓝图直到还原度≥95%
环境变量FIDELITY_TARGET / FIDELITY_MAX_ROUNDS / FIDELITY_REAL_SCREEN
- 无截图:严格按用户文字与数据字段还原;勿套默认「记录列表/概览」。
- 业务名称、平台抬头、页签、按钮文案必须来自用户/截图/HTML禁止擅自换成套话。
- 尽量写全 meta.platform_title / project_context / ui.nav_items / ui.filter_radios /
ui.select_field / ui.filter_hint / ui.*_side_label / ui.table_title无材料则留空禁止编造行业词
其它:
- 功能按键写入 pages[].layout.actions + action_labels原文显示。
- 数据Excel/CSV 表头→字段JSON list→表并行数组先展平再关联。snake_case高基数不做 enum。
- RESTGET/POST/PUT/DELETE看板 widgetskpi/line_chart/status_strip/table/bar/pie按截图或需求取舍。
- 全量导入:校验失败只记错误,不提前中断。
""".strip()
SCREENSHOT_RULES = """
[截图忠实 · 用户不可见 · 适用于任意业务]
已上传界面截图。目标展示页与截图一模一样screenshot_faithful
必须从截图提取并写入蓝图:平台抬头、系统名称、页签文案、筛选控件(单选/下拉)、工具栏按键原文、
主区分区(含左侧竖标签文案)、图表类型与系列名、表格列名。
主图为「着色测点/短棒 + 设计曲线」时meta.ui.chart_style=section_marks并写
value_field / color_field / design_field / legend_items / y_axis_label / invert_y。
监督条为上灰下绿双段时status_strip.variant=stacked_daysvalue_field+secondary_fieldcycle_days。
禁止只有侧标「主图」却不配 line_chart widget 或让主图区空白。
多张图若为同一页展开/收起/滚动态:合并为一套布局,以信息最完整的一帧为准,不要生成两套互斥页面。
禁止套用其它行业模板(含铁路沉降专用文案),除非截图本身就是该业务。
""".strip()
def screenshot_opted_out(prompt: str) -> bool:
p = prompt or ""
return any(k in p for k in _SCREENSHOT_OPT_OUT)
def wants_screenshot_layout(prompt: str, image_count: int, html_count: int = 0) -> bool:
"""有截图或页面 HTML 即默认忠实还原;用户显式退出除外。"""
p = prompt or ""
if screenshot_opted_out(p):
return False
if image_count > 0 or html_count > 0:
return True
if any(k in p for k in _SCREENSHOT_OPT_IN):
return True
return False
def _map_button_label(raw: str) -> tuple[str, str] | None:
"""返回 (action_key, display_label)。"""
label = re.sub(r"\s+", " ", (raw or "").strip()).strip("。.;")
if not label or len(label) > 32:
return None
low = label.lower().replace(" ", "")
for alias, key in _ACTION_ALIASES:
a = alias.replace(" ", "")
if low == a or low.startswith(a) or a in low:
return key, label
slug = re.sub(r"[^a-z0-9_]+", "_", low)[:24].strip("_") or "action"
return f"custom_{slug}", label
def parse_action_bar(prompt: str) -> dict[str, Any]:
"""从用户说明/截图理解中解析功能按键,得到 actions 顺序与 action_labels。"""
p = prompt or ""
labels: dict[str, str] = {}
order: list[str] = []
def add(raw: str) -> None:
mapped = _map_button_label(raw)
if not mapped:
return
key, lab = mapped
if key not in labels:
order.append(key)
labels[key] = lab
for m in re.finditer(
r"(?:操作|功能按键|按键|按钮|工具栏|工具条)[:]\s*(.+)",
p,
):
chunk = m.group(1).splitlines()[0]
chunk = re.split(r"[。;;(]", chunk, 1)[0]
for part in re.split(r"[、,,/|]\s*", chunk):
add(part)
for m in re.finditer(
r"(?:包含|含有|提供|支持)[:]?\s*((?:新增|新建|刷新|导入|导出|编辑|删除|查询|保存)[^。\n]{0,40})",
p,
):
for part in re.split(r"[、,,/|]\s*", m.group(1)):
add(part)
for m in re.finditer(r"(?:工具栏|顶栏)?按钮[:\s]+(.+)", p):
chunk = m.group(1).splitlines()[0][:80]
for part in re.split(r"[、,,/|\s]{1,}", chunk):
if part.strip():
add(part)
if not order:
return {}
for k in ("edit", "delete"):
if k not in labels:
labels[k] = DEFAULT_ACTION_LABELS[k]
toolbar = [k for k in order if k not in ("edit", "delete")]
actions = toolbar + [k for k in ("edit", "delete") if k in labels]
seen: set[str] = set()
uniq: list[str] = []
for k in actions:
if k not in seen:
seen.add(k)
uniq.append(k)
return {"actions": uniq, "action_labels": labels, "toolbar_from_user": True}
def compose_effective_prompt(
user_prompt: str,
image_count: int,
vision_text: str = "",
html_layout_text: str = "",
html_count: int = 0,
) -> str:
"""把系统规则拼进有效提示词;控制台只传用户原文。"""
parts = [SYSTEM_RULES]
if wants_screenshot_layout(user_prompt, image_count, html_count):
parts.append(SCREENSHOT_RULES)
if html_count:
parts.append(
"[HTML 布局优先 · 用户不可见]\n"
"已上传浏览器另存为的 HTML/MHTML。文案与控件名以 HTML 为准;分区与配色以截图为准。"
)
user = (user_prompt or "").strip()
if user:
parts.append("[用户需求]\n" + user)
else:
parts.append("[用户需求]\n(未填写文字需求:仅依据数据文件、截图与 HTML 生成。)")
if html_layout_text:
parts.append(html_layout_text)
if vision_text:
parts.append("[截图理解]\n" + vision_text)
return "\n\n".join(parts)