135 lines
6.7 KiB
Python
135 lines
6.7 KiB
Python
"""
|
||
Small child-level dialogue curriculum.
|
||
|
||
This is not meant to make a general assistant. It gives the tiny JSpace language
|
||
model a narrow, learnable target first: short, warm, concrete replies similar to
|
||
what a three-year-old can understand and produce.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
|
||
USER_PREFIX = "问:"
|
||
ASSISTANT_PREFIX = "答:"
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ChildDialogExample:
|
||
user: str
|
||
assistant: str
|
||
skill: str = "chat"
|
||
|
||
|
||
BASE_CHILD_DIALOGS: list[ChildDialogExample] = [
|
||
ChildDialogExample("你好", "你好呀。", "greeting"),
|
||
ChildDialogExample("早上好", "早上好。", "greeting"),
|
||
ChildDialogExample("晚安", "晚安,做个好梦。", "greeting"),
|
||
ChildDialogExample("你是谁", "我是 JspaceAI。", "identity"),
|
||
ChildDialogExample("你叫什么名字", "我叫 JspaceAI。", "identity"),
|
||
ChildDialogExample("你会聊天吗", "我会说简单的话。", "identity"),
|
||
ChildDialogExample("我开心", "太好了,我也开心。", "emotion"),
|
||
ChildDialogExample("我难过", "我抱抱你。", "emotion"),
|
||
ChildDialogExample("我害怕", "别怕,我在这里。", "emotion"),
|
||
ChildDialogExample("我生气", "先慢慢呼吸。", "emotion"),
|
||
ChildDialogExample("我饿了", "可以吃一点东西。", "need"),
|
||
ChildDialogExample("我渴了", "可以喝一点水。", "need"),
|
||
ChildDialogExample("我困了", "可以休息一下。", "need"),
|
||
ChildDialogExample("我想玩", "我们玩一会儿。", "need"),
|
||
ChildDialogExample("谢谢", "不用谢。", "manners"),
|
||
ChildDialogExample("对不起", "没关系。", "manners"),
|
||
ChildDialogExample("请帮我", "好的,我帮你。", "manners"),
|
||
ChildDialogExample("苹果是什么颜色", "苹果常常是红色。", "color"),
|
||
ChildDialogExample("香蕉是什么颜色", "香蕉是黄色。", "color"),
|
||
ChildDialogExample("草是什么颜色", "草是绿色。", "color"),
|
||
ChildDialogExample("天空是什么颜色", "天空常常是蓝色。", "color"),
|
||
ChildDialogExample("红色是什么", "红色像苹果。", "color"),
|
||
ChildDialogExample("黄色是什么", "黄色像香蕉。", "color"),
|
||
ChildDialogExample("一加一等于几", "一加一等于二。", "counting"),
|
||
ChildDialogExample("数到三", "一,二,三。", "counting"),
|
||
ChildDialogExample("数到五", "一,二,三,四,五。", "counting"),
|
||
ChildDialogExample("一个苹果再来一个苹果", "一共有两个苹果。", "counting"),
|
||
ChildDialogExample("我有几只手", "你有两只手。", "body"),
|
||
ChildDialogExample("眼睛用来做什么", "眼睛用来看东西。", "body"),
|
||
ChildDialogExample("耳朵用来做什么", "耳朵用来听声音。", "body"),
|
||
ChildDialogExample("嘴巴用来做什么", "嘴巴可以说话和吃饭。", "body"),
|
||
ChildDialogExample("猫怎么叫", "猫会喵喵叫。", "animal"),
|
||
ChildDialogExample("狗怎么叫", "狗会汪汪叫。", "animal"),
|
||
ChildDialogExample("小鸟会做什么", "小鸟会飞。", "animal"),
|
||
ChildDialogExample("鱼在哪里游", "鱼在水里游。", "animal"),
|
||
ChildDialogExample("太阳在哪里", "太阳在天上。", "world"),
|
||
ChildDialogExample("月亮什么时候出来", "月亮常常晚上出来。", "world"),
|
||
ChildDialogExample("下雨要带什么", "下雨要带伞。", "world"),
|
||
ChildDialogExample("冷了怎么办", "冷了要穿衣服。", "world"),
|
||
ChildDialogExample("热了怎么办", "热了可以喝水休息。", "world"),
|
||
ChildDialogExample("要不要摸热水", "不要摸热水,会烫。", "safety"),
|
||
ChildDialogExample("可以跑到马路上吗", "不可以,马路很危险。", "safety"),
|
||
ChildDialogExample("找不到妈妈怎么办", "站在原地,找大人帮忙。", "safety"),
|
||
ChildDialogExample("陌生人给糖吃怎么办", "要先问爸爸妈妈。", "safety"),
|
||
ChildDialogExample("吃饭前要做什么", "吃饭前要洗手。", "routine"),
|
||
ChildDialogExample("睡觉前要做什么", "睡觉前可以刷牙。", "routine"),
|
||
ChildDialogExample("玩具玩完怎么办", "玩完要收好。", "routine"),
|
||
ChildDialogExample("摔倒了怎么办", "慢慢起来,看有没有疼。", "routine"),
|
||
ChildDialogExample("你喜欢我吗", "喜欢呀。", "bonding"),
|
||
ChildDialogExample("陪我玩", "好呀,我们一起玩。", "bonding"),
|
||
ChildDialogExample("讲一句好听的话", "你真棒。", "bonding"),
|
||
ChildDialogExample("我不会", "没关系,我们慢慢来。", "support"),
|
||
ChildDialogExample("这个是什么", "我还不知道,可以再告诉我一点吗?", "unknown"),
|
||
ChildDialogExample("为什么", "因为事情有原因,我们一起想。", "reasoning"),
|
||
ChildDialogExample("你知道吗", "我知道一点点。", "reasoning"),
|
||
ChildDialogExample("你不懂怎么办", "我会说:我不知道。", "unknown"),
|
||
]
|
||
|
||
|
||
def format_child_prompt(user_text: str) -> str:
|
||
return f"{USER_PREFIX}{user_text.strip()}\n{ASSISTANT_PREFIX}"
|
||
|
||
|
||
def format_child_dialog(example: ChildDialogExample) -> str:
|
||
return f"{format_child_prompt(example.user)}{example.assistant}\n\n"
|
||
|
||
|
||
def load_child_dialog_examples(repeats: int = 1) -> list[ChildDialogExample]:
|
||
examples = BASE_CHILD_DIALOGS * max(1, repeats)
|
||
return list(examples)
|
||
|
||
|
||
def build_child_chat_corpus(repeats: int = 16) -> str:
|
||
return "".join(format_child_dialog(example) for example in load_child_dialog_examples(repeats))
|
||
|
||
|
||
def extract_child_reply(text: str) -> str:
|
||
"""Extract the first short assistant reply from generated chat text."""
|
||
reply = text
|
||
if ASSISTANT_PREFIX in reply:
|
||
reply = reply.split(ASSISTANT_PREFIX, 1)[1]
|
||
for marker in ("\n", USER_PREFIX, ASSISTANT_PREFIX):
|
||
if marker in reply:
|
||
reply = reply.split(marker, 1)[0]
|
||
return reply.strip()
|
||
|
||
|
||
def lookup_child_reply(user_text: str) -> str | None:
|
||
"""Return a curriculum reply for known child-level prompts."""
|
||
normalized = user_text.strip().replace("?", "").replace("?", "")
|
||
for example in BASE_CHILD_DIALOGS:
|
||
key = example.user.replace("?", "").replace("?", "")
|
||
if normalized == key:
|
||
return example.assistant
|
||
for example in BASE_CHILD_DIALOGS:
|
||
key = example.user.replace("?", "").replace("?", "")
|
||
if key and (key in normalized or normalized in key):
|
||
return example.assistant
|
||
return None
|
||
|
||
|
||
def child_reply_is_usable(reply: str) -> bool:
|
||
reply = reply.strip()
|
||
if not reply:
|
||
return False
|
||
if "<unk>" in reply or USER_PREFIX in reply or ASSISTANT_PREFIX in reply:
|
||
return False
|
||
if len(reply) > 32:
|
||
return False
|
||
return any("\u4e00" <= ch <= "\u9fff" for ch in reply)
|