# PineAgents 品牌抽离方案(Branding Abstraction) > 目标:把散落在 271 个 Python + 147 个前端文件里的品牌字符串,**收敛到单一配置层**。做到「改品牌只改一处」,并最大限度降低与上游 QwenPaw rebase 时的冲突。 > 现状:`constant.py` 里品牌已部分集中(`PROJECT_NAME`、`WORKING_DIR`、env 前缀),但仍有大量硬编码散落在 cli / routers / providers / 前端。 --- ## 1. 设计原则 1. **单一事实源(Single Source of Truth)**:所有持久化标识、前缀、展示名集中在一个 `Branding` 配置对象。 2. **三前缀回退**:环境变量支持 `PINEAGENTS_`(主)→ `QWENPAW_` → `COPAW_`(遗留)逐级回退,保证旧部署/旧环境变量不被破坏。 3. **持久化标识与展示名分离**:数据目录、keyring、消息 tag 等持久化标识可独立于展示名配置(避免改展示名影响旧数据)。 4. **不破坏上游合并路径**:新增独立模块,尽量少改 `constant.py` 既有语义;上游改动通过「读 Branding 而非字面量」自然落地。 --- ## 2. 目标结构 ### 2.1 新增模块 `src/pineagents/branding.py` ```python # -*- coding: utf-8 -*- """PineAgents 品牌与命名配置 —— 单一事实源。 改造/二次开发时:改这里,不要改散落在各文件的字面量。 """ from __future__ import annotations from dataclasses import dataclass, field from pathlib import Path @dataclass(frozen=True) class Branding: # ---- 展示名(可安全全量改名)---- project_name: str = "PineAgents" # 产品名(原 QwenPaw) cli_name: str = "pineagents" # CLI 命令(原 qwenpaw/copaw) package_name: str = "pineagents" # Python 包名 env_prefix: str = "PINEAGENTS_" # 环境变量主前缀 version: str = "2.1.0b1" # ---- 持久化标识(改名需迁移,见第 3 节)---- working_dir_name: str = ".qwenpaw" # 数据目录(保守:沿用) keyring_account: str = "qwenpaw" # 钥匙串主密钥账号(沿用) message_tag_key: str = "qwenpaw_tag" # 消息元数据 tag(沿用) message_client_id_key: str = "qwenpaw_client_message_id" builtin_qa_agent_id: str = "QwenPaw_QA_Agent_0.2" # ---- 兼容前缀(逐级回退)---- legacy_prefixes: tuple[str, ...] = ("QWENPAW_", "COPAW_") # ---- 依赖主前缀派生出的 env key 前缀列表(供测试/检查用)---- env_keys: tuple[str, ...] = ( "WORKING_DIR", "SECRET_DIR", "KEYRING_ACCOUNT", "LOG_LEVEL", ) BRAND = Branding() ``` ### 2.2 改造 `constant.py` 的 `_get_env`(三前缀回退) 把现有的 `QWENPAW_` → `COPAW_` 两级回退,扩展为 `PINEAGENTS_` → `QWENPAW_` → `COPAW_` 三级: ```python def _get_env(key, default=""): if key in os.environ: return os.environ[key] if key.startswith(BRAND.env_prefix): for legacy in BRAND.legacy_prefixes: legacy_key = legacy + key[len(BRAND.env_prefix):] if legacy_key in os.environ: return os.environ[legacy_key] return default ``` ### 2.3 收敛关键常量(`constant.py` 改为读 `BRAND`) ```python PROJECT_NAME = BRAND.project_name QWENPAW_MESSAGE_TAG_KEY = BRAND.message_tag_key # 保留别名避免大改 QWENPAW_CLIENT_MESSAGE_ID_KEY = BRAND.message_client_id_key BUILTIN_QA_AGENT_ID = BRAND.builtin_qa_agent_id ``` > 说明:`QWENPAW_MESSAGE_TAG_KEY` 等**别名保留原名**,只是值改由 `BRAND` 提供。这样内部引用无需改动,持久化值也不变(沿用 `qwenpaw_tag`),仅品牌展示名切换为 PineAgents。 --- ## 3. 持久化标识处理矩阵 | 标识 | 决策 | 理由 | |------|------|------| | 数据目录 `~/.qwenpaw` | **沿用**(不改) | 避免旧数据不可见;如需改,配 `working_dir_name` 并加迁移 | | 钥匙串账号 | **沿用** | 改则主密钥失配、无法解密旧密钥 | | 消息 tag | **沿用** | 旧消息上的 tag 需继续可读 | | 内置 Agent ID | **沿用** | 旧会话归属需对上 | | env 前缀 | **新增 `PINEAGENTS_` 为主** | 新部署用新前缀;旧变量靠回退仍生效 | **结论**:PineAgents 默认「展示名全改、持久化标识沿用」,实现零迁移平滑改造。 --- ## 4. 前端抽离(`console/`) 前端同样收敛到一个品牌常量文件,例如 `console/src/constants/branding.ts`: ```ts export const BRAND = { name: "PineAgents", cli: "pineagents", docBase: "https://<你的文档域名>/", // ... } as const; ``` - 所有组件(`layouts/`、`locales/`、`pages/**`)改读 `BRAND.name`,替换字面量 `"QwenPaw"` / `"qwenpaw"`。 - `console/index.html` 的 `` 与 favicon、`console/package.json` 的 name 直接改为 PineAgents。 --- ## 5. 打包与部署入口 - `pyproject.toml`:`name="pineagents"`,`[project.scripts] pineagents = "pineagents.cli.main:cli"`(保留 `qwenpaw`/`copaw` 作为兼容别名可选),coverage source、package-data 同步。 - `docker-compose.yml` / `deploy/` / `scripts/`:镜像名、容器卷名、启动命令改 `pineagents`。 - 包目录改名 `src/pineagents` → `src/pineagents`(或保留目录名、仅内部标识改名,视同步策略二选一,见第 6 节)。 --- ## 6. 同步策略:目录改名 or 不改? 两条路,按上游同步频率取舍: | 方案 | 做法 | 优点 | 缺点 | |------|------|------|------| | **A. 目录改名** `src/pineagents/` | 一劳永逸品牌干净 | 产物、import 全干净 | 每次 rebase 上游 271 个文件路径全变,冲突巨大 | | **B. 目录沿用** `src/pineagents/` | 包名/import 保留 qwenpaw,仅展示层改 PineAgents | rebase 冲突最小 | import 里仍带 qwenpaw 字样 | **建议**:PineAgents 走「Fork + 上游同步」,选 **方案 B**(目录沿用 `src/pineagents/`、import 保留),只把**展示层 + 配置 + 打包入口**切到 PineAgents。这样 rebase 上游时只处理少量展示层文件。若将来决定彻底冻结上游,再一次性迁移到 `src/pineagents/`。 --- ## 7. 分步落地 1. 新增 `src/pineagents/branding.py`(`Branding` 配置对象)。 2. `constant.py`:`PROJECT_NAME` 等改读 `BRAND`;`_get_env` 改三前缀回退。 3. 前端新增 `console/src/constants/branding.ts`,替换关键展示名。 4. `pyproject.toml` / `docker-compose.yml` / `deploy/`:打包与部署入口切 `pineagents`。 5. 替换 `console/public` 品牌素材(logo/favicon/启动图)。 6. 关闭/替换遥测上报。 7. 运行 `tests/` 与 e2e 回归,确认持久化标识未破坏。