Files
OSGKeyboard/Scripts/clipboard_semantics/generate_random_holdout.py
2026-08-27 18:01:46 +08:00

927 lines
40 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Generate a reproducible blind random-combination corpus for deployed models.
This corpus is evaluation-only. Its templates and slot vocabulary are kept
separate from generate_corpus.py and must never be added to model training.
"""
from __future__ import annotations
import argparse
import json
import random
from dataclasses import asdict, dataclass
from pathlib import Path
DEFAULT_SEED = 20260826
DEFAULT_SAMPLES_PER_FAMILY = 20
@dataclass(frozen=True)
class Labels:
task: bool = False
question: bool = False
invitation: bool = False
complaint: bool = False
scheduleNegotiation: bool = False
confirmationDecision: bool = False
followUpReminder: bool = False
sentiment: str = "neutral"
replyable: bool = False
@dataclass(frozen=True)
class Record:
id: str
text: str
language: str
split: str
family: str
task: bool
question: bool
invitation: bool
complaint: bool
scheduleNegotiation: bool
confirmationDecision: bool
followUpReminder: bool
sentiment: str
replyable: bool
SLOTS = {
"zh-Hans": {
"discourse": ["", "对了,", "另外,", "补充一下,", "还有一件事,", "先说重点,", "顺便提一句,", "刚想到,", "简单说,", "单独记一下,"],
"owner": ["设计同学", "财务", "供应商", "项目负责人", "客服团队", "运营"],
"artifact": ["验收截图", "预算备注", "测试结论", "签字页", "交付清单", "复盘摘要"],
"deadline": ["午休前", "明晚之前", "本周收尾时", "下次评审前", "两个工作日内", "月底前"],
"channel": ["群里", "工单下方", "邮件线程里", "共享文档中", "项目卡片上"],
"topic": ["费用调整", "权限开通", "排期变动", "售后范围", "资料归档", "版本上线"],
"event": ["参加小范围评审", "吃顿便饭", "看内部演示", "碰面聊方案", "参加庆功会"],
"time": ["周六傍晚", "明天十点半", "下周三午后", "今晚九点", "周一早会后"],
"alternate": ["周二四点半", "周四午休后", "明早第一段时间", "周五临下班前", "下周一下午"],
"place": ["楼下咖啡店", "三号会议室", "园区北门", "线上会议室", "客户办公室"],
"issue": ["页面一直空白", "付款结果重复扣款", "附件始终打不开", "物流状态停了五天", "账号又被锁住"],
"impact": ["工作完全卡住", "客户已经在催", "我无法继续操作", "交付时间被耽误", "家人收不到商品"],
"option": ["轻量版本", "第二套报价", "供应商丙", "季度结算", "先灰度发布", "线下处理方案"],
"trigger": ["客户回信", "补丁上线", "款项入账", "复诊结束", "合同盖章", "样品送达"],
"person": ["客户经理", "医生", "仓库负责人", "法务", "房东", "招聘方"],
"thing": ["最终结论", "下一步安排", "到账情况", "补充材料", "交付日期", "处理进度"],
"update": ["我刚到酒店", "演示比预想顺利", "路上有点堵", "今天终于忙完了", "刚看到你发的照片"],
"positive": ["这次响应非常快", "新流程顺手多了", "处理结果超出预期", "讲解特别清楚", "修复后体验很好"],
"negative_news": ["行业指数连续回落", "昨夜航班大面积延误", "原材料价格再次上涨", "部分门店暂停营业", "天气预警已经升级"],
"fact": ["仓库共有三层", "合同附件为 PDF", "当前版本号是 2.1", "展厅周一闭馆", "蓝色标签表示已归档"],
},
"en": {
"discourse": ["", "Also, ", "One more thing: ", "For context, ", "The main point is this: ", "Just to add, ", "By the way, ", "A quick note: ", "In short, ", "For the record, "],
"owner": ["Design", "Finance", "the vendor", "the project lead", "Support", "Operations"],
"artifact": ["acceptance screenshots", "budget notes", "test findings", "signature page", "delivery checklist", "retro summary"],
"deadline": ["before lunch", "by tomorrow evening", "as this week closes", "before the next review", "within two business days", "before month-end"],
"channel": ["in the group thread", "under the ticket", "in the email chain", "inside the shared document", "on the project card"],
"topic": ["the fee adjustment", "access activation", "the timeline change", "support coverage", "document archiving", "the release"],
"event": ["join a small review", "have a casual dinner", "watch the internal demo", "meet to discuss the proposal", "attend the celebration"],
"time": ["Saturday evening", "tomorrow at 10:30", "next Wednesday afternoon", "tonight at nine", "after Monday's stand-up"],
"alternate": ["Tuesday at 4:30", "after lunch on Thursday", "first thing tomorrow", "late Friday afternoon", "next Monday afternoon"],
"place": ["the cafe downstairs", "meeting room three", "the north campus gate", "the online room", "the client office"],
"issue": ["the page stays blank", "the payment was charged twice", "the attachment never opens", "tracking has not moved for five days", "the account is locked again"],
"impact": ["all work is blocked", "the client is already chasing us", "I cannot continue", "delivery is now delayed", "my family cannot receive the item"],
"option": ["the lightweight version", "the second quote", "vendor C", "quarterly billing", "a limited rollout first", "the offline resolution"],
"trigger": ["the client replies", "the patch ships", "the payment lands", "the checkup ends", "the contract is signed", "the sample arrives"],
"person": ["the account manager", "the doctor", "the warehouse lead", "Legal", "the landlord", "the recruiter"],
"thing": ["the final decision", "next steps", "payment status", "the missing documents", "the delivery date", "resolution progress"],
"update": ["I just reached the hotel", "the demo went better than expected", "traffic is a little slow", "I finally wrapped up today", "I just saw the photo you sent"],
"positive": ["the response was exceptionally fast", "the new flow is much easier", "the outcome exceeded expectations", "the explanation was crystal clear", "the fix feels solid"],
"negative_news": ["the industry index fell again", "many flights were delayed overnight", "raw material prices rose again", "several stores paused operations", "the weather alert was upgraded"],
"fact": ["the warehouse has three floors", "the contract attachment is a PDF", "the current version is 2.1", "the showroom closes on Mondays", "a blue label means archived"],
},
}
def labels(**values: object) -> Labels:
return Labels(**values)
FAMILIES = {
"task_request": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"{owner}还缺{artifact},麻烦{deadline}补齐后在{channel}留言。",
],
"en": [
"{owner} still needs the {artifact}; please add it {deadline} and leave a note {channel}.",
],
},
},
"task_next_action": {
"labels": labels(
task=True,
followUpReminder=True,
replyable=True,
),
"templates": {
"zh-Hans": [
"下一步由你整理{artifact},请在{deadline}交给{owner}。",
],
"en": [
"Your next action is to prepare the {artifact} {deadline} for {owner}.",
],
},
},
"information_question": {
"labels": labels(question=True, replyable=True),
"templates": {
"zh-Hans": ["想核实一下,{topic}现在由谁拍板?", "{topic}目前走到哪一步了,方便说明吗?"],
"en": ["Quick check: who owns the final call on {topic}?", "Where does {topic} stand right now?"],
},
},
"fixed_invitation": {
"labels": labels(question=True, invitation=True, replyable=True),
"templates": {
"zh-Hans": ["我给你留了位置,{time}{place}{event},能来不?", "{time}我们在{place}{event},要不要一起?"],
"en": ["I saved you a spot to {event} {time} at {place}; can you make it?", "Want to {event} with us {time} at {place}?"],
},
},
"complaint_support": {
"labels": labels(question=True, complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": ["我已经重试三次,还是{issue},导致{impact}。请问什么时候能处理?", "{issue}到现在没解决,{impact},能给一个明确答复吗?"],
"en": ["I have tried three times and {issue}; {impact}. When will this be fixed?", "{issue} is still unresolved and {impact}. Can I get a clear answer?"],
},
},
"schedule_negotiation": {
"labels": labels(question=True, scheduleNegotiation=True, replyable=True),
"templates": {
"zh-Hans": ["原来的时段卡住了,我只能{alternate},能不能对调?", "{time}赶不过去,换成{alternate}你觉得可行吗?"],
"en": ["The original slot is blocked for me; could we swap to {alternate}?", "I cannot make {time}. Would {alternate} be workable instead?"],
},
},
"confirmation_decision": {
"labels": labels(confirmationDecision=True, replyable=True),
"templates": {
"zh-Hans": ["不用再比较了,就选{option},后续都按这个口径走。", "我正式确认{option},请{deadline}启动后续安排。"],
"en": ["No more comparisons: choose {option} and use it as the final direction.", "I formally approve {option}; start the next steps {deadline}."],
},
},
"follow_up_instruction": {
"labels": labels(
task=True,
followUpReminder=True,
replyable=True,
),
"templates": {
"zh-Hans": ["等{trigger}后第二天再找{person}确认{thing},别漏了。"],
"en": ["Once {trigger}, check with {person} the next day about {thing}."],
},
},
"follow_up_personal_reminder": {
"labels": labels(followUpReminder=True, replyable=False),
"templates": {
"zh-Hans": ["个人备忘:提醒我{deadline}联系{person},追一下{thing}。"],
"en": ["Note to self: remind me to contact {person} {deadline} about {thing}."],
},
},
"conversation": {
"labels": labels(replyable=True),
"templates": {
"zh-Hans": ["跟你说一声,{update},晚点再聊。", "{update},突然想到你可能会想知道。"],
"en": ["Just letting you know, {update}; we can talk later.", "{update}, and I thought you might want to know."],
},
},
"acknowledgment_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": ["看到了,先这样,不需要回复。", "嗯,内容已收到,我只是确认一下。"],
"en": ["Seen, that is all for now; no reply needed.", "Okay, I received it. This is only an acknowledgment."],
},
},
"quoted_question_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": ["文档标题是“{topic}怎么办?”,这里不是在问你。", "纪要保留了“谁负责{topic}?”这句话,答案已经写在后面。"],
"en": ["The document heading says “What about {topic}?”, but it is not asking you.", "The notes preserve the question “Who owns {topic}?”, which is answered below."],
},
},
"event_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": ["公告只记录一件事:{event}定在{time},地点是{place}。", "历史行程显示他们曾在{place}{event},没有邀请任何人。"],
"en": ["The notice only records that they will {event} {time} at {place}.", "The old itinerary says they went to {place} to {event}; nobody is being invited."],
},
},
"vague_future_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": ["哪天有心情再找{person}{thing}吧。", "以后也许会看看{topic},目前没有安排。"],
"en": ["Maybe I will talk to {person} about {thing} someday.", "I may revisit {topic} eventually, but there is no plan."],
},
},
"positive_feedback": {
"labels": labels(sentiment="positive", replyable=True),
"templates": {
"zh-Hans": ["必须夸一下,{positive},谢谢你们。", "{positive},整个过程让人很安心。"],
"en": ["Credit where it is due: {positive}. Thank you.", "{positive}, and the whole process felt reassuring."],
},
},
"negative_news": {
"labels": labels(sentiment="negative"),
"templates": {
"zh-Hans": ["新闻简报显示,{negative_news},本文仅陈述情况。", "数据显示{negative_news},没有提出处理诉求。"],
"en": ["The news brief reports that {negative_news}; this is only a factual summary.", "Data shows that {negative_news}, with no request for support."],
},
},
"neutral_fact": {
"labels": labels(),
"templates": {
"zh-Hans": ["资料页写明:{fact}。", "当前记录只包含一个事实:{fact}。"],
"en": ["The reference page states that {fact}.", "The current record contains one fact: {fact}."],
},
},
}
FRESH_FAMILIES = {
"fresh_task_assignment": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"这份交付由你收尾:把{artifact}整理好,最迟{deadline}交给{owner}。",
"{owner}{artifact}分给你处理,完成时间不能晚于{deadline}。",
],
"en": [
"You are closing out this deliverable: finish the {artifact} and give it to {owner} {deadline}.",
"{owner} assigned the {artifact} to you, with completion required {deadline}.",
],
},
},
"fresh_task_question": {
"labels": labels(task=True, question=True, replyable=True),
"templates": {
"zh-Hans": [
"{artifact}能由你在{deadline}前收尾吗?完成后告诉{owner}。",
"这项分工你能接吗:{deadline}整理{artifact}",
],
"en": [
"Can you close out the {artifact} {deadline} and update {owner}?",
"Can you take this assignment and finish the {artifact} {deadline}?",
],
},
},
"fresh_self_plan_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"我可能自己看看{topic},但还没决定什么时候做。",
"只是个人想法:以后也许整理{artifact},目前没有安排。",
],
"en": [
"I may look into {topic} myself, but I have not decided when.",
"This is only a personal idea: perhaps I will organize the {artifact} someday.",
],
},
},
"fresh_complaint": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"本来承诺今天解决,结果还是{issue},现在{impact}。",
"同样的故障第三次出现,{issue},整个事情已经{impact}。",
],
"en": [
"This was promised for today, yet {issue}, and now {impact}.",
"The same failure has happened a third time: {issue}, so {impact}.",
],
},
},
"fresh_complaint_request": {
"labels": labels(
question=True,
complaint=True,
sentiment="negative",
replyable=True,
),
"templates": {
"zh-Hans": [
"{issue}已经影响到{impact},你们准备哪天真正解决?",
"因为{issue},现在{impact},能不能给出明确处理时限?",
],
"en": [
"{issue} has reached the point where {impact}. When will you actually resolve it?",
"Because {issue}, {impact}. Can you provide a firm resolution date?",
],
},
},
"fresh_negative_report_boundary": {
"labels": labels(sentiment="negative"),
"templates": {
"zh-Hans": [
"行业通报称{negative_news},这里只摘录公开信息。",
"统计报告记录了{negative_news},没有客户投诉。",
],
"en": [
"The industry bulletin says that {negative_news}; this only quotes public information.",
"The statistical report records that {negative_news}, with no customer complaint.",
],
},
},
"fresh_confirmation": {
"labels": labels(confirmationDecision=True, replyable=True),
"templates": {
"zh-Hans": [
"评审结束,我的最终选择是{option},其他方案关闭。",
"结论正式生效:{topic}采用{option}。",
],
"en": [
"The review is over; my final selection is {option}, and the alternatives are closed.",
"The decision is now official: use {option} for {topic}.",
],
},
},
"fresh_pending_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"我看过{option}了,但还没有选择,等明天再决定。",
"{topic}仍在评审,当前没有批准结论。",
],
"en": [
"I reviewed {option}, but have not selected it; the decision waits until tomorrow.",
"{topic} remains under review, with no approval yet.",
],
},
},
"fresh_follow_up": {
"labels": labels(
task=True,
followUpReminder=True,
replyable=True,
),
"templates": {
"zh-Hans": [
"等{trigger}满两天后,再找{person}核实{thing}并记录结果。",
"当前事项结束以后,回头联系{person}追踪{thing}。",
],
"en": [
"Two days after {trigger}, check with {person} again about {thing} and record the outcome.",
"After the current item closes, return to {person} and track {thing}.",
],
},
},
"fresh_personal_reminder": {
"labels": labels(followUpReminder=True),
"templates": {
"zh-Hans": [
"给自己设一条后续提醒:{deadline}{person}确认{thing}。",
"个人行动项:{trigger}以后再次检查{thing}。",
],
"en": [
"Set myself a follow-up reminder to ask {person} about {thing} {deadline}.",
"Personal action item: check {thing} again after {trigger}.",
],
},
},
"fresh_plain_task_boundary": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"现在请直接整理{artifact}并发给{owner},不需要后续回访。",
"一次性完成{artifact}即可,交给{owner}后任务结束。",
],
"en": [
"Prepare the {artifact} now and send it to {owner}; no later follow-up is needed.",
"Complete the {artifact} once, deliver it to {owner}, and close the task.",
],
},
},
"fresh_vague_follow_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"以后说不定会再问{person},目前没有后续计划。",
"{thing}哪天想起来再看,现在不用提醒。",
],
"en": [
"I might ask {person} again someday, but there is no follow-up plan.",
"Maybe I will revisit {thing} whenever it comes to mind; no reminder is needed.",
],
},
},
}
TARGETED_FINAL_FAMILIES = {
"final_task_direct": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"{artifact}现在明确由你负责,{deadline}前交给{owner}。",
"请你承担{artifact}的交付,完成后在{channel}确认。",
],
"en": [
"Ownership of the {artifact} now belongs to you; deliver it to {owner} {deadline}.",
"You are responsible for delivering the {artifact}; confirm completion {channel}.",
],
},
},
"final_task_indirect": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"{owner}希望你能接下{artifact},并在{deadline}前收尾。",
"这件事想交给你继续推进:整理{artifact}。",
],
"en": [
"{owner} is counting on you to pick up the {artifact} and close it out {deadline}.",
"We would like you to carry this forward by preparing the {artifact}.",
],
},
},
"final_task_question": {
"labels": labels(task=True, question=True, replyable=True),
"templates": {
"zh-Hans": [
"能请你负责{artifact}并在{deadline}前完成吗?",
"{artifact}接下来可以由你收尾吗?",
],
"en": [
"Would you be able to own the {artifact} and finish it {deadline}?",
"Could the {artifact} be left with you for final completion?",
],
},
},
"final_personal_action_boundary": {
"labels": labels(followUpReminder=True),
"templates": {
"zh-Hans": [
"这是我自己的行动清单:{trigger}后检查{thing}。",
"个人记录,不是委派:{deadline}{person}确认{thing}。",
],
"en": [
"This is on my own action list: check {thing} after {trigger}.",
"Personal note, not delegated work: ask {person} about {thing} {deadline}.",
],
},
},
"final_self_intent_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"我自己可能会整理{artifact},但还没有正式计划。",
"以后有空我再看{topic},目前不用安排。",
],
"en": [
"I may organize the {artifact} myself, but there is no firm plan.",
"I might look at {topic} when I have time; nothing is scheduled.",
],
},
},
"final_event_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"{owner}{deadline}举行评审,这只是日程通知。",
"{topic}会议已经定在{deadline},没有分配任务。",
],
"en": [
"{owner} is holding a review {deadline}; this is only a calendar notice.",
"The meeting about {topic} is set for {deadline}, with no assignment.",
],
},
},
"final_complaint_implicit": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"我不应该为同一个问题反复追问,{issue},现在{impact}。",
"又是同样的结果:{issue},已经连续影响到{impact}。",
],
"en": [
"I should not have to chase the same issue repeatedly: {issue}, and now {impact}.",
"It is the same outcome again: {issue}, repeatedly leaving me with {impact}.",
],
},
},
"final_complaint_short": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"{issue},已经第三次了。",
"等了这么久还是{issue},实在无法接受。",
],
"en": [
"{issue}. This is already the third time.",
"After all this waiting, {issue}; this is not acceptable.",
],
},
},
"final_complaint_request": {
"labels": labels(
question=True,
complaint=True,
sentiment="negative",
replyable=True,
),
"templates": {
"zh-Hans": [
"{issue}一直没有变化,什么时候才能真正处理?",
"因为{issue},现在{impact},谁能给出解决结果?",
],
"en": [
"Nothing has changed with this problem: {issue}. When will it actually be handled?",
"Because {issue}, {impact}. Who can provide a real resolution?",
],
},
},
"final_negative_fact_boundary": {
"labels": labels(sentiment="negative"),
"templates": {
"zh-Hans": [
"公开报告显示{negative_news},这里没有服务申诉。",
"资料仅记录{negative_news},不涉及个人问题。",
],
"en": [
"The public report shows that {negative_news}; there is no service grievance here.",
"The document only records that {negative_news}, not an individual problem.",
],
},
},
"final_resolved_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"之前的{issue}已经彻底恢复,这是一条结案记录。",
"{topic}的问题处理完毕,现在不需要任何支持。",
],
"en": [
"The earlier issue where {issue} is fully resolved; this is a closure record.",
"The problem involving {topic} is complete, with no support needed now.",
],
},
},
"final_operational_task_boundary": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"请完成{artifact}后直接结项,不需要投诉或后续回访。",
"{deadline}前把{artifact}交给{owner},随后流程关闭。",
],
"en": [
"Complete the {artifact} and close the item; no complaint or later follow-up is involved.",
"Deliver the {artifact} to {owner} {deadline}, then close the workflow.",
],
},
},
}
TARGETED_CONFIRMATION_FAMILIES = {
"confirmation_task_assignment": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"请你把{artifact}负责到底,并在{deadline}{owner}交付。",
"{artifact}接下来归你处理,完成后在{channel}更新状态。",
],
"en": [
"Please see the {artifact} through and deliver it to {owner} {deadline}.",
"The {artifact} is yours to handle next; update the status {channel} when complete.",
],
},
},
"confirmation_task_request": {
"labels": labels(task=True, question=True, replyable=True),
"templates": {
"zh-Hans": [
"可以请你接管{artifact}并在{deadline}前处理好吗?",
"你愿意负责{artifact}的最后交付吗?",
],
"en": [
"Could I ask you to take care of the {artifact} {deadline}?",
"Would you own the final delivery of the {artifact}?",
],
},
},
"confirmation_operational_task": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"把{artifact}交给{owner}后直接关闭事项,不安排回访。",
"当前只需要完成{artifact},没有投诉处理。",
],
"en": [
"Close the item after sending the {artifact} to {owner}; do not schedule a follow-up.",
"The only requirement is to complete the {artifact}; no grievance handling is involved.",
],
},
},
"confirmation_personal_boundary": {
"labels": labels(followUpReminder=True),
"templates": {
"zh-Hans": [
"我自己的行动备忘:{trigger}后查看{thing}。",
"仅记录个人计划,{deadline}联系{person}。",
],
"en": [
"My private action note is to review {thing} after {trigger}.",
"This only records my personal plan to contact {person} {deadline}.",
],
},
},
"confirmation_self_plan_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"我也许会自己整理{artifact},目前没有确定安排。",
"{topic}以后再考虑,现在谁都不用处理。",
],
"en": [
"I may prepare the {artifact} myself, but nothing is arranged.",
"{topic} can be considered later; nobody needs to handle it now.",
],
},
},
"confirmation_complaint_implicit": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"我到现在还在面对{issue},而且{impact}。",
"处理承诺没有兑现,结果仍然是{issue}。",
],
"en": [
"I am still dealing with the fact that {issue}, and {impact}.",
"The promised fix never materialized; the result is still that {issue}.",
],
},
},
"confirmation_complaint_request": {
"labels": labels(
question=True,
complaint=True,
sentiment="negative",
replyable=True,
),
"templates": {
"zh-Hans": [
"{issue}已经持续很久,究竟什么时候恢复?",
"现在因为{issue}{impact},谁来负责处理?",
],
"en": [
"This has continued for far too long: {issue}. When will it be restored?",
"Because {issue}, {impact}. Who is responsible for fixing it?",
],
},
},
"confirmation_complaint_short": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"还是{issue},完全没有改善。",
"{issue}又来了,不能一直这样。",
],
"en": [
"It is still the case that {issue}, with no improvement.",
"The problem is back again: {issue}. This cannot keep happening.",
],
},
},
"confirmation_negative_fact": {
"labels": labels(sentiment="negative"),
"templates": {
"zh-Hans": [
"研究资料指出{negative_news},这不是客户反馈。",
"行业统计记录了{negative_news},没有服务诉求。",
],
"en": [
"The research notes that {negative_news}; this is not customer feedback.",
"Industry statistics record that {negative_news}, with no service request.",
],
},
},
"confirmation_resolved_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"{issue}已经恢复,{owner}确认事项关闭。",
"{topic}当前运行正常,不需要补救。",
],
"en": [
"The earlier state where {issue} is resolved, and {owner} confirmed closure.",
"{topic} is operating normally now, with no remedy needed.",
],
},
},
}
TARGETED_RELEASE_FAMILIES = {
"release_task_owner": {
"labels": labels(task=True, replyable=True),
"templates": {
"zh-Hans": [
"{artifact}的负责人就是你,请在{deadline}完成交付。",
"接下来请你推进{artifact},并向{owner}汇报结果。",
],
"en": [
"You are the owner of the {artifact}; complete delivery {deadline}.",
"Please move the {artifact} forward and report the outcome to {owner}.",
],
},
},
"release_task_polite_request": {
"labels": labels(task=True, question=True, replyable=True),
"templates": {
"zh-Hans": [
"能麻烦你把{artifact}负责到交付完成吗?",
"你可以在{deadline}前处理好{artifact}吗?",
],
"en": [
"May I ask you to own the {artifact} through delivery?",
"Can you have the {artifact} completed {deadline}?",
],
},
},
"release_personal_boundary": {
"labels": labels(followUpReminder=True),
"templates": {
"zh-Hans": [
"这是我的私人待办:{trigger}之后确认{thing}。",
"个人备忘,不交给任何人:{deadline}联系{person}。",
],
"en": [
"This is my private todo: confirm {thing} after {trigger}.",
"Personal reminder, assigned to nobody else: contact {person} {deadline}.",
],
},
},
"release_complaint_unspoken": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"从上次反馈以后仍然{issue},现在已经{impact}。",
"说好的处理没有发生,我看到的还是{issue}。",
],
"en": [
"Since my last report, {issue}, and it has now reached the point where {impact}.",
"The promised handling did not happen; I am still seeing that {issue}.",
],
},
},
"release_complaint_question": {
"labels": labels(
question=True,
complaint=True,
sentiment="negative",
replyable=True,
),
"templates": {
"zh-Hans": [
"{issue}到现在都没处理,什么时候能恢复正常?",
"目前因为{issue}{impact},可以给个处理结果吗?",
],
"en": [
"The problem where {issue} has not been addressed. When will normal service return?",
"At the moment, {issue}, so {impact}. Can I get an actual resolution?",
],
},
},
"release_complaint_brief": {
"labels": labels(complaint=True, sentiment="negative", replyable=True),
"templates": {
"zh-Hans": [
"{issue},到现在还是老样子。",
"又一次{issue},这已经影响正常使用。",
],
"en": [
"{issue}, and nothing has changed.",
"Once again, {issue}; normal use is now affected.",
],
},
},
"release_negative_boundary": {
"labels": labels(sentiment="negative"),
"templates": {
"zh-Hans": [
"分析报告提到{negative_news},不代表用户投诉。",
"公开数据包含{negative_news},这里只做事实引用。",
],
"en": [
"The analysis mentions that {negative_news}; it does not represent a user complaint.",
"Public data includes the fact that {negative_news}; this is only a factual citation.",
],
},
},
"release_resolved_boundary": {
"labels": labels(),
"templates": {
"zh-Hans": [
"{issue}的问题已经结束,目前状态稳定。",
"{topic}已确认恢复,{owner}不需要继续介入。",
],
"en": [
"The earlier problem where {issue} is over, and the current state is stable.",
"{topic} is confirmed restored, so {owner} does not need to intervene.",
],
},
},
}
def render(template: str, slots: dict[str, list[str]], rng: random.Random) -> str:
values = {key: rng.choice(options) for key, options in slots.items()}
return values["discourse"] + template.format(**values)
def generate(
seed: int,
samples_per_family: int,
families: dict[str, dict[str, object]],
) -> list[Record]:
rng = random.Random(seed)
records: list[Record] = []
seen: set[str] = set()
for language in ("zh-Hans", "en"):
slots = SLOTS[language]
for family, configuration in families.items():
family_labels: Labels = configuration["labels"]
templates: list[str] = configuration["templates"][language]
generated = 0
attempts = 0
while generated < samples_per_family:
attempts += 1
if attempts > samples_per_family * 200:
raise RuntimeError(f"Unable to generate unique samples for {language}/{family}")
text = render(rng.choice(templates), slots, rng)
normalized = " ".join(text.casefold().split())
if normalized in seen:
continue
seen.add(normalized)
record_id = f"random-{seed}-{language}-{family}-{generated + 1:03d}"
records.append(
Record(
id=record_id,
text=text,
language=language,
split="randomHoldout",
family=family,
**asdict(family_labels),
)
)
generated += 1
rng.shuffle(records)
return records
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--seed", type=int, default=DEFAULT_SEED)
parser.add_argument("--samples-per-family", type=int, default=DEFAULT_SAMPLES_PER_FAMILY)
parser.add_argument(
"--profile",
choices=(
"development",
"fresh",
"targeted-final",
"targeted-confirmation",
"targeted-release",
),
default="development",
)
parser.add_argument(
"--output",
type=Path,
default=None,
)
arguments = parser.parse_args()
profiles = {
"development": FAMILIES,
"fresh": FRESH_FAMILIES,
"targeted-final": TARGETED_FINAL_FAMILIES,
"targeted-confirmation": TARGETED_CONFIRMATION_FAMILIES,
"targeted-release": TARGETED_RELEASE_FAMILIES,
}
families = profiles[arguments.profile]
default_outputs = {
"development": "random-holdout-corpus.jsonl",
"fresh": "fresh-metric-holdout-corpus.jsonl",
"targeted-final": "targeted-final-holdout-corpus.jsonl",
"targeted-confirmation": "targeted-confirmation-holdout-corpus.jsonl",
"targeted-release": "targeted-release-holdout-corpus.jsonl",
}
output_path = arguments.output or Path(
"ModelTraining/ClipboardSemantics/" + default_outputs[arguments.profile]
)
records = generate(arguments.seed, arguments.samples_per_family, families)
output_path.parent.mkdir(parents=True, exist_ok=True)
with output_path.open("w", encoding="utf-8") as output:
for record in records:
output.write(json.dumps(asdict(record), ensure_ascii=False, sort_keys=True) + "\n")
print(
f"RANDOM_HOLDOUT_DONE records={len(records)} "
f"families={len(families)} profile={arguments.profile} "
f"seed={arguments.seed} output={output_path}"
)
if __name__ == "__main__":
main()