AI(Artificial Intelligence,人工智能)写代码有一个很常见的问题:

它特别喜欢“顺手写死”。

timeout = 30
max_retries = 3
model = "qwen3:14b"
raise ValueError("用户不存在")

单独看,每一行都没什么大问题。

但当几十个模块都有自己的 303、模型名称和提示文案时,维护者就会开始头疼:

这些值是什么意思?在哪里修改?换环境怎么办?支持英文怎么办?

因此,AI 编程项目最好提前建立一条明确的架构规则:

具有业务含义、环境差异或用户可见含义的值,不允许散落在业务代码中。

解决这个问题,主要依靠两套机制:

中心化配置 + I18n 国际化。


📌 技术名片

中心化配置(Centralized Configuration,集中式配置)

把可能随环境、业务规则或运行策略变化的参数,统一放到配置入口管理。

例如:

  • 服务地址和端口
  • 超时时间、重试次数
  • 最大并发数
  • 密码安全策略
  • 模型名称
  • 功能开关

业务代码只负责读取配置,不负责决定这些值。

I18n(Internationalization,国际化)

Internationalization 首尾字母之间有 18 个字母,因此简称 I18n

它的核心思想是:

用户能够看到的文字,不直接写进业务代码,而是通过统一的语言资源读取。

例如:

t("auth.login_failed")

中文可以返回:

用户名或密码错误

英文则可以返回:

Incorrect username or password

💡 通俗理解:别让每个房间自己决定空调温度

可以把软件项目想象成一家酒店。

酒店不会让每个房间的装修工人自己决定:

“我觉得 26℃ 挺舒服,就焊死在墙里吧。”

而是把温度等可调参数接入统一控制系统,这就是中心化配置

同样,酒店也不会把中文提示焊死在每个业务流程里,而会准备统一的多语言话术。

这就是 I18n

AI 编程尤其需要这种约束。因为 AI 很擅长快速完成局部任务,如果没有架构规则,它很容易在不同文件里分别写下:

timeout = 30
for _ in range(3):
    ...
raise Exception("Login failed")

每段代码都能运行,但项目会逐渐失去统一规则。


一、什么应该被禁止?

Magic Value(魔法值)通常指直接出现在代码中,却看不出业务含义的数字或字符串

例如:

if failed_count >= 5:
    lock_user(10)

5 是什么?10 是秒、分钟还是小时?

更清晰的写法是:

if failed_count >= settings.login_max_failed_attempts:
    lock_user(settings.login_lock_duration_minutes)

Hard Coding(硬编码)的范围更广,例如:

API_URL = "http://127.0.0.1:8088"
model = "qwen3:14b"
message = "用户不存在"

这些内容本来应该能够配置、替换或翻译,却被直接塞进了程序逻辑。

但需要注意:

禁止魔法值,不等于禁止所有字面量。

例如:

if count > 0:

这里的 0 含义已经非常明确,没有必要为了“零硬编码”再创造一个 ZERO = 0

真正应该严格管理的是:

数据应该放在哪里
环境、部署、运行参数中心化配置
API Key、密码、Secret环境变量或密钥系统
稳定的领域状态常量或枚举
用户可见文案I18n
无业务含义且语义明确的字面量可以保留

API 是 Application Programming Interface,即“应用程序编程接口”;Secret 在这里指密钥等敏感信息。


二、架构规范:让每一种值都有自己的家

实际项目中,可以把规则压缩成四条。

1. 可变化参数 → 配置

不要:

timeout = 30
max_tokens = 4000

而应该:

settings.request_timeout_seconds
settings.max_conversation_tokens

时间、容量等配置最好直接写明单位,例如:

login_lock_duration_minutes
request_timeout_seconds
max_file_size_mb

2. 敏感信息 → 环境变量

禁止:

API_KEY = "sk-xxxxxxxx"
JWT_SECRET = "123456"

API Key、密码、Token(令牌)、Secret 等敏感信息不应进入源码。

JWT 是 JSON Web Token,即“一种常用的身份认证令牌”。

3. 稳定领域值 → 常量或枚举

不要到处复制:

if role == "admin":

可以使用 Enumeration(枚举):

class UserRole(str, Enum):
    ADMIN = "admin"
    USER = "user"

然后:

if role == UserRole.ADMIN:

4. 用户文案 → I18n

不要:

raise ValueError("用户账号不存在")

而应该:

raise ValueError(t("user.not_found"))

语言资源:

user:
  not_found: 用户账号不存在

带变量的句子也不要通过字符串拼接完成:

auth:
  account_locked: "账户已锁定,请在 {minutes} 分钟后重试。"

调用:

t("auth.account_locked", minutes=10)

这样英文可以采用完全不同的语序,而不需要修改业务逻辑。


三、为什么 AI 编程尤其需要这套规则?

传统开发者可能记得:

“这个 30 是超时时间。”

AI 不会天然拥有这种长期项目记忆。

即使项目已经存在:

settings.max_tool_calls

如果没有明确要求先搜索现有配置,AI 仍可能生成:

for _ in range(5):

功能没错,架构却开始出现分叉。

所以 AI 编程不能只检查:

代码能不能运行?

还要检查:

这个值应该由谁管理?

建立中心化配置与 I18n 后,会直接带来几个结果:修改参数只需要修改统一入口;开发、测试、生产环境更容易切换;增加语言时不需要搜索整个项目;Code Review(代码审查)也能快速识别裸数字、硬编码地址和用户文案。

更重要的是:

架构规则减少了 AI 的自由度,却提高了整个项目的一致性。


四、把规则直接写进 AI Prompt

Prompt(提示词)不要只写:

“请写高质量、可维护的代码。”

这种要求太抽象。

可以直接加入项目级规则:

## Configuration & I18n Rules

生成或修改代码时必须遵守:

1. 禁止新增具有业务含义、环境差异或用户可见含义的魔法值和硬编码参数。

2. 超时、重试、模型名称、接口地址、端口、路径、
   容量限制、功能开关等可变化参数,必须进入现有中心化配置体系。

3. 新增配置前必须搜索现有配置。
   已存在语义相同配置时必须复用,禁止重复定义。

4. API Key、密码、Token、Secret 等敏感信息禁止写入源码。

5. 稳定的领域状态和值优先使用常量或枚举。

6. 所有用户可见文案必须进入现有 I18n 体系,
   禁止直接在业务代码中写死中文或英文提示。

7. 新增 I18n 内容时:
   - 使用表达业务语义的 Key;
   - 补齐项目支持的语言;
   - 动态内容使用占位符;
   - 禁止拼接多语言句子。

8. 时间、大小、容量配置应明确单位,例如:
   timeout_seconds
   lock_duration_minutes
   max_file_size_mb

9. 完成后检查:
   - 是否新增业务魔法值?
   - 是否硬编码环境参数?
   - 是否存在用户可见硬编码文案?
   - 是否重复创建已有配置或 I18n Key?

发现问题时,先重构再输出最终代码。

这里最重要的一句话其实是:

新增之前,先搜索已有实现。

否则 AI 即使知道“应该配置化”,也可能创建第二套配置。


五、正面产出:miniagent 的真实实现

miniagent 是一个智能体平台,其后端已经把配置与 I18n 放入基础设施层,项目同时提供中文和英文支持。

miniagent的I8n和中心配置系统

中心化配置

miniagentbackend/app/core/config.py 中,使用 Pydantic Settings(Pydantic 配置管理组件)统一定义配置:

class Settings(BaseSettings):

    api_port: int = Field(
        default=8088,
        description="API port"
    )

    max_concurrent_requests: int = Field(
        default=10,
        description="Maximum concurrency"
    )

    max_conversation_tokens: int = Field(
        default=4000,
        description="Maximum number of tokens in a single conversation"
    )

    max_tool_calls: int = Field(
        default=5,
        description="Maximum number of tool calls"
    )

同时配置:

model_config = SettingsConfigDict(
    env_file=".env",
    env_file_encoding="utf-8",
    case_sensitive=False,
    extra="ignore"
)

这样,.env 环境配置文件可以覆盖默认值,业务模块只需要消费统一的 settings

安全规则也是一样:

password_min_length: int = Field(default=8, ge=1)

login_max_failed_attempts: int = Field(
    default=5,
    ge=1
)

login_lock_duration_minutes: int = Field(
    default=10,
    ge=1
)

尤其是 login_lock_duration_minutes,名称直接包含“分钟”这个单位,避免了 10 到底代表什么的歧义。


I18n 基础设施

miniagentbackend/app/core/i18n/i18n.py 会读取系统语言,并加载对应的 YAML(YAML Ain’t Markup Language,一种常用的配置数据格式)语言文件:

self._language = (
    await self._setting_service.get_system_language()
)

locale_file = Path(
    f"app/locales/{self._language}.yaml"
)

if locale_file.exists():
    with open(locale_file, "r", encoding="utf-8") as f:
        translations = yaml.safe_load(f) or {}

随后通过统一的:

t("auth.login_failed")

获取用户文案,而不是让每个业务模块自己判断中文还是英文。

miniagent 当前后端语言资源目录同时包含:

backend/app/locales/
├── zh.yaml
└── en.yaml

真实中文资源中已经可以看到:

common:
  success: 操作成功
  failed: 操作失败

auth:
  login_failed: 用户名或密码错误
  unauthorized: 未授权,请先登录
  token_invalid: Token 无效或已过期

  account_locked: >
    账户因连续登录失败已锁定,
    请在 {minutes} 分钟后重试,
    或联系管理员解锁。

业务代码负责提供 minutes 等数据,语言资源负责决定最终如何表达。

因此,miniagent 的整体思路可以浓缩成两条链路:

环境变量
Settings
业务代码
系统语言
I18n
zh.yaml / en.yaml
t("xxx.xxx")
业务代码

两套机制解决的是同一个核心问题:

把容易变化的数据,从相对稳定的业务逻辑中分离出去。


六、结语:代码负责做事,不负责“顺手决定规则”

AI 编程真正危险的地方,不一定是它写错代码。

更常见的情况反而是:

每一小段代码都能运行,但整个项目越来越难维护。

timeout = 30

可能没错。

max_retries = 3

也可能没错。

raise ValueError("用户不存在")

甚至完全符合当前需求。

真正应该问的是:

为什么这个值应该出现在这里?

好的架构会提前规定:

运行参数  → 配置
用户文案  → I18n
领域状态  → 常量 / 枚举
敏感信息  → 环境变量
业务代码  → 消费上述定义

miniagent 这样,提前建立 Settings.env、常量和 I18n 基础设施,本质上是在给 AI 划定施工边界。

不是限制 AI 写代码,而是限制 AI 随手创造新的规则。

最终我们想要的不是“AI 写得更快”,而是:

AI 写得越快,项目仍然越整齐。


开源代码


🪐祝您好运🪐