10 · 工程实践与附录
架构剖析第十篇:环境变量完整参考、GPU 分级、VRAM 门控、代码组织速查、构建运行、外部 Agent 适配。 对应
ARCHITECTURE.md§12–§15。
§12 外部 Agent 适配
- 支持接入用户自己跑的外部 agent(自定义 skill / 自定义 pipeline 的宿主)
- 外部 agent 通过 bridge REST + 工具网关参与同一张图
- IDENTITY.md 模板由
_external_identity_md()生成
python
# src/models/assistant.py
def _external_identity_md(*, kind: str, display_name: str,
cli_path: str, workspace_dir: str,
base_url: str, model: str) -> str:
"""Minimal IDENTITY.md for externally-backed agent."""
return (
f"# {display_name}\n\n"
f"This agent is a **proxy** for an external AI runtime...\n"
)§13 环境变量完整参考
13.1 LLM 配置(LLMConfig)
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
BASE_URL | ✅ | — | OpenAI 兼容 API base URL |
API_KEY | ✅ | — | API key |
DEFAULT_MODEL | ✅ | — | 默认聊天模型 |
MANAGER_MODEL | — | "gpt-4o" | Manager 执行摘要模型 |
MINI_MODEL | — | "Qwen2.5-1.5B" | 轻量本地模型 |
FAST_MODEL | — | "" | 低延迟快速模型(空=回退 DEFAULT_MODEL) |
BETA | — | 0.3 | 模型路由公式中成本权重 |
13.2 基础设施(InfrastructureConfig)
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
TIMEOUT | — | 300 | HTTP 超时(秒) |
MAX_TURNS | — | 50 | 工具调用循环最大轮次 |
DIALOGUE_EXPIRE_TIME | — | 3600 | 对话过期时间(秒) |
SLIDE_WINDOW_SIZE | — | 5 | 对话历史滑动窗口大小 |
WORKSPACE_ROUTE | — | "" | 根工作目录(空=cwd) |
TOP_K | — | 5 | RAG 召回 top-K |
13.3 Embedding 配置(EmbeddingConfig)
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
EMBEDDING_MODEL | — | "BAAI/bge-m3" | Sentence-transformers 模型 |
BASE_ROUTE | — | "" | 本地模型/router 文件根目录 |
13.4 熔断器配置(CircuitBreakerConfig)
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
CB_FAILURE_THRESHOLD | — | 5 | 熔断器开启前的失败次数 |
CB_RECOVERY_SECONDS | — | 30.0 | 半开探测前的恢复时间(秒) |
LLM_MAX_RETRIES | — | 3 | 每次 LLM 调用的最大重试次数 |
LLM_RETRY_BASE_DELAY | — | 1.0 | 初始退避延迟(秒) |
LLM_RETRY_MAX_DELAY | — | 30.0 | 最大退避延迟(秒) |
LLM_MAX_CONCURRENT_PER_URL | — | 8 | 每 URL 最大并发 LLM 调用(AIMD 上限) |
LLM_MIN_CONCURRENT_PER_URL | — | 1 | AIMD 收缩下限 |
LLM_QUEUE_WAIT_S | — | 120 | 等待 gate 许可的最大秒数 |
LLM_AIMD_GROW_AFTER | — | 20 | 连续成功多少次后 AIMD 目标 +1 |
LLM_AIMD_SHRINK_FACTOR | — | 0.5 | 超时时 AIMD 目标乘数 |
LLM_GATE_DISABLED | — | False | 完全禁用并发门 |
13.5 GPU / VRAM 配置
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
FAMILIARS_TIER4_MAX_MB | — | 5000 | tier4 VRAM 上限(MiB) |
FAMILIARS_TIER6_MAX_MB | — | 7500 | tier6 VRAM 上限(MiB) |
FAMILIARS_GPU_TIER | — | 自动 | 覆盖自动检测(mode0/tier4/tier6/tier8) |
FAMILIARS_GPU_TIER_FORCE_REDETECT | — | 0 | 强制重新检测 GPU 分级 |
13.6 安全配置
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
FAMILIARS_APPROVAL_DISABLED | — | 0 | 1 = 完全跳过审批门 |
13.7 Bridge 配置
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
FAMILIARS_HTTP_INSECURE | — | 0 | 1 = 回退到 uvicorn + HTTP |
FAMILIARS_DAEMON | — | 0 | 1 = daemon 模式(pidfile + 日志重定向) |
FAMILIARS_DB_PATH | — | workspace/familiars.db | SQLite 数据库路径(:memory: 用于测试) |
FAMILIARS_STREAM_LLM | — | auto | true/false 覆盖流式决策 |
BRIDGE_PORT | — | 18711 | Bridge 监听端口 |
13.8 外部服务配置
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
HUGGING_FACE_API_KEY | — | "" | Hugging Face API key |
GPT_SOVITS_DIR | — | external/GPT-SoVITS | GPT-SoVITS 集成包路径 |
THA3_DIR | — | external/talking-head-anime-3-demo | THA3 checkout 路径 |
DATAGEN_BASE_URL | — | "" | 数据生成 API base URL |
DATAGEN_API_KEY | — | "" | 数据生成 API key |
§14 GPU 分级(src/base/gpu_tier.py)
14.1 分级矩阵
python
Tier = Literal["mode0", "tier4", "tier6", "tier8"]
# 阈值(MiB,nvidia-smi 报告单位)
_TIER4_MAX_MB = float(os.getenv("FAMILIARS_TIER4_MAX_MB", "5000"))
_TIER6_MAX_MB = float(os.getenv("FAMILIARS_TIER6_MAX_MB", "7500"))| 分级 | VRAM 范围 | 功能集 |
|---|---|---|
mode0 | 无 GPU | API-only(无本地模型) |
tier4 | ≤5000 MiB | 4-bit + 顺序加载;decider/memorizer 队列触发 |
tier6 | ≤7500 MiB | rewriter+embedder+reranker 共载;音频通话允许 |
tier8 | >7500 MiB | 全功能集 |
14.2 检测流程
python
def _probe_total_vram_mb() -> Optional[float]:
"""nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits
Apple Silicon / macOS 无 nvidia-smi → mode0(除非 FAMILIARS_GPU_TIER 覆盖)。
结果缓存到 workspace/gpu_tier.json。
"""14.3 瞬态降级
python
# _transient_tier: Optional[Tier] = None
# 用户在前端选 "downgrade tier" → 设置 _transient_tier
# 不写入 gpu_tier.json → 下次启动恢复自动检测§15 VRAM 门控(src/base/vram_gate.py)
15.1 优先级分类
python
# Priority taxonomy (lower = more important):
# 1 — CRITICAL 用户活跃请求(TTS 说话中, operate() in flight, LLM 生成中)
# 2 — IMPORTANT 稳态用户交互(选中 agent 的 decider, bge embedding, reranker)
# 3 — NICE 有用但可等(memorizer writer, mini-model, EasyOCR, emotion tagger)
# 4 — BACKGROUND 索引/训练(永不用户可见)15.2 Entry 结构
python
@dataclass
class _Entry:
model_id: str
priority: int
estimated_mb: float
on_evict: Optional[Callable[[], None]] = None
is_subprocess: bool = False
category: Literal["sync", "queue"] = "sync"
is_working: bool = False
on_checkpoint: Optional[Callable[[], Any]] = None
admitted_at: float = field(default_factory=time.monotonic)
last_active_at: float = field(default_factory=time.monotonic)
idle_ttl_sec: Optional[float] = None15.3 类别语义
| 类别 | 说明 | 驱逐规则 |
|---|---|---|
sync | 用户阻塞。UI 动作同步加载 | 可驱逐 idle resident 或 queue resident |
queue | 可中断后台工作 | eviction 时运行 on_checkpoint() 快照 |
15.4 永不 dropped 保证
acquired()在Condition.wait()上阻塞- 任何
release/update_priority调用notify_all() - caller 异常 → entry 释放,slot 不无限持有
- 唯一不 admission 的路径:显式
timeout参数 →TimeoutError
15.5 线程优先级提升
python
@contextmanager
def boost_thread_priority(priority: int) -> Iterator[None]:
"""为用户阻塞线程临时提升优先级。
嵌套取 min → 内层 CRITICAL + 外层 NICE = 内层 CRITICAL。
防止内部 boost 意外削弱外部保证。
"""使用:
python
from src.base.vram_gate import boost_thread_priority, PRIORITY_CRITICAL
with boost_thread_priority(PRIORITY_CRITICAL):
do_user_facing_work() # 内部所有 gate.acquired() 都是 CRITICAL§16 代码组织速查
16.1 核心跳转表
| 文件 | 职责 |
|---|---|
src/bridge/app.py | FastAPI 挂载、CUDA pin、模型按需 JIT 加载、autorun、监控 rehydrate |
src/bridge/deps.py | DI 提供者(AssistantManager/DialogueManager/CommHub/UsageTracker) |
src/bridge/run.py | Bridge 启动入口(hypercorn + HTTPS/2) |
src/models/assistant.py | Assistant + AssistantManager(生命周期/报告/人设演化) |
src/models/conversation.py | ConversationSession + DialogueManager + RAG 组合 |
src/models/transferer.py | 跨 agent / 跨 client / 跨模型派发(6 优先级) |
src/models/clients/base_client.py | ToolClient 基类 — 工具循环、历史修剪、schema 归一化 |
src/tools/tool_dispatcher.py | 工具名→可调用表(15 默认工具实例) |
src/tools/security.py | 风险等级 / 审批门 / 动态分数升级 |
src/tools/sensitive_detector.py | 三层敏感信息检测(gitleaks + presidio + mini-LLM) |
src/base/crypto.py | 加密原语(HKDF b"familiars-secrets-v1") |
src/utils/browser_tools.py | Chrome CDP 自动化(单一共享实例) |
src/utils/memory_tools.py | Node, DialogTree, Memorizer |
src/utils/memory_store.py | 记忆存储层(scope × category 矩阵) |
src/utils/daily_tools.py | 日程/待办/提醒/报告工具 |
src/utils/reminder_dispatch.py | 提醒派发(inbox.jsonl + 原生通知) |
src/base/vram_gate.py | 单一 VRAM 准入闸(4 级优先级 / sync+queue 类别) |
src/base/gpu_tier.py | GPU 分级检测(mode0/tier4/tier6/tier8) |
src/base/config.py | pydantic-settings Settings 聚合 + validate_config() |
src/storage/db.py | SQLite WAL + 迁移 runner(v1…v50) |
src/broker/hub.py | Broker 层根(身份/传输/匹配/限流/账本) |
src/flow/engine.py | FlowEngine 并发执行 |
src/flow/resume.py | 断点续跑(mutation log + hash 校验) |
src/digital_human/session.py | DigitalHumanSession(三任务异步管线) |
src/digital_human/config.py | DigitalHumanConfig |
electron/electron/main.js | Electron 主进程 |
electron/electron/python.js | Bridge 生命周期(端口探测/健康检查/子进程管理) |
16.2 工具类跳转表
| 工具类 | 源文件 | 方法示例 |
|---|---|---|
BaseTools | src/utils/base_tools.py | read, write, execute_shell_script, find, grep, download |
BaseTools.InputController | 内嵌在 BaseTools | keyboard_type, keyboard_write |
BrowserTools | src/utils/browser_tools.py | browser_open, browser_click, browser_type, explore_page |
MultimodalTools | src/utils/multimodal_tools.py | vision_describe, operate, screenshot |
DailyTools | src/utils/daily_tools.py | create_schedule, create_todo, create_reminder |
DailyMonitorTools | src/utils/daily_monitor/ | 每日上下文收集 |
DocTools | src/utils/doc/tools.py | read_docx, write_docx, generate_ppt, generate_excel |
MemoryTools | src/utils/memory_tools.py | read_memory, write_memory, rag_query |
SystemTools | src/utils/system_tools.py | get_system_info, network_info |
UserTools | src/utils/user_tools.py | get_user_profile, update_user_profile |
UserSecretTools | src/utils/user_secret_tools.py | get_user_secret, store_user_secret |
PhoneTools | src/utils/phone_tools.py | 电话相关 |
SkillTools | src/tools/skill_tools.py | execute_skill |
SkillWatcherTools | src/utils/skill_watcher_tools.py | 技能变化监控 |
BrokerTools | src/utils/broker_tools.py | Broker 操作 |
§17 构建与运行
17.1 开发环境
bash
# Python 环境
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# 配置
cp .env.example .env # 编辑 .env 填入 API 配置
# 启动 Bridge
python -m src.bridge.run # HTTPS/2 on 127.0.0.1:18711
# 启动 Electron(开发模式)
cd electron && npm run electron:dev17.2 CLI
bash
# CLI 驱动运行中的 bridge(X-Bridge-Token 自动读 workspace/.bridge_token)
python main.py --help # 39 命令组, 200+ 子命令
python main.py agents list --pretty
python main.py chat stream --prompt "hi" --agent-id 1
python main.py todos create --title "buy milk" --deadline 2026-07-05T18:00完整参考见 CLI 命令参考。
17.3 生产构建
bash
cd electron && npm run electron:build17.4 测试
bash
# 所有测试须用真 SQLite
FAMILIARS_DB_PATH=:memory: pytest
# 特定模块
pytest test/recipes/ # 配方测试 (300+)
pytest test/bridge/ # Bridge 测试
pytest test/models/ # 模型测试17.5 生产 Relay
deploy/relay/app/ # Docker + Caddy, Cloudflare DNS-01§18 关键设计约束
18.1 CUDA 首触线程
Windows WDDM 不允许 worker 线程首触 CUDA。
src/bridge/app.py 在 import 时执行 torch.cuda.init() + torch.zeros(1, device="cuda")。
违反 → 进程以 0xC0000005 (access violation) 崩溃。18.2 路径对齐时序
align_all_paths() 必须在 DialogueManager._load_existing_dialogues 之前。
否则过期路径被读入上下文 → LLM 循环 ls 幽灵路径。18.3 端口稳定性
18711 是默认端口,不是固定端口。
端口文件 workspace/.bridge.port 是实际信号。
CLI 读端口文件;Electron 探测后写端口文件。
autorun watchdog 始终用 18711;Electron 发现冲突时 pickFreePort()。18.4 单 Embedder 实例
所有 DialogTree 不缓存模型实例(property 路由到 singleton getter)。
允许 unload_embedding_model 释放 ~700MB VRAM。
旧版 N dialogues = N refs → 驱逐后 CUDA tensors 仍被引用 → TTS 准入失败。18.5 consult_doc 仍活跃
src/models/clients/base_client.py 的 _CONSULT_TARGETS 仍路由 consult_doc → doc_tools。
历史声明 "consult_doc 已移除" 是错误的 → 映射仍活跃,planner 使用。§19 附录:迁移清单
完整迁移列表见 05-持久化层 §6.4.2。
当前版本:v46(v46__guild_task_tags.sql)。
