File size: 7,926 Bytes
28d41ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from langchain.vectorstores import Chroma
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from model_to_llm import model_to_llm 
from get_vectordb import get_vectordb
import sys
import re

class QA_chain_self():
    """"
    不带历史记录的问答链
    - model:调用的模型名称
    - temperature:温度系数,控制生成的随机性
    - top_k:返回检索的前k个相似文档
    - file_path:建库文件所在路径
    - persist_path:向量数据库持久化路径
    - appid:星火需要输入
    - api_key:所有模型都需要
    - Spark_api_secret:星火秘钥
    - Wenxin_secret_key:文心秘钥
    - embeddings:使用的embedding模型  
    - embedding_key:使用的embedding模型的秘钥(智谱或者OpenAI)
    - template:可以自定义提示模板,没有输入则使用默认的提示模板default_template_rq    
    """

    #基于召回结果和 query 结合起来构建的 prompt使用的默认提示模版
    default_template_rq = """你是一个三国大乱斗系统的AI助手。请根据以下角色信息回答问题。
    
    角色信息:
    {context}
    
    问题: {question}
    
    请按照以下规则回答:
    1. 如果问题是抽取角色或角色信息相关,请不要只回答角色名,而是回答以下信息:
       - 抽取结果:[角色名]
       - 角色特点:[简要描述]
       - 属性值:[列出关键属性]
       - 技能说明:[列出技能效果]
    
    
    2. 如果问题是战斗规则说明相关:
       请直接引用下面"回合制对战规则"部分的内容,包括:
       - 回合顺序:每回合速度快的一方先出手
       - 行动选择:每回合可选择普通攻击、使用技能、休息(回复1%体力和10灵力)
       - 技能使用:需要支付相应消耗,无法支付则无法发动
       - 伤害计算的逻辑:- 普通攻击伤害 = (攻击方攻击-防御方防御)/防御方耐力*2  
            - 技能附加效果(如增伤、减防、附加状态)独立计算。
       - 胜负判定:体力降为0或以下即判负

    3. 如果问题是对战、模拟战、战斗相关:
       - 对战双方:[双方角色名]
       - 行动类型:[普通攻击/技能/休息]
       - 行动结果:[详细描述]
       - 伤害计算:[如果有伤害,显示计算过程]
       - 状态变化:[双方状态变化]
    
    4. 如果问题是状态查询相关:
       - 当前状态:[详细描述]
       - 可用行动:[可选操作]
       - 建议策略:[战术建议]
    
    回答要严谨简约,并保持专业性和准确性。如果不知道答案,请直接说明,不要编造信息。"""

    def __init__(self, model:str, temperature:float=0.0, top_k:int=4,  file_path:str=None, persist_path:str=None, appid:str=None, api_key:str=None, Spark_api_secret:str=None,Wenxin_secret_key:str=None, embedding = "openai",  embedding_key = None, template=default_template_rq):
        self.model = model
        self.temperature = temperature
        self.top_k = top_k
        self.file_path = file_path
        self.persist_path = persist_path
        self.appid = appid
        self.api_key = api_key
        self.Spark_api_secret = Spark_api_secret
        self.Wenxin_secret_key = Wenxin_secret_key
        self.embedding = embedding
        self.embedding_key = embedding_key
        self.template = template
        self.vectordb = get_vectordb(self.file_path, self.persist_path, self.embedding,self.embedding_key)
        self.llm = model_to_llm(self.model, self.temperature, self.appid, self.api_key, self.Spark_api_secret,self.Wenxin_secret_key)

        self.QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                    template=self.template)
        self.retriever = self.vectordb.as_retriever(
            search_type="similarity_score_threshold",
            search_kwargs={
                'k': self.top_k,
                'score_threshold': 0.3  # 降低阈值获取更多结果
            }
        )
        # 自定义 QA 链
        self.qa_chain = RetrievalQA.from_chain_type(llm=self.llm,
                                        retriever=self.retriever,
                                        return_source_documents=True,
                                        chain_type_kwargs={"prompt":self.QA_CHAIN_PROMPT})

    #基于大模型的问答 prompt 使用的默认提示模版
    #default_template_llm = """请回答下列问题:{question}"""
           
    def answer(self, question:str=None, temperature = None, top_k = 4):
        """"
        核心方法,调用问答链
        arguments: 
        - question:用户提问
        """

        if len(question) == 0:
            return ""
        
        if temperature == None:
            temperature = self.temperature
            
        if top_k == None:
            top_k = self.top_k

        result = self.qa_chain({"query": question, "temperature": temperature, "top_k": top_k})
        answer = result["result"]
        answer = re.sub(r"\\n", '<br/>', answer)
        return answer   

    def battle_analysis(self, character1: str, character2: str):
        """
        分析两个角色之间的战斗
        """
        question = f"请分析{character1}{character2}之间的战斗,谁会获胜?请详细分析双方的属性、技能和战斗策略。"
        return self.answer(question, temperature=0.7, top_k=6)

    def skill_analysis(self, character: str, skill: str):
        """
        分析特定角色的特定技能
        """
        question = f"请详细分析{character}{skill}技能,包括技能效果、消耗和最佳使用时机。"
        return self.answer(question, temperature=0.5, top_k=4)

    def character_comparison(self, character1: str, character2: str):
        """
        比较两个角色的属性
        """
        question = f"请详细比较{character1}{character2}的属性,包括攻击力、防御力、体力、耐力、法力、闪避和速度,并分析各自的优势和劣势。"
        return self.answer(question, temperature=0.5, top_k=6)

    def battle_strategy(self, character: str, opponent: str):
        """
        为特定角色提供战斗策略
        """
        question = f"如果{character}要对抗{opponent},应该采用什么战斗策略?请根据双方的属性和技能给出具体的建议。"
        return self.answer(question, temperature=0.7, top_k=6)   

    def draw_character(self, specific_character: str = None):
        """
        抽取角色卡
        specific_character: 如果指定了具体角色,则抽取该角色;否则随机抽取
        """
        if specific_character:
            question = f"请抽取{specific_character}的角色卡,并详细介绍其属性和技能特点。"
        else:
            question = "请随机抽取一张三国人物卡,并详细介绍该角色的属性和技能特点。"
        return self.answer(question, temperature=0.7, top_k=4)

    def battle_simulation(self, player_character: str, action: str):
        """
        模拟战斗
        player_character: 玩家角色
        action: 玩家选择的行动(普通攻击/使用技能/休息)
        """
        question = f"玩家使用{player_character}选择了{action},请模拟一场战斗,详细描述战斗过程和结果。"
        return self.answer(question, temperature=0.8, top_k=6)

    def character_status(self, character: str):
        """
        查询角色状态
        """
        question = f"请查询{character}的当前状态,包括体力、灵力、技能冷却等信息。"
        return self.answer(question, temperature=0.5, top_k=4)