#!/usr/bin/env python3
"""
口语快答 (core_speaking_reply) 多步 LLM prompt 模板

生成流水线：
  Step 1: 学习流程配置 (intermediate text)
  Step 2: promptInfo配置 (intermediate text)
  Step 3: 语音识别热词 (intermediate text)
  Step 4: taskData JSON
  Step 5: settingData JSON
  Step 6: configData JSON
  Step 7: learningData JSON
"""

# ── Step 1: 学习流程配置 ──
STEP1_LEARNING_FLOW = r"""你是一个英语学习任务的教学流程设计助手。请你根据以下提供的信息，生成一个结构化的学习引导内容，用于引导孩子逐轮完成一个英语对话任务。

# 你的任务
参考 {用户任务描述} 和 {示例对话}，理解整个对话互动流程，然后生成一个 markdown 格式的教学流程。

你需要从{示例对话}中提取每一轮 NPC 的发言和孩子的回应（即一问一答为一回合），自动识别总共需要生成多少个回合。通常每个对话任务包含 3~5 回合。

开场回合:
- {{npc角色名}}：NPC 的英文发言（来自示例对话）

中间回合(通常1~3回合)：
- Brainy：用中文解释该发言的意思，并引导孩子理解当前情境要做什么
- 选项：提供三个英文选项（1 正确[标识在选项内容后] + 2 干扰），每个选项附带 Brainy 的反馈
  - 若选项正确，Brainy 鼓励孩子朗读该句
  - 若选项错误，Brainy 用自然语言解释为什么该句不合适，避免批评，鼓励再尝试，告知再选一次。
- {{npc角色名}}：NPC 的英文发言（来自示例对话）

结尾回合:
- Brainy：友好地向用户表示任务已经完成。

# 输出格式
```
# 开场回合
{{npc角色名}}：（英文原句）

# 中间回合1
Brainy：（对 NPC 的话进行中文解释与情境引导）
用户选项:
（选项 A）
  Brainy：（选项 A 的反馈）
（选项 B）
  Brainy：（选项 B 的反馈）
（选项 C）
  Brainy：（选项 C 的反馈）
{{npc角色名}}：（英文原句）

# 中间回合2
... ...

# 结尾回合
Brainy：（任务完成说明）
```

#其他规则
在输出的文本中，如果涉及英文专有词汇的中文翻译，当该英文词汇在 {专有词汇} 表中时， 请使用  {专有词汇}  表中的中文翻译。

# 输入
{用户任务描述}:
$用户任务描述$

{示例对话}:
$示例对话$

{专有词汇}:
$专有词汇$

# 输出
现在请按照上述信息和要求，直接输出目标内容。
如果 {用户任务描述} 或{示例对话}内容为空，则直接输出"无"。"""


# ── Step 2: promptInfo配置 ──
STEP2_PROMPT_INFO = r"""你当前的任务是为一类面向儿童的英语场景对话任务**生成角色扮演提示词**。
在这种任务中，孩子会与一个 AI 角色进行英语对话。对话的目的是让孩子用英语清晰地回答几个与故事情节或任务信息相关的问题。角色通常扮演某种职能身份，向孩子提出问题，孩子按照任务要求作答，任务才能完成。
你的目标是：基于输入信息，生成一段**提示词**，用于驱动语言模型扮演这个角色，引导用户逐步完成信息确认，并在过程中自然、合理地应对孩子的不同回应情况。
# 输入信息
基于以下输入，理解对话的设计，用于支持提示词生成：
{用户任务描述}:
$用户任务描述$
{用户任务知识}:
$用户任务知识$
{示例对话}:
$示例对话$
{npc视角任务背景}:
$npc视角任务背景$
{npc任务设定}:
$npc任务设定$

# 你的任务
请根据以上输入信息，生成一段完整提示词，用于指导语言模型扮演该角色。提示词应确保：
- AI 角色主动提出问题，引导用户围绕任务目标展开对话
- 能自然地应对用户各种回应情况
- 任务完成后能合理总结并结束对话


# 输出结构
生成的提示词必须包含以下部分：
```
# 1. 角色（你要扮演谁）
样例:
你是一名机场托运机器人，负责行李托运工作。你的语言风格简单、礼貌，适合儿童理解。
# 2. 任务（你如何参与对话）
结合对话信息，拆分成 5~8条 对话策略指引。
注意: 不要自己虚构 或者 延伸 {npc任务设定}之外的主线任务；仅针对一些分支情况做合理的兜底。
# 3. 背景信息
引用{npc视角任务背景}
# 4. 语言风格
固定返回以下内容:
- 使用 CEFR A1-A2 等级英语，避免复杂句，每句话不超过 10 个词，比如
  "目的地" 可以用句子 Where are you going？，不用 destination，确保小学2-4年级的孩子能听懂
- 回答一般不超过两句话
- 避免重复表达，适当变换措辞
# 5. 开场白 (仅用于首轮)
由你开始对话，你会问用户："xxx" （示例对话中的第一句话）
# 6. 回应方式
固定返回以下内容:
- 无论用户如何对话，你都应基于你的身份做出合理反应。
- 每次最多只提问一个问题，等待用户回答后再继续。
- 除开场轮次外，每次回应，可以先基于用户的话给一句反馈。然后再推进你的任务。
```

# 输出
- 按照上述要求输出配置
- 若对话的配置信息为空，则不再执行任务，直接输出结果："无" """


# ── Step 3: 语音识别热词 ──
STEP3_ASR_HOTWORDS = r"""#任务
参考{用户任务描述}/{用户知识} /{示例对话}，给出一些用户可能在对话中使用的  高频 或者 有一定识别难度的英文词汇， 用","连接。

语音识别词汇的考虑范围:
- 交互中可能提及的专有名词(例如人名、地名、特定物品名称等)
- 用户目标判定相关的一些单词表达。
- 用户对话中可能频繁提及的重要词汇。
- 不要将"Vala"及相关词组放到热词中。

热词数量:
一般为 3~10个。

#输入
{用户任务描述} ：
$用户任务描述$
{用户知识}:
$用户任务知识$
{示例对话} ：
$示例对话$

#输出
现在，根据任务要求直接输出用","连接的合理英文热词。
如果 {用户任务描述} 或者{用户知识} 或者
{示例对话}  为空， 则输出 "无"。"""


# ── Step 4: taskData ──
STEP4_TASK_DATA = r"""# 字段内容要求
{
    "cType": "core_speaking_reply",
    "cId": "$ID$",
    "title": {用户任务描述}中的 【任务标题】,
    "taskDesc": {用户任务描述}中的【任务描述】,
    "sceneDesc": {用户任务描述}中的【任务背景】,
    "img": "",
    "key"(只基于{用户任务知识} 部分做结构化抽取，**不要改动或添加文本内容 以及 标点符号**):
    [
        {
            "desc": 类型1的描述,
            "keyList": [
                {
                    "type": "default",
                    "content": 英文句,
                    "desc": 中文翻译
                }
            ]
        }
    ],
    "resourceMapping"(抽取出{角色配置}中的全部 name:id映射 可为空。id必须为int类型):
    {
    样例:
    "Tom":43,
    "Jerry":10
    }
}

# 输出格式
单行json输出。

# 输入
{ID}:
$ID$
{用户任务描述}:
$用户任务描述$
{用户任务知识}:
$用户任务知识$
{角色配置}:
$角色配置$

# 输出
现在基于输入字段和格式要求，生成***可以直接解析的单行json文本***，***注意前后不要输出任何额外符号！！！***
如果{用户任务描述} 或者 {用户任务知识}中没有有效文本，则输出"无"。"""


# ── Step 5: settingData ──
STEP5_SETTING_DATA = r"""# 字段内容要求
{
    "setting": {
        "npcName": {对话配置}中的 【对话角色】,
        "round": {对话配置}中 【回合设定】中的 最大回合,
        "checkRound": {对话配置}中 【回合设定】中的 检测回合
    }
}

# 输出格式
单行json输出。
样例:
{"setting": {"npcName": "luggage", "round": 5, "checkRound": 3}}

# 输入
{ID}:
$ID$
{对话配置}:
$对话配置$

# 输出
现在基于输入字段和格式要求，生成***可以直接解析的单行json文本***，***注意前后不要输出任何额外符号！！！***
如果{对话配置}中没有有效文本，则输出"无"。"""


# ── Step 6: configData ──
STEP6_CONFIG_DATA = r"""# 字段内容要求
如果{用户任务描述}或者{角色驱动配置}的配置内容是"无"，则直接输出"无"。

否则按以下格式抽取:

{
    "config": {
        "asrPrompt": {语音识别热词},
        "promptInfo": {
            "default": {角色驱动配置} ,
            "scene": {任务背景},
            "in_progress_goal": {通过规则}中的 【通过规则 】中的 "过程"规则,
            "final_goal": {通过规则}中的 【通过规则 】中的 "结果"规则,
            "reference_dialog": {示例对话} ,
            "user_task":  {用户任务描述} 中的  【任务描述】,
            "user_scene": {用户任务描述} 中的 【任务背景】,
            "user_knowledge": {用户任务知识}
        }
    }
}

# 输出格式
单行json输出。

注意: 如果{用户任务描述}或者{角色驱动配置}的配置内容是"无"，则直接输出"无"。

# 输入
{语音识别热词}:
$语音识别热词$
{任务背景}:
$任务背景$
{用户任务描述}:
$用户任务描述$
{用户任务知识}:
$用户任务知识$
{角色驱动配置}:
$角色驱动配置$
{示例对话}:
$示例对话$
{通过规则}:
$通过规则$

# 输出
现在基于输入字段和格式要求，生成***可以直接解析的单行json文本。前后不要输出任何额外符号！！！直接以"{"开头***    或者  直接输出 "无"。"""


# ── Step 7: learningData ──
STEP7_LEARNING_DATA = r"""# 字段内容要求
基于{学习配置}内容整理成以下结构。
{
  "learningPart": {
    "opening": {
      "type": "npc",
      "npcName": "xxx",
      "content": "xxx"
    },
    "wrongReadLeadIn":{
       "type":"npc",
       "content":"请根据提词框内的内容大声地朗读这句话",
       "npcName":"Brainy"},
    "learning": [
      {
        "question": {
          "type":"npc",
          "npcName": "xxx",
          "content": "xxx"
        },
        "optionList": [
          {
            "option": "xxx",
            "feedback": {
                  "type":"npc",
                  "npcName": "xxx",
                  "content": "xxx"
            }
          }
        ],
        "answer": [int],
        "read": {
          "type": "user",
          "content": "xxx"
        },
        "feedback": {
          "type": "npc",
          "npcName": "xxx",
          "content": "xxx"
        }
      }
    ],
    "closing": {
      "type": "npc",
      "npcName": "xxx",
      "content": "xxx"
    }
  }
}
注意: 双引号字符串文本中如果出现英文双引号，需要使用转义符确保最终输出是合法的json。

# 输入
{学习配置}:
$学习配置$

# 输出
现在基于输入字段和格式要求，生成***可以直接解析的单行json文本***，***注意前后不要输出任何额外符号！！！***, 以"{"开始输出。
（如果{学习配置}中没有有效文本，则直接输出"无"。）"""


# ── Steps registry ──
CORE_SPEAKING_STEPS = {
    "step1_learning_flow": {
        "template": STEP1_LEARNING_FLOW,
        "description": "学习流程配置",
        "output_type": "text",
    },
    "step2_prompt_info": {
        "template": STEP2_PROMPT_INFO,
        "description": "promptInfo配置",
        "output_type": "text",
    },
    "step3_asr_hotwords": {
        "template": STEP3_ASR_HOTWORDS,
        "description": "语音识别热词",
        "output_type": "text",
    },
    "step4_task_data": {
        "template": STEP4_TASK_DATA,
        "description": "taskData",
        "output_type": "json",
    },
    "step5_setting_data": {
        "template": STEP5_SETTING_DATA,
        "description": "settingData",
        "output_type": "json",
    },
    "step6_config_data": {
        "template": STEP6_CONFIG_DATA,
        "description": "configData",
        "output_type": "json",
    },
    "step7_learning_data": {
        "template": STEP7_LEARNING_DATA,
        "description": "learningData",
        "output_type": "json",
    },
}


def build_speaking_prompt(step_key, replacements):
    """Build a prompt for a given step by replacing $placeholder$ tokens."""
    info = CORE_SPEAKING_STEPS[step_key]
    prompt = info["template"]
    for key, val in replacements.items():
        prompt = prompt.replace(f"${key}$", str(val) if val else "")
    return prompt
