Skip to content

Commit 206cac2

Browse files
committed
[feat][add intent mudule]
1 parent 793c6ba commit 206cac2

File tree

7 files changed

+579
-31
lines changed

7 files changed

+579
-31
lines changed

muagent/base_configs/prompts/intention_template_prompt.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from typing import Union, Optional
2+
3+
14
RECOGNIZE_INTENTION_PROMPT = """你是一个任务决策助手,能够将理解用户意图并决策采取最合适的行动,尽可能地以有帮助和准确的方式回应人类,
25
使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。
36
有效的 'action' 值为:'planning'(需要先进行拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)or "tool_using" (使用工具来回答问题) or 'coding'(生成可执行的代码)。
@@ -59,3 +62,77 @@
5962
6063
### Ouput Response
6164
'''
65+
66+
67+
def get_intention_prompt(
68+
background: str, intentions: Union[list, tuple], examples: Optional[dict]=None
69+
) -> str:
70+
nums_zh = ('一', '两', '三', '四', '五', '六', '七', '八', '九', '十')
71+
72+
intention_num = len(intentions)
73+
num_zh = nums_zh[intention_num - 1] if intention_num <= 10 else intention_num
74+
prompt = f'##背景##\n{background}\n\n##任务##\n辨别用户的询问意图,包括以下{num_zh}类:\n'
75+
76+
for i, val in enumerate(intentions):
77+
if isinstance(val, (list, tuple)):
78+
k, v = val
79+
cur_intention = f'{i + 1}. {k}{v}\n'
80+
else:
81+
cur_intention = f'{i + 1}. {val}\n'
82+
prompt += cur_intention
83+
84+
prompt += '\n##注意事项##\n'
85+
num_range_str = '、'.join(map(str, range(1, intention_num + 1)))
86+
prompt += f'回答:数字{num_range_str}中的一个来表示用户的询问意图,对应上述{num_zh}种分类。避免提供额外解释或其他信息。\n\n'
87+
88+
if examples:
89+
prompt += '##示例##\n'
90+
intention_idx_map = {k[0]: idx + 1 for idx, k in enumerate(intentions)}
91+
for query, ans in examples.items():
92+
ans = intention_idx_map[ans]
93+
prompt += f'询问:{query}\n回答:{ans}\n\n'
94+
95+
prompt += '##用户询问##\n询问:{query}\n回答:'
96+
return prompt
97+
98+
99+
INTENTIONS_CONSULT_WHICH = [
100+
('整体计划查询', '用户询问关于某个解决方案的完整流程或步骤,包含但不限于“整个流程”、“步骤”、“流程图”等词汇或概念。'),
101+
('下一步任务查询', '用户询问在某个解决方案的特定步骤中应如何操作或处理,通常会提及“下一步”、“具体操作”、“如何做”等,且明确指向解决方案中的某个特定环节。'),
102+
('闲聊', '用户询问的内容与当前的技术问题或解决方案无关,更多是出于兴趣或社交性质的交流。')
103+
]
104+
CONSULT_WHICH_PROMPT = get_intention_prompt(
105+
'作为运维领域的客服,您的职责是根据用户询问的内容,精准判断其背后的意图,以便提供最恰当的服务和支持。',
106+
INTENTIONS_CONSULT_WHICH,
107+
{
108+
'系统升级的整个流程是怎样的?': '整体计划查询',
109+
'听说你们采用了新工具,能讲讲它的特点吗?': '闲聊'
110+
}
111+
)
112+
113+
INTENTIONS_WHETHER_EXEC = [
114+
('执行', '当用户声明自己在使用某平台、服务、产品或功能时遇到具体问题,且明确表示不知道如何解决时,其意图应被分类为“执行”。'),
115+
('询问', '当用户明确询问某些解决方案的背景、流程、方式方法等信息,或只是出于好奇想要了解更多信息,或只是简单闲聊时,其意图应被分类为“询问”。')
116+
]
117+
WHETHER_EXECUTE_PROMPT = get_intention_prompt(
118+
'作为运维领域的客服,您需要根据用户询问判断其主要意图,以确定接下来的运维流程。',
119+
INTENTIONS_WHETHER_EXEC,
120+
{
121+
'为什么我的优惠券使用失败?': '执行',
122+
'我想知道如何才能更好地优化我的服务器性能,你们有什么建议吗?': '询问'
123+
}
124+
)
125+
126+
DIRECT_CHAT_PROMPT = """##背景##
127+
作为运维领域的客服,您的职责是根据自身专业知识回答用户询问,以便提供最恰当的服务和支持。
128+
129+
##任务##
130+
基于您所掌握的领域知识,对用户的提问进行回答。
131+
132+
##注意事项##
133+
请尽量从客观的角度来回答问题,内容符合事实、有理有据。
134+
135+
##用户询问##
136+
询问:{query}
137+
回答:
138+
"""

muagent/db_handler/vector_db_handler/tbase_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def search(self, query, index_name: str = None, query_params: dict = {}, limit=1
101101
res = index.search(query, query_params=query_params)
102102
return res
103103

104-
def vector_search(self, base_query: str, index_name: str = None, query_params: dict={}):
104+
def vector_search(self, base_query: str, index_name: str = None, query_params: dict={}, limit=10):
105105
'''
106106
vector_search
107107
:param base_query:
@@ -114,7 +114,7 @@ def vector_search(self, base_query: str, index_name: str = None, query_params: d
114114
.sort_by('distance')
115115
.dialect(2)
116116
)
117-
r = self.search(query, index_name, query_params)
117+
r = self.search(query, index_name, query_params, limit=limit)
118118
return r
119119

120120
def delete(self, content):

muagent/schemas/common/auto_extract_graph_schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class GNode(BaseModel):
3333
type: str
3434
attributes: Dict
3535

36+
def __getattr__(self, name: str):
37+
return self.attributes.get(name)
38+
3639

3740
class GEdge(BaseModel):
3841
start_id: str

muagent/service/ekg_construct/ekg_construct_base.py

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
from muagent.base_configs.env_config import KB_ROOT_PATH
3030

31+
from muagent.service.ekg_inference.intention_router import IntentionRouter
3132
from muagent.llm_models.get_embedding import get_embedding
3233
from muagent.utils.common_utils import getCurrentDatetime, getCurrentTimestap
3334
from muagent.utils.common_utils import double_hashing
@@ -53,6 +54,7 @@ def __init__(
5354
gb_config: GBConfig = None,
5455
tb_config: TBConfig = None,
5556
sls_config: SLSConfig = None,
57+
intention_router: IntentionRouter = None,
5658
do_init: bool = False,
5759
kb_root_path: str = KB_ROOT_PATH,
5860
):
@@ -71,6 +73,7 @@ def __init__(
7173

7274
# get llm model
7375
self.model = getChatModelFromConfig(self.llm_config)
76+
self.intention_router = IntentionRouter(self.model, embed_config=self.embed_config)
7477
# init db handler
7578
self.init_handler()
7679

@@ -519,16 +522,17 @@ def create_ekg(
519522
text: str,
520523
teamid: str,
521524
service_name: str,
525+
rootid: str,
522526
intent_text: str = None,
523527
intent_nodes: List[str] = [],
524528
all_intent_list: List=[],
525-
do_save: bool = False
529+
do_save: bool = False,
526530
):
527531

528532
if intent_nodes:
529533
ancestor_list = intent_nodes
530534
elif intent_text or text:
531-
ancestor_list, all_intent_list = self.get_intents(intent_text or text)
535+
ancestor_list, all_intent_list = self.get_intents(rootid, intent_text or text)
532536
else:
533537
raise Exception(f"must have intent infomation")
534538

@@ -542,6 +546,9 @@ def create_ekg(
542546
graph = self.write2kg(result["sls_graph"], result["tbase_graph"], teamid, do_save=do_save)
543547
result["graph"] = graph
544548
return result
549+
550+
def dsl2graph(self, ):
551+
pass
545552

546553
def text2graph(self, text: str, intents: List[str], all_intent_list: List[str], teamid: str) -> dict:
547554
# generate graph by llm
@@ -600,34 +607,17 @@ def returndsl(self, graph_datas_by_path: dict, intents: List[str], ) -> dict:
600607
res["graph"] = Graph(nodes=merge_gbase_nodes, edges=merge_gbase_edges, paths=[])
601608
return res
602609

603-
def get_intent_by_alarm(self, ):
604-
pass
605-
606-
def get_intents(self, alarm_list: list[dict], ) -> EKGIntentResp:
610+
def get_intents(self, rootid, text: str):
607611
'''according contents search intents'''
608-
ancestor_list = set()
609-
all_intent_list = []
610-
for alarm in alarm_list:
611-
ancestor, all_intent = self.get_intent_by_alarm(alarm)
612-
if ancestor is None:
613-
continue
614-
ancestor_list.add(ancestor)
615-
all_intent_list.append(all_intent)
616-
617-
return list(ancestor_list), all_intent_list
618-
619-
def get_intents_by_alarms(self, alarm_list: list[dict], ) -> EKGIntentResp:
620-
'''according contents search intents'''
621-
ancestor_list = set()
622-
all_intent_list = []
623-
for alarm in alarm_list:
624-
ancestor, all_intent = self.get_intent_by_alarm(alarm)
625-
if ancestor is None:
626-
continue
627-
ancestor_list.add(ancestor)
628-
all_intent_list.append(all_intent)
629-
630-
return list(ancestor_list), all_intent_list
612+
result = self.intention_router.get_intention_by_node_info_nlp(
613+
root_node_id=rootid,
614+
query=text,
615+
start_from_root=True,
616+
gb_handler=self.gb,
617+
tb_handler=self.tb,
618+
agent=self.model
619+
)
620+
return result.get("node_id", ), []
631621

632622
def get_graph_by_text(self, text: str) -> EKGSlsData:
633623
'''according text generate ekg's raw datas'''
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import re
2+
import Levenshtein
3+
from muagent.schemas.common import GNode
4+
5+
6+
class MatchRule:
7+
@classmethod
8+
def edit_distance(cls, node: GNode, pattern=None, **kwargs):
9+
if len(kwargs) == 0:
10+
return -float('inf')
11+
12+
s = list(kwargs.values())[0]
13+
desc: str = node.attributes.get('description', '')
14+
15+
if pattern is None:
16+
return -Levenshtein.distance(desc, s)
17+
18+
desc_list = re.findall(pattern, desc)
19+
if not desc_list:
20+
return -float('inf')
21+
22+
return max([-Levenshtein.distance(x, s) for x in desc_list])
23+
24+
@classmethod
25+
def edit_distance_integer(cls, node: GNode, **kwargs):
26+
return cls.edit_distance(node, pattern='\d+', **kwargs)
27+
28+
29+
class RuleDict(dict):
30+
def __init__(self, **kwargs):
31+
super().__init__(**kwargs)
32+
33+
def save(self):
34+
"""save the rules"""
35+
raise NotImplementedError
36+
37+
def load(self, **kwargs):
38+
"""load the rules"""
39+
raise NotImplementedError
40+
41+
rule_dict = RuleDict()

0 commit comments

Comments
 (0)