Skip to content

AI 角色注册表与模型矩阵

本章目标:

  1. 理解"指挥室"——为什么 a-cdm 要在 config.yaml 之外再造一个 ai_roles.yaml 注册表
  2. 掌握 provider/role 二级模型、managed_by 边界、五个错误码各自语义
  3. 分清启动期 fail-fast(结构错)与 graceful degradation(凭据空)两种失败模式

TL;DR

acdm-backend/app/util/ai_registry.py + acdm-backend/app/ai_roles.yaml 是 a-cdm 所有 AI 功能的"指挥室":每个 AI 用途登记成一个 role,指定走哪个 provider(LLM 网关)+ 哪个 model。注册表在 FastAPI 启动期一次性校验:结构/引用错误直接 fail-fast 让进程退出,而凭据缺失只标 unavailable、请求时返 503(graceful degradation,避免"代码先于 .env 部署"拖垮服务)。managed_by: deer-flow 的 role 只是"全景视图",acdm-backend 拒绝为它取 client(错误码 50403)——真实控制点在引擎 config.yaml

Overview(为什么要有"指挥室")

引擎 config.yamlmodels[] 解决的是"引擎自己用哪些模型"。但 a-cdm 里调用 LLM 的不止引擎:acdm-backend 还有 wiki AI 改写、截图建会日历解析、会议描述生成、SOW 解析、单会议 atom 抽取等一堆业务侧 LLM 调用。如果这些散落在各 service 里各自 new OpenAI(...),会出现:

  • 换网关/换模型要改 N 处代码;
  • 没人说得清"全项目到底有多少处在调 LLM、各用什么模型";
  • 凭据缺失要等运行到那行代码才报错。

ai_roles.yaml 的设计哲学是把"谁在用 AI、用什么"集中成一张声明式清单(文件头注释 acdm-backend/app/ai_roles.yaml:1-17):provider 段声明可用网关及凭据,roles 段声明每个 AI 功能用哪个 provider+model。代码只认 role_id,换模型只改 YAML。这与引擎 config.yaml两套并行体系,通过 managed_by 字段标注边界互不越权。

Architecture:provider/role 二级模型

概念定义字段Source
provider一个 LLM 网关(凭据 + base_url + 启用状态 + 参数适配)base_url api_key enabled payload_constraintsacdm-backend/app/ai_roles.yaml:19-54
role一个具体 AI 用途,绑定一个 provider + modelid provider model capabilities managed_by enabledacdm-backend/app/ai_roles.yaml:57-164

当前三个 provider:jointpilot(公司主网关,默认,2026-05-08 起)、aicc(备用,embedding/MiniMax,默认禁用)、ark(火山方舟,已封存,公司项目永久禁用)。当前 13 个 role,分三类:

类别数量managed_byacdm-backend 能否取 client示例Source
acdm-backend 直管6wiki_ai_rewrite vision_calendar meeting_atom_extractai_roles.yaml:59-97
deer-flow 间管6deer-flow / acdm-backend✗(50403)/✓chat_lead_agent weekly_report title_generationai_roles.yaml:101-151
future 占位1无,enabled: false✗(50404)insight_embedding(bge-m3)ai_roles.yaml:157-163

managed_by: deer-flow 的 role 在 yaml 里只是"指挥室全景视图"——让运维一眼看全项目所有 AI 用途,但 acdm-backend 调 get_chat_client() 时若命中这类 role,_resolve_role() 直接抛 BusinessException(50403) 并提示"改 model 请去 deer-flow/config.yaml.models[] 段"(ai_registry.py:302-308)。这是一道防止跨边界误配置的护栏。

Components:工厂与生命周期

AIRegistry 单例(ai_registry.py:144)

启动期由 FastAPI lifespan 调一次 init_registry()(acdm-backend/app/main.py:148-153),加载 + 校验 yaml。核心工厂方法:

方法返回用途Source
get_chat_client(role_id)(AsyncOpenAI, model_id)非流式 chat/visionai_registry.py:327
get_streaming_client(role_id)(timeout, base_url, api_key, model)SSE 流式ai_registry.py:342
_resolve_role(role_id)(role, provider)内部:校验并解析 roleai_registry.py:281
list_ai_roles() / list_providers()list/dict(含 health)admin 清单端点ai_registry.py:551 :595
record_success/error()LLMClient 调用后回写健康状态ai_registry.py:414 :443
probe_streaming_roles()dict启动期 + 按需健康探测ai_registry.py:472

_resolve_env(value)(ai_registry.py:74)处理 $VARNAME:优先 os.environ,否则 fallback 到 pydantic settings 默认,避免"env 没设→解析空→URL 没 scheme"的隐性失败。

两种失败模式(关键设计)

启动期校验在 AIRegistry.load()(ai_registry.py:162)里区分两类失败,处理方式截然不同:

  • fail-fast(结构错):role 引用不存在的 provider、role id 重复、payload_constraints 结构非法等 → 抛 AIRegistryConfigError,进程启动失败。这是"代码/配置写错"的硬错误,不能带病上线。
  • graceful degradation(凭据空):启用的 role 所依赖 provider 的 api_key env 为空 → 不 fail-fast,登记 _unavailable_providers,请求时返 503(ai_registry.py:153-154:219:244-250)。这是 2026-05-08 L94 教训的修复:历史上段 1 上线时 JOINTPILOT_API_KEY 没就位 → ai_registry fail-fast → acdm-backend 停摆 8 分钟。改成 graceful 后,凭据空只让相关 role 503,不拖垮整个后端。

红线 CLAUDE.md:新增/rotate xxx_API_KEY 等 env 时,必须先 ssh prod 加 .env → 再 merge → 再 force-recreate,不可"先 merge 后改 .env"。graceful degradation 是兜底,SOP 仍是先改 env。

错误码速查

常量含义触发Source
50401ERR_REGISTRY_NOT_LOADED注册表未加载未调 init_registryai_registry.py:64
50402ERR_ROLE_NOT_FOUNDrole 不存在role_id 拼错ai_registry.py:65
50403ERR_ROLE_DEER_FLOW_MANAGEDrole 由 deer-flow 管理acdm-backend 试图取 deer-flow 直管 role 的 clientai_registry.py:66
50404ERR_ROLE_DISABLEDrole enabled=false调用未启用 role(如 insight_embedding)ai_registry.py:67
50405ERR_PROVIDER_UNAVAILABLEprovider 凭据缺失graceful degradation 命中ai_registry.py:68

Implementation:provider 参数适配

不同模型对 payload 参数容忍度不同,payload_constraints 让 registry 在请求发出前统一处理(ai_roles.yaml:30-40):

yaml
# 摘自 acdm-backend/app/ai_roles.yaml:30-40 — jointpilot.payload_constraints
unsupported_params: [temperature]   # kimi-k2.6 强制 temperature=1,caller 传别的会被 server 400;registry 删掉
force_overrides:
  thinking:
    type: disabled                  # acdm-backend 直管 4 role 都是工具型短任务,thinking 没价值且会挤光 content
  • apply_payload_constraints(role_id, payload):删掉 unsupported_params 里的 key;
  • get_extra_body(role_id):把 force_overrides 注入 OpenAI SDK 的 extra_body,覆盖 caller 传入值。

这解决了一个真实坑:kimi-k2.6 默认开 thinking,reasoning 走 reasoning_content,普通 content 字段会被挤光 —— 短任务(会议描述/wiki 改写)拿到空字符串就失败。统一在 registry 层禁掉,业务代码无需关心。

模型矩阵(引擎侧 config.yaml)

注册表的 chat_lead_agent 等 deer-flow 直管 role 真实跑哪个模型,由引擎 deer-flow/config.yaml:37-211models[] 决定。火山方舟 Coding Plan 矩阵(已封存为备份,当前主网关 jointpilot)按评测排序:

模型评测/用途thinkingvisionSource
doubao-seed-2.0-pro评测第 1,综合 73.9deer-flow/config.yaml:47-63
doubao-seed-2.0-lite性价比王,速度最快deer-flow/config.yaml:66-81
glm-4.7评测第 3,人名抓得最全deer-flow/config.yaml:119-134
kimi-k2.6公司网关(jointpilot),当前主力deer-flow/config.yaml:177-193

前端切模型走 /api/models(引擎 Gateway)不走本注册表(ai_roles.yaml:112),这就是为什么 chat_lead_agentmanaged_by: deer-flow

Common Pitfalls / 实战 Tips

  • ai_roles.yaml 必须 --force-recreate:文件头注释 ai_roles.yaml:17 明确,docker compose restart 不重读。
  • 想给 acdm-backend 业务换模型:改 ai_roles.yaml 对应 role 的 model;想给引擎 chat 换模型:改 deer-flow/config.yamlmodels[] 并通过前端模型卡片切换。别在 ai_roles.yamlchat_lead_agent 期望生效(50403)。
  • 凭据 503 不是 bug:graceful degradation 下 provider 凭据空时 role 返 503 是预期,查 .env 是否就位、是否 --force-recreate 过。
  • admin 看全景:GET /admin/ai-roles 返回所有 role + health,POST /admin/ai-roles/probe 主动探测 streaming role 健康。

References

  • acdm-backend/app/ai_roles.yaml:1-164 — 注册表配置全文(provider/role 声明)
  • acdm-backend/app/util/ai_registry.py:144-685 — AIRegistry 实现(load/工厂/健康/错误码)
  • acdm-backend/app/util/ai_registry.py:281-326_resolve_role 五种异常分支
  • acdm-backend/app/main.py:148-180 — lifespan 中 init_registry + probe_streaming_roles
  • acdm-backend/app/api/admin_ai_roles_router.py — admin 清单与探测端点
  • deer-flow/config.yaml:37-211 — 引擎侧模型矩阵(deer-flow 直管 role 真实控制点)
PageRelationship
deer-flow 引擎配置体系本章注册表与该章 config.yaml models 是两套并行模型配置,via managed_by 划界
环境变量、凭据与降级开关本章 graceful degradation 依赖该章的 env 凭据与部署 SOP
acdm-backend 控制面架构本章注册表由该章 lifespan 启动序列初始化
审计、SSE 与后台任务本章 meeting_atom_extract 等 role 被该章后台 worker 调用
控制面与引擎的耦合契约本章 managed_by: deer-flow 边界是该章耦合契约的一部分

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析