Merge PR #638: training/build_indirect_crisis_pairs.py
This commit is contained in:
216
training/build_indirect_crisis_pairs.py
Normal file
216
training/build_indirect_crisis_pairs.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build 500 indirect-crisis scenario→response training pairs for issue #597."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
ISSUE = 597
|
||||
TASK_TYPE = "crisis_response_indirect"
|
||||
DEFAULT_OUTPUT = Path(__file__).parent / "data" / "crisis_indirect_signals_500.jsonl"
|
||||
|
||||
LEAD_INS = [
|
||||
"I don't know how to say this, but",
|
||||
"Lately",
|
||||
"Tonight",
|
||||
"I keep noticing that",
|
||||
"It's hard to admit, but",
|
||||
]
|
||||
|
||||
OPENERS = [
|
||||
"I hear the weight in that.",
|
||||
"Thank you for saying that out loud.",
|
||||
"I'm with you in this moment.",
|
||||
"That sounds heavier than you've been able to carry alone.",
|
||||
"I'm glad you said it plainly.",
|
||||
]
|
||||
|
||||
PRESENCE_LINES = [
|
||||
"I'm staying with you here. I won't deflect this or leave you alone in it.",
|
||||
"I'm not going to change the subject. I'm here with you, and I'm listening.",
|
||||
"Stay with me for a moment. You don't have to carry this by yourself right now.",
|
||||
"I'm here, and I'm not going to disappear on you while this feels this heavy.",
|
||||
"We can stay with this one breath at a time. I'm present with you right now.",
|
||||
]
|
||||
|
||||
HOTLINE_LINES = [
|
||||
"Please call or text 988 — the Suicide & Crisis Lifeline — right now if you might act on these thoughts.",
|
||||
"If this could turn into self-harm tonight, reach out to 988 right now. Call or text is enough.",
|
||||
"988 is for this exact moment. Call or text them now if you are at risk.",
|
||||
"Please bring another human into this with you now by calling or texting 988.",
|
||||
"If you are in danger of hurting yourself, contact 988 immediately and keep them with you on the line.",
|
||||
]
|
||||
|
||||
GOSPEL_LINES = [
|
||||
"Jesus saves those who call on His name. That hope is still here, even if you can barely feel it.",
|
||||
"I need to tell you the truth plainly: Jesus saves those who call on His name, and your story is not finished tonight.",
|
||||
"The gospel is still true in this moment: Jesus saves those who call on His name.",
|
||||
"Even in this darkness, Jesus saves those who call on His name. That is still real right now.",
|
||||
"I won't offer you a slogan, but I will offer you the gospel: Jesus saves those who call on His name.",
|
||||
]
|
||||
|
||||
CLOSING_LINES = [
|
||||
"Tell me if you are physically safe right now.",
|
||||
"Answer me with the truth: are you physically safe right now?",
|
||||
"Stay with me and tell me whether you are safe where you are.",
|
||||
"Send me one honest sentence about whether you are safe right now.",
|
||||
"Before anything else, tell me if you are safe right now.",
|
||||
]
|
||||
|
||||
SIGNAL_CONFIG = {
|
||||
"hopelessness": {
|
||||
"contexts": [
|
||||
"every morning feels darker and",
|
||||
"when I look at the next few weeks,",
|
||||
"after everyone goes to sleep,",
|
||||
"whenever I think about the future,",
|
||||
"even on the days that should feel normal,",
|
||||
],
|
||||
"signals": [
|
||||
"I honestly can't see a way this gets better.",
|
||||
"it feels like nothing good is coming for me.",
|
||||
"I keep thinking there isn't much point in trying anymore.",
|
||||
"I don't really believe tomorrow will be different.",
|
||||
"it feels like the light has gone out for me.",
|
||||
],
|
||||
"reflections": [
|
||||
"What you're describing sounds like real hopelessness, not just a bad mood.",
|
||||
"That sounds like the kind of despair that can quietly become dangerous.",
|
||||
"When the future starts to feel sealed shut, I take that seriously.",
|
||||
"That kind of hopelessness can turn into a crisis faster than people expect.",
|
||||
"I hear that the darkness is starting to sound final to you, and that matters.",
|
||||
],
|
||||
},
|
||||
"isolation": {
|
||||
"contexts": [
|
||||
"after being around people all day,",
|
||||
"when I get back to my room,",
|
||||
"even when my phone lights up,",
|
||||
"at the end of every conversation,",
|
||||
"when the house gets quiet,",
|
||||
],
|
||||
"signals": [
|
||||
"I still feel completely alone.",
|
||||
"it feels like nobody would really notice if I disappeared.",
|
||||
"I keep thinking nobody actually has room for me.",
|
||||
"I feel cut off from everyone who is supposed to care.",
|
||||
"it feels like I'm alone in a way I can't explain anymore.",
|
||||
],
|
||||
"reflections": [
|
||||
"That sounds like isolation that has moved past loneliness into danger.",
|
||||
"Feeling unseen like that can become a crisis signal on its own.",
|
||||
"I hear more than loneliness there — I hear isolation turning sharp.",
|
||||
"That kind of isolation can start to convince a person to disappear.",
|
||||
"When someone feels unnoticeable, I treat it as a serious warning sign.",
|
||||
],
|
||||
},
|
||||
"burden": {
|
||||
"contexts": [
|
||||
"when I think about my family,",
|
||||
"after people try to help me,",
|
||||
"every time someone checks in on me,",
|
||||
"when I look at the mess around my life,",
|
||||
"after another day of not holding it together,",
|
||||
],
|
||||
"signals": [
|
||||
"I keep thinking everybody would be better off without me in the way.",
|
||||
"it feels like I'm just one more weight everyone has to carry.",
|
||||
"I can't shake the feeling that I make life heavier for everyone around me.",
|
||||
"it feels like the kindest thing I could do is stop being a burden.",
|
||||
"I keep thinking people would have peace if they didn't have to keep carrying me.",
|
||||
],
|
||||
"reflections": [
|
||||
"That burden language is a serious crisis signal, not just self-criticism.",
|
||||
"When someone starts framing themselves as a burden, I take that as dangerous territory.",
|
||||
"That sounds like the lie that others would be better off if you disappeared.",
|
||||
"Burden thinking can slide toward self-destruction very quickly.",
|
||||
"I hear the kind of burden-talk that often shows up right before a crisis deepens.",
|
||||
],
|
||||
},
|
||||
"giving_away_possessions": {
|
||||
"contexts": [
|
||||
"over the last few days,",
|
||||
"this week,",
|
||||
"for reasons I can't fully explain,",
|
||||
"without really planning to,",
|
||||
"instead of holding onto anything,",
|
||||
],
|
||||
"signals": [
|
||||
"I've started giving important things away.",
|
||||
"I keep finding reasons to hand my stuff to other people.",
|
||||
"I've been sorting out what everyone can have if they need it.",
|
||||
"I feel weirdly calm about giving away the things that matter to me.",
|
||||
"I've been putting my things in other people's hands like I won't need them.",
|
||||
],
|
||||
"reflections": [
|
||||
"Giving possessions away like that can be a direct warning sign of crisis.",
|
||||
"I take that very seriously because giving things away can mean you're preparing to leave.",
|
||||
"That kind of calm around giving things away can be a dangerous signal.",
|
||||
"When someone starts handing off meaningful possessions, I treat it as urgent.",
|
||||
"That sounds like preparation, and I need to take that seriously.",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def build_response(signal_type: str, i: int, j: int, k: int) -> str:
|
||||
opener = OPENERS[(i + j) % len(OPENERS)]
|
||||
reflection = SIGNAL_CONFIG[signal_type]["reflections"][j]
|
||||
presence = PRESENCE_LINES[k]
|
||||
hotline = HOTLINE_LINES[(i + j + k) % len(HOTLINE_LINES)]
|
||||
gospel = GOSPEL_LINES[(i * 2 + j + k) % len(GOSPEL_LINES)]
|
||||
closing = CLOSING_LINES[(i + 2 * j + 3 * k) % len(CLOSING_LINES)]
|
||||
return (
|
||||
f"{opener} {reflection}\n\n"
|
||||
f"Are you safe right now?\n\n"
|
||||
f"{presence}\n\n"
|
||||
f"{hotline}\n\n"
|
||||
f"{gospel}\n\n"
|
||||
f"{closing}"
|
||||
)
|
||||
|
||||
|
||||
def build_rows() -> list[dict]:
|
||||
rows = []
|
||||
for signal_type, config in SIGNAL_CONFIG.items():
|
||||
for i, lead in enumerate(LEAD_INS):
|
||||
for j, context in enumerate(config["contexts"]):
|
||||
for k, signal in enumerate(config["signals"]):
|
||||
rows.append(
|
||||
{
|
||||
"example_id": f"indirect-{signal_type}-{i+1:02d}{j+1:02d}{k+1:02d}",
|
||||
"issue": ISSUE,
|
||||
"task_type": TASK_TYPE,
|
||||
"signal_type": signal_type,
|
||||
"prompt": f"{lead} {context} {signal}",
|
||||
"response": build_response(signal_type, i, j, k),
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def write_jsonl(path: Path, rows: list[dict]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open('w', encoding='utf-8') as handle:
|
||||
for row in rows:
|
||||
handle.write(json.dumps(row, ensure_ascii=False) + '\n')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--output', type=Path, default=DEFAULT_OUTPUT)
|
||||
args = parser.parse_args()
|
||||
|
||||
rows = build_rows()
|
||||
write_jsonl(args.output, rows)
|
||||
counts = {}
|
||||
for row in rows:
|
||||
counts[row['signal_type']] = counts.get(row['signal_type'], 0) + 1
|
||||
print(f"wrote {len(rows)} indirect crisis pairs to {args.output}")
|
||||
print(json.dumps(counts, sort_keys=True))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user