RAG from Scratch-优化-routing


路由与查询构建篇

Part 10: Logical & Semantic Routing

逻辑路由 (Logical Routing)

使用函数调用进行分类路由。

python 复制代码
from typing import Literal
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

# 1. 定义路由数据模型
class RouteQuery(BaseModel):
    """Route a user query to the most relevant datasource."""
    datasource: Literal["python_docs", "js_docs", "golang_docs"] = Field(
        ...,
        description="Given a user question choose which datasource would be most relevant"
    )

# 2. 使用结构化输出
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm = llm.with_structured_output(RouteQuery)

# 3. 创建路由 Prompt
system = """You are an expert at routing a user question to the appropriate data source.
Based on the programming language the question is referring to, route it to the relevant data source."""

prompt = ChatPromptTemplate.from_messages([
    ("system", system),
    ("human", "{question}"),
])

router = prompt | structured_llm

# 4. 测试路由
question = """Why doesn't the following code work:
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages(["human", "speak in {language}"])
"""

result = router.invoke({"question": question})
print(result.datasource)  # 输出: python_docs
python 复制代码
# 5. 根据路由结果执行不同逻辑
def choose_route(result):
    if "python_docs" in result.datasource.lower():
        return "chain for python_docs"
    elif "js_docs" in result.datasource.lower():
        return "chain for js_docs"
    else:
        return "golang_docs"

from langchain_core.runnables import RunnableLambda

full_chain = router | RunnableLambda(choose_route)
result = full_chain.invoke({"question": question})
print(result)  # 输出: chain for python_docs

语义路由 (Semantic Routing)

基于 Embedding 相似度进行路由。

python 复制代码
from langchain.utils.math import cosine_similarity
from langchain_openai import OpenAIEmbeddings

# 1. 定义不同领域的 Prompt 模板
physics_template = """You are a very smart physics professor.
You are great at answering questions about physics in a concise manner.
Here is a question: {query}"""

math_template = """You are a very good mathematician.
You are great at answering math questions.
Here is a question: {query}"""

# 2. 对 Prompt 进行 Embedding
embeddings = OpenAIEmbeddings()
prompt_templates = [physics_template, math_template]
prompt_embeddings = embeddings.embed_documents(prompt_templates)

# 3. 路由函数
def prompt_router(input):
    query_embedding = embeddings.embed_query(input["query"])
    similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
    most_similar = prompt_templates[similarity.argmax()]
    print("Using MATH" if most_similar == math_template else "Using PHYSICS")
    return PromptTemplate.from_template(most_similar)

# 4. 构建 Chain
chain = (
    {"query": RunnablePassthrough()}
    | RunnableLambda(prompt_router)
    | ChatOpenAI()
    | StrOutputParser()
)

# 5. 测试
answer = chain.invoke("What's a black hole")
# 输出: Using PHYSICS
# A black hole is a region in space where gravity is so strong...

Part 11: Query Structuring

核心思想

将自然语言查询转换为结构化的数据库查询,支持元数据过滤。

定义查询结构

python 复制代码
import datetime
from typing import Optional
from langchain_core.pydantic_v1 import BaseModel, Field

class TutorialSearch(BaseModel):
    """Search over a database of tutorial videos."""

    content_search: str = Field(
        ...,
        description="Similarity search query applied to video transcripts.",
    )
    title_search: str = Field(
        ...,
        description="Alternate version to apply to video titles.",
    )
    min_view_count: Optional[int] = Field(
        None,
        description="Minimum view count filter, inclusive.",
    )
    max_view_count: Optional[int] = Field(
        None,
        description="Maximum view count filter, exclusive.",
    )
    earliest_publish_date: Optional[datetime.date] = Field(
        None,
        description="Earliest publish date filter, inclusive.",
    )
    latest_publish_date: Optional[datetime.date] = Field(
        None,
        description="Latest publish date filter, exclusive.",
    )
    min_length_sec: Optional[int] = Field(
        None,
        description="Minimum video length in seconds.",
    )
    max_length_sec: Optional[int] = Field(
        None,
        description="Maximum video length in seconds.",
    )

查询分析器

python 复制代码
system = """You are an expert at converting user questions into database queries.
You have access to a database of tutorial videos about a software library.
Given a question, return a database query optimized to retrieve the most relevant results."""

prompt = ChatPromptTemplate.from_messages([
    ("system", system),
    ("human", "{question}"),
])

llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm = llm.with_structured_output(TutorialSearch)
query_analyzer = prompt | structured_llm

测试示例

python 复制代码
# 示例 1: 简单查询
query_analyzer.invoke({"question": "rag from scratch"})
# 输出:
# content_search: rag from scratch
# title_search: rag from scratch

# 示例 2: 带时间过滤
query_analyzer.invoke({"question": "videos on chat langchain published in 2023"})
# 输出:
# content_search: chat langchain
# title_search: chat langchain
# earliest_publish_date: 2023-01-01
# latest_publish_date: 2024-01-01

# 示例 3: 带时长过滤
query_analyzer.invoke({
    "question": "how to use multi-modal models in an agent, only videos under 5 minutes"
})
# 输出:
# content_search: multi-modal models agent
# title_search: multi-modal models agent
# max_length_sec: 300
相关推荐
补三补四4 小时前
参数高效微调技术详解:理论基础与实践应用
人工智能·深度学习·机器学习
爬山算法4 小时前
MongoDB(80)如何在MongoDB中使用多文档事务?
数据库·python·mongodb
njsgcs4 小时前
怎么把cad从右边的图案特征学习到会标注按左边这样 wl图核
人工智能·cad
5系暗夜孤魂4 小时前
系统越复杂,越需要“边界感”:从 Java 体系理解大型工程的可维护性本质
java·开发语言
二月夜4 小时前
Spring循环依赖深度解析:从三级缓存原理到跨环境“灵异”现象
java·spring
hughnz4 小时前
Palantir Technologies公司的竞争格局
人工智能·microsoft
陈天伟教授4 小时前
智能体架构:大语言模型驱动的自主系统深度解析与演进研究(一)
人工智能·语言模型·架构
R²AIN SUITE4 小时前
AI 智能体重构医药价值链:研发 / 临床 / 供应链三大场景深度落地与量化收益
人工智能
YuanDaima20484 小时前
基于 LangChain 1.0 的检索增强生成(RAG)实战
人工智能·笔记·python·langchain·个人开发·langgraph
大力财经4 小时前
纳米漫剧流水线接入满血版Seedance 2.0 实现工业级AI漫剧确定性交付
大数据·人工智能