# MIT License # Copyright (c) 2024 """LLM 抽象与 Dummy 实现。""" from abc import ABC, abstractmethod from typing import Any, Dict, List import yaml from .schema import DSLSpec, EventRecord PROMPT_TEMPLATE = """你是一名自动化工程师,请将以下事件序列归纳为可参数化的自动化 DSL。 事件序列使用 JSON 描述,每个事件包含 kind、control(AutomationId/Name/ClassName/ControlType/BoundingRect)等。 输出 YAML,字段包括:params、steps、assertions、retry_policy、waits,支持 steps 内的 if/else、for_each。 输出示例: params: text: "示例参数" steps: - action: click target: {{AutomationId: "15", ControlType: "Edit"}} - action: type target: {{AutomationId: "15"}} text: "{{text}}" assertions: - "输入框非空" retry_policy: {{max_attempts: 2, interval: 1.0}} waits: {{appear: 5.0, disappear: 5.0}} 现在请基于输入事件生成 YAML:""" class LLMClient(ABC): """LLM 抽象接口。""" @abstractmethod def generate(self, events: List[EventRecord]) -> DSLSpec: """将事件序列转为 DSL 规格。""" class DummyLLM(LLMClient): """离线 dummy,实现一个简单的规则映射。""" def generate(self, events: List[EventRecord]) -> DSLSpec: steps: List[Dict[str, Any]] = [] for ev in events: ctrl = ev.control.dict(by_alias=True) if ev.control else {} if ev.kind == "mouse_click": steps.append({"action": "click", "target": ctrl}) elif ev.kind == "key_down" and ev.data.get("name"): # 仅在按键时记录输入 steps.append({"action": "type", "target": ctrl, "text": ev.data.get("name")}) if not steps: steps.append({"action": "assert_exists", "target": {"Name": "dummy"}}) spec = DSLSpec( params={}, steps=steps, assertions=["dummy generated"], ) return spec def render_prompt(events: List[EventRecord]) -> str: """把事件序列渲染到 prompt。""" event_dicts = [ev.dict(by_alias=True) for ev in events] return f"{PROMPT_TEMPLATE}\n\n{yaml.safe_dump(event_dicts, allow_unicode=True)}"