做AI全栈开发这两年,我最大的感受是:这个岗位的核心竞争力不是会调几个大模型接口,而是能不能把模型能力当成一块普通的“基础设施”嵌进工程体系里。很多人上来就研究Prompt、微调、Agent框架,结果项目卡在数据接不上、网关不稳定、线上没法观测这些土问题上。这篇内容不聊概念,就聊我在真实项目里沉淀下来的一套可落地的AI全栈实践路径——从模型接入、RAG数据管线、Agent编排,到前端交互、测试评估和部署迭代,覆盖一个AI应用从零到上线再到持续演进的全过程。适合正在做AI应用开发的工程师,也适合想从传统全栈转向AI方向的开发者做参考。
1. 先把“全栈”的范围划清楚:AI应用的五个层次
“AI全栈”这个词太容易让人误解了。我见过不少人以为AI全栈就是会用LangChain写个Agent、能调OpenAI的API、再会点React就够了。真把一个AI产品丢到生产环境里,你会发现它比传统Web全栈要宽得多。
1.1 从传统Web全栈到AI全栈的变化
传统Web全栈的典型链路是:前端页面 → 后端接口 → 数据库 → 服务器部署。数据是结构化的,逻辑是确定的,你写个接口,传入参数就能返回预期结果。
AI应用多出来的东西,是原来这套体系里完全没有的:
- 模型层:LLM本身是你系统里的一个“不可控组件”,有延迟、有随机性、有token成本,还经常更新版本。
- 向量数据层:RAG需要一套独立于业务数据库之外的向量存储与检索链路。
- 编排层:Agent、多工具调用、记忆管理,这层负责让模型“会用”你的系统和数据。
- 评估层:传统测试断言的是“返回是否等于预期”,AI测试断言的是“返回是否合理、是否满足用户意图”。
- 成本与观测层:每一次对话都在烧钱,每个慢请求都可能让用户流失,你得对模型的每一次输出做到可追溯、可计量、可降级。
1.2 AI全栈的五个核心层次
我习惯把一个AI应用的工程结构拆成五层,每一层都有清晰的边界和核心任务:
| 层次 | 核心任务 | 典型技术选型 |
|---|---|---|
| 交互层 | 流式输出、对话状态、前端降级 | React/Vue + SSE/WebSocket |
| 应用编排层 | Agent逻辑、工具调用、业务流程调度 | LangChain、自研状态机、Spring AI |
| 数据接入层 | RAG索引、向量检索、业务数据接入 | PostgreSQL/pgvector、Milvus、Elasticsearch |
| 模型网关层 | 多模型统一接入、路由、限流、成本控制 | LiteLLM Proxy、自研网关 |
| 模型与推理层 | 基座模型、微调、推理部署 | OpenAI API、开源模型本地部署、vLLM/SGLang |
这里每一层都不是孤立的。最典型的问题是:很多团队在模型网关层省了事,让前端直接调各家模型SDK,前期跑Demo很爽,一上线就出问题——某个模型供应商挂了,整个系统不可用;想换模型,代码里散落着十几个调用点要改;月底账单来了,根本分不清钱花在哪个业务线上。
1.3 团队能力模型与个人定位
如果你是一个人在做AI全栈项目,我建议按“一专多能”来布局:深度掌握应用编排和数据接入这两层,因为这是AI应用的核心业务价值所在;模型网关和推理层知道怎么配、怎么选、怎么排查问题就行;交互层至少要看得懂流式协议,能做基本的联调。
如果是团队协作,这个分层天然就是分工边界:前端工程师负责交互层,后端/平台工程师负责网关和编排,数据工程师负责RAG管线,算法工程师负责模型选型与评估。各层之间以明确的API契约对接,避免“AI应用没法分工”这种伪命题。
2. 模型接入层:统一网关的价值,远不止“省事”
我现在接手一个AI项目,第一件事永远是看模型层是怎么接的。如果看到代码里到处是OpenAI、Claude、国产模型各自的原生SDK调用,我就知道后续的开发体验会有多痛。
2.1 为什么直接调用SDK不够用
直接调用模型SDK在Demo阶段完全没问题,代码还更短。但一旦进入生产,你会面临一系列实际问题:
- 可用性:单一模型供应商的API不稳定是常态,你需要在多家之间做自动故障转移,而不是人工改配置。
- 成本治理:不同模型价格差异很大,业务场景需要按需分配。一个内部知识问答和一个小学生解题应用,用同一个旗舰模型,成本是灾难级的。
- 版本管理:同一家供应商的模型版本也会升级,升级后行为可能变化。你需要有能力把某个业务锁定在特定版本上。
- 可观测性:线上出问题的时候,你需要知道某个请求用了哪个模型、输入输出是什么、耗时多少、token消耗多少。原生SDK不会替你记这些东西。
- 权限与安全:内部系统不能让每个开发者都有模型API的秘钥,需要统一入口做密钥管理和访问控制。
2.2 LiteLLM Proxy的落地实践
在这个环节,我是LiteLLM Proxy的重度用户。它的核心能力是:把OpenAI兼容的API格式作为统一标准,后端对接几十家模型供应商和本地推理服务,你只需要改一行配置就能切换模型。
我在项目中通常这么配网关层:
model_list: - model_name: chat-primary litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: chat-primary litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY - model_name: chat-cheap litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY router_settings: routing_strategy: "latency-based-routing" fallbacks: [{"chat-primary": ["chat-primary-2"]}]这里的关键设计是:业务系统只认识chat-primary和chat-cheap这样的“逻辑模型名”,不认识具体供应商和版本。配置中心一改,业务零改动就能切换模型。出问题的时候,网关自动走fallback到备用模型,用户无感知。
提示:不要一上来就搞自研网关。LiteLLM Proxy这类项目已经解决了协议转换、重试、fallback、限流、预算告警等大量生产问题,自研的成本远高于你的想象。等你真的遇到网关本身的性能瓶颈或特殊需求时,再考虑二次开发也不迟。
2.3 网关层的成本控制与可观测实践
网关层还有一个容易忽略的价值:它是全应用唯一的“模型流量关口”,天然适合做成本计量。
我的做法是在网关层做三件事:
- 按业务线和用户维度打标签:每次请求带上
metadata,里面记录业务线、用户ID、功能模块。这样月底对账时,能准确知道“A功能花了多少钱、B用户消耗了多少token”。 - 配预算告警:用LiteLLM Proxy内置的预算策略,给每个业务线设月度token上限,超过阈值自动限制或告警。
- 请求日志落库:把每条请求的输入输出、模型、耗时、token数、错误信息写入日志存储。这是后面做评测、排查问题、优化Prompt的数据基础。
这套东西建好以后,模型层对业务而言就成了一个稳定性、成本都可控的能力出口。你和算法团队、业务方沟通的时候,拿出来的不再是“感觉”,而是精确到每次请求的数据。
3. RAG与数据管线:让模型“看得见”业务数据的核心路径
如果说网关是AI应用的血管,那RAG数据管线就是AI应用的大脑供血系统。做过RAG的人都知道,网上教程里的“三步走”看起来很简单——导入文档、切分、调API查询。但真实落地时,问题一个接一个:PDF排版乱、切分把语义切碎了、向量检索召回了不相干的内容、模型拿着错误资料一本正经地胡说八道。
3.1 索引链路设计:不是所有数据都适合先切再嵌
RAG管线的基础是索引。传统做法是:读文件 → 按固定大小切块 → 调Embedding接口 → 存入向量库。但根据我的经验,不同来源的数据应该走不同的处理方式:
| 数据来源 | 推荐处理方式 | 原因 |
|---|---|---|
| PDF/Word/PPT中的正文 | 先做版面解析,提取标题层级,再按章节切分 | 保留文档结构信息,检索时能按章节召回 |
| HTML/Web页面 | 提取正文内容,去掉导航、广告、版权信息 | 避免噪声影响向量语义 |
| 表格数据 | 转成Markdown表格或键值对描述,不要直接切块 | 表格被切碎后,上下文完全丢失 |
| 数据库中的业务数据 | 直接走结构化查询,必要时转成自然语言描述再嵌入 | 结构化数据用SQL更精准,别硬套RAG |
在切分策略上,我常用的是“层级切分”:先按Markdown标题或文档结构切出大的章节块,每块再按内容长度和语义边界切成适合Embedding的片段。每个切出来的块要保留两个关键元数据:父块ID(方便回溯上下文)和来源信息(引用溯源用)。
以常见的文档问答为例,我的切分大致是这样的:
from markdown_splitter import MarkdownHeaderSplitter, RecursiveCharacterSplitter header_splitter = MarkdownHeaderSplitter( headers_to_split_on=[ ("#", "H1"), ("##", "H2"), ("###", "H3"), ] ) chunks_with_meta = header_splitter.split_text(markdown_doc) # 对每个H1/H2/H3下的内容,再按1024字符窗口、128字符重叠做二次切分 final_chunks = [] for section in chunks_with_meta: recursive_splitter = RecursiveCharacterSplitter( chunk_size=1024, chunk_overlap=128 ) sub_chunks = recursive_splitter.split_text(section.content) for i, sub in enumerate(sub_chunks): final_chunks.append({ "content": sub, "metadata": { **section.metadata, "sub_chunk_index": i, } })这样做的好处是:既能拿到语义聚焦的小块用于向量检索,又能在命中后通过元数据回溯到大章节,把完整的上下文交给大模型。
3.2 召回策略:向量检索不是万能的
很多RAG项目召回质量差,问题不在Embedding模型,而在召回策略太单一——只有向量相似度,没有词法匹配,也没有结果重排。
我的建议是采用“多路召回 + 重排”的策略:
- 向量召回:用Embedding模型把查询转成向量,在向量库里召回Top 50。这里要选择合适的Embedding模型,我建议用专门做中文优化的模型,效果比直接用英文模型好很多。
- 关键词召回:同时对查询做分词,用BM25在全文索引中召回Top 20。这种算法的优势是精确匹配专有名词、型号、人名、编号,这些往往是向量模型的弱项。
- 融合与重排:把两路结果合并去重,用交叉编码器(cross-encoder)做精排,取Top 5交给大模型生成回答。重排这一步效果提升非常明显,优先保留段落级的内容而不要对长文本直接做向量召回。
“混合检索”的典型实现,在Elasticsearch里加一个向量字段,用BM25和向量评分做倒数排序融合(RRF),代码量不大,但对召回质量的提升是立竿见影的。
3.3 上下文组装与引用溯源
召回解决了“找什么”的问题,接下来还要解决“怎么给”的问题。直接把Top 5的碎片拼起来塞给模型,会出现几个问题:
- 召回结果之间互相矛盾;
- 每一块单独看有信息,但合在一起语义不连贯;
- 模型回答后用户不知道信息来自哪里,可信度低。
我的上下文组装原则是:给模型一个带有编号的引用清单,每个引用带上来源标题、章节路径和原文关键句。同时要求模型在回答时用[1]、[2]这样的标记来标注信息来源。
Prompt里的约束大致是这样:
你是一位基于资料回答问题的助手。 回答时只使用“参考资料”中提供的信息,如果资料不足,明确回答“资料中没有相关信息”。 参考资料: [1] 来源:产品手册.pdf > 3.2 安装步骤 原文摘要:... [2] 来源:常见问题.md > Q15 设备无法启动 原文摘要:... 请回答用户问题,并在回答句末使用[1][2]标注引用来源。这样处理后,用户可以对回答进行溯源验证,模型的幻觉比例也会显著下降。这不仅是技术优化,也是产品可信度的关键。
4. Agent编排:从“对话框”到“能干活的系统”工程跃迁
AI Agent是近两年最热的方向,也是最容易被高估的部分。我的看法是:Agent不是比谁调用了更多工具,而是比谁的编排逻辑更稳、更可控。真正能上生产的Agent应用,靠的不只是模型的推理能力,更是工程系统对不确定性的约束能力。
4.1 工具调用的协议设计
一个Agent要干活,就必须调用外部工具:查数据库、调API、发邮件、写工单。这里第一个工程决策就是:工具调用协议怎么定。
最成熟的方案是走Function Calling。OpenAI、Claude、国产模型都支持类似的JSON Schema声明方式。你需要做的是把“工具”抽象成统一的执行单元:
工具名:search_business_orders 描述:按用户ID或订单号查询业务订单详情 参数: user_id: string, 可选 order_id: string, 可选 date_range: object, 可选 返回:订单列表JSON在代码层面,我习惯给每个工具加一个统一的鉴权层和审计日志,明确“谁能调用”“调用参数是什么”“返回了什么”“这步消耗了多少token”。AI Agent最容易出问题的不是“不会调”,而是“乱调”——模型在没有足够依据时擅自执行了高权限操作。
4.2 记忆、规划与多步执行:让人工干预成为必要环节
Agent的复杂之处在于它需要多步决策。一次任务可能涉及“查询需求 → 拆解子任务 → 调用多个工具 → 汇总结果”。这个过程中最大的坑是:让模型自由发挥,流程很容易失控。
我的工程约束策略:
- 用有限状态机约束Agent流程:设定明确状态,比如
waiting_for_user_input、collecting_data、waiting_for_approval、executing、completed。模型只能触发状态转移,不能随意跳到未定义的操作。 - 每步工具调用都要有“前置条件校验”:如果Agent要查询某个用户的订单,必须先确认用户身份已经鉴权,否则调用被系统拒绝。
- 关键操作设置人工审批环节:涉及发送消息、删除数据、支付、对外承诺等场景,Agent只生成操作草稿,必须由人工确认后再执行。
以“客户投诉自动处理”的Agent为例,流程是这样的:
收到用户投诉 -> Agent将投诉分类并抽取关键信息 -> 查询订单历史与售后记录 -> 生成处理建议 -> 进入人工审批 -> 审批通过后自动执行退款/补发/工单创建这个流程里,模型负责“理解和建议”,规则负责“约束和兜底”,人负责“关键决策”。这是我认为目前最可靠的生产级Agent形态。
4.3 Agent的失败恢复与日志
Agent跑多步骤,必然会有中间失败:某个工具超时、某个数据格式不对、模型连续几次都给出无效的调用参数。不处理这些异常,用户看到的就是“对话卡死了”。
我在项目中建了一套Agent任务追踪数据结构:
{ "task_id": "agent_task_20250101_001", "status": "failed", "current_step": "tool_call", "attempts": 3, "error": "tool_search_business_orders timeout after 5s", "conversation_state": "..." }每次工具调用都记录到任务日志里,失败时支持三种恢复策略:
- 自动重试(对幂等且安全的重试1-2次);
- 换个更简单的工具模型重跑当前步骤,而不是整个任务重启;
- 无法自动恢复时,把当前步骤和可选项打包给用户,让用户选择下一步。
提示:Agent项目上线前,一定要用历史对话数据做一次“旅程回放”。把过去一个月真实用户的提问喂给Agent,看它能不能在每个任务里走到结束状态。别只在几个手工构造的例子上自嗨。
5. AI原生前端与交互层:流式、状态与降级方案
很多AI应用的前端看起来很简单——一个聊天框,一个流式输出区域。但真做起来,交互层要处理的问题非常琐碎,而且直接影响用户对系统“聪明不聪明”的感知。
5.1 流式输出:体验的核心
AI对话应用最基础的交互就是流式输出。这里我强烈建议用SSE而不是WebSocket,原因很简单:SSE是单向Channel,天然适配“服务端生成token推给客户端”的场景,断线重连机制也更成熟。
前端拿到流式数据的典型处理方式,是把返回的增量内容持续追加到同一个消息块里:
const eventSource = new EventSource("/api/chat/stream?conversation_id=123"); let currentMessage = { role: "assistant", content: "" }; eventSource.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === "token") { currentMessage.content += data.content; updateMessageDOM(currentMessage); } else if (data.type === "done") { eventSource.close(); setMessageStatus("completed"); } };流式渲染时有一个容易被忽略的性能点:不要每拿到一个token就做一次完整DOM更新。正确做法是维护一个缓冲,用requestAnimationFrame节流刷屏,否则高并发输出时页面会卡顿。长对话场景下,超过一定长度还要做虚拟滚动或折叠老消息,否则内存会撑不住。
5.2 对话状态管理
AI应用的状态管理比传统表单复杂得多。我总结出最少需要管理这些状态:
- 当前对话Id与历史消息数组;
- 每条消息的渲染状态:
pending(等待响应)、streaming(流式输出中)、completed、failed; - 推荐问题和快捷操作按钮(用于空状态引导);
- 引用资料的折叠面板状态。
在多轮对话里,前端还要维护“上下文窗口”的概念。用户可能会中途修改问题、切换话题,你要决定哪些历史消息需要传给后端。这个决策建议放在后端做——前端只回传必要的消息ID,由后端根据token预算和相关性动态选择上下文窗口。前端如果一股脑把全部历史都发过去,Agent的上下文很容易被无关信息污染。
5.3 交互降级与错误反馈
模型接口不稳定是AI应用的家常便饭。前端必须有明确降级策略,否则用户看到一个大红报错框,体验直接归零。
我在前端做的几层降级:
- 流式中断:自动重拉一次,如果仍然失败,显示“当前服务繁忙”,同时建议用户稍后再试。
- 超时兜底:如果请求超过设定阈值还没有返回,先展示“内容生成中”,并给用户一个“停掉生成”的按钮。
- 无结果处理:模型说“不知道”的时候,不能只给一句话,还要推荐相关功能入口或人工客服,把对话引导到可解决的路径上。
这些细节看起来不起眼,但它们决定了用户对你的AI产品是“智能”还是“人工智障”的印象。
6. 测试、评估与可观测性:AI应用的质量“三件套”
传统软件测试的核心是“确定性断言”,AI应用没有这种确定性。同一个Prompt,模型两次输出可能不同;同一个问题,换了模型版本结果天差地别。但这不代表AI应用没法做质量保障,只是质量体系要换一套思路。
6.1 回归测试:先保住底线质量
我做的第一层回归测试是“断言式”的,针对的是那些边界清晰的场景:
- 输入为空、超长输入、非法参数,系统必须给出正确异常响应,而不是崩溃;
- 输出必须是合法格式(如JSON),不能截断、不能缺字段;
- 涉及敏感词和越权访问的请求,必须被拦截;
- 工具调用的参数必须通过JSON Schema校验,非法参数不允许执行。
这些规则不依赖模型能力,保证的是系统的“下限”。这部分测试用传统测试框架就能写,跑在CI里自动化执行。
第二层回归是“语义评估”。做法是:准备一个覆盖核心场景的黄金测试集,每个case包含用户输入、期望行为、检验标准。每次发版前,让模型跑一遍测试集,统计通过率。比如一个客服类Agent,测试集里要有“查订单状态”“改地址”“退款政策咨询”“无理投诉”等典型场景。
6.2 用LLM评估LLM:引入裁判模型
针对开放输出的质量评估,现在比较成熟的方式是“LLM-as-a-Judge”。用一套评估Prompt,让另一个模型(通常是更强的主模型或独立的评估模型)对回答从多个维度打分:
| 评估维度 | 说明 |
|---|---|
| 正确性 | 回答与标准语料/事实是否一致 |
| 完整性 | 是否覆盖用户问题的所有关键点 |
| 可读性 | 表达是否清晰、有条理、不过度冗长 |
| 引用准确性 | 标注的引用是否真的支持对应的结论 |
需要注意,评估模型也会有偏好偏见。我建议每个维度出分数的时候同时要求它输出判断理由,方便人工查看。关键场景的评测结果每周人工抽检一次,确保自动评测和自己的判断方向是一致的。
6.3 可观测性:把每次模型交互变成可审计数据
AI应用出问题,最大的麻烦是“说不清哪里错了”。没有观测数据,用户投诉一个回答不对,你完全无法定位是Prompt的问题、召回的问题还是模型本身的问题。
我的埋点数据模型长这样:
request_id, conversation_id, user_id model_name, model_version prompt_messages(完整的实际发送内容) response_text tool_calls(调用了哪些工具,参数和返回) latency_ms, prompt_tokens, completion_tokens rag_sources(命中了哪些引用) created_at这里关键是prompt_messages和rag_sources要完整记录。前者能让你复现“当时模型到底看到了什么”,后者能让你判断“是不是召回的资料不对”。没有这两样,你在线上排查AI问题就是盲人摸象。
可观测数据量会很大,建议按天分表,长期数据冷存到对象存储。日志平台的选型上,单机和中小规模用企业微信告警加Elasticsearch就够,大规模再上专门的Trace系统。
7. 部署与迭代:从“能跑”到“能持续跑”的工程化差距
AI应用的部署和传统后端不太一样:除了业务代码,你还得考虑模型推理资源、Embedding服务、向量库、以及模型版本的更新策略。这里最容易出问题的是“本地跑通了,上生产就挂”。
7.1 模型部署与推理优化选项
如果业务用的是云端模型API,部署相对简单,主要做网关和配置管理。如果涉及私有化部署开源模型,就要认真评估推理框架和硬件资源。
开源模型推理这块,我常用的方案是vLLM或SGLang,利用PagedAttention和Continuous Batching提升并发吞吐。部署时关注几个关键配置:
# 示例:用vLLM部署Qwen2.5-72B-Instruct python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-72B-Instruct \ --served-model-name my-qwen-72b \ --tensor-parallel-size 4 \ --max-model-len 32768 \ --gpu-memory-utilization 0.9关键参数含义:
tensor-parallel-size:多卡并行度,需要根据GPU显存和模型大小计算。比如72B模型用BF16权重大约需要144G显存,4张A100 80G或8张4090 24G都能跑,但前者更稳。max-model-len:最大序列长度。设得太短,长对话直接被截断;设得太长,显存占用和显存碎片会激增。gpu-memory-utilization:给KV Cache留多少显存。调得太满,并发上来容易OOM;一般0.85到0.9比较平衡。
7.2 CI/CD与模型版本管理
AI项目的CI/CD要比传统项目多考虑一个环节:模型和Prompt的版本。代码回滚容易,模型回滚就没那么快了。
我在CI流水线里配置了这么几个阶段:
lint + unit test:代码静态检查和工具函数测试;golden-set evaluation:跑语义评估测试集,对比基线模型分数,低于阈值直接fail;build + push:构建镜像推送到仓库;deploy to staging:部署到预发环境,用真实数据切片做冒烟测试;deploy to prod:金丝雀发布,先切5%流量观察错误率和延迟,稳定后全量。
Prompt的改动不要直接改代码里的字符串。我习惯把Prompt也纳入配置中心管理,每个Prompt有版本号和生效时间,线上出问题可以秒级回滚到上一个Prompt版本,不必重新发布服务。
7.3 上线后的数据闭环
AI应用上线不是终点,而是数据积累的起点。用户真实的对话日志,是你优化Prompt、召回质量、产品体验的最宝贵资产。
我建议每两周做一次“bad case评审会”,从线上日志里抽取出那些用户不满意的回答,包括“用户点了不喜欢反馈”“用户重复问同一个问题”“Agent中途放弃”等信号。每一条bad case都要往下拆一层:到底是知识库没有资料、检索没召回、还是模型指令遵循不强?
这个环节没法自动化完全替代,但它恰恰是AI全栈工程师区别于“只会调API的开发者”的分水岭。你能从数据里定位问题、改对地方,系统就会持续变好。
8. 反模式清单:我在项目中反复踩过的坑
最后整理一份反模式清单,都是我亲自踩过、也看到身边团队反复踩的教训。每一条都是一个“我当初要是早知道就好了”的坑。
8.1 上来就微调模型,而不是先做RAG
很多团队业务知识问答效果不好,第一反应是微调模型。这个方向大多数时候是错的。微调适合改变模型的行为风格和输出格式,不适合灌输事实知识。事实类知识应该走RAG,靠检索注入上下文。
微调一个72B模型的一次实验成本,足够你把RAG管线反复调优好几轮了。先上RAG,再根据RAG覆盖不了的长尾问题考虑微调,性价比完全不一样。
8.2 忽视延迟控制,导致产品体验崩塌
大模型调用天然有延迟,但很多开发者在设计流程时不加控制。一个用户问题走了“意图识别 → 数据查询 → 二次生成 → 摘要总结”四步模型调用,单步2秒,总耗时8秒,用户早走了。
延迟预算要拆解到每一步:哪些步骤可以并行,哪些步骤可以用小模型快速处理,哪些步骤可以用缓存命中。现在的模型网关层都支持多模型路由,快慢模型穿插使用是基本操作。
8.3 对用户输入没有基本防护
AI应用只要对外暴露,就会遇到各种输入,有些是恶意的、有些是无意识的破坏。不能因为“模型很聪明”就放松输入校验。
防护至少要做到:
- 在网关和业务层做基于规则的输入长度、格式、敏感词过滤;
- 对工具调用的参数做严格JSON Schema校验;
- Agent执行高权限操作前,必须有独立的审批确认环节;
- 对外提供的接口要有独立于模型层的鉴权体系。
8.4 日志里没有Prompt和召回快照
这是排查效率的天花板。没有完整的Prompt记录和RAG引用快照,线上问题基本没法定位。你只看得到一个“回答错误”,不知道是哪个环节错了。这个前面已经强调过,这里再放一次,因为它的重要性怎么强调都不为过。
结个尾,分享一个我最近的体会
这两年我越来越觉得,AI全栈开发真正的难点不在AI算法本身,而在“如何用工程手段把一个高不确定性系统管住”。模型能力会持续变强,今天的最佳实践可能半年后就过时了,但分层架构、统一网关、RAG管线、评估体系、可观测数据闭环这五根柱子是不会塌的。它保证的是:不管底层换哪个模型、Agent框架怎么变,你的系统都能快速适配、稳定运行。
最后给一个可以立刻落地的小建议:别急着现在就把系统做得多复杂。先用统一网关把你的模型调用收敛到一个入口,再把请求日志和Prompt快照落库。这两步做完,你后续做评估、做优化、做任何AI改造,都会发现手上终于有了“数据”而不是“感觉”。项目一步步长起来,这大概就是AI全栈开发最实在的乐趣所在。