Data Formulator 多语言与 Agent 语言注入开发规范:从 Accept-Language 到 LLM Prompt 的完整链路
【免费下载链接】data-formulator🪄 Data Formulator is an interactive AI-powered data analysis system makes it easy to connect, explore and visualize data.项目地址: https://gitcode.com/GitHub_Trending/da/data-formulator
导读
Data Formulator 是一个交互式 AI 数据分析系统,Agent 需要根据用户界面语言生成图表标题、洞察结论、澄清问题等自然语言内容。本规范文档(docs/dev-guides/6-i18n-language-injection.md)定义了"LLM 输出语言约束 + 固定文案翻译"两条多语言链路:前端通过Accept-Language头把当前 UI 语言传给后端,后端构造language_instruction注入 Agent 的 system prompt;而按钮、错误提示等固定字符串则通过message_code/content_code交给前端 i18n 翻译。读完本文,你将掌握 Data Formulator 中 Agent prompt 语言注入的full/compact模式选择、后端固定消息的 code 化协议、前端translateBackend()消费路径,以及如何为系统接入一门新语言。
1. 架构概览:两条互不混淆的多语言链路
Data Formulator 的多语言处理分为两条链路,二者职责严格分离:
- LLM 输出语言约束:前端当前语言通过
Accept-Language传给后端,后端构造language_instruction注入 Agent prompt,让 LLM 生成与用户界面语言一致的文本。 - 固定文案翻译:代码中的按钮、提示、错误等固定字符串不交给 LLM 翻译,必须通过前端 i18n 或
message_code/content_code由前端翻译。
完整的请求链路如下:
frontend i18n.language -> fetchWithIdentity() sets Accept-Language -> routes/agents.py get_language_instruction() -> agents/agent_language.py build_language_instruction() -> Agent prompt1.1 核心模块与职责
| 模块 | 职责 |
|---|---|
| src/app/utils.tsx | getAgentLanguage()、fetchWithIdentity()、translateBackend() |
| src/app/App.tsx | LanguageSwitcher,基于AVAILABLE_LANGUAGES切换前端语言 |
| py-src/data_formulator/routes/agents.py | _get_ui_lang()、get_language_instruction() |
| py-src/data_formulator/agents/agent_language.py | build_language_instruction()、inject_language_instruction() |
| src/i18n/locales/{en,zh}/ | 前端翻译资源 |
1.2 当前代码对照状态
从原设计文档迁移时已按当前代码重新核对,以下状态以当前仓库为准,不能把早期计划里的状态原样视为事实:
| 项目 | 当前状态 | 说明 |
|---|---|---|
SortDataAgent | 已接入 | 构造函数接收language_instruction,route 使用compact模式 |
workspace-name | 已接入 | SimpleAgents接收language_instruction,生成 session/workspace 展示名使用full |
nl-to-filter | 暂不注入 | 当前返回结构化 JSON;未来若返回用户可见自然语言再接入 |
test-model | 明确豁免 | 健康检查需要固定返回,不应被语言指令影响 |
rec_language_instruction | 已清理 | 当前routes/agents.py未再保留该误导性参数 |
message_code/content_code/option_codes | 已落地 | Python 固定用户消息由前端翻译,后端保留英文 fallback |
| 前端 i18n | 已有 en/zh 主链路 | LanguageSwitcher、fetchWithIdentity()、locale 资源已接入 |
| 静态检查 / CI | 未落地 | scripts/check_language_injection.py、pre-commit 强制检查仍是未来项 |
2. Agent Prompt 语言注入
新增或修改会调用 LLM 的 Agent route 时,先判断输出是否面向用户展示,再决定是否注入语言指令:
| 输出类型 | 是否注入 | 说明 |
|---|---|---|
| 用户可读解释、建议、报告、对话、自动命名 | 是 | 必须跟随 UI 语言 |
| 生成代码、JSON key、字段名、变量名 | 部分 | 使用compact,只约束用户可见字段 |
| 纯健康检查 / 固定连通性测试 | 否 | 例如test-model,保持固定英文更稳定 |
| 纯结构化 JSON 且不展示自然语言 | 通常否 | 例如当前nl-to-filter,未来若返回用户文案再接入 |
决策树:
新增 LLM 调用 -> 输出是否面向用户展示? -> 否:健康检查、内部工具调用、日志,不注入 -> 是: -> 独立 Agent 类:构造函数添加 language_instruction="",用 inject_language_instruction() -> 内联 LLM 调用:route 中直接把 language_instruction 放入 system prompt -> 自然语言为主:mode="full" -> 代码 / 结构化 JSON / 短文本为主:mode="compact"2.1 Route 层:读取 UI 语言并传给 Agent
在 route handler 中通过get_language_instruction()读取当前 UI 语言并构造指令。以SortDataAgent为例(py-src/data_formulator/routes/agents.py):
language_instruction = get_language_instruction(mode="compact") agent = SortDataAgent(client=client, language_instruction=language_instruction)底层实现中,_get_ui_lang()直接从每个请求的Accept-Language头提取主语言代码:
def _get_ui_lang() -> str: """Extract the primary language code from the Accept-Language header.""" return request.headers.get('Accept-Language', 'en').split(',')[0].split('-')[0].strip().lower() def get_language_instruction(*, mode: str = "full") -> str: """Read the UI language from the Accept-Language header and build the prompt instruction.""" return build_language_instruction(_get_ui_lang(), mode=mode)注意这里的关键约束:语言必须来自每个请求的Accept-Language,而不是进程级环境变量,否则多用户、多语言并发场景下会串语言。在仓库中可以看到各 route 的实际用法:
SortDataAgent、DataRecAgent、DataTransformationAgent、ChartRestyleAgent相关 route 使用mode="compact"(见 routes/agents.py 与ChartRestyleAgent构造处 L774);workspace-name、DataAgent等文本型场景使用mode="full"(见 L380、L630、L882)。
mode选择表:
| Mode | 适用场景 |
|---|---|
full | 文本型 Agent:探索、报告、解释、聊天、洞察、数据加载对话 |
compact | 代码生成、数据转换、排序、自动命名、短文本生成、结构化输出 |
建议模式对照:
| 场景 | Mode |
|---|---|
DataAgent、ChartInsightAgent、InteractiveExploreAgent、ReportGenAgent | full |
CodeExplanationAgent、DataLoadingAgent | full |
DataRecAgent、DataTransformationAgent、DataLoadAgent | compact |
SortDataAgent、ChartRestyleAgent | compact |
workspace-name | full |
test-model、模型列表、纯状态检查 | 不注入 |
nl-to-filter、classify-chart-intent | 不注入(纯结构化输出) |
2.2 Agent 层:接收并注入语言指令
Agent 构造函数应接收language_instruction: str = "",并用inject_language_instruction()注入 system prompt:
from data_formulator.agents.agent_language import inject_language_instruction system_prompt = inject_language_instruction(system_prompt, language_instruction)inject_language_instruction()的实现(py-src/data_formulator/agents/agent_language.py)支持两种插入策略:
def inject_language_instruction( system_prompt: str, language_instruction: str, *, marker: str | None = None, ) -> str: if not language_instruction: return system_prompt if marker: idx = system_prompt.find(marker) if idx > 0: return ( system_prompt[:idx] + language_instruction + "\n\n" + system_prompt[idx:] ) return system_prompt + "\n\n" + language_instruction复杂 prompt 可以指定marker,把语言指令插入到技术细节之前:
system_prompt = inject_language_instruction( system_prompt, language_instruction, marker="**About the execution environment:**", )注入位置策略:
| 策略 | 适用场景 |
|---|---|
| marker 前插入 | 复杂 prompt,需要在技术细节前声明语言要求 |
| 末尾追加 | 简单 prompt,或者动态构建的 system prompt |
这两种策略都可以接受,不需要为了形式统一而重构所有 Agent。
2.3 必须遵守的硬性规则
- 不要在 prompt 中硬编码
"回答请使用中文"之类的语言要求。 - 不要用进程级环境变量决定语言;语言必须来自每个请求的
Accept-Language。 - 不要新增并行的
MessageBuilder或 LLM client 全局拦截器。 - 不要跳过
get_language_instruction()在 route 中直接调用build_language_instruction()。 - 不要把语言指令塞进 user message;语言约束应放在 system prompt。
build_language_instruction("en")返回空字符串,英文用户不需要额外 prompt(见下方实现细节)。
2.4build_language_instruction()的底层实现
py-src/data_formulator/agents/agent_language.py 中:
def build_language_instruction(language: str, *, mode: str = "full") -> str: lang = ((language or "").strip().lower()) or DEFAULT_LANGUAGE.lower() if lang == "en": return "" display_name = LANGUAGE_DISPLAY_NAMES.get(lang, lang) extra = LANGUAGE_EXTRA_RULES.get(lang, "") if mode == "compact": return _build_compact(display_name, extra) return _build_full(display_name, extra)几个关键行为:
- 英文返回空字符串:
en是默认语言,不需要向 prompt 注入任何额外指令,这也解释了为何inject_language_instruction()对空指令直接 no-op。 - 未识别语言代码(例如
"xx"):仍会返回非空指令块,并使用原始 code 作为显示名(LANGUAGE_DISPLAY_NAMES.get(lang, lang)),保证任何语言下 LLM 都有明确的输出约束。 full模式生成的是字段级明细规则:声明用户可见字段(title、takeaways、text、goal、tag、display_instruction、message、summary、explanation、data_summary、suggested_table_name、field_display_names、报告 Markdown 全文等)必须使用目标语言,而内部字段(output_variable、output_fields、chart_type、encodings、config、semantic_type、field_metadata、reason、detailed_instruction、thought、difficulty、所有 JSON key、Python 代码及注释)必须保持英文,同时明确"原始数据集列名不得翻译、新派生列在代码中使用 snake_case 英文、用户可见文本中使用目标语言描述"。compact模式是面向代码生成 Agent 的三句精简指令:只要求display_instruction和suggested_table_name使用目标语言,其余 JSON 字段、Python 代码、变量名、列引用与注释保持英文,并强调不得翻译原始数据集列名——这样额外文本不会干扰模型编写正确代码。
此外,模块内置了 20 种语言的显示名注册表LANGUAGE_DISPLAY_NAMES(含 en/zh/ja/ko/fr/de/es/pt/ru/ar/hi/th/vi/it/nl/pl/tr/id/ms/sv),以及按语言定制的额外规则LANGUAGE_EXTRA_RULES(例如中文要求使用简体而非繁体、日文要求用户可见文本使用です/ます体敬体)。
3. 后端用户可见消息:英文 fallback + 翻译 code
Python 中固定的用户可见消息不能只靠language_instruction,因为它们不是 LLM 生成内容。后端应返回英文 fallback和翻译 code,让前端翻译。
3.1 单条消息(error 事件)
yield { "type": "error", "message": "Output DataFrame is empty (0 rows).", "message_code": "agent.emptyDataframe", }3.2 结果 content
result = { "status": "error", "content": "No code block found in the response.", "content_code": "agent.noCodeBlock", }3.3 结构化澄清问题(clarify 事件)
event = { "type": "clarify", "questions": [{ "id": "continue_after_tool_rounds", "text": "How would you like to proceed?", "text_code": "agent.clarifyExhausted", "text_params": {"steps": steps_desc}, "responseType": "single_choice", "options": [ { "id": "continue", "label": "Continue exploring", "label_code": "agent.clarifyOptionContinue", }, { "id": "simplify", "label": "Simplify the task", "label_code": "agent.clarifyOptionSimplify", }, ], }], }3.4 命名规则与协议约束
- Agent 相关 key 放在
messages.agent.*。后端字段中只写agent.emptyDataframe,前端会拼成messages.agent.emptyDataframe。 - 有参数时使用
message_params、content_params、text_params,例如{"missing": "...", "available": "..."}。 clarify事件使用questions[].text_code和questions[].options[].label_code,不要再新增顶层message/options/option_codes协议。- LLM 根据当前 UI 语言生成的问题和选项通常只需要
text/label;固定后端文案才需要同时提供 fallback 文本和 code。 questions[].options[]的翻译只作用于当前问题的选项,不要把多个问题的选项合并到同一个数组。- 不要新增 Python 侧翻译表或
agent_messages.py;早期设计中的该方案已由前端message_code翻译模式取代。 - 后端普通 HTTP 响应优先走统一错误处理的
ErrorCode/AppError(详见 docs/dev-guides/7-unified-error-handling.md);还未纳入统一错误体系的jsonify(message="...")不应宣称已经全部完成国际化。
3.5 已迁移的 Agent 消息 key
当前第一批高频 Agent 固定消息已经在 src/i18n/locales/en/messages.json 和 src/i18n/locales/zh/messages.json 中提供翻译:
| Key | 来源 |
|---|---|
agent.clarifyExhausted | DataAgentclarify |
agent.clarifyOptionContinue/Simplify/Present | DataAgentclarify options |
agent.maxIterationsSummary | DataAgentcompletion summary |
agent.emptyDataframe | DataAgenterror event |
agent.fieldsNotFound | DataAgentchart field validation |
agent.llmApiError | DataAgentLLM error |
agent.llmEmptyResponse | DataAgentempty model response |
agent.parseActionFailed | DataAgentaction parse failure |
agent.unknownAction | DataAgentaction dispatch |
agent.noCodeBlock | DataRecAgent/DataTransformationAgent |
agent.unexpectedError | DataRecAgentfallback |
agent.codeExecError | code execution fallback |
agent.unableExtractScript | DataLoadAgent/SortDataAgent |
agent.errorCallingModel | DataLoadingChatAgent |
4. 前端消费后端消息:translateBackend()
前端使用translateBackend()(src/app/utils.tsx)消费后端消息。普通后端消息直接翻译;结构化澄清问题逐题翻译text_code和label_code:
import { translateBackend } from '../app/utils'; const message = translateBackend( event.message, event.message_code, event.message_params, ); const questionText = translateBackend( question.text, question.text_code, question.text_params, ); const optionLabel = translateBackend(option.label, option.label_code);translateBackend()的实现非常简洁,核心是fallback 优先:
export function translateBackend( fallback: string, code?: string, params?: Record<string, unknown>, ): string { if (!code) return fallback; const key = `messages.${code}`; const translated = i18n.t(key, { ...params, defaultValue: fallback }); return translated; }如果没有 code 或没有翻译,函数会回退到后端英文 fallback,保证缺翻译时界面不会空白。前端还有配套的translateBackendOptions(options, codes),用于平行翻译一组选项 label 数组。
澄清面板自己的固定 UI 文案,例如标题、按钮、占位符和"直接说明"标签,放在 src/i18n/locales/{en,zh}/common.json 的chartRec下;不要从后端事件里下发这些前端壳层文案。
5. 前端 UI 文案:所有用户可见字符串必须走 i18n
所有用户可见 UI 字符串必须走 i18n:
import { useTranslation } from 'react-i18next'; const { t } = useTranslation(); return <Button>{t('common.save')}</Button>;必须翻译:
- 按钮、菜单、tooltip、placeholder、dialog 标题
- toast/snackbar 文案
- 空状态、加载状态、错误提示
- 表格列头、面板标题、说明文字
可以不翻译:
console.log/ debug 日志- CSS class、test id、内部常量
- 跨前后端共享的 sentinel value,例如内部状态 marker
翻译文件位于 src/i18n/locales/en/ 与 src/i18n/locales/zh/。新增 key 时必须同时更新 en 和 zh。命名空间按现有文件选择:common、upload、chart、model、encoding、messages、navigation、dataLoading、errors等。
5.1 翻译 key 命名规范
优先使用现有 namespace。大量独立功能文案可以新建 namespace,但必须同时添加 en/zh 资源并注册到 locale index(例如 src/i18n/locales/index.ts 中的import en from './en'; import zh from './zh'; export { en, zh };)。
命名模式:
<namespace>.<component-or-feature>.<element> dataLoading.toolLabels.readingFile dataLoading.actions.loadTable dataLoading.placeholder.describeData common.actions.close messages.error.failedToOpenWorkspace设计文档中曾列出一批前端硬编码审计结果;迁移到本规范后,这些清单不再作为"当前待办事实"维护。开发时以.cursor/rules/i18n-no-hardcoded-strings.mdc和本节规则为准,发现新增或修改的用户可见文案时就地迁移到 i18n。
6. 新语言接入指南
agent_language.py支持的 20 种 LLM 输出语言不等于前端 UI 已完整翻译 20 种语言。只有 locale 文件和AVAILABLE_LANGUAGES都配置完成的语言,才应出现在前端语言切换器中。
接入一门新语言的完整步骤:
- 在 agents/agent_language.py 的
LANGUAGE_DISPLAY_NAMES中添加语言代码和显示名。 - 如有特殊要求,添加到
LANGUAGE_EXTRA_RULES(例如中文的简体/繁体约束、日文的敬体约束)。 - 在
src/i18n/locales/<lang>/添加完整翻译资源。 - 在服务端配置
AVAILABLE_LANGUAGES,让前端语言切换器显示该语言。 - 验证
fetchWithIdentity()请求头、Agent 输出、固定 UI 文案都使用新语言。
每种新语言至少需要与 en/zh 等价的 locale 结构:
src/i18n/locales/<lang>/ ├── common.json ├── upload.json ├── chart.json ├── model.json ├── encoding.json ├── messages.json ├── navigation.json ├── dataLoading.json ├── errors.json ├── loader.json └── index.ts6.1AVAILABLE_LANGUAGES的服务端配置
服务端在 py-src/data_formulator/app.py 中读取该配置,默认值为['en', 'zh'],同时支持通过环境变量AVAILABLE_LANGUAGES(默认'en,zh',逗号分隔)或--available_languages命令行参数覆盖(见 app.py 与 L383)。
6.2 前端语言切换器的联动逻辑
src/app/App.tsx 中的LanguageSwitcher从serverConfig.AVAILABLE_LANGUAGES读取可选语言列表:
- 若可用语言数量
<= 1,切换器直接不渲染(return null); - 每个语言按钮展示
LANGUAGE_LABELS中的短标签(如EN、中文、日本語、한국어、FR、DE),未知语言回退为lang.toUpperCase(); - 点击按钮调用
i18n.changeLanguage(value)切换前端语言,随后所有/api/请求的Accept-Language头会随之改变(见下文第 7 节)。
7. 前端到后端的语言传递:fetchWithIdentity()
语言传递的起点是getAgentLanguage()(src/app/utils.tsx):
export function getAgentLanguage(): string { return i18n.language.split('-')[0]; }它把i18n.language(可能是zh-CN这种带 region 的完整标签)裁剪成 BCP-47 主语言代码(zh)。随后,fetchWithIdentity()包装所有/api/请求(utils.tsx),在构建 Headers 时设置:
headers.set('Accept-Language', getAgentLanguage());同时还会附加X-Identity-Id、X-Workspace-Id头,并在前端认证模式下附加Authorization: Bearer <token>;若后端返回 401,会尝试一次静默 OIDC token 刷新并重试请求。后端_get_ui_lang()正是解析这个Accept-Language头来得到语言(见第 2.1 节),由此完成"前端语言 → HTTP 头 → LLM prompt"的闭环。
8. 自动化现状与未来约束
当前已有:
- 前端
fetchWithIdentity测试覆盖身份和认证 header 行为。 - 前端 error code/i18n 映射测试覆盖结构化错误翻译。
- 后端 Agent 和 error handler 测试覆盖部分
message_code/ warning 事件。
当前未作为已完成能力声明:
- 没有专门的
scripts/check_language_injection.py静态检查脚本。 - 没有 pre-commit/CI 强制扫描所有新增 LLM 调用点。
- 普通后端 HTTP 响应消息仍在逐步迁移到 error code / message code 体系。
如果后续实现自动化检查,应更新本文档和相关 Cursor rules。
建议的未来自动化:
- 后端 AST 检查:扫描新增用户可见 Agent route 是否调用
get_language_instruction()。 - 前端 ESLint:启用
i18next/no-literal-string,初期可设为warn,逐步提高到error。 - PR checklist:要求新增 Agent、route、后端 message code、前端文案都按本文档检查。
这些内容是未来约束,不是当前已完成能力。
9. 新模块开发 Checklist
新增 Agent
- 构造函数接收
language_instruction: str = "" - system prompt 使用
inject_language_instruction() - route 层调用
get_language_instruction() - 正确选择
full或compact - Python 固定用户消息带
message_code/content_code
新增 Agent Route
- 读取
Accept-Language派生语言指令 test-model这类健康检查明确记录为不注入nl-to-filter这类纯结构化 JSON route 若新增自然语言输出,需要重新评估注入- 流式事件中的错误、clarify、summary 使用 message code
- 前端消费路径调用
translateBackend()
新增前端组件
- 使用
useTranslation()和t() - en/zh 都添加翻译 key
- 不翻译内部 sentinel value
- 后端 message code 用
translateBackend()消费
新增后端固定消息
- 判断消息是否用户可见
- 用户可见则提供英文 fallback + code
- code 在
src/i18n/locales/en/messages.json和zh/messages.json中都有翻译 - 如属于错误处理体系,优先使用统一
ErrorCode/AppError
Review Checklist
- 没有进程级默认语言或硬编码中文/英文 prompt 约束
- 没有新增 Python 侧翻译字典
- 没有在 user message 中注入语言要求
- 没有新增未翻译的用户可见 TSX 字符串
- 英文 fallback 存在,缺翻译时不会空白
10. 相关规范文档
.cursor/skills/language-injection/SKILL.md.cursor/rules/language-injection-conventions.mdc.cursor/rules/i18n-no-hardcoded-strings.mdc- docs/dev-guides/1-streaming-protocol.md
- docs/dev-guides/7-unified-error-handling.md
【免费下载链接】data-formulator🪄 Data Formulator is an interactive AI-powered data analysis system makes it easy to connect, explore and visualize data.项目地址: https://gitcode.com/GitHub_Trending/da/data-formulator
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考