diff --git a/src/individuality/offline_llm.py b/src/individuality/offline_llm.py index 8d6820651..711743778 100644 --- a/src/individuality/offline_llm.py +++ b/src/individuality/offline_llm.py @@ -20,7 +20,7 @@ class LLM_request_off: if not self.api_key or not self.base_url: raise ValueError("环境变量未正确加载:SILICONFLOW_KEY 或 SILICONFLOW_BASE_URL 未设置") - logger.info(f"API URL: {self.base_url}") # 使用 logger 记录 base_url + # logger.info(f"API URL: {self.base_url}") # 使用 logger 记录 base_url def generate_response(self, prompt: str) -> Union[str, Tuple[str, str]]: """根据输入的提示生成模型的响应""" diff --git a/src/individuality/per_bf_gen.py b/src/individuality/per_bf_gen.py index 596229280..387e5729f 100644 --- a/src/individuality/per_bf_gen.py +++ b/src/individuality/per_bf_gen.py @@ -1,36 +1,30 @@ -""" -The definition of artificial personality in this paper follows the dispositional para-digm and adapts a definition of -personality developed for humans [17]: -Personality for a human is the "whole and organisation of relatively stable tendencies and patterns of experience and -behaviour within one person (distinguishing it from other persons)". This definition is modified for artificial -personality: -Artificial personality describes the relatively stable tendencies and patterns of behav-iour of an AI-based machine that -can be designed by developers and designers via different modalities, such as language, creating the impression -of individuality of a humanized social agent when users interact with the machine.""" - from typing import Dict, List import json import os from pathlib import Path from dotenv import load_dotenv import sys +import toml +import random +from tqdm import tqdm -""" -第一种方案:基于情景评估的人格测定 -""" -current_dir = Path(__file__).resolve().parent -project_root = current_dir.parent.parent.parent -env_path = project_root / ".env" - -root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) +# 添加项目根目录到 Python 路径 +root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) sys.path.append(root_path) -from src.individuality.scene import get_scene_by_factor, PERSONALITY_SCENES # noqa: E402 -from src.individuality.questionnaire import FACTOR_DESCRIPTIONS # noqa: E402 -from src.individuality.offline_llm import LLMModel # noqa: E402 +# 加载配置文件 +config_path = os.path.join(root_path, "config", "bot_config.toml") +with open(config_path, "r", encoding="utf-8") as f: + config = toml.load(f) + +# 现在可以导入src模块 +from src.individuality.scene import get_scene_by_factor, PERSONALITY_SCENES +from src.individuality.questionnaire import FACTOR_DESCRIPTIONS +from src.individuality.offline_llm import LLM_request_off # 加载环境变量 -if env_path.exists(): +env_path = os.path.join(root_path, ".env") +if os.path.exists(env_path): print(f"从 {env_path} 加载环境变量") load_dotenv(env_path) else: @@ -38,10 +32,60 @@ else: print("将使用默认配置") +def adapt_scene(scene: str) -> str: + + personality_core = config['personality']['personality_core'] + personality_sides = config['personality']['personality_sides'] + personality_side = random.choice(personality_sides) + identity_details = config['identity']['identity_detail'] + identity_detail = random.choice(identity_details) + + """ + 根据config中的属性,改编场景使其更适合当前角色 + + Args: + scene: 原始场景描述 + + Returns: + str: 改编后的场景描述 + """ + try: + prompt = f""" +这是一个参与人格测评的角色形象: +- 昵称: {config['bot']['nickname']} +- 性别: {config['identity']['gender']} +- 年龄: {config['identity']['age']}岁 +- 外貌: {config['identity']['appearance']} +- 性格核心: {personality_core} +- 性格侧面: {personality_side} +- 身份细节: {identity_detail} + +请根据上述形象,改编以下原始场景,在测评中,用户将根据该场景给出上述角色形象的反应: +{scene} + +保持场景的本质不变,但最好贴近生活且具体,并且让它更适合这个角色。 +改编后的场景应该自然、连贯,并考虑角色的年龄、身份和性格特点。只返回改编后的场景描述,不要包含其他说明。注意{config['bot']['nickname']}是面对这个情景的人,而不是场景的其他人。""" + + llm = LLM_request_off(model_name=config['model']['llm_normal']['name']) + adapted_scene, _ = llm.generate_response(prompt) + + # 检查返回的场景是否为空或错误信息 + if not adapted_scene or "错误" in adapted_scene or "失败" in adapted_scene: + print("场景改编失败,将使用原始场景") + return scene + + return adapted_scene + except Exception as e: + print(f"场景改编过程出错:{str(e)},将使用原始场景") + return scene + + class PersonalityEvaluator_direct: def __init__(self): self.personality_traits = {"开放性": 0, "严谨性": 0, "外向性": 0, "宜人性": 0, "神经质": 0} self.scenarios = [] + self.final_scores = {"开放性": 0, "严谨性": 0, "外向性": 0, "宜人性": 0, "神经质": 0} + self.dimension_counts = {trait: 0 for trait in self.final_scores.keys()} # 为每个人格特质获取对应的场景 for trait in PERSONALITY_SCENES: @@ -67,7 +111,7 @@ class PersonalityEvaluator_direct: {"场景": scene["scenario"], "评估维度": [trait, secondary_trait], "场景编号": scene_key} ) - self.llm = LLMModel() + self.llm = LLM_request_off() def evaluate_response(self, scenario: str, response: str, dimensions: List[str]) -> Dict[str, float]: """ @@ -125,71 +169,139 @@ class PersonalityEvaluator_direct: except Exception as e: print(f"评估过程出错:{str(e)}") return {dim: 3.5 for dim in dimensions} + + def run_evaluation(self): + """ + 运行整个评估过程 + """ + print(f"欢迎使用{config['bot']['nickname']}形象创建程序!") + print("接下来,将给您呈现一系列有关您bot的场景(共15个)。") + print("请想象您的bot在以下场景下会做什么,并描述您的bot的反应。") + print("每个场景都会进行不同方面的评估。") + print("\n角色基本信息:") + print(f"- 昵称:{config['bot']['nickname']}") + print(f"- 性格核心:{config['personality']['personality_core']}") + print(f"- 性格侧面:{config['personality']['personality_sides']}") + print(f"- 身份细节:{config['identity']['identity_detail']}") + print("\n准备好了吗?按回车键开始...") + input() + + total_scenarios = len(self.scenarios) + progress_bar = tqdm(total=total_scenarios, desc="场景进度", ncols=100, bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]') + + for i, scenario_data in enumerate(self.scenarios, 1): + # print(f"\n{'-' * 20} 场景 {i}/{total_scenarios} - {scenario_data['场景编号']} {'-' * 20}") + + # 改编场景,使其更适合当前角色 + print(f"{config['bot']['nickname']}祈祷中...") + adapted_scene = adapt_scene(scenario_data["场景"]) + scenario_data["改编场景"] = adapted_scene + + print(adapted_scene) + print(f"\n请描述{config['bot']['nickname']}在这种情况下会如何反应:") + response = input().strip() + + if not response: + print("反应描述不能为空!") + continue + + print("\n正在评估您的描述...") + scores = self.evaluate_response(adapted_scene, response, scenario_data["评估维度"]) + + # 更新最终分数 + for dimension, score in scores.items(): + self.final_scores[dimension] += score + self.dimension_counts[dimension] += 1 + + print("\n当前评估结果:") + print("-" * 30) + for dimension, score in scores.items(): + print(f"{dimension}: {score}/6") + + # 更新进度条 + progress_bar.update(1) + + # if i < total_scenarios: + # print("\n按回车键继续下一个场景...") + # input() + + progress_bar.close() + + # 计算平均分 + for dimension in self.final_scores: + if self.dimension_counts[dimension] > 0: + self.final_scores[dimension] = round(self.final_scores[dimension] / self.dimension_counts[dimension], 2) + + print("\n" + "=" * 50) + print(f" {config['bot']['nickname']}的人格特征评估结果 ".center(50)) + print("=" * 50) + for trait, score in self.final_scores.items(): + print(f"{trait}: {score}/6".ljust(20) + f"测试场景数:{self.dimension_counts[trait]}".rjust(30)) + print("=" * 50) + + # 返回评估结果 + return self.get_result() + + def get_result(self): + """ + 获取评估结果 + """ + return { + "final_scores": self.final_scores, + "dimension_counts": self.dimension_counts, + "scenarios": self.scenarios, + "bot_info": { + "nickname": config['bot']['nickname'], + "gender": config['identity']['gender'], + "age": config['identity']['age'], + "height": config['identity']['height'], + "weight": config['identity']['weight'], + "appearance": config['identity']['appearance'], + "personality_core": config['personality']['personality_core'], + "personality_sides": config['personality']['personality_sides'], + "identity_detail": config['identity']['identity_detail'] + } + } def main(): - print("欢迎使用人格形象创建程序!") - print("接下来,您将面对一系列场景(共15个)。请根据您想要创建的角色形象,描述在该场景下可能的反应。") - print("每个场景都会评估不同的人格维度,最终得出完整的人格特征评估。") - print("评分标准:1=非常不符合,2=比较不符合,3=有点不符合,4=有点符合,5=比较符合,6=非常符合") - print("\n准备好了吗?按回车键开始...") - input() - evaluator = PersonalityEvaluator_direct() - final_scores = {"开放性": 0, "严谨性": 0, "外向性": 0, "宜人性": 0, "神经质": 0} - dimension_counts = {trait: 0 for trait in final_scores.keys()} + result = evaluator.run_evaluation() - for i, scenario_data in enumerate(evaluator.scenarios, 1): - print(f"\n场景 {i}/{len(evaluator.scenarios)} - {scenario_data['场景编号']}:") - print("-" * 50) - print(scenario_data["场景"]) - print("\n请描述您的角色在这种情况下会如何反应:") - response = input().strip() - - if not response: - print("反应描述不能为空!") - continue - - print("\n正在评估您的描述...") - scores = evaluator.evaluate_response(scenario_data["场景"], response, scenario_data["评估维度"]) - - # 更新最终分数 - for dimension, score in scores.items(): - final_scores[dimension] += score - dimension_counts[dimension] += 1 - - print("\n当前评估结果:") - print("-" * 30) - for dimension, score in scores.items(): - print(f"{dimension}: {score}/6") - - if i < len(evaluator.scenarios): - print("\n按回车键继续下一个场景...") - input() - - # 计算平均分 - for dimension in final_scores: - if dimension_counts[dimension] > 0: - final_scores[dimension] = round(final_scores[dimension] / dimension_counts[dimension], 2) - - print("\n最终人格特征评估结果:") - print("-" * 30) - for trait, score in final_scores.items(): - print(f"{trait}: {score}/6") - print(f"测试场景数:{dimension_counts[trait]}") - - # 保存结果 - result = {"final_scores": final_scores, "dimension_counts": dimension_counts, "scenarios": evaluator.scenarios} + # 准备简化的结果数据 + simplified_result = { + "openness": round(result["final_scores"]["开放性"] / 6, 1), # 转换为0-1范围 + "conscientiousness": round(result["final_scores"]["严谨性"] / 6, 1), + "extraversion": round(result["final_scores"]["外向性"] / 6, 1), + "agreeableness": round(result["final_scores"]["宜人性"] / 6, 1), + "neuroticism": round(result["final_scores"]["神经质"] / 6, 1), + "bot_nickname": config['bot']['nickname'] + } # 确保目录存在 - os.makedirs("results", exist_ok=True) + save_dir = os.path.join(root_path, "data", "personality") + os.makedirs(save_dir, exist_ok=True) + + # 创建文件名,替换可能的非法字符 + bot_name = config['bot']['nickname'] + # 替换Windows文件名中不允许的字符 + for char in ['\\', '/', ':', '*', '?', '"', '<', '>', '|']: + bot_name = bot_name.replace(char, '_') + + file_name = f"{bot_name}_personality.per" + save_path = os.path.join(save_dir, file_name) + + # 保存简化的结果 + with open(save_path, "w", encoding="utf-8") as f: + json.dump(simplified_result, f, ensure_ascii=False, indent=4) - # 保存到文件 + print(f"\n结果已保存到 {save_path}") + + # 同时保存完整结果到results目录 + os.makedirs("results", exist_ok=True) with open("results/personality_result.json", "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2) - print("\n结果已保存到 results/personality_result.json") - if __name__ == "__main__": main() diff --git a/麦麦开始学习(测试临时版).bat b/(临时版)麦麦开始学习.bat similarity index 100% rename from 麦麦开始学习(测试临时版).bat rename to (临时版)麦麦开始学习.bat diff --git a/(测试版)麦麦生成人格.bat b/(测试版)麦麦生成人格.bat new file mode 100644 index 000000000..e2aa5c06a --- /dev/null +++ b/(测试版)麦麦生成人格.bat @@ -0,0 +1,56 @@ +@echo off +chcp 65001 > nul +setlocal enabledelayedexpansion +cd /d %~dp0 + +title 麦麦人格生成 + +cls +echo ====================================== +echo 警告提示 +echo ====================================== +echo 1.这是一个demo系统,仅供体验,特性可能会在将来移除 +echo ====================================== + +echo. +echo ====================================== +echo 请选择Python环境: +echo 1 - venv (推荐) +echo 2 - conda +echo ====================================== +choice /c 12 /n /m "请输入数字选择(1或2): " + +if errorlevel 2 ( + echo ====================================== + set "CONDA_ENV=" + set /p CONDA_ENV="请输入要激活的 conda 环境名称: " + + :: 检查输入是否为空 + if "!CONDA_ENV!"=="" ( + echo 错误:环境名称不能为空 + pause + exit /b 1 + ) + + call conda activate !CONDA_ENV! + if errorlevel 1 ( + echo 激活 conda 环境失败 + pause + exit /b 1 + ) + + echo Conda 环境 "!CONDA_ENV!" 激活成功 + python src/individuality/per_bf_gen.py +) else ( + if exist "venv\Scripts\python.exe" ( + venv\Scripts\python src/individuality/per_bf_gen.py + ) else ( + echo ====================================== + echo 错误: venv环境不存在,请先创建虚拟环境 + pause + exit /b 1 + ) +) + +endlocal +pause