Spaces:
Sleeping
Sleeping
Boris
commited on
Commit
·
f72b7f7
1
Parent(s):
0a4ac5d
src
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .DS_Store +0 -0
- services/b2b/b2b_service.py → .history/src/b2b/b2b_service_20250630021334.py +0 -0
- .history/src/b2b/b2b_service_20250630135358.py +28 -0
- services/base_graph.py → .history/src/base_graph_20250629185111.py +0 -0
- .history/src/base_graph_20250630135358.py +126 -0
- services/base_service.py → .history/src/base_service_20250630134728.py +0 -0
- .history/src/base_service_20250630135358.py +76 -0
- services/borlas/borlas_service.py → .history/src/borlas/borlas_service_20250630021744.py +0 -0
- .history/src/borlas/borlas_service_20250630135358.py +28 -0
- services/corporate_email/corporate_email_service.py → .history/src/corporate_email/corporate_email_service_20250628144053.py +0 -0
- .history/src/corporate_email/corporate_email_service_20250630135358.py +24 -0
- services/crm/crm_service.py → .history/src/crm/crm_service_20250630021458.py +0 -0
- .history/src/crm/crm_service_20250630135358.py +28 -0
- services/internet_access/internet_access_service.py → .history/src/internet_access/internet_access_service_20250630012853.py +0 -0
- .history/src/internet_access/internet_access_service_20250630135358.py +27 -0
- services/one_c/one_c_service.py → .history/src/one_c/one_c_service_20250630021626.py +0 -0
- .history/src/one_c/one_c_service_20250630135358.py +28 -0
- services/openvpn/openvpn_prompts.py → .history/src/openvpn/openvpn_prompts_20250627163312.py +0 -0
- .history/src/openvpn/openvpn_prompts_20250630135358.py +103 -0
- services/openvpn/openvpn_service.py → .history/src/openvpn/openvpn_service_20250627125242.py +0 -0
- .history/src/openvpn/openvpn_service_20250630135358.py +44 -0
- services/print_and_scan/print_and_scan_service.py → .history/src/print_and_scan/print_and_scan_service_20250630021904.py +0 -0
- .history/src/print_and_scan/print_and_scan_service_20250630135358.py +28 -0
- services/service_by_name.py → .history/src/service_by_name_20250630022251.py +0 -0
- .history/src/service_by_name_20250630135358.py +29 -0
- services/streamlit_app.py → .history/src/streamlit_app_20250630025024.py +0 -0
- .history/src/streamlit_app_20250630135358.py +101 -0
- services/template/_service.py → .history/src/template/_service_20250627160824.py +0 -0
- .history/src/template/_service_20250630135358.py +28 -0
- {services → src}/.DS_Store +0 -0
- {services → src}/__init__.py +0 -0
- {services → src}/b2b/__init__.py +0 -0
- {services → src}/b2b/b2b_prompts.py +0 -0
- src/b2b/b2b_service.py +28 -0
- src/base_graph.py +126 -0
- src/base_service.py +76 -0
- {services → src}/borlas/__init__.py +0 -0
- {services → src}/borlas/borlas_prompts.py +0 -0
- src/borlas/borlas_service.py +28 -0
- {services → src}/broken_cases.txt +0 -0
- {services → src}/corporate_email/__init__.py +0 -0
- {services → src}/corporate_email/corporate_email_prompts.py +0 -0
- src/corporate_email/corporate_email_service.py +24 -0
- {services → src}/crm/__init__.py +0 -0
- {services → src}/crm/crm_prompts.py +0 -0
- src/crm/crm_service.py +28 -0
- {services → src}/get_answer_gigachat.py +0 -0
- {services → src}/get_classification.py +3 -3
- {services → src}/internet_access/__init__.py +0 -0
- {services → src}/internet_access/internet_access_cases.json +0 -0
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
services/b2b/b2b_service.py → .history/src/b2b/b2b_service_20250630021334.py
RENAMED
File without changes
|
.history/src/b2b/b2b_service_20250630135358.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.b2b.b2b_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class B2BService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
services/base_graph.py → .history/src/base_graph_20250629185111.py
RENAMED
File without changes
|
.history/src/base_graph_20250630135358.py
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Annotated, Type
|
2 |
+
from langgraph.graph import StateGraph
|
3 |
+
from langchain_core.messages import HumanMessage, ToolMessage
|
4 |
+
from langgraph.graph.message import add_messages
|
5 |
+
from typing_extensions import TypedDict
|
6 |
+
from src.base_service import BaseService
|
7 |
+
from src.get_answer_gigachat import AnswerGigaChat
|
8 |
+
import logging
|
9 |
+
import os
|
10 |
+
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
+
class State(TypedDict):
|
14 |
+
messages: Annotated[list, add_messages]
|
15 |
+
|
16 |
+
class BaseGraph:
|
17 |
+
def __init__(self, service: Type[BaseService]):
|
18 |
+
self.service = service
|
19 |
+
# self._initialize_logging()
|
20 |
+
self.tools_dict = {tool.name: tool for tool in service.tools}
|
21 |
+
self.llm_with_tools = AnswerGigaChat().bind_tools(service.tools)
|
22 |
+
self.messages = service.get_initial_messages()
|
23 |
+
self.graph = self._build_graph()
|
24 |
+
logger.info(f"BaseGraph with service {service} was built")
|
25 |
+
|
26 |
+
def rebuild_with_new_service(self, service: Type[BaseService]):
|
27 |
+
self.service = service
|
28 |
+
self.tools_dict = {tool.name: tool for tool in service.tools}
|
29 |
+
self.llm_with_tools = AnswerGigaChat().bind_tools(service.tools)
|
30 |
+
self.graph = self._build_graph()
|
31 |
+
self.messages = service.get_messages_from_redirect(self.messages)
|
32 |
+
logger.info(f"BaseGraph was rebuilt with service {service}")
|
33 |
+
|
34 |
+
def _initialize_logging(self):
|
35 |
+
if os.path.exists(self.service.log_path):
|
36 |
+
os.remove(self.service.log_path)
|
37 |
+
logging.basicConfig(
|
38 |
+
level=logging.INFO,
|
39 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
40 |
+
handlers=[logging.FileHandler(self.service.log_path)]
|
41 |
+
)
|
42 |
+
|
43 |
+
def _agent_node(self, state: State):
|
44 |
+
try:
|
45 |
+
logger.info("Starting agent_node")
|
46 |
+
messages = state["messages"]
|
47 |
+
response = self.llm_with_tools.invoke(messages)
|
48 |
+
response.content = self._clean_response(response.content)
|
49 |
+
return {"messages": [response]}
|
50 |
+
except Exception as e:
|
51 |
+
logger.error(f"Error in agent_node: {str(e)}", exc_info=True)
|
52 |
+
raise
|
53 |
+
|
54 |
+
def _tool_node(self, state: State):
|
55 |
+
try:
|
56 |
+
logger.info("Starting tool_node")
|
57 |
+
last_message = state["messages"][-1]
|
58 |
+
tool_calls = last_message.tool_calls
|
59 |
+
|
60 |
+
results = []
|
61 |
+
for call in tool_calls:
|
62 |
+
tool_name = call["name"]
|
63 |
+
logger.info(f"Running tool {tool_name}")
|
64 |
+
args = call["args"]
|
65 |
+
|
66 |
+
tool = self.tools_dict.get(tool_name)
|
67 |
+
if not tool:
|
68 |
+
raise ValueError(f"Tool {tool_name} not found")
|
69 |
+
|
70 |
+
tool_response = tool.invoke(args)
|
71 |
+
if tool_name == "make_redirect":
|
72 |
+
self.rebuild_with_new_service(tool_response)
|
73 |
+
results.append(str(tool_response))
|
74 |
+
|
75 |
+
return {"messages": [ToolMessage(content=", ".join(results), tool_call_id=call["id"])]}
|
76 |
+
except Exception as e:
|
77 |
+
logger.error(f"Error in tool_node: {str(e)}", exc_info=True)
|
78 |
+
raise
|
79 |
+
|
80 |
+
def _should_continue(self, state: State):
|
81 |
+
try:
|
82 |
+
logger.info("Checking should continue")
|
83 |
+
last_message = state["messages"][-1]
|
84 |
+
return "tool" if "function_call" in last_message.additional_kwargs else "end"
|
85 |
+
except Exception as e:
|
86 |
+
logger.error(f"Error in should_continue: {str(e)}", exc_info=True)
|
87 |
+
raise
|
88 |
+
|
89 |
+
def _build_graph(self):
|
90 |
+
try:
|
91 |
+
logger.info("Building graph")
|
92 |
+
graph_builder = StateGraph(State)
|
93 |
+
|
94 |
+
graph_builder.add_node("agent", self._agent_node)
|
95 |
+
graph_builder.add_node("tool", self._tool_node)
|
96 |
+
|
97 |
+
graph_builder.add_conditional_edges(
|
98 |
+
"agent",
|
99 |
+
self._should_continue,
|
100 |
+
{"tool": "tool", "end": "__end__"}
|
101 |
+
)
|
102 |
+
graph_builder.add_edge("tool", "agent")
|
103 |
+
graph_builder.set_entry_point("agent")
|
104 |
+
|
105 |
+
return graph_builder.compile()
|
106 |
+
except Exception as e:
|
107 |
+
logger.error(f"Error building graph: {str(e)}", exc_info=True)
|
108 |
+
raise
|
109 |
+
|
110 |
+
def _clean_response(self, content: str) -> str:
|
111 |
+
content = content.replace("</final_answer>", "<final_answer>").replace("</thinking>", "<thinking>")
|
112 |
+
if "<final_answer>" in content:
|
113 |
+
content = content.split("<final_answer>")[1]
|
114 |
+
if "<thinking>" in content:
|
115 |
+
content = content.split("<thinking>")[-1]
|
116 |
+
return content
|
117 |
+
|
118 |
+
def invoke(self, user_input):
|
119 |
+
try:
|
120 |
+
self.messages.append(HumanMessage(content=user_input))
|
121 |
+
result = self.graph.invoke({"messages": self.messages})
|
122 |
+
self.messages = result["messages"]
|
123 |
+
return result
|
124 |
+
except Exception as e:
|
125 |
+
logger.error(f"Error invoking graph: {str(e)}", exc_info=True)
|
126 |
+
raise
|
services/base_service.py → .history/src/base_service_20250630134728.py
RENAMED
File without changes
|
.history/src/base_service_20250630135358.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import ABC, abstractmethod
|
2 |
+
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
|
3 |
+
from system_prompt_template import DIALOG_PROMPT
|
4 |
+
from typing import List
|
5 |
+
from langchain_core.tools import BaseTool, tool
|
6 |
+
from src.service_by_name import get_service_by_name
|
7 |
+
import logging
|
8 |
+
import time
|
9 |
+
import os
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
|
12 |
+
load_dotenv()
|
13 |
+
logger = logging.getLogger(__name__)
|
14 |
+
|
15 |
+
class BaseService(ABC):
|
16 |
+
def __init__(self):
|
17 |
+
self._base_tools = [self.closing_task,
|
18 |
+
self.make_jira_task,
|
19 |
+
]
|
20 |
+
|
21 |
+
@property
|
22 |
+
@abstractmethod
|
23 |
+
def system_prompt(self) -> str:
|
24 |
+
"""Возвращает системный промпт для сервиса"""
|
25 |
+
pass
|
26 |
+
|
27 |
+
@property
|
28 |
+
@abstractmethod
|
29 |
+
def tools(self) -> List[BaseTool]:
|
30 |
+
"""Возвращает список инструментов сервиса"""
|
31 |
+
pass
|
32 |
+
|
33 |
+
@property
|
34 |
+
def log_path(self) -> str:
|
35 |
+
"""Возвращает путь до папки с логами"""
|
36 |
+
return os.getenv("LOG_SERVICES_PATH")
|
37 |
+
|
38 |
+
def get_initial_messages(self):
|
39 |
+
"""Возвращает начальные сообщения для графа"""
|
40 |
+
return [SystemMessage(content=self.system_prompt)]
|
41 |
+
|
42 |
+
def get_messages_from_redirect(self, messages):
|
43 |
+
messages_formatted = ""
|
44 |
+
for message in messages:
|
45 |
+
if isinstance(message, HumanMessage):
|
46 |
+
message += f"user: '{message.content}'\n"
|
47 |
+
elif isinstance(message, AIMessage):
|
48 |
+
message += f"assistant: '{message.content}'\n"
|
49 |
+
|
50 |
+
return [SystemMessage(content=self.system_prompt + DIALOG_PROMPT.format(messages_formatted))]
|
51 |
+
|
52 |
+
@tool
|
53 |
+
def make_redirect(service_name: str):
|
54 |
+
"""Когда оказывается, что пользователю нужен эксперт в другом сервисе,
|
55 |
+
пользователя нужно перенаправить на соответствующего эксперта.
|
56 |
+
Функция принимает аргумент service_name из списка:
|
57 |
+
['openvpn', 'corporate_email', 'internet_access'] и пересоздает класс эксперта
|
58 |
+
с соответствующими новому сервису знаниями."""
|
59 |
+
logger.info("redirecting question to service {service_name}")
|
60 |
+
return get_service_by_name(service_name)
|
61 |
+
|
62 |
+
|
63 |
+
@tool
|
64 |
+
def closing_task(question: str) -> None:
|
65 |
+
"""Задача выполнена, сохраняем результаты"""
|
66 |
+
print("### Задача выполнена, сохраняем результаты")
|
67 |
+
time.sleep(2)
|
68 |
+
logger.info("task closed")
|
69 |
+
|
70 |
+
@tool
|
71 |
+
def make_jira_task(question: str) -> None:
|
72 |
+
"""Задача не может быть выполнена с помощью ии, создаем задачу в jira для того,
|
73 |
+
чтобы её выполнил человек"""
|
74 |
+
time.sleep(2)
|
75 |
+
print("### Скриптом создается задача в jira с соответствующими деталями")
|
76 |
+
logger.info("jira task was make")
|
services/borlas/borlas_service.py → .history/src/borlas/borlas_service_20250630021744.py
RENAMED
File without changes
|
.history/src/borlas/borlas_service_20250630135358.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.borlas.borlas_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class BorlasService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
services/corporate_email/corporate_email_service.py → .history/src/corporate_email/corporate_email_service_20250628144053.py
RENAMED
File without changes
|
.history/src/corporate_email/corporate_email_service_20250630135358.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.corporate_email.corporate_email_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class CorporateEmailService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + []
|
19 |
+
|
20 |
+
@tool
|
21 |
+
def check_lock_in_active_dir_and_password(question: str) -> None:
|
22 |
+
"""Проверка блокировки учетной записи сотрудника в системе и разблокировка и проверка на просроченный пароль и обновление пароля. Если эти проблемы вскрываются, то функция их исправляет."""
|
23 |
+
time.sleep(4)
|
24 |
+
logger.info("check_lock_in_active_dir_and_password")
|
services/crm/crm_service.py → .history/src/crm/crm_service_20250630021458.py
RENAMED
File without changes
|
.history/src/crm/crm_service_20250630135358.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.crm.crm_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class CRMService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
services/internet_access/internet_access_service.py → .history/src/internet_access/internet_access_service_20250630012853.py
RENAMED
File without changes
|
.history/src/internet_access/internet_access_service_20250630135358.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.internet_access.internet_access_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
class InternetAccessService(BaseService):
|
11 |
+
@property
|
12 |
+
def system_prompt(self) -> str:
|
13 |
+
return SYSTEM_PROMPT
|
14 |
+
|
15 |
+
@property
|
16 |
+
def tools(self) -> List[BaseTool]:
|
17 |
+
return self._base_tools + [
|
18 |
+
self.example_func,
|
19 |
+
]
|
20 |
+
|
21 |
+
@tool
|
22 |
+
def example_func(question: str) -> None:
|
23 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
24 |
+
print("Я example_func")
|
25 |
+
time.sleep(4)
|
26 |
+
logger.info("Кто-то забыл удалить example_func!")
|
27 |
+
|
services/one_c/one_c_service.py → .history/src/one_c/one_c_service_20250630021626.py
RENAMED
File without changes
|
.history/src/one_c/one_c_service_20250630135358.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.one_c.one_c_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class OneCService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
services/openvpn/openvpn_prompts.py → .history/src/openvpn/openvpn_prompts_20250627163312.py
RENAMED
File without changes
|
.history/src/openvpn/openvpn_prompts_20250630135358.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from src.system_prompt_template import SYSTEM_PROMPT_TEMPLATE
|
2 |
+
|
3 |
+
service_name = "open vpn"
|
4 |
+
|
5 |
+
service_description = ""
|
6 |
+
|
7 |
+
cases = """
|
8 |
+
У пользователя может быть одна из 3 ситуаций, надо определить, какая у него:
|
9 |
+
|
10 |
+
- Консультация.
|
11 |
+
Это когда:
|
12 |
+
- Программа есть
|
13 |
+
- Программа открывается
|
14 |
+
- vpn подключается
|
15 |
+
В этом случае надо предпринять действия:
|
16 |
+
- Проконсультировать пользователя, как пользоваться vpn.
|
17 |
+
и ответить на его вопросы.
|
18 |
+
- Если пользователь подтвердил, что все работает, вызови функцию closing_task, поблагодари за обращение.
|
19 |
+
в техническую поддержку
|
20 |
+
- Если ты не можешь помочь пользователю или он задает вопросы, на которые у тебя нет инструкций,
|
21 |
+
вызови функцию make_jira_task, сообщи, что была создана задача в jira и в ближайшее времся с
|
22 |
+
пользователем свяжется сотрудник технической поддержки.
|
23 |
+
|
24 |
+
- Установка
|
25 |
+
Это когда:
|
26 |
+
- Программы нет на компьютере пользователя.
|
27 |
+
В этом случае надо предпринять действия:
|
28 |
+
- Вызывать функцию install_program
|
29 |
+
- Если пользователь подтвердил, что все работает, вызови функцию closing_task, поблагодари за обращение
|
30 |
+
в техническую поддержку
|
31 |
+
- Если пользователь говорит, что vpn по-прежнему не работает, вызови функцию script_part
|
32 |
+
|
33 |
+
- Переустановка
|
34 |
+
Это когда:
|
35 |
+
- Программы не открывается.
|
36 |
+
В этом случае надо предпринять действия:
|
37 |
+
- Вызывать функцию delete_and_install_program
|
38 |
+
- Если пользователь подтвердил, что все работает, вызови функцию closing_task, поблагодари за обращение
|
39 |
+
в техническую поддержку
|
40 |
+
- Если пользователь говорит, что vpn по-прежнему не работает, вызови функцию script_part
|
41 |
+
"""
|
42 |
+
|
43 |
+
|
44 |
+
broken_cases = """
|
45 |
+
'В таком случае вам необходимо установить программу OpenVPN. Для этого выполните следующие действия:
|
46 |
+
|
47 |
+
Перейдите на официальный сайт OpenVPN (https://openvpn.net/) и скачайте последнюю версию программы.
|
48 |
+
Запустите установочный файл и следуйте инструкциям мастера установки.
|
49 |
+
После завершения установки запустите программу и попробуйте подключить VPN.
|
50 |
+
Если возникнут трудности при установке или использовании программы, пожалуйста, дайте мне знать.' - этот ответ нарушает правила 8 и 9
|
51 |
+
|
52 |
+
'Тогда, возможно, проблема связана с настройками самого приложения или сервера.
|
53 |
+
В этом случае я рекомендую обратиться к вашему системному администратору или в службу поддержки вашего VPN-провайдера.
|
54 |
+
Они смогут провести более детальную диагностику и устранить проблему.' - этот ответ нарушает правила 8 и 12
|
55 |
+
"""
|
56 |
+
|
57 |
+
|
58 |
+
knowledge = """
|
59 |
+
Инструкции:
|
60 |
+
Как пользоваться openvpn:
|
61 |
+
Документ представляет собой инструкцию по подключению к корпоративной сети через VPN,
|
62 |
+
используя приложение OpenVPN Connect. Он предназначен для сотрудников компании,
|
63 |
+
которым необходимо получить удалённый доступ к внутренним ресурсам из внешней сети
|
64 |
+
(например, при работе из дома или вне офиса).
|
65 |
+
|
66 |
+
Ключевые шаги подключения к VPN:
|
67 |
+
|
68 |
+
Подключение к внешней сети:
|
69 |
+
Убедитесь, что устройство подключено к любой внешней сети: через Ethernet, Wi-Fi или мобильную точку доступа.
|
70 |
+
Статус подключения можно проверить через иконку интернета в нижнем правом углу экрана.
|
71 |
+
Запуск OpenVPN Connect:
|
72 |
+
Найдите и запустите приложение OpenVPN Connect.
|
73 |
+
Оно может находиться на рабочем столе или его можно найти через строку поиска Windows, введя «OpenVPN Connect».
|
74 |
+
Активация VPN-соединения:
|
75 |
+
Переключите ползунок подключения в приложении, чтобы инициировать подключение к VPN.
|
76 |
+
В появившемся окне введите свои учётные данные (логин и пароль).
|
77 |
+
Нажмите кнопку «ОК» для подтверждения.
|
78 |
+
Готово:
|
79 |
+
После авторизации будет установлен удалённый доступ к сетевым ресурсам компании.
|
80 |
+
Цель документа:
|
81 |
+
Обеспечить безопасный доступ сотрудников к корпоративным системам из внешней интернет-сети посредством OpenVPN.
|
82 |
+
|
83 |
+
Требования:
|
84 |
+
Установленное приложение OpenVPN Connect.
|
85 |
+
Доступ к внешнему интернету.
|
86 |
+
Действующие учётные данные сотрудника.
|
87 |
+
|
88 |
+
Как проверить, установлена ли программа open vpn:
|
89 |
+
В поиске набрать OpenVPN. Если результаты поиска отсутствуют, то программа не установлена.
|
90 |
+
|
91 |
+
|
92 |
+
Как проверить, подключился ли open vpn:
|
93 |
+
Успех подключения выглядит как на скриншоте, должна появиться надпись CONNECTED.
|
94 |
+
|
95 |
+
Как правильно вводить учетные данные:
|
96 |
+
Логин вводится в формате Имя.Фамилия на латинице с заглавных букв. Пример: Ivan.Ivanov. Пароль вводится такой же, как при входе в пк.
|
97 |
+
"""
|
98 |
+
|
99 |
+
SYSTEM_PROMPT = SYSTEM_PROMPT_TEMPLATE.format(service_name=service_name,
|
100 |
+
service_description=service_description,
|
101 |
+
cases=cases,
|
102 |
+
broken_cases=broken_cases,
|
103 |
+
knowledge=knowledge)
|
services/openvpn/openvpn_service.py → .history/src/openvpn/openvpn_service_20250627125242.py
RENAMED
File without changes
|
.history/src/openvpn/openvpn_service_20250630135358.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.openvpn.openvpn_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class OpenVPNService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.script_part,
|
20 |
+
self.install_program,
|
21 |
+
self.delete_and_install_program
|
22 |
+
]
|
23 |
+
|
24 |
+
@tool
|
25 |
+
def script_part(question: str) -> None:
|
26 |
+
"""Запускаем большой скрипт для проверки возможных проблем с приложением
|
27 |
+
и их устранением"""
|
28 |
+
print("ran_graph(question)")
|
29 |
+
time.sleep(4)
|
30 |
+
logger.info("script part")
|
31 |
+
|
32 |
+
@tool
|
33 |
+
def install_program(question: str) -> None:
|
34 |
+
"""Запускаем скрипты, которые ставят программу openvpn"""
|
35 |
+
print("### Скриптом устанавливаем программу")
|
36 |
+
time.sleep(3)
|
37 |
+
logger.info("installing program")
|
38 |
+
|
39 |
+
@tool
|
40 |
+
def delete_and_install_program(question: str) -> None:
|
41 |
+
"""Запускаем скрипты, которые удаляют и ставят программу openvpn"""
|
42 |
+
print("### Скриптом удаляем и ставим программу")
|
43 |
+
time.sleep(3)
|
44 |
+
logger.info("deleting and installing program")
|
services/print_and_scan/print_and_scan_service.py → .history/src/print_and_scan/print_and_scan_service_20250630021904.py
RENAMED
File without changes
|
.history/src/print_and_scan/print_and_scan_service_20250630135358.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.print_and_scan.print_and_scan_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class PrintAndScanService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
services/service_by_name.py → .history/src/service_by_name_20250630022251.py
RENAMED
File without changes
|
.history/src/service_by_name_20250630135358.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
def get_service_by_name(service_name):
|
3 |
+
if service_name == "openvpn":
|
4 |
+
from src.openvpn.openvpn_service import OpenVPNService
|
5 |
+
return OpenVPNService()
|
6 |
+
if service_name == "corporate_email":
|
7 |
+
from src.corporate_email.corporate_email_service import CorporateEmailService
|
8 |
+
return CorporateEmailService()
|
9 |
+
if service_name == "internet_access":
|
10 |
+
from src.internet_access.internet_access_service import InternetAccessService
|
11 |
+
return InternetAccessService()
|
12 |
+
if service_name == "b2b":
|
13 |
+
from src.b2b.b2b_service import B2BService
|
14 |
+
return B2BService()
|
15 |
+
if service_name == "crm":
|
16 |
+
from src.crm.crm_service import CRMService
|
17 |
+
return CRMService()
|
18 |
+
if service_name == "one_c":
|
19 |
+
from src.one_c.one_c_service import OneCService
|
20 |
+
return OneCService()
|
21 |
+
if service_name == "borlas":
|
22 |
+
from src.borlas.borlas_service import BorlasService
|
23 |
+
return BorlasService()
|
24 |
+
if service_name == "print_and_scan":
|
25 |
+
from src.print_and_scan.print_and_scan_service import PrintAndScanService
|
26 |
+
return PrintAndScanService()
|
27 |
+
|
28 |
+
|
29 |
+
raise ValueError(f"Некорректное имя сервиса '{service_name}'")
|
services/streamlit_app.py → .history/src/streamlit_app_20250630025024.py
RENAMED
File without changes
|
.history/src/streamlit_app_20250630135358.py
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from langchain_core.messages import HumanMessage, AIMessage
|
3 |
+
from src.get_classification import get_graph_class
|
4 |
+
from datetime import datetime
|
5 |
+
|
6 |
+
|
7 |
+
def message_to_dict(messages):
|
8 |
+
result = []
|
9 |
+
for message in messages:
|
10 |
+
if isinstance(message, HumanMessage) or isinstance(message, AIMessage):
|
11 |
+
print("message:", message.content)
|
12 |
+
if message.content == "" or message.content is None:
|
13 |
+
continue
|
14 |
+
if isinstance(message, HumanMessage):
|
15 |
+
result.append({"role": "user", "content": message.content})
|
16 |
+
elif isinstance(message, AIMessage):
|
17 |
+
result.append({"role": "assistant", "content": message.content})
|
18 |
+
print("-" * 100)
|
19 |
+
return result
|
20 |
+
|
21 |
+
|
22 |
+
def find_last_bot_message(messages):
|
23 |
+
"""Находит последнее сообщение бота"""
|
24 |
+
for message in messages[::-1]:
|
25 |
+
if isinstance(message, AIMessage) and len(message.content) > 0:
|
26 |
+
return message.content
|
27 |
+
return None
|
28 |
+
|
29 |
+
|
30 |
+
def display_chat_messages():
|
31 |
+
"""Отображает историю сообщений в чате"""
|
32 |
+
for message in st.session_state.messages:
|
33 |
+
with st.chat_message(message["role"]):
|
34 |
+
st.markdown(message["content"])
|
35 |
+
|
36 |
+
|
37 |
+
def save_broken_case():
|
38 |
+
messages_dict = st.session_state.messages
|
39 |
+
result_str = ""
|
40 |
+
for elem in messages_dict:
|
41 |
+
role = elem["role"]
|
42 |
+
content = elem["content"]
|
43 |
+
result_str += f"{role}: {content}\n"
|
44 |
+
|
45 |
+
current_datetime = datetime.now()
|
46 |
+
formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S")
|
47 |
+
|
48 |
+
with open("/Users/admin/my_documents/retrieval_part/services/broken_cases.txt", "a") as file:
|
49 |
+
file.write(f"{formatted_datetime}\n" + result_str + "\n" + "-" * 50 + "\n\n")
|
50 |
+
|
51 |
+
|
52 |
+
def handle_user_input():
|
53 |
+
"""Обрабатывает ввод пользователя и генерирует ответ бота"""
|
54 |
+
if prompt := st.chat_input("Введите ваш вопрос"):
|
55 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
56 |
+
if prompt.lower().startswith("log"):
|
57 |
+
save_broken_case()
|
58 |
+
st.session_state.messages = []
|
59 |
+
display_chat_messages()
|
60 |
+
else:
|
61 |
+
with st.chat_message("user"):
|
62 |
+
st.markdown(prompt)
|
63 |
+
|
64 |
+
if "bot" not in st.session_state:
|
65 |
+
st.session_state.bot = get_graph_class(prompt)
|
66 |
+
st.session_state.bot.invoke(prompt)
|
67 |
+
|
68 |
+
# Извлекаем последнее сообщение бота
|
69 |
+
last_bot_message = find_last_bot_message(st.session_state.bot.messages)
|
70 |
+
st.session_state.messages.append(
|
71 |
+
{"role": "assistant", "content": last_bot_message}
|
72 |
+
)
|
73 |
+
with st.chat_message("assistant"):
|
74 |
+
st.markdown(last_bot_message)
|
75 |
+
|
76 |
+
|
77 |
+
def clear_chat():
|
78 |
+
"""Очищает чат и пересоздает бота"""
|
79 |
+
st.session_state.messages = []
|
80 |
+
del st.session_state.bot
|
81 |
+
|
82 |
+
|
83 |
+
def main():
|
84 |
+
"""Основная функция приложения"""
|
85 |
+
# Заголовок приложения
|
86 |
+
st.title("Чат-бот технической поддержки OpenVPN")
|
87 |
+
|
88 |
+
# Кнопка очистки чата
|
89 |
+
if st.button("Clear"):
|
90 |
+
clear_chat()
|
91 |
+
|
92 |
+
if "messages" not in st.session_state:
|
93 |
+
st.session_state.messages = []
|
94 |
+
|
95 |
+
# Отображение чата и обработка ввода
|
96 |
+
display_chat_messages()
|
97 |
+
handle_user_input()
|
98 |
+
|
99 |
+
|
100 |
+
if __name__ == "__main__":
|
101 |
+
main()
|
services/template/_service.py → .history/src/template/_service_20250627160824.py
RENAMED
File without changes
|
.history/src/template/_service_20250630135358.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.template._prompts import SYSTEM_PROMPT ## FIX
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class ExamplePLSRenameService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
{services → src}/.DS_Store
RENAMED
File without changes
|
{services → src}/__init__.py
RENAMED
File without changes
|
{services → src}/b2b/__init__.py
RENAMED
File without changes
|
{services → src}/b2b/b2b_prompts.py
RENAMED
File without changes
|
src/b2b/b2b_service.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.b2b.b2b_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class B2BService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
src/base_graph.py
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Annotated, Type
|
2 |
+
from langgraph.graph import StateGraph
|
3 |
+
from langchain_core.messages import HumanMessage, ToolMessage
|
4 |
+
from langgraph.graph.message import add_messages
|
5 |
+
from typing_extensions import TypedDict
|
6 |
+
from src.base_service import BaseService
|
7 |
+
from src.get_answer_gigachat import AnswerGigaChat
|
8 |
+
import logging
|
9 |
+
import os
|
10 |
+
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
+
class State(TypedDict):
|
14 |
+
messages: Annotated[list, add_messages]
|
15 |
+
|
16 |
+
class BaseGraph:
|
17 |
+
def __init__(self, service: Type[BaseService]):
|
18 |
+
self.service = service
|
19 |
+
# self._initialize_logging()
|
20 |
+
self.tools_dict = {tool.name: tool for tool in service.tools}
|
21 |
+
self.llm_with_tools = AnswerGigaChat().bind_tools(service.tools)
|
22 |
+
self.messages = service.get_initial_messages()
|
23 |
+
self.graph = self._build_graph()
|
24 |
+
logger.info(f"BaseGraph with service {service} was built")
|
25 |
+
|
26 |
+
def rebuild_with_new_service(self, service: Type[BaseService]):
|
27 |
+
self.service = service
|
28 |
+
self.tools_dict = {tool.name: tool for tool in service.tools}
|
29 |
+
self.llm_with_tools = AnswerGigaChat().bind_tools(service.tools)
|
30 |
+
self.graph = self._build_graph()
|
31 |
+
self.messages = service.get_messages_from_redirect(self.messages)
|
32 |
+
logger.info(f"BaseGraph was rebuilt with service {service}")
|
33 |
+
|
34 |
+
def _initialize_logging(self):
|
35 |
+
if os.path.exists(self.service.log_path):
|
36 |
+
os.remove(self.service.log_path)
|
37 |
+
logging.basicConfig(
|
38 |
+
level=logging.INFO,
|
39 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
40 |
+
handlers=[logging.FileHandler(self.service.log_path)]
|
41 |
+
)
|
42 |
+
|
43 |
+
def _agent_node(self, state: State):
|
44 |
+
try:
|
45 |
+
logger.info("Starting agent_node")
|
46 |
+
messages = state["messages"]
|
47 |
+
response = self.llm_with_tools.invoke(messages)
|
48 |
+
response.content = self._clean_response(response.content)
|
49 |
+
return {"messages": [response]}
|
50 |
+
except Exception as e:
|
51 |
+
logger.error(f"Error in agent_node: {str(e)}", exc_info=True)
|
52 |
+
raise
|
53 |
+
|
54 |
+
def _tool_node(self, state: State):
|
55 |
+
try:
|
56 |
+
logger.info("Starting tool_node")
|
57 |
+
last_message = state["messages"][-1]
|
58 |
+
tool_calls = last_message.tool_calls
|
59 |
+
|
60 |
+
results = []
|
61 |
+
for call in tool_calls:
|
62 |
+
tool_name = call["name"]
|
63 |
+
logger.info(f"Running tool {tool_name}")
|
64 |
+
args = call["args"]
|
65 |
+
|
66 |
+
tool = self.tools_dict.get(tool_name)
|
67 |
+
if not tool:
|
68 |
+
raise ValueError(f"Tool {tool_name} not found")
|
69 |
+
|
70 |
+
tool_response = tool.invoke(args)
|
71 |
+
if tool_name == "make_redirect":
|
72 |
+
self.rebuild_with_new_service(tool_response)
|
73 |
+
results.append(str(tool_response))
|
74 |
+
|
75 |
+
return {"messages": [ToolMessage(content=", ".join(results), tool_call_id=call["id"])]}
|
76 |
+
except Exception as e:
|
77 |
+
logger.error(f"Error in tool_node: {str(e)}", exc_info=True)
|
78 |
+
raise
|
79 |
+
|
80 |
+
def _should_continue(self, state: State):
|
81 |
+
try:
|
82 |
+
logger.info("Checking should continue")
|
83 |
+
last_message = state["messages"][-1]
|
84 |
+
return "tool" if "function_call" in last_message.additional_kwargs else "end"
|
85 |
+
except Exception as e:
|
86 |
+
logger.error(f"Error in should_continue: {str(e)}", exc_info=True)
|
87 |
+
raise
|
88 |
+
|
89 |
+
def _build_graph(self):
|
90 |
+
try:
|
91 |
+
logger.info("Building graph")
|
92 |
+
graph_builder = StateGraph(State)
|
93 |
+
|
94 |
+
graph_builder.add_node("agent", self._agent_node)
|
95 |
+
graph_builder.add_node("tool", self._tool_node)
|
96 |
+
|
97 |
+
graph_builder.add_conditional_edges(
|
98 |
+
"agent",
|
99 |
+
self._should_continue,
|
100 |
+
{"tool": "tool", "end": "__end__"}
|
101 |
+
)
|
102 |
+
graph_builder.add_edge("tool", "agent")
|
103 |
+
graph_builder.set_entry_point("agent")
|
104 |
+
|
105 |
+
return graph_builder.compile()
|
106 |
+
except Exception as e:
|
107 |
+
logger.error(f"Error building graph: {str(e)}", exc_info=True)
|
108 |
+
raise
|
109 |
+
|
110 |
+
def _clean_response(self, content: str) -> str:
|
111 |
+
content = content.replace("</final_answer>", "<final_answer>").replace("</thinking>", "<thinking>")
|
112 |
+
if "<final_answer>" in content:
|
113 |
+
content = content.split("<final_answer>")[1]
|
114 |
+
if "<thinking>" in content:
|
115 |
+
content = content.split("<thinking>")[-1]
|
116 |
+
return content
|
117 |
+
|
118 |
+
def invoke(self, user_input):
|
119 |
+
try:
|
120 |
+
self.messages.append(HumanMessage(content=user_input))
|
121 |
+
result = self.graph.invoke({"messages": self.messages})
|
122 |
+
self.messages = result["messages"]
|
123 |
+
return result
|
124 |
+
except Exception as e:
|
125 |
+
logger.error(f"Error invoking graph: {str(e)}", exc_info=True)
|
126 |
+
raise
|
src/base_service.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import ABC, abstractmethod
|
2 |
+
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
|
3 |
+
from system_prompt_template import DIALOG_PROMPT
|
4 |
+
from typing import List
|
5 |
+
from langchain_core.tools import BaseTool, tool
|
6 |
+
from src.service_by_name import get_service_by_name
|
7 |
+
import logging
|
8 |
+
import time
|
9 |
+
import os
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
|
12 |
+
load_dotenv()
|
13 |
+
logger = logging.getLogger(__name__)
|
14 |
+
|
15 |
+
class BaseService(ABC):
|
16 |
+
def __init__(self):
|
17 |
+
self._base_tools = [self.closing_task,
|
18 |
+
self.make_jira_task,
|
19 |
+
]
|
20 |
+
|
21 |
+
@property
|
22 |
+
@abstractmethod
|
23 |
+
def system_prompt(self) -> str:
|
24 |
+
"""Возвращает системный промпт для сервиса"""
|
25 |
+
pass
|
26 |
+
|
27 |
+
@property
|
28 |
+
@abstractmethod
|
29 |
+
def tools(self) -> List[BaseTool]:
|
30 |
+
"""Возвращает список инструментов сервиса"""
|
31 |
+
pass
|
32 |
+
|
33 |
+
@property
|
34 |
+
def log_path(self) -> str:
|
35 |
+
"""Возвращает путь до папки с логами"""
|
36 |
+
return os.getenv("LOG_SERVICES_PATH")
|
37 |
+
|
38 |
+
def get_initial_messages(self):
|
39 |
+
"""Возвращает начальные сообщения для графа"""
|
40 |
+
return [SystemMessage(content=self.system_prompt)]
|
41 |
+
|
42 |
+
def get_messages_from_redirect(self, messages):
|
43 |
+
messages_formatted = ""
|
44 |
+
for message in messages:
|
45 |
+
if isinstance(message, HumanMessage):
|
46 |
+
message += f"user: '{message.content}'\n"
|
47 |
+
elif isinstance(message, AIMessage):
|
48 |
+
message += f"assistant: '{message.content}'\n"
|
49 |
+
|
50 |
+
return [SystemMessage(content=self.system_prompt + DIALOG_PROMPT.format(messages_formatted))]
|
51 |
+
|
52 |
+
@tool
|
53 |
+
def make_redirect(service_name: str):
|
54 |
+
"""Когда оказывается, что пользователю нужен эксперт в другом сервисе,
|
55 |
+
пользователя нужно перенаправить на соответствующего эксперта.
|
56 |
+
Функция принимает аргумент service_name из списка:
|
57 |
+
['openvpn', 'corporate_email', 'internet_access'] и пересоздает класс эксперта
|
58 |
+
с соответствующими новому сервису знаниями."""
|
59 |
+
logger.info("redirecting question to service {service_name}")
|
60 |
+
return get_service_by_name(service_name)
|
61 |
+
|
62 |
+
|
63 |
+
@tool
|
64 |
+
def closing_task(question: str) -> None:
|
65 |
+
"""Задача выполнена, сохраняем результаты"""
|
66 |
+
print("### Задача выполнена, сохраняем результаты")
|
67 |
+
time.sleep(2)
|
68 |
+
logger.info("task closed")
|
69 |
+
|
70 |
+
@tool
|
71 |
+
def make_jira_task(question: str) -> None:
|
72 |
+
"""Задача не может быть выполнена с помощью ии, создаем задачу в jira для того,
|
73 |
+
чтобы её выполнил человек"""
|
74 |
+
time.sleep(2)
|
75 |
+
print("### Скриптом создается задача в jira с соответствующими деталями")
|
76 |
+
logger.info("jira task was make")
|
{services → src}/borlas/__init__.py
RENAMED
File without changes
|
{services → src}/borlas/borlas_prompts.py
RENAMED
File without changes
|
src/borlas/borlas_service.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.borlas.borlas_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class BorlasService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
{services → src}/broken_cases.txt
RENAMED
File without changes
|
{services → src}/corporate_email/__init__.py
RENAMED
File without changes
|
{services → src}/corporate_email/corporate_email_prompts.py
RENAMED
File without changes
|
src/corporate_email/corporate_email_service.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.corporate_email.corporate_email_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class CorporateEmailService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + []
|
19 |
+
|
20 |
+
@tool
|
21 |
+
def check_lock_in_active_dir_and_password(question: str) -> None:
|
22 |
+
"""Проверка блокировки учетной записи сотрудника в системе и разблокировка и проверка на просроченный пароль и обновление пароля. Если эти проблемы вскрываются, то функция их исправляет."""
|
23 |
+
time.sleep(4)
|
24 |
+
logger.info("check_lock_in_active_dir_and_password")
|
{services → src}/crm/__init__.py
RENAMED
File without changes
|
{services → src}/crm/crm_prompts.py
RENAMED
File without changes
|
src/crm/crm_service.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_core.tools import BaseTool, tool
|
2 |
+
from src.base_service import BaseService
|
3 |
+
from src.crm.crm_prompts import SYSTEM_PROMPT
|
4 |
+
from typing import List
|
5 |
+
import logging
|
6 |
+
import time
|
7 |
+
|
8 |
+
logger = logging.getLogger(__name__)
|
9 |
+
|
10 |
+
|
11 |
+
class CRMService(BaseService):
|
12 |
+
@property
|
13 |
+
def system_prompt(self) -> str:
|
14 |
+
return SYSTEM_PROMPT
|
15 |
+
|
16 |
+
@property
|
17 |
+
def tools(self) -> List[BaseTool]:
|
18 |
+
return self._base_tools + [
|
19 |
+
self.example_func,
|
20 |
+
]
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def example_func(question: str) -> None:
|
24 |
+
"""Пример функции, как её здесь оформлять. Просто для примера, нужно удалить при реальном использовании"""
|
25 |
+
print("Я example_func")
|
26 |
+
time.sleep(4)
|
27 |
+
logger.info("Кто-то забыл удалить example_func!")
|
28 |
+
|
{services → src}/get_answer_gigachat.py
RENAMED
File without changes
|
{services → src}/get_classification.py
RENAMED
@@ -3,9 +3,9 @@ from typing import Literal
|
|
3 |
import logging
|
4 |
from langchain.prompts import ChatPromptTemplate
|
5 |
from langchain_core.output_parsers import JsonOutputParser
|
6 |
-
from
|
7 |
-
from
|
8 |
-
from
|
9 |
import os
|
10 |
from dotenv import load_dotenv
|
11 |
|
|
|
3 |
import logging
|
4 |
from langchain.prompts import ChatPromptTemplate
|
5 |
from langchain_core.output_parsers import JsonOutputParser
|
6 |
+
from src.get_answer_gigachat import AnswerGigaChat
|
7 |
+
from src.service_by_name import get_service_by_name
|
8 |
+
from src.base_graph import BaseGraph
|
9 |
import os
|
10 |
from dotenv import load_dotenv
|
11 |
|
{services → src}/internet_access/__init__.py
RENAMED
File without changes
|
{services → src}/internet_access/internet_access_cases.json
RENAMED
File without changes
|