个人技术分享

原理

忠实度(Faithfulness)

def faithfulness_test():

    """ 忠实度 1 """
    question = "爱因斯坦出生于何时何地?"
    answer = "爱因斯坦于1879年3月14日出生于德国。"
    
    f_1_prompt = f"""
    给定一个问题和答案,请你从给定答案的每个句子中创建一个或多个陈述。并按如下格式编号输出:
    陈述1:[陈述1]
    陈述2:[陈述2]
    ...

    问题:{question}
    答案:{answer}
    """

    """ 忠实度 2 """
    relevant_context = ["阿尔伯特·爱因斯坦(Albert Einstein,1879 年 3 月 14 日出生)是一位出生于德国的理论物理学家,被广泛认为是有史以来最伟大、最有影响力的科学家之一。",]
    statements = '陈述1:爱因斯坦出生于德国。\n陈述2:爱因斯坦的出生日期是1879年3月14日。'
    f_2_prompt = f"""
    你的任务是根据给定的上下文判断以下陈述的可信度。具体要求如下:
    1. 对于每个陈述,请你判断它是否可以从给定的上下文信息中推断。
    2. 如果上下文支持该陈述,请返回 1;如果不支持该陈述,请返回 0。
    3. 请按照以下格式返回结果:
    陈述1:1或0
    陈述2:1或0
    ...

    上下文:{relevant_context}
    陈述:{statements}
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        # messages=[{"role": "user", "content": f_1_prompt}],
        messages=[{"role": "user", "content": f_2_prompt}],
    )

    print(response.choices[0].message)
    answer = response.choices[0].message.content
    print(answer)

答案相关性(Answer Relevance)

def answer_relevance_test():

    gt_question = "法国在哪里,首都是哪里?"

    """ 答案相关性 """
    # answer = "法国位于西欧,巴黎是其首都。"
    answer = "中国的首都是北京,中国位于亚洲,中国万岁!"
    # answer = "我今天中午想吃拉面了,兰州牛肉拉面。"
    # prompt = f"""
    # 请你为给定的答案生成一个或多个不重复的问题。
    # 请注意,你需要判断给定的答案是否模棱两可。如果是模棱两可,请返回 1;否则返回 0。模棱两可的答案是指那些回避的、含糊的或模糊不清的答案。例如:“不知道”、“不确定”等。

    # 答案:{answer}

    # 结果请以List形式返回:["问题1","问题2",...],并在列表最后添加判断结果(1或0)。
    # """
    prompt = f"""
    请你为给定的答案生成一个或多个不重复的问题。

    答案:{answer}

    结果请以JSON形式返回:{{"生成问题1": 问题1, "生成问题2": 问题2, ...}}
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    print(response.choices[0].message)
    answer = response.choices[0].message.content
    print("[DEBUG] answer: ", answer)

    print("[DEBUG] type(answer): ", type(answer))

    answer = list(json.loads(answer).values())

    questions = answer
    print("[DEBUG] questions: ", questions)

    sim_sum = np.array([cosine_similarity(get_text_embedding(client, gt_question), get_text_embedding(client, q)) for q in questions])
    ar = np.average(sim_sum)

    print("[DEBUG] sim_sum: ", sim_sum)
    print("[DEBUG] AR: ", ar)

上下文相关性(Context Relevance)

def context_relevance_test():
    """上下文相关性
    """
    question = "法国的首都是哪里?"
    context = ["法国位于西欧,拥有中世纪城市、高山村庄和地中海海滩。其首都巴黎以其时装屋、卢浮宫等古典艺术博物馆和埃菲尔铁塔等古迹而闻名。",
               "法国首都巴黎位于法国东部,是法国第二大城市。该国还以其葡萄酒和精致的美食而闻名。"]

    prompt = f"""
    请你从提供的上下文中提取所有可能有助于回答以下问题的相关句子。具体要求如下:
    1.提取所有不重复的相关句子。
    2.如果找不到相关句子,或者你认为无法从给定的上下文中回答该问题,请返回“信息不足”。
    3.在提取相关句子时,请勿修改上下文中的任何句子。
    4.需要再次强调的是,请勿直接回答问题,只提取相关句子。

    上下文:{context}
    问题:{question}
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    answer = response.choices[0].message.content
    print("[DEBUG] answer: ", answer)

上下文召回率(Context Recall)

def context_recall_test():
    """上下文召回率
    """
    gt = "法国位于西欧,巴黎是其首都。我想吃火锅。"
    # context = "法国位于西欧,拥有中世纪城市、高山村庄和地中海海滩。该国还以其葡萄酒和精致的美食而闻名。拉斯科的古代洞穴壁画、里昂的罗马剧院和广阔的凡尔赛宫都证明了其丰富的历史"
    context = "法国位于西欧,拥有中世纪城市、高山村庄和地中海海滩。其首都巴黎以其时装屋、卢浮宫等古典艺术博物馆和埃菲尔铁塔等古迹而闻名。"

    # prompt = f"""
    # 请你根据给定的上下文和真实答案,分析答案中的每个子句,并判断该子句是否可以由提供的上下文所验证。具体要求如下:
    # 1. 只使用 1 或 0 进行二分类。如果该子句可以归因于给定的上下文,请返回 1,否则请返回 0。
    # 2. 在判断过程中,请勿修改真实答案的子句与上下文的内容。
    # 3. 请以JSON形式返回结果:{{"子句1": 1/0, "子句2": 1/0,...}}。
    # 4. 需要强调的是,你分析的是真实答案中的每个子句,返回结果里请勿有上下文相关的信息。

    # 上下文:{context}
    # 真实答案:{gt}
    # """

    # prompt = f"""
    # 请根据给定的上下文和真实答案,分析答案中的每个子句,并判断其是否可以由上下文验证。具体要求如下:
    # 1. 如果子句可以由上下文验证,请返回 1,否则返回 0。
    # 2. 判断过程中,请勿修改真实答案的子句和上下文的内容。
    # 3. 结果以 JSON 形式返回:{{"子句1": 1/0, "子句2": 1/0, ...}}。
    # 4. 仅分析真实答案中的子句,结果中不包含上下文的信息。

    # 上下文:{context}
    # 真实答案:{gt}
    # """

    prompt = f"""
    请你根据给定的真实答案,将其分解成1条或多条不重复的陈述。

    真实答案:{gt}

    请以JSON形式返回结果:{{"陈述1": 陈述1, "陈述2": 陈述2,...}}。
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    statements = response.choices[0].message.content
    statements = list(json.loads(statements).values())
    print("[DEBUG] statements: ", statements)

    prompt = f"""
    请你根据给定的上下文和一些陈述,分析每条陈述,判断该陈述是否可以由上下文验证。具体要求如下:
    1. 如果该陈述可以由上下文验证,请返回 1,否则返回 0。
    2. 判断过程中,请勿修改陈述和上下文的内容。
    3. 结果以 JSON 形式返回:{{"陈述1": 1/0, "陈述2": 1/0, ...}}。
    4. 仅分析每条陈述,结果中不包含上下文的信息。

    上下文:{context}
    陈述:{statements}
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[{"role": "user", "content": prompt}],
    )
    response = response.choices[0].message.content
    print("[DEBUG] response: ", response)

答案正确性(Answer Correctness)

def answer_correctness_test():
    """答案正确性
    事实正确性 + 语义相似性
    """
    # gt = "爱因斯坦1879年出生于德国。"
    # answer = "爱因斯坦1879年出生于西班牙。"

    # gt = "法国位于西欧,巴黎是其首都。"
    # # answer = "根据给定的资料,法国位于亚洲,巴黎是其首都。"
    # answer = "根据给定的资料,我不确定法国是否位于亚洲,还是位于西欧,但我确定巴黎是其首都。"

    gt = """
    这个文档主要讨论了在“互联网+”背景下,永顺县塔卧镇红色旅游产品开发的现状、存在的问题以及相应的对策。文档分为三个部分:
    1. **红色旅游产品开发现状**:介绍了塔卧镇红色旅游产品的主要类型,包括游览路线参观、红色讲堂和纪念品。指出了核心产品开发建设不足、旅游体验产品经济效益低和产品价值定位不准确等问题。
    2. **存在的不足**:分析了塔卧镇红色旅游产品开发中的问题,如旅游景点整合程度低、产品开发程度小、基础设施配套不完善、旅游产品缺乏体验特点等。
    3. **开发对策**:提出了针对上述问题的对策,包括整合旅游景点、明确产品目标市场、打造塔卧式旅游产品、增强旅游体验、注重互联网与产品开发结合以及提升红色旅游产品知名度等。
    文档还强调了互联网在红色旅游产品开发中的重要作用,提出了利用互联网技术进行市场调研、产品设计、宣传推广和提升游客体验的建议。
    """
    answer = "这个文档的主要内容是关于永顺县塔卧镇红色旅游产品开发现状和存在的问题的分析。"


    prompt = f"""
    给你一个真实答案和一个回答,请你分析回答中的每个陈述,并将其分类到以下类别之一:
    - TP(真正例):在回答和真实答案中都存在的陈述。
    - FP(假正例):在回答中存在但在真实答案中未找到的陈述。
    - FN(假负例):在真实答案中存在但在回答中遗漏的相关陈述。

    需要强调的是:
    1. 首先你需要根据给定的回答,将其分解成1条或多条不重复的陈述,请勿改动回答的内容。
    2. 然后分析每条陈述,将该陈述分类到上述所提到的三个类别当中。
    3. 每个陈述必须准确地分类到一个类别中,不要试图解释真实答案和回答的含义,只需要比较它们中陈述的存在情况。
    4. 在你的返回结果中,请勿改动回答以及真实答案的内容。

    真实答案:{gt}
    回答:{answer}

    结果请以JSON形式返回:{{"TP": [数量、陈述], "FP": [数量、陈述], "FN": [数量、陈述], "陈述": [所有的陈述]}}
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    response = response.choices[0].message.content
    response = json.loads(response)
    print("[DEBUG] response: ", response)
    tp, fp, fn = response["TP"][0], response["FP"][0], response["FN"][0]
    print("[DEBUG] TP: {}, FP: {}, FN: {}".format(tp, fp, fn))

    ac_1 = tp / (tp + 0.5 * (fp + fn))
    print("[DEBUG] ac_1: ", ac_1)

    g_emb = get_text_embedding(client, gt)
    a_emb = get_text_embedding(client, answer)

    ac_2 = cosine_similarity(g_emb, a_emb)
    print("[DEBUG] ac_2: ", ac_2)

    ac_weight = [0.5, 0.5]
    ac = np.average([ac_1, ac_2], weights=ac_weight)
    print("[DEBUG] ac: ", ac)

    print("----------", np.linalg.norm(g_emb - a_emb))

答案完整性(Answer Integrity)

def content_integrity_test():
    """内容完整性
    """

    gt = """
    这个文档主要讨论了在“互联网+”背景下,永顺县塔卧镇红色旅游产品开发的现状、存在的问题以及相应的对策。文档分为三个部分:
    1. **红色旅游产品开发现状**:介绍了塔卧镇红色旅游产品的主要类型,包括游览路线参观、红色讲堂和纪念品。指出了核心产品开发建设不足、旅游体验产品经济效益低和产品价值定位不准确等问题。
    2. **存在的不足**:分析了塔卧镇红色旅游产品开发中的问题,如旅游景点整合程度低、产品开发程度小、基础设施配套不完善、旅游产品缺乏体验特点等。
    3. **开发对策**:提出了针对上述问题的对策,包括整合旅游景点、明确产品目标市场、打造塔卧式旅游产品、增强旅游体验、注重互联网与产品开发结合以及提升红色旅游产品知名度等。
    文档还强调了互联网在红色旅游产品开发中的重要作用,提出了利用互联网技术进行市场调研、产品设计、宣传推广和提升游客体验的建议。
    """
    answer = "这个文档的主要内容是关于永顺县塔卧镇红色旅游产品开发现状和存在的问题的分析。"

    # prompt = f"""
    # 请你分析并提取以下段落所涵盖的关键点。具体要求如下:
    # - 你提取的关键点要包含段落的主要内容。
    # - 请勿提取重复的关键点。

    # 段落:{gt}

    # 结果请以JSON形式返回:{{"关键点1": 关键点1, "关键点2": 关键点2, ...}}
    # """

    prompt = f"""
    请分析以下段落,并提取出涵盖其主要内容的所有关键词。具体要求如下:
    1. 关键词应全面覆盖段落的主要信息。
    2. 关键词应不重复,并尽可能简洁明确。
    3. 请勿包含标点符号或多余的修饰词。

    段落:{gt}

    结果请以Python列表的形式返回,列表中的每个元素为你提取的关键词,关键词为字符串类型,格式为:[关键词1, 关键词2, ...]
    """

    response = client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    response = response.choices[0].message.content
    keypoints = ast.literal_eval(response)
    print("[DEBUG] 关键词: ", keypoints)
    print("[DEBUG] 关键词数量: ", len(keypoints))

    matched_keypoints = [k for k in keypoints if k in answer]
    print("[DEBUG] 匹配的关键词: ", matched_keypoints)

    coverage = len(matched_keypoints) / len(keypoints)
    print("[DEBUG] 关键词覆盖率: ", coverage)

    # 计算信息密度
    answer_sentences = cut_sent(answer)
    gt_sentences = cut_sent(gt)

    print("[DEBUG] answer_sentences: ", answer_sentences, len(answer_sentences))
    print("[DEBUG] gt_sentences: ", gt_sentences, len(gt_sentences))

    punctuation_list = " 。?!,、;:“”‘’『』()[]〔〕〈〉《》「」【】……——-—―·.?!,;:'\"()[]{}<>@#$%^&*_+-=~`|\\/"
    # baidu_stopword = load_stopwords("./stopwords/baidu_stopwords.txt")

    with PynlpirContextManager():
        answer_words = [s for s in pynlpir.segment(answer.strip().replace("\n", "。"), pos_tagging=False) if s not in punctuation_list]
        gt_words = [s for s in pynlpir.segment(gt.strip().replace("\n", "。"), pos_tagging=False) if s not in punctuation_list]

    print("[DEBUG] answer_words: ", answer_words, len(answer_words))
    print("[DEBUG] gt_words: ", gt_words, len(gt_words))

    info_density = len(set(answer_words) & set(gt_words)) / len(set(answer_words) | set(gt_words))
    info_density2 = (len(answer_sentences) / len(gt_sentences) + len(answer_words) / len(gt_words)) / 2
    info_density3 = (len(answer_sentences) / len(gt_sentences) + len(answer_words) / len(gt_words) + info_density) / 3
    print("[DEBUG] 信息密度: ", info_density, info_density2, info_density3)