ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agentic AI生产环境落地指南:从提示工程到LangGraph实践

Agentic AI生产环境落地指南:从提示工程到LangGraph实践 我直接讲标题里的“Agentic AI”放在生产环境里其实没有那么多玄学。它就是让大模型不再只是回答一句话的聊天机器人而是能基于任务目标主动拆解步骤、检索资料、调用工具、执行动作、检查结果最后给业务方交付一个完整结果。我参与的那套系统业务面是给售前团队做智能投标助手输入一份招标文件系统自动解析需求、检索企业资料库、调用报价模板、生成初稿。后端全部用 PythonFastAPI 接管 APILangChain 做组件整合LangGraph 负责状态编排RAG 拿 pgvector 存知识库整个提示词体系由一位专职的“提示工程架构师”负责。这个角色不只是写写 prompt你得同时懂模型能力边界、业务数据形态、评测方法和工程约束。这篇文章就是把这套流程拆开来讲。适合刚接到类似系统搭建任务的朋友参考也适合后端工程师理解提示工程为什么是 Agentic 系统的智能上限和用户体验的重要决定因素。1. Agentic AI 项目全景先想清楚再动工1.1 提示工程架构师到底负责什么很多人以为提示工程架构师就是“把 prompt 写长一点”实际完全不是。在一个 Agentic 系统里提示词是运行时最关键的输入之一它和业务数据、工具定义、模型参数共同决定了行为的稳定性。我在这套项目里主要负责四件事定义智能体的“人格”和边界告诉模型什么该做什么不该做为每个工具写清晰、无歧义、可被模型理解的描述设计“规划-执行-反思”的提示循环并控制每轮之间的信息流搭建评测集量化每个提示改动带来的收益或回退这套分工在一个人单干的时候看似多余团队一上规模就非常重要。提示词不是一次性写出来的它是和 Agent 行为一起迭代出来的。没有评测集的提示词改动基本等于在裸奔。我见过太多团队花一周调 Prompt上线一测发现某类问题全变严重就是因为没有把改动量化。1.2 整体架构与技术栈选型技术选型上有两条主流路线一条是直接上托管 Agent 平台比如阿里云百炼这类产品把模型编排、知识库、工具调用都交给平台减少了自建工作量另一条就是自己拿开源框架搭可定制性更强。我们最终选了自建原因是业务方对数据保密和私有化部署有硬性要求同时需要对检索链路做深度调优。最终落地的技术栈FastAPIAPI 网关和服务入口支持异步适合同时承接多个对话会话LangChain提供统一的模型调用、PromptTemplate、检索器和工具封装LangGraph把 Agent 流程建模成有状态图支持条件分支、循环、人工介入RAG把企业知识库变成模型可查询的动态上下文pgvector向量检索走 PostgreSQL 扩展便于和业务数据一起做事务管理关于“为什么不用更重的向量数据库而用 pgvector”我后面会专门解释。先记住一个原则技术栈越贴近业务已有的数据库运维负担越小Agent 系统落地成功率越高。任何额外部署的中间件都会变成你和交付日期之间的敌人。2. 核心模块拆解RAG、Agent 编排与提示词设计2.1 用 pgvector 做知识库的基石RAG 的核心逻辑是用户问题进来先从知识库里检索出最相关的片段再把这些片段塞进上下文让模型基于事实回答。因此知识库的质量直接决定生成质量。我用 pgvector 主要看中三点和 PostgreSQL 同库企业不需要额外维护一套向量数据库支持 HNSW 索引在千万级向量下仍然有可接受的召回速度可以直接用 SQL 做过滤比如按产品线、时间、部门过滤后再检索建索引的时候我把 embedding 维度设为 1024使用的模型是 bge-large-zhHNSW 的 m 参数设成 16ef_search 默认 40。如果你的数据量在百万级以下这些参数起手不用调太激进先把链路跑通更重要。分块策略是 RAG 里最容易翻车的环节。我用的方案是 LangChain 的 RecursiveCharacterTextSplitterchunk_size 设为 500chunk_overlap 设为 80。这个参数不是拍脑袋而是根据测试集的平均提问长度和文档结构定的。比如发票条款文档经常出现“但下列情形除外”之类的前后依赖overlap 太小就会把语义切断。提示如果你处理的文档有很强的结构比如标书、合同、技术规范可以先用标题做层级拆分再把正文按照 500 字左右的窗口切块。把“章节标题”存进 metadata检索返回时带上标题信息生成质量会明显提升。2.2 LangGraph 如何把多步 Agent 变成可控状态机LangGraph 是我在整套架构里最欣赏的一个组件。它的核心思想是把 Agent 的思考过程建模成一张图节点是操作边是跳转条件。相比 LangChain 早期的 AgentExecutorLangGraph 给了你四个重要能力显式的循环控制Agent 调用工具最多重试多少次可以硬编码条件分支检索没有结果时可以走“让用户补充问题”的通道状态持久化多轮对话的状态可以存到后端不会每轮都从头算人工介入敏感操作前设置中断由人确认后再继续执行我用 LangGraph 搭了一个四节点的流程retrieve检索→ plan拆解任务→ tool_call决定是否调用工具→ generate生成最终回复。这四个节点通过条件判断连接形成一个可观测、可断点调试的 Agent。这样设计最大的好处是问题定位非常快。用户反馈某次回答不对我只要看是哪个节点的输出异常就能判断是检索问题、规划问题还是生成问题而不是像以前那样把整个 Chain 从头排查一遍。2.3 提示词系统设计角色、工具描述与自我反思提示词绝不是“一段 system prompt 走天下”。我把提示词拆成三层第一层是系统级提示词负责定义 Agent 的整体行为边界。比如我们明确要求“你必须基于检索到的内容回答如果检索为空请直接说明未找到禁止编造”。这一层的变化会同时影响所有对话所以必须走版本管理和灰度发布。第二层是工具描述。LangGraph 调用外部工具时模型是靠工具描述来判断何时调用的。我吃过一个亏最开始把报价模板工具描述写成“获取报价信息”结果模型在任何提问下都尝试调用它。改成“当用户明确需要生成报价单且已有完整项目参数时调用该工具参数不足时请先向用户询问”之后误调用率下降非常明显。第三层是反思提示。在生成最终回答之前我加了一个 self-check 节点让模型检查自己的回答是否覆盖了用户的全部子问题、是否与检索内容冲突。这个环节会增加一轮模型调用但能明显压掉幻觉输出。特别是面对多问题场景用户一次问了三件事模型经常只答最后一件反思节点会强制模型逐项核对。3. 从 0 到 1 的工程落地实战3.1 环境准备与项目骨架我用的 Python 版本是 3.11依赖用 uv 管理。这里直接给一份最小依赖清单fastapi0.115.x uvicorn[standard] langchain0.3 langchain-openai langgraph0.2 pgvector psycopg[binary] sentence-transformersembedding 模型和 LLM 我建议分开配置。检索用的 embedding 模型我们私有化部署为本地服务生成用的 LLM 走公司统一的模型网关。这样在生成模型迭代时不用重新做一遍知识库向量。项目骨架建议这样组织agent_app/ ├── app/ │ ├── main.py # FastAPI 入口 │ ├── config.py # 模型、数据库配置 │ ├── graph.py # LangGraph 流程构建 │ ├── nodes.py # 各节点实现 │ ├── prompts.py # 所有提示词 │ ├── rag/ │ │ ├── indexer.py # 知识库索引脚本 │ │ ├── retriever.py # 检索器 │ │ └── store.py # pgvector 连接 │ └── tools/ │ └── quote_tool.py # 示例工具 ├── tests/ └── data/把 prompts.py 单独拎出来的目的就是让提示工程架构师只在指定文件里改内容不碰业务代码。这个隔离在多人协作中特别关键也方便做提示词版本比对。3.2 知识索引分块、向量化与入库索引脚本解决的问题是给一批文档目录批量切分、向量化、写入 pgvector。我建议先跑一次脚本把全量文档建档之后再有增量文档时走同样的接口。核心代码片段如下from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import PGVector from langchain_community.embeddings import HuggingFaceEmbeddings embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-large-zh-v1.5, ) splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap80, separators[\n\n, \n, 。, , , , ], )分块时有一个常被忽略的问题同一个标题下的表格内容会被切成很多小片段检索回来之后丢失上下文。我的解决办法是保留文档原始元数据比如把“章节标题”和“表格标题”作为 metadata 字段写入向量库在拼提示词时带上这些信息模型就能判断自己正在看哪一部分内容。写入 pgvector 时注意PGVector.from_documents在数据量大的场景里效率不高。我一般是先构建 embedding 列表再批量执行 upsert同时用VARCHAR保存 content 的哈希值避免重复插入。如果你要处理增量更新我建议在表里加一个doc_version字段。每次导入文档时带版本号查询时只筛最高版本这样旧版本数据可以留着做对比分析但不会污染线上检索。3.3 Agent 核心逻辑实现Agent 流程用 LangGraph 实现时第一步先定义状态。我定义一个AgentStatefrom typing import TypedDict, List class AgentState(TypedDict): question: str retrieved_docs: List[str] plan: str tool_result: str answer: str iterations: intiterations字段是防止死循环的关键。我在图里对循环次数做硬限制超过 3 次就强制结束避免模型反复调用同一个工具不收敛。接下来定义节点和边的连接逻辑。这里我给出简化的图结构from langgraph.graph import StateGraph, END def retrieve_node(state: AgentState) - AgentState: docs retriever.retrieve(state[question]) return {**state, retrieved_docs: docs} def plan_node(state: AgentState) - AgentState: # 调用模型生成执行计划 ... def tool_node(state: AgentState) - AgentState: # 根据计划决定是否调用工具 ... def generate_node(state: AgentState) - AgentState: # 生成最终回复 ... graph StateGraph(AgentState) graph.add_node(retrieve, retrieve_node) graph.add_node(plan, plan_node) graph.add_node(tool, tool_node) graph.add_node(generate, generate_node) graph.set_entry_point(retrieve) graph.add_edge(retrieve, plan) graph.add_conditional_edges( plan, route_after_plan, {tool: tool, generate: generate}, ) graph.add_edge(tool, generate) graph.add_edge(generate, END)route_after_plan的判断逻辑是模型输出的 plan 里如果包含工具调用的意图就进入 tool 节点否则直接生成。这个判断不能只靠关键词匹配我建议让模型输出结构化 JSON明确requires_tool字段应用代码再根据这个字段路由。工具节点里还有一个细节工具返回结果必须标准化。不管底层调用的是内部 API、数据库还是其他服务统一转成{ status: ok, data: ..., message: ... }这类结构。模型理解结构化返回的准确率远高于自由文本返回。3.4 API 接口封装与观测FastAPI 层的封装很简单核心是给每个对话分配独立状态from fastapi import FastAPI from pydantic import BaseModel app FastAPI(titleAgentic RAG API) class AskRequest(BaseModel): session_id: str question: str app.post(/ask) async def ask(req: AskRequest): # 从缓存或数据库加载历史状态 result await run_agent(req.session_id, req.question) return {answer: result[answer], trace: result.get(trace)}观测是 Agentic 系统和普通 API 最大的区别。普通 API 只要记录入参出参和耗时Agent 系统还要记录每一轮模型调用、工具调用、检索结果。我们用 LangSmith 做链路追踪上线前还会在日志里打印每个节点的输入输出方便出问题时回放。如果你不想引入额外平台也要至少把 trace_id、node_name、token_usage、retrieved_docs_id 落库。没有 trace 的 Agent 系统出了错会让人完全无从下手。我一个很深刻的体会是Agent 系统排障的时间大部分花在“复现现场”上有了完整 trace复现成本能降一个数量级。4. 常见问题排查与避坑手册4.1 Agent 循环、指令跟随弱、检索质量差我把项目里遇到的高频问题整理成一张速查表现象可能原因处理办法Agent 反复调用同一个工具工具描述不明确或返回格式异常在工具返回中增加状态字段如 success/need_info模型不按 system prompt 执行system prompt 过长把核心约束前移采用重复强调和负面清单检索结果与问题相关度低分块过大或 embedding 模型不合适先做评测集再看 top_k 里是否出现正确片段回答出现幻觉检索片段不足或未约束生成强制要求引用来源文档 id检索为空时拒绝回答上下文超长top_k 过大导致检索内容过多动态压缩片段按相关度截断我印象最深的一个问题工具返回结果是 JSON 字符串但模型在下一步生成时误解了字段含义。后来我在工具返回里加了status: ok和message: ...同时把工具调用说明写进系统提示词问题就没了。这个经验听起来基础但很多团队真正排查时会找半天。还有一次生产环境用户连续问了十个关于“验收标准”的问题系统回答总是丢三落四。查 trace 发现用户问题里没有出现“验收”这个词但语义上就是在问验收条款。后来我在检索前加了一个查询改写节点先把口语化问题改写成适合全文检索的查询词召回率提升了近 20%。4.2 评估方法论没有评测就没有优化提示工程做到后面拼的不是写 Prompt 的语感而是评测能力。我维护了一个包含 300 条问答的评测集覆盖检索多样性、工具调用、多轮对话三类场景。每条评测记录包含标准答案、期望引用的文档、是否允许调用工具。每次改提示词或者检索参数我就把这套评测集跑一遍然后对比通过率。评测打分我使用 LLM-as-judge 的方案让一个固定的评审模型按以下维度打分答案正确性0-5是否引用检索内容0-5是否严格遵守工具调用规则0-5面对知识库缺失时是否诚实拒答0-5只有总分提升明显且单项没有回退我才会把新提示词推到生产。这个方法看起来费时但可以避免“看起来顺眼了上线就翻车”的悲剧。实操心得评估集不是一次性建完就结束的。每次线上出现了新的失败case我会把它补充进评测集。三个月下来300 条里大概有 100 条来自线上真实失败样本这套评测集才真正变成了系统的安全网。5. 真实项目中的体会与扩展建议5.1 架构师视角的三条核心体会第一别一开始就把 Agent 做得太复杂。我们的第一版只有“检索-生成”两个节点先跑通业务流程再把工具调用和反思节点加进去。复杂度是一点点长出来的而不是设计出来的。一上来就规划十节点图调试成本会高到你怀疑人生。第二提示词要像代码一样管理。我在项目里给每个提示词都加了版本和生效时间生产环境全部走配置中心下发不直接改代码。这样做的好处是提示改坏了能快速回滚。有一次我改了系统级提示词的措辞上线后所有回答都变得过于啰嗦回滚配置后十分钟就恢复了。第三领域知识的梳理比模型选型更影响效果。同样一个开源模型在知识库整理得干净、分块合理、提示词约束明确的系统里效果可能超过用更大型号的裸模型。数据工程在 Agentic 系统里的地位比很多人想象中要高。5.2 后续还能往哪个方向扩展这套架构可以继续扩展的点很多。我们已经在规划的方向包括把记忆从单轮会话扩展到长期用户画像给工具调用增加权限控制敏感操作必须人工审批做多 Agent 协作让一个规划 Agent 同时调度多个专家 Agent。如果你在预算和时间允许的情况下也可以先用平台型产品做 PoC比如阿里云百炼这类平台它们把很多工程细节托管了适合验证业务流程。等确认业务价值之后再决定要不要迁移到自建链路。这样既控制了试错成本又不会让自己的系统绑定在一家厂商上。最后分享一个我反复提到的经验把提示词改动当成产品改动而不是技术改动。每次改完上线都要观察真实用户行为、失败率和用户反馈。Agentic 系统的上限由模型决定下限则由提示工程、数据质量和工程可观测性共同决定。这句话是我这套项目下来最想提醒后来者的。
返回列表