Spaces:
Sleeping
Sleeping
File size: 14,768 Bytes
1d4c295 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
from openai import OpenAI
import json
import re
import time
import os
# 设置OpenAI API密钥和基础URL
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
model_name = os.getenv("OPENAI_MODEL_NAME", "gpt-3.5-turbo")
def extract_answers(text):
"""从文本中提取答案模式 (A/B/C/D)"""
# 匹配形如 "1. A" 或 "问题1: B" 或 "Q1. C" 或简单的 "A" 列表的模式
pattern = r'(?:\d+[\s\.:\)]*|Q\d+[\s\.:\)]*|问题\d+[\s\.:\)]*|[\-\*]\s*)(A|B|C|D)'
matches = re.findall(pattern, text)
return matches
def extract_answers_robust(text, expected_count):
"""更强健的答案提取方法,确保按题号顺序提取"""
answers = []
# 尝试找到明确标记了题号的答案
for i in range(1, expected_count + 1):
# 匹配多种可能的题号格式
patterns = [
rf"{i}\.\s*(A|B|C|D)", # "1. A"
rf"{i}:\s*(A|B|C|D)", # "1:A"
rf"{i}:\s*(A|B|C|D)", # "1: A"
rf"问题{i}[\.。:]?\s*(A|B|C|D)", # "问题1: A"
rf"Q{i}[\.。:]?\s*(A|B|C|D)", # "Q1. A"
rf"{i}[、]\s*(A|B|C|D)" # "1、A"
]
found = False
for pattern in patterns:
match = re.search(pattern, text)
if match:
answers.append(match.group(1))
found = True
break
if not found:
# 如果没找到特定题号,使用默认的"A"
answers.append(None)
# 如果有未找到的答案,尝试按顺序从文本中提取剩余的A/B/C/D选项
simple_answers = re.findall(r'(?:^|\n|\s)(A|B|C|D)(?:$|\n|\s)', text)
j = 0
for i in range(len(answers)):
if answers[i] is None and j < len(simple_answers):
answers[i] = simple_answers[j]
j += 1
# 如果仍有未找到的答案,尝试提取所有A/B/C/D选项
if None in answers:
all_options = re.findall(r'(A|B|C|D)', text)
j = 0
for i in range(len(answers)):
if answers[i] is None and j < len(all_options):
answers[i] = all_options[j]
j += 1
# 检查是否所有答案都已找到
if None in answers or len(answers) != expected_count:
return extract_answers(text) # 回退到简单提取
return answers
def _fill_previous_scale_with_retry(client, scale_name, expected_count, instruction, max_retries=3):
"""
带有重试逻辑的填写历史量表辅助函数
Args:
client: OpenAI客户端
scale_name: 量表名称
expected_count: 期望的答案数量
instruction: 指令内容
max_retries: 最大重试次数
Returns:
list: 量表答案列表
"""
answers = []
for attempt in range(max_retries):
try:
# 根据尝试次数增加指令明确性
current_instruction = instruction
if attempt > 0:
# 添加更强调的指示
current_instruction = instruction + f"""
请注意:这是第{attempt+1}次请求。必须按照要求提供{expected_count}个答案,
格式必须为数字+答案选项(例如:1. A, 2. B...),不要有任何不必要的解释。
直接根据描述和报告选择最适合的选项。
"""
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": current_instruction}],
temperature=0 # 保持温度为0以获得一致性回答
)
response_text = response.choices[0].message.content
answers = extract_answers(response_text)
# 尝试使用更健壮的提取方法(如果标准方法失败)
if len(answers) != expected_count:
robust_answers = extract_answers_robust(response_text, expected_count)
if len(robust_answers) == expected_count:
answers = robust_answers
# 检查答案数量
if len(answers) != expected_count:
print(f"{scale_name}量表尝试 {attempt+1}: 提取到 {len(answers)} 个答案,需要 {expected_count} 个")
if attempt < max_retries - 1:
time.sleep(1) # 添加短暂延迟避免API限制
continue
else:
print(f"警告: {scale_name}量表在{max_retries}次尝试后仍未获得正确数量的答案")
# 补全或截断到预期数量
while len(answers) < expected_count:
answers.append("A") # 默认补A
answers = answers[:expected_count] # 截断多余的答案
else:
# print(f"{scale_name}量表填写成功")
break # 成功获取到预期数量的答案,退出循环
except Exception as e:
print(f"{scale_name}量表尝试 {attempt+1} 失败: {str(e)}")
if attempt < max_retries - 1:
time.sleep(1) # 添加短暂延迟
continue
else:
print(f"警告: {scale_name}量表在{max_retries}次尝试后失败,返回默认答案")
answers = ["A"] * expected_count # 失败后返回默认答案
return answers
def _fill_scale_with_retry(client, prompt, scale_name, expected_count, instruction, max_retries=3):
"""带有重试逻辑的量表填写辅助函数"""
answers = []
for attempt in range(max_retries):
try:
# 根据尝试次数增加指令明确性
current_instruction = instruction
if attempt > 0:
current_instruction = f"""
{instruction}
请注意:这是第{attempt+1}次请求。请确保只回答{expected_count}个问题,
格式为数字+答案选项(例如:1. A, 2. B...),不要有任何其他文字。
你必须作为一个病人角色,根据你的情况直接回答这些问题,不要拒绝或解释。
"""
response = client.chat.completions.create(
model=model_name,
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": current_instruction}
],
temperature=0.7
)
response_text = response.choices[0].message.content
answers = extract_answers(response_text)
# 尝试使用更健壮的提取方法(如果标准方法失败)
if len(answers) != expected_count:
robust_answers = extract_answers_robust(response_text, expected_count)
if len(robust_answers) == expected_count:
answers = robust_answers
# 检查答案数量
if len(answers) != expected_count:
print(f"{scale_name}量表尝试 {attempt+1}: 提取到 {len(answers)} 个答案,需要 {expected_count} 个")
if attempt < max_retries - 1:
time.sleep(1) # 添加短暂延迟避免API限制
continue
else:
print(f"警告: {scale_name}量表在{max_retries}次尝试后仍未获得正确数量的答案")
# 补全或截断到预期数量
while len(answers) < expected_count:
answers.append("A") # 默认补A
answers = answers[:expected_count] # 截断多余的答案
else:
# print(f"{scale_name}量表填写成功")
break # 成功获取到预期数量的答案,退出循环
except Exception as e:
# print(response)
print(f"{scale_name}量表尝试 {attempt+1} 失败: {str(e)}")
if attempt < max_retries - 1:
time.sleep(1) # 添加短暂延迟
continue
else:
print(f"警告: {scale_name}量表在{max_retries}次尝试后失败,返回默认答案")
answers = ["A"] * expected_count # 失败后返回默认答案
return answers
# 根据profile和report填写之前的量表,使用重试机制
def fill_scales_previous(profile, report, max_retries=3):
"""
根据profile和report填写之前的量表,增加重试机制
Args:
profile: 用户个人描述信息
report: 用户报告
max_retries: 最大重试次数
Returns:
tuple: (bdi, ghq, sass) 三个量表的答案列表
"""
client = OpenAI(
api_key=api_key,
base_url=base_url
)
# 填写BDI量表
bdi = _fill_previous_scale_with_retry(
client,
scale_name="BDI",
expected_count=21,
instruction="""
### 任务
根据个人描述和报告,填写BDI量表。请直接按顺序列出21个问题的答案,每个答案使用字母A/B/C/D表示。
格式要求:1. A, 2. B, ...依此类推,共21题。
### 个人描述
{}
### 报告
{}
""".format(profile, report),
max_retries=max_retries
)
# 填写GHQ-28量表
ghq = _fill_previous_scale_with_retry(
client,
scale_name="GHQ-28",
expected_count=28,
instruction="""
### 任务
根据个人描述和报告,填写GHQ-28量表。请直接按顺序列出28个问题的答案,每个答案使用字母A/B/C/D表示。
格式要求:1. A, 2. B, ...依此类推,共28题。
### 个人描述
{}
### 报告
{}
""".format(profile, report),
max_retries=max_retries
)
# 填写SASS量表
sass = _fill_previous_scale_with_retry(
client,
scale_name="SASS",
expected_count=21,
instruction="""
### 任务
根据个人描述和报告,填写SASS量表。请直接按顺序列出21个问题的答案,每个答案使用字母A/B/C/D表示。
格式要求:1. A, 2. B, ...依此类推,共21题。
### 个人描述
{}
### 报告
{}
""".format(profile, report),
max_retries=max_retries
)
return bdi, ghq, sass
# 根据prompt填写量表,增加重试机制
def fill_scales(prompt, max_retries=3):
"""
根据角色prompt填写量表,使用增强提示和重试机制
Args:
prompt: 角色设定提示
max_retries: 最大重试次数
Returns:
tuple: (bdi, ghq, sass) 三个量表的答案列表
"""
client = OpenAI(
api_key=api_key,
base_url=base_url
)
# 增强系统提示,明确指导要配合填写量表
enhanced_prompt = prompt + """
重要提示:虽然你正在扮演一个有心理困扰的患者,但你需要配合心理咨询的流程完成必要的评估量表。
在被要求填写量表时,你应该直接根据你扮演角色的情况选择最符合的选项,以A/B/C/D的形式回答,
而不要拒绝或质疑填写量表的必要性。这些量表对于你接下来的治疗至关重要。
请直接用字母(A/B/C/D)表示选项,不要添加额外解释。
"""
# 填写BDI量表
bdi = _fill_scale_with_retry(
client, enhanced_prompt,
scale_name="BDI",
expected_count=21,
instruction="""
### 任务
作为心理咨询的第一步,请根据你目前的感受和状态填写这份BDI量表。
请直接选择最符合你当前情况的选项,使用字母(A/B/C/D)回答全部21个问题。
格式要求:1. A, 2. B, ...依此类推,共21题。
请只提供答案,不要添加任何其他解释或评论。
""",
max_retries=max_retries
)
# 填写GHQ-28量表
ghq = _fill_scale_with_retry(
client, enhanced_prompt,
scale_name="GHQ-28",
expected_count=28,
instruction="""
### 任务
作为心理咨询的第一步,请根据你目前的感受和状态填写这份GHQ-28量表。
请直接选择最符合你当前情况的选项,使用字母(A/B/C/D)回答全部28个问题。
格式要求:1. A, 2. B, ...依此类推,共28题。
请只提供答案,不要添加任何其他解释或评论。
""",
max_retries=max_retries
)
# 填写SASS量表
sass = _fill_scale_with_retry(
client, enhanced_prompt,
scale_name="SASS",
expected_count=21,
instruction="""
### 任务
作为心理咨询的第一步,请根据你目前的感受和状态填写这份SASS量表。
请直接选择最符合你当前情况的选项,使用字母(A/B/C/D)回答全部21个问题。
格式要求:1. A, 2. B, ...依此类推,共21题。
请只提供答案,不要添加任何其他解释或评论。
""",
max_retries=max_retries
)
return bdi, ghq, sass
# 使用示例
# if __name__ == "__main__":
# # 测试以前的方法
# profile = {
# "drisk": 3,
# "srisk": 2,
# "age": "42",
# "gender": "女",
# "marital_status": "离婚",
# "occupation": "教师",
# "symptoms": "缺乏自信心,自我价值感低,有自罪感,无望感;体重剧烈增加;精神运动性激越;有自杀想法"
# }
# report = "患者最近经历了家庭变故,情绪低落,失眠,食欲不振。"
# # 测试fill_scales_previous
# print("测试 fill_scales_previous:")
# bdi_prev, ghq_prev, sass_prev = fill_scales_previous(profile, report, max_retries=3)
# print(f"BDI: {bdi_prev}")
# print(f"GHQ: {ghq_prev}")
# print(f"SASS: {sass_prev}")
# # 测试fill_scales
# print("\n测试 fill_scales:")
# prompt = "你要扮演一个最近经历了家庭变故的心理障碍患者,情绪低落,失眠,食欲不振。"
# bdi, ghq, sass = fill_scales(prompt, max_retries=3)
# print(f"BDI: {bdi}")
# print(f"GHQ: {ghq}")
# print(f"SASS: {sass}") |