产品级AI应用的核心:上下文工程

去年,提示工程(Prompt Engineering)的风很大。

各种Prompt范式层出不穷,但一圈实践下来,真正好用的,还是结构化提示词、零样本(Zero-shot)、少样本(Few-shot)、思维链(Chain-of-Thought)这几样。

提示工程有它的价值,但它的本质,是一问一答,用完即忘。

这种“无状态”的交互,没法处理需要长线记忆的多轮对话,也很难搞定需要持续跟进的复杂工作流。

当我们对大模型应用的效果和稳定性要求越来越高,单靠提示工程, 很难实现预期的效果。

为此,AI大神Andrej Karpathy提出了一个新理念:上下文工程(Context Engineering)

picture.image

他把大模型比作新时代的操作系统,那上下文窗口,就是它的内存(RAM)。

我的理解更直接一点:上下文工程,就是精心设计和管理模型推理时所处的整个信息环境。它的核心不再是像提示工程一样琢磨“怎么问”,而是要构建“提问时,模型应该知道什么”。

今天这篇文章,就从我的实践出发,聊聊上下文工程到底有什么,以及我们该如何设计它。

聊聊上下文工程

拿我们常用的Cursor举例。

当我们在聊天框里提个需求,Cursor为了精准生成代码,背后其实有一套完整的上下文工程在运作:

  • 记忆: 翻阅之前的聊天记录,搞清楚你的真实意图。
  • 工具调用: 调用命令行工具,查看你的项目文件结构。
  • RAG: 提取和你需求最相关的代码,比如当前文件、你通过 @ 主动引用的上下文、会话中的其它信息等。
  • 提示工程: 把收集到的所有信息,和你提的需求,打包成一个高质量的Prompt,再发给大模型。
  • Multi-Agent: 可能还不止一个Agent在干活。一个负责理解、执行任务,另一个可能在旁边监工,审查结果,决定要不要返工或优化。

可以看到,提示工程只是上下文工程里的一环。

而上下文工程,代表的是一种架构层面的理念转变:从“写好单条指令”,到“编排一个有状态、有记忆的智能信息系统”

picture.image

接下来,就来详细聊聊完整上下文工程中的一些细节问题。

提示工程

在Prompt里加几个例子(Few-shot),能让模型输出更稳定,这是咱们的共识。但这里面其实也有不少门道。

首先,例子不是越多越好。有人做过实验,随着示例数量增加,模型效果的提升会进入一个明显的“收益递减”区间。加例子要消耗token,但带来的性能改进却越来越小。

picture.image

因此,在实际应用中,我们要找到一个“性价比”最高的平衡点。对于大多数任务来说,存在一个平衡输出质量和token消耗的实践:

  • 分类任务: 每类给1-3个例子就够。
  • 生成任务: 2-5个例子效果最好。
  • 结构化提取: 2-4个例子,尽量覆盖所有要提取的字段。
  • 推理任务: 2-3个带思考步骤的例子
  • 翻译任务: 3-5个不同复杂度的例子。

除了数量,例子的多样性、是否覆盖边缘场景、甚至是例子的排序,都会影响最终效果。

所以,还有一种更高级的玩法是:准备一个“示例库”,根据用户的每次输入,动态地从库里挑出最相关的几个例子,塞进Prompt里。

除了添加示例样本之外,Prompt模版格式也是一个高性价比的,能提升模型输出质量的技巧。

比如我们熟知的ReAct Prompting ,它模仿人解决问题的思路:思考 -> 行动 -> 观察 -> 调整。这个循环在需要多步推理的复杂任务上,表现非常出色。

picture.image

受ReAct启发,还有一种递归提示,在要求高稳定性的场景很常用。

  
def recursive\_prompt(question, model, iterations=2):  
    """Apply recursive prompting to improve responses."""  
      
    # Initial response  
    response = model.generate(f"Question: {question}\nAnswer:")  
      
    for i in range(iterations):  
        # Self-reflection prompt  
        reflection\_prompt = f"""  
        Question: {question}  
          
        Your previous answer:   
        {response}  
          
        Please reflect on your answer:  
        1. What information might be missing?  
        2. Are there any assumptions that should be questioned?  
        3. How could the explanation be clearer or more accurate?  
          
        Now, provide an improved answer:  
        """  
          
        # Generate improved response  
        response = model.generate(reflection\_prompt)  
      
    return response  

可以看到,它的精髓在于,通过不断地自我审视和批判,让模型自己完善答案,显著提高输出质量。

总之,提示工程的技巧很多,关键是因地制宜,选择最合适的。

对话记忆

想让大模型从一个只会预测下一个词的“组件”,进化成一个真正的“智能体”,记忆是很关键的能力。

一个智能体在执行一个长期目标时,必须能够记住它的总体规划、当前所处的步骤以及它通过工具(比如搜索引擎、代码执行器)观察到的世界状态。没有记忆,任何形式的长期规划都无从谈起。

但模型的上下文窗口终归是有限的,对话一长就满了。所以,必须要有一套记忆管理策略。

通常情况下,我们会采取以下几种策略来优化有限的上下文窗口的使用。

1. 滑动窗口

顾名思义,这种方式很简单,就是仅保存最近的对话轮次,缺点也很明显,会忘记之前提到的关键信息。

picture.image

2. 压缩之前的对话

这种方式稍微负载点,核心是每次都将旧的对话记录压缩成一个历史摘要。摘要可以保留关键信息,同时减少token的消耗。

picture.image

3. 结构化提取之前的对话

为了更好的控制,我们还可以结构化提取和存储历史对话中重要的事实。这比单纯对历史对话进行摘要化压缩,能更精确的控制需要保留的信息。

picture.image

4. 复杂应用状态管理

面对更复杂的应用场景,比如一个产品级AI应用,我们通常需要一个完整的状态管理模块,用来:

  • 记住对话中出现的变量。
  • 实时更新和维护这些变量。
  • 跟踪多步骤任务的进度。
  • 记住每一步的结果,给下一步用。
5. 持久化存储

以上方法还是受限于单次交互。终极方案是把对话信息存入外部数据库(比如向量数据库)。每次新对话开始时,再把最相关的信息检索出来,放进当前上下文。

这本质上就是RAG的一种应用,非常考验检索的精准度。

以上就是为大模型添加记忆的一些常用方式,我们在不同的场景选择合适的,具有性价比的方式即可。

多智能体

单个智能体处理复杂任务,好比让一个人同时扮演多个角色,很容易精力分散、顾此失彼。认知负担太重,导致结果跑偏。

更好的方式是任务分解。

让一个“总指挥”Agent理解最终目标,把任务拆解,然后分发给不同工种的“执行”Agent。每个执行Agent只用关心自己那一小块上下文,自然能做得更专注、更出色。

关于多智能体的应用,我在之前的文章:《搭建一个AI研究团队:我对Claude多智能体研究系统的思考与实践》做过详细分析,例如,在最后的实践环节,我将一个基于公域数据的深度研究系统拆解成一个多智能架构:Lead Agent:平台研究员信息提取与分析师事实核查员报告撰写师

这里要注意,每个Agent有自己的小上下文,但必须有一个全局上下文来同步整体进度,确保大家最终能合力完成任务。这依然离不开前面讲的记忆模块。

理论说了不少,我们用一个**“AI深度研究助理”** 的案例,把上下文工程的各个环节串起来看看。

  
import json  
  
class ResearchAssistant:  
    """一个用于综合来自多个信息源的系统。"""  
      
    def \_\_init\_\_(self, llm\_service, retrieval\_service):  
        self.llm = llm\_service  
        self.retrieval = retrieval\_service  
        self.research\_state = {  
            "topic": "",  
            "query\_results": [],  
            "extracted\_concepts": {},  
            "concept\_relationships": [],  
            "synthesis": "",  
            "knowledge\_gaps": []  
        }  
      
    def set\_research\_topic(self, topic):  
        """设置研究主题并生成初始查询。"""  
        self.research\_state["topic"] = topic  
          
        # 使用一个提示程序(prompt program)生成结构化的查询  
        query\_prompt = f"""任务:为研究主题 “{topic}” 生成有效的搜索查询  
  
        流程:  
        1. 将主题分解为其核心组成部分  
        2. 为每个组成部分生成具体的搜索查询  
        3. 包含针对该主题不同视角的查询  
        4. 添加用于获取背景/基础信息的查询  
  
        格式:  
        核心组成部分:  
        - [组成部分1]  
        - [组成部分2]  
  
        推荐查询:  
        1. [具体查询1]  
        2. [具体查询2]  
        3. [具体查询3]  
  
        视角查询:  
        1. [视角1的查询]  
        2. [视角2的查询]  
  
        背景查询:  
        1. [背景1的查询]  
        2. [背景2的查询]  
        """  
          
        query\_suggestions = self.llm.generate(query\_prompt)  
          
        # 在实际应用中,你需要解析这个结构化的输出  
        # 在本示例中,我们使用占位符查询  
        return ["query1", "query2", "query3"]  
      
    def retrieve\_information(self, queries):  
        """使用生成的查询来检索信息。"""  
        # 在真实的实现中,这里会调用一个实际的检索服务  
        # 在本示例中,我们使用占位符结果  
        for query in queries:  
            # 模拟检索结果  
            results = [  
                {"title": f"关于 {query} 的结果1", "content": "示例内容1", "source": "来源A"},  
                {"title": f"关于 {query} 的结果2", "content": "示例内容2", "source": "来源B"}  
            ]  
            self.research\_state["query\_results"].extend(results)  
          
        return self.research\_state["query\_results"]  
      
    def extract\_concepts(self):  
        """从检索到的信息中提取关键概念。"""  
        # 从检索结果构建上下文  
        context = self.\_build\_retrieval\_context()  
          
        # 使用基于模式(schema)的提示来提取概念  
        concept\_prompt = f"""任务:从以下研究信息中提取关键概念。  
        研究主题:{self.research\_state["topic"]}  
  
        信息来源:  
        {context}  
  
        流程:  
        1. 识别在多个来源中都提到的关键概念  
        2. 为每个概念提取相关的细节和定义  
        3. 注意不同来源在描述概念时的差异或分歧  
        4. 为每个概念分配一个相关性分数(1-10)  
  
        格式:  
        概念:[概念名称1]  
        定义:[综合定义]  
  
        关键属性:  
        - [属性1]  
        - [属性2]  
  
        来源差异:  
        - [来源A]:[该来源的描述方式]  
        - [来源B]:[该来源的描述方式]  
  
        相关性分数:[1-10]  
  
        概念:[概念名称2]  
        ...  
        """  
          
        extraction\_results = self.llm.generate(concept\_prompt)  
          
        # 在实际应用中,你需要解析这个结构化的输出  
        # 在本示例中,我们使用占位符概念  
        self.research\_state["extracted\_concepts"] = {  
            "概念1": {  
                "definition": "概念1的定义",  
                "properties": ["属性1", "属性2"],  
                "source\_variations": {  
                    "来源A": "来自A的描述",  
                    "来源B": "来自B的描述"  
                },  
                "relevance": 8  
            },  
            "概念2": {  
                "definition": "概念2的定义",  
                "properties": ["属性1", "属性2"],  
                "source\_variations": {  
                    "来源A": "来自A的描述",  
                    "来源B": "来自B的描述"  
                },  
                "relevance": 7  
            }  
        }  
          
        return self.research\_state["extracted\_concepts"]  
      
    def \_build\_retrieval\_context(self):  
        """从检索结果构建上下文。"""  
        ifnot self.research\_state["query\_results"]:  
            return"尚未检索到任何信息。"  
          
        # 包含一部分检索到的信息样本  
        # 在实际应用中,由于token限制,你可能需要进行摘要或筛选  
        context = ""  
        for i, result in enumerate(self.research\_state["query\_results"][:5]):  
            context += f"来源 {i+1}: {result['title']}\n"  
            context += f"内容: {result['content'][:200]}...\n"  
            context += f"出处: {result['source']}\n\n"  
          
        return context  
      
    def analyze\_relationships(self):  
        """分析已提取概念之间的关系。"""  
        ifnot self.research\_state["extracted\_concepts"]:  
            return"尚未提取任何概念。"  
          
        # 获取概念名称的列表  
        concepts = list(self.research\_state["extracted\_concepts"].keys())  
          
        # 使用一个比较矩阵模板进行关系分析  
        relationship\_prompt = f"""任务:分析研究主题中关键概念之间的关系。  
        研究主题:{self.research\_state["topic"]}  
  
        待分析的概念:  
        {", ".join(concepts)}  
  
        流程:  
        1. 创建所有概念间的关系矩阵  
        2. 为每一对概念确定其关系类型  
        3. 标注每种关系的强度(1-5)  
        4. 识别任何冲突或互补的关系  
  
        格式:  
        关系矩阵:  
         | 概念 | {" | ".join(concepts)} |  
         |---------|{"-|" * len(concepts)}        """  
          
        # 为每个概念添加行  
        for concept in concepts:  
            relationship\_prompt += f"| {concept} |"  
            for other in concepts:  
                if concept == other:  
                    relationship\_prompt += " X |"  
                else:  
                    relationship\_prompt += " ? |"  
            relationship\_prompt += "\n"  
          
        relationship\_prompt += """  
        详细关系:  
        [概念A] → [概念B]  
        类型:[因果/层级/相关/等]  
        强度:[1-5]  
        描述:[简要描述它们如何关联]  
             [继续分析其他相关组合...]  
        """  
          
        relationship\_results = self.llm.generate(relationship\_prompt)  
          
        # 在实际应用中,你需要解析这个结构化的输出  
        # 在本示例中,我们使用占位符关系  
        self.research\_state["concept\_relationships"] = [  
            {  
                "source": "概念1",  
                "target": "概念2",  
                "type": "因果关系",  
                "strength": 4,  
                "description": "概念1直接影响概念2"  
            }  
        ]  
          
        return self.research\_state["concept\_relationships"]  
      
    def synthesize\_research(self):  
        """综合生成一份全面的研究摘要。"""  
        # 确保我们已经提取了概念和关系  
        ifnot self.research\_state["extracted\_concepts"]:  
            self.extract\_concepts()  
          
        ifnot self.research\_state["concept\_relationships"]:  
            self.analyze\_relationships()  
          
        # 从概念和关系中构建上下文  
        concepts\_str = json.dumps(self.research\_state["extracted\_concepts"], indent=2, ensure\_ascii=False)  
        relationships\_str = json.dumps(self.research\_state["concept\_relationships"], indent=2, ensure\_ascii=False)  
          
        synthesis\_prompt = f"""任务:就该主题综合生成一份全面的研究摘要。  
        研究主题:{self.research\_state["topic"]}  
  
        关键概念:  
        {concepts\_str}  
  
        概念关系:  
        {relationships\_str}  
  
        流程:  
        1. 创建一个连贯的叙述,将关键概念整合起来  
        2. 突出不同来源达成共识的领域  
        3. 指出重要的分歧或矛盾之处  
        4. 识别知识空白或需要进一步研究的领域  
        5. 总结最重要的发现  
  
        格式:  
        # 研究综述:[主题]  
        ## 核心发现  
        [最重要见解的摘要]  
        ## 概念整合  
        [连接概念及其关系的叙述]  
        ## 共识领域  
        [各来源达成一致的观点]  
        ## 分歧领域  
        [各来源存在分歧或矛盾的观点]  
        ## 知识空白  
        [需要更多研究的领域]  
        ## 结论  
        [对当前知识状况的总体评估]  
        """  
          
        synthesis = self.llm.generate(synthesis\_prompt)  
        self.research\_state["synthesis"] = synthesis  
          
        # 提取知识空白(在实际应用中,你会从综述中解析这些内容)  
        self.research\_state["knowledge\_gaps"] = [  
            "空白1:需要对X进行更多研究",  
            "空白2:Y和Z之间的关系尚不清楚"  
        ]  
          
        return synthesis  
      
    def complete\_research\_cycle(self, topic):  
        """运行一个从主题到综述的完整研究周期。"""  
        # 设置研究主题并生成查询  
        queries = self.set\_research\_topic(topic)  
          
        # 检索信息  
        self.retrieve\_information(queries)  
          
        # 提取并分析概念  
        self.extract\_concepts()  
        self.analyze\_relationships()  
          
        # 综合研究发现  
        synthesis = self.synthesize\_research()  
          
        return {  
            "topic": topic,  
            "synthesis": synthesis,  
            "concepts": self.research\_state["extracted\_concepts"],  
            "relationships": self.research\_state["concept\_relationships"],  
            "knowledge\_gaps": self.research\_state["knowledge\_gaps"]  
        }  

在这个深度研究助理的实例中,我们应用了多种上下文工程的模式:

  • 状态管理: 用research_state字典跟踪整个研究流程的状态。
  • 渐进式上下文: 一步步地生成查询、检索信息、提取概念,上下文逐渐丰富和聚焦。
  • 结构化模式: 无论是生成查询,还是提取概念,都使用了结构化的格式要求,确保输出稳定可用。
  • 模板程序: 每个 ...\_prompt 都是一个为特定子任务设计的、可复用的提示模板。
  • 多步处理: 将复杂的研究任务,拆分成了查询、检索、提取、分析、综合等多个阶段。

这些都是在实践中被证明行之有效的模式,可以在咱们的项目中灵活运用。

结语

从提示工程到上下文工程,这不仅仅是一个技术名词的升级,更像是一种思维模式的跃迁。

在上下文工程中,有记忆,有工具,有结构化的知识,有多样的智能体协同。它为模型提供了一个稳定、可靠且信息丰富的工作环境。

希望今天的分享,能让你在构建自己的AI应用时,不止于打磨那一句精妙的Prompt,而是能退后一步,从系统和架构的视角,去思考如何为你的AI,构建一个真正强大的“上下文”。

本文中用到的一些示例来源于开源项目:https://github.com/davidkimai/Context-Engineering,它能帮助我们更加系统化的去理解和学习上下文工程,感兴趣的读者可以自行深入学习。

0
0
0
0
评论
未登录
暂无评论