尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从提示工程到Agentic AI:FastAPI+LangGraph+RAG实战指南

从提示工程到Agentic AI:FastAPI+LangGraph+RAG实战指南 做了几年提示工程再回头看“提示工程师”这个概念确实已经被重塑了。过去我们写Prompt本质上是在跟一个对话模型打交道你一句我一句把上下文“喂”到窗口里期待它吐出符合预期的文本。到了2025年大家讨论的Agentic AI直接把玩法改了模型不再是被动的应答机器而是能够自主规划任务、调用外部工具、读取知识库、循环迭代直至达成目标的“执行体”。这种形态下提示工程不再是一段段孤立的Prompt文本而是整个系统的控制逻辑本身。很多团队在Agent项目上翻车核心原因就是把Agent做成了“套壳的对话接口”——给一个System Prompt配上两个工具就对外宣称做了大模型应用。真正能稳定跑起来的Agentic系统背后是提示词架构、编排框架、记忆体系、检索策略和工程化部署五个层面的协同设计。这篇文章把我从0到1搭建一套基于FastAPI、LangChain、LangGraph、RAG和pgvector的Agentic系统的完整过程拆开来讲包括每个环节为什么这么选型、提示词怎么拆才能控制住Agent的行为边界、检索层如何和推理层配合、服务上线时又踩了哪些坑。这篇实战手册面向的不是只想跑通Demo的初学者而是已经具备一定LLM应用基础、准备把Agent推向真实业务场景的工程师和技术负责人。你不需要照抄每一行代码但这里面的架构决策、提示词结构、参数调优思路以及那些在官方文档里不会告诉你的坑都是可以直接复用的。1. Agentic AI的本质与提示工程的角色升级1.1 从“指令对话”到“目标驱动”的范式转移在做Agent之前先把概念对齐一下。传统基于LLM的应用本质上是一个“输入-输出”映射用户提问模型回答上下文由每次对话的窗口决定模型通常不具备调用外部系统的能力也不知道自己什么时候该停下来。一套基于GPT-4或国产大模型搭建的问答机器人哪怕接入了知识库本质也还是一个增强检索的文本生成器。Agentic AI换了一个底层逻辑系统不再以“对话回合”为单位思考而是以“任务”为单位。系统拿到一个目标自己拆解子任务、决定调用哪个工具、观察工具返回结果、判断是否达成目标、决定继续还是终止。听起来很美好但这个“自己决定”的过程恰好是工程上最难约束的地方。提示工程在这个范式里扮演的角色从“指挥模型说话”变成了“指挥模型行为”。你需要告诉模型的不再是“请用三段式回答”而是“你有这几个工具可以用在什么条件下使用哪个工具遇到什么结果时应该重试什么情况下必须停下来向用户解释”。这套逻辑如果只靠一段写得很长的System Prompt硬撑大概率会在复杂任务里失衡。1.2 提示工程师在Agent项目中的新型职责清单传统提示工程师的核心产出是Prompt文本模板、少样本示例和输出格式约束。到了Agentic系统里职责范围明显扩大第一工具语义设计。每个工具的描述、参数说明、适用条件其实都是提示词的一部分。模型是通过自然语言理解“什么时候该用这个工具”的工具描述写得含糊Agent在决策时就会乱选工具。工具描述写得好不好直接影响所有后续步骤。第二状态转移规则设计。Agent在什么条件下从一个节点跳到另一个节点这部分逻辑通常由代码实现但“判断依据”往往来自提示词中的指令约束。比如“当工具连续两次返回同一错误时停止重试并告知用户”。这些规则需要组织成模型能稳定遵循的语言。第三记忆管理策略。哪些信息需要写入短期记忆哪些需要持久化到长期记忆怎么在上下文中压缩和提取关键事实这些都需要提示词层面的格式约定。没有约定格式记忆写入和读取就会出现混乱。第四失败恢复路径。Agent一定会出错关键是出错后能否自愈。是让模型重新调用工具、换一种表达方式查询还是直接进入兜底对话每一种策略都对应不同的提示词分支设计。这四项职责意味着Agent项目里的提示工程不再是一个独立的“写词”岗位而是一个贴近系统架构的设计岗位。搭建Agent的第一天就要把提示词当成和代码一样需要版本管理、测试和监控的一等公民。1.3 为什么说提示词是Agent系统的“控制面”用一个通俗的类比解释传统LLM应用里的提示词类似于给一个聪明但被动的新员工写任务说明书他按说明书交付Agentic系统里的提示词则类似于给这个员工写绩效考核制度和决策授权书制度定义了他在什么边界内可以自主行动超出边界怎么升级处理。代码定义了“有哪些审批流”提示词定义了“什么时候走哪个审批流”。典型案例如下你现在要做一个“竞品动态追踪Agent”目标是每周自动抓取海外科技媒体的产品发布信息生成竞品分析报告。代码层面你能做的是提供搜索工具、网页抓取工具、数据库写入工具、报告生成工具然后用LangGraph把节点串起来。但Agent到底应该搜索几个关键词、读到什么信息算“产品发布”、多条信息冲突时信哪条、报告写到什么详细程度这些全部依赖提示词来约束。如果你的System Prompt写的是“请根据搜索到的信息生成竞品分析报告”模型大概率会泛泛而谈如果把System Prompt改成“你是竞品分析师先调用search_news工具搜索最近7天目标公司新闻对每条结果用2-3行提炼要点再调用fetch_webpage读取原文重点提取产品名、发布时间、目标客群、与本公司产品差异所有论点列出信息出处若搜索结果为空必须说明”输出的稳定性和深度会完全不同。这就是“控制面”的含义提示词决定了Agent在每个决策节点上的行为取向它不是幕后文案而是系统的主干逻辑。2. 架构拆解FastAPILangGraphpgvector这套组合到底怎么协作2.1 各组件在系统中的分工逻辑先给出我自己验证过的一套架构再逐个解释为什么选它们。FastAPI对外的HTTP服务层负责接收用户请求、调用Agent执行、以SSEServer-Sent Events方式流式返回结果。选它是因为异步原生支持和Pydantic类型校验在处理长时间运行的Agent任务时不会阻塞事件循环。LangChain不是必需的“胶水”但在工具封装、Prompt模板、输出解析、文本分割这些环节确实能省下大量重复代码。我更倾向于只把它当工具库用不把整个业务逻辑依托在LangChain的抽象链路上。LangGraphAgent编排引擎负责定义节点Node、边Edge和状态State。和普通LangChain Chain最大的区别是LangGraph支持条件分支、循环和状态持久化这正好匹配Agent的“规划-执行-观察-再规划”循环。pgvectorPostgreSQL的向量检索插件负责存储知识库片段、会话历史、向量Embedding并通过相似度检索提供RAG数据。选它不选Milvus或者Weaviate的原因很简单如果业务库本来就是PostgreSQL直接加上pgvector少维护一套搜索集群。RAG知识接入层让Agent在回答具体问题时能引用私有知识库或实时检索到的信息而不是完全依赖模型参数记忆。FastAPI LangGraph RAG pgvector的落点其实就是一套“对外API → 编排引擎 → 知识检索 → 工具调用”的闭合链路。2.2 一次任务请求的完整数据流为了让整个架构的协作过程更具体我拆解一次典型请求的流转路径。第一步用户通过FastAPI的/agent/run接口提交一个任务描述例如“帮我调研A公司最近一个月发布的AI芯片产品写一份800字左右的简报”。FastAPI层先做参数校验把用户ID和任务描述组织成初始状态。第二步FastAPI把请求交给LangGraph编译好的Agent图执行。初始状态包含用户目标、历史记忆摘要从PostgreSQL中读取、可用工具列表、最大执行步数限制。第三步Agent在第一个节点上做“规划”。模型读取System Prompt和用户任务调用规划工具或者直接输出可执行的步骤列表比如[search_news: A公司 AI芯片 发布时间 最近一个月, read_content: 前5条链接, analyze: 产品列表与差异点, report: 生成Markdown简报]。第四步执行节点按步骤调用工具。工具返回的结果会写回State模型继续判断信息够不够不够就再规划一轮新步骤够了就进入生成报告节点。第五步在生成节点中Agent除了参考工具返回的信息还会调用pgvector检索企业内部的“产品对比框架”文档按照既定框架输出报告。最后通过FastAPI流式返回给前端同时将本次任务的关键摘要写入会话历史表。第一次搭这套链路时容易犯的错是让每一步工具调用都经过一次完整的“模型决策”导致任务执行时间长、Token消耗大。正确做法是把能固化的子流程写成确定性代码比如“搜索结果→抽取正文→文本切片”这些不需要模型参与判断模型只负责做那些“无法预先穷举”的决策比如“该用什么关键词组合扩大搜索”。2.3 经验结论不是越强的模型越好而是越可控的模型越好架构选型阶段有个有意思的发现对于Agent系统不一定要把最强模型用在所有节点上。最强模型往往也更贵、更慢、更倾向于发挥“创造性”这在开放式写作场景是优点在需要严格按步骤执行的Agent场景反而容易跑偏。我在实践中的策略是把任务分层。规划节点和最终报告节点使用更强的模型GPT-4级别或同档国产模型负责理解和生成复杂内容工具调用结果解析、简单上下文判断这类子任务用中等模型如GPT-4o mini级别或开源模型的量化版延迟更低、费用更低。LangGraph的节点级设计非常适合这种多模型混合路由——每个节点可以定义独立的LLM实例。模型选型的另一个标准是工具调用稳定性。Agent系统中最核心的链路是“模型输出结构化指令→系统解析并执行工具”模型的输出如果不稳定后面所有环节都跟着遭殃。在一套需要复杂工具组合的任务上我专门对比过几款主流模型的工具调用能力结论是不要只看Benchmark分数一定要拿自己定义好的20个工具场景做回归测试统计“有效工具调用率”——即模型输出的工具名和参数能否被正确解析并执行。3. Agent的思考开关提示词如何控制规划、工具调用与自我修正3.1 System Prompt的模块化设计方法很多Agent效果不好根因在System Prompt被写成了一整块“道德经”。模型不是读不懂而是在长上下文里容易忽略中后段的约束。推荐的写法是模块化分区每个模块之间用明确的标记分隔。我自己在项目里常用的一套System Prompt结构大致如下# 角色定义 你是[角色名]负责[任务域]。 # 协作边界 你可以自主调用以下工具[工具列表]。 你不可以做的事情[明确禁止的行为]。 # 任务执行框架 开始任务时先输出Plan节点。 Plan节点格式{steps: [step1, step2]} 执行每步后必须输出Observe节点。 Observe节点格式{observation: 结果摘要, need_more: true/false} # 输出护栏 如果信息不足必须调用search工具补充信息不得编造。 如果工具连续出错超过2次停止并告知用户。 最终报告必须包含信息出处。模块化设计的好处有三个。第一模型对不同指令的“注意力权重”更均匀不会因为某条关键指令写在末尾而被忽略第二便于做A/B测试你可以单独替换某个模块而不影响其他模块的稳定性第三便于排障——当Agent行为异常时能快速定位是角色设定、工具边界、还是输出护栏出了问题。有一个关键技巧模块之间的边界标记要单一且明确。不要一会儿用#一会儿用---一会儿又用自然语言的“接下来请注意”。模型对固定的标记格式更敏感统一的边界符号能显著提升指令遵循度。3.2 规划节点的提示词设计把目标拆解成可执行步骤Agent的“规划能力”一半靠模型另一半靠提示词给出的拆解框架。单纯对模型说“请规划任务”它给出的步骤往往太粗或者太理想化——假设一切工具都能一次成功这在现实世界不可能。我在提示词中专门设计了一套“小步快跑”指令规划原则 1. 每个步骤必须对应一个明确的工具调用或明确的输出。 2. 初始步骤以最简单的信息收集开始不要假设已知答案。 3. 已完成的步骤结果不满足需求时允许在后续规划中追加新步骤。 4. 单个任务的步骤总数不超过[最大步数上限]。 5. 如果目标中包含“列出N个…”的量化要求在规划时必须包含“数量不足时二次补充搜索”的逻辑。这套指令的效果是让Agent天然具备“迭代式研究”的行为模式而不是“想到什么搜什么”。以竞品报告任务为例如果Agent的规划是“搜索竞品A相关信息”这个粒度太粗执行时模型不知道该搜索几次、搜索哪些维度换成如下规划执行稳定程度大幅提升搜索“竞品A 新品发布 最近30天”记录前10条标题和摘要。阅读前5条链接的正文提炼产品名、发布时间、核心功能。对比本公司产品线标记直接竞品和间接竞品。若产品不足3个追加搜索“竞品A 2025 产品线”填补缺口。生成结构化报告。“若不足则追加”这类条件式规划语句非常有效它让Agent在规划阶段就预判了失败的可能并给出了恢复路径。3.3 工具调用的提示词控制格式、参数与错误恢复工具调用是Agentic系统与普通LLM应用最显著的区别之一。在LangChain生态中模型输出工具调用指令后由框架负责解析并执行对应的Python函数。但模型“选择哪个工具、传什么参数”依然由提示词影响。我整理了几个在实践中非常有效的工具调用约束写法第一工具描述中必须包含“何时使用”和“何时不使用”。直接在工具描述里写清楚边界。例如工具名称: search_news 工具描述: 用于搜索外部新闻信息。当需要获取最新资讯、行业动态、产品发布信息时使用。 不适用于: 用户询问产品使用教程、内部文档查询应使用search_docs。这个边界说明极大减少了工具误选率。没有这个说明时模型经常拿外部搜索工具去查内部文档。第二参数描述要给出格式示例。LangChain的函数参数如果只是简单说明“关键词”模型可能输出不规范的查询词。在参数描述中加上“请用空格分隔关键词例如‘AI芯片 新品发布 2025’”检索质量会提升一个档次。因为搜索工具的好坏高度依赖查询词命中的概率提示词在参数层做一次“改写指导”相当于隐式优化了整个检索质量。第三错误恢复策略要在提示词中显式声明。我的System Prompt里有这样一段工具调用失败时 1. 检查是否参数格式有误修正后重试一次。 2. 若仍失败换一组关键词或换一个工具重试。 3. 若连续失败2次停止该步骤转入人工说明节点。没有这段约束时模型可能在一个失败的工具上反复重试四五次白白浪费时间和Token。3.4 自我修正与反思提示词层面的“复盘机制”LangGraph支持在工具执行后增加反思节点Agent有机会对之前的结果进行质量自检再决定继续还是重新执行。这个机制非常有用但如果提示词引导不好反思节点会变成“无脑重新跑一遍”的耗钱机器。我会在反思节点的提示词里加三个自检问题当前收集的信息是否回答了用户任务的所有维度如果没有缺失哪些是否有信息冲突如果有哪个来源可信度更高当前步骤数是否接近上限如果接近是否可以基于现有信息完成任务这三个问题把反思从“要不要重跑”变成“缺什么、信什么、怎么收尾”。Agent的行为会明显变得更谨慎——它会在信息不足时主动补搜在信息冲突时按可信度排序在步骤数接近上限时收敛输出而不是无限发散。4. 让RAG真正Agentic从向量检索到多步推理的完整闭环4.1 传统RAG与Agentic RAG的差别很多从传统RAG迁移过来的团队会问我的知识库已经能检索了为什么还要搞Agentic RAG关键差别在于“检索多少次”、“检索什么东西”和“检索结果如何使用”。传统RAG链路是“用户问题 → 向量化 → TopK检索 → 拼接到上下文 → 生成回答”。它的问题很明显第一只检索一次如果第一轮检索关键词不准后面全错第二不问“需要什么信息”而是直接检索对复杂的多跳问题无能为力第三检索结果不经过推理筛选经常被无关片段干扰生成。Agentic RAG把检索变成了Agent规划的一部分。系统先判断“要回答这个问题需要哪些信息”再决定“分几次检索、先检索什么再检索什么”每次检索后观察结果是否满足需求不满足就换查询词再检索。业务上这种形态更像一个研究员在查资料先查目录再查具体章节信息不够就换一本资料而不是把一整本书扔给模型让它自己找答案。4.2 pgvector落地向量表结构、索引与混合检索pgvector在这里不只是一个“向量数据库”它是与业务数据共存的关系型扩展。我用Docker起一个带pgvector扩展的PostgreSQL核心配置不复杂服务使用PostgreSQL 15以上版本启动时启用vector扩展。建表的核心结构包括知识片段表字段包含自增ID、切片段落、向量列、来源文档ID、元数据、时间戳。建表SQL大致如下CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE knowledge_chunks ( id BIGSERIAL PRIMARY KEY, content TEXT NOT NULL, embedding vector(1536), source_doc VARCHAR(255) NOT NULL, metadata JSONB DEFAULT {}, created_at TIMESTAMP DEFAULT now() ); CREATE INDEX ON knowledge_chunks USING hnsw (embedding vector_cosine_ops);向量维度选择看你用哪个Embedding模型OpenAI的text-embedding-3-small输出1536维国产的BGE系列输出1024维或者768维按实际模型设置。HNSW索引是目前pgvector里性能和召回平衡最好的索引类型适合中等规模几十万到几百万量级的知识库太小的库直接用暴力扫描也无所谓。真正的突破点是混合检索。只用向量相似度检索容易忽略关键词精确匹配的场景比如产品型号“PT-500”这种专有名词向量化后往往找不到精确对应。我在检索层做了“向量检索关键词检索”的并行方案向量检索通过embedding $query_vector计算余弦距离取TopN。关键词检索用PostgreSQL自带的tsvector全文搜索匹配产品名、型号、人名等命名实体。结果融合两张结果集按RFFReciprocal Rank Fusion方式合并排序让同时被两种方式命中的片段排在前面。这个混合检索在真实项目的收益很明显特别是那些包含大量型号、表格、代码标识符的技术文档库纯向量检索的召回率偏低混合检索能补上精确匹配这块短板。4.3 检索代理工具的设计上下文感知的查询改写在Agentic系统里RAG的“检索”被封装成一个工具Agent可以多次调用。这就带来一个全新的提示词工程问题如何让Agent生成高质量检索查询检索工具完成两个任务查询改写和补充信息搜索。查询改写不只是简单地把用户问题丢进向量检索而是从对话上下文和Agent已获取的信息中提炼出一个独立的、能在大规模知识库里“命中”的查询。我在提示词中以“检索前思考”指令让Agent遵循如下逻辑检索前确认 1. 当前已有哪些信息如果信息来自之前的检索且用户未提出新需求无需重复检索。 2. 什么查询词能最大化命中率使用具体术语不要使用口语不要添加修饰词。 3. 是否需要拆分查询如果用户问题包含多个子主题先检索第一个子主题观察结果后再决定是否检索下一个。这套逻辑把“该搜什么”的决策权交给了Agent而不是用户。用户说“那个什么AI芯片现在到底能不能用”如果直接把这句话向量化检索效果大概率很差Agent先改写查询为“AI芯片 量产 状态 合规认证”命中率完全不同。补充信息搜索的触发条件有两个第一是首次检索的TopK片段与问题的直接相关性低于阈值第二是回答中出现了需要进一步核实的实体比如模型回答中写“公司的产品线覆盖边缘计算和云端训练”但当前上下文没有足够依据Agent会触发一次“实体验证搜索”检索“公司 产品线 边缘计算 云端训练”来确认事实。4.4 检索结果的提示词组织上下文注入与可信度标注检索结果返回后不是简单地拼进上下文就完事。拼接格式和可信度提示也会实质影响生成质量。我的做法是把每个知识片段包成结构化块供生成阶段引用[知识片段1] 来源: doc_name 第X章 相关性: 高 内容: 原文片段... [知识片段2] 来源: doc_name 相关性: 中 内容: 原文片段...同时在System Prompt里约定引用格式回答中所有核心事实必须在末尾使用「来源文档名」标注。检索片段中出现互相矛盾的信息时优先采信相关性标注为“高”的片段并在回答中注明“不同来源存在差异”。如果检索结果为空或全部与问题无关必须明确告知“知识库中未找到相关信息”禁止用模型自身知识编造。这里有一条容易被忽视的安全边界Agent在回答私有知识库相关问题时要控制模型不要混入参数记忆中的通用知识。用一个较强的指令约束“本系统的知识库是唯一事实来源回答知识库相关问题时不引用你训练数据中存在的知识”。实测下来这个指令能明显降低“把模型幻觉当作知识库内容”的错误。5. 工程落地FastAPI服务层、状态持久化与任务监控5.1 FastAPI接入LangGraph同步接口到流式响应的改造到工程层面第一步是把LangGraph图封装到FastAPI里。LangGraph支持异步图执行只要你的节点函数定义为async def整个图可以在事件循环中运行不会阻塞FastAPI处理其他请求。这里给出一个最小可运行框架的伪代码from fastapi import FastAPI from fastapi.responses import StreamingResponse from langgraph.graph import StateGraph, END app FastAPI() def build_agent_graph(): g StateGraph(AgentState) g.add_node(plan, plan_node) g.add_node(tool, tool_node) g.add_node(reflect, reflect_node) g.add_node(respond, respond_node) g.add_edge(plan, tool) g.add_edge(tool, reflect) g.add_conditional_edges(reflect, should_continue, {continue: plan, end: respond}) g.add_edge(respond, END) return g.compile() agent_app build_agent_graph() app.post(/agent/run) async def run_agent(request: AgentRequest): initial_state { user_goal: request.query, messages: [], step_count: 0, max_steps: 10, } return StreamingResponse(stream_agent(initial_state))stream_agent是关键。Agent任务通常耗时数秒甚至数十秒如果让用户干等HTTP响应体验很差。LangGraph的.astream()方法可以逐节点产出执行事件我在FastAPI里用StreamingResponse把事件类型是规划完成、正在调用工具、正在生成回复实时推送给前端配合text/event-stream格式实现SSE。SSE和WebSocket的选择如果你的Agent只需要服务端向客户端单向推送状态用SSE就够了它是HTTP协议的一部分天然适配FastAPI不需要维护WebSocket连接。只有需要用户中途打断甚至与Agent实时交互时才考虑WebSocket。5.2 会话状态与长期记忆的存储方案状态持久化是Agent系统里最容易“先省后补”但后期重构成本最高的部分。我的原则是从第一天就按长期记忆的标准设计。表结构分为两部分。第一部分是会话表保存每个会话的元数据、最近摘要、状态标识。CREATE TABLE agent_sessions ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, title VARCHAR(255), summary TEXT, status VARCHAR(20) DEFAULT active, created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now() ); CREATE TABLE agent_messages ( id BIGSERIAL PRIMARY KEY, session_id UUID REFERENCES agent_sessions(id), role VARCHAR(20) NOT NULL, content TEXT NOT NULL, metadata JSONB DEFAULT {}, created_at TIMESTAMP DEFAULT now() );第二部分是角色长期记忆表保存从多轮任务中提炼出来的用户偏好、关键事实和项目上下文。这一层的写入逻辑由Agent在每次任务结束后自动执行提示词中约定“任务结束时用三句话总结用户的核心需求、已解决事项、未决事项并提取可用于未来任务的关键约束写入长期记忆”。长期记忆的读取优先级是明确调用 最近更新 高置信度。不要一股脑把长期记忆全塞进上下文只读取与当前任务相关的三元组。5.3 单机生产环境的高可用部署要点Agent系统在单机部署时也能做到相对可靠的工程标准关键是关注四个细节任务队列化。FastAPI接口直接同步执行Agent任务在高并发下会把工作线程占满。我的做法是引入一个任务队列Celery或RQ都可以FastAPI收到请求后立刻返回task_id后台Worker执行Agent图前端通过轮询或SSE感知任务状态。超时与重试机制。调用大模型API要设置比预期更长的超时比如生成长报告时给到120秒同时实现指数退避重试。但如果Agent已经执行到了第8步且状态正常此时大模型API超时我不建议直接重试整个Agent图更合理的是让当前节点单独重试充分利用已执行的中间结果。Embedding和向量检索的缓存。同一个文档的向量不需要每次请求都重新计算在文档切分入库后就把Embedding持久化到pgvector。查询阶段相同的查询词在短时间内可以在Redis里做一层缓存。日志链路追踪。给每次Agent执行分配一个trace_id所有节点、工具调用、Token消耗都带上这个ID写入结构化日志。排障时的第一个动作就是查这个ID看走了哪些节点、每一步花了多久、哪个工具返回异常。5.4 提示词版本管理与线上评估提示词的版本管理在Agent项目里的重要性不亚于代码版本管理。我见过太多团队用“最终版V8”实际是第12版这样的命名方式改坏了想回滚都找不到版本。我的做法是把提示词作为配置文件和代码一起进Git仓库每个版本的System Prompt、各节点Prompt模板、工具描述定义都放在独立的目录结构里。发版流程是先离线评估再金丝雀流量。离线评估集准备50到100条典型Agent任务每版提示词上线前跑一遍统计工具调用成功率、任务完成率、回答质量分这三个指标和上一个版本对比后再决定是否全量。金丝雀阶段只把5%的流量切到新提示词并对比业务指标。这在大模型时代尤其重要——模型的输出有随机性没有对照组就靠感觉上线大概率会把用户体验当成试验品。6. 实测中的坑与调优幻觉、循环、延迟这三个老大难6.1 幻觉的重灾区函数调入上下文导致的事实锚定失效Agent系统里幻觉出现的场景和普通聊天应用不同。普通聊天主要靠Prompt约束“不知道就说不知道”Agent系统里模型会阅读工具返回的内容但有时它会“读”到相似但不相关的内容把它当成答案的依据。我踩过最典型的坑竞品分析任务中Agent读取了一篇“公司发布了新一代边缘AI芯片”的文章然后又读到一篇老产品复盘文章。模型没有仔细核对时间戳在报告中写出了“该芯片是今年发布”的结论。排查后发现问题出在检索工具返回的片段里没有时间标签模型把不同时间的消息压成了一块。修复方案分两层。第一层在检索工具返回的结构化字段中强制包含发布时间、信息来源、是否原文片段这三个元数据字段注入上下文时用固定标记包围第二层在生成提示词中提到“报告中的所有时间信息必须以原始来源中的时间为准禁止推断相对时间。”还有一类幻觉源于工具描述本身的误导。如果工具描述里说“该工具返回公司最新动态”但工具实际返回的是按更新时间排序的近期文档并不保证是最新产品动态模型就会把最近更新当成最新发布。工具描述必须精确表达真实能力否则模型的错误判断会一路传导到最终输出。6.2 循环与死锁Agent在多步任务中的“原地踏步”Agent进入循环是编排层面的高频故障。表现是模型觉得自己信息不足反复调用同一个工具每次工具返回的结果都差不多但它就是不收敛。LangGraph提供的最大步数限制只是兜底不能根本解决问题。我在实践中的第一道防线是“步骤去重”在State里维护一个visited_queries列表每次工具调用前的查询词都会被规范化后做比较如果Agent准备执行一个已经查过的查询反射节点会提醒它“该查询已执行过且未产生新信息请尝试换一个角度或直接基于现有信息回答”。第二道防线是“信息增量判断”。提示词中约定如果当前查询结果与已有信息的增量低于阈值则停止该方向检索转入总结或换方向。这条约束用自然语言表达给模型比纯代码判断灵活得多。第三道防线是“主动收敛指令”当步骤数达到上限的80%时提示词自动注入“你还有N步可用请优先完成核心问题的回答次要信息可以在最终答案中标注为‘待进一步核实’”。这能让Agent在真实世界中学会抓大放小。6.3 延迟优化一刀一刀切出来的响应时间Agent系统最影响体感的是延迟。一个完整的多步任务如果耗时在20秒以上用户多半觉得“卡死了”。延迟的优化不是孤立的我梳理过一张延迟分布表和对应的优化方案实测下来效果很明显LLM首Token延迟占总延迟40%到60%。优化方式是使用支持流式的模型接口同时把长输出节点的模型切换为推理速度更快的版本。工具执行延迟占总延迟20%到30%。优化方式是尽量用异步并发调用多个独立工具比如需要同时搜索三家公司时不要串行用asyncio.gather并行执行。检索延迟占总延迟5%到15%。优化方式是pgvector的HNSW索引参数调优ef_search调高提升召回但增加延迟调低则相反以及在检索结果稳定时加Redis缓存。上下文组装延迟占总延迟5%左右。优化方式是控制注入的上下文长度避免每轮把全部历史记录和检索结果一起塞给模型做一次基于相关性的裁剪。优化后的整体任务时间保守估计能压缩40%以上。优化时别一上来就换小模型——先用Profile定位瓶颈在哪一段再对症下药。6.4 Embedding一致性的隐蔽问题与金丝雀评估集写到最后分享一个最隐蔽但后果严重的坑Embedding模型的一致性。项目初期用某个Embedding模型把知识库向量化并存入pgvector后来为了降本换了一个更小的Embedding模型。看起来只是维度从1536变成了768改一下向量列就行但实际上新旧Embedding位于不同的语义空间查询向量和库内向量根本无法有效匹配检索质量瞬间崩塌。解决方法是知识库的全部向量化必须固定一个Embedding模型版本模型换版时必须重新全量向量化。这个约束要直接写进工程代码的版本管理配置里不只是靠团队沟通。金丝雀评估集为我避免了很多次“上线即翻车”。每个Agent项目我都会从真实历史对话中抽100条任务作为回归集定义5到8条评估维度每次改动——无论是提示词、模型版本、检索参数还是工具描述——先跑一遍回归集对比关键指标。这个过程虽然枯燥但它是Agent系统稳定迭代的唯一底气。最后再分享一个小技巧工具描述本身就是一种提示词。很多人花大量时间雕琢System Prompt却不重视工具描述里的那几句话。实际上模型在决策“用哪个工具”时工具描述是它的第一信息来源。把工具描述当成Agent的“牌面”来写——说清楚这个工具擅长什么、不擅长什么、参数怎么填效果最好一次工具误选的修复成本往往比写十句System Prompt的收益还大。
返回列表