Skip to content

[Feature Request]: Separate LLM func by role #2741

@danielaskdd

Description

@danielaskdd

PRD:按角色分离 LLM 模型

一、背景与目标

1.1 背景

LightRAG 在文档处理与检索问答流程中,存在多类 LLM 调用,但当前统一使用同一个 llm_model_func。这会导致:

  • 抽取、关键词、最终回答使用同一模型,成本和时延无法按任务特性优化。
  • 角色间并发队列与超时策略共用,无法针对热点链路单独扩容或限流。
  • 无法在运行时按角色热切换模型(前端管理配置场景受限)。

1.2 目标

  1. 保持向后兼容:不新增任何配置时,行为与当前版本一致。
  2. 角色化模型路由:支持 extract / keyword / query 三角色独立 LLM。
  3. 可继承且可覆盖:同 Provider 时按字段继承,按需最小覆盖。
  4. 强隔离:每个角色始终创建独立 LLM 函数和独立并发队列,不复用 base 函数。
  5. 运行时可变:支持运行中更新角色的 provider/model/host/api_key/options/timeout/max_async

二、术语与角色定义

  • base LLMllm_model_func 对应的默认模型配置。
  • extract 角色:实体关系抽取 + 描述摘要。
  • keyword 角色:查询关键词提取。
  • query 角色:最终回答生成(包含 KG/naive/bypass 以及 emulation 直调问答)。

三、设计原则(新增约束)

3.1 独立函数与连接创建规则

三个角色函数(extract/keyword/query)一律独立创建,且一律使用各自独立并发队列,不允许角色函数复用 base 包装函数。

说明:

  1. base 的作用是提供默认参数来源(继承源),不是角色函数复用目标。
  2. 即使角色最终参数与 base 完全一致,也仍要构造独立角色函数与独立队列。
  3. 角色 host/api_key/model/provider options/max_async/timeout 变化都只影响该角色函数。

3.2 命名统一规则

并发环境变量统一为:

  • MAX_ASYNC(base)
  • MAX_ASYNC_EXTRACT_LLM
  • MAX_ASYNC_KEYWORD_LLM
  • MAX_ASYNC_QUERY_LLM

超时环境变量统一为:

  • LLM_TIMEOUT(base)
  • LLM_TIMEOUT_EXTRACT_LLM
  • LLM_TIMEOUT_KEYWORD_LLM
  • LLM_TIMEOUT_QUERY_LLM

3.3 跨 Provider 继承规则

role_binding != base_binding 时:

  1. 不得继承 base host/api_key/provider_options
  2. host 必须显式提供(校验失败即启动失败)。
  3. api_keymodel 必须显式提供(校验失败即启动失败)。
  4. provider options 从新 provider 类型默认值起步,再叠加角色覆盖项。

四、方案总览

4.1 LightRAG 新增字段

# per-role llm function
extract_llm_model_func: Callable[..., object] | None = field(default=None)
keyword_llm_model_func: Callable[..., object] | None = field(default=None)
query_llm_model_func: Callable[..., object] | None = field(default=None)

# per-role queue limit
extract_llm_model_max_async: int | None = field(default=None)
keyword_llm_model_max_async: int | None = field(default=None)
query_llm_model_max_async: int | None = field(default=None)

# per-role timeout
extract_llm_timeout: int | None = field(default=None)
keyword_llm_timeout: int | None = field(default=None)
query_llm_timeout: int | None = field(default=None)

4.2 __post_init__ 行为

  1. 先保存 base 原始函数 _raw_llm_func
  2. 解析每个角色的有效 max_async/timeoutNone -> fallback base)。
  3. 包装 base self.llm_model_func
  4. 对每个角色都执行独立构造:基于角色最终生效参数创建独立函数并单独包装队列。

关键点:

  • 角色函数不能指向 self.llm_model_func,必须基于角色参数独立构造,避免双队列嵌套和队列串扰。
  • asdict(self) 每次查询/插入都会重建快照,因此实例属性更新可在下一次调用生效。

4.3 调用路由变更

operate.py

  • extract_entities() -> extract_llm_model_func
  • _summarize_descriptions() -> extract_llm_model_func
  • extract_keywords_only() -> keyword_llm_model_func
  • kg_query() -> query_llm_model_func
  • naive_query() -> query_llm_model_func
  • aquery_llm() bypass -> query_llm_model_func

api/routers/ollama_api.py

  • /api/generate/api/chat 中所有直接 LLM 调用改为使用 query 角色 LLM

五、API 配置与构造改造

5.1 config.py 新增角色参数

每角色新增参数:

  • *_LLM_BINDING
  • *_LLM_MODEL
  • *_LLM_BINDING_HOST
  • *_LLM_BINDING_API_KEY
  • MAX_ASYNC_*_LLM
  • LLM_TIMEOUT_*_LLM

其中 *EXTRACT/KEYWORD/QUERY

5.2 binding_options.py 扩展

新增:

@classmethod
def options_dict_for_role(cls, args: Namespace, role_prefix: str) -> dict[str, Any]:
    ...

用途:

  • 同 Provider:从 base options 继承,再叠加角色项。
  • 跨 Provider:从该 Provider 默认 options 起步,再叠加角色项。

角色 provider 参数命名:

  • {ROLE}_{PROVIDER_PREFIX}_{FIELD}
  • 例如:EXTRACT_OPENAI_LLM_TEMPERATURE

5.3 lightrag_server.py 构造流程

A. 新增 create_llm_func_with_params(...)

输入完整的 binding/model/host/api_key/provider_options/timeout,返回预配置 LLM callable。

B. 新增 create_role_llm_config(role, args, config_cache)

负责解析并校验角色最终配置:

  • 应用继承规则;
  • 判断是否跨 Provider;
  • 执行必填校验(跨 Provider 强制 model/api_key/host);
  • 返回结构化配置对象(不做与 base 的复用判定)。

C. 跨 Provider Host 必填校验

取消跨 Provider 的 host 自动默认逻辑。若角色切换了 provider 且未设置 *_LLM_BINDING_HOST,应该使用新Provider默认的端点URL。

LightRAG对象初始化完成后,应该把各个角色的LLM函数配置输出到控制台日志;/health端点应该各个角色的LLM函数配资。

六、运行时动态更新接口(增强版)

update_llm_role_func() 升级为配置级接口,支持完整热更新:

def update_llm_role_config(
    self,
    role: Literal["extract", "keyword", "query"],
    *,
    binding: str | None = None,
    model: str | None = None,
    host: str | None = None,
    api_key: str | None = None,
    provider_options: dict[str, Any] | None = None,
    max_async: int | None = None,
    timeout: int | None = None,
    model_func: Callable[..., object] | None = None,
) -> None:
    """
    Update role config at runtime.
    - If model_func is provided, treat as fully configured callable.
    - Otherwise rebuild callable from binding/model/host/api_key/options.
    - max_async/timeout changes always trigger rewrap.
    """

行为要求:

  1. 原子替换角色函数与角色配置字段。
  2. 更新后下一次 aquery()/ainsert() 生效。
  3. 失败时不污染当前可用配置(回滚/不提交)。

七、环境变量示例(统一命名)

7.1 仅改模型名(同 Provider)

LLM_BINDING=gemini
LLM_MODEL=gemini-2.5-flash
LLM_BINDING_API_KEY=xxx

KEYWORD_LLM_MODEL=gemini-2.0-flash-lite
QUERY_LLM_MODEL=gemini-2.5-pro

7.2 独立并发与超时

MAX_ASYNC=4
MAX_ASYNC_EXTRACT_LLM=8
MAX_ASYNC_QUERY_LLM=12

LLM_TIMEOUT=180
LLM_TIMEOUT_EXTRACT_LLM=240
LLM_TIMEOUT_QUERY_LLM=300

7.3 跨 Provider(必须显式 model/api_key/host)

# base
LLM_BINDING=ollama
LLM_MODEL=qwen2.5:7b
LLM_BINDING_HOST=http://localhost:11434

# extract -> openai
EXTRACT_LLM_BINDING=openai
EXTRACT_LLM_MODEL=gpt-4o
EXTRACT_LLM_BINDING_API_KEY=sk-xxx
EXTRACT_LLM_BINDING_HOST=https://api.openai.com/v1
EXTRACT_OPENAI_LLM_TEMPERATURE=0.1

7.4 角色函数修改配置示例(同 Provider,独立函数独立队列)

# base (openai)
LLM_BINDING=openai
LLM_MODEL=gpt-4o-mini
LLM_BINDING_HOST=https://api.openai.com/v1
LLM_BINDING_API_KEY=sk-base
MAX_ASYNC=4
LLM_TIMEOUT=180

# extract 角色:改模型 + 改 key + 改并发 + 改超时 + 改 provider 参数
EXTRACT_LLM_MODEL=gpt-4o
EXTRACT_LLM_BINDING_API_KEY=sk-extract
MAX_ASYNC_EXTRACT_LLM=10
LLM_TIMEOUT_EXTRACT_LLM=300
EXTRACT_OPENAI_LLM_TEMPERATURE=0.1
EXTRACT_OPENAI_LLM_MAX_COMPLETION_TOKENS=6000

# keyword 角色:轻量模型与小并发
KEYWORD_LLM_MODEL=gpt-4o-mini
MAX_ASYNC_KEYWORD_LLM=3
LLM_TIMEOUT_KEYWORD_LLM=60
KEYWORD_OPENAI_LLM_TEMPERATURE=0.0

# query 角色:强模型与大并发
QUERY_LLM_MODEL=o3
QUERY_LLM_BINDING_API_KEY=sk-query
MAX_ASYNC_QUERY_LLM=16
LLM_TIMEOUT_QUERY_LLM=360
QUERY_OPENAI_LLM_REASONING_EFFORT=high

八、实施清单

  1. lightrag/lightrag.py
  2. lightrag/operate.py
  3. lightrag/api/config.py
  4. lightrag/api/lightrag_server.py
  5. lightrag/llm/binding_options.py
  6. lightrag/api/routers/ollama_api.py
  7. env.example
  8. 测试与文档同步

九、测试与验收标准

9.1 回归与兼容

  1. 不设置任何新变量,行为与旧版一致。
  2. 三角色函数均独立创建,且各自拥有独立并发队列。

9.2 路由正确性

  1. 只设 KEYWORD_LLM_MODEL,仅关键词提取链路模型变化。
  2. bypass 和 emulation 直调均走 query 角色函数。

9.3 隔离性

  1. 即便角色参数与 base 一致,角色函数对象也应与 base 不同。
  2. 三角色队列互不干扰(压测下无串队)。

9.4 跨 Provider 校验

  1. 跨 Provider 且缺失 model -> 启动失败并给出明确错误。
  2. 跨 Provider 且缺失 api_key(对于需 key 的 provider)-> 启动失败。
  3. 跨 Provider 且缺失 host -> 启动失败并给出明确错误。

9.5 动态更新

  1. update_llm_role_config 修改 max_async/timeout/provider 后下一次调用生效。
  2. 非法更新不影响已有可用配置。

十、风险与缓解

  1. 配置组合增多导致复杂度上升
    处理:引入统一配置解析与校验模块,避免散落判断。

  2. 环境变量命名不一致导致迁移混乱
    处理:统一采用新命名并在发布说明中明确“无旧命名兼容”。

  3. 跨 Provider 配置漏填导致启动失败
    处理:在错误信息中明确缺失字段与角色名(如 EXTRACT_LLM_BINDING_HOST)。

十一、非目标

  1. 本期不调整 Embedding 的角色化路由。
  2. 本期不引入更多角色(如 rerank 专用 LLM)。

十二、后续工作

在把Ray-Anything整合到LightRAG过程中需要引入VLM角色。

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttrackedIssue is tracked by project

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions