目录

  1. 前言
  2. 进阶环境依赖补充
  3. 模块一:多模态文档解析与结构化分块落地
  4. 模块二:用户反馈闭环与 RAG 效果自迭代
  5. 模块三:Agent 编排升级:从 ReAct 到 Plan-and-Execute
  6. 进阶开发踩坑复盘
  7. 进阶面试考点自检清单
  8. 写在最后

前言

上篇把基础版 RAG+Agent 知识库的架子搭完之后,这段时间接着做了三轮进阶优化,把之前留的三个扩展点全部落地了:多模态文档解析、用户反馈闭环、更复杂的 Agent 编排。

基础版能跑通纯文本问答,但真要落到实际业务里,还会碰到很多问题:PDF 里的表格图片解析不出来、上线后效果没法持续优化、复杂多步任务 Agent 容易跑偏。这篇就是把这些进阶能力的实现过程拆解开,顺着代码看思路不脱节。

这篇是基础篇的进阶延伸,默认你已经跑通了基础的向量检索、ReAct Agent 链路。 —— 讲清楚项目的演进思路、踩过的进阶坑、落地的优化手段。


2. 进阶环境依赖补充

在上篇依赖的基础上,补装几个进阶功能需要的包,尽量保持轻量化,不引入太重的组件。多模态解析我用了unstructured处理 PDF 结构化,图片 OCR 直接调用通义多模态 API,不用本地装大模型;反馈数据用 Python 自带的 SQLite 存,不用额外搭服务。

pip install unstructured[pdf] pillow python-multipart

需要额外准备:

  • 通义千问 VL 多模态模型 API 权限(处理图片 OCR)
  • 带表格、图片的测试 PDF 文档(用来验证多模态解析效果)

3. 模块一:多模态文档解析与结构化分块落地

基础版只能处理纯文本,真拿企业里的 PDF 文档测试就会发现坑很多:表格解析出来全是乱行、图片里的文字直接丢失、章节层级完全没保留,很多关键信息明明在文档里,就是检索不到。

我最终的方案是:先做结构化文档解析,区分文本、表格、图片三类内容 —— 表格转成带结构的 Markdown 文本,图片做 OCR 提取文字,再基于章节层级做父子块分块,每个内容块都带上所属章节的标题信息,既保留语义上下文,又保证检索粒度。

核心代码实现

from unstructured.partition.pdf import partition_pdf
from langchain.schema import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter

def parse_pdf_multimodal(pdf_path: str):
    """
    多模态PDF解析:区分文本、表格、图片,结构化提取内容
    表格转Markdown格式,图片调用OCR提取文字
    """
    elements = partition_pdf(
        filename=pdf_path,
        strategy="hi_res",  # 高清解析模式,能识别表格、图片
        extract_images_in_pdf=True,
        infer_table_structure=True,
        chunking_strategy="by_title"  # 按标题层级初步拆分
    )

    docs = []
    current_section = "前言"  # 记录当前章节标题,作为父块上下文

    for elem in elements:
        # 记录章节标题,作为所有子块的上下文
        if elem.category == "Title":
            current_section = elem.text.strip()
            continue

        # 处理表格:转成Markdown格式,保留行列结构
        if elem.category == "Table":
            table_md = f"**表格:{current_section}**\n{elem.metadata.text_as_html}"
            docs.append(Document(page_content=table_md, metadata={"type": "table", "section": current_section, "source": pdf_path}))

        # 处理图片:调用多模态模型做OCR提取文字
        elif elem.category == "Image":
            # 这里简化调用通义VL模型做OCR,实际可替换成本地OCR
            image_text = ocr_image_by_vl(elem.metadata.image_path)
            image_content = f"**图片说明:{current_section}**\n图片文字内容:{image_text}"
            docs.append(Document(page_content=image_content, metadata={"type": "image", "section": current_section, "source": pdf_path}))

        # 处理普通文本
        else:
            text_content = f"**章节:{current_section}**\n{elem.text.strip()}"
            docs.append(Document(page_content=text_content, metadata={"type": "text", "section": current_section, "source": pdf_path}))

    return docs

def advanced_split_documents(docs: list[Document]):
    """
    进阶分块:基于章节的父子块结构,保证每个块都有章节标题上下文
    避免纯文本分块丢失层级信息
    """
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=600,
        chunk_overlap=80,
        separators=["\n\n", "\n", "。", "!", "?", " ", ""],
        length_function=len
    )
    chunks = []
    for doc in docs:
        # 章节标题部分不拆分,保证每个块都带上下文
        section_header = f"**{doc.metadata['section']}**\n"
        content_body = doc.page_content.split("\n", 1)[1] if "\n" in doc.page_content else doc.page_content
        
        body_chunks = text_splitter.split_text(content_body)
        for body in body_chunks:
            full_chunk = section_header + body
            chunks.append(Document(page_content=full_chunk, metadata=doc.metadata))
    
    print(f"进阶分块完成,共{len(chunks)}个文档块,均携带章节上下文")
    return chunks

补充一个极简的图片 OCR 调用封装,实际项目里可以替换成本地 PaddleOCR:

import dashscope
from dashscope import MultiModalConversation

def ocr_image_by_vl(image_path: str) -> str:
    """调用通义多模态模型提取图片文字"""
    messages = [{
        "role": "user",
        "content": [
            {"image": image_path},
            {"text": "请提取这张图片里的所有文字内容,直接输出文字,不要多余描述。"}
        ]
    }]
    
    response = MultiModalConversation.call(
        model="qwen-vl-plus",
        messages=messages,
        api_key=DASHSCOPE_API_KEY
    )
    return response.output.choices[0].message.content[0]["text"]

实测下来,带章节上下文的分块,比纯文本分块的召回率高了 15% 左右,尤其是跨章节的相似内容,不会再混在一起;表格和图片的内容也能正常被检索到,不会出现 “文档里有答案但搜不到” 的情况。

对应面试考点(附答题思路)

  1. 多模态 RAG 怎么处理表格、图片这类非文本内容? 答:核心思路是把非文本内容转成可检索的文本向量,同时尽量保留结构信息。 表格:优先做结构化提取,转成 Markdown 或者 HTML 格式,保留行列关系,再参与分块和向量化;不能直接把表格拆成零散文字,不然结构全丢了,检索到也没用。 图片:用 OCR 提取文字内容,同时补充图片所在的章节上下文,一起做成文档块;如果是流程图、架构图这类纯视觉内容,还可以用多模态模型生成文字描述,再参与检索。 关键是不能丢上下文,不管是表格还是图片,都要带上所属章节的标题信息,不然检索出来不知道是哪部分的内容。

  2. 什么是父子块 / 分层分块?它比普通分块好在哪里? 答:父子块就是分层分块,父块是大的语义单元(比如章节、大段落),子块是细粒度的检索单元;检索的时候先匹配子块,再把对应的父块完整上下文传给大模型。 好处很明显:既保证了检索的细粒度,又不会丢失完整的语义上下文,解决了普通分块 “切太碎没上下文、切太粗噪声多” 的矛盾;而且每个子块都带上层章节信息,能减少跨章节的误匹配,提升召回准确率。 我们用的带章节标题的分块,就是轻量化的分层分块,每个内容块都带上所属章节,实现简单,提升效果很明显。

  3. PDF 文档解析有哪些常见的坑?怎么优化解析准确率? 答:常见的坑特别多:比如扫描版 PDF 全是图片,直接解析不出文字;带复杂排版的 PDF,列错乱、表格错位;加密 PDF 解析失败;还有页眉页脚、页码这类噪声内容混进去。 优化方式:首先区分原生 PDF 和扫描版,扫描版必须走 OCR;复杂排版优先用高清解析模式,识别表格和文档结构;然后做后处理,过滤页眉页脚、页码、水印这些噪声;表格尽量保留结构化格式,不要纯文本平铺。 落地里不用追求 100% 解析准确,核心是保证关键内容能被检索到,噪声内容尽量过滤掉。

  4. 除了按字符分块,你还了解哪些进阶分块策略? 答:除了基础的递归字符分块,还有几种常用的进阶策略: 一是语义分块,根据 Embedding 相似度来切,语义相近的放在一个块里,变化大的地方切开,语义完整性最好,但速度慢、成本高。 二是结构化分块,根据文档本身的标题层级、段落结构来切,比如 Markdown、HTML 文档按 #标题层级拆分,保留文档原生结构。 三是父子块分层分块,粗粒度存上下文,细粒度做检索,兼顾准确率和上下文完整度。 落地里一般用结构化分块 + 递归字符分块的组合,性价比最高,纯语义分块成本太高,适合小体量高要求的场景。

易错点提醒:很多人回答多模态 RAG,只会说 “图片转文字”,说不出表格怎么处理、怎么保留结构、怎么解决上下文丢失的问题;还有人不知道分层分块的思路,只会说固定大小分块,这都是深度不够的表现。


4. 模块二:用户反馈闭环与 RAG 效果自迭代

基础版搭完只是开始,RAG 系统不是上线就完事了,实际用起来总会有答非所问、检索不准的情况。如果全靠人工排查优化,效率特别低。所以我加了一套轻量的用户反馈闭环,让系统能基于用户的点赞点踩数据,自动迭代优化效果。

核心逻辑很简单:用户对答案点赞 / 点踩,系统把问题、检索结果、用户反馈存下来;积累一定数据后,自动做两件事:一是把负反馈的问题和错误答案加入黑名单,下次检索时过滤;二是用正负样本优化 query 改写的 Prompt,提升检索匹配度。不用动模型微调,成本很低,见效很快。

核心代码实现

import sqlite3
from datetime import datetime

# 初始化反馈数据库
def init_feedback_db():
    conn = sqlite3.connect("./feedback.db")
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS rag_feedback (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            session_id TEXT,
            user_query TEXT,
            answer TEXT,
            retrieved_docs TEXT,
            feedback_type INTEGER,  # 1点赞 0点踩
            create_time TEXT
        )
    ''')
    conn.commit()
    conn.close()

# 上报用户反馈
def save_feedback(session_id: str, user_query: str, answer: str, retrieved_docs: list, feedback_type: int):
    """保存用户反馈,点赞/点踩"""
    conn = sqlite3.connect("./feedback.db")
    cursor = conn.cursor()
    docs_str = "\n---\n".join([doc.page_content for doc in retrieved_docs])
    cursor.execute(
        "INSERT INTO rag_feedback VALUES (NULL, ?, ?, ?, ?, ?, ?)",
        (session_id, user_query, answer, docs_str, feedback_type, datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    )
    conn.commit()
    conn.close()

# 基于负反馈优化检索:过滤低质量文档块
def get_negative_block_list() -> set:
    """
    提取多次被点踩的文档片段,加入检索黑名单
    同一片段被3次以上点踩就过滤,避免单点噪声
    """
    conn = sqlite3.connect("./feedback.db")
    cursor = conn.cursor()
    cursor.execute('''
        SELECT retrieved_docs FROM rag_feedback 
        WHERE feedback_type = 0
        GROUP BY retrieved_docs
        HAVING COUNT(*) >= 3
    ''')
    bad_docs = set([row[0] for row in cursor.fetchall()])
    conn.close()
    return bad_docs

# 带黑名单过滤的检索器
def get_optimized_retriever(vector_store):
    base_retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 12})
    compression_retriever = ContextualCompressionRetriever(
        base_compressor=reranker,
        base_retriever=base_retriever
    )
    
    # 包装一层,过滤黑名单内容
    class FilteredRetriever(BaseRetriever):
        def _get_relevant_documents(self, query, *, run_manager=None):
            docs = compression_retriever.get_relevant_documents(query)
            bad_docs = get_negative_block_list()
            # 过滤掉多次负反馈的片段
            filtered = [doc for doc in docs if doc.page_content not in bad_docs]
            return filtered[:3]  # 最终保留Top3
    
    return FilteredRetriever()

除了黑名单过滤,我还做了 query 改写 Prompt 的自动优化:定期把用户点踩的问题整理出来,让大模型分析检索失败的原因,自动优化 query 改写的提示词,不用人工一点点调。

这套机制跑起来之后,不用天天盯着日志查问题,用户用得越多,系统就越准,属于典型的低成本高收益优化。

对应面试考点(附答题思路)

  1. RAG 系统上线后,怎么持续优化效果?有哪些迭代手段? 答:我一般从四个方向持续迭代,成本从低到高: 第一是用户反馈闭环,也是成本最低的。做点赞点踩功能,收集正负样本,先做低质内容过滤,再优化分块、Prompt,见效最快。 第二是检索侧优化,基于 bad case 调分块参数、优化分隔符、加 query 改写、调整 TopK 和 Rerank 参数,还可以加关键词混合检索,补充 BM25 召回。 第三是 Prompt 侧优化,针对高频错误场景优化系统提示词,加示例、加强约束,减少幻觉和答非所问。 第四是模型侧优化,数据积累多了之后,可以微调 Embedding 模型或者 Rerank 模型,适配垂直领域;成本最高,但提升上限也最高。 落地里肯定是先做低成本的,反馈闭环和参数调优优先,最后再考虑微调模型。

  2. 怎么设计 RAG 的用户反馈闭环?核心流程是什么? 答:核心是 “数据收集→问题分析→优化落地→效果验证” 的闭环。 首先是数据收集:前端做点踩点赞按钮,用户点踩的时候可以补充原因,后台记录问题、检索结果、答案、反馈类型、时间。 然后是问题归因:区分是检索不准(没搜到正确内容)、还是生成幻觉(搜到了但答错了)、还是文档本身没有答案。 接着是优化落地:检索问题就调分块、加 query 改写、优化 Rerank;生成问题就改 Prompt、调 temperature;文档缺失就补充知识库。 最后是效果验证:优化后回归测试 bad case,看是否解决,同时监控整体好评率变化。 自动化程度可以逐步提升,先人工抽样分析,数据多了再做自动过滤、自动优化 Prompt。

  3. 怎么衡量 RAG 系统的效果好坏?有哪些评估指标? 答:分离线指标和线上指标两类。 离线指标:构建测试集,评估召回率、准确率,比如 TopK 召回率、Rerank 后的命中率;还有生成答案的指标,比如忠实度(有没有幻觉)、相关性、完整度,可以用大模型打分,也可以人工评估。 线上指标:最核心的是用户好评率 / 点踩率,最真实反映用户体验;还有检索耗时、首 token 延迟、答案平均长度这些性能指标;另外还有 “无答案率”,就是多少问题知识库答不上来,反映知识库的覆盖度。 不能只看向量相似度分数,那个不代表真实效果,最终还是要结合人工评估和线上用户反馈。

  4. 用户反馈数据有噪声怎么办?比如用户乱点踩、误操作? 答:不能单条反馈就改系统,一定要做噪声过滤,不然反而会越优化越差。 首先做频次过滤,同一个问题、同一个文档片段,多次出现负反馈才纳入优化,比如 3 次以上,避免单点误操作影响。 然后做置信度判断,比如用户点踩的时候补充原因,有明确原因的优先级高;还可以用大模型自动判断负反馈是否合理,排除明显误判的。 最后优化的时候灰度生效,先在小流量上验证效果,没问题再全量,避免一次坏数据带偏整个系统。

易错点提醒:很多人只会说 “做用户反馈”,说不出闭环的完整流程、怎么衡量效果、怎么处理噪声,一听就是没真正落地过。


5. 模块三:Agent 编排升级:从 ReAct 到 Plan-and-Execute

基础版用的 ReAct Agent,处理单工具、简单任务没问题,但碰到复杂的多步任务就容易跑偏。比如用户问 “帮我查一下销售部的年假规则,再算一下入职满 2 年能休多少天,最后整理成一句话总结”,ReAct 经常走一步看一步,算完天数就忘了前面的规则,或者重复调用工具。

所以我做了 Agent 编排的升级,引入 Plan-and-Execute 模式:先让大模型做整体规划,把任务拆成几个子步骤,明确每个步骤用什么工具;然后按步骤依次执行,收集结果;最后把所有结果整合起来,输出最终答案。复杂任务的完成度提升特别明显。

核心代码实现

from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.agents import AgentExecutor, create_plan_and_execute_agent

# 1. 规划Prompt:让大模型拆解任务步骤
plan_prompt = PromptTemplate(
    input_variables=["input", "tools"],
    template="""
你是一个任务规划师,请根据用户的问题,拆解成清晰的执行步骤。
可用工具:{tools}

要求:
1. 拆解成2-5个清晰的子步骤,每个步骤对应一个工具
2. 明确每个步骤的输入和目标
3. 步骤之间要逻辑连贯,最终能解决用户问题

用户问题:{input}
请输出执行计划:
"""
)

# 2. 构建Plan-and-Execute Agent
plan_agent = create_plan_and_execute_agent(
    llm=llm,
    tools=tools,
    planner_llm=llm,
    planner_prompt=plan_prompt
)

plan_agent_executor = AgentExecutor(
    agent=plan_agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True,
    max_iterations=10
)

# 3. 动态调度:简单任务用ReAct,复杂任务自动升级
def smart_agent_invoke(user_input: str, chat_history):
    """
    智能调度:先判断任务复杂度
    单工具简单问题用ReAct,省token速度快;
    多工具复杂问题用Plan-and-Execute,完成度更高
    """
    # 简单复杂度判断:包含多个动作关键词、多步需求的走规划模式
    complex_keywords = ["分别", "同时", "先", "再", "然后", "计算", "整理", "汇总"]
    is_complex = any(k in user_input for k in complex_keywords)
    
    if is_complex:
        executor = plan_agent_executor
    else:
        executor = agent_executor  # 基础版ReAct执行器
    
    result = executor.invoke({
        "input": user_input,
        "chat_history": chat_history
    })
    return result["output"]

我做了动态调度,不是所有任务都走规划模式。简单问题还用 ReAct,速度快、省 token;复杂多步任务才自动切规划模式,兼顾了效率和完成度。实测下来,多步任务的完成准确率从原来的 50% 左右提升到了 85% 以上,代价就是 token 消耗大概多了 30%,延迟也高一点,但复杂场景下体验提升很明显。

对应面试考点(附答题思路)

  1. ReAct 和 Plan-and-Execute 分别适合什么场景?怎么选型? 答:ReAct 是边想边做,走一步看一步,适合工具少、任务简单、单步就能解决的场景,优点是轻量、省 token、灵活、能动态调整,缺点是复杂多步任务容易跑偏,没有全局观。 Plan-and-Execute 是先规划再执行,先把任务拆成子步骤再一步步做,适合多工具协同、多步骤、逻辑复杂的任务,优点是任务完成度高、逻辑连贯,缺点是 token 消耗大、延迟高、不够灵活,规划错了容易一路错到底。 落地里不用二选一,可以做动态调度:简单问题用 ReAct,复杂问题自动切规划模式,兼顾效率和效果。我们就是这么做的,大部分简单问答走轻量模式,复杂任务自动升级。

  2. 复杂 Agent 任务的拆解原则是什么?怎么保证规划的合理性? 答:拆解核心原则是:单步单一目标、步骤粒度适中、依赖关系清晰、可落地执行。 首先每个子步骤只做一件事,对应一个工具,不要一步里塞多个动作;然后粒度要合适,不能太粗也不能太细,太粗执行不了,太细效率低;还要理清楚步骤的依赖关系,前面步骤的输出可以作为后面步骤的输入。 保证规划合理性的手段:一是在 Prompt 里给示例,展示正确的拆解方式;二是加步骤数量限制,防止拆得太碎;三是执行中做校验,如果某一步执行失败,可以触发重规划,调整步骤。

  3. 多工具协同的场景下,怎么处理工具之间的数据传递? 答:核心是做好上下文管理,把上一个工具的输出,作为下一个工具的输入上下文。 一般有两种方式:一种是全量传递,把每一步的执行结果都追加到全局上下文里,后面的步骤和最终整合都能看到所有历史结果;另一种是显式映射,规划的时候就定义好每个步骤的输入来自哪一步的输出,数据传递更精准。 轻量化实现一般用全量传递就行,靠大模型自己从上下文里提取需要的信息;复杂编排可以用显式的变量传递,比如 LangGraph 里的状态机,每个节点的输入输出都定义清楚。

  4. 你了解哪些 Agent 编排框架 / 模式?除了 ReAct 和 Plan-and-Execute 答:还有几种主流的模式: 一是 Function Calling 模式,大模型原生支持工具调用,直接输出工具名和参数,不用 ReAct 的思考格式,速度更快、格式更稳,现在大部分大模型都支持,是生产里很常用的。 二是状态机模式,比如 LangGraph,把 Agent 的每个步骤定义成节点,按状态流转执行,支持分支、循环、回退,适合做复杂的工作流编排,可控性最强。 三是多 Agent 模式,多个不同角色的 Agent 协同,比如规划 Agent、执行 Agent、评审 Agent,各司其职,适合特别复杂的大型任务。 落地选型看任务复杂度:简单工具调用用原生 Function Calling;中等复杂度用 ReAct;复杂多步任务用 Plan-and-Execute;工作流类的复杂场景用 LangGraph 状态机。

易错点提醒:很多人只会背 ReAct 的概念,说不出其他编排模式,也讲不清选型依据;还有人不知道动态调度的思路,觉得两种模式只能二选一,这都是经验不足的表现。


6. 进阶开发踩坑复盘

进阶优化的坑比基础版多很多,挑几个最典型的说说,面试被问到 “做进阶优化遇到过什么难点”,直接拿这些说,特别真实。

坑 1:PDF 表格解析行列错位,结构全乱

现象:复杂合并单元格的表格,解析出来文字顺序全乱,行列对应不上,检索出来也没法看。 排查思路:对比了多种解析策略,发现默认的文本提取模式会把表格按行平铺,合并单元格就会错位;高清结构化模式能识别表格结构,但对特别复杂的表格还是会出错。 解决方案:优先输出 HTML/Markdown 格式的表格,尽量保留行列结构;对于解析失败的复杂表格, fallback 成 “表格标题 + 关键内容摘要” 的形式,保证核心信息能被检索到,不追求 100% 还原格式。

坑 2:负反馈噪声导致检索误伤

现象:有用户因为 “答案太长”“排版不好看” 点踩,导致正确的文档片段被加入黑名单,反而把正确答案过滤掉了。 排查思路:追溯黑名单里的内容,发现很多片段本身是正确的,只是用户体验问题被点踩,不是内容错误。 解决方案:提高负反馈的生效阈值,从 1 次改成 3 次,同一片段多次被点踩才过滤;同时区分反馈类型,把 “内容错误” 和 “体验不好” 分开,只有内容错误的反馈才影响检索逻辑;加人工审核环节,定期校验黑名单内容,避免误伤。

坑 3:规划模式 token 爆炸,延迟翻倍

现象:复杂任务走 Plan-and-Execute,规划 + 执行 + 总结,大模型调用次数多,token 消耗是 ReAct 的两倍多,延迟也很高。 排查思路:打印了完整链路的 token 消耗,发现规划 Prompt 写得太啰嗦,执行过程中又把所有历史结果全量传递,很多冗余内容。 解决方案:精简规划 Prompt,去掉无用套话;执行结果做摘要压缩,再传给下一步,不用全量传递;加上动态调度,只有真正复杂的任务才走规划模式,简单问题继续用 ReAct,整体平均 token 消耗只涨了 10% 左右。

对应进阶故障排查类面试题(附答题思路)

  1. 多模态 RAG 解析效果差,表格图片内容搜不到,怎么排查优化? 答:我会先定位问题环节,从解析→分块→检索逐层排查,先确认内容有没有被正确提取,再确认有没有被正确索引和召回。 第一步先定位根因:拿目标文档本地复现,打印解析后的原始内容,看表格、图片的文字有没有被提取出来。如果提取结果里根本没有对应内容,就是解析环节的问题;如果内容有但搜不到,就是分块或检索环节的问题。 如果是解析环节问题:

    • 表格:先看解析模式是不是用了纯文本平铺,换成高清结构化解析模式,输出 HTML/Markdown 格式,保留行列结构;复杂合并单元格的表格,做降级处理,提取表格标题 + 核心内容摘要,不追求 100% 格式还原,保证关键信息可检索。
    • 图片:先判断是原生 PDF 里的图片还是扫描版 PDF,扫描版必须全页 OCR;普通图片调用 OCR 或多模态模型提取文字,同时补充图片所在的章节标题作为上下文,避免孤立内容匹配不上。
    • 如果是分块检索问题:检查是不是分块把表格、图片的内容切碎了,结构被破坏;给表格、图片单独成块,并且强制带上所属章节的上下文信息,做成父子块结构,保证语义完整。
    • 检查向量相似度,图文转文字后语义和纯文本有差异,可以适当调大召回 TopK,再靠 Rerank 精排,提升召回率。
  2. 上线了反馈闭环后,检索效果反而下降了,可能是什么原因? 答:大概率是反馈数据的噪声和误伤导致的,常见原因集中在四点: 第一是负反馈阈值太低,单点误操作就生效。比如用户因为 “答案太长”“排版不好看” 点踩,不是内容错误,但系统直接把对应文档片段拉黑了,反而把正确答案过滤掉了;解决方式是提高生效阈值,比如同一片段累计 3 次以上负反馈才纳入黑名单,避免单点误判。 第二是反馈类型没区分,所有点踩都作用于检索。用户点踩的原因很多,有内容错误、答案不完整、体验不好,甚至误操作,如果全都当成检索错误去优化,就会误伤正确内容;解决方式是拆分反馈维度,区分 “内容错误”“答案不全”“体验问题”,只有内容错误类的反馈才调整检索逻辑。 第三是反馈数据有偏差,样本分布不均。比如某一类问题用户问得多,反馈也多,优化过度导致其他场景效果下降;解决方式是定期全量评估效果,不能只盯着反馈数据优化,还要兼顾整体召回率和准确率。 第四是黑名单粒度太粗,直接过滤整段内容。可能只是片段里部分信息有误,但整个片段都被拉黑了,连带正确内容也搜不到;解决方式是细化粒度,只过滤错误内容,或者做内容修正,不要整片拉黑。 排查的时候先看黑名单里的内容,校验是不是有误伤的正确片段,再回溯负反馈的原因,基本就能定位到问题。

  3. Agent 升级规划模式后,响应耗时大幅增加,怎么优化? 答:我会先把链路耗时拆开,定位瓶颈在哪个环节,再针对性优化。规划模式耗时增加主要来自三个地方:多了一次规划调用、执行轮次变多、上下文冗余传递。 首先做动态调度,不要所有任务都走规划模式。简单单步问题继续用轻量的 ReAct 或原生 Function Calling,只有多步复杂任务才触发 Plan-and-Execute,从整体上拉低平均耗时。我们就是这么做的,简单问答走轻量模式,复杂任务才升级,平均耗时只涨了 10% 左右。 然后优化规划阶段:精简规划 Prompt,去掉冗余的套话和示例,只保留核心约束;限制规划的步骤数量,比如最多拆 5 步,避免过度拆分;也可以用更小更快的模型做规划,不用和生成答案用同一个大模型。 接着优化执行阶段:控制每一步的上下文长度,执行结果做摘要压缩后再传给下一步,不要全量传递所有历史结果,避免上下文越来越长导致生成变慢;同时可以做并行执行,没有依赖关系的步骤同时调用工具,不用串行等待。 最后做工程优化:加结果缓存,常见的规划和工具调用结果直接复用;优化工具执行耗时,比如检索接口加缓存、下游服务提速;再加超时兜底,避免极端复杂任务长时间阻塞。 一般做完动态调度和上下文压缩,耗时就能降下来一大半。

把进阶篇的考点整理成清单,配合基础篇的内容。

多模态与分块进阶

  1. 多模态 RAG 处理表格、图片的方案
  2. 父子块 / 分层分块的原理与优势
  3. 进阶分块策略的对比与选型
  4. PDF 文档解析的常见问题与优化手段

反馈闭环与效果迭代

  1. RAG 上线后的持续优化手段
  2. 用户反馈闭环的设计与核心流程
  3. RAG 系统的效果评估指标
  4. 反馈数据噪声的处理方案

Agent 编排进阶

  1. ReAct 与 Plan-and-Execute 的选型与适用场景
  2. 复杂任务的拆解原则与规划合理性保障
  3. 多工具协同的数据传递方案
  4. 主流 Agent 编排模式与框架对比

8. 写在最后

从基础版到进阶版,整套知识库的演进思路其实很务实:先搭架子跑通核心链路,再针对真实业务里的痛点逐个优化,不追求一步到位,也不堆没用的技术概念。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐