From 4fc1fa435ce48fdea20bf93592ec2b0923ff2a2b Mon Sep 17 00:00:00 2001 From: ulleo Date: Mon, 26 Jan 2026 17:19:04 +0800 Subject: [PATCH] feat: add context record count configuration to chat #773 --- backend/apps/chat/task/llm.py | 54 ++++++++-- backend/common/core/config.py | 1 + frontend/src/i18n/en.json | 2 + frontend/src/i18n/ko-KR.json | 2 + frontend/src/i18n/zh-CN.json | 2 + frontend/src/views/system/parameter/index.vue | 98 ++++++++++++------- 6 files changed, 115 insertions(+), 44 deletions(-) diff --git a/backend/apps/chat/task/llm.py b/backend/apps/chat/task/llm.py index dfceaf2c..d5b6cb5f 100644 --- a/backend/apps/chat/task/llm.py +++ b/backend/apps/chat/task/llm.py @@ -54,8 +54,6 @@ warnings.filterwarnings("ignore") -base_message_count_limit = 6 - executor = ThreadPoolExecutor(max_workers=200) dynamic_ds_types = [1, 3] @@ -95,6 +93,7 @@ class LLMService: articles_number: int = 4 enable_sql_row_limit: bool = settings.GENERATE_SQL_QUERY_LIMIT_ENABLED + base_message_round_count_limit: int = settings.GENERATE_SQL_QUERY_HISTORY_ROUND_COUNT def __init__(self, session: Session, current_user: CurrentUser, chat_question: ChatQuestion, current_assistant: Optional[CurrentAssistant] = None, no_reasoning: bool = False, @@ -185,6 +184,14 @@ async def create(cls, *args, **kwargs): instance.enable_sql_row_limit = True else: instance.enable_sql_row_limit = False + if config.pkey == 'chat.context_record_count': + count_value = config.pval + if count_value is None: + count_value = settings.GENERATE_SQL_QUERY_HISTORY_ROUND_COUNT + count_value = int(count_value) + if count_value < 0: + count_value = 0 + instance.base_message_round_count_limit = count_value return instance def is_running(self, timeout=0.5): @@ -206,22 +213,23 @@ def init_messages(self): filter(lambda obj: obj.pid == self.chat_question.regenerate_record_id, self.generate_sql_logs), None) last_sql_messages: List[dict[str, Any]] = _temp_log.messages if _temp_log else [] - # todo maybe can configure - count_limit = 0 - base_message_count_limit + count_limit = self.base_message_round_count_limit self.sql_message = [] # add sys prompt self.sql_message.append(SystemMessage( content=self.chat_question.sql_sys_question(self.ds.type, self.enable_sql_row_limit))) if last_sql_messages is not None and len(last_sql_messages) > 0: - # limit count - for last_sql_message in last_sql_messages[count_limit:]: + # 获取最后3轮对话 + last_rounds = get_last_conversation_rounds(last_sql_messages, rounds=count_limit) + + for _msg_dict in last_rounds: _msg: BaseMessage - if last_sql_message['type'] == 'human': - _msg = HumanMessage(content=last_sql_message['content']) + if _msg_dict.get('type') == 'human': + _msg = HumanMessage(content=_msg_dict.get('content')) self.sql_message.append(_msg) - elif last_sql_message['type'] == 'ai': - _msg = AIMessage(content=last_sql_message['content']) + elif _msg_dict.get('type') == 'ai': + _msg = AIMessage(content=_msg_dict.get('content')) self.sql_message.append(_msg) last_chart_messages: List[dict[str, Any]] = self.generate_chart_logs[-1].messages if len( @@ -1666,3 +1674,29 @@ def get_lang_name(lang: str): if normalized.startswith('ko'): return '韩语' return '简体中文' + + +def get_last_conversation_rounds(messages, rounds=settings.GENERATE_SQL_QUERY_HISTORY_ROUND_COUNT): + """获取最后N轮对话,处理不完整对话的情况""" + if not messages or rounds <= 0: + return [] + + # 找到所有用户消息的位置 + human_indices = [] + for index, msg in enumerate(messages): + if msg.get('type') == 'human': + human_indices.append(index) + + # 如果没有用户消息,返回空 + if not human_indices: + return [] + + # 计算从哪个索引开始 + if len(human_indices) <= rounds: + # 如果用户消息数少于等于需要的轮数,从第一个用户消息开始 + start_index = human_indices[0] + else: + # 否则,从倒数第N个用户消息开始 + start_index = human_indices[-rounds] + + return messages[start_index:] diff --git a/backend/common/core/config.py b/backend/common/core/config.py index e3fa3614..1b3cc24e 100644 --- a/backend/common/core/config.py +++ b/backend/common/core/config.py @@ -109,6 +109,7 @@ def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn | str: # 是否启用SQL查询行数限制,默认值,可被参数配置覆盖 GENERATE_SQL_QUERY_LIMIT_ENABLED: bool = True + GENERATE_SQL_QUERY_HISTORY_ROUND_COUNT: int = 3 PARSE_REASONING_BLOCK_ENABLED: bool = True DEFAULT_REASONING_CONTENT_START: str = '' diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 16f89f71..9a43c0f1 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -13,6 +13,8 @@ "memo": "Memo update rules: Match table name and field name. If a match is found, update the table memo and field memo; otherwise, leave the memo unchanged.", "parameter_configuration": "Parameter Config", "question_count_settings": "Question Count Settings", + "context_record_count": "Context Record Count", + "context_record_count_hint": "Number of user question rounds", "model_thinking_process": "Expand Model Thinking Process", "rows_of_data": "Limit 1000 Rows of Data", "third_party_platform_settings": "Third-Party Platform Settings", diff --git a/frontend/src/i18n/ko-KR.json b/frontend/src/i18n/ko-KR.json index 2dae6eac..b080da79 100644 --- a/frontend/src/i18n/ko-KR.json +++ b/frontend/src/i18n/ko-KR.json @@ -13,6 +13,8 @@ "memo": "메모 업데이트 규칙: 테이블 이름과 필드 이름이 일치하는지 확인합니다. 일치하는 항목이 있으면 테이블 메모와 필드 메모를 업데이트하고, 그렇지 않으면 메모를 변경하지 않고 그대로 둡니다.", "parameter_configuration": "매개변수 구성", "question_count_settings": "질문 수 설정", + "context_record_count": "컨텍스트 기록 수", + "context_record_count_hint": "사용자 질문 라운드 수", "model_thinking_process": "모델 사고 프로세스 확장", "rows_of_data": "데이터 1,000행 제한", "third_party_platform_settings": "타사 플랫폼 설정", diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json index 5996ee94..fc73a5b7 100644 --- a/frontend/src/i18n/zh-CN.json +++ b/frontend/src/i18n/zh-CN.json @@ -13,6 +13,8 @@ "memo": "备注更新规则:根据表名和字段名匹配,如果匹配则更新表备注和字段备注;如果匹配不上,备注保持不变。", "parameter_configuration": "参数配置", "question_count_settings": "问数设置", + "context_record_count": "上下文记录数", + "context_record_count_hint": "用户提问轮数", "model_thinking_process": "展开模型思考过程", "rows_of_data": "限制 1000 行数据", "third_party_platform_settings": "第三方平台设置", diff --git a/frontend/src/views/system/parameter/index.vue b/frontend/src/views/system/parameter/index.vue index 6e7cc9c3..e341feb1 100644 --- a/frontend/src/views/system/parameter/index.vue +++ b/frontend/src/views/system/parameter/index.vue @@ -31,6 +31,13 @@ const loadData = () => { }) } +const onContextRecordCountChange = (count: number) => { + if (count < 0) { + state.parameterForm['chat.context_record_count'] = 0 + } + state.parameterForm['chat.context_record_count'] = Math.floor(count) +} + const beforeChange = (): Promise => { return new Promise((resolve) => { if (!state.parameterForm['chat.rows_of_data']) { @@ -88,41 +95,66 @@ onMounted(() => {
{{ t('parameter.question_count_settings') }}
-
-
- {{ t('parameter.model_thinking_process') }} + +
+
+ {{ t('parameter.model_thinking_process') }} - - - - - + + + + + +
+
+ +
-
- +
+
+ {{ t('parameter.rows_of_data') }} + + + + + +
+
+ +
-
- -
-
- {{ t('parameter.rows_of_data') }} - - - - - -
-
- + + +
+
+ {{ t('parameter.context_record_count') }} + + + + + +
+
+ +
-
+
@@ -152,7 +184,6 @@ onMounted(() => { } .title { font-weight: 500; - font-style: Medium; font-size: 20px; line-height: 28px; margin-bottom: 16px; @@ -164,12 +195,11 @@ onMounted(() => { padding: 16px; border: 1px solid #dee0e3; display: flex; - flex-wrap: wrap; + flex-direction: column; margin-top: 16px; .card-title { font-weight: 500; - font-style: Medium; font-size: 16px; line-height: 24px; width: 100%;