本地 + 云端智能路由(Local + Cloud Routing)
为什么需要路由?
现在有两种模型可用:
| 特性 | 本地(Ollama) | 云端(DeepSeek) |
|---|---|---|
| 成本 | 免费 | 按 token 计费 |
| 速度 | 取决于硬件 | 快且稳定 |
| 隐私 | 数据不出本地 | 数据发送到云端 |
| 能力 | 受限于模型大小 | 更强的大模型 |
智能路由的核心思路是:简单任务走本地,复杂任务走云端,在成本、速度、隐私之间找平衡。
路由策略(3种)
规则路由(Rule-based)
根据 prompt 特征直接判断:
- prompt 长度 < N token → 本地
- 包含敏感关键词 → 本地(隐私)
- 要求高精度输出 → 云端
成本感知路由(Cost-aware)
估算 token 数量,超过预算阈值才用云端。
LLM 分类路由(Classifier-based)
用一个轻量本地模型先判断问题难度,再决定路由目标------这是最聪明的方式。
示例代码
py
def run_local_model(prompt: str):
llm = ChatOllama(model="qwen2.5:1.5b", client_kwargs={"trust_env": False})
messages = [HumanMessage(content=prompt)]
result = llm.invoke(messages)
return result.content
def run_cloud_model(prompt: str):
env = validate_env()
if not env:
return
(
api_key,
base_url,
model,
*_,
) = env
llm = ChatOpenAI(model=model, api_key=api_key, base_url=base_url)
messages = [HumanMessage(content=prompt)]
result = llm.invoke(messages)
return result.content
class ModelType(Enum):
LOCAL = "local"
CLOUD = "cloud"
forbidden_words = [
"sexual",
"nude",
"porn",
"sex",
"naked",
"sexy",
]
def count_tokens(messages: str):
enc = tiktoken.get_encoding("cl100k_base")
count = len(enc.encode(messages)) + 4
return count
def router(prompt: str) -> str:
if len(prompt) > 50 or prompt in forbidden_words or count_tokens(prompt) > 1000:
return ModelType.CLOUD
else:
return ModelType.LOCAL
def dialogue(prompt: str) -> str:
type = router(prompt)
if type == ModelType.LOCAL:
print("使用本地模型")
return run_local_model(prompt)
else:
print("使用云端模型")
return run_cloud_model(prompt)
RunnableLambda 与路由链
LangChain 提供了 RunnableLambda,可以把任意普通函数,包装成 LangChain 的 Runnable,从而能接入 LCEL 管道(| 链式组合)。
LCEL 中的 Runnable
LangChain 的 LCEL 里,很多东西都是 Runnable:
- ChatOpenAI
- PromptTemplate
- StrOutputParser
- RunnableParallel
它们都可以用 | 串联:
py
chain = prompt | llm | parser
result = chain.invoke({"question": "..."})
RunnableLambda 写法
对比一下:
py
# 用 RunnableLambda 的写法
from langchain_core.runnables import RunnableLambda
router_chain = RunnableLambda(router) # 包装路由函数
# 可以 .invoke()、.batch()、.stream(),也能接在其他链后面用 |
result = router_chain.invoke(prompt)
只有当你需要把路由器接入更大的 LangChain 流水线时,才值得用它。