union-app-chat-stream 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +16 -0
- package/PROJECT_OVERVIEW.md +187 -0
- package/app/.env +63 -0
- package/app/.env.dev +63 -0
- package/app/.env.prod.bj11 +63 -0
- package/app/.env.prod.sh20 +63 -0
- package/app/.env.prod.sz31 +63 -0
- package/app/.env.test.bj12 +63 -0
- package/app/__init__.py +42 -0
- package/app/__pycache__/__init__.cpython-312.pyc +0 -0
- package/app/__pycache__/authenticated_user.cpython-312.pyc +0 -0
- package/app/__pycache__/extensions.cpython-312.pyc +0 -0
- package/app/__pycache__/wsgi.cpython-312.pyc +0 -0
- package/app/authenticated_user.py +77 -0
- package/app/config/__pycache__/config_loader.cpython-312.pyc +0 -0
- package/app/config/__pycache__/env_config.cpython-312.pyc +0 -0
- package/app/config/__pycache__/logger_config.cpython-312.pyc +0 -0
- package/app/config/env_config.py +96 -0
- package/app/config/logger_config.py +46 -0
- package/app/manager/__init__.py +4 -0
- package/app/manager/__pycache__/__init__.cpython-312.pyc +0 -0
- package/app/manager/__pycache__/chatstream_manager.cpython-312.pyc +0 -0
- package/app/manager/__pycache__/prompts.cpython-312.pyc +0 -0
- package/app/manager/__pycache__/runtime_manager.cpython-312.pyc +0 -0
- package/app/manager/__pycache__/toolcall_manager.cpython-312.pyc +0 -0
- package/app/manager/chatstream_manager.py +90 -0
- package/app/manager/prompts.py +62 -0
- package/app/manager/runtime_manager.py +552 -0
- package/app/models/__pycache__/schemas.cpython-312.pyc +0 -0
- package/app/models/schemas.py +30 -0
- package/app/service/__init__.py +4 -0
- package/app/service/__pycache__/__init__.cpython-312.pyc +0 -0
- package/app/service/__pycache__/chat_service.cpython-312.pyc +0 -0
- package/app/service/__pycache__/llm_service.cpython-312.pyc +0 -0
- package/app/service/__pycache__/rag_service.cpython-312.pyc +0 -0
- package/app/service/__pycache__/tool_call_service.cpython-312.pyc +0 -0
- package/app/service/__pycache__/union_service.cpython-312.pyc +0 -0
- package/app/service/chat_service.py +228 -0
- package/app/service/llm_service.py +214 -0
- package/app/service/rag_service.py +866 -0
- package/app/service/union_service.py +201 -0
- package/app/utils/__init__.py +5 -0
- package/app/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/app/utils/__pycache__/common_utils.cpython-312.pyc +0 -0
- package/app/utils/__pycache__/debug_context.cpython-312.pyc +0 -0
- package/app/utils/__pycache__/function_utils.cpython-312.pyc +0 -0
- package/app/utils/__pycache__/jwt_utils.cpython-312.pyc +0 -0
- package/app/utils/common_utils.py +169 -0
- package/app/utils/debug_context.py +16 -0
- package/app/utils/function_utils.py +274 -0
- package/app/utils/jwt_utils.py +39 -0
- package/app/views/__init__.py +6 -0
- package/app/views/__pycache__/__init__.cpython-312.pyc +0 -0
- package/app/views/__pycache__/view_chatstream.cpython-312.pyc +0 -0
- package/app/views/__pycache__/view_healthcheck.cpython-312.pyc +0 -0
- package/app/views/__pycache__/view_runtime.cpython-312.pyc +0 -0
- package/app/views/view_chatstream.py +53 -0
- package/app/views/view_healthcheck.py +14 -0
- package/app/views/view_runtime.py +72 -0
- package/app/wsgi.py +37 -0
- package/ci.yml +14 -0
- package/deploy/autoconf/templates/env.j2 +25 -0
- package/deploy/autoconf.yml +15 -0
- package/deploy/scripts/healthcheck.sh +0 -0
- package/deploy/scripts/requirements.txt +53 -0
- package/deploy/scripts/start.sh +75 -0
- package/deploy/scripts/stop.sh +31 -0
- package/knowledge/.gitkeep +0 -0
- package/knowledge/000001-biz-offline-85b99bd43b-v1.md +88 -0
- package/knowledge/000002-biz-offline-717e8d823e-v1.md +90 -0
- package/knowledge/000003-biz-offline-c963227cc8-v1.md +84 -0
- package/knowledge/000004-biz-offline-2a5868e7da-v1.md +92 -0
- package/knowledge/000005-biz-offline-f9d9cf1a88-v1.md +79 -0
- package/knowledge/000006-biz-offline-c4fa2df3bd-v1.md +77 -0
- package/knowledge/000007-biz-offline-78304b70ca-v1.md +76 -0
- package/knowledge/000008-biz-offline-987ae67b35-v1.md +75 -0
- package/knowledge/000009-biz-offline-4d656bcea3-v1.md +85 -0
- package/knowledge/000010-sop-offline-a9e1050719-v1.md +100 -0
- package/knowledge/000011-biz-offline-5de0624891-v1.md +86 -0
- package/knowledge/000012-biz-offline-7dfacccba3-v1.md +82 -0
- package/knowledge/000013-biz-offline-5e1d29d2ed-v1.md +81 -0
- package/knowledge/000014-biz-offline-1d0ed8b841-v1.md +68 -0
- package/knowledge/000015-biz-offline-8a1376ee3e-v1.md +78 -0
- package/knowledge/000016-biz-offline-c8bfc2aa08-v1.md +99 -0
- package/knowledge/000017-biz-offline-9dffb28032-v1.md +88 -0
- package/knowledge/000018-biz-offline-f935bc9a6a-v1.md +80 -0
- package/knowledge/000019-biz-offline-858b3ecd89-v1.md +86 -0
- package/knowledge/000020-biz-offline-65cb5c4f40-v1.md +113 -0
- package/knowledge/000021-biz-offline-1bf211639c-v1.md +148 -0
- package/knowledge/000022-biz-offline-8c5a637879-v1.md +140 -0
- package/knowledge/000023-biz-offline-fe872b8712-v1.md +188 -0
- package/knowledge/000024-biz-offline-a85010c500-v1.md +133 -0
- package/knowledge/000025-biz-offline-8af58a3638-v1.md +136 -0
- package/knowledge/000026-biz-offline-6754102e93-v1.md +142 -0
- package/knowledge/000027-biz-offline-ea2e5ca5f9-v1.md +150 -0
- package/knowledge/000028-scenario-offline-dab45cebb4-v1.md +136 -0
- package/knowledge/000029-scenario-offline-5b8ae5ea9f-v1.md +143 -0
- package/knowledge/000030-scenario-offline-9a82d42f3f-v1.md +136 -0
- package/knowledge/000031-scenario-offline-cc2edc0197-v1.md +122 -0
- package/knowledge/000032-scenario-offline-e5f6e5cbfa-v1.md +122 -0
- package/knowledge/000033-scenario-offline-e1955849aa-v1.md +135 -0
- package/knowledge/000034-scenario-offline-3a13d49a3a-v1.md +138 -0
- package/knowledge/000035-scenario-offline-fd5560211f-v1.md +147 -0
- package/knowledge/000036-scenario-offline-function-call-mock-v1.md +134 -0
- package/package.json +18 -0
- package/requirements.txt +53 -0
- package/tools/prompts.yaml +10 -0
- package/tools/tool_definitions.yaml +303 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from flask import Flask, request, jsonify, g, has_request_context
|
|
2
|
+
from werkzeug.exceptions import HTTPException
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from app.utils import common_utils
|
|
5
|
+
from app.config.logger_config import setup_logger
|
|
6
|
+
from contextvars import ContextVar
|
|
7
|
+
|
|
8
|
+
_current_ip: ContextVar[str] = ContextVar("current_ip", default="unknown_ip")
|
|
9
|
+
|
|
10
|
+
logger = setup_logger()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FlaskSessionClient:
|
|
14
|
+
def __init__(self, app):
|
|
15
|
+
self.auth_user_server = app.config['GET_USE_INFO_URL']
|
|
16
|
+
self.permissions = app.config['PERMISSIONS']
|
|
17
|
+
self.init_app(app)
|
|
18
|
+
|
|
19
|
+
def init_app(self, app: Flask):
|
|
20
|
+
def patcher(record):
|
|
21
|
+
record["extra"]["ip"] = _current_ip.get()
|
|
22
|
+
|
|
23
|
+
logger.configure(patcher=patcher)
|
|
24
|
+
|
|
25
|
+
@app.before_request
|
|
26
|
+
def require_auth():
|
|
27
|
+
try:
|
|
28
|
+
if has_request_context():
|
|
29
|
+
ip = common_utils.get_client_ip()
|
|
30
|
+
except LookupError:
|
|
31
|
+
ip = "unknown_context-lost"
|
|
32
|
+
_current_ip.set(ip)
|
|
33
|
+
|
|
34
|
+
if request.method == 'OPTIONS':
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
public_paths = [
|
|
38
|
+
'/healthcheck.html',
|
|
39
|
+
'/favicon.ico'
|
|
40
|
+
]
|
|
41
|
+
if any(request.path == path or request.path.startswith(path) for path in public_paths):
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
g.current_user = {'jsessionid': 'test-jsessionid'}
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
jsessionid = request.headers.get('Cookie')
|
|
48
|
+
|
|
49
|
+
if not jsessionid:
|
|
50
|
+
logger.error("未登录")
|
|
51
|
+
return jsonify({"error": "auth error"}), 401
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
response = common_utils.call_https_api(url=self.auth_user_server,
|
|
55
|
+
headers={'Cookie': 'CASSESSIONID=' + jsessionid},
|
|
56
|
+
method='GET', verify_ssl=False)
|
|
57
|
+
if response.get('status_code') != 200 or not response.get('data'):
|
|
58
|
+
logger.error("验证失败")
|
|
59
|
+
return jsonify({"error": "auth error"}), 401
|
|
60
|
+
user = response.get('data')
|
|
61
|
+
if user['loginName'] and self.permissions and self.permissions not in user['permissions']:
|
|
62
|
+
return jsonify({"error": "auth error"}), 401
|
|
63
|
+
g.current_user = response.get('data')
|
|
64
|
+
g.current_user['jsessionid'] = jsessionid
|
|
65
|
+
|
|
66
|
+
return
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"auth 验证失败: {str(e)},路径: {request.path}")
|
|
69
|
+
return jsonify({"error": "auth error"}), 401
|
|
70
|
+
|
|
71
|
+
@app.errorhandler(Exception)
|
|
72
|
+
def handle_exception(e):
|
|
73
|
+
if isinstance(e, HTTPException):
|
|
74
|
+
return e
|
|
75
|
+
|
|
76
|
+
logger.error(f"未处理的异常: {str(e)}")
|
|
77
|
+
return jsonify({"error": "Internal Server Error"}), 500
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _env_bool(name, default=False):
|
|
5
|
+
value = os.getenv(name)
|
|
6
|
+
if value is None:
|
|
7
|
+
return default
|
|
8
|
+
return value.lower() in {"1", "true", "yes", "on"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _env_int(name, default):
|
|
12
|
+
value = os.getenv(name)
|
|
13
|
+
if value is None:
|
|
14
|
+
return default
|
|
15
|
+
try:
|
|
16
|
+
return int(value)
|
|
17
|
+
except ValueError:
|
|
18
|
+
return default
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _env_float(name, default):
|
|
22
|
+
value = os.getenv(name)
|
|
23
|
+
if value is None:
|
|
24
|
+
return default
|
|
25
|
+
try:
|
|
26
|
+
return float(value)
|
|
27
|
+
except ValueError:
|
|
28
|
+
return default
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _env_list(name, default=None):
|
|
32
|
+
value = os.getenv(name)
|
|
33
|
+
if value is None:
|
|
34
|
+
return default or []
|
|
35
|
+
return [item.strip() for item in value.split(",") if item.strip()]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Config:
|
|
39
|
+
SECRET_KEY = os.getenv('SECRET_KEY')
|
|
40
|
+
GET_USE_INFO_URL = os.getenv('GET_USE_INFO_URL')
|
|
41
|
+
GET_ORG_INFO_URL = os.getenv('GET_ORG_INFO_URL')
|
|
42
|
+
GET_JIRA_INFO_URL = os.getenv('GET_JIRA_INFO_URL')
|
|
43
|
+
GET_BIGDATA_URL = os.getenv('GET_BIGDATA_URL')
|
|
44
|
+
GET_UNION_BASE_URL = os.getenv('GET_UNION_BASE_URL')
|
|
45
|
+
GET_ORG_INFO_URL_TOKEN = os.getenv('GET_ORG_INFO_URL_TOKEN')
|
|
46
|
+
GET_JIRA_INFO_URL_TOKEN = os.getenv('GET_JIRA_INFO_URL_TOKEN')
|
|
47
|
+
PERMISSIONS = os.getenv('PERMISSIONS')
|
|
48
|
+
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
|
49
|
+
CONSOLE_STDOUT = os.getenv("CONSOLE_STDOUT", "FALSE")
|
|
50
|
+
LOG_DIR = os.getenv("LOG_DIR", "/data/appLogs")
|
|
51
|
+
FLASK_ENV = os.getenv("FLASK_ENV", "prod")
|
|
52
|
+
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", f"union-fall-back-secret-for-{FLASK_ENV}")
|
|
53
|
+
JWT_EXPIRATION_SECOND = os.getenv("JWT_EXPIRATION_SECOND", 900)
|
|
54
|
+
JWT_RENEW_SECOND = os.getenv("JWT_RENEW_SECOND", 120)
|
|
55
|
+
|
|
56
|
+
# 大模型地址
|
|
57
|
+
LLM_URL = os.getenv('LLM_URL')
|
|
58
|
+
LLM_KEY = os.getenv('LLM_KEY')
|
|
59
|
+
LLM_MODEL = os.getenv('LLM_MODEL') # 默认模型名称
|
|
60
|
+
LLM_MAX_TOKENS = _env_int("LLM_MAX_TOKENS", 4096)
|
|
61
|
+
LLM_TEMPERATURE = _env_float("LLM_TEMPERATURE", 0.7)
|
|
62
|
+
LLM_TOP_P = _env_float("LLM_TOP_P", 0.9)
|
|
63
|
+
|
|
64
|
+
SYSTEM_PROMPT = os.getenv("SYSTEM_PROMPT", "")
|
|
65
|
+
|
|
66
|
+
FILTER_ENABLED = _env_bool("FILTER_ENABLED", False)
|
|
67
|
+
FILTER_ALLOWED_KEYWORDS = _env_list("FILTER_ALLOWED_KEYWORDS")
|
|
68
|
+
FILTER_REJECTION_MESSAGE = os.getenv(
|
|
69
|
+
"FILTER_REJECTION_MESSAGE",
|
|
70
|
+
"抱歉,我是联合运维智能客服,只能回答与联合运维相关的问题。",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
TOOLS_MAX_ROUNDS = _env_int("TOOLS_MAX_ROUNDS", 5)
|
|
74
|
+
|
|
75
|
+
CONVERSATION_MAX_HISTORY = _env_int("CONVERSATION_MAX_HISTORY", 20)
|
|
76
|
+
CONVERSATION_TTL = _env_int("CONVERSATION_TTL", 3600)
|
|
77
|
+
|
|
78
|
+
RAG_ENABLED = _env_bool("RAG_ENABLED", True)
|
|
79
|
+
RAG_KNOWLEDGE_DIR = os.getenv("RAG_KNOWLEDGE_DIR", "knowledge")
|
|
80
|
+
RAG_PERSIST_DIR = os.getenv("RAG_PERSIST_DIR", ".chroma")
|
|
81
|
+
RAG_COLLECTION = os.getenv("RAG_COLLECTION", "ops_knowledge")
|
|
82
|
+
RAG_EMBEDDING_MODEL = os.getenv("RAG_EMBEDDING_MODEL", "embedding-3")
|
|
83
|
+
RAG_EMBEDDING_MAX_CHARS = _env_int("RAG_EMBEDDING_MAX_CHARS", 6000)
|
|
84
|
+
RAG_EMBEDDING_BATCH_SIZE = _env_int("RAG_EMBEDDING_BATCH_SIZE", 8)
|
|
85
|
+
RAG_TOP_K = _env_int("RAG_TOP_K", 5)
|
|
86
|
+
RAG_SEMANTIC_CANDIDATE_K = _env_int("RAG_SEMANTIC_CANDIDATE_K", 40)
|
|
87
|
+
RAG_CONTEXT_K = _env_int("RAG_CONTEXT_K", 8)
|
|
88
|
+
RAG_EXACT_CONTEXT_K = _env_int("RAG_EXACT_CONTEXT_K", 6)
|
|
89
|
+
RAG_EXACT_PER_FILE_CONTEXT_K = _env_int("RAG_EXACT_PER_FILE_CONTEXT_K", 1)
|
|
90
|
+
RAG_PER_FILE_CONTEXT_K = _env_int("RAG_PER_FILE_CONTEXT_K", 2)
|
|
91
|
+
RAG_CHUNK_SIZE = _env_int("RAG_CHUNK_SIZE", 1200)
|
|
92
|
+
RAG_REBUILD_ON_STARTUP = _env_bool("RAG_REBUILD_ON_STARTUP", False)
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def init_app(cls, app):
|
|
96
|
+
pass
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from loguru import logger
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def setup_logger():
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
env = os.getenv("FLASK_ENV", "prod")
|
|
9
|
+
log_level = os.getenv("LOG_LEVEL", "INFO")
|
|
10
|
+
console_stdout = os.getenv("CONSOLE_STDOUT", "FALSE")
|
|
11
|
+
log_dir = os.getenv("LOG_DIR", "/data/appLogs")
|
|
12
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
13
|
+
app_log_path = os.path.join(log_dir, "union-py-app.log")
|
|
14
|
+
error_log_path = os.path.join(log_dir, "union-py-error.log")
|
|
15
|
+
logger.remove()
|
|
16
|
+
|
|
17
|
+
logger.configure(extra={"ip": "unknown_ip"})
|
|
18
|
+
log_format = "{time:YYYY-MM-DD HH:mm:ss}|{level:8}|{extra[ip]}|{name}:{function}:{line}|{message}"
|
|
19
|
+
|
|
20
|
+
logger.add(
|
|
21
|
+
app_log_path,
|
|
22
|
+
rotation="500 MB",
|
|
23
|
+
retention="30 days",
|
|
24
|
+
compression="gz",
|
|
25
|
+
level=log_level,
|
|
26
|
+
format=log_format,
|
|
27
|
+
enqueue=True,
|
|
28
|
+
encoding="utf=8"
|
|
29
|
+
)
|
|
30
|
+
logger.add(
|
|
31
|
+
error_log_path,
|
|
32
|
+
rotation="500 MB",
|
|
33
|
+
retention="30 days",
|
|
34
|
+
compression="gz",
|
|
35
|
+
level="ERROR",
|
|
36
|
+
encoding="utf=8"
|
|
37
|
+
)
|
|
38
|
+
if console_stdout:
|
|
39
|
+
logger.add(
|
|
40
|
+
sink=lambda msg: print(msg, end=""),
|
|
41
|
+
level=log_level,
|
|
42
|
+
format=log_format,
|
|
43
|
+
enqueue=True
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return logger
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
from typing import Dict, Generator, List, Optional
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from app.models.schemas import ChatResponse
|
|
7
|
+
from app.service.chat_service import ChatService
|
|
8
|
+
from app.service.rag_service import RagService
|
|
9
|
+
from app.utils.function_utils import tools
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ChatstreamManager:
|
|
13
|
+
def __init__(self, config, chat_service: ChatService, rag_service: RagService):
|
|
14
|
+
self._chat_service = chat_service
|
|
15
|
+
self._rag_service = rag_service
|
|
16
|
+
self._conversations: Dict[str, Dict] = {}
|
|
17
|
+
self._max_history = config["CONVERSATION_MAX_HISTORY"]
|
|
18
|
+
self._ttl = config["CONVERSATION_TTL"]
|
|
19
|
+
self._lock = threading.Lock()
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def normalize_conversation_id(conversation_id: Optional[str]) -> str:
|
|
23
|
+
normalized = (conversation_id or "").strip()
|
|
24
|
+
return normalized or f"conv-{uuid4().hex}"
|
|
25
|
+
|
|
26
|
+
def check_rag(self) -> Dict:
|
|
27
|
+
return self._rag_service.check()
|
|
28
|
+
|
|
29
|
+
def force_rebuild_rag(self) -> Dict:
|
|
30
|
+
return self._rag_service.force_rebuild()
|
|
31
|
+
|
|
32
|
+
def _cleanup_expired(self):
|
|
33
|
+
now = time.time()
|
|
34
|
+
expired = [cid for cid, c in self._conversations.items() if now - c["last_active"] > self._ttl]
|
|
35
|
+
for cid in expired:
|
|
36
|
+
del self._conversations[cid]
|
|
37
|
+
|
|
38
|
+
def _ensure_conversation(self, conversation_id: str):
|
|
39
|
+
if conversation_id not in self._conversations:
|
|
40
|
+
self._conversations[conversation_id] = {
|
|
41
|
+
"messages": [],
|
|
42
|
+
"created_at": time.time(),
|
|
43
|
+
"last_active": time.time(),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def _get_history(self, conversation_id: str) -> List[Dict[str, str]]:
|
|
47
|
+
with self._lock:
|
|
48
|
+
self._cleanup_expired()
|
|
49
|
+
self._ensure_conversation(conversation_id)
|
|
50
|
+
return list(self._conversations[conversation_id]["messages"])
|
|
51
|
+
|
|
52
|
+
def _append_exchange(self, conversation_id: str, user_question: str, assistant_answer: str):
|
|
53
|
+
with self._lock:
|
|
54
|
+
self._ensure_conversation(conversation_id)
|
|
55
|
+
conversation = self._conversations[conversation_id]
|
|
56
|
+
conversation["messages"].extend([
|
|
57
|
+
{"role": "user", "content": user_question},
|
|
58
|
+
{"role": "assistant", "content": assistant_answer},
|
|
59
|
+
])
|
|
60
|
+
conversation["last_active"] = time.time()
|
|
61
|
+
max_messages = self._max_history * 2
|
|
62
|
+
if len(conversation["messages"]) > max_messages:
|
|
63
|
+
conversation["messages"] = conversation["messages"][-max_messages:]
|
|
64
|
+
|
|
65
|
+
def chat_stream(
|
|
66
|
+
self,
|
|
67
|
+
conversation_id: Optional[str],
|
|
68
|
+
question: str,
|
|
69
|
+
jsessionid: str,
|
|
70
|
+
) -> Generator[ChatResponse, None, None]:
|
|
71
|
+
normalized_conversation_id = self.normalize_conversation_id(conversation_id)
|
|
72
|
+
history = self._get_history(normalized_conversation_id)
|
|
73
|
+
answer_parts: List[str] = []
|
|
74
|
+
stopped = False
|
|
75
|
+
|
|
76
|
+
for chunk in self._chat_service.tool_call_stream(
|
|
77
|
+
normalized_conversation_id,
|
|
78
|
+
question,
|
|
79
|
+
tools,
|
|
80
|
+
history,
|
|
81
|
+
jsessionid,
|
|
82
|
+
):
|
|
83
|
+
if chunk.content:
|
|
84
|
+
answer_parts.append(chunk.content)
|
|
85
|
+
if chunk.finish_reason == "stop":
|
|
86
|
+
stopped = True
|
|
87
|
+
yield chunk
|
|
88
|
+
|
|
89
|
+
if stopped:
|
|
90
|
+
self._append_exchange(normalized_conversation_id, question, "".join(answer_parts))
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""LLM提示词模板定义"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# 提取时间范围的提示词
|
|
5
|
+
PROMPT_EXTRACT_TIME_RANGE_SYSTEM = """You are a helpful assistant tasked with extracting structured information based on specific criteria provided. Follow the guidelines below to ensure consistency and accuracy.
|
|
6
|
+
### Task
|
|
7
|
+
Always call the `extract_parameters` function with the correct parameters. Ensure that the information extraction is contextual and aligns with the provided criteria.
|
|
8
|
+
### Memory
|
|
9
|
+
Here is the chat history between the human and assistant, provided within <histories> tags:
|
|
10
|
+
<histories>
|
|
11
|
+
|
|
12
|
+
</histories>
|
|
13
|
+
### Instructions:
|
|
14
|
+
Some additional information is provided below. Always adhere to these instructions as closely as possible:
|
|
15
|
+
<instruction>
|
|
16
|
+
当前时间是{current_time}
|
|
17
|
+
</instruction>
|
|
18
|
+
Steps:
|
|
19
|
+
1. Review the chat history provided within the <histories> tags.
|
|
20
|
+
2. Extract the relevant information based on the criteria given, output multiple values if there is multiple relevant information that match the criteria in the given text.
|
|
21
|
+
3. Generate a well-formatted output using the defined functions and arguments.
|
|
22
|
+
4. Use the `extract_parameter` function to create structured outputs with appropriate parameters.
|
|
23
|
+
5. Do not include any XML tags in your output.
|
|
24
|
+
### Example
|
|
25
|
+
To illustrate, if the task involves extracting a user's name and their request, your function call might look like this: Ensure your output follows a similar structure to examples.
|
|
26
|
+
### Final Output
|
|
27
|
+
Produce well-formatted function calls in json without XML tags, as shown in the example."""
|
|
28
|
+
|
|
29
|
+
PROMPT_EXTRACT_TIME_RANGE_USER = """### Structure
|
|
30
|
+
Here is the structure of the JSON object, you should always follow the structure.
|
|
31
|
+
<structure>
|
|
32
|
+
{"queryTimeBegin": {"description": "查询的开始时间,格式为yyyy-MM-dd,如果结果没有明确查询年份,则根据当前时间获取年份", "type": "string"}, "queryTimeEnd": {"description": "查询的结束时间,如果结果没有明确结束时间,则默认为当前时间,格式为yyyy-MM-dd", "type": "string"}, "queryContext": {"description": "用户原文", "type": "string"}}
|
|
33
|
+
</structure>
|
|
34
|
+
|
|
35
|
+
### Text to be converted to JSON
|
|
36
|
+
Inside <text></text> XML tags, there is a text that you should convert to a JSON object.
|
|
37
|
+
<text>
|
|
38
|
+
{query_text}
|
|
39
|
+
</text>"""
|
|
40
|
+
|
|
41
|
+
# 质量下降月份分析的提示词
|
|
42
|
+
PROMPT_QUALITY_DECLINE_MONTHS = """根据输入的机构的运行质量情况,{data_json},回答问题{{answer}}。并提取出对比上一个月成功率下降的月份集合{{declineMonth}},注意,比较的精度是小数点后6位,月份的格式为yyyy-MM:。
|
|
43
|
+
例子:{{\\"answer\\":\\"xxxx年xx月份以来,全链路系统成功率整体趋于下降,具体数据如下:- xxxx年x月:99.9989- xxxx年x月:99.9986- xxxx年x月:99.9985(有几个显示几个)。其中,xxxx年x月系统成功率最高,达到99.9990,最低为xxxx年x月的99.9985。\\",\\"declineMonth\\":\\"yyyy-mm,yyyy-mm(用逗号分隔,没有空格)\\"}}"""
|
|
44
|
+
|
|
45
|
+
# 机构维度故障详情的提示词
|
|
46
|
+
PROMPT_FAULT_DETAILS_BY_ORG = """根据输入的机构的运行质量情况,{data_json}。按以下为主要内容回答,可以使用markdown格式丰富展示:注意:faultCause属性里为固定名词,不要改变;影响笔数为整数,没有小数点。
|
|
47
|
+
|
|
48
|
+
<b>以成员机构为维度统计:</b>
|
|
49
|
+
xxx年x月:
|
|
50
|
+
成员机构层面主要因为xx银行\\xx信用社\\xx公司(取前三)的系统失败导致。具体情况(取所有)如下:
|
|
51
|
+
1)xx银行,影响笔数xxx,主要原因:faultCause"""
|
|
52
|
+
|
|
53
|
+
# 失败模式分析的提示词
|
|
54
|
+
PROMPT_FAILURE_PATTERNS_BY_ORG = """根据输入的机构的运行质量情况,{data_json}。按以下为主要内容回答。可以使用markdown格式丰富展示。注意:1.查出来的机构都是成功率低于全链路系统成功率的。2.分析内容不要输出属性值
|
|
55
|
+
|
|
56
|
+
按以下进行分析:
|
|
57
|
+
|
|
58
|
+
xxx年x月
|
|
59
|
+
1.找到交易量较大( scale值等于\"large\"),并且sysSuccessRate大于0.999955的银行单位。做出以下结论:
|
|
60
|
+
1)成员单位xxx,xxx,xxx虽然造成系统失败交易量较大,但系统成功率已达到99.9955%,提升空间有限。
|
|
61
|
+
2.排除提升有限的机构,统计下造成influence高的前3个机构,统计他们的totalCntFail之和。做出以下结论(不要把属性展示出来):
|
|
62
|
+
2)成员机构xxx,xxx,xxx等合计造成系统失败量xxx(totalCntFail之和),同时其系统弄成功率相对较低,建议从xxx机构入手推动推动。"""
|