LangChain上下文工程智能体应用2024年权威排行榜

2026-06-20阅读 0热度 0
ai 人工智能
LangChain与LangGraph如何通过上下文工程打造更智能的AI助手?掌握这项技术,对构建AI应用的方式来说,确实是一次质变。 核心内容: 1. 上下文工程的核心要素与工作原理 2. LangGraph在状态管理和记忆处理上的技术优势 3. 智能体架构设计与上下文隔离的最佳实践 ![基于上下文工程的LangChain人工智能智能体应用](http://img.318050.com/uploads/20260531/17801890176a1b875982be0622360842.webp) 先说第一个核心概念。上下文工程,简单理解就是在任务开始前,为AI构建一个合理的“配置框架”。这个框架具体包含哪些东西? * **行为准则**:明确AI的角色定位,比如定义它为“经济型旅行规划助手”还是“代码调试专家”。 * **信息接入**:允许AI访问数据库、文档或实时数据源中的关键信息。 * **会话记忆**:保留历史对话记录,避免重复提问或遗漏重要线索。 * **工具集成**:支持调用计算器、搜索引擎等辅助功能来完成任务。 * **用户画像**:掌握用户的个人偏好、地理位置等核心信息,让回复更个性化。 你看,AI工程师的工作重心正在从最初的“提示词工程”转向“上下文工程”。原因很简单——单靠巧妙的提示词已经难以应对越来越复杂的任务了。 > 上下文工程的核心,就是为AI提供恰到好处的背景信息和工具,使其回答更智能、更实用。 这篇文章将重点拆解**LangChain**和**LangGraph**这两大利器,看它们如何在构建AI智能体、RAG应用和LLM应用的过程中,落地上下文工程的具体策略。 --- ## 1. 什么是上下文工程? 如果把大语言模型看作一台新型操作系统,LLM本身是CPU,那它的上下文窗口就相当于RAM——负责短期记忆。但和物理内存一样,这个“RAM”的容量也是有限的,不可能什么都往里塞。 > 操作系统负责决定RAM里存什么,而“上下文工程”要做的,就是帮LLM决定它的“工作内存”里该保留哪些信息。 构建LLM应用时,需要管理的上下文类型大致有三类: * **指令类**:提示词、示例、记忆片段、工具描述。 * **知识类**:事实数据、存储信息、记忆库。 * **工具类**:工具调用的反馈结果和执行输出。 今年以来,随着LLM在思维链和工具调用能力上的大幅提升,智能体技术迎来了爆发。智能体群组通过协同LLM与各类工具,可以处理那些需要多步骤、长周期的复杂任务——边干边决策,根据工具的反馈动态调整下一步行动。 但麻烦也随之而来。任务越长,工具反馈越多,token消耗就像开了水龙头一样哗哗往外流。这不仅可能导致上下文窗口溢出,还会显著增加成本和延迟,甚至拖慢智能体的决策速度。 Drew Breunig在研究中详细解释了上下文失控如何损害性能: * **上下文污染**:当错误或幻觉内容混入上下文时。 * **上下文干扰**:当过量信息让模型难以抓住重点时。 * **上下文混淆**:当无关细节干扰了正确答案的判断时。 * **上下文冲突**:当上下文中的不同部分提供相互矛盾的信息时。 Anthropic在其研究中也特别强调过这一点: > 智能体群组通常需要进行数百轮对话,因此谨慎管理上下文至关重要。 那么,业界目前有哪些应对之策?AI智能体的上下文工程策略可以归纳为四大类: * **编写**:创建干净、清晰的上下文指令。 * **筛选**:只从大量信息中挑出最相关的部分。 * **压缩**:缩短上下文,节省宝贵的token空间。 * **隔离**:让不同类型、不同任务的上下文互不干扰。 LangGraph这套框架在设计之初就全面支持了这些策略。接下来,我们就逐一来解析这些组件如何在LangGraph中落地,以及它们如何提升AI智能体的实际效能。 --- ## 2. LangGraph暂存区应用 人类在做复杂任务时,会随手记笔记来提醒自己重点。智能体也一样,它需要一个“暂存区”——将关键信息存储在上下文窗口之外,确保随时可以调取核心数据。 一个典型的例子是Anthropic的多智能体研究系统: > 首席研究员制定计划后,会将计划存入“记忆”(暂存区)。因为当上下文窗口超过20万token容量时,旧的内容会被截断,而保存计划可以确保它不丢失。 暂存区的实现方式可灵活选择: * 通过**工具调用**实现文件写入功能。 * 作为会话期间持续存在的**运行时状态对象**中的一个字段来实现。 总之,暂存区的价值就是帮智能体在对话过程中保存关键笔记,从而高效完成任务。 在LangGraph框架中,系统同时支持短期记忆(线程范围)和长期记忆两种机制。 * **短期记忆**通过检查点机制,在会话期间保存智能体的状态。它就像个临时便签,允许智能体运行中存储并在后续阶段调用信息。 这里的核心是**状态对象**——它是在图节点间传递的核心数据结构。你可以自定义格式(通常用Python字典),充当一个共享的暂存区,每个节点都可以读取并更新特定字段。 接下来,让我们用代码来实际感受一下。先导入必要的模块,并从定义状态开始: ```python from typing import TypedDict from rich.console import Console from rich.pretty import pprint console = Console() class State(TypedDict): topic: str joke: str ``` 这定义了一个简单的状态,包含主题和笑话两个字段。 --- ## 3. 创建状态图 有了状态对象,就可以通过StateGraph来向其中写入上下文了。 StateGraph是LangGraph构建有状态智能体或工作流的核心工具。你可以把它理解成一个有向图: * **节点**:代表工作流中的处理步骤。每个节点接收当前状态,更新后返回变更结果。 * **边**:连接节点并定义执行流向,支持线性、条件判断甚至循环路径。 接下来我们要做的: 1. 挑选一个Anthropic模型,初始化聊天模型。 2. 将它应用到LangGraph工作流中。 先设置模型: ```python import getpass import os from IPython.display import Image, display from langchain.chat_models import init_chat_model from langgraph.graph import END, START, StateGraph from dotenv import load_dotenv api_key = os.getenv("ANTHROPIC_API_KEY") if not api_key: raise ValueError("Missing ANTHROPIC_API_KEY in environment") llm = init_chat_model("anthropic:claude-sonnet-4-20250514", temperature=0) ``` 然后创建一个节点函数,用它来生成笑话: ```python def generate_joke(state: State) -> dict[str, str]: topic = state["topic"] print(f"Generating a joke about: {topic}") msg = llm.invoke(f"Write a short joke about {topic}") return {"joke": msg.content} ``` 接下来构建并编译图: ```python workflow = StateGraph(State) workflow.add_node("generate_joke", generate_joke) workflow.add_edge(START, "generate_joke") workflow.add_edge("generate_joke", END) chain = workflow.compile() display(Image(chain.get_graph().draw_mermaid_png())) ``` 现在可以执行了: ```python joke_generator_state = chain.invoke({"topic": "cats"}) console.print("n[bold blue]Joke Generator State:[/bold blue]") pprint(joke_generator_state) #### 输出结果 #### { 'topic': 'cats', 'joke': 'Why did the cat join a band?nnBecause it wanted to be the purr-cussionist!' } ``` 返回的字典就是智能体在当前话题下的状态。这个简单的例子,展示了如何将上下文写入状态。 > 更复杂的场景中,你还可以利用检查点机制(保存和恢复图状态)以及人机协同(暂停工作流等待人工输入)。 --- ## 4. LangGraph中的记忆写入 暂存区帮智能体在单次会话中工作,但很多时候,它需要跨会话保持记忆。 * **Reflexion** 提出了一种概念:让智能体在每轮交互后进行反思,并将自我生成的提示复用起来。 * **生成式智能体** 则通过总结历史交互反馈,构建长期记忆系统。 这些理念已经在ChatGPT、Cursor和Windsurf等产品中落地,它们能自动从用户交互中生成长期记忆。 在LangGraph中: * **检查点机制**会在每一步将图的状态保存到线程中。每个线程有唯一ID,通常代表一次交互,好比ChatGPT中的一次会话。 * **长期记忆**功能允许你在不同线程间保持特定上下文。可以保存独立文件(如用户档案)或记忆集合。 * 该功能采用键值存储的`BaseStore`接口实现,既可以在内存中试用,也可用于LangGraph平台的生产部署。 让我们创建一个内存存储,以便后续在笔记本中跨会话使用: ```python from langgraph.store.memory import InMemoryStore store = InMemoryStore() namespace = ("rlm", "joke_generator") store.put( namespace, "last_joke", {"joke": joke_generator_state["joke"]}, ) ``` 使用`search`方法可以查看命名空间内的条目,确认写入成功: ```python stored_items = list(store.search(namespace)) console.print("n[bold green]Stored Items in Memory:[/bold green]") pprint(stored_items) ``` 现在,把这一切整合到LangGraph工作流中。编译时,会用到两个参数: * `checkpointer`:在每个线程的步骤中保存图状态。 * `store`:实现跨线程的上下文保持。 ```python from langgraph.checkpoint.memory import InMemorySa ver from langgraph.store.base import BaseStore checkpointer = InMemorySa ver() memory_store = InMemoryStore() def generate_joke(state: State, store: BaseStore) -> dict[str, str]: existing_jokes = list(store.search(namespace)) if existing_jokes: existing_joke = existing_jokes[0].value print(f"Existing joke: {existing_joke}") else: print("Existing joke: No existing joke") msg = llm.invoke(f"Write a short joke about {state['topic']}") store.put(namespace, "last_joke", {"joke": msg.content}) return {"joke": msg.content} workflow = StateGraph(State) workflow.add_node("generate_joke", generate_joke) workflow.add_edge(START, "generate_joke") workflow.add_edge("generate_joke", END) chain = workflow.compile(checkpointer=checkpointer, store=memory_store) ``` 执行第一个线程,会发现内存中还没有笑话题目: ```python config = {"configurable": {"thread_id": "1"}} joke_generator_state = chain.invoke({"topic": "cats"}, config) console.print("n[bold cyan]Workflow Result (Thread 1):[/bold cyan]") pprint(joke_generator_state) #### 输出结果 #### Existing joke: No existing joke ``` 现在,换一个线程ID重新运行: ```python config = {"configurable": {"thread_id": "2"}} joke_generator_state = chain.invoke({"topic": "cats"}, config) console.print("n[bold yellow]Workflow Result (Thread 2):[/bold yellow]") pprint(joke_generator_state) #### 输出结果 #### Existing joke: {'joke': 'Why did the cat join a band?nnBecause it wanted to be the purr-cussionist!'} ``` 瞧,第一个线程产出的笑话,成功保存在了内存中,并在第二个线程里被识别出来。这就是长期记忆的威力。 > 关于更多内存抽象机制,可以看看LangMem;关于LangGraph智能体中的内存管理,可以关注Ambient智能体课程。 --- ## 5. 暂存区选择策略 从暂存区里怎么取数据,取决于它是怎么存进去的: * 如果是通过**工具**存进去的,那智能体可以直接通过工具调用读取。 * 如果是在智能体的**运行时状态**中,那开发者需要自己决定每一步向智能体暴露哪些状态字段。这给了你精确控制上下文的能力。 前面我们已经学会了如何向LangGraph状态对象写入数据。现在来学怎么从中选择上下文,再传递给下游的LLM调用。 先定义两个函数,一个负责生成笑话,一个负责优化: ```python def generate_joke(state: State) -> dict[str, str]: msg = llm.invoke(f"Write a short joke about {state['topic']}") return {"joke": msg.content} def improve_joke(state: State) -> dict[str, str]: print(f"Initial joke: {state['joke']}") msg = llm.invoke(f"Make this joke funnier by adding wordplay: {state['joke']}") return {"improved_joke": msg.content} ``` 为了让工作流更有层次,我们加一个新节点:先初始生成,再改进优化。这样就能直观看到LangGraph中暂存区选择的机制了。 ```python workflow = StateGraph(State) workflow.add_node("generate_joke", generate_joke) workflow.add_node("improve_joke", improve_joke) workflow.add_edge(START, "generate_joke") workflow.add_edge("generate_joke", "improve_joke") workflow.add_edge("improve_joke", END) chain = workflow.compile() display(Image(chain.get_graph().draw_mermaid_png())) ``` 执行这个工作流,可以看到改进节点成功读取了初始节点写入状态的笑话。 --- ## 6. 记忆选择能力 如果智能体配备了记忆存储,就需要根据当前任务筛选出相关的记忆。这个机制适用于: * **情景记忆**:展示预期行为的少样本示例。 * **程序性记忆**:指导行为操作的指令集。 * **语义记忆**:提供任务相关背景的事实与关系网络。 有些智能体会使用预定义的狭窄文件来存储记忆——比如Claude的`CLAUDE.md`,或者Cursor和Windsurf的“规则”文件。但当存储大量事实集合时,记忆筛选就会变得困难。用嵌入向量或知识图谱来建立索引,是目前更优的方案。 上一节里,我们向`InMemoryStore`写了数据。现在用`get`方法把它读出来,引入到工作流中。 ```python retrieved_joke = store.get(namespace, "last_joke").value console.print("n[bold green]Retrieved Context from Memory:[/bold green]") pprint(retrieved_joke) ``` 现在来写一个带记忆选择的`generate_joke`函数,让它能: 1. 从当前状态里取上下文。 2. 从记忆里获取过往笑话,避免重复。 ```python def generate_joke(state: State, store: BaseStore) -> dict[str, str]: prior_joke = store.get(namespace, "last_joke") if prior_joke: prior_joke_text = prior_joke.value["joke"] print(f"Prior joke: {prior_joke_text}") else: print("Prior joke: None!") prompt = ( f"Write a short joke about {state['topic']}, " f"but make it different from any prior joke you've written: {prior_joke_text if prior_joke else 'None'}" ) msg = llm.invoke(prompt) store.put(namespace, "last_joke", {"joke": msg.content}) return {"joke": msg.content} ``` 执行后,第一个线程会提示“Prior joke: None!”,第二个线程则会打印出第一个线程的成果。系统成功地从记忆中获取了正确的信息。 --- ## 7. LangGraph大工具调用优势 智能体手头的工具一多,就容易挑花眼,尤其是当工具描述出现重叠时。与其一股脑全塞给模型,不如让模型自己“挑着用”。 这里有个好办法:采用RAG技术,通过工具描述的语义相似度筛选出最相关的工具——Drew Breunig称之为“工具负载方案”。 > 根据最新研究,这个方法能把工具选择的准确率提升三倍。 在这个领域,**LangGraph Bigtool**库是值得一等的好工具。它能对工具描述做语义相似性搜索,动态为任务匹配最合适的工具。 让我们用Python的`math`库来演示一下它的工作机制。先把所有函数装进列表: ```python import math all_tools = [] for function_name in dir(math): function = getattr(math, function_name) if not isinstance(function, types.BuiltinFunctionType): continue if tool := convert_positional_only_function_to_tool(function): all_tools.append(tool) ``` 接着,把这些工具的描述转化为向量嵌入: ```python embeddings = init_embeddings("openai:text-embedding-3-small") store = InMemoryStore( index={ "embed": embeddings, "dims": 1536, "fields": ["description"], } ) for tool_id, tool in tool_registry.items(): store.put( ("tools",), tool_id, { "description": f"{tool.name}: {tool.description}", }, ) ``` 现在,智能体可以根据问题的语义,自动检索并调用正确的工具了。比如问“计算0.5的反余弦值”,它会精准地找到并调用`acos`函数。 --- ## 8. 基于上下文工程的RAG系统 RAG(检索增强生成)是一个庞大的研究领域,而“代码智能体”正是它在生产环境中的最佳实践。在实际应用中,RAG往往是上下文工程的核心挑战。 正如风帆冲浪公司的Varun所言: > 索引 ≠ 上下文检索。基于AST分块的嵌入搜索虽然有效,但随着代码库规模的扩大,效果会越来越差。我们需要混合检索方案:grep/文件搜索、知识图谱链接以及基于相关性的重排序。 LangGraph提供了完整的教程与视频,指导如何将RAG集成到智能体系统中。 来看一个具体的例子:从Lilian Weng的博客中提取最近三篇文章,构建一个RAG知识库。 先加载页面内容: ```python from langchain_community.document_loaders import WebBaseLoader urls = [ "https://lilianweng.github.io/posts/2025-05-01-thinking/", "https://lilianweng.github.io/posts/2024-11-28-reward-hacking/", "https://lilianweng.github.io/posts/2024-07-07-hallucination/", ] docs = [WebBaseLoader(url).load() for url in urls] ``` 然后进行分块,这是影响检索效果的关键一步。我们用递归字符文本分割器,设置2000字符的块大小和50字符的重叠: ```python from langchain_text_splitters import RecursiveCharacterTextSplitter docs_list = [item for sublist in docs for item in sublist] text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(chunk_size=2000, chunk_overlap=50) doc_splits = text_splitter.split_documents(docs_list) ``` 将分块存入向量存储: ```python from langchain_core.vectorstores import InMemoryVectorStore vectorstore = InMemoryVectorStore.from_documents(documents=doc_splits, embedding=embeddings) retriever = vectorstore.as_retriever() ``` 接着创建一个检索器工具,让智能体可以调用它来搜博客: ```python from langchain.tools.retriever import create_retriever_tool retriever_tool = create_retriever_tool( retriever, "retrieve_blog_posts", "Search and return information about Lilian Weng blog posts.", ) ``` 现在,构建一个简单的ReAct智能体,让它能使用检索工具,并结合系统提示来回答关于博客内容的问题。当用户问“博客讨论了哪几类奖励黑客攻击”时,智能体会启动检索流程,从工具中获取信息并整合出答案。 --- ## 9. 知识型智能体的压缩策略 智能体交互可以跨越几百轮,并且涉及大量消耗token的工具调用。摘要生成是管理这类问题的主流方法。 比如,当上下文窗口使用率超过95%时,Claude代码会启动“自动压缩”,对完整的交互历史进行摘要。你也可以通过**递归摘要**或**分层摘要**等策略,来压缩智能体的轨迹数据。 LangGraph作为一个底层编排框架,给了你完全的控制权: * 将智能体设计为一组节点。 * 在每个节点内明确定义逻辑。 * 在节点间传递共享状态对象。 这使得压缩上下文变得非常灵活。比如,可以在工具调用后(尤其是token消耗巨大的搜索工具),对工具返回的文档进行摘要,而不是一股脑全塞进主上下文。 实践出真知。同样一个查询,不压缩时消耗了11.5万token,而在工具节点中加入摘要处理后,只用了6万token——成本降低了近一半。 > 这个简单的改动显著提升了智能体的效率和经济效益。 --- ## 10. 通过子智能体架构隔离上下文 隔离上下文的一个常见方法,是让不同的子智能体分担不同的上下文。OpenAI的Swarm库就是为了实现这种“关注点分离”而设计的——每个智能体通过专属工具、指令和上下文窗口,管理特定的子任务。 Anthropic的多智能体研究表明,采用独立上下文的智能体群组比单智能体性能提升了90.2%,因为每个子智能体能更聚焦于更窄的子任务。 > 子智能体通过各自的上下文窗口并行运作,同时探索问题的不同维度。 但多智能体系统也面临挑战:token消耗会急剧增加(有时是单智能体的15倍以上),并且协调过程复杂。 LangGraph支持多种多智能体架构,其中监督器架构是常用方案。监督器负责将任务委派给子智能体,每个子智能体在独立上下文中运行。 举个例子,我们构建一个简易的监督器,管理一个数学专家和一个研究专家。当用户问“FAANG公司2024年的总员工数是多少?”,监督器会先派研究专家去查数据,再派数学专家去做计算,完美隔离了事实检索和数学计算两个不同维度的上下文。 --- ## 11. 沙盒环境隔离技术 HuggingFace展示了一种创新的上下文隔离方法。大多数智能体使用工具调用API,返回JSON参数来运行工具;而HuggingFace则采用了**代码智能体**,让模型自己编写代码来执行任务。代码在安全的沙盒环境中运行,执行结果再返回给LLM。 这种方法让海量数据(如图像或音频)可以突破LLM的token限制。HuggingFace解释说: > [代码智能体能够]更好地处理状态……需要暂存图像、音频或其他数据供后续使用?直接将其保存为状态的变量,后续调用即可。 在LangGraph中使用沙盒环境很方便。LangChain沙箱通过Pyodide(编译为WebAssembly的Python)安全地执行非受信代码,你可以把它当作一个工具集成到任意LangGraph智能体中。 (注意:需要预先安装Deno环境。) ```python from langchain_sandbox import PyodideSandboxTool tool = PyodideSandboxTool(allow_net=True) agent = create_react_agent(llm, tools=[tool]) result = await agent.ainvoke({"messages": [{"role": "user", "content": "what's 5 + 7?"}]}) ``` 这种隔离方式,不仅安全,还能有效管理上下文。 --- ## 12. LangGraph中的状态隔离 智能体的**运行时状态对象**本身就是一个天然的上下文隔离区,有点像沙盒。你可以通过模式设计(比如用Pydantic模型)来精确控制不同字段的可见性。 比如,`messages`字段每轮都向LLM展示,而其他字段则“藏”起来,直到需要时才拿出来用。 LangGraph围绕状态对象构建,允许你创建自定义状态模式,并在工作流中自由访问。你可以把工具调用的结果存到特定字段里,让它只在必要的时刻才对LLM可见。 --- ## 13. 全面总结 来盘点一下我们都做了什么: * 用LangGraph的`StateGraph`创建了短期记忆“暂存区”,并用`InMemoryStore`实现了长期记忆存储,让智能体能读能写。 * 演示了如何从状态和记忆中精准提取信息,包括用RAG获取知识,以及用`langgraph-bigtool`从工具库中精准选择。 * 为管理长对话和高token消耗的输出,实现了摘要生成机制,让智能体更高效。 * 展示了如何动态压缩RAG结果,大幅削减token使用,提升成本效益。 * 通过构建多智能体监督系统和使用沙盒环境,实现了上下文隔离,防止不同任务互相干扰。 这些技术,都属于**“上下文工程”**的范畴。它的核心,就是精细地管理AI的“工作内存”,让智能体更精准、更高效,能够从容应对那些复杂而长期的任务。
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策