☰
LLM、RAG与Agent三核驱动:AI应用架构演进与实战指南
2026/10/20 2:37:18 网站建设 项目流程

最近有朋友问我,说现在谈AI应用开发,动不动就是 Agent、RAG、LLM 三个词捆在一起,到底是什么意思?是不是要把三个系统都从零写一遍才算懂?如果是个刚入门的程序员,该怎么理解这套“三核驱动架构”的演进?

这个问题其实问得很好。我自己的理解是:LLM 是大脑,RAG 是外接的知识库和记忆,Agent 是调度中心。三者不是为了拼概念凑在一起的,而是层层递进、互相补齐短板。早期我刚开始做AI应用时,也以为只要把模型接上就完事了,后来踩过不少坑才发现,真正能落地的应用,基本都会走到 Agent + LLM + RAG 这个组合上。这篇文章我把演进过程、核心模块、最小可跑通的方案和常见问题一次讲透,适合那些想从“调API”进阶到“搭应用架构”的朋友。

1. 为什么是“三核驱动”,而不是“模型为王”

很多刚接触这一行的开发者会有个惯性思维:选个聪明的大模型,问题就解决了。实际上模型只解决“下一步该输出什么token”的问题,不解决“信息从哪来”“动作怎么做”“连续任务怎么拆解”这类工程问题。现在的 AI 应用,本质上是一个把模型、知识和行动串起来的系统中枢。

1.1 单独用LLM,就像让实习生直接上产线

我习惯把大语言模型比作名校毕业但没有任何行业经验的实习生:知识量巨大、理解力强、表达流利,但一问到2023年之后的事它就犯难,再问到企业内部某个流程文档,它更是完全没见过。

这里涉及一个核心概念——预训练的知识截止点。模型在训练阶段见过大量公开语料,之后的实时数据和私有数据它根本没接触过。很多刚入行的朋友第一次做知识库问答时,问模型“我们公司的报销流程是什么”,得到的答案是一段很像那么回事的、但完全编造的流程说明,这就是典型的幻觉问题。

另一方,LLM 本身也不擅长多轮、长链条的决策。比如“帮我查一下订单状态,如果延误超过一天就申请退款,再发一封邮件通知用户”,这样一个简单的三步任务,如果只靠一次 Prompt 输出,模型经常会漏步骤或者顺序搞错。它并不是逻辑能力差,而是缺少“分步执行”和“检查中间结果”的机制。

1.2 RAG解决的是一件事:让模型“知道它不知道的”

RAG 全称是检索增强生成,核心思路一句话:在模型回答之前,先去外部资料库查一圈,把相关的片段拿回来,塞进上下文里,再让模型基于这些片段作答。

这就像实习生上岗前,公司没指望他脑子里自带流程,而是塞了一本非常厚的操作手册,让他每次回答前先把对应章节翻出来照着讲。优点是回答有据可依,来源可控,而且不需要重新训练模型就能让模型掌握新知识。

在实际项目中,RAG 约等于把“存储”和“推理”分开:文档切片、向量化、索引、检索、重排,最后把命中的内容作为上下文交给LLM。这套链路每个环节都有坑,比如切块粒度太大导致上下文超长,切太碎导致语义断裂,召回不够精准导致模型只能基于残缺信息硬编。很多“好像回答得不太对”的问题,根子其实不在模型,而在检索质量。

1.3 Agent真正带来的是:从“会说话”到“会干活”

Agent(智能体)的核心不在于“更聪明的对话”,而在于“把任务拆解出来后去执行”。它会把用户的一句话变成行动计划,决定什么时候调用搜索、什么时候查数据库、什么时候写文件,再根据每一步的结果动态调整下一轮行为。

如果说 RAG 是给模型递资料,Agent 就是给模型发了一套工具,还给了它一张任务清单。典型结构包括:任务规划器、工具注册中心、执行器、短期工作记忆。任务规划器负责将“帮我处理退款”拆成“查订单”“判断状态”“执行退款”“通知用户”;工具注册中心维护了一张“有哪些能力可用”的清单;执行器则按照规划逐个调用工具,每一步都拿到真实返回值。

把 Agent、LLM、RAG 三个概念放到一起看,其实就形成了一个闭环:Agent 从上游任务里提炼出决策路径,LLM 负责每个决策点上的语义理解和生成,RAG 在需要知识时把最相关的资料送到模型眼前,三者各司其职,这就是现在主流AI应用的基本运转逻辑。

2. 架构演进四阶段,每一步都是被真实需求逼出来的

技术演进从不靠概念驱动,靠的是实际需求倒逼。我这个理解纯属个人总结,但基本符合当下大家普遍认可的路线:单模型裸奔 → 接RAG → 上Agent → 三核融合。

2.1 第一阶段:语言模型单打独斗

刚开始接触AI应用开发时,大部分人的第一个Demo都是类似的:把用户问题拼到 Prompt 里,请求模型,返回回答。有的模型当时已经很强,但你只要问两类问题它就会露馅:

一类是“今天/上周”这种时效性问题。模型的知识是快照式的,它无法凭空知道最新事件,又为了让回答显得完整,就会自己编一段看起来很权威的内容。这就是幻觉的直接原因。

另一类是私有领域知识。比如把内部规章制度文档做成一个问答系统,模型压根没见过这些文档,你问什么它都只能按常识答,结果在业务上基本不可用。

这个阶段的架构非常简单,但问题也非常明显:没有记忆、没有知识来源、没有工具调用,遇到需要“事后更新”的知识场景,全部失灵。

2.2 第二阶段:加上RAG这个“外挂知识库”

RAG 出现后,模板变成了:用户问题 → 向量检索 → 相关片段 → 拼接Prompt → 模型生成。这一步解决了“信息怎么进模型”的问题。

我当时第一次做知识库问答时,用了某个开源向量数据库,加载了一批产品文档,每个段落切成若干个块,转成向量存储,回答时先把问题转向量再检索top-k,然后拼进Prompt。效果相比裸奔模型是质的飞跃:引用真实文档后,回答不再天马行空。

但新的问题又来了:用户未必只想“问知识”,还需要“办事”。比如客服场景里,用户问“我的退款到底什么时候到账”,这不仅需要查知识文档,还需要真正查库里这张订单的状态,然后给出针对性答复。RAG 本身是不具备调用外部接口能力的,它只能把“你知道什么”做得更好,但没办法触及“你能做什么”。这一步是Agent进入架构的契机。

2.3 第三阶段:Agent让模型长出“手脚”

Agent 让模型从“回答问题”上升到“完成任务”。当时我做过一个内部工单处理助手:用户描述问题后,Agent 判断要不要调用工单查询接口、要不要自动创建工单、要不要把结果写入进度表,每一步通过调用预先注册好的函数实现,而不是靠LLM直接编答案。

这个阶段有个非常明显的感受:模型的推理能力终于被用在“决策链路”上了,而不是只用来“写句子”。但意外情况也很多——当Agent需要回答一个涉及具体业务术语的问题时,因为它缺少领域知识,经常给出错误的拆解方向。比如把“退款状态”误判成“订单类型”这种概念性的偏差。

所以单纯 Agent 能干活,但方向感会跑偏。它需要一个知识源在每步推理时不断校准,RAG 的价值第二次体现出来:给决策提供上下文依据。

2.4 第四阶段:三个核心协同的现代形态

到了现在这个阶段,比较主流的架构就是把两者串起来:用户输入进入 Agent,Agent 的规划模块明确当前步骤需要什么信息,如果发现该步骤包含业务实体或领域术语,就会触发 RAG 去检索相应资料;检索结果连同当前任务状态一起交给 LLM,由 LLM 生成下一步行动;工具执行的真实返回值再返回给 Agent,循环往复,直到任务完成。

我画过一张简化的流程图来表达这个过程:

  1. 接收用户请求,判断是否需要外部知识、是否需要调用工具。
  2. 若需要知识支撑,先查询向量库,召回相关片段并重排。
  3. 将“背景知识 + 历史对话摘要 + 用户目标”组装成当前步骤的Prompt。
  4. 模型输出结构化决策(比如JSON格式),解析出工具名和参数。
  5. 执行工具,拿到真实数据,判断是否还需要补充知识,若需要再回第2步。
  6. 直到满足结束条件,生成最终回复。

这个形态已经不是一个单纯的“问答系统”了,更像一个可以感知资料、执行操作、自我校验的智能服务工作流。

3. 三核各自的内功:模型、检索、编排的核心选型逻辑

理解了架构之后,下一步就是动手选型和落地。很多新手一上来就卡在“选什么模型”“向量库用什么”“Agent框架怎么写”,这里我按三个核心模块分别拆解。

3.1 模型选型:不是越大越好,是越合适越好

LLM 是三核里最被关注的,但也是最好决策的。选型主要看四个点:

  • 上下文窗口:决定了你能塞多少知识片段和工具返回结果。想省事就选长上下文,但成本会随窗口非线性上升,不是越长越好。
  • 工具调用能力:是否支持标准的工具调用格式,能否稳定输出结构化参数,这是Agent能否跑通的关键。
  • 指令遵循与结构化输出:你是否能让模型稳定输出符合你定义的JSON或函数签名,直接决定你的解析层容错复杂度。
  • 成本与响应速度:线上业务场景每秒并发一高,模型推理耗时和价格就是生死线。很多实际场景选择“主力模型+轻量分流”的策略,小模型过滤简单问题,大模型处理复杂推理。

我一般会给团队建议:如果你做企业内部工具,而不是面向全球用户的通用助手,一个中杯尺寸的开源模型部署在私有环境往往比调用商用API更可控;但如果你只是做Demo验证,商用API的快上手优势就很明显。没有唯一正确的答案,只有合适不合适。

3.2 检索链路:切块、向量化和召回重排的细节

RAG 这部分是最容易出现“看似能用,实际一测就崩”的环节。我把它拆成数据准备、入库、检索三个阶段。

数据准备阶段的切块,很多人直接按固定token数切,结果常常把一个完整表格从中间切断,或把一个业务流程的第三步单独隔开,检索时语义碎了一地。我常用的做法是:优先按文档结构切分,比如标题、段落、列表项、表格行。对于一个章节,如果超过阈值再二次切分,并保留上下文描述。这样能最大程度保证每个片段都是自洽的信息单元。

向量化阶段要看嵌入模型质量。不要只看榜单分数,要做一次实际召回测试:准备几个典型用户问题,查几个真正的相似片段,肉眼判断有没有召回真正有用的内容。这块是纯工程手感,多测几次就明白了。

检索阶段最容易被忽视的是重排。向量召回只能保证“语义相似”,但不保证“对当前问题最有用”。很多系统召回top-20,直接把全部塞给模型,上下文被无关内容撑爆,回答质量反而下降。更合理的流程是:先用快而便宜的向量召回和关键词召回拿到候选集,再用一个精排模型对候选进行相关性打分,最后只保留top-3或top-5进Prompt。

3.3 Agent编排:规划、工具注册、记忆管理的实现方式

Agent 的开发框架很多,但核心概念基本一致。我自己的实现习惯是三个部分:

第一部分是任务规划。最粗浅的做法是让模型直接输出 JSON 格式的步骤数组,复杂一点的做法是把一个大任务拆成阶段性子任务,每个任务都带状态。新手最容易犯的错是让Agent一次性生成所有步骤,然后按步骤盲走。真实场景中,后续步骤往往依赖前面工具返回的实际结果,所以更好的方式是“边执行边规划”:每完成一步,把结果总结后重新评估下一步。

第二部分是工具注册。每个工具的元信息要包含工具名、功能描述、参数Schema、返回结构。模型的工具选择能力完全依赖这些描述的清晰程度。描述写得像废话,模型就会选择错误的工具,或者干脆不停问你。这里值得多花时间打磨。

第三部分是记忆管理。短期工作记忆保存当前任务的关键信息,比如订单号、用户ID、中间结果;长期记忆则可以沉淀用户偏好或历史处理模式。很多项目上来就做长期记忆,其实短期工作记忆才是保证任务不跑偏的基石,先扎实做好短时记忆,再谈更高级的记忆机制。

4. 最小三核架构实操:做一个能服务的知识库Agent

理论讲再多,不如跑一个最小可运行的Demo。下面这套是我在本地做验证时常用的方案,简化过,但完整具备“知识+推理+行动”三要素。

4.1 整体项目结构与选型说明

我建议的技术栈组合是:一个具备工具调用能力的开源对话模型,一个轻量级向量数据库,一个支持函数调用的 Agent 编排框架。项目目录结构大概长这样:

mini_agent/ ├── config.yaml ├── data/ │ ├── policies/ # 业务知识文档 │ └── processed_chunks/ # 切分后的片段 ├── src/ │ ├── indexer.py # 文档切分、向量化、入库 │ ├── retriever.py # 召回+重排 │ ├── tools.py # 工具定义 │ ├── agent.py # Agent编排主逻辑 │ └── main.py # 交互入口 ├── requirements.txt └── run.sh

这几个模块的职责单一:indexer 建索引,retriever 提供知识,tools 提供可执行操作,agent 负责决策调度。数据层和逻辑层分离后,后续替换任何一个组件都方便。

选型时有一个我后来才意识到的心得:向量数据库不要选最大最重的,选一个文件型、部署零成本的轻量方案就够了。数据量到了百万级再迁移不迟,前期Quick Proto最重要。

4.2 索引构建:把业务文档转换成可检索的知识底座

我用一个模拟场景来说明:某公司的客服知识库,里面有“退款政策”“发货时效说明”“保价规则”三份文档。我要做个客服助手,能回答这些规则,也能查询模拟订单。

第一步是切块。我按 Markdown 的二级标题分块,每块最大约800字符,超过则按句子再切,但保留所在章节的标题作为上下文前缀。

# src/indexer.py import os from typing import List def split_markdown_by_sections(text: str) -> List[str]: chunks = [] current_section = "通用" current_buffer = [] for line in text.splitlines(): if line.startswith("## "): if current_buffer: chunks.append(current_section + "\n" + "\n".join(current_buffer)) current_section = line.strip("# ") current_buffer = [] else: current_buffer.append(line) if current_buffer: chunks.append(current_section + "\n" + "\n".join(current_buffer)) return chunks

第二步是向量化,直接调某个通用的嵌入模型接口,把上面拿到的chunk变成向量。入库时除了向量,还要保存文本内容、来源文件名、章节标题等元信息,便于之后溯源。

# src/indexer.py 续 def build_index(doc_dir: str, embedder, vector_store) -> None: for fname in os.listdir(doc_dir): text = open(os.path.join(doc_dir, fname), encoding="utf-8").read() for chunk in split_markdown_by_sections(text): vector = embedder.embed_query(chunk) vector_store.add_item( vector=vector, payload={ "text": chunk, "source": fname, } )

这里要注意一个实践细节:嵌入模型的选择要和你真实场景的数据类型对齐。如果文档里有大量中英混排的表格,用纯中文优化的嵌入模型效果往往比通用模型好很多。

4.3 工具定义:让Agent拥有查询与操作能力

接下来定义两个模拟工具:一个是查询订单状态的函数,一个是计算退款金额的函数。为了让Agent能稳定调用,我把每个工具都包装成带Schema的注册结构。

# src/tools.py import json def get_order_status(order_id: str) -> dict: # 模拟真实订单系统 fake_table = { "A1001": {"status": "shipped", "days": 3}, "A1002": {"status": "delivered", "days": 1}, "A1003": {"status": "delayed", "days": 5}, } return fake_table.get(order_id, {"status": "not_found", "days": 0}) def calculate_refund(order_id: str, reason: str) -> dict: status = get_order_status(order_id) if reason == "delay" and status["days"] > 2: refund = 20.0 else: refund = 0.0 return {"order_id": order_id, "refund_amount": refund} TOOL_SCHEMAS = [ { "name": "get_order_status", "description": "根据订单号查询订单当前状态和物流天数", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单号"} }, "required": ["order_id"] } }, { "name": "calculate_refund", "description": "根据订单号和原因计算退款金额", "parameters": { "type": "object", "properties": { "order_id": {"type": "string"}, "reason": {"type": "string"} }, "required": ["order_id", "reason"] } } ] TOOL_FUNCS = { "get_order_status": get_order_status, "calculate_refund": calculate_refund, }

工具描述要写清楚“它是什么、适合在什么时候调用、参数含义”。模型在规划时真的会逐字读这些描述,一个描述模糊的工具可能永远不被调用。

4.4 跑通全链路:从一句话到一次完整服务流转

主流程我的写法是:用户输入→召回相关知识→Agent规划→循环调用工具→汇总回复。这里先走一遍模拟场景。

# src/agent.py def run_once(user_query: str, memory: dict) -> str: # 1. RAG召回 knowledge = retriever.retrieve(user_query, top_k=3) system = build_system_prompt(knowledge, TOOL_SCHEMAS) messages = [ {"role": "system", "content": system}, {"role": "user", "content": user_query}, ] # 2. Agent循环 for step in range(MAX_STEPS): resp = llm.chat(messages, tools=TOOL_SCHEMAS) if resp.has_tool_call: tool_name = resp.tool_name args = json.loads(resp.tool_args) result = TOOL_FUNCS[tool_name](**args) messages.append(resp.message) messages.append({ "role": "tool", "tool_call_id": resp.tool_call_id, "content": json.dumps(result, ensure_ascii=False) }) memory["last_tool_result"] = result continue # 3. 没有工具调用时,说明Agent认为可以最终回答 return resp.content return "任务步骤较多,请尝试拆分子任务后重试。"

测试一下:输入“我的订单A1002已经超过两天了还没收到,我能退款吗”。

第一步,Retriever 召回“发货时效说明”和“退款政策”相关片段。第二步,Agent 规划后先调用 get_order_status 查询订单,返回 delivered、1天,说明其实已签收;于是重新评估,发现用户说的“超过两天”不成立,最终回答“该订单状态显示已签收,尚未超过退款政策中约定的延迟条件,因此不满足退款条件”。全程没有让模型硬编答案,而是基于真实工具返回结果做结论,这就是三核协同的意义。

5. 落地过程中的高频问题与排查手册

这部分是领导价值所在。跑通Demo只是第一步,真正让系统在业务场景稳定工作,一定会遇到各种边界问题。以下是我在实际项目中反复见过的问题,整理成一套高频排查要点。

5.1 答非所问:召回环节怎么排查

现象:用户问“退款需要什么条件”,系统回复了一段关于售后的泛泛规则,看起来相关,但完全没击中要害。

这类问题八成出在 RAG 召回阶段。我排查顺序是:

  • 先看切片质量:相关条款是否被切散,是否被错误归入其他章节。
  • 再看召回结果:把用户问题转成向量后,在向量库里查top-5,肉眼看每个结果是否相关。
  • 最后看重排策略:top-5里高分段是否有正确片段,重排模型有没有把最优片段压到后面。

如果召回结果本身不相关,那就是前期数据清洗和切分策略问题;如果召回相关但最终回答跑偏,则可能是Prompt里知识片段太多,模型注意力被无关内容干扰。

5.2 Agent转圈:死循环和工具调用失败的应对

现象:Agent 不停调用同一个工具,参数还略有变化;或者某次工具返回异常,Agent 也不汇报,而是反复尝试其他工具,最后超时。

我的处理办法:

  • 设置最大轮次限制,比如6次工具调用后强制结束,并提示“请简化你的操作方案”。
  • 每个工具加上错误返回结构,工具内部异常时返回一个带错误码的dict,而不是抛异常,让Agent能“看到”失败原因并调整。
  • 在Prompt里强制要求:同一个工具最多调用两次,两次后必须更换策略或转为人工介入。

这里有个细节:工具返回内容也要设计“人话”。如果返回的是一个嵌套很深的JSON,模型读起来费劲,也容易误判。比较好的做法是把关键信息提炼成简单文本或扁平JSON,再交给模型。

5.3 成本失控:如何把单次对话的花费用在刀刃上

现象:功能很好,但一聊起来上下文越堆越长,每轮请求的token数不断膨胀,费用成倍增长。

原因多半是历史对话和工具返回结果一直在累加。对策有三个层次:

  • 轻量层:简单问候、功能导航类问题,由一个小模型处理,不进复杂链路。
  • 压缩层:每次工具调用后,对返回结果做摘要,只保留关键结论进上下文,而不是保留原始JSON。
  • 截断层:历史对话超过阈值时,用“近期目标+最新结果”替代完整历史,短期内只需保住当前任务的连续性。

很多成本问题不是选的模型太贵,而是上下文管理太粗糙。

5.4 一套自查清单:出问题先查这五件事

现象排查方向常见根因
回答内容泛泛RAG是否真正命中知识切片粒度不合理、嵌入模型不匹配
回答包含错误数据是否调用了正确工具工具描述不清、参数Schema有问题
Agent 反复调用工具规划逻辑缺乏约束缺少轮次限制和重复调用检查
上下文超长历史与工具结果膨胀缺少摘要压缩和截断策略
模型答非所问Prompt中上下文过载召回片段太多,重点信息淹没

这张表我一直贴在项目看板上,线上反馈处理不过来时,先按表里的方向定位,比每次从头捋代码快很多。

最后再分享一个小技巧

如果只给一条建议:先别急着把 Agent 做得复杂。我见过很多项目一开始就上了全套“多智能体协作+长期记忆”,结果连最基础的知识问答准确性都没过关。更稳妥的打法是先把 RAG 调到足够稳,再给 Agent 加一个工具,让它在真实链路里跑两星期,看日志、看错误率,再逐步扩展。这样每一步都是可回退的,不会一上来就被一堆不确定性淹没。

架构演进到这个阶段,三核联动已经不是“要不要用”的问题,而是“怎么组织好”的问题。对小白程序员来说,理解好这三个角色的分工,比看懂某一段新框架代码重要得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询