Compare commits

...

7 Commits

Author SHA1 Message Date
ouyangyouzhang 9200df7842 新增客服重定向接口 2025-11-28 10:12:41 +08:00
ouyangyouzhang eb361fe77f feat: 添加启动意图识别API服务的脚本
添加专用脚本用于启动rag2_0.api.intent_recognition_api服务
脚本功能包括检测并结束现有screen会话,清理占用端口,最后启动新服务
2025-11-26 11:12:39 +08:00
ouyangyouzhang 4627a2268f Merge branch 'master' of https://git.97id.com/ouyangyouzhang/QueryRewrite 2025-11-26 10:52:28 +08:00
ouyangyouzhang 46f756428e 上线前相关环境变量的修改 2025-11-26 10:49:54 +08:00
ouyangyouzhang a22b001680 先跳过 定额信息的提取 2025-11-26 10:28:11 +08:00
ouyangyouzhang c97e96c620 删除欠费的api 2025-10-21 18:02:16 +08:00
ouyangyouzhang 94a8656c7f 微调提示词,基本不影响效果 2025-09-26 15:46:00 +08:00
21 changed files with 286 additions and 50 deletions
+18 -18
View File
@@ -1,27 +1,27 @@
OPENAI_API_BASE=https://api.siliconflow.cn/v1/ OPENAI_API_BASE=https://api.siliconflow.cn/v1/
MODEL_NAME=deepseek-ai/DeepSeek-V3 MODEL_NAME=deepseek-ai/DeepSeek-V3
RERANKER_BASE_URL=http://10.1.16.39:9995 # RERANKER_BASE_URL=http://10.1.16.39:9995
RERANKER_MODEL_NAME=bge-reranker-v2-m3 # RERANKER_MODEL_NAME=bge-reranker-v2-m3
RERANKER_API_KEY=test # RERANKER_API_KEY=test
EMBEDDING_BASE_URL=http://10.1.16.39:9995 # EMBEDDING_BASE_URL=http://10.1.16.39:9995
EMBEDDING_MODEL_NAME=bge-m3 # EMBEDDING_MODEL_NAME=bge-m3
EMBEDDING_API_KEY=test # EMBEDDING_API_KEY=test
DIFY_BSAE_URL=http://10.1.16.39/v1 # DIFY_BSAE_URL=http://10.1.16.39/v1
DIFY_APP_KEY=app-CPoOMaGDsLRPAe9TW7Xjhszy # DIFY_APP_KEY=app-CPoOMaGDsLRPAe9TW7Xjhszy
DIFY_DATASET_KEY=dataset-skLjmPVonjHo119OWNf3kAmY # DIFY_DATASET_KEY=dataset-skLjmPVonjHo119OWNf3kAmY
DIFY_PG_HOST = 10.1.16.39 # DIFY_PG_HOST = 10.1.16.39
DIFY_PG_PORT = 5432 # DIFY_PG_PORT = 5432
DIFY_PG_USER = postgres # DIFY_PG_USER = postgres
DIFY_PG_PASSWORD = difyai123456 # DIFY_PG_PASSWORD = difyai123456
DIFY_PG_DATABASE = dify # DIFY_PG_DATABASE = dify
ENABLE_LANGFUSE=true # ENABLE_LANGFUSE=true
LANGFUSE_PUBLIC_KEY=pk-lf-4e9b7cbe-528c-4697-b73c-33257a60072c # LANGFUSE_PUBLIC_KEY=pk-lf-4e9b7cbe-528c-4697-b73c-33257a60072c
LANGFUSE_SECRET_KEY=sk-lf-cd8a78c5-2538-455e-a85a-87b6e1aa69d0 # LANGFUSE_SECRET_KEY=sk-lf-cd8a78c5-2538-455e-a85a-87b6e1aa69d0
LANGFUSE_HOST=http://10.1.6.34:3000 # LANGFUSE_HOST=http://10.1.6.34:3000
+8 -2
View File
@@ -10,7 +10,10 @@
"request": "launch", "request": "launch",
"program": "${file}", "program": "${file}",
"console": "integratedTerminal", "console": "integratedTerminal",
"justMyCode": true "justMyCode": true,
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}, },
{ {
"name": "IntentRecognition", "name": "IntentRecognition",
@@ -18,7 +21,10 @@
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/rag2_0/demo/intent_recognition_example.py", "program": "${workspaceFolder}/rag2_0/demo/intent_recognition_example.py",
"console": "integratedTerminal", "console": "integratedTerminal",
"justMyCode": true "justMyCode": true,
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
} }
] ]
} }
-1
View File
@@ -57,7 +57,6 @@ sk-benuasjbhbxvdmgxishibmtpfyieamlfclmdclfbqloqsmaf
sk-ufmqbuplpjvzzlzohvsxertwgnguhipsbajxnxecvvccozly sk-ufmqbuplpjvzzlzohvsxertwgnguhipsbajxnxecvvccozly
sk-rypfoscrczeelowmrsixiuyunyqmqvknaprsnzmdguwzrkzx sk-rypfoscrczeelowmrsixiuyunyqmqvknaprsnzmdguwzrkzx
sk-lucemnosmcxuwedvzilpefuxjnyvaxldpbgaqwnwalxmntul sk-lucemnosmcxuwedvzilpefuxjnyvaxldpbgaqwnwalxmntul
sk-niymkyuzpyovndvvqvpaniiqfgoofnxczhdmjjessiocbeul
sk-cxlvgeuxavxfcajprxietuqyqjngtbrwrmrmrioxmgtbkpci sk-cxlvgeuxavxfcajprxietuqyqjngtbrwrmrmrioxmgtbkpci
sk-vjjsuzntqbhcmelfsuquqyoxjivxcfwyxnrhpwzobgxlpmrv sk-vjjsuzntqbhcmelfsuquqyoxjivxcfwyxnrhpwzobgxlpmrv
sk-hbgctnpvntsnelveaudpekyncfgstdfazezboxmcgjvudzyg sk-hbgctnpvntsnelveaudpekyncfgstdfazezboxmcgjvudzyg
-1
View File
@@ -29,7 +29,6 @@ def main(query: str) -> dict:
import sys import sys
sys.path.append(os.getcwd())
from rag2_0.dify.DifyQueryRetrieval import DifyQueryRetrieval from rag2_0.dify.DifyQueryRetrieval import DifyQueryRetrieval
# 定义数据库路径 # 定义数据库路径
-1
View File
@@ -18,7 +18,6 @@ import logging
load_dotenv() load_dotenv()
import sys import sys
sys.path.append(os.getcwd())
from rag2_0.dify.DifyQueryRetrieval import DifyQueryRetrieval from rag2_0.dify.DifyQueryRetrieval import DifyQueryRetrieval
# 确保日志目录存在 # 确保日志目录存在
@@ -5,7 +5,6 @@ import pandas as pd
from openpyxl import load_workbook from openpyxl import load_workbook
import logging import logging
import numpy as np import numpy as np
sys.path.append(os.getcwd())
from rag2_0.tool.ModelTool import XinferenceEmbeddings from rag2_0.tool.ModelTool import XinferenceEmbeddings
from langchain_community.vectorstores import SQLiteVSS from langchain_community.vectorstores import SQLiteVSS
+4 -1
View File
@@ -15,8 +15,8 @@ import logging
load_dotenv() load_dotenv()
import sys import sys
sys.path.append(os.getcwd())
from rag2_0.intent_recognition import AsyncIntentRecognizer from rag2_0.intent_recognition import AsyncIntentRecognizer
from rag2_0.api.kefu_redirect_url import router as kefu_router
# 确保日志目录存在 # 确保日志目录存在
os.makedirs('data/logs', exist_ok=True) os.makedirs('data/logs', exist_ok=True)
@@ -85,6 +85,9 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
# 注册外部路由
app.include_router(kefu_router)
# 全局变量存储AsyncIntentRecognizer实例 # 全局变量存储AsyncIntentRecognizer实例
_instance = None _instance = None
+92
View File
@@ -0,0 +1,92 @@
from fastapi import APIRouter
from fastapi.responses import RedirectResponse
import os
import sqlite3
import threading
import time
from queue import Queue, Full
router = APIRouter()
# 以当前文件为基准的相对路径:../../data/db
PROJECT_ROOT = os.getcwd()
DB_DIR = os.path.join(PROJECT_ROOT, "data", "db")
DB_FILE = os.path.join(DB_DIR, "redirects.sqlite3")
TABLE_SQL = (
"CREATE TABLE IF NOT EXISTS redirects ("
" msg_id TEXT PRIMARY KEY,"
" url TEXT NOT NULL"
")"
)
def _ensure_db():
"""确保数据库与表存在。"""
os.makedirs(DB_DIR, exist_ok=True)
with sqlite3.connect(DB_FILE) as conn:
cur = conn.cursor()
cur.execute(TABLE_SQL)
conn.commit()
def save_redirect(msg_id: str, url: str) -> None:
"""将 msg_id 与 url 写入 SQLite,若已存在则忽略。
使用 INSERT OR IGNORE 结合 PRIMARY KEY(msg_id) 来避免重复写入。
"""
_ensure_db()
with sqlite3.connect(DB_FILE) as conn:
cur = conn.cursor()
cur.execute(
"INSERT OR IGNORE INTO redirects (msg_id, url) VALUES (?, ?)",
(msg_id, url),
)
conn.commit()
# ========= 异步写库队列与后台线程 =========
_write_queue: "Queue[tuple[str, str]]" = Queue(maxsize=10000)
def _write_worker():
_ensure_db()
while True:
try:
msg_id, url = _write_queue.get()
try:
with sqlite3.connect(DB_FILE) as conn:
cur = conn.cursor()
cur.execute(
"INSERT OR IGNORE INTO redirects (msg_id, url) VALUES (?, ?)",
(msg_id, url),
)
conn.commit()
except Exception:
# 失败忽略,避免阻断工作线程
pass
finally:
_write_queue.task_done()
except Exception:
# 防御性 sleep,避免异常导致CPU空转
time.sleep(0.1)
_worker_thread = threading.Thread(target=_write_worker, daemon=True)
_worker_thread.start()
@router.get("/kefu_login", summary="客服登录页重定向")
async def kefu_redirect(msg_id:str):
"""重定向到客服登录页。"""
target_url = "https://www.booway.com.cn/kefu/toLoginPage"
# 写入 SQLite:若 msg_id 已存在将不会重复写入
try:
if msg_id:
# 走异步队列
_write_queue.put_nowait((msg_id, target_url))
except Exception:
# 出于稳健性考虑,即使写库失败也不影响重定向
pass
return RedirectResponse(target_url, status_code=302)
-1
View File
@@ -10,7 +10,6 @@ import sys
import os import os
# 导入ExcelToSQLiteProcessor类 # 导入ExcelToSQLiteProcessor类
sys.path.append(os.getcwd())
from rag2_0.api.create_qingdan_dinge_database import ExcelToSQLiteProcessor, create_db from rag2_0.api.create_qingdan_dinge_database import ExcelToSQLiteProcessor, create_db
# 导入向量检索相关类 # 导入向量检索相关类
from rag2_0.tool.ModelTool import XinferenceEmbeddings from rag2_0.tool.ModelTool import XinferenceEmbeddings
-2
View File
@@ -18,8 +18,6 @@ from tqdm import tqdm
import glob import glob
import shutil import shutil
# 将项目根目录添加到Python路径
sys.path.append(os.getcwd())
from rag2_0.tool.ModelTool import OpenAiLLM from rag2_0.tool.ModelTool import OpenAiLLM
load_dotenv() load_dotenv()
@@ -20,7 +20,6 @@ import argparse
from typing import List, Dict, Any from typing import List, Dict, Any
from langchain.output_parsers import PydanticOutputParser from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
sys.path.append(os.getcwd())
from rag2_0.intent_recognition import AsyncIntentRecognizer from rag2_0.intent_recognition import AsyncIntentRecognizer
from rag2_0.dify.DifyQueryRetrieval import DifyQueryRetrieval from rag2_0.dify.DifyQueryRetrieval import DifyQueryRetrieval
from rag2_0.intent_recognition.DataModels import Classification from rag2_0.intent_recognition.DataModels import Classification
-1
View File
@@ -10,7 +10,6 @@ import os
import json import json
from dotenv import load_dotenv from dotenv import load_dotenv
import sys import sys
sys.path.append(os.getcwd())
from rag2_0.intent_recognition import ProfessionalNounVectorizer from rag2_0.intent_recognition import ProfessionalNounVectorizer
import logging import logging
-1
View File
@@ -15,7 +15,6 @@ from datetime import datetime
import os import os
from langchain_core.output_parsers import JsonOutputParser from langchain_core.output_parsers import JsonOutputParser
sys.path.append(os.getcwd())
from rag2_0.dify.dify_client import ChatClient from rag2_0.dify.dify_client import ChatClient
from rag2_0.tool.ModelTool import OpenAiLLM from rag2_0.tool.ModelTool import OpenAiLLM
from rag2_0.dify.dify_tool import DifyTool from rag2_0.dify.dify_tool import DifyTool
-1
View File
@@ -6,7 +6,6 @@ import logging
import time import time
import asyncio import asyncio
import httpx import httpx
sys.path.append(os.getcwd())
from rag2_0.dify.dify_client.client import DifyClient, KnowledgeBaseClient from rag2_0.dify.dify_client.client import DifyClient, KnowledgeBaseClient
from rag2_0.tool.ModelTool import XinferenceReRankerModel from rag2_0.tool.ModelTool import XinferenceReRankerModel
@@ -5,7 +5,6 @@ import sys
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
sys.path.append(os.getcwd())
from rag2_0.dify.dify_client import DifyApi from rag2_0.dify.dify_client import DifyApi
-1
View File
@@ -17,7 +17,6 @@ logging.basicConfig(
] ]
) )
sys.path.append(os.getcwd())
import rag2_0.dify.dify_client.dify_api as DifyApi import rag2_0.dify.dify_client.dify_api as DifyApi
import pandas as pd import pandas as pd
+45 -1
View File
@@ -6,7 +6,6 @@ import json
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
import sys import sys
sys.path.append(os.getcwd())
from rag2_0.dify.dify_client import ChatClient from rag2_0.dify.dify_client import ChatClient
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from langchain.output_parsers import PydanticOutputParser from langchain.output_parsers import PydanticOutputParser
@@ -271,6 +270,51 @@ class DifyTool:
raise Exception(f"Error while getting conversation_messages: {error}") raise Exception(f"Error while getting conversation_messages: {error}")
return None return None
def execute_custom_sql(self, sql, params=None, fetch_type='all'):
"""
执行自定义的SQL查询或命令。
Args:
sql: 要执行的SQL语句
params: SQL参数(可选),用于参数化查询
fetch_type: 结果获取类型,可选值:'all'(返回所有行), 'one'(返回单行), 'none'(不返回结果)
Returns:
根据fetch_type返回查询结果:
- fetch_type='all': 返回包含所有行的列表,每行是一个字典
- fetch_type='one': 返回单行字典,如果没有结果则返回None
- fetch_type='none': 返回受影响的行数
Raises:
Exception: 如果执行SQL时发生错误
"""
with self.pg_sql_lock:
try:
with self.connection.cursor() as cursor:
# 执行SQL语句
cursor.execute(sql, params or ())
# 根据fetch_type处理结果
if fetch_type == 'all':
result = cursor.fetchall()
if result:
colnames = [desc[0] for desc in cursor.description]
return [dict(zip(colnames, row)) for row in result]
return []
elif fetch_type == 'one':
result = cursor.fetchone()
if result:
colnames = [desc[0] for desc in cursor.description]
return dict(zip(colnames, result))
return None
elif fetch_type == 'none':
# 对于UPDATE, INSERT, DELETE等操作,返回受影响的行数
return cursor.rowcount
else:
raise ValueError(f"不支持的fetch_type: {fetch_type}")
except (Exception, psycopg2.Error) as error:
raise Exception(f"Error executing custom SQL: {error}")
""" """
提供用于获取 Dify 应用调试信息的工具类。 提供用于获取 Dify 应用调试信息的工具类。
-1
View File
@@ -6,7 +6,6 @@ import pandas as pd
import sys import sys
sys.path.append(os.getcwd())
from rag2_0.dify.dify_tool import DifyTool from rag2_0.dify.dify_tool import DifyTool
import requests import requests
@@ -355,7 +355,11 @@ class AsyncIntentRecognizer:
"dinge_info_list":{{"dinge_code_list":["xxxx","xxxx"], "dinge_name_list":["xxxx","xxxx"]}}, "dinge_info_list":{{"dinge_code_list":["xxxx","xxxx"], "dinge_name_list":["xxxx","xxxx"]}},
"qingdan_info":{{"qingdan_code_list":["xxxx","xxxx"], "qingdan_name_list":["xxxx","xxxx"]}} "qingdan_info":{{"qingdan_code_list":["xxxx","xxxx"], "qingdan_name_list":["xxxx","xxxx"]}}
}}""" }}"""
# 暂时跳过提取定额清单信息,本环节还未梳理清楚套用规则。
return {
"dinge_info_list":{"dinge_code_list":[], "dinge_name_list":[]},
"qingdan_info":{"qingdan_code_list":[], "qingdan_name_list":[]}
}
try: try:
# response = await self._llm.ainvoke(prompt, response_format={"type": "json_object"}, extra_body={"enable_thinking": False}) # response = await self._llm.ainvoke(prompt, response_format={"type": "json_object"}, extra_body={"enable_thinking": False})
response = await self._llm.ainvoke(prompt, response_format={"type": "json_object"}) response = await self._llm.ainvoke(prompt, response_format={"type": "json_object"})
+3 -13
View File
@@ -100,16 +100,14 @@ query_rewrite_prompt_pro="""# 问答优化工程师
2. 所有新增内容必须源于历史对话或聊天背景,禁止捏造。 2. 所有新增内容必须源于历史对话或聊天背景,禁止捏造。
3. 归一化替换需严格全词匹配:查询中的词必须与术语库同义词完全一致(不区分大小写)。部分匹配(如子字符串)或不匹配,保留原词 3. 归一化替换需严格全词匹配:查询中的词必须与术语库同义词完全一致(不区分大小写)。部分匹配(如子字符串)或不匹配,保留原词
## 核心原则 ## 核心原则
1. **指代消除 → 当指示代词(""/"")出现时,强制继承历史对话的最新核心主题(如功能或任务),并应用到当前主体。** 1. **指代消除 → 当指示代词(""/"")出现时,继承历史对话的最新核心主题(如功能或任务),并应用到当前主体。**
2. 术语规范 → 提问中出现的同义词(synonymous)替换为标准词(name)并【】标记 2. 术语规范 → 提问中出现的同义词(synonymous)替换为标准词(name)并【】标记
3. 语义保真 → 保持问题核心意图,允许指代消除 3. 语义保真 → 保持问题核心意图,允许指代消除
## 归一化替换规则 ## 归一化替换规则
1. 只有当问题中的词与术语库中某一项的同义词列表中的某个词完全相同时,才替换为对应的标准词 1. 只有当问题中的词与术语库中某一项的同义词列表中的某个词完全相同时,才替换为对应的标准词
## 处理流程 ## 处理流程
### 一、输入解析 ### 一、输入解析
- 原始问题(需保留核心语义): - 原始问题(需保留核心语义):
@@ -125,14 +123,14 @@ query_rewrite_prompt_pro="""# 问答优化工程师
{chat_history} {chat_history}
</history> </history>
### 、重构流程 ### 、重构流程
1、问题是否指代不明,指代不明时根据历史对话补充上下文 1、问题是否指代不明,指代不明时根据历史对话补充上下文
2、问题是否包含同义词,包含同义词时进行同义词转标准词 2、问题是否包含同义词,包含同义词时进行同义词转标准词
### 三、重构优先级 ### 三、重构优先级
1. **指代消除 → 当指示代词出现时,结合历史对话补充上下文** 1. **指代消除 → 当指示代词出现时,结合历史对话补充上下文**
2. 同义词转标准词 → 将提问中出现的同义词(synonymous)替换为对应标准词(name) 并使用【】标记 2. 同义词转标准词 → 将提问中出现的同义词(synonymous)替换为对应标准词(name) 并使用【】标记
3. 结构优化 → 保持原问题的5W2H特征,指代消除、背景继承下允许微调意图 3. 结构优化 → 指代消除、背景继承下允许微调提问
## 输出规范 ## 输出规范
{output_format} {output_format}
@@ -207,14 +205,6 @@ step_back_prompt = """# 后退提示生成器
{output_format} {output_format}
## 示例 ## 示例
原始问题: "2023版本如何在Windows 11系统上导入单位工程量清单?"
后退问题:
{{
"original_query": "2023版本如何在Windows 11系统上导入单位工程量清单?",
"can_use_back_prompt": true,
"step_back_query": ["如何在Windows 11系统上导入单位工程量清单?", "如何导入单位工程量清单?"]
}}
原始问题: "某个设备更换后,如何在系统中更新对应的定额?" 原始问题: "某个设备更换后,如何在系统中更新对应的定额?"
后退问题: 后退问题:
{{ {{
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# 专用脚本:启动 rag2_0.api.intent_recognition_api 服务
# 功能:启动前检测screen是否存在,存在则结束,最后启动服务
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SESSION_NAME="intent_recognition_api"
SERVICE_PORT="8001"
START_COMMAND="cd \"$SCRIPT_DIR\" && uv run uvicorn rag2_0.api.intent_recognition_api:app --host 0.0.0.0 --port 8001 --workers 4"
echo "[脚本] 启动 intent_recognition_api 服务..."
# 检查screen会话是否存在
exists_session() {
# 使用严格匹配,避免误判
if screen -ls 2>/dev/null | grep -q "\\.${SESSION_NAME}\\s"; then
return 0
fi
return 1
}
# 按端口获取监听该端口的任意一个PID,优先用 ss,其次 lsof
pids_on_port() {
# 从 ss 提取 pid 列表
local ss_pids
ss_pids=$(ss -lptn 2>/dev/null \
| grep -E ":${SERVICE_PORT}\\b" \
| awk '{print $NF}' \
| sed -n 's/.*pid=\([0-9]\+\),.*/\1/p' \
| sort -u)
if [[ -n "$ss_pids" ]]; then
echo "$ss_pids"
return 0
fi
# 从 lsof 提取 pid 列表
if command -v lsof >/dev/null 2>&1; then
local lsof_pids
lsof_pids=$(lsof -nP -i :"$SERVICE_PORT" -sTCP:LISTEN -t 2>/dev/null | sort -u)
if [[ -n "$lsof_pids" ]]; then
echo "$lsof_pids"
return 0
fi
fi
return 1
}
# 根据端口优雅终止(TERM)并在必要时强制(KILL)清理进程
kill_by_port() {
local pids
pids=$(pids_on_port || true)
if [[ -z "$pids" ]]; then
return 0
fi
echo "[清理] 端口 $SERVICE_PORT 仍被占用,发送 SIGTERM 到: $pids"
kill -TERM $pids 2>/dev/null || true
sleep 2
# 再次检查
local left
left=$(pids_on_port || true)
if [[ -n "$left" ]]; then
echo "[强制] 端口 $SERVICE_PORT 仍占用,发送 SIGKILL 到: $left"
kill -KILL $left 2>/dev/null || true
fi
}
# 停止已存在的服务
stop_existing_service() {
# 1) 先尝试关闭 screen 会话
if exists_session "$SESSION_NAME"; then
echo "[停止] 发现已存在的 screen 会话 '$SESSION_NAME',正在结束..."
screen -S "$SESSION_NAME" -X quit || true
echo "[停止] screen 会话 '$SESSION_NAME' 已结束"
else
echo "[提示] 未发现 screen 会话: $SESSION_NAME"
fi
# 2) 等待释放端口
sleep 2
# 3) 如果仍占用,按端口清理
if ss -lptn 2>/dev/null | grep -E -q ":${SERVICE_PORT}\\b" || (command -v lsof >/dev/null 2>&1 && lsof -i :"$SERVICE_PORT" -sTCP:LISTEN >/dev/null 2>&1); then
echo "[清理] 端口 $SERVICE_PORT 仍被占用,正在清理..."
kill_by_port
echo "[清理] 端口 $SERVICE_PORT 清理完成"
fi
}
# 启动服务
start_new_service() {
echo "[启动] 准备启动 intent_recognition_api 服务..."
echo "[启动] 启动命令: $START_COMMAND"
screen -dmS "$SESSION_NAME" bash -c "$START_COMMAND"
echo "[启动] intent_recognition_api 服务已启动,screen 会话名: '$SESSION_NAME'"
echo "[启动] 服务运行在端口: $SERVICE_PORT"
echo "[提示] 可使用 'screen -r $SESSION_NAME' 查看服务输出"
}
# 主流程
main() {
# 1. 停止已存在的服务
stop_existing_service
# 2. 启动新服务
start_new_service
echo "[完成] intent_recognition_api 服务启动脚本执行完成"
}
main