chore(task): archive 04-28-feat-points-ledger
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
# IMPLEMENTATION_PLAN:统一语言设置
|
||||
|
||||
## 前置条件
|
||||
|
||||
| 条件 | 状态 |
|
||||
|------|------|
|
||||
| 后端 Schema 定义清晰 | ✅ |
|
||||
| 前端 Model 定义清晰 | ✅ |
|
||||
| AI 运行时使用 `ai_language` | ✅ |
|
||||
| 协议文档存在 | ✅ |
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### Step 1: 更新协议文档
|
||||
|
||||
**文件**: `docs/protocols/profile/profile-protocol.md`
|
||||
|
||||
- 将 `interface_language` 和 `ai_language` 合并为 `language`
|
||||
- 更新示例 JSON
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 后端 Schema
|
||||
|
||||
**文件**: `backend/src/schemas/shared/user.py`
|
||||
|
||||
```python
|
||||
class PreferenceSettings(BaseModel):
|
||||
language: str = "zh-CN"
|
||||
timezone: str = "Asia/Shanghai"
|
||||
country: str = "US"
|
||||
|
||||
@field_validator("language")
|
||||
@classmethod
|
||||
def validate_language(cls, value: str) -> str:
|
||||
if not _BCP47_PATTERN.fullmatch(value):
|
||||
raise ValueError("language must be a valid BCP-47 tag")
|
||||
return value
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 后端 AI 运行时
|
||||
|
||||
**文件**: `backend/src/core/agentscope/runtime/runner.py`
|
||||
|
||||
```python
|
||||
# 第 268-276 行
|
||||
language = "zh-CN"
|
||||
if user_context.settings is not None:
|
||||
prefs = getattr(user_context.settings, "preferences", None)
|
||||
if prefs is not None:
|
||||
language = getattr(prefs, "language", "zh-CN") or "zh-CN"
|
||||
|
||||
system_prompt = build_system_prompt(
|
||||
agent_type=stage_config.agent_type,
|
||||
language=language,
|
||||
llm_config=stage_config.llm_config,
|
||||
tools=None,
|
||||
now_utc=datetime.now(timezone.utc),
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: 后端 Prompt 构建
|
||||
|
||||
**文件**: `backend/src/core/agentscope/prompts/system_prompt.py`
|
||||
|
||||
```python
|
||||
def _build_output_rules(*, language: str) -> str:
|
||||
lang_label = _get_language_label(language)
|
||||
...
|
||||
|
||||
def build_system_prompt(
|
||||
*,
|
||||
agent_type: AgentType,
|
||||
language: str,
|
||||
llm_config: LlmConfig,
|
||||
tools: list[ToolSchema] | None,
|
||||
now_utc: datetime,
|
||||
) -> str:
|
||||
...
|
||||
_build_output_rules(language=language),
|
||||
```
|
||||
|
||||
**文件**: `backend/src/core/agentscope/prompts/worker_rules.py`
|
||||
|
||||
```python
|
||||
def get_worker_role_playing(language: str) -> str:
|
||||
_ = language
|
||||
...
|
||||
|
||||
def get_worker_output_rules(language: str) -> str:
|
||||
if language.startswith("en"):
|
||||
...
|
||||
```
|
||||
|
||||
**文件**: `backend/src/core/agentscope/prompts/agent_prompt.py`
|
||||
|
||||
```python
|
||||
def build_agent_prompt(
|
||||
*,
|
||||
language: str = "zh-CN",
|
||||
) -> str:
|
||||
role_playing = get_worker_role_playing(language)
|
||||
output_rules = get_worker_output_rules(language)
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: 后端测试
|
||||
|
||||
**文件**: `backend/tests/unit/test_parse_profile_settings.py`
|
||||
|
||||
- 字段名 `ai_language` → `language`
|
||||
- 字段名 `interface_language` → `language`
|
||||
|
||||
**文件**: `backend/tests/unit/test_agentscope_prompts.py`
|
||||
|
||||
- 参数名 `ai_language` → `language`
|
||||
|
||||
---
|
||||
|
||||
### Step 6: 数据库数据更新
|
||||
|
||||
直接用 Supabase MCP 执行 SQL(无需迁移脚本):
|
||||
|
||||
```sql
|
||||
UPDATE profiles
|
||||
SET settings = jsonb_set(
|
||||
settings - 'interface_language' - 'ai_language',
|
||||
'{preferences,language}',
|
||||
COALESCE(
|
||||
settings->'preferences'->>'interface_language',
|
||||
settings->'preferences'->>'ai_language',
|
||||
'"zh-CN"'
|
||||
)::jsonb
|
||||
)
|
||||
WHERE settings->'preferences' ?| array['interface_language', 'ai_language'];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 7: 前端 Model
|
||||
|
||||
**文件**: `apps/lib/features/settings/data/models/profile_settings.dart`
|
||||
|
||||
```dart
|
||||
class PreferenceSettings {
|
||||
const PreferenceSettings({
|
||||
this.language = 'zh-CN',
|
||||
this.timezone = 'Asia/Shanghai',
|
||||
this.country = 'US',
|
||||
});
|
||||
|
||||
final String language;
|
||||
final String timezone;
|
||||
final String country;
|
||||
|
||||
PreferenceSettings copyWith({
|
||||
String? language,
|
||||
String? timezone,
|
||||
String? country,
|
||||
}) {
|
||||
return PreferenceSettings(
|
||||
language: language ?? this.language,
|
||||
timezone: timezone ?? this.timezone,
|
||||
country: country ?? this.country,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 defaultsForLocale
|
||||
factory ProfileSettingsV1.defaultsForLocale(Locale locale) {
|
||||
final tag = languageTagFromLocale(locale);
|
||||
return ProfileSettingsV1(
|
||||
preferences: PreferenceSettings(language: tag),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 8: 前端 API
|
||||
|
||||
**文件**: `apps/lib/features/settings/data/apis/profile_api.dart`
|
||||
|
||||
```dart
|
||||
// 序列化 (第 45 行)
|
||||
'language': settings.preferences.language,
|
||||
|
||||
// 反序列化 (第 114 行)
|
||||
language: (preferencesRaw['language'] as String?) ?? 'zh-CN',
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 9: 前端设置界面
|
||||
|
||||
**文件**: `apps/lib/features/settings/presentation/screens/general_settings_screen.dart`
|
||||
|
||||
移除第 75-95 行的 AI 语言 `SettingsMenuTile`,修改剩余的语言选项:
|
||||
|
||||
```dart
|
||||
SettingsMenuTile(
|
||||
icon: Icons.language_rounded,
|
||||
title: l10n.settingsLanguage,
|
||||
subtitle: displayLanguageLabel(
|
||||
l10n,
|
||||
_settings.preferences.language,
|
||||
),
|
||||
tint: colors.primary,
|
||||
background: colors.surfaceContainerHighest,
|
||||
showDivider: false,
|
||||
onTap: () => _selectLanguage(
|
||||
_settings.preferences.language,
|
||||
(lang) => setState(() {
|
||||
_settings = _settings.copyWith(
|
||||
preferences: _settings.preferences.copyWith(
|
||||
language: lang,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 10: 前端 i18n
|
||||
|
||||
**文件**: `apps/lib/l10n/app_zh.arb`
|
||||
|
||||
```diff
|
||||
- "settingsInterfaceLanguage": "界面语言",
|
||||
- "settingsAiLanguage": "AI回复语言",
|
||||
- "settingsAiLanguageHint": "该字段将对齐..."
|
||||
+ "settingsLanguage": "语言",
|
||||
```
|
||||
|
||||
**文件**: `apps/lib/l10n/app_en.arb`
|
||||
|
||||
```diff
|
||||
- "settingsInterfaceLanguage": "Interface Language",
|
||||
- "settingsAiLanguage": "AI Response Language",
|
||||
- "settingsAiLanguageHint": "This field will align..."
|
||||
+ "settingsLanguage": "Language",
|
||||
```
|
||||
|
||||
**文件**: `apps/lib/l10n/app_zh_hant.arb`
|
||||
|
||||
```diff
|
||||
- "settingsInterfaceLanguage": "介面語言",
|
||||
- "settingsAiLanguage": "AI 回覆語言",
|
||||
+ "settingsLanguage": "語言",
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 11: 重新生成 l10n
|
||||
|
||||
```bash
|
||||
cd apps && flutter gen-l10n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 12: 更新其他协议文档
|
||||
|
||||
**文件**: `docs/protocols/divination/divination-run-protocol.md`
|
||||
|
||||
第 240 行:
|
||||
```diff
|
||||
- - Language rule: `conclusion`, `focus_points`, `advice`, `keywords`, `answer` should follow user `ai_language` preference unless user explicitly requests otherwise.
|
||||
+ - Language rule: `conclusion`, `focus_points`, `advice`, `keywords`, `answer` should follow user `language` preference unless user explicitly requests otherwise.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
# 后端
|
||||
cd backend
|
||||
uv run pytest tests/unit/test_parse_profile_settings.py tests/unit/test_agentscope_prompts.py -v
|
||||
./infra/scripts/dev-migrate.sh migrate
|
||||
|
||||
# 前端
|
||||
cd apps
|
||||
flutter gen-l10n
|
||||
flutter analyze
|
||||
```
|
||||
Reference in New Issue
Block a user