十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

AI应用开发核心概念解析:Token、Skill、Agent与RAG的工程实践

AI应用开发核心概念解析:Token、Skill、Agent与RAG的工程实践 1. 项目概述为什么我们需要重新审视这些概念最近在跟很多同行交流尤其是那些正在从传统开发转向AI应用落地的团队发现一个挺普遍的现象大家嘴里都挂着“Agent”、“RAG”、“Skill”这些词但仔细一聊发现每个人心里的定义和边界都不一样。有人觉得Agent就是能自动调用API的脚本有人把RAG简单等同于“向量检索GPT”至于Skill和Token更是经常混为一谈。这种概念上的模糊直接导致了项目沟通成本剧增、技术方案选型失准甚至项目失败。我自己在近一年的多个AI原生应用项目中也深刻体会到了厘清这些基础概念的重要性。这不仅仅是学术讨论而是实实在在的工程问题。一个设计良好的Skill能让你的Agent能力边界清晰、可维护性大增对Token词元的深刻理解直接关系到你如何设计提示词、控制成本以及处理长文本而明确Agent与RAG的能力边界则决定了整个系统的架构是优雅高效还是臃肿混乱。所以我想结合自己踩过的坑和成功的经验把这些概念掰开揉碎了讲清楚。目标不是给出教科书式的定义而是从一线工程视角厘清它们的核心定义、能力边界以及最终如何影响你的落地结果。无论你是正在规划第一个AI应用的产品经理还是负责实现的技术负责人希望这篇内容都能帮你建立起一张清晰的技术地图。2. 基石Token词元——一切计算的起点与成本单元在讨论任何高级架构之前我们必须先回到最基础的单元Token词元。很多人把它简单理解为“单词”这是第一个常见的认知误区也会在后续的设计中埋下隐患。2.1 Token的本质超越“单词”的语义切片Token是大型语言模型LLM理解和生成文本的基本单位。它并非严格对应一个英文单词或一个汉字。OpenAI的Tokenizer分词器会将文本切分成更细的颗粒。例如单词 “hamburger” 可能被分解为 “ham”, “bur”, “ger” 三个token。汉字“蚂蚁”通常会被视为一个token但一些复杂词或专业术语可能被拆开。标点符号、空格甚至数字的每一位都可能是独立的token。这种设计源于模型的训练方式Byte-Pair Encoding, BPE等目的是在词汇表大小和语义表示效率之间取得平衡。一个实用的认知是对于英文1个token约等于0.75个单词对于中文1个token约等于1.5-2个汉字。但这只是个粗略估计具体需要调用API或使用本地库如tiktokenfor OpenAI,transformersfor Hugging Face models进行精确计算。实操心得永远不要凭感觉估算Token数量。在涉及成本控制、上下文窗口长度判断时务必使用准确的分词工具进行计算。我曾在一个项目中因低估了用户输入文档的Token数导致设计的上下文窗口溢出Agent频繁调用失败后期重构代价很大。2.2 Token如何直接影响系统设计与成本对Token的认知会直接渗透到系统设计的每一个环节提示词Prompt工程复杂的思维链Chain-of-Thought或少样本Few-Shot提示会消耗大量Token。你需要权衡提示的详细程度与带来的Token成本。有时一个精心设计的、Token更少的提示效果可能优于一个冗长模糊的提示。上下文窗口管理这是核心挑战。模型的上下文窗口如128K是硬限制。你的系统设计必须包含“Token预算”分配策略系统提示System Prompt定义Agent角色和核心规则应尽量精简。用户查询与历史对话需要保留多少轮历史如何摘要Summarization或选择性记忆检索到的上下文RAG这是最大的变数。你从向量库中检索出5条片段每条片段多长Token数如何裁剪和拼接才能最大化信息密度而不超出窗口成本核算与控制LLM API的计费基本按Token进行。一个频繁调用、处理长文档的Agent其月度成本可能非常惊人。在设计阶段就需要对典型用户交互路径进行Token消耗预估并将其作为架构选型比如是否引入更便宜的模型处理预处理的重要依据。# 示例使用 tiktoken 计算Token数针对OpenAI模型 import tiktoken def num_tokens_from_string(string: str, encoding_name: str cl100k_base) - int: 返回文本的Token数量 encoding tiktoken.get_encoding(encoding_name) num_tokens len(encoding.encode(string)) return num_tokens text 接下来我们将详细解析Skill的设计模式。 token_count num_tokens_from_string(text) print(f文本 {text} 的Token数约为: {token_count}) # 这对于预估每次API调用的成本至关重要。3. 能力模块化Skill技能的设计哲学与实现如果说Token是砖瓦那么Skill就是用这些砖瓦砌成的、功能明确的“房间”。它是Agent能力的具体承载单元也是实现复杂Agent的关键。3.1 Skill的定义高内聚、可编排的原子能力一个Skill应该是一个自包含、可独立测试、完成特定任务的模块。它的输入和输出是明确定义的。例如“查询天气”Skill输入是{“location”: “北京”}输出是{“weather”: “晴”, “temperature”: “25°C”}。“数据库查询”Skill输入是{“sql”: “SELECT * FROM orders WHERE date ‘2023-10-01’”}输出是JSON格式的查询结果。“文本摘要”Skill输入是一段长文本输出是摘要文本。Skill的核心特征是“高内聚、低耦合”。它不应该关心是谁调用了它也不应该直接操作其他Skill的状态。它只负责接收输入、处理、返回输出。这种设计使得Skill可以被不同的Agent复用也便于单独进行单元测试和版本升级。3.2 优秀Skill的设计模式与避坑指南在实践中设计一个好的Skill需要遵循一些原则输入验证与标准化在Skill内部第一步永远是对输入参数进行严格的验证和清洗。一个健壮的Skill应该能处理边缘情况比如参数缺失、格式错误、或超出处理范围的值并返回结构化的错误信息而不是直接抛出异常导致整个Agent崩溃。输出规范化Skill的输出应该是一个结构化的数据对象如Python字典、Pydantic模型。这有利于后续的Skill或Agent进行解析和处理。避免直接返回一段自由文本作为结果除非这个Skill的职责就是生成文本。依赖注入Skill如果需要访问外部资源如数据库连接、API密钥、向量数据库客户端应该通过依赖注入的方式在初始化时传入而不是在内部硬编码。这提升了可测试性和灵活性。异步支持考虑到很多操作网络请求、大模型调用是IO密集型的Skill的实现应尽可能支持异步Async以避免阻塞整个Agent的执行流。# 示例一个结构良好的“天气查询”Skill雏形 from pydantic import BaseModel, Field from typing import Optional import aiohttp class WeatherInput(BaseModel): Skill的输入模型 location: str Field(description城市名称如‘北京’) unit: str Field(defaultcelsius, description温度单位celsius 或 fahrenheit) class WeatherOutput(BaseModel): Skill的输出模型 location: str weather: str temperature: float unit: str error: Optional[str] None class WeatherSkill: def __init__(self, api_key: str): self.api_key api_key self.base_url https://api.weather.com async def execute(self, input_data: WeatherInput) - WeatherOutput: 执行技能的核心方法 # 1. 输入验证Pydantic已做 # 2. 构造请求 params {key: self.api_key, q: input_data.location, units: input_data.unit} try: async with aiohttp.ClientSession() as session: async with session.get(f{self.base_url}/current.json, paramsparams) as resp: data await resp.json() except Exception as e: # 3. 错误处理 return WeatherOutput( locationinput_data.location, weather, temperature0.0, unitinput_data.unit, errorf网络请求失败: {str(e)} ) # 4. 解析并规范化输出 return WeatherOutput( locationdata[location][name], weatherdata[current][condition][text], temperaturedata[current][temp_c] if input_data.unit celsius else data[current][temp_f], unitinput_data.unit )常见问题很多初学者会把一个庞大的、多步骤的流程写在一个Skill里比如“获取用户需求-分析-搜索-生成报告”。这违反了单一职责原则。正确的做法是将其拆分为“需求解析Skill”、“智能搜索Skill”、“报告生成Skill”然后由上层Orchestrator编排器或Agent来协调它们。这样每个Skill都更简单、更易维护和复用。4. 智能中枢Agent智能体的架构与决策逻辑当我们将多个Skill组合在一起并赋予其决策和推理能力时就得到了Agent。Agent是具备一定自主性能够理解目标、规划步骤、调用工具Skill、并处理不确定性的系统。4.1 Agent的核心组件不止是“if-else”链一个典型的Agent架构包含以下核心部分远不止简单的顺序调用规划器Planner这是Agent的“大脑”。它根据用户的目标或查询分解出需要执行的步骤序列。规划可以是简单的线性链也可以是复杂的树状结构如ReAct模式Thought - Action - Observation。规划器本身可能是一个LLM通过提示词让其生成一个计划。技能库Skill Registry存储所有可用的Skill及其描述名称、功能、输入输出格式。Agent的规划器需要基于这个库来决定调用哪个Skill。执行器Executor负责按照规划器的计划依次调用相应的Skill并传递参数。它需要处理Skill执行中的错误并决定重试或失败处理策略。记忆MemoryAgent的“短期工作记忆”和“长期经验记忆”。短期记忆通常指当前会话的上下文对话历史。长期记忆可能指通过RAG访问的外部知识库或者是对过去成功/失败经验的存储与学习。反思Reflection或 验证Validation高级Agent在行动后会评估结果是否满足目标。如果不满足它可以重新规划或尝试替代方案。这是实现“自我纠正”能力的关键。4.2 从简单到复杂Agent的演进路径根据复杂度Agent可以大致分为几个层次层次名称特点典型实现适用场景L1工具调用型根据用户指令直接调用对应的单一Skill。决策逻辑简单如关键词匹配。基于规则的路由。客服机器人固定问答、简单数据查询。L2流程编排型能执行多步骤任务流程相对固定预定义工作流。规划能力弱。LangChain Expression Language, Prefect, Temporal。订单处理、内容审核流水线、数据ETL。L3自主规划型核心能力。利用LLM进行动态任务分解和规划如ReAct。能处理未知情况。ReAct模式 AutoGPT早期版本 LangChain Agent。复杂问题解答、研究分析、开放式创作。L4多Agent协作多个特化Agent协同工作通过通信共同解决复杂问题。CrewAI, AutoGen。软件项目开发、市场策略分析、跨领域研究。对于大多数落地应用L2和L3是当前的主战场。L1过于死板L4则对架构和协调逻辑要求极高复杂度呈指数增长。实操心得不要盲目追求“完全自主”的L3 Agent。在很多商业场景中一个设计精良的L2“流程编排型”Agent因为其确定性和可控性反而更容易成功落地。例如一个“用户投诉处理Agent”其步骤1.情感分析 2.问题分类 3.查询知识库 4.生成回复模板 5.人工审核是固定的用L2实现更稳定。将LLM仅用于其中需要灵活性的环节如情感分析、回复润色而非全盘规划。5. 知识增强RAG检索增强生成的精准定位RAG是2023年以来最火热的技术之一但它经常被误解为Agent的一部分或者被过度神化。我们需要清晰地界定它的能力边界。5.1 RAG是什么不是什么RAG是什么它是一种为LLM提供特定、实时、私有知识的技术架构。核心流程是用户查询 - 检索从知识库中找到相关片段- 增强将片段作为上下文插入提示词- 生成LLM基于上下文生成回答。RAG不是什么它不是Agent不具备自主规划和执行多步骤任务的能力。它本质上是LLM的一个“外部记忆体”或“参考书库”。关键区别Agent决定“做什么”和“怎么做”规划与行动而RAG负责在“生成答案”这个具体动作上提供更准确的参考资料。你可以让一个Agent在规划中决定“要回答这个问题我需要先去查一下公司知识库即调用RAG流程”。5.2 RAG落地的核心挑战与优化策略实现一个“能用”的RAG很简单但实现一个“好用”的RAG非常难。以下是几个核心挑战及应对思路检索质量召回率与精确率问题检索不到相关文档召回率低或检索到太多不相关文档精确率低。优化分块策略不要简单按固定字数分块。尝试按段落、按标题、按语义重叠如LangChain的RecursiveCharacterTextSplitter进行分块。向量模型针对中文、专业领域医学、法律选择或微调专用的嵌入模型通用模型如text-embedding-ada-002可能不够精准。混合检索结合向量检索语义相似和关键词检索如BM25解决术语、缩写精确匹配问题综合排序。元数据过滤为每个文本块添加来源、日期、章节等元数据检索时进行过滤缩小范围。上下文窗口与信息密度问题检索到的多个文本块直接拼接可能超出LLM上下文窗口或包含冗余信息。优化重排序Re-ranking使用一个更小、更快的模型如BGE-Reranker对初步检索结果进行重排序只保留最相关的几条。摘要或压缩对长文本块进行摘要再将摘要送入上下文。迭代检索先进行一轮粗略检索根据LLM的初步回答生成更精确的查询进行第二轮检索。生成阶段的“幻觉”与忽略上下文问题LLM即使拿到了正确答案的上下文也可能自己胡编乱造或完全忽略。优化提示词工程在系统提示中强约束如“严格且仅依据以下提供的上下文信息来回答问题。如果上下文不包含答案请直接说‘根据现有资料无法回答’。”引用溯源要求LLM在生成答案时注明引用的原文块编号便于用户核查和系统评估。# 示例一个包含混合检索和重排序的RAG流程核心思路 from langchain.vectorstores import Chroma from langchain.retrievers import BM25Retriever, EnsembleRetriever from langchain.retrievers.document_compressors import LLMChainExtractor from langchain.llms import OpenAI # 1. 准备检索器 vectorstore Chroma(...) # 已加载文档的向量库 vector_retriever vectorstore.as_retriever(search_kwargs{k: 5}) texts [...] # 原始文档文本列表 bm25_retriever BM25Retriever.from_texts(texts) # 2. 构建混合检索器 ensemble_retriever EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.4, 0.6] # 可以调整权重 ) # 3. 可选定义重排序或压缩器 compressor LLMChainExtractor.from_llm(OpenAI(temperature0)) # 这里可以使用专门的交叉编码器模型进行重排序效果更好 # 4. 检索并生成 def rag_query(query: str): # 混合检索 docs ensemble_retriever.get_relevant_documents(query) # 可选对docs进行重排序或压缩 # compressed_docs compressor.compress_documents(docs, query) # 构建增强提示 context \n\n.join([doc.page_content for doc in docs]) prompt f基于以下上下文回答问题。如果无法从中得出答案请说不知道。 上下文{context} 问题{query} 答案 # 调用LLM生成 answer llm(prompt) return answer, docs # 返回答案和引用来源6. 厘清边界Skill、Agent、RAG的协同作战模式现在让我们把这三个概念放在一个具体的场景里看它们如何各司其职协同工作。场景一个“智能研发助手”需要回答“我们项目目前用Redis做缓存最近经常出现缓存击穿该如何优化”Agent规划中枢接收到这个问题。规划器LLM驱动分析这个问题涉及“技术概念理解”、“现状诊断”和“方案推荐”。我需要先理解“缓存击穿”是什么查知识库然后分析可能的原因需要推理最后给出优化方案可能需要结合最佳实践。规划输出调用RAG流程从公司内部技术文档和公共技术博客库中检索“Redis 缓存击穿 定义 原因”。调用“代码模式分析”Skill传入当前的Redis配置和使用代码片段假设能获取到分析潜在的不当模式。综合前两步的结果由LLM或调用“方案生成”Skill生成结构化的优化建议报告。RAG知识检索被Agent的步骤1调用。它接收查询“Redis 缓存击穿 定义 原因”从向量知识库中检索出最相关的3个片段例如定义、常见原因、一个布隆过滤器的示例代码。将这些片段格式化后返回给Agent。Skill原子能力被Agent的步骤2调用。“代码模式分析”Skill被激活。它接收的参数是{“code_snippet”: “...”, “config”: “...”}。这个Skill内部封装了静态代码分析规则和针对Redis的常见反模式检查逻辑。它运行分析输出一个结构化的报告{“issue_found”: [“未设置热点key过期时间分散”, “未使用互斥锁”], “risk_level”: “medium”}。Agent综合与生成收到RAG返回的知识片段和Skill返回的分析报告。它将所有信息整合形成最终的提示词请求LLM生成一份针对该项目的、具体的优化方案并可能附上检索到的参考文档链接和Skill分析出的问题点。在这个流程中边界非常清晰Agent负责宏观的任务分解、流程控制和决策下一步该做什么。RAG负责在需要外部知识时提供精准的“文献支持”。Skill负责执行具体的、可复用的“动作”或“分析”。7. 落地结果架构选择如何决定项目成败理解了这些概念的定义和边界最终要服务于落地。不同的选择会导致截然不同的项目结果。7.1 错误架构的典型症状与根源“巨无霸”Agent症状一个Agent代码文件长达数千行包含了从用户输入解析、到业务逻辑判断、再到调用外部API、最后生成回复的所有代码。根源没有进行Skill抽象所有功能耦合在一起。结果难以测试、难以调试、无法复用、任何小修改都可能引发未知错误。项目很快陷入泥潭。“RAG即一切”症状试图用RAG解决所有问题将复杂的、需要多步骤推理和工具操作的任务也强行塞进一个提示词期望LLM通过“阅读”大量上下文自己完成。根源混淆了RAG提供知识和Agent规划与行动的边界。结果提示词极其冗长Token成本高昂回答质量不稳定对于需要实际操作如更新数据库、发送邮件的任务完全无能为力。“Skill混乱”症状定义了无数细碎的Skill但每个Skill的输入输出格式不统一依赖关系混乱Agent的规划器难以正确理解和调用它们。根源缺乏对Skill接口的标准化设计。结果Agent的规划逻辑复杂且脆弱系统集成难度大。7.2 正向实践从概念到稳定交付的路径从Skill开始设计不要一上来就设计庞大的Agent。先枚举你的应用需要哪些核心能力如数据查询、信息摘要、邮件发送、图像生成将这些能力封装成一个个标准化、可独立测试的Skill。这是构建稳定系统的基石。明确Agent的职责Agent的核心价值是“规划”和“协调”。用LLM或规则引擎来实现一个轻量、鲁棒的规划器。它的任务是根据用户意图从Skill库中选择并排序要执行的Skill序列。按需引入RAG只有当你的任务严重依赖特定的、静态的或实时性要求不高的外部知识时才引入RAG。对于需要实时数据如股票价格或执行具体操作如关闭阀门的任务应该通过Skill调用API来实现而不是指望RAG检索到操作手册然后让LLM“想象”如何操作。建立评估与监控体系落地后必须建立监控。关键指标包括Token消耗与成本监控每个会话、每个任务的Token使用情况。Skill调用成功率与延迟定位性能瓶颈和故障点。RAG检索相关性定期抽样评估检索结果是否相关。任务完成率与用户满意度衡量最终效果。我个人在主导一个企业级数据分析助手项目时就严格遵循了上述路径。我们先定义了“SQL查询生成”、“图表生成”、“报告摘要”三个核心Skill并规范了它们的JSON接口。然后构建了一个简单的规划Agent它只做一件事判断用户问题是需要数据、图表还是报告然后调用相应的Skill链。对于产品专有名词和业务规则我们建立了一个小型的RAG知识库仅在Agent识别到需要解释概念时才调用。这套架构使得系统模块清晰每个部分都可以单独优化和升级最终交付非常平稳后续维护成本也大大低于同期其他“大杂烩”式的项目。说到底技术概念的价值在于帮助我们更好地划分边界、降低复杂度。Token是尺子帮我们度量成本和容量Skill是乐高积木保证系统的模块化和可靠性Agent是导演负责编排剧情RAG是百科全书提供精准的参考资料。当你下次再启动一个AI应用项目时不妨先画一张图问问自己这个功能到底该由谁来实现厘清了这一点你的项目就成功了一半。
返回列表