refactor(api): TOML 配置 SSOT、统一错误契约、Auth/事务加固与可观测性 (#33)

配置 SSOT(TOML + .env)
统一错误契约
Auth 与事务边界
Redis / Celery 可靠性:业务 Redis(DB/0)与 Celery broker/backend(DB/1)显式拆分;连接池、sync client
可观测性(OpenTelemetry + LGTM)
This commit is contained in:
Sully
2026-05-22 13:44:50 +08:00
committed by GitHub
parent f09ae248f9
commit 53e0065e3e
298 changed files with 15247 additions and 4344 deletions

View File

@@ -48,6 +48,9 @@ from app.core.config import settings
from app.core.llm_gateway import LlmGateway, LlmUseCase
from app.core.logging import get_logger
from app.features.conversation.input_normalize import normalize_chat_input_for_agent
from app.core.runtime_constants import agent_log_defaults
from app.features.conversation.constants import chat
from app.features.story.constants import story
logger = get_logger(__name__)
@@ -171,8 +174,8 @@ class InterviewAgent:
if normalized_user_message is not None:
return (normalized_user_message or "").strip()
llm_n = None
if settings.chat_input_normalize_enabled and (
(settings.chat_input_normalize_mode or "").strip().lower() == "llm"
if chat.input_normalize_enabled and (
(chat.input_normalize_mode or "").strip().lower() == "llm"
):
llm_n = self.llm
return normalize_chat_input_for_agent(user_message or "", llm=llm_n)
@@ -218,16 +221,16 @@ class InterviewAgent:
du = self._detect_user_stage(text_for_model)
hw = await get_history_with_window(
conversation_id,
max_pairs=settings.chat_history_max_pairs,
max_chars=settings.chat_history_max_chars,
max_pairs=chat.history_max_pairs,
max_chars=chat.history_max_chars,
)
recent_questions = extract_recent_questions(hw.window)
conversation_turn_total = hw.turn_total
all_stages_coverage = narrative_state.all_stages_coverage()
persona = normalize_interview_persona(settings.chat_interview_persona)
max_segments = int(settings.chat_interview_max_segments)
max_tokens = int(settings.chat_interview_max_tokens)
max_chars = int(settings.chat_interview_max_chars_per_segment)
persona = normalize_interview_persona(chat.interview_persona)
max_segments = int(chat.interview_max_segments)
max_tokens = int(chat.interview_max_tokens)
max_chars = int(chat.interview_max_chars_per_segment)
turn_plan = plan_interview_turn(
current_stage=memoir_state.current_stage,
@@ -246,7 +249,7 @@ class InterviewAgent:
reply_planner_raw = ""
baseline_mode = turn_plan.mode
baseline_primary_focus = turn_plan.primary_focus
if settings.chat_reply_planner_llm_enabled:
if chat.reply_planner_llm_enabled:
rq_preview = (
"\n".join(recent_questions[-4:])
if recent_questions
@@ -258,8 +261,8 @@ class InterviewAgent:
text_for_model=text_for_model,
memory_evidence_text=(memory_planner_text or memory_evidence_text)
or "",
max_tokens=int(settings.chat_reply_planner_max_tokens),
temperature=float(settings.chat_reply_planner_temperature),
max_tokens=int(chat.reply_planner_max_tokens),
temperature=float(chat.reply_planner_temperature),
scene_cues_for_planner=scene_cues_for_planner or [],
recent_questions_preview=rq_preview,
)
@@ -310,12 +313,12 @@ class InterviewAgent:
"InterviewAgent.generate_response.prompt",
format_history_string(
messages,
omit_system_body=settings.agent_log_omit_system_message_body,
omit_system_body=agent_log_defaults.omit_system_message_body,
),
)
chat_llm = self.llm.bind(
max_tokens=max_tokens,
temperature=float(settings.chat_interview_temperature),
temperature=float(chat.interview_temperature),
)
prompt_chars = _message_contents_char_count(messages)
llm_t0 = time.perf_counter()
@@ -377,7 +380,7 @@ class InterviewAgent:
"InterviewAgent.generate_response.retry_prompt",
format_history_string(
retry_messages,
omit_system_body=settings.agent_log_omit_system_message_body,
omit_system_body=agent_log_defaults.omit_system_message_body,
),
)
llm_t1 = time.perf_counter()
@@ -445,7 +448,7 @@ class InterviewAgent:
"duplicate_question_guard_llm_retry": retry_used,
"autobiographical_boundary_guard_triggered": auto_bio,
"reply_planner_llm_used": bool(
settings.chat_reply_planner_llm_enabled
chat.reply_planner_llm_enabled
and (reply_planner_raw or "").strip()
),
"reply_planner_raw_preview": (reply_planner_raw or "")[:800],
@@ -483,7 +486,7 @@ class InterviewAgent:
)
slot_table = SLOT_NAME_MAP_EN if language == "en" else SLOT_NAME_MAP
empty_slots_readable = [slot_table.get(s, s) for s in empty_slots]
persona = normalize_interview_persona(settings.chat_interview_persona)
persona = normalize_interview_persona(chat.interview_persona)
prompt = get_opening_prompt(
current_stage=memoir_state.current_stage,
empty_slots_readable=empty_slots_readable,
@@ -497,8 +500,8 @@ class InterviewAgent:
)
hw = await get_history_with_window(
conversation_id,
max_pairs=settings.chat_history_max_pairs,
max_chars=settings.chat_history_max_chars,
max_pairs=chat.history_max_pairs,
max_chars=chat.history_max_chars,
)
messages: List[Any] = [SystemMessage(content=prompt)]
messages.extend(hw.window)
@@ -520,12 +523,12 @@ class InterviewAgent:
"InterviewAgent.opening.prompt",
format_history_string(
messages,
omit_system_body=settings.agent_log_omit_system_message_body,
omit_system_body=agent_log_defaults.omit_system_message_body,
),
)
opening_llm = self.llm.bind(
max_tokens=settings.chat_opening_max_tokens,
temperature=float(settings.chat_interview_temperature),
max_tokens=chat.opening_max_tokens,
temperature=float(chat.interview_temperature),
)
prompt_chars = _message_contents_char_count(messages)
llm_t0 = time.perf_counter()
@@ -564,7 +567,7 @@ class InterviewAgent:
raw_list = segments_from_llm_response(response_text, max_segments=2)
if not raw_list:
raw_list = [response_text.strip()]
max_chars = int(settings.chat_interview_max_chars_per_segment)
max_chars = int(chat.interview_max_chars_per_segment)
out = truncate_chat_segments(
raw_list,
max_segments=2,
@@ -612,7 +615,7 @@ class InterviewAgent:
)
slot_table = SLOT_NAME_MAP_EN if language == "en" else SLOT_NAME_MAP
empty_slots_readable = [slot_table.get(s, s) for s in empty_slots]
persona = normalize_interview_persona(settings.chat_interview_persona)
persona = normalize_interview_persona(chat.interview_persona)
prompt = get_re_greeting_prompt(
current_stage=memoir_state.current_stage,
empty_slots_readable=empty_slots_readable,
@@ -627,8 +630,8 @@ class InterviewAgent:
)
hw = await get_history_with_window(
conversation_id,
max_pairs=settings.chat_history_max_pairs,
max_chars=settings.chat_history_max_chars,
max_pairs=chat.history_max_pairs,
max_chars=chat.history_max_chars,
)
messages: List[Any] = [SystemMessage(content=prompt)]
messages.extend(hw.window)
@@ -647,12 +650,12 @@ class InterviewAgent:
"InterviewAgent.re_greeting.prompt",
format_history_string(
messages,
omit_system_body=settings.agent_log_omit_system_message_body,
omit_system_body=agent_log_defaults.omit_system_message_body,
),
)
re_greet_llm = self.llm.bind(
max_tokens=settings.chat_opening_max_tokens,
temperature=float(settings.chat_interview_temperature),
max_tokens=chat.opening_max_tokens,
temperature=float(chat.interview_temperature),
)
llm_t0 = time.perf_counter()
with agent_span(
@@ -691,7 +694,7 @@ class InterviewAgent:
raw_list = segments_from_llm_response(response_text, max_segments=2)
if not raw_list:
raw_list = [response_text.strip()]
max_chars = int(settings.chat_interview_max_chars_per_segment)
max_chars = int(chat.interview_max_chars_per_segment)
out = truncate_chat_segments(
raw_list,
max_segments=2,