AI(Artificial Intelligence,人工智能)写代码有一个很常见的问题:
它特别喜欢“顺手写死”。
timeout = 30
max_retries = 3
model = "qwen3:14b"
raise ValueError("用户不存在")
单独看,每一行都没什么大问题。
但当几十个模块都有自己的 30、3、模型名称和提示文案时,维护者就会开始头疼:
这些值是什么意思?在哪里修改?换环境怎么办?支持英文怎么办?
因此,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 的 backend/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 基础设施
miniagent 的 backend/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 写得越快,项目仍然越整齐。
开源代码
🪐祝您好运🪐