什么是上下文工程 (Context Engineering)?


                                                                                                                                                <div> 

你听说过这个新术语上下文工程,但不确定它是什么吗?加入我们,我们将解释它是什么以及 RAG 与 Elasticsearch 如何提供帮助。

Elasticsearch 包含许多新功能,可帮助您为您的用例构建最佳搜索解决方案。深入了解我们的示例笔记本以了解更多信息,开始免费云试用,或立即在您的本地计算机上试用 Elastic。


随着人工智能的快节奏和不断发展的性质,新的术语和技术不断出现。最新的讨论之一是围绕上下文工程。如果您不确定什么是上下文工程、为什么它很重要,或者您可以使用哪些技术来优化代理系统使用的上下文,请继续阅读以找出答案。

什么是上下文工程?

上下文工程是指可以组合起来为大型语言模型(或 LLM)提供正确信息以帮助它们完成所需任务的实践的集合。重要的是要确保我们在代理和 MCP 工具中使用的 LLM 具有正确的信息源,以确保它们提供准确的结果,并且不会产生幻觉或无法给出所需的答案。我的高中数学老师总是在我们为计算和证明提供的输入方面谈论“垃圾进,垃圾出”的概念。

ChatGPT 输出询问作者对上下文工程的评价

LLM 也是一样。如果不给它们提供正确的信息,就不能指望它们准确地提供我们所需的答案和自动化功能。正如上面 ChatGPT 的例子所示,它只能利用训练时获得的信息,或者通过后续章节所讨论的组件在上下文中提供的信息。

组件

下面的可视化展示了上下文的关键组件,我们可以使用这些组件来改进 AI 代理调用和 MCP 工具调用的 LLM 的响应:

上下文工程

来源:https://www.philschmid.de/context-engineering

正如 Dexter Horthy 在他的  12-Factor Agents 的第三条原则 中所指出的,拥有自己的上下文对于确保 LLM 能生成最佳输出至关重要。

RAG

RAG  是一种架构模式,其中来自信息检索系统(如 Elasticsearch )的数据被提供给 LLM,用于支撑和增强其生成的结果。我们在许多 Elasticsearch Labs 博客中讨论过 RAG, 包括这篇 提供该结构概览的文章,以及这篇介绍如何用 Python、Langchain、React 和 Elasticsearch 构建一个单一 RAG  聊天机器人的教程

RAG 示例体系结构

尽管有些人认为较新的 LLM 不断扩大的上下文窗口大小意味着“RAG 已死”,但事实上,他们可能会发现他们的 LLM 存在上下文混淆,正如 Drew Breunig 所描述的那样。上下文混淆是指提供给 LLM 的过剩信息导致次优响应的问题。RAG 有助于引导 LLM 获得所需的结果,因为它解决了通用 LLM 的常见局限性,包括:

  1. 缺乏金融服务或工程等行话密集型学科的特定领域知识。
  2. 模型训练后发生的较新信息或事件。
  3. 幻觉,其中 LLM 生成错误答案。

RAG 通常涉及从数据存储中提取相关文档并通过提示或通过 LLM 调用的专用 AI 工具传递。下面给出了利用 Elasticsearch JavaScript 客户端的 AI SDK Travel Planner 的一个简单示例:

import { tool as createTool } from 'ai';
import { z } from 'zod';

import { Client } from '@elastic/elasticsearch';
import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types';

import { Flight } from '../model/flight.model';

const index: string = "upcoming-flight-data";
const client: Client = new Client({
  node: process.env.ELASTIC_ENDPOINT,
  auth: {
    apiKey: process.env.ELASTIC_API_KEY || "",
  },
});

function extractFlights(response: SearchResponseBody<Flight>): (Flight | undefined)[] {
    return response.hits.hits.map(hit => { return hit._source})
}

export const flightTool = createTool({
  description:
    "Get flight information for a given destination from Elasticsearch, both outbound and return journeys",
  parameters: z.object({
    destination: z.string().describe("The destination we are flying to"),
    origin: z
      .string()
      .describe(
        "The origin we are flying from (defaults to London if not specified)"
      ),
  }),
  execute: async function ({ destination, origin }) {
    try {
      const responses = await client.msearch({
        searches: [
          { index: index },
          {
            query: {
              bool: {
                must: [
                  {
                    match: {
                      origin: origin,
                    },
                  },
                  {
                    match: {
                      destination: destination,
                    },
                  },
                ],
              },
            },
          },

          // Return leg
          { index: index },
          {
            query: {
              bool: {
                must: [
                  {
                    match: {
                      origin: destination,
                    },
                  },
                  {
                    match: {
                      destination: origin,
                    },
                  },
                ],
              },
            },
          },
        ],
      });

      if (responses.responses.length < 2) {
        throw new Error("Unable to obtain flight data");
      }

      return {
        outbound: extractFlights(responses.responses[0] as SearchResponseBody<Flight>),
        inbound: extractFlights(responses.responses[1] as SearchResponseBody<Flight>)
      };
    } catch (e) {
      console.error(e);
      return {
        message: "Unable to obtain flight information",
        location: location,
      };
    }
  },
});
从 Elasticsearch 或其他来源检索相关信息,甚至像我的同事 Alex 在构建 MCP 数据时所实现的那样利用 LLM 摘要或数据聚合等技术来总结和查询他的健康数据,可以确保 LLM 拥有提供答案所需的精确数据。然后可以使用新兴协议(例如模型上下文协议 (MCP)或 Agent2Agent 协议(称为 A2A))来传递此上下文。

Prompts(提示)

提示工程可能被认为是一个更成熟的实践,但仍然是上下文工程的一个子集。提示工程指的是精炼和设计有效输入(或提示)给 LLM,以产生我们想要的结果。尽管通常以简单文本形式存在,提示也可以包含其他媒体来源,如图像和声音。Sander Schulhoff 等人在其 提示工程调查 中定义了提示的以下组成部分:

  • Directive(指令):作为请求主要意图的指令或问题

  • Exemplars(示例):可演示的例子,引导 LLM 完成任务

  • Output Formatting(输出格式):期望返回输出信息的格式,如 JSON 或非结构化文本。这很重要,因为根据数据来源,LLM 可能需要进行转换(例如将 Elasticsearch 查询响应的结构化 JSON 转换为另一种格式,而不是直接返回结果)

  • Style Instructions(样式指令):指导如何改变输出结构,这被视为输出格式的特定类型

  • Role(角色):LLM 需要模拟的角色以完成任务(例如旅行代理)

  • Additional Information(附加信息):完成任务所需的其他有用细节,包括来自其他来源的上下文

下面的示例具体展示了用于旅行规划 agent 的提示中每个元素的应用:

 

示例提示

所有这些元素都可以进行调整和评估,以确保 LLM 获得最佳结果。除了这些元素之外,还有许多技术可用于构建和优化提示以获得您需要的答案。例如,Wei 等人在 2023 年的论文中发现,与算术和推理任务的思维链提示技术相比,我们向 LLM 提出一个简单结构化问题的标准零样本提示效率较低。以下示例总结了这些差异:

标准提示与思维链提示

来源:https://arxiv.org/pdf/2201.11903

在考虑要提供的提示格式时,您需要考虑几个因素,包括:

  • 任务类型(例如,与复杂算术推理相比的简单回忆或翻译)。
  • 任务复杂性和模糊性。模棱两可的请求可能会导致不可预测的结果。
  • 您作为上下文提供的输入以及格式。
  • 所需的输出。- 您选择的 LLM 的功能。
  • 您希望 LLM 模拟的角色。

记忆(Memory)

就像人类一样,AI 应用依赖短期和长期记忆来回忆信息。在上下文工程中:

  1. 短期记忆,通常称为状态或聊天历史记录,是指用户和模型之间在当前对话中交换的消息。这包括用户提出的初始问题和后续问题。
  2. 长期记忆,简称为记忆,是指在对话中共享的信息。关键示例是相关的常见信息或最近的先前对话。

以我们的 Travel Planner Agent 为例, 短期记忆将包括旅行日期和地点,以及用户改变主意并想要探索另一个目的地时的任何后续消息。这个地方的长期记忆可以包含有关用户旅行偏好的个人资料信息,以及过去的旅行信息,这些信息可用于告知新行程中应包含哪些活动的建议(例如,为那些在之前的假期中参加过这些活动的人提供品酒机会)。

大多数 AI 框架都提供了管理聊天记录和记忆的功能,因为重要的是要确保对历史记录进行管理,以确保它与其他上下文元素一起适合上下文窗口。以 LangGrap 为例,短期记忆是使用检查点作为代理状态的一部分进行管理的,而长期记忆则持久化到长期存储中:

LangGraph 内存管理

来源:https://langchain ai.github.io/langgraphjs/concepts/memory/

在构建多 agent 架构时, 我们还需要注意内存和上下文的隔离。 当在更大的流程中将任务分配给子 agent 时,每个 agent 可能需要了解其他 agent 的结果以保持同步。然而,随着时间推移,这些累积会导致上下文窗口溢出:

多代理上下文溢出

来源:https://cognition.ai/blog/dont-build-multi-agents#a-theory-of-building-long-running-agents))](/assets/images/context-engineering/cognition-memory-overflow.png

重要的是,将上下文存储在两种类型的内存中,以确保向 LLM 提供相关且最新的上下文。否则可能会导致上下文中毒。这可以通过恶意来执行,正如我们在 OWASP LLM 应用程序前 10 名的提示注入和数据中毒攻击中看到的那样。但它也可能出于无辜的原因而发生,例如历史的积累可能会分散模型的注意力,甚至是导致冲突的相互矛盾的信息。

在 Gemini 2.5 报告中, 研究人员发现一个玩 Pokémon 的 Gemini agent 倾向重复其历史中的动作,而不是形成新的方法,这意味着增长的上下文反而成为解决问题的障碍。基于这些原因,应管理聊天历史修剪、摘要以及相关检索信息。

结构化输出(Structured Outputs)

随着 AI agent 架构变得复杂,需要确保 LLM 输出遵循模式或契约,这样可以更容易解析并与其他系统和工作流集成。

结构化输出

来源:https://js.langchain.com/docs/concepts/structured_outputs/

我们都习惯于自由格式的文本结果, 但这些格式可能难以集成到依赖系统和 agent 中。就像设计一组 REST 接口时,不仅要遵循  OpenAPI  等最佳实践,还要遵循与其他组件兼容的契约一样,我们需要指定期望 LLM 返回的输出格式和模式。下面的示例展示了如何使用  AI SDK  指定模式以生成符合特定模式的对象:

import { generateObject } from 'ai';
import { z } from 'zod';

const { object } = await generateObject({
  model: 'openai/gpt-4.1',
  schemaName: 'Travel Itinerary',
  schemaDescription: 'Sample travel itinerary for a trip',
  schema: z.object({
    title: z.string(),
    location: z.string(),
    hotel: z.object({name: z.string(), roomType: z.string(), amount: z.number(), checkin: z.iso.date(), checkout: z.iso.date()}),
    flights: z.array(z.object({carrier: z.string(), flightNo: z.string().max(8), origin: z.string(), destination: z.string(), date: z.iso.datetime()})),
    excursions: z.array(z.object({ name: z.string(), amount: z.string(), date: z.iso.datetime()}))
  }),
  prompt: 'Generate a travel itinerary based on the specified location',
});
为 LLM 输出引入 JSON 结构化输出是有意义的。通常需要平衡处理结构化和非结构化数据,就像 Elasticsearch 在内部所做的那样。出于这个原因,一些模型正在支持生成符合提供的 JSON 模式的输出,包括通过 OpenAI 平台中提供的结构化输出功能。当与函数调用结合使用时,这允许我们定义用于在工具之间传递信息的标准合约。但是,鉴于 LLM 可以生成具有语法问题的 JSON,因此在处理结果时优雅地处理潜在错误非常重要。

可用工具(Available Tools)

上下文工程中可以使用的最后一个元素是我们提供给 LLM 的工具,用于提供数据。工具允许我们执行诸如自动化操作(例如根据行程预订旅行)、使用前文讨论的 RAG 检索数据,或提供来自其他信息源的信息。我们在上文的 flightTool 中展示了一个 RAG 工具的示例,但工具也可以用于获取其他信息来源,例如下面用  AI SDK  构建的天气工具:

import { tool as createTool } from 'ai';
import { z } from 'zod';

import { WeatherResponse } from '../model/weather.model';

export const weatherTool = createTool({
  description: 
  'Display the weather for a holiday location',
  parameters: z.object({
    location: z.string().describe('The location to get the weather for')
  }),
  execute: async function ({ location }) {
    // While a historical forecast may be better, this example gets the next 3 days
    const url = `https://api.weatherapi.com/v1/forecast.json?q=${location}&days=3&key=${process.env.WEATHER_API_KEY}`;

    try {
      const response = await fetch(url);
      const weather : WeatherResponse = await response.json();
      return { 
        location: location, 
        condition: weather.current.condition.text, 
        condition_image: weather.current.condition.icon,
        temperature: Math.round(weather.current.temp_c),
        feels_like_temperature: Math.round(weather.current.feelslike_c),
        humidity: weather.current.humidity
      };
    } catch(e) {
      console.error(e);
      return { 
        message: 'Unable to obtain weather information', 
        location: location
      };
    }
  }
});

 无论使用何种框架,工具都包括:

  • 该工具为通知 LLM 所做的作的描述。
  • 函数所需的参数,以及定义的数据类型。在这里,我们使用 Typescript 验证库 zod 来定义这些。
  • 使用工具时 LLM 要调用的函数。

如果 LLM 支持工具调用,它可以选择调用(可能)一个或多个工具来解决问题。我之前在构建自己的多工具 AI 代理时讨论过我的模型选择经验。在选择模型时,使用 Hugging Face Open LLM 排行榜或 Berkeley 函数调用排行榜等资源调查工具调用支持级别非常重要。问题在于,鉴于 LLM 决定哪些工具与目标相关,它可能会被一个工具、许多工具混淆并调用不相关的工具,正如 Drew Breunig 所讨论的那样Paramanayakam 等人在 2024 年的论文中也讨论了这种工具混淆的想法,他们发现 Llama 3.1 8b 的性能在提供较少的工具时得到改善(19 个对 46 个)。

优化可用工具的数量是一个开放的研究领域。应用 RAG 架构来消除工具混淆的实验,例如检索工具描述以优化 MCP 中的工具选择,以便向 LLM 提供相关的工具描述以获得更准确的结果。

结论

本文介绍了什么是上下文工程,并概述了上下文的关键组件。如果您有兴趣了解更多信息,请查看以下资源。

资源

 

原文: What is Context Engineering? – Elasticsearch Labs

                                                                                </div>



Source link

未经允许不得转载:紫竹林-程序员中文网 » 什么是上下文工程 (Context Engineering)?

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的