内部开发手册
面向在源码里做二次开发的开发者。 不是用户手册——这里讲的是数据流、启动顺序、内存模型、工具系统与"别踩的坑"。
来源:
CLAUDE.md+ARCHITECTURE.md+src/bridge/app.py+ 实测沉淀。
0. 一句话
Familiars = Electron (Vue 3) 前端 → 本地 FastAPI bridge (127.0.0.1:18711, HTTPS/HTTP2) → OpenAI 兼容 LLM + 本地 QLoRA 小模型 + 工具系统。后端单进程、Windows-first、本地优先。
Renderer ──window.familiars API──▶ main.js (preload / IPC)
│ spawn
▼
src.bridge.run (hypercorn HTTPS/2)
│ X-Bridge-Token
┌────────────┴──────────────┐
▼ ▼
DialogueManager ToolDispatcher
/ ConversationSession (LLM tool-loop, MAX_TURNS=50)
│ │
▼ ├─ security gate (风险分/审批)
MemoryWriter ├─ browser/shared-Chrome
(Scoping: ├─ multimodal/vision
dialogue/agent/user) ├─ memory/tools
│ └─ broker / skill……
▼ ▼
SQLite (familiars.db, WAL) workspace/ 文件树1. 三层 Agent 架构
1.1 AssistantManager (src/models/assistant.py)
- Agent 0(管理者)。持有 N 个
Assistant实例。 - 跑独立的
MANAGER_MODEL(默认gpt-4o)做执行摘要与人设演化。 - 拥有
bridge_call工具(唯一),可以调用 bridge 的全部 REST 端点。 - 系统提示中注入框架文档索引 + Bridge API 面索引。
1.2 Assistant
- 单个专业化 agent。拥有独立的
DialogueManager、MemoryWriter、视觉管线、语音/数字人管线。 - 发送通知:飞书 webhook / Windows toast / macOS osascript / Telegram。
- 写报告:daily/weekly/monthly/annual 存于
workspace/agents/agent_<id>/reports/。
1.3 ConversationSession + DialogueManager (src/models/conversation.py)
ConversationSession:从 OpenAI 兼容端点流式读取,驱动多轮工具调用循环(上限MAX_TURNS=50)。DialogueManager:拥有对话树(DialogTree),为每轮构建 RAG 历史(α·cos + e^{-β·Δt} + γ·tree_distance)。MAX_TURNS不再是硬上限——到达预算时ConversationSession会问_turn_budget_escalator(manager LLM)是否续期或中止。
2. Bridge 启动副作用顺序(关键!)
src/bridge/app.py 在 import 时按精确顺序执行副作用,改顺序会出问题:
| # | 步骤 | 代码位置 | 为什么这个顺序 |
|---|---|---|---|
| 1 | Pin WORKSPACE_ROUTE | app.py:11-14 | 在任何其他模块 import 前把仓库根注入环境变量 |
| 2 | 主线程 pin CUDA | app.py:28-44 | torch.cuda.init() + zeros(1, device="cuda") + synchronize()。Windows WDDM 不允许 worker 线程首次触碰 CUDA → access violation |
| 3 | align_all_paths() | app.py:46-80 | 路径漂移自动对齐。必须在 DialogueManager 读对话前,否则 LLM 会把旧路径当上下文复读 |
| 4 | Import 46+ 路由模块 | app.py:82-86 | 一次性 import 全部路由 + comm_router |
| 5 | CORS + BridgeTokenMiddleware | app.py:108/123/235/381 | 只允许 localhost:5173 / 127.0.0.1:5173 / null origin |
| 6 | include_router 挂路由 | app.py:383-443 | 顺序即代码顺序。先注册的 prefix 赢 |
| 7 | _bootstrap_broker_workshop | on_event("startup") | 同步内置 broker 到 workspace,idempotent |
| 8 | _bump_anyio_thread_limiter | 同上 | 线程池上限 40 → 128 |
| 9 | _start_pool_saturation_watchdog | 同上 | 每 30s 采样,连续 ≥90% 写 stack dump |
| 10 | _install_browser_lifecycle_hooks | 同上 | atexit 回收 Playwright driver,idle reaper 守护 |
| 11 | _kill_orphan_gpu_subprocesses_on_boot | 模块级(app.py:863,调用点 1058) | 通过 nvidia-smi + .bridges.json 杀孤儿 GPU 进程 |
| 12 | 模型预热改为按需 JIT(无 _prewarm_models) | 模块级 | embedder / rembg / rewriter 由 vram_gate 准入时惰性加载;数字人 session 由路由按需触发(routes/digital_human.py:336/381) |
| 13 | autorun drift 检查 | 同上 | 后台运行时 OS autorun 丢失自动重装 |
| 14 | _rehydrate_monitors | 同上 | 从 DB 恢复 monitor watchers |
| 15 | _rehydrate_cron_jobs | 同上 | 强制 DailyTools 实例化 + catch-up 12h 内错过的 fire |
| 16 | _register_manager_jobs | 同上 | 注册 manager 日/周/月/年报聚合 cron |
| 17 | _rehydrate_skill_scheduler | 同上 | 恢复 skill scheduler 周期任务(必须在 #16 之后) |
3. 路径对齐机制(改路径别慌)
两套机制合作:
3.1 仓库根漂移(自动检测)
启动时比较所有 dialogue.path 前缀与 _repo_root()。漂移时自动改写:
dialogue.json(节点内容 +metadata.tool_callsargs/results + attachments)turn_*/plan.jsonstep_*.json
记录到 workspace/.paths_aliases.json 以供审计。
3.2 外部目录别名(手动录入)
# 预览
python -m src.tools.paths_migrate <old_path> <new_path> --dry-run
# 执行(每个改动文件备份为 .bak_<ts>)
python -m src.tools.paths_migrate <old_path> <new_path> --record之后每次开机回放。
3.3 不改什么
日志文件(.bridge.log、audit.log)永不改写——保留真实历史。
4. 内存 / VRAM 模型
4.1 单一 VRAM 准入闸
src/base/vram_gate.py:4 级优先级、3 级驱逐。所有需要显存的组件(embedder / rembg / rewriter / DH / ASR)走同一道闸。
4.2 对话 JIT 加载
DialogueManager.release_memory+ensure_dialogue_loaded:MRU 回调驱逐不活跃对话,新请求 JIT 加载。- HTTP 端点:
POST /api/agents/{id}/release-memory;GET /api/memory观测。
4.3 on_evict 约定
凡持有显存/内存的对象都必须实现 on_evict 回调,驱逐时可协作释放。这是硬性约定——不实现 = VRAM 泄漏。
5. 工具系统
5.1 ToolDispatcher 注册表
src/tools/tool_dispatcher.py 默认注册 15 个工具实例:
| # | 实例 | 来源 |
|---|---|---|
| 1 | DailyTools | src/utils/daily_tools.py |
| 2 | DailyMonitorTools | src/utils/daily_monitor/ |
| 3 | BaseTools | src/utils/base_tools.py |
| 4 | BaseTools.InputController | src/utils/base_tools.py |
| 5 | MultimodalTools | src/utils/multimodal_tools.py |
| 6 | BrowserTools | src/utils/browser_tools.py |
| 7 | DocTools | src/utils/doc/tools.py |
| 8 | MemoryTools | src/utils/memory_tools.py |
| 9 | SystemTools | src/utils/system_tools.py |
| 10 | UserTools | src/utils/user_tools.py |
| 11 | UserSecretTools | src/utils/user_secret_tools.py |
| 12 | PhoneTools | src/utils/phone_tools.py |
| 13 | SkillTools | src/tools/skill_tools.py |
| 14 | SkillWatcherTools | src/utils/skill_watcher_tools.py |
| 15 | BrokerTools | src/utils/broker_tools.py |
按需额外注册:
run_call_conversation(电话通话会话可导入时)ask_user_questions(src/models/processor/ask_user.py可导入时)bridge_call(manager only,src/tools/bridge_call.py)
5.2 Schema 分发
- 工具 schema 存
workspace/tools.json。 - 按客户端 merge:
src/models/clients/{base,browser,multimodal,input,doc,daily,system,mini_model}_client.py各自筛选。 _CONSULT_TARGETS仍然把consult_doc路由到 doc_tools —— 不要删base_client.py里的映射。- Recipe 的 SKILL 通过
execute_skill伪工具 +tools.whitelist到达 flow agents。
5.3 安全门
src/tools/security.py:
- 每个工具标注风险等级
allow_tool_for_turn根据 agent 模式(intense/turbo)+ 当前回合上下文决定放行- 高风险操作暂停等待用户确认
FAMILIARS_APPROVAL_DISABLED=1关闭审批门(开发用)
5.4 Turn Secrets
src/tools/turn_secrets.py:本轮替换 ENC:: 密文 token,回合结束即销毁。
6. Agent 管线(Generation.py)
用户消息 → Rewriter → Planner → Executor(+Transferer) → Verifier → Synthesizer → 最终回复
↓ 失败
Replanner → 回到 Planner
↓
MemoryWriter (后台)| 阶段 | 文件 | 职责 |
|---|---|---|
| Rewriter | src/models/rewriter.py | 原始 prompt → 结构化查询 |
| Planner | src/models/processor/planner.py | 输出 Step 列表(工具调用计划) |
| Executor | src/models/processor/executor.py | 通过 Transferer dispatch 每个 Step |
| Transferer | src/models/transferer.py | 6 优先级跨 agent/client/model 调度器 |
| Verifier | src/models/processor/verifier.py | RuleVerifier(确定性)+ ModelVerifier(LLM 语义) |
| Replanner | src/models/processor/replanner.py | 验证失败时重新规划 |
| Synthesizer | src/models/processor/synthesizer.py | 执行结果 → 自然语言回复 |
| MemoryWriter | src/models/memorizer/writer.py | 后台提炼结构化记忆(不阻塞 SSE) |
6.1 模型路由
ModelRouter(src/models/router/model_router.py):根据任务特征选DEFAULT_MODEL或更强模型。MetaRouter(src/models/router/meta_router.py):manager 元角色统一调用入口。- 所有模型调用统一走
ConversationSession。
6.2 客户端抽象
ToolClient 基类(src/models/clients/base_client.py):
chat()实现有界工具循环(MAX_TURNS)- 自动处理
consult_*跨 client 调用 end_step合成校验 + 死亡循环检测_guard_native_calls防止跨 client 幻影调用
7. 记忆架构
7.1 双轴
| 轴 | 持久化位置 |
|---|---|
dialogue | workspace/agents/agent_<id>/dialogue_<n>/ |
agent | workspace/agents/agent_<id>/ |
user / identity | workspace/(USER.md / IDENTITY.md / memory layers) |
ephemeral | 每轮,轮末驱逐 |
7.2 八类
profile、preference、relation、knowledge、event、state、procedure、directive。各类有独立衰减常数。
7.3 DialogTree
Node带 embedding(BAAI/bge-m3)- 嵌入模型被 DialogTree / AssistantRouter / RAG dense recall / rewriter 数据生成管线共享
- 所有组件产生可比较的向量
8. 配置系统
src/base/config.py 使用 pydantic-settings,validate_config() 返回 Settings 单例。
8.1 关键环境变量
详见 环境搭建 → 环境变量。此处列重点:
BASE_URL/API_KEY/DEFAULT_MODEL:必填,缺失抛ValueErrorFAMILIARS_DB_PATH:SQLite 路径,测试用:memory:FAMILIARS_HTTP_INSECURE=1:回退 HTTP + uvicornFAMILIARS_APPROVAL_DISABLED=1:关闭审批门FAMILIARS_TIER*_MAX_MB:GPU 分级 VRAM 阈值
8.2 HKDF 密钥派生
src/base/crypto.py:info 字符串b"familiars-secrets-v1"- 轮换会失效所有
workspace/SECRET.json,须同时做 re-encryption 迁移
8.3 SQLite
- 文件:
workspace/familiars.db(WAL 模式) src/storage/db.py:get_default_db_path- 迁移:
src/storage/migrations/v1 ~ v19(含 Python 迁移 v16)
9. 测试红线
| # | 红线 | 为什么 |
|---|---|---|
| 1 | 必须真 SQLite(FAMILIARS_DB_PATH=:memory:) | mock 仓库无法覆盖 WAL 并发 / 迁移 / 事务边界 |
| 2 | Bridge 路由测试:恢复 sys.modules 捕获项 | 不要 importlib.reload 路由模块——import 时副作用(CUDA pin / 路径对齐)无法重放 |
| 3 | Scratch agent 目录放 runs/ | bridge 启动扫描 workspace/agents/ 会真实创建 agent |
| 4 | 浏览器自动化走 familiars_cdp_port() | 单一共享 Chrome 实例(bridge + Playwright + 透视渲染共用) |
| 5 | 新增日志要进 KNOWN_LOGS | src/utils/framework_tools.py 的列表控制 manager 可见性 |
| 6 | 永远不用 git stash | CLAUDE.md 硬规则。stash pop 曾多次损坏工作树(path.txt / 源码回退) |
10. 常见坑(血泪)
| 坑 | 说明 | 正确做法 |
|---|---|---|
uvicorn src.bridge.app:app --reload | 丢 HTTP/2 + worker 线程 race CUDA init → 崩溃 | 用 python -m src.bridge.run |
| 两个 router 同 prefix | 先注册的赢。broker_user vs user 曾在 /api/user/profile 碰撞 | 加 prefix 路由前先想冲突 |
| HKDF info 字符串 | b"familiars-secrets-v1"——轮换失效全部 SECRET.json | 须同时做 re-encryption 迁移 |
workspace/tools.json 空 parameters | LLM 看不到该工具 | 给每个空 schema 加 reason + placeholder |
| Windows toast 被勿扰吞 | WinRT Show() 无异常但全屏游戏触发 Focus Assist 静默丢弃 Normal 级 | build_toast_script 设 ToastNotificationPriority.High 穿透 |
| Bridge 端口不是固定 18711 | bridge 运行时读 workspace/.bridge.port | 探测端口文件再判断 bridge 是否 down |
| copilot LLM 看不到 custom tools | filtered_schemas 跳过 falsy parameters | 每个空 schema 必须有 reason + placeholder |
| VueFlow node-types 必须 markRaw | inline { graph: GraphNode } → Vue 包在 Proxy 里 | markRaw(types) |
| VueFlow 自定义 edge/node 单根 | 双根 template → warning spam → 主线程饱和 | 包在单 <g> 根 |
| Deep watcher on store.graph | watch(store.graph, ..., {deep:true}) → render loop | 用 fingerprint string watcher |
| contextvars 跨请求泄漏 | BaseHTTPMiddleware 复用 worker thread | _fresh_request_context() 重置 vars |
11. Manager 的三个接线点
Manager(agent 0)有三个 always-on 接线点:
11.1 框架文档索引
src/models/RAG/framework_docs.py:render_framework_index 每轮注入:
- OPERATION.md / ARCHITECTURE.md / SECURITY.md / REPOSITORY.md / CLAUDE.md / CLI.md 的一行摘要
- 日志注册表
11.2 Bridge API 面索引
src/models/RAG/bridge_api_surface.py 手工整理的 REST 端点目录。
11.3 四个专用工具
src/utils/framework_tools.py 中 filtered_schemas 为 manager 额外注入:
list_framework_docs— 列出框架文档段read_framework_doc— 读取文档内容list_logs— 列出已知日志文件read_log— 读取日志内容
KNOWN_LOGS 列表控制可见性。新增值得关注的日志必须在此注册。
12. Broker 交换所
12.1 四层成熟度
| 层 | 状态 | 说明 |
|---|---|---|
| A 集群内通信 | ✅ 完成 | 本地 broker hub:identity + transport + match engine + rate limiter |
| B 应用容器 | 部分 | 无沙箱,插件运行在 bridge 进程中 |
| C 市场分发 | ✅ MVP | pack + sign + verify + install + relay publish |
| D 跨集群联邦 | 仅 hooks | 服务基础寻址,无完整协议 |
12.2 关键文件
| 子系统 | 代码 |
|---|---|
| Hub | src/broker/hub.py |
| Workshop | src/broker/workshop/__init__.py |
| 路由 | src/bridge/routes/broker.py / broker_workshop.py / broker_app.py |
| 作者密钥 | ~/.familiars/authors/<name>.key(base64 Ed25519 seed) |
| 参考模板 | workspace/manager/brokers/starter_broker/ |
12.3 签名/安装流程
# 作者
python main.py broker keygen --name mykey
python main.py broker pack workspace/manager/brokers/my_broker
python main.py broker sign my_broker.broker.zip --author-key mykey
python main.py broker publish ... --author-key mykey --relay https://api.familiars.cn
# 用户
python main.py broker install --url https://... --sha256 <hex> --pubkey <b64>安装时强制校验 sha256 + Ed25519 签名 + 作者 pubkey 匹配。
13. Recipe / Flow 生态
13.1 Recipe
- 文件夹协议:
manifest.json+knowledge/+utils/+docs/+renderers/ - 编译 → 干跑 → e2e 测试(
POST /api/recipe_dev/*) - 绑定到 agent → 运行时
maybe_override替换管线链段 - 作者工具:
python main.py recipe pack/sign/verify/publish - 能力审批:
capabilities.extra_modules+ per-(recipe,agent) 审批哈希
13.2 Flow
- v2 轨道式流编辑器
- 模板存
workspace/manager/flows/<template_id>/ - 实例生命周期:
/api/flows(list / get / trace / cancel) - 模板 CRUD:
/api/flow-templates - 内置模板:
src/flow/_builtin/<id>/,install_built_ins()只读 - 引擎:
src/flow/engine.py(owner-thread + per-role worker-thread 并发) - 恢复:
src/flow/resume.py(mutation replay + hash 校验)
14. 安全模型
14.1 数据流
- 用户数据默认落
workspace/ - 只有必要时把上下文发给云端 LLM
src/base/crypto.py加密敏感值 →workspace/SECRET.json
14.2 审批门
src/tools/security.py:风险等级 + 审批门- 高风险操作暂停等用户确认
FAMILIARS_APPROVAL_DISABLED=1跳过(开发用)
14.3 敏感数据检测
src/tools/sensitive_detector.py:gitleaks → Presidio → mini-LLM 三通道检测器。
14.4 Token 认证
X-Bridge-Token(header 或?token=query)BridgeTokenMiddleware全局校验- 豁免:
/health、/api/auth/*、/api/broker_app/*、/api/demo/*、/api/chat/*
15. 前端架构要点
15.1 两套 manager 首页
- 新版 Desktop.vue:桌面式 shell(图标 + 浮动窗口 + 壁纸),内置能力 + broker iframe
- 旧版 ManagerLanding.vue:五张卡片布局,通过
localStorage.familiars:legacyLanding切换
15.2 Broker iframe 沙箱
BrokerAppWindow挂 iframe 加载 broker UI- 静态资源走
/api/broker_app/{broker_id}/ui/{rel}(token-exempt) - 敏感操作走 postMessage → renderer → bridge(带 token)
15.3 API SDK
electron/renderer/src/api/index.ts 集中封装 fetch wrapper,自动挂 X-Bridge-Token + HTTPS/2 base URL。
