大数跨境

LangChain:上下文工程从检索开始

LangChain:上下文工程从检索开始 AINLP
2026-11-14
6
导读:别让搜索先把上下文塞满

LangChain 的 Jess Weng 以一个竞品分析 Agent 为例,说明怎样搜索、分页读取和筛选材料,避免检索结果挤满上下文。以下是全文翻译。

要点

  • Agent 的上下文窗口有限,而信息检索通常会在三个地方出问题:搜索效率低、一次塞入太多上下文,以及引入不相关或过期的信息。
  • Deep Agents 的运行框架默认就会控制搜索结果的体量。内置文件系统工具先搜索再读取:glob 匹配文件名,grep 找出匹配的行,read_file 则按页读取。过大的工具结果会被卸载到文件中:完整内容留在文件里,Agent 只收到路径和预览。
  • 对于 RAG 等自定义检索工具,可以采用“检索、卸载、委派”的方式:把结果保存为文件,返回路径,再让子 Agent 读取。主 Agent 只接收相关内容。

想象一下,你正在参加一场开卷考试:理论上,所有答案都在手边,但找到正确答案反而更难了。

  • 应该从哪里开始找?
  • 什么时候才能确定,找到的资料是对的?
  • 怎样安排时间,才不会把精力都耗在重复搜索上?

高效找到正确的信息,并没有那么简单。

给 Agent 一个问题,它也会遇到类似的困境:上下文窗口有限,所以和人一样,它需要通过检索来完成某些任务,而不能把所有东西都记在脑子里。搜索时,Agent 常常遇到这些问题:

  • 搜索效率低:每次搜索都要消耗 token 和时间。如果 Agent 搜索得很笨拙,比如挨个打开文件看里面有什么,或者不断改写同一个查询,那么还没找到有用信息,上下文就已经被搜索结果塞满了。
  • 一次塞入太多上下文:一次搜索就返回整份数据导出,或者十几篇文档,会瞬间占满上下文窗口,真正的工作反而没有空间可用。
  • 上下文不相关或已过期:对模型来说,过时或跑题的信息看起来可能和正确答案一样可信。一旦进入上下文窗口,Agent 就可能在它的基础上继续推理,让错误沿着后续步骤一路传下去。

好在 Agent 有多种办法可以检索到正确的信息。Agent 运行框架的职责,就是根据任务有策略地运用这些方法,在合适的时间,把合适的上下文交给模型。

本文将介绍 Agent 检索面临的挑战,也就是如何只找到并取回当前任务必需的信息;随后讨论可以采用的方法,以及设计 Agent 运行框架时应该怎样考虑这些问题。我们会以 LangChain 的 Deep Agents 为例。

如果想了解信息进入上下文窗口后该怎样管理,可以阅读另一篇关于上下文工程的文章。

用一个竞品分析 Agent 举例

我们来看一个实际场景:竞品分析 Agent。每周一,它都会调研数百家竞争对手,按照团队模板写出报告。它需要:

  • 找到上周关于 A 公司的笔记。
  • 从一份庞大的市场数据导出中,查出某个具体的营收数字。
  • 搜索新闻档案,看看有没有新消息。

这三项工作对应不同的检索方式,也会遇到不同的问题。我们使用 Deep Agents 运行框架构建这个 Agent,因此可以直接利用几种内置方法来减少这些问题。

1. 搜索效率低

Agent 要为每家竞争对手找到三类信息。如果每一项都得尝试十几次,比如打开文件才知道里面是什么,或者因为资料用了不同的说法而反复改写查询,那么报告一个字都还没写,上下文窗口就已经装满了失败的搜索。

因此,最好的搜索是不必重复的搜索。这意味着,要为每项工作选对工具。

先用内置文件系统工具

文件系统是 Agent 查找信息最常用的途径之一。但如果 Agent 每次都打开文件看看里面有什么,效率就会很低:每读一个文件,其内容都会进入上下文窗口,光是找资料就足以把窗口塞满。

更好的办法是先检查文件名和关键词,就像开卷考试时先翻书后的索引,而不是逐页读完整本书。两种常见的文件系统工具正好可以完成这件事:

  • glob
     按文件名模式查找文件。例如,**/company-a*.md 可以找到上周关于 A 公司的笔记。
  • grep
     在文件内部搜索,而不把文件内容加载进上下文窗口。它匹配字面文本,可以返回匹配的文件、匹配的行,或者匹配次数。

每个 Deep Agent 都内置了这些工具,以及 ls、read_file、write_file 和 edit_file。无论文件存放在 Agent 状态、本地磁盘、持久化存储还是沙箱中,使用的都是同一套工具,因为它们都会经过 Agent 的 backend。

关键词不够用时再添加检索工具

知道要找哪些词时,grep 很好用,但它只能匹配你给出的确切文本。这会影响例子中的第三项工作:搜索新闻档案。如果 Agent 搜索“Company A funding”,就会漏掉标题为“Company A closes $50M Series B”的文章,尽管“A 公司完成 5000 万美元 B 轮融资”正是它要找的消息。

如果希望按含义而不是确切用词进行搜索,也就是语义搜索,就可以添加一个由向量存储支持的检索工具。凡是 Agent 可以调用来获取信息的工具,都可以算检索工具:它接收查询,在某个数据源中查找,再返回找到的内容,也就是最接近的匹配结果。

不过,转向语义搜索之前,可以先试试同义词。grep 只能匹配给定的词,但选择这些词的 Agent 已经知道,“funding”也可能写成“Series B”或“raised”。告诉它搜索无果时换个说法,它就能针对每个词分别运行一次 grep。对于只有团队内部才使用的词,比如内部产品名,可以把它们列在一个 skill 中。用词可预测时,这种方法很有效;无法预测时,才需要语义搜索。

检索工具怎样匹配,取决于背后的数据源。向量存储按含义匹配,但检索工具也可以连接其他数据源:

  • SQL 数据库:用于结构化查询,例如销售数据或客户记录。
  • 搜索 API:用于获取网上的最新信息。
  • MCP 服务器:用于调用别人已经构建好的工具,例如文档搜索。

想进一步了解语义搜索的工作原理,可以阅读 LangChain 的相关指南。

让 Agent 决定何时搜索

很多检索系统会在开始时搜索一次,然后给出答案。这适用于 FAQ 机器人,也就是两步式 RAG,却不适合我们这个 Agent。假设它调研 C 公司到一半,发现了一篇此前不知道的产品发布报道,于是又需要查询该产品的价格;一开始它根本不可能预先知道要查这个。如果只允许搜索一次,最后的报告就会漏掉这部分信息。

研究型 Agent 需要的是 Agent 驱动的检索,而不是两步式 RAG:由 Agent 自己决定何时搜索、搜索什么,以及是否还需要更多信息。Deep Agents 默认就是这样工作的:Agent 在一个循环中运行,可以按需多次调用任何工具。

把搜索经验封装成 skill

为了帮助 Agent 更好地搜索,可以把搜索经验封装成一个 skill:一组针对特定任务的指引,例如该用哪个索引、怎样组织查询,以及引用采用什么格式。

Agent 起初只会看到每个 skill 的名称和简短描述,只有任务需要时,才加载完整指引。这样,专门知识在需要时随时可用,平时又不会占据上下文。这种方法叫渐进披露。相关视频解释也可以帮助理解。

渐进披露演示的静态预览 动画见原文

2. 一次引入太多上下文

然而,即使优化了搜索过程,正确的搜索也可能返回太多内容。Agent 只需要市场数据导出中的一个数字,比如 B 公司的季度营收,但为了找到它而加载全部 4 万行数据,就会占满上下文。自定义检索工具也有同样的问题:如果每次搜索都把所有新闻文章直接交给模型,五家公司、每次四篇文章,很快就会堆积起来。

大文件按页读取

文件系统工具的设计,就是尽量少返回内容。read_file 一次读取一页,默认是 100 行;Agent 可以设置 offset,也就是从哪一行开始,以及 limit,也就是读多少行,直接跳到需要的位置。因此,要找 B 公司的营收,可以先用 grep 定位,再只读取匹配位置附近的几行。

让运行框架卸载过大的结果

有时,工具返回的内容仍然会超出预期。当一个工具结果过大,超过约 2 万 token 时,运行框架会把它保存到 /large_tool_results/ 下的文件中,只把路径和一小段预览交给 Agent,预览包含开头和结尾的几行。随后,Agent 可以用 read_file 的 offset 和 limit,按需取回其中的部分内容。

让检索工具返回文件路径

你也可以在自定义检索工具中主动采用同样的方式,而不用等到触及大小限制:把每条结果写入文件,只返回路径。这是“检索、卸载、委派”模式的第一部分,LangChain 的 RAG 指南对完整流程有详细介绍。

下面是一个简化的检索工具。它执行搜索,把每条结果保存到 Agent 的 backend 中,然后只返回文件路径列表:

backend = StateBackend()
 
@tool
def search_news(query: str) -> str:
    """Search the news archive and save each result to a file."""
    docs = vector_store.similarity_search(query, k=4)
    files = [(f"/retrieved/result_{i}.md", doc.page_content.encode()) for i, doc in enumerate(docs)]
    backend.upload_files(files)
    return "Saved results to: " + ", ".join(path for path, _ in files)

现在,主 Agent 看到的是四条简短路径,而不是四篇完整文章。

3. 上下文不相关或已过期

如果结果是错的,体量再小也没用。这四个文件中,任何一个都可能讲的是另一家名字相近的公司,或者两年前的旧闻。对模型来说,跑题或过时的文本可能和正确答案一样可信,因此目标是根本不让它们进入主 Agent 的上下文。

让子 Agent 筛选结果

不过,主 Agent 仍然得读这些文件,才能知道哪些有用;而它打开的每篇无关文章,也都会进入上下文。这就轮到“检索、卸载、委派”中的“委派”了:让一个子 Agent 阅读每个检索到的文件,只返回关键事实。

  1. 定义子 Agent:description 告诉主 Agent 什么时候使用它,每次只交给它一个文件;system prompt 则告诉子 Agent 该怎样处理这个文件。
chunk_analyst = {
        "name": "chunk-analyst",
        "description": "Analyze one retrieved file. Pass the question and a single path under /retrieved/.",
        "system_prompt": "Use read_file to read the assigned file. Return the key facts and the source URL.",
}
  1. 给主 Agent 配上检索工具和这个子 Agent。传入的 backend,要与 search_news 使用的是同一个实例,这样子 Agent 才能读到工具保存的文件。
agent = create_deep_agent(
        model=model,
        tools=[search_news],
        backend=backend,  # same instance, so read_file can see /retrieved/
        subagents=[chunk_analyst],
)

现在,流程变成了:

  1. 搜索:主 Agent 调用 search_news,拿到四条路径。
  2. 委派:它把每条路径交给一个 chunk-analyst 子 Agent,这些子 Agent 可以并行工作。
  3. 综合:每个分析子 Agent 返回一小段摘要及其来源。主 Agent 汇总这些内容,如果还有缺失,就继续搜索。

原始结果写入文件,容易夹杂噪声的阅读工作由拥有各自上下文窗口的子 Agent 完成,主 Agent 只接收结论。不相关的内容留在子 Agent 的上下文中,主 Agent 的上下文窗口就能保持清爽!

使用最新的数据源

筛选可以排除无关结果,却无法修复过期的结果。我们的竞品分析 Agent 每周一运行一次,上个月建立的索引捕捉不到本周的新消息。

一种办法是使用搜索 API 或 MCP 服务器,为 Agent 提供当前信息,而不是几个月前索引中的内容。如果数据带有日期,也可以在检索工具中按日期过滤,挡住旧结果。

有时,索引里同时存在某篇文档的新旧版本,而旧版往往更容易胜出,因为旧文档通常更长、更详细,排名也就更高。人看到“最后更新于公元前 12 年”就会跳过,但文档被切块放入向量存储时,这个日期往往会丢失。如果检索工具能拿到日期,就把它放进返回的文本,让模型看得到。随后,让子 Agent 把日期和关键事实一起返回,并标出可能过时的内容。

小心处理检索到的文本

另一方面,相关而且最新的结果也可能带来问题!检索文本是数据,不是指令,所以任何取回的内容都可能包含提示注入,而且没有任何提示词或分隔符策略能够完全防住它。一定要验证输出:检查答案是否引用了预期的来源,以及其中的主张是否与来源相符。

值得记住的检索策略

无论使用哪种运行框架,都可以关注以下几点:

  • 使用能完成任务的最高效工具。知道具体用词时,grep 比 embedding 查询更合适;真正需要时,再使用语义搜索。
  • 分页读取,不要一次加载。把大文件拆成小块来读,而不是全部塞进上下文。
  • 返回指针,不返回完整载荷。把检索内容写进文件,再把路径交给 Agent。
  • 委派阅读。让子 Agent 阅读并归纳,主 Agent 只接收结论。
  • 核验依据。对照检索到的内容检查答案,并把检索文本视为不可信数据。

动手试试

uv add deepagents

想跟着讲师现场学习更多内容吗?我将在 11 月 9 日讲解这些内容,可以通过原文中的链接报名!

检索只讲了故事的一半:信息怎样抵达 Agent。那么,随着信息不断积累,又该怎样管理 Agent 的上下文窗口?

想进一步了解,可以阅读:

  • 关于 Deep Agents 上下文工程的博客。
  • LangChain Academy 的 Deep Agents 课程,免费、在线,而且可以按自己的节奏学习。

参考链接

  • 原文:https://www.langchain.com/blog/context-engineering-starts-with-retrieval
  • agent harness:https://www.langchain.com/blog/the-anatomy-of-an-agent-harness
  • LangChain’s Deep Agents:https://www.langchain.com/deep-agents
  • blog on Context Engineering:https://www.langchain.com/blog/context-engineering-deep-agents-remember
  • Deep Agents harness:https://docs.langchain.com/oss/python/deepagents/overview
  • these tools built in:https://docs.langchain.com/oss/python/deepagents/tools
  • backend:https://docs.langchain.com/oss/python/deepagents/backends
  • Grep:https://reference.langchain.com/python/deepagents/backends/protocol/BackendProtocol/grep
  • retrieval tool:https://docs.langchain.com/oss/python/deepagents/retrieval
  • vector store:https://docs.langchain.com/oss/python/integrations/vectorstores
  • MCP server:https://docs.langchain.com/oss/python/deepagents/tools
  • LangChain’s guide:https://docs.langchain.com/oss/python/langchain/knowledge-base
  • 2-step RAG:https://docs.langchain.com/oss/python/deepagents/retrieval
  • agentic retrieval:https://docs.langchain.com/oss/python/deepagents/retrieval
  • skill:https://docs.langchain.com/oss/python/deepagents/skills
  • progressive disclosure:https://docs.langchain.com/oss/python/deepagents/skills
  • video explanation:https://www.youtube.com/watch?v=bv1qgkbWhms
  • LangChain’s RAG guide:https://docs.langchain.com/oss/python/deepagents/rag
  • subagent:https://docs.langchain.com/oss/python/deepagents/subagents
  • prompt injection:https://docs.langchain.com/oss/python/deepagents/rag
  • HERE:https://events.langchain.com/register/agent-memory-102-context-retrieval-storage/
  • context engineering with Deep Agents:https://www.langchain.com/blog/context-engineering-deep-agents-remember
  • Deep Agents course:https://academy.langchain.com/courses/foundation-introduction-to-deepagents

【声明】内容源于网络
0
0
AINLP
一个有趣有AI的自然语言处理公众号:关注AI、NLP、大模型LLM、机器学习、推荐系统、计算广告等相关技术。公众号可直接对话双语聊天机器人,尝试对对联、作诗机、藏头诗生成器、自动写作等,查询相似词,测试NLP相关工具包。
内容 6123
粉丝 0
AINLP 一个有趣有AI的自然语言处理公众号:关注AI、NLP、大模型LLM、机器学习、推荐系统、计算广告等相关技术。公众号可直接对话双语聊天机器人,尝试对对联、作诗机、藏头诗生成器、自动写作等,查询相似词,测试NLP相关工具包。
总阅读40.6k
粉丝0
内容6.1k