从提示词到智能体:大语言模型应用开发的系统工程演进

📅 2026/8/14 2:20:57
从提示词到智能体:大语言模型应用开发的系统工程演进
1. 从“提示词工程”到“驾驭工程”一个必然的演进如果你在过去一年里深度使用过任何大语言模型那么“Prompt Engineering”提示词工程这个词对你来说一定不陌生。从最初的“请扮演一个专家”到后来复杂的“思维链”、“少样本学习”我们都在学习如何用更精巧的指令让模型输出更符合预期的结果。这就像是在学习一门与AI沟通的“咒语学”我们不断调整词句试图精准地“命令”模型。然而随着应用场景从简单的问答、写作扩展到需要多步推理、调用工具、处理长文档的复杂任务单纯优化一个“提示词”开始显得力不从心。你可能会遇到这样的困境精心设计的提示词在对话进行到第10轮时突然失效模型在处理一份50页的PDF时遗忘了开头的关键信息或者当你试图让AI调用外部API完成一个任务链时整个流程变得脆弱不堪一个小错误就导致全盘崩溃。这些问题的根源在于我们面对的已经不再是一个静态的“问答机”而是一个动态的、有状态的“智能体”。这时一个更宏大的概念——“Harness Engineering”我倾向于翻译为“驾驭工程”或“系统工程”——便应运而生。它不再仅仅关注输入的那一句话Prompt而是将视野扩大到整个交互的上下文Context并最终落脚于构建一个健壮、可靠、可执行的智能体Agent系统。简单来说Prompt Engineering 是战术而 Harness Engineering 是战略。前者教你如何打好一发子弹后者则教你如何设计整场战役的指挥、后勤和协同作战体系。2. 驾驭工程的三层架构Prompt、Context与Agent的协同要理解驾驭工程我们必须将其拆解为三个相互依存、层层递进的核心层次。这并非三个孤立的模块而是一个完整的系统工程框架。2.1 第一层Prompt Engineering —— 精准的“点火指令”Prompt是驱动模型的直接指令是交互的起点。在驾驭工程的视角下Prompt的设计目标发生了转变它不再追求“一次性解决所有问题”而是追求“为后续的复杂交互奠定一个清晰、稳定、可扩展的起点”。核心转变从“万能指令”到“系统初始化”早期的Prompt尝试塞入所有规则和示例期望模型能一劳永逸。但在复杂系统中这会导致上下文窗口被迅速占满且难以维护。现代的Prompt设计更倾向于定义清晰的角色与边界明确告诉模型“你是谁”例如一个严谨的代码审查助手、“你的能力范围”例如只能分析提供的代码片段不能生成新代码以及“你的行为准则”例如优先指出安全漏洞。设定输出格式与结构化要求强制要求模型以JSON、Markdown表格或特定关键词开头如“结论”进行回复。这为后续的自动化处理如程序解析提供了可能。预留扩展接口在Prompt中暗示或明示模型在需要时可以请求更多信息“如果你需要查看第X章节的内容请告诉我”或调用特定工具“你可以使用‘计算器’工具来验证这个数值”。实操心得一个高效的“系统Prompt”往往简短而有力。避免使用冗长的、充满形容词的句子。直接使用“你是一个...”、“你必须...”、“你的输出必须包含...字段”这样的祈使句。将具体的示例和复杂规则移到“上下文”层去动态管理。2.2 第二层Context Engineering —— 动态的“记忆与舞台”如果说Prompt是剧本的第一句台词那么Context就是整场戏剧的舞台布景、道具和之前的所有剧情。Context Engineering上下文工程是驾驭工程中最具挑战性也最核心的部分它负责管理模型“看到”的一切信息。核心挑战与策略模型有上下文窗口限制如常见的128K、200K tokens而我们的任务数据长文档、多轮对话历史、工具调用结果可能远超这个限制。上下文工程的核心就是解决“有限记忆”与“无限信息”之间的矛盾。分层存储与检索对话历史管理不是把所有历史对话都塞进上下文。通常只保留最近几轮例如最近5轮的完整对话并对更早的历史进行摘要Summary。例如每10轮对话后让模型自己生成一段“此前我们讨论了A、B、C问题并得出了X结论”的摘要用这段摘要替代原始的10轮内容。长文档处理对于超长文档如一本书、一份长报告采用“向量数据库检索”的方式。将文档切分成块Chunk转换成向量存入数据库。当用户提问时将问题也转换成向量在数据库中检索出最相关的几个文本块只将这些相关块作为上下文提供给模型。这被称为“检索增强生成”。关键信息缓存将系统核心指令、用户身份信息等极少变更的关键信息始终固定在上下文窗口的头部System Prompt之后确保模型在任何时候都不会忘记基本规则。上下文窗口的“装修艺术” 上下文窗口的排列顺序直接影响模型的表现。一个常见的有效结构是[系统角色Prompt] [关键固定信息] [相关文档片段1] [相关文档片段2] [最近对话摘要] [最近3轮完整对话] [当前用户问题]这种结构确保了模型优先关注系统指令和最新、最相关的信息。踩坑实录我曾构建一个客服Agent将用户长达100条的聊天历史全部放入上下文结果模型响应速度急剧下降且经常混淆一周前和当前的问题。后来改为“最近5条完整对话 之前历史的逐日摘要”模式准确率和速度都大幅提升。教训是更多的上下文不等于更好的上下文精准的相关性才是关键。2.3 第三层Agent Engineering —— 自主的“执行与协同”Agent是Prompt和Context所服务的终极形态是一个能够感知、规划、执行和反思的自主系统。在这一层工程化的重点从“如何与模型对话”转向“如何构建一个可靠的应用系统”。智能体的核心循环与工程化实现一个典型的Agent框架如LangChain、LlamaIndex、AutoGen会实现以下循环感知接收用户输入结合当前上下文由Context Engineering层管理理解意图。规划决定下一步该做什么。是直接回答还是需要调用某个工具如搜索、计算、写代码如果需要多步分解成子任务。执行调用相应的工具或模块并获取执行结果。这里的工程难点在于工具调用的可靠性。API可能会超时、返回错误格式、甚至完全失败。反思评估执行结果是否解决了问题。如果没有是否需要调整计划或重试最后将本轮行动的结果整理成自然语言并更新对话上下文。工程化的关键考量状态管理Agent在多次循环中必须维持内部状态例如当前任务完成到哪一步了已经收集了哪些信息。这通常需要一个独立于模型上下文的外部状态机或数据库来维护。错误处理与韧性工具调用失败怎么办模型输出不符合解析格式怎么办一个健壮的Agent必须有完整的异常处理链路重试、降级方案如换用备用工具、向用户清晰报错。流式输出与用户体验对于耗时较长的任务如编写一篇长文让用户看到“思考过程”或“实时生成”的内容至关重要。这需要处理模型的流式响应并中间插入规划步骤的提示。成本与延迟优化每次调用模型都产生成本和延迟。工程师需要设计策略比如缓存常见推理结果、使用小模型进行简单任务分类、将多个小请求批处理等。3. 实战构建一个简易的“技术文档问答Agent”让我们通过一个具体的例子将上述三层架构串联起来。目标是构建一个Agent它能理解用户关于某技术框架比如React的问题并从官方文档中寻找答案。3.1 系统设计与组件选型目标用户输入自然语言问题如“React中如何优化组件重渲染”Agent能自动从React官方文档中查找相关信息并组织成连贯答案。架构选型LLM核心使用OpenAI GPT-4或Claude 3等具备较强推理和指令遵循能力的模型。框架使用LangChain因为它提供了完整的Agent、工具链和检索器抽象。向量数据库使用ChromaDB轻量且易于集成用于存储文档向量。文档处理使用LangChain的文档加载器如WebBaseLoader和文本分割器。3.2 分步实现与代码剖析第一步上下文工程层准备——文档嵌入# 伪代码展示核心流程 from langchain_community.document_loaders import WebBaseLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma # 1. 加载文档假设是React官网的优化指南页面 loader WebBaseLoader(https://react.dev/learn/optimizing-performance) docs loader.load() # 2. 分割文本 text_splitter RecursiveCharacterTextSplitter(chunk_size1000, chunk_overlap200) splits text_splitter.split_documents(docs) # 3. 嵌入并存储到向量库 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) vectorstore Chroma.from_documents(documentssplits, embeddingembeddings, persist_directory./react_docs_db) retriever vectorstore.as_retriever(search_kwargs{k: 4}) # 检索最相关的4个片段这一步是离线进行的构建了Agent的“长期记忆库”。chunk_size和overlap的选择至关重要太小会丢失上下文太大会降低检索精度。200-500的重叠有助于保持段落间的连贯性。第二步定义Agent的工具与Promptfrom langchain.agents import AgentExecutor, create_openai_tools_agent from langchain_openai import ChatOpenAI from langchain.tools.retriever.tool import create_retriever_tool from langchain import hub # 1. 将检索器封装成Agent可用的工具 search_tool create_retriever_tool( retriever, search_react_docs, Searches and returns information from the React official documentation. Use this tool when you need to find specific API references, best practices, or optimization guides. ) # 2. 从LangChain Hub拉取一个预设的Agent Prompt模板并自定义 prompt_template hub.pull(hwchase17/openai-tools-agent) # 自定义系统消息部分这是我们的“点火指令” prompt_template.messages[0].prompt.template You are an expert React.js assistant. Your sole purpose is to answer questions about React using ONLY the information provided by the search_react_docs tool. You must follow these rules: 1. ALWAYS use the search tool to look up information before answering. 2. If the tool returns no relevant results, say I couldnt find specific information on that in the current documentation. 3. Base your answer strictly on the retrieved document snippets. Do not hallucinate or use your general knowledge. 4. Format your answer in clear, bullet-pointed lists if applicable, and cite which document chunk the information came from (e.g., [Doc1]). 这里Prompt被严格设计为定义角色、规定必须使用工具、强调基于检索结果回答、格式化输出。它不再包含具体的React知识知识被转移到了Context向量库和工具中。第三步组装并运行Agent# 3. 初始化LLM llm ChatOpenAI(modelgpt-4-turbo-preview, temperature0) # 4. 创建Agent tools [search_tool] agent create_openai_tools_agent(llm, tools, prompt_template) # 5. 创建执行器这里可以配置错误处理、最大迭代次数等 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, # 打印思考过程便于调试 handle_parsing_errorsTrue, # 处理输出解析错误 max_iterations5, # 防止无限循环 early_stopping_methodgenerate # 设定停止条件 ) # 6. 运行 result agent_executor.invoke({input: What are the best practices to avoid unnecessary re-renders in React?}) print(result[output])当用户提问时AgentExecutor会驱动整个循环LLM根据Prompt和问题决定调用search_react_docs工具。工具执行从向量库检索出4个最相关的文档片段返回给LLM。LLM将检索结果作为新的上下文结合原始问题生成最终答案。AgentExecutor监控整个过程确保不超过最大迭代次数并处理任何中间错误。3.3 可能遇到的问题与调试技巧即使这样一个简单的Agent也会遇到许多工程问题检索不相关用户问“如何用useEffect”可能检索到的是“useEffect的清理函数”。这可能是因为嵌入模型不够好或者文本分割不合理。调试方法检查检索出的原文片段调整chunk_size或尝试不同的嵌入模型。也可以增加检索数量k值让LLM自己从更多材料中筛选。Agent陷入循环LLM可能反复调用同一个工具无法得出答案。这通常是由于Prompt指令不清晰或工具返回的结果始终无法满足LLM的“回答标准”。调试方法开启verboseTrue观察Agent的思考链ReAct格式看它在哪一步逻辑卡住了。然后修改Prompt给出更明确的停止条件比如“如果你搜索了两次仍未找到直接答案可以基于已找到的信息进行合理推断并说明信息来源有限”。上下文超限如果检索返回的文档片段很长加上对话历史可能超出模型上下文窗口。解决方案在工具定义中可以对检索结果进行二次摘要只返回最核心的几句话而不是整个文本块。4. 进阶模式多智能体协作与复杂工作流当单个Agent无法处理复杂任务时就需要引入多智能体协作系统。这标志着驾驭工程进入了更高阶的阶段系统架构设计。场景设想一个“全栈开发助手”需要处理从产品需求分析到前端、后端、数据库设计的全过程。架构设计主控Agent项目经理接收用户原始需求“我想做一个个人博客系统”。它的Prompt被设计为擅长任务分解和协调。它不负责具体实现而是将任务拆解为“需求规格说明书”、“数据库设计”、“API设计”、“前端页面设计”。专家Agent群需求分析师Agent擅长与用户澄清细节输出结构化的需求文档。数据库设计师Agent精通SQL和数据库范式根据需求文档输出ER图和数据表DDL。后端架构师Agent熟悉Node.js/Python等根据API设计输出控制器、服务层代码框架。前端工程师Agent熟悉React/Vue输出组件树结构和页面原型代码。工作流引擎定义Agent之间的协作协议。例如主控Agent先调用需求分析师Agent与用户交互产出需求文档。需求文档同时传递给数据库设计师Agent和后端架构师Agent。后端架构师Agent需要等待数据库设计完成以获取数据模型。最后前端工程师Agent根据需求文档和API设计进行开发。共享上下文与状态管理所有Agent共享一个中央“项目上下文”包括需求文档、设计图、API规范等。每个Agent完成任务后将产出物更新到共享上下文中。这通常需要一个外部存储如数据库或文件系统来实现。工程挑战通信开销Agent间频繁的消息传递会增加延迟和成本。需要精心设计通信格式避免传递冗余信息。一致性保证如何确保后端Agent设计的API与前端Agent期望的接口一致需要引入“契约测试”或“规范校验”环节。故障隔离一个Agent的失败不应导致整个系统崩溃。需要为每个Agent设置超时、重试和降级策略。5. 核心原则与未来展望回顾从Prompt到Context再到Agent的旅程驾驭工程的核心思想可以归结为以下几点解耦与模块化将知识Context、推理逻辑LLMPrompt、执行能力Tools、流程控制Agent Loop分离。这使得每个部分都可以独立优化、测试和替换。韧性设计假设任何环节都可能出错——LLM会胡言乱语、工具会调用失败、上下文会溢出。系统必须在设计层面包含错误检测、恢复和降级机制。可观测性系统必须是透明的。详细的日志包括Agent的思考过程、工具调用输入输出、性能指标延迟、token消耗、错误率是调试和优化的生命线。以人为本最终目的是服务用户。流式输出、进度提示、清晰的错误信息、提供人工接管入口这些体验细节和功能性同样重要。展望未来随着模型上下文窗口的持续扩大、多模态能力的融合以及工具调用可靠性的提升驾驭工程的复杂性只会增加。我们可能会看到更标准的Agent设计模式、更强大的工作流编排引擎、以及专门用于评估Agent性能的基准测试套件出现。对于开发者而言掌握驾驭工程意味着从“与大模型对话的艺术家”转变为“构建智能系统的工程师”。这要求我们不仅要有软件工程的基本功设计模式、系统架构、测试还要深刻理解LLM的能力边界和行为特性并在两者之间找到精妙的平衡点。这条路充满挑战但也正是其魅力所在——我们正在亲手为这些强大的“大脑”装配四肢和感官并教会它们如何可靠地与世界互动。