diff --git a/backend/app/api/graph.py b/backend/app/api/graph.py index 12ff1ba2..7b3d8540 100644 --- a/backend/app/api/graph.py +++ b/backend/app/api/graph.py @@ -10,6 +10,7 @@ from . import graph_bp from ..config import Config +from ..utils.request_locale import get_request_locale from ..services.ontology_generator import OntologyGenerator from ..services.graph_builder import GraphBuilderService from ..services.text_processor import TextProcessor @@ -211,13 +212,15 @@ def generate_ontology(): ProjectManager.save_extracted_text(project.project_id, all_text) logger.info(f"文本提取完成,共 {len(all_text)} 字符") - # 生成本体 - logger.info("调用 LLM 生成本体定义...") + # 生成本体(传递用户语言,LLM 输出与该语言一致) + locale = get_request_locale() + logger.info(f"调用 LLM 生成本体定义... (locale={locale})") generator = OntologyGenerator() ontology = generator.generate( document_texts=document_texts, simulation_requirement=simulation_requirement, - additional_context=additional_context if additional_context else None + additional_context=additional_context if additional_context else None, + language=locale ) # 保存本体到项目 diff --git a/backend/app/api/report.py b/backend/app/api/report.py index e05c73c3..f3b055bf 100644 --- a/backend/app/api/report.py +++ b/backend/app/api/report.py @@ -10,6 +10,8 @@ from . import report_bp from ..config import Config +from ..utils.request_locale import get_request_locale +from ..utils.error_messages import get_error_message from ..services.report_agent import ReportAgent, ReportManager, ReportStatus from ..services.simulation_manager import SimulationManager from ..models.project import ProjectManager @@ -105,6 +107,9 @@ def generate_report(): "error": "缺少模拟需求描述" }), 400 + # 获取用户语言偏好(在启动线程前捕获,线程内 request 可能不可用) + report_language = get_request_locale() + # 提前生成 report_id,以便立即返回给前端 import uuid report_id = f"report_{uuid.uuid4().hex[:12]}" @@ -130,11 +135,12 @@ def run_generate(): message="初始化Report Agent..." ) - # 创建Report Agent + # 创建Report Agent(传入用户语言,报告和对话均使用该语言) agent = ReportAgent( graph_id=graph_id, simulation_id=simulation_id, - simulation_requirement=simulation_requirement + simulation_requirement=simulation_requirement, + report_language=report_language ) # 进度回调 @@ -498,16 +504,17 @@ def chat_with_report_agent(): message = data.get('message') chat_history = data.get('chat_history', []) + report_lang = get_request_locale() if not simulation_id: return jsonify({ "success": False, - "error": "请提供 simulation_id" + "error": get_error_message('missing_simulation_id', report_lang) }), 400 - + if not message: return jsonify({ "success": False, - "error": "请提供 message" + "error": get_error_message('missing_message', report_lang) }), 400 # 获取模拟和项目信息 @@ -517,30 +524,32 @@ def chat_with_report_agent(): if not state: return jsonify({ "success": False, - "error": f"模拟不存在: {simulation_id}" + "error": f"{get_error_message('simulation_not_found', report_lang)}: {simulation_id}" }), 404 - + project = ProjectManager.get_project(state.project_id) if not project: return jsonify({ "success": False, - "error": f"项目不存在: {state.project_id}" + "error": f"{get_error_message('project_not_found', report_lang)}: {state.project_id}" }), 404 - + graph_id = state.graph_id or project.graph_id if not graph_id: return jsonify({ "success": False, - "error": "缺少图谱ID" + "error": get_error_message('missing_graph_id', report_lang) }), 400 simulation_requirement = project.simulation_requirement or "" + report_language = report_lang - # 创建Agent并进行对话 + # 创建Agent并进行对话(使用用户选择的语言) agent = ReportAgent( graph_id=graph_id, simulation_id=simulation_id, - simulation_requirement=simulation_requirement + simulation_requirement=simulation_requirement, + report_language=report_language ) result = agent.chat(message=message, chat_history=chat_history) diff --git a/backend/app/services/ontology_generator.py b/backend/app/services/ontology_generator.py index 2d3e39bd..f5ba9558 100644 --- a/backend/app/services/ontology_generator.py +++ b/backend/app/services/ontology_generator.py @@ -168,7 +168,8 @@ def generate( self, document_texts: List[str], simulation_requirement: str, - additional_context: Optional[str] = None + additional_context: Optional[str] = None, + language: str = 'zh' ) -> Dict[str, Any]: """ 生成本体定义 @@ -177,10 +178,20 @@ def generate( document_texts: 文档文本列表 simulation_requirement: 模拟需求描述 additional_context: 额外上下文 + language: 输出语言 ('zh' 或 'en'),用于 analysis_summary 等字段 Returns: 本体定义(entity_types, edge_types等) """ + self._language = language + # 语言指令:让 LLM 的 analysis_summary 等自然语言输出与用户语言一致 + lang_instruction = ( + "\n\n**语言要求**:analysis_summary 必须使用中文撰写。" + if language == 'zh' else + "\n\n**Language requirement**: You MUST write analysis_summary in English." + ) + system_prompt = ONTOLOGY_SYSTEM_PROMPT + lang_instruction + # 构建用户消息 user_message = self._build_user_message( document_texts, @@ -189,7 +200,7 @@ def generate( ) messages = [ - {"role": "system", "content": ONTOLOGY_SYSTEM_PROMPT}, + {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ] diff --git a/backend/app/services/report_agent.py b/backend/app/services/report_agent.py index 02ca5bdc..dace1b19 100644 --- a/backend/app/services/report_agent.py +++ b/backend/app/services/report_agent.py @@ -587,6 +587,10 @@ def to_dict(self) -> Dict[str, Any]: 注意:sections数组最少2个,最多5个元素!""" +# 语言指令(根据 report_language 动态追加到各 prompt) +LANGUAGE_INSTRUCTION_ZH = "\n\n【语言要求】报告标题、摘要、章节标题和内容描述必须全部使用中文撰写。" +LANGUAGE_INSTRUCTION_EN = "\n\n【Language requirement】You MUST write the report title, summary, section titles and all content in English only. Think and respond in English." + PLAN_USER_PROMPT_TEMPLATE = """\ 【预测场景设定】 我们向模拟世界注入的变量(模拟需求):{simulation_requirement} @@ -653,8 +657,7 @@ def to_dict(self) -> Dict[str, Any]: 3. 【语言一致性 - 引用内容必须翻译为报告语言】 - 工具返回的内容可能包含英文或中英文混杂的表述 - - 如果模拟需求和材料原文是中文的,报告必须全部使用中文撰写 - - 当你引用工具返回的英文或中英混杂内容时,必须将其翻译为流畅的中文后再写入报告 + - {language_consistency_rule} - 翻译时保持原意不变,确保表述自然通顺 - 这一规则同时适用于正文和引用块(> 格式)中的内容 @@ -851,7 +854,9 @@ def to_dict(self) -> Dict[str, Any]: 【回答风格】 - 简洁直接,不要长篇大论 - 使用 > 格式引用关键内容 -- 优先给出结论,再解释原因""" +- 优先给出结论,再解释原因 + +【语言】{chat_language_instruction}""" CHAT_OBSERVATION_SUFFIX = "\n\n请简洁回答问题。" @@ -886,7 +891,8 @@ def __init__( simulation_id: str, simulation_requirement: str, llm_client: Optional[LLMClient] = None, - zep_tools: Optional[ZepToolsService] = None + zep_tools: Optional[ZepToolsService] = None, + report_language: str = 'zh' ): """ 初始化Report Agent @@ -897,10 +903,12 @@ def __init__( simulation_requirement: 模拟需求描述 llm_client: LLM客户端(可选) zep_tools: Zep工具服务(可选) + report_language: 报告输出语言 'zh' 或 'en',LLM 将用该语言思考和生成 """ self.graph_id = graph_id self.simulation_id = simulation_id self.simulation_requirement = simulation_requirement + self.report_language = report_language if report_language in ('zh', 'en') else 'zh' self.llm = llm_client or LLMClient() self.zep_tools = zep_tools or ZepToolsService() @@ -1162,7 +1170,8 @@ def plan_outline( if progress_callback: progress_callback("planning", 30, "正在生成报告大纲...") - system_prompt = PLAN_SYSTEM_PROMPT + lang_inst = LANGUAGE_INSTRUCTION_ZH if self.report_language == 'zh' else LANGUAGE_INSTRUCTION_EN + system_prompt = PLAN_SYSTEM_PROMPT + lang_inst user_prompt = PLAN_USER_PROMPT_TEMPLATE.format( simulation_requirement=self.simulation_requirement, total_nodes=context.get('graph_statistics', {}).get('total_nodes', 0), @@ -1251,9 +1260,15 @@ def _generate_section_react( if self.report_logger: self.report_logger.log_section_start(section.title, section_index) + lang_rule = ( + "报告必须全部使用中文撰写。当你引用工具返回的英文或中英混杂内容时,必须将其翻译为流畅的中文后再写入报告。" + if self.report_language == 'zh' else + "The report MUST be written entirely in English. When you quote content from tools that is in Chinese or mixed language, translate it into fluent English before including it in the report." + ) system_prompt = SECTION_SYSTEM_PROMPT_TEMPLATE.format( report_title=outline.title, report_summary=outline.summary, + language_consistency_rule=lang_rule, simulation_requirement=self.simulation_requirement, section_title=section.title, tools_description=self._get_tools_description(), @@ -1800,10 +1815,12 @@ def chat( except Exception as e: logger.warning(f"获取报告内容失败: {e}") + chat_lang = "回复必须使用中文。" if self.report_language == 'zh' else "You MUST respond in English only." system_prompt = CHAT_SYSTEM_PROMPT_TEMPLATE.format( simulation_requirement=self.simulation_requirement, report_content=report_content if report_content else "(暂无报告)", tools_description=self._get_tools_description(), + chat_language_instruction=chat_lang, ) # 构建消息 diff --git a/backend/app/utils/error_messages.py b/backend/app/utils/error_messages.py new file mode 100644 index 00000000..857c683e --- /dev/null +++ b/backend/app/utils/error_messages.py @@ -0,0 +1,26 @@ +""" +Error messages in zh/en for API responses. +""" + +MESSAGES = { + 'zh': { + 'missing_simulation_id': '请提供 simulation_id', + 'missing_message': '请提供 message', + 'simulation_not_found': '模拟不存在', + 'project_not_found': '项目不存在', + 'missing_graph_id': '缺少图谱ID', + }, + 'en': { + 'missing_simulation_id': 'Please provide simulation_id', + 'missing_message': 'Please provide message', + 'simulation_not_found': 'Simulation not found', + 'project_not_found': 'Project not found', + 'missing_graph_id': 'Missing graph ID', + }, +} + + +def get_error_message(key: str, locale: str = 'zh') -> str: + """Return localized error message. Fallback to zh if key missing for locale.""" + lang = 'en' if locale == 'en' else 'zh' + return MESSAGES.get(lang, MESSAGES['zh']).get(key, MESSAGES['zh'].get(key, str(key))) diff --git a/backend/app/utils/request_locale.py b/backend/app/utils/request_locale.py new file mode 100644 index 00000000..c0fa5892 --- /dev/null +++ b/backend/app/utils/request_locale.py @@ -0,0 +1,28 @@ +""" +从请求中获取用户选择的语言(用于 LLM 输出语言) +支持 X-Locale 请求头或 JSON body 中的 locale 参数 +""" +from flask import request + + +def get_request_locale(default: str = 'zh') -> str: + """ + 从当前请求中获取语言偏好。 + + - X-Locale: en 或 zh + - 或 request.get_json().get('locale') + - 或 request.form.get('locale') + + Returns: + 'en' 或 'zh' + """ + locale = request.headers.get('X-Locale', '').strip().lower() + if not locale and request.is_json: + data = request.get_json(silent=True) or {} + locale = (data.get('locale') or data.get('language') or '').strip().lower() + if not locale: + locale = (request.form.get('locale') or request.form.get('language') or '').strip().lower() + + if locale in ('en', 'zh'): + return locale + return default diff --git a/frontend/package.json b/frontend/package.json index f7e995a1..7e05e891 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "axios": "^1.13.2", "d3": "^7.9.0", "vue": "^3.5.24", + "vue-i18n": "^9.14.5", "vue-router": "^4.6.3" }, "devDependencies": { diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b7cd71ca..8ce882f8 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -3,7 +3,15 @@ diff --git a/frontend/src/components/Step1GraphBuild.vue b/frontend/src/components/Step1GraphBuild.vue index de33a3fd..83db7e92 100644 --- a/frontend/src/components/Step1GraphBuild.vue +++ b/frontend/src/components/Step1GraphBuild.vue @@ -6,25 +6,25 @@
01 - 本体生成 + {{ $t('step1.ontology') }}
- 已完成 - 生成中 - 等待 + {{ $t('step1.completed') }} + {{ $t('step1.processing') }} + {{ $t('step1.waiting') }}

POST /api/graph/ontology/generate

- LLM分析文档内容与模拟需求,提取出现实种子,自动生成合适的本体结构 + {{ $t('step1.desc') }}

- {{ ontologyProgress.message || '正在分析文档...' }} + {{ ontologyProgress.message || $t('step1.analyzing') }}
@@ -110,34 +110,34 @@
02 - GraphRAG构建 + {{ $t('step1.graphBuild') }}
- 已完成 + {{ $t('step1.completed') }} {{ buildProgress?.progress || 0 }}% - 等待 + {{ $t('step1.waiting') }}

POST /api/graph/build

- 基于生成的本体,将文档自动分块后调用 Zep 构建知识图谱,提取实体和关系,并形成时序记忆与社区摘要 + {{ $t('step1.desc2') }}

{{ graphStats.nodes }} - 实体节点 + {{ $t('step1.entityNodes') }}
{{ graphStats.edges }} - 关系边 + {{ $t('step1.relationEdges') }}
{{ graphStats.types }} - SCHEMA类型 + {{ $t('step1.schemaTypes') }}
@@ -148,23 +148,23 @@
03 - 构建完成 + {{ $t('step1.buildComplete') }}
- 进行中 + {{ $t('step1.inProgress') }}

POST /api/simulation/create

-

图谱构建已完成,请进入下一步进行模拟环境搭建

+

{{ $t('step1.descNext') }}

diff --git a/frontend/src/components/Step2EnvSetup.vue b/frontend/src/components/Step2EnvSetup.vue index eae776aa..b7d4bcaa 100644 --- a/frontend/src/components/Step2EnvSetup.vue +++ b/frontend/src/components/Step2EnvSetup.vue @@ -6,18 +6,18 @@
01 - 模拟实例初始化 + {{ $t('step2.simInstance') }}
- 已完成 - 初始化 + {{ $t('step1.completed') }} + {{ $t('step2.init') }}

POST /api/simulation/create

- 新建simulation实例,拉取模拟世界参数模版 + {{ $t('step2.desc1') }}

@@ -35,7 +35,7 @@
Task ID - {{ taskId || '异步任务已完成' }} + {{ taskId || $t('step2.taskDone') }}
@@ -46,41 +46,41 @@
02 - 生成 Agent 人设 + {{ $t('step2.agentProfiles') }}
- 已完成 + {{ $t('step1.completed') }} {{ prepareProgress }}% - 等待 + {{ $t('step1.waiting') }}

POST /api/simulation/prepare

- 结合上下文,自动调用工具从知识图谱梳理实体与关系,初始化模拟个体,并基于现实种子赋予他们独特的行为与记忆 + {{ $t('step2.desc2') }}

{{ profiles.length }} - 当前Agent数 + {{ $t('step2.agentCount') }}
{{ expectedTotal || '-' }} - 预期Agent总数 + {{ $t('step2.expectedAgents') }}
{{ totalTopicsCount }} - 现实种子当前关联话题数 + {{ $t('step2.topicCount') }}
- 已生成的 Agent 人设 + {{ $t('step2.generatedProfiles') }}
@{{ profile.name || `agent_${idx}` }}
- {{ profile.profession || '未知职业' }} + {{ profile.profession || $t('step2.unknownProfession') }}
-

{{ profile.bio || '暂无简介' }}

+

{{ profile.bio || $t('step2.noBio') }}

03 - 生成双平台模拟配置 + {{ $t('step2.dualPlatformConfig') }}
- 已完成 - 生成中 - 等待 + {{ $t('step1.completed') }} + {{ $t('step1.processing') }} + {{ $t('step1.waiting') }}

POST /api/simulation/prepare

- LLM 根据模拟需求与现实种子,智能设置世界时间流速、推荐算法、每个个体的活跃时间段、发言频率、事件触发等参数 + {{ $t('step2.desc3') }}

@@ -139,40 +139,40 @@
- 模拟时长 - {{ simulationConfig.time_config?.total_simulation_hours || '-' }} 小时 + {{ $t('step2.simDuration') }} + {{ simulationConfig.time_config?.total_simulation_hours || '-' }} {{ $t('step2.hours') }}
- 每轮时长 - {{ simulationConfig.time_config?.minutes_per_round || '-' }} 分钟 + {{ $t('step2.perRound') }} + {{ simulationConfig.time_config?.minutes_per_round || '-' }} {{ $t('step2.minutes') }}
- 总轮次 - {{ Math.floor((simulationConfig.time_config?.total_simulation_hours * 60 / simulationConfig.time_config?.minutes_per_round)) || '-' }} 轮 + {{ $t('step2.totalRounds') }} + {{ Math.floor((simulationConfig.time_config?.total_simulation_hours * 60 / simulationConfig.time_config?.minutes_per_round)) || '-' }} {{ $t('step2.rounds') }}
- 每小时活跃 + {{ $t('step2.activePerHour') }} {{ simulationConfig.time_config?.agents_per_hour_min }}-{{ simulationConfig.time_config?.agents_per_hour_max }}
- 高峰时段 + {{ $t('step2.peakPeriod') }} {{ simulationConfig.time_config?.peak_hours?.join(':00, ') }}:00 ×{{ simulationConfig.time_config?.peak_activity_multiplier }}
- 工作时段 + {{ $t('step2.workPeriod') }} {{ simulationConfig.time_config?.work_hours?.[0] }}:00-{{ simulationConfig.time_config?.work_hours?.slice(-1)[0] }}:00 ×{{ simulationConfig.time_config?.work_activity_multiplier }}
- 早间时段 + {{ $t('step2.morningPeriod') }} {{ simulationConfig.time_config?.morning_hours?.[0] }}:00-{{ simulationConfig.time_config?.morning_hours?.slice(-1)[0] }}:00 ×{{ simulationConfig.time_config?.morning_activity_multiplier }}
- 低谷时段 + {{ $t('step2.lowPeriod') }} {{ simulationConfig.time_config?.off_peak_hours?.[0] }}:00-{{ simulationConfig.time_config?.off_peak_hours?.slice(-1)[0] }}:00 ×{{ simulationConfig.time_config?.off_peak_activity_multiplier }}
@@ -182,8 +182,8 @@
- Agent 配置 - {{ simulationConfig.agent_configs?.length || 0 }} 个 + {{ $t('step2.agentConfig') }} + {{ simulationConfig.agent_configs?.length || 0 }}{{ $t('step2.agentsCount') }}
- 活跃时段 + {{ $t('step2.activeTime') }}
- 发帖/时 + {{ $t('step2.postsPerHour') }} {{ agent.posts_per_hour }}
- 评论/时 + {{ $t('step2.commentsPerHour') }} {{ agent.comments_per_hour }}
- 响应延迟 + {{ $t('step2.responseDelay') }} {{ agent.response_delay_min }}-{{ agent.response_delay_max }}min
- 活跃度 + {{ $t('step2.activityLevel') }} {{ (agent.activity_level * 100).toFixed(0) }}%
- 情感倾向 + {{ $t('step2.sentimentBias') }} {{ agent.sentiment_bias > 0 ? '+' : '' }}{{ agent.sentiment_bias?.toFixed(1) }}
- 影响力 + {{ $t('step2.influence') }} {{ agent.influence_weight?.toFixed(1) }}
@@ -267,59 +267,59 @@
- 推荐算法配置 + {{ $t('step2.recAlgoConfig') }}
- 平台 1:广场 / 信息流 + {{ $t('step2.platform1') }}
- 时效权重 + {{ $t('step2.recencyWeight') }} {{ simulationConfig.twitter_config.recency_weight }}
- 热度权重 + {{ $t('step2.popularityWeight') }} {{ simulationConfig.twitter_config.popularity_weight }}
- 相关性权重 + {{ $t('step2.relevanceWeight') }} {{ simulationConfig.twitter_config.relevance_weight }}
- 病毒阈值 + {{ $t('step2.viralThreshold') }} {{ simulationConfig.twitter_config.viral_threshold }}
- 回音室强度 + {{ $t('step2.echoChamber') }} {{ simulationConfig.twitter_config.echo_chamber_strength }}
- 平台 2:话题 / 社区 + {{ $t('step2.platform2') }}
- 时效权重 + {{ $t('step2.recencyWeight') }} {{ simulationConfig.reddit_config.recency_weight }}
- 热度权重 + {{ $t('step2.popularityWeight') }} {{ simulationConfig.reddit_config.popularity_weight }}
- 相关性权重 + {{ $t('step2.relevanceWeight') }} {{ simulationConfig.reddit_config.relevance_weight }}
- 病毒阈值 + {{ $t('step2.viralThreshold') }} {{ simulationConfig.reddit_config.viral_threshold }}
- 回音室强度 + {{ $t('step2.echoChamber') }} {{ simulationConfig.reddit_config.echo_chamber_strength }}
@@ -330,7 +330,7 @@
- LLM 配置推理 + {{ $t('step2.llmConfig') }}
04 - 初始激活编排 + {{ $t('step2.initialActivation') }}
- 已完成 - 编排中 - 等待 + {{ $t('step1.completed') }} + {{ $t('step2.arranging') }} + {{ $t('step1.waiting') }}

POST /api/simulation/prepare

- 基于叙事方向,自动生成初始激活事件与热点话题,引导模拟世界的初始状态 + {{ $t('step2.desc4') }}

@@ -380,14 +380,14 @@ - 叙事引导方向 + {{ $t('step2.narrativeDir') }}

{{ simulationConfig.event_config.narrative_direction }}

- 初始热点话题 + {{ $t('step2.hotTopics') }}
# {{ topic }} @@ -397,7 +397,7 @@
- 初始激活序列 ({{ simulationConfig.event_config.initial_posts.length }}) + {{ $t('step2.initialPosts') }} ({{ simulationConfig.event_config.initial_posts.length }})
@@ -423,29 +423,29 @@
05 - 准备完成 + {{ $t('step2.ready') }}
- 进行中 - 等待 + {{ $t('step1.inProgress') }} + {{ $t('step1.waiting') }}

POST /api/simulation/start

-

模拟环境已准备完成,可以开始运行模拟

+

{{ $t('step2.desc5') }}

- 模拟轮数设定 - MiroFish 自动规划推演现实 {{ simulationConfig?.time_config?.total_simulation_hours || '-' }} 小时,每轮代表现实 {{ simulationConfig?.time_config?.minutes_per_round || '-' }} 分钟时间流逝 + {{ $t('step2.roundsSetting') }} + {{ $t('step2.roundsDesc', { hours: simulationConfig?.time_config?.total_simulation_hours || '-', mins: simulationConfig?.time_config?.minutes_per_round || '-' }) }}
@@ -454,10 +454,10 @@
{{ customMaxRounds }} - + {{ $t('step2.rounds') }}
- 若Agent规模为100:预计耗时约 {{ Math.round(customMaxRounds * 0.6) }} 分钟 + {{ $t('step2.estTime', { n: Math.round(customMaxRounds * 0.6) }) }}
@@ -478,7 +478,7 @@ :class="{ active: customMaxRounds === 40 }" @click="customMaxRounds = 40" :style="{ position: 'absolute', left: `calc(${(40 - 10) / (autoGeneratedRounds - 10) * 100}% - 30px)` }" - >40 (推荐) + >40 ({{ $t('step2.recOver40') }}) {{ autoGeneratedRounds }}
@@ -488,7 +488,7 @@
{{ autoGeneratedRounds }} - + {{ $t('step2.rounds') }}
@@ -497,11 +497,11 @@ - 若Agent规模为100:预计耗时 {{ Math.round(autoGeneratedRounds * 0.6) }} 分钟 + {{ $t('step2.estTimeAuto', { n: Math.round(autoGeneratedRounds * 0.6) }) }}
-

若首次运行,强烈建议切换至‘自定义模式’减少模拟轮数,以便快速预览效果并降低报错风险 ➝

+

{{ $t('step2.customTip') }}

@@ -514,14 +514,14 @@ class="action-btn secondary" @click="$emit('go-back')" > - ← 返回图谱构建 + {{ $t('step2.backToGraph') }}
@@ -547,32 +547,32 @@