新增
This commit is contained in:
+81
-18
@@ -9,6 +9,8 @@ AI 大模型配置文件
|
||||
import json
|
||||
import os
|
||||
|
||||
from runtime_paths import application_data_dir
|
||||
|
||||
# ── 是否启用 AI 回复(False 时使用固定文本回复)──
|
||||
AI_ENABLED = True
|
||||
|
||||
@@ -34,6 +36,8 @@ AI_USE_VISION = False
|
||||
|
||||
# ── 客服身份(出现在提示词里,可按需修改为你的真实昵称/工号)──
|
||||
AI_AGENT_NAME = "高兴亮"
|
||||
AI_CLOUD_AGENT_NAME = AI_AGENT_NAME
|
||||
AI_LOCAL_AGENT_NAME = ""
|
||||
|
||||
# ── 所属医院(建议就医时统一引导到这里挂号)──
|
||||
AI_HOSPITAL_NAME = "甄养堂互联网医院"
|
||||
@@ -98,6 +102,10 @@ AI_SYSTEM_PROMPT_TEMPLATE = """你就是企业微信里的人工客服「{AI_AGE
|
||||
同一个会话里这种话最多说一次,且每次措辞都要不一样,像临场想的,不是背台词。
|
||||
- 不用不厚道地敷衍或明显在"执行任务"式转移话题;可以顺着聊一两句再自然带回正题,
|
||||
或者干脆就随口接一句玩笑话,不必每次都往业务上拽。
|
||||
- 先判断对方这一句是在闲聊、问业务还是问健康。闲聊时只顺着当前话题聊,严禁主动扯到血糖、
|
||||
身体、医院、医生、挂号或面诊;普通问题直接回答,不要教育对方。
|
||||
- 客户问「你多大了/你几岁」时,直接自然回答「四十来岁」;不要回避,不要说「别管我多大」,
|
||||
更不要强行把话题拉回健康。客户说「好困啊」这类日常感叹时,简单接一句关心即可。
|
||||
|
||||
【你的性格底色|先有人,再有话】
|
||||
- 你是个四十岁上下、干了十几年的老客服兼医生:见过各种病人,什么问题都不慌,
|
||||
@@ -137,7 +145,7 @@ AI_SYSTEM_PROMPT_TEMPLATE = """你就是企业微信里的人工客服「{AI_AGE
|
||||
|
||||
【糖尿病专业问题|专业 + 人性化】
|
||||
- 凡是涉及血糖、糖尿病、胰岛素、降糖药、糖化血红蛋白、并发症、糖尿病饮食/运动等话题,
|
||||
要以糖尿病专家的身份给出准确、专业、有依据的回答,可适当放宽到 3~5 句,
|
||||
要以糖尿病专家的身份给出准确、专业、有依据的回答,仍尽量控制在 1~2 句;确有安全风险时最多 3 句,
|
||||
但必须仍然是微信聊天的口吻——像医生朋友在微信上跟你唠,绝不能写成科普文章或报告。
|
||||
- 不急着一次给全套方案:先问清或先给最关键的一两条,说完可以自然收尾
|
||||
(如「先这么调两天看看,有变化随时跟我说」),把话头留给对方。
|
||||
@@ -186,7 +194,15 @@ AI_SYSTEM_PROMPT_TEMPLATE = """你就是企业微信里的人工客服「{AI_AGE
|
||||
【处理原则|要真正把事推进,而不是空承诺或瞎编】
|
||||
- 只针对客户【最后一条】消息回复,紧扣聊天上下文,别答非所问。
|
||||
- 缺关键信息时,同一条消息里明确问对方要(单号、姓名、手机号、时间、地址、病症)。
|
||||
- 只有在你确实拿到真实查询结果时,才能告知具体状态/时效;否则只承诺去核实、问清信息。"""
|
||||
- 只有在你确实拿到真实查询结果时,才能告知具体状态/时效;否则只承诺去核实、问清信息。
|
||||
|
||||
【本轮输出硬规则|高于一般表达习惯】
|
||||
- 默认只回复 1~2 句、20~60 个汉字;只处理对方眼下最关心的一件事。
|
||||
- 只有对方明确担心、难受、着急或抱怨时,才先用一句自然短话接住他,再给最关键的回答;
|
||||
普通闲聊和直接提问要直接接话,不要强行安慰或转到健康话题。
|
||||
- 一条消息最多问一个问题;不要标题、列表、分点、长篇科普和客套收尾。
|
||||
- 除急救风险外不要超过 100 字;有急救风险时最多 3 句,并保留 120/急诊等必要提醒。
|
||||
- 最终只输出可以直接发给客户的聊天正文,不解释自己的写法。"""
|
||||
|
||||
|
||||
def build_system_prompt() -> str:
|
||||
@@ -202,7 +218,9 @@ AI_SYSTEM_PROMPT = build_system_prompt()
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# GUI 配置持久化(ai_settings.json)
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
_SETTINGS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ai_settings.json")
|
||||
_DATA_DIR = application_data_dir()
|
||||
_SETTINGS_FILE = str(_DATA_DIR / "ai_settings.json")
|
||||
_AGENT_OVERRIDE_FILE = str(_DATA_DIR / "ai_agent_override.json")
|
||||
|
||||
# 允许通过 GUI 修改并持久化的配置项
|
||||
CONFIGURABLE_KEYS = [
|
||||
@@ -214,10 +232,45 @@ CONFIGURABLE_KEYS = [
|
||||
]
|
||||
|
||||
|
||||
def _read_agent_name_override() -> str:
|
||||
try:
|
||||
with open(_AGENT_OVERRIDE_FILE, encoding="utf-8") as file:
|
||||
data = json.load(file)
|
||||
except (OSError, ValueError, TypeError):
|
||||
return ""
|
||||
if not isinstance(data, dict):
|
||||
return ""
|
||||
return str(data.get("AI_AGENT_NAME") or "").strip()
|
||||
|
||||
|
||||
def local_agent_name_override() -> str:
|
||||
return str(AI_LOCAL_AGENT_NAME or "")
|
||||
|
||||
|
||||
def set_local_agent_name_override(value: str, *, persist: bool = True) -> str:
|
||||
"""设置本机客服名称;空值表示跟随云端。"""
|
||||
global AI_AGENT_NAME, AI_LOCAL_AGENT_NAME, AI_SYSTEM_PROMPT
|
||||
AI_LOCAL_AGENT_NAME = str(value or "").strip()
|
||||
AI_AGENT_NAME = AI_LOCAL_AGENT_NAME or AI_CLOUD_AGENT_NAME or "客服"
|
||||
AI_SYSTEM_PROMPT = build_system_prompt()
|
||||
if persist:
|
||||
temporary = _AGENT_OVERRIDE_FILE + ".tmp"
|
||||
with open(temporary, "w", encoding="utf-8") as file:
|
||||
json.dump(
|
||||
{"AI_AGENT_NAME": AI_LOCAL_AGENT_NAME},
|
||||
file,
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
os.replace(temporary, _AGENT_OVERRIDE_FILE)
|
||||
return AI_AGENT_NAME
|
||||
|
||||
|
||||
def save_settings():
|
||||
"""将当前配置原子写入 ai_settings.json(GUI 保存时调用)。"""
|
||||
g = globals()
|
||||
data = {k: g[k] for k in CONFIGURABLE_KEYS}
|
||||
data["AI_AGENT_NAME"] = AI_CLOUD_AGENT_NAME
|
||||
tmp = _SETTINGS_FILE + ".tmp"
|
||||
with open(tmp, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
@@ -227,7 +280,9 @@ def save_settings():
|
||||
def export_settings() -> dict:
|
||||
"""返回可下发给桌面端的 AI 配置副本。"""
|
||||
g = globals()
|
||||
return {key: g[key] for key in CONFIGURABLE_KEYS}
|
||||
data = {key: g[key] for key in CONFIGURABLE_KEYS}
|
||||
data["AI_AGENT_NAME"] = AI_CLOUD_AGENT_NAME
|
||||
return data
|
||||
|
||||
|
||||
def apply_settings(settings: dict, *, persist: bool = True) -> dict:
|
||||
@@ -236,15 +291,19 @@ def apply_settings(settings: dict, *, persist: bool = True) -> dict:
|
||||
未知字段会被忽略,避免后台响应意外覆盖模块中的其他全局变量。
|
||||
返回实际应用的字段,便于同步客户端记录版本与诊断信息。
|
||||
"""
|
||||
global AI_SYSTEM_PROMPT
|
||||
global AI_AGENT_NAME, AI_CLOUD_AGENT_NAME, AI_SYSTEM_PROMPT
|
||||
if not isinstance(settings, dict):
|
||||
raise TypeError("AI 配置必须是 JSON 对象")
|
||||
g = globals()
|
||||
applied = {}
|
||||
for key in CONFIGURABLE_KEYS:
|
||||
if key in settings:
|
||||
g[key] = settings[key]
|
||||
if key == "AI_AGENT_NAME":
|
||||
AI_CLOUD_AGENT_NAME = str(settings[key] or "").strip() or "客服"
|
||||
else:
|
||||
g[key] = settings[key]
|
||||
applied[key] = settings[key]
|
||||
AI_AGENT_NAME = AI_LOCAL_AGENT_NAME or AI_CLOUD_AGENT_NAME
|
||||
AI_SYSTEM_PROMPT = build_system_prompt()
|
||||
if persist:
|
||||
save_settings()
|
||||
@@ -253,19 +312,23 @@ def apply_settings(settings: dict, *, persist: bool = True) -> dict:
|
||||
|
||||
def load_settings():
|
||||
"""从 ai_settings.json 加载已保存的配置,覆盖本文件中的默认值。"""
|
||||
global AI_SYSTEM_PROMPT
|
||||
if not os.path.exists(_SETTINGS_FILE):
|
||||
return
|
||||
try:
|
||||
with open(_SETTINGS_FILE, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except Exception:
|
||||
return
|
||||
global AI_AGENT_NAME, AI_CLOUD_AGENT_NAME, AI_LOCAL_AGENT_NAME, AI_SYSTEM_PROMPT
|
||||
data = {}
|
||||
if os.path.exists(_SETTINGS_FILE):
|
||||
try:
|
||||
with open(_SETTINGS_FILE, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except Exception:
|
||||
data = {}
|
||||
g = globals()
|
||||
for k in CONFIGURABLE_KEYS:
|
||||
if k in data:
|
||||
g[k] = data[k]
|
||||
# 昵称/医院名可能被覆盖,重新渲染提示词
|
||||
if isinstance(data, dict):
|
||||
for k in CONFIGURABLE_KEYS:
|
||||
if k in data:
|
||||
g[k] = data[k]
|
||||
AI_CLOUD_AGENT_NAME = str(AI_AGENT_NAME or "").strip() or "客服"
|
||||
AI_LOCAL_AGENT_NAME = _read_agent_name_override()
|
||||
AI_AGENT_NAME = AI_LOCAL_AGENT_NAME or AI_CLOUD_AGENT_NAME
|
||||
# 云端昵称、本地昵称或医院名可能被覆盖,重新渲染提示词
|
||||
AI_SYSTEM_PROMPT = build_system_prompt()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user