1. 项目概述从概念到实践的AI智能体最近和几个做产品和技术的朋友聊天发现大家虽然都在谈“AI智能体”但理解却千差万别。有人觉得它就是个大号ChatGPT有人认为是能自动执行任务的脚本还有人把它想象成科幻电影里的数字生命。其实AI智能体既没那么简单也没那么玄乎。简单来说它是一个能够感知环境、自主决策并执行动作以实现特定目标的智能系统。这听起来可能有点抽象但如果你用过能自动帮你总结邮件、安排日程的AI助手或者看过那些能自己玩《我的世界》的AI那你已经接触过智能体的雏形了。这个内容的核心就是帮你彻底搞懂AI智能体到底是怎么一回事更重要的是手把手地带你从零开始搭建出你的第一个能真正干活的智能体。无论你是好奇的技术爱好者还是想在实际工作中应用AI的开发者甚至是产品经理想搞清楚这波技术浪潮到底能做什么这篇文章都会给你一个清晰、可操作的路线图。我们会避开那些晦涩的学术论文直接从“它是什么”、“它能干什么”以及“你怎么做一个”这三个最实际的问题切入。2. 智能体的核心架构与工作原理拆解2.1 超越聊天机器人智能体的核心组件很多人会把AI智能体和大型语言模型混为一谈这是一个常见的误解。你可以把LLM看作是智能体的“大脑”或“决策中枢”但一个完整的智能体远不止于此。它更像一个配备了感官、大脑和四肢的完整个体。一个典型的智能体架构通常包含以下几个核心组件感知模块这是智能体的“眼睛和耳朵”。它负责从环境中接收信息。这个环境可以是文本输入如用户指令、网页内容、API返回的数据、数据库查询结果甚至是图像或音频。感知模块的任务是将这些原始信息进行解析、清洗和结构化转换成智能体“大脑”能够理解的标准格式。规划与决策模块核心这就是通常由LLM扮演的角色。它接收来自感知模块的结构化信息结合智能体自身的目标和记忆进行推理、规划和决策。比如当用户说“帮我订一张明天下午去上海的最便宜机票”决策模块需要分解任务先查询天气和航班信息再比价最后执行订票操作。它不直接行动而是生成一个清晰的“行动计划”。工具调用模块这是智能体的“双手”。决策模块想好了要做什么但具体执行需要靠工具。这个模块负责将决策转化为对具体工具或API的调用。例如调用搜索引擎API查询航班信息、调用支付接口完成订票、操作鼠标键盘进行GUI自动化等。工具库的丰富程度直接决定了智能体的能力边界。记忆模块智能体需要有“记忆”才能进行连贯的对话和任务。记忆分为短期记忆当前会话的上下文和长期记忆存储用户偏好、历史交互、学到的知识等。良好的记忆机制能让智能体避免重复提问实现个性化服务并在多轮交互中保持目标一致。执行与反馈循环工具执行后会产生结果成功、失败、部分数据这些结果会作为新的环境信息再次输入给感知模块从而形成一个“感知-思考-行动-观察”的闭环。智能体根据执行结果决定是继续下一步还是调整策略。2.2 工作流程一个完整的任务执行周期让我们通过一个具体例子看看这些组件是如何协同工作的。假设我们构建一个“智能研究助手”任务是“找出近三年人工智能在医疗影像诊断领域最重要的三篇论文并总结其核心方法”。步骤一感知与目标解析。智能体接收到你的自然语言指令。感知模块将其解析识别出关键要素领域AI医疗影像、时间范围近三年、任务类型文献检索与总结、数量要求三篇。步骤二规划与决策。决策模块LLM开始思考“要完成这个任务我需要先访问学术数据库然后用特定的关键词进行搜索接着从结果中筛选出高质量、高引用的论文最后阅读摘要或全文进行总结。” 它会生成一个初步计划比如1. 调用 arXiv/PubMed API 搜索关键词2. 按引用量或相关性排序3. 获取前三篇的详细信息4. 总结每篇的核心贡献。步骤三工具调用与执行。工具调用模块根据计划依次执行调用学术搜索工具传入关键词“medical image diagnosis AI 2021-2024”。收到JSON格式的搜索结果后调用数据过滤工具按“引用次数”降序排列。对前三篇论文调用文献摘要获取工具或PDF解析工具来获取内容。步骤四观察与迭代。工具执行后返回数据。假设搜索工具返回了50篇论文过滤工具成功排序但摘要获取工具对其中一篇失败需要付费。这个“失败”的反馈会再次进入感知模块。步骤五再规划与执行。决策模块根据反馈调整计划“有一篇无法直接获取摘要尝试通过其他公开渠道如会议网站查找其核心方法描述或者用第四篇候选论文替代。” 然后指挥工具调用模块执行新的操作。步骤六整合与输出。最终智能体收集齐三篇论文的核心信息由决策模块LLM进行归纳、总结并用流畅的自然语言输出给你同时可能附上论文链接。这个循环可能不止一次复杂的任务需要多次“思考-行动-观察”的迭代。关键在于智能体在这个过程中是自主的它自己决定下一步该调用哪个工具、如何处理异常而不是每一步都需要你手把手指导。注意这里最容易出现的误区是“过度依赖LLM的幻觉”。如果让LLM直接“编造”三篇论文和总结它很可能做得像模像样但内容全是假的。因此智能体的价值在于用LLM的规划能力驱动可靠的工具如真实的搜索API、数据库去获取事实再让LLM基于事实进行总结从而将LLM的创造力与工具的确定性结合起来。3. 构建你的第一个智能体从零到一的实战指南理论讲得再多不如亲手搭建一个。这一部分我们将选择一条最实用、最低门槛的路径使用当前最流行的框架之一——LangChain搭配OpenAI的GPT模型来构建一个功能明确的智能体。我们以“一个能联网查询信息并给出总结的智能体”为目标。3.1 环境准备与工具选型为什么选LangChain因为它像一个“乐高积木”框架把LLM、记忆、工具链、智能体逻辑都模块化了让我们不用从零开始造轮子。对于初学者来说它能极大降低复杂度。基础环境搭建安装Python确保你的电脑安装了Python 3.8或以上版本。建议使用虚拟环境来管理依赖避免包冲突。# 创建并激活虚拟环境以venv为例 python -m venv ai_agent_env source ai_agent_env/bin/activate # Linux/Mac # ai_agent_env\Scripts\activate # Windows安装核心库pip install langchain langchain-openai langchain-communitylangchain: 核心框架。langchain-openai: 官方维护的OpenAI模型集成。langchain-community: 包含大量第三方工具和组件。准备API密钥你需要一个OpenAI的API密钥。前往OpenAI平台注册并获取。安全起见不要将密钥硬编码在代码中而是设置为环境变量。# 在终端中设置临时 export OPENAI_API_KEYyour-api-key-here # 或者在代码中通过python-dotenv等库加载核心工具选择对于“联网查询”这个功能我们需要给智能体配备“搜索”这个工具。这里有几个选择Serper API一个专门针对LLM优化的谷歌搜索API价格低廉返回结果结构清晰非常适合智能体调用。这是我们的首选。Tavily Search API另一个为AI代理设计的搜索API直接返回高质量的摘要信息更省心。自定义封装如果你有特定网站的爬虫或内部API也可以封装成LangChain工具。我们选择Serper因为它简单、稳定且成本可控。你需要去Serper官网注册并获取一个API密钥。3.2 定义工具与创建智能体现在让我们开始写代码。第一步是让智能体拥有“搜索”的能力。# 导入必要的模块 import os from langchain.agents import AgentExecutor, create_openai_tools_agent from langchain_openai import ChatOpenAI from langchain.tools import Tool from langchain_community.utilities import GoogleSerperAPIWrapper from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.memory import ConversationBufferMemory # 1. 初始化搜索工具 # 确保已设置环境变量 SERPER_API_KEY search GoogleSerperAPIWrapper() search_tool Tool( nameSearch, funcsearch.run, descriptionUseful for when you need to answer questions about current events or real-time information. Input should be a search query. ) # 2. 初始化LLM我们使用GPT-3.5-turbo性价比高 llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # temperature0 使输出更确定减少随机性对于执行任务的智能体更合适。 # 3. 创建提示词模板 # 这是告诉智能体“你是谁”、“你该怎么做”的说明书 prompt ChatPromptTemplate.from_messages([ (system, You are a helpful assistant that can search the web. Answer questions as accurately as possible based on the search results. If you are not sure, say so.), MessagesPlaceholder(variable_namechat_history), # 为记忆留出位置 (human, {input}), MessagesPlaceholder(variable_nameagent_scratchpad), # 智能体思考过程暂存处 ]) # 4. 添加记忆功能 memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) # 5. 组装智能体 tools [search_tool] agent create_openai_tools_agent(llm, tools, prompt) # 6. 创建执行器 agent_executor AgentExecutor(agentagent, toolstools, memorymemory, verboseTrue) # verboseTrue 会打印出详细的思考过程方便调试生产环境可关闭。这段代码做了什么我们创建了一个拥有“搜索”工具、具备简单对话记忆、基于GPT-3.5-turbo进行决策的智能体骨架。AgentExecutor是负责运行整个感知-决策-执行循环的引擎。3.3 运行与测试让你的智能体动起来现在让我们问它一个问题看看它如何工作。# 运行智能体 result agent_executor.invoke({input: 2024年巴黎奥运会中国代表团获得了多少枚金牌}) print(result[output])当你运行这段代码如果verboseTrue你会在控制台看到类似这样的思考过程这是智能体最迷人的部分 Entering new AgentExecutor chain... Thought: 用户问的是2024年巴黎奥运会中国代表团的金牌数。这是一个需要最新实时信息的问题我应该使用搜索工具。 Action: Search Action Input: 2024年巴黎奥运会 中国 金牌数 Observation: [Serper API返回的搜索结果例如“根据2024年巴黎奥运会官方数据中国代表团共获得40枚金牌位列金牌榜第一。”] Thought: 我已经搜索到了准确信息可以回答用户了。 Action: Answer Action Input: 根据最新信息在2024年巴黎奥运会上中国代表团共获得了40枚金牌。 Finished chain. 根据最新信息在2024年巴黎奥运会上中国代表团共获得了40枚金牌。看它自己完成了“思考-调用搜索工具-获取结果-组织语言回答”的全过程这就是一个最基本但已具备自主性的AI智能体。实操心得一控制成本与超时。每次工具调用和LLM思考都会消耗API费用OpenAI Serper。在AgentExecutor中可以设置max_iterations最大迭代次数和max_execution_time最大执行时间来防止智能体陷入死循环或执行过于复杂的任务避免意外的高额账单。例如agent_executor AgentExecutor(..., max_iterations5, max_execution_time30)。4. 深入核心规划策略与工具调用详解4.1 理解智能体的“思考”模式ReAct范式在上面的输出中你看到了“Thought:”、“Action:”、“Observation:”这样的结构。这不是随意的而是遵循了一种名为ReAct (Reason Act)的经典范式。这是让LLM能够可靠使用工具的核心技术。Reason (思考)LLM分析当前情况用户问题、已有信息、历史记录决定下一步该做什么。是直接回答还是调用某个工具如果调用工具输入应该是什么Act (行动)根据思考的结果执行一个具体的动作。通常是格式化地调用一个工具如Search(query)。Observation (观察)获取工具执行后的结果。这个结果会成为下一轮“思考”的输入。这个循环反复进行直到智能体认为任务完成生成最终答案或达到迭代上限。LangChain的create_openai_tools_agent默认就采用了适配ReAct的提示词模板所以我们才能看到结构化的输出。为什么ReAct有效它强制LLM将“内在推理”外显化、步骤化。把“想”和“做”分开让LLM的推理过程变得可控、可调试。否则LLM可能会在内部进行多步推理但一步输出一旦出错我们很难知道它错在哪一环。4.2 扩展你的工具库让智能体更全能只有一个搜索工具智能体还是个“瘸子”。真正的能力来源于丰富的工具库。在LangChain中集成新工具非常简单。假设我们想让智能体还能做计算和查天气。from langchain.tools import tool from langchain_community.tools import OpenWeatherMapAPIWrapper import math # 1. 自定义一个计算器工具 tool def calculator(expression: str) - str: Evaluates a mathematical expression. Input should be a string like 2 2 or sqrt(16). try: # 使用eval有安全风险仅作示例。生产环境应用更安全的解析库如ast.literal_eval或专用计算库。 # 这里为了安全我们限制只使用math库中的函数。 allowed_names {k: v for k, v in math.__dict__.items() if not k.startswith(_)} result eval(expression, {__builtins__: {}}, allowed_names) return str(result) except Exception as e: return fError evaluating expression: {e} # 2. 集成天气工具需要OPENWEATHERMAP_API_KEY weather OpenWeatherMapAPIWrapper() weather_tool Tool( nameGetCurrentWeather, funcweather.run, descriptionUseful for getting the current weather in a location. Input should be a location string like London or New York. ) # 3. 更新工具列表并重新创建智能体 tools [search_tool, calculator, weather_tool] # 需要更新提示词简要描述新工具的功能 prompt ChatPromptTemplate.from_messages([ (system, You are a helpful assistant with access to web search, a calculator, and weather lookup. Use the tools when needed.), MessagesPlaceholder(variable_namechat_history), (human, {input}), MessagesPlaceholder(variable_nameagent_scratchpad), ]) agent create_openai_tools_agent(llm, tools, prompt) agent_executor AgentExecutor(agentagent, toolstools, memorymemory, verboseTrue) # 测试复杂任务 result agent_executor.invoke({ input: 先搜索一下北京今天的天气然后如果温度高于20度就计算一下华氏度是多少。 }) print(result[output])现在你的智能体已经具备了信息检索搜索、逻辑判断LLM、数据计算计算器、专业查询天气的复合能力。它可以自主决定调用链先查天气拿到摄氏温度数据判断条件再调用计算器进行单位换算。4.3 工具描述的艺术让智能体更“聪明”地选择你可能注意到了每个Tool对象都有一个description参数。这个描述至关重要它是LLM选择工具的唯一依据。LLM并不理解工具背后的代码它只根据你的文字描述来判断“这个工具是干什么的我该在什么时候用它”编写优秀工具描述的技巧明确用途以“Useful for...”开头直接说明适用场景。差“A calculator.”好“Useful for performing mathematical calculations. Input should be a clear arithmetic expression like 15 * (3 7).”规定输入格式明确说明输入应该是什么样子。LLM会尝试构造符合描述的输入。差“Gets the weather.”好“Useful for getting the current weather. Input should be a location name as a string, e.g., San Francisco or Tokyo, Japan.”区分相似工具如果有多个工具功能相近描述要突出其差异。工具A“Searches for general information on the web. Good for broad questions.”工具B“Searches for academic papers and research. Input should include technical terms or paper titles.”实操心得二工具描述的精准度直接决定智能体性能。我遇到过智能体反复调用错误工具的情况排查后发现是工具描述模糊LLM无法区分。花时间打磨description就像给员工一份清晰的岗位说明书能极大提升协作效率。一个技巧是在verboseTrue模式下观察智能体的思考链如果它错误地选择了工具很可能是描述出了问题。5. 记忆与状态管理实现连贯对话与个性化没有记忆的智能体每次对话都是“初见”。要让智能体真正有用必须让它能记住上下文。5.1 短期记忆对话缓冲区我们之前使用的ConversationBufferMemory就是一种简单的短期记忆。它会把整个对话历史包括用户输入、智能体输出、工具观察结果都保存下来并在每次调用时作为上下文传递给LLM。优点实现简单信息完整。缺点当对话轮次很多时会消耗大量Token增加成本可能超出模型上下文长度而且无关的历史信息可能会干扰当前决策。5.2 长期记忆向量数据库的引入对于需要记住用户偏好、历史事实或大量知识库的场景我们需要长期记忆。最常用的方法是使用向量数据库。原理将需要记忆的文本信息如“用户喜欢喝黑咖啡”、“项目X的截止日期是下周五”通过嵌入模型转换成高维向量存储到向量数据库如Chroma、Pinecone、Weaviate中。当需要回忆时将当前问题也转换成向量在数据库中搜索“语义上最相似”的过往记忆。# 示例使用Chroma实现一个简单的长期记忆 from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.docstore.document import Document from langchain.tools.retriever import create_retriever_tool # 1. 初始化嵌入模型和向量数据库 embeddings OpenAIEmbeddings() vectorstore Chroma(embedding_functionembeddings, persist_directory./chroma_db) # 假设我们有一些初始记忆 initial_memories [ The users name is Alex., Alex prefers meetings in the afternoon., The project Alpha deadline is 2024-06-30., ] # 将文本转换为Document对象并存入向量库 docs [Document(page_contenttext) for text in initial_memories] vectorstore.add_documents(docs) # 2. 创建一个检索器 retriever vectorstore.as_retriever() # 3. 将检索器包装成一个“记忆查询”工具 memory_tool create_retriever_tool( retriever, search_memory, Searches through the assistants long-term memory for relevant past information about the user or facts., ) # 4. 将这个工具加入到智能体的工具列表中 tools.append(memory_tool) # 5. 更新智能体提示词告诉它可以使用这个记忆工具 prompt ChatPromptTemplate.from_messages([ (system, You are a helpful assistant. You have access to a memory store about the user. Use the search_memory tool to recall relevant information before answering.), # ... 其他部分不变 ]) # 重新创建智能体...现在当你问智能体“我下午有空吗”它可能会先调用search_memory工具查询到“Alex prefers meetings in the afternoon.”这条记忆从而给出更个性化的回答“根据您的偏好下午通常是您安排会议的时间请问需要我为您查看具体的日程安排吗”5.3 记忆的更新与维护记忆不是只读的。智能体在对话中了解到的新信息如用户说“我最近开始喝绿茶了”也应该被存储起来。这可以通过在对话结束后或者设计一个专门的“更新记忆”工具来实现将新的Document添加到向量库中。注意事项记忆的准确性至关重要。错误的记忆幻觉或过时信息会导致智能体持续犯错。需要设计机制来验证重要信息如让用户确认或定期清理/更新记忆。对于关键事实最好还是让智能体通过搜索工具去查询实时信息而非完全依赖内部记忆。6. 高级主题与性能优化6.1 智能体类型的选择LangChain提供了多种预设的智能体类型适用于不同场景OpenAI Tools Agent我们上面使用的。专为适配OpenAI的function calling函数调用功能设计与GPT系列模型集成度最高执行效率好。推荐首选。Structured Chat Agent使用更结构化的提示词适合需要输出严格格式如JSON或处理多输入工具的复杂场景。ReAct Document Store Agent专为在大量文档中问答和推理设计集成了ReAct和文档检索。Self-Ask with Search一种简单的智能体专门用于问答它会自动将复杂问题分解成多个可搜索的子问题。选择哪种对于大多数通用任务OpenAI Tools Agent是最稳定、最方便的选择。如果你的任务极度复杂需要非常精细的控制流可能需要自定义智能体逻辑。6.2 处理复杂任务智能体与工作流的结合单个智能体处理简单任务很棒但面对“写一份行业报告”这样的复杂任务时可能会力不从心。这时可以采用“智能体编排”或“分层规划”的思路。思路一主管智能体。创建一个“主管”智能体它的工具是调用其他“专家”智能体如研究智能体、写作智能体、校对智能体。主管负责任务分解和调度。思路二LangGraph。这是LangChain的新模块允许你用图Graph的方式来定义智能体之间的工作流。节点可以是智能体、工具或判断逻辑边代表执行路径。这非常适合有固定流程的复杂业务场景。# 伪代码示例主管智能体模式 from langchain.agents import AgentExecutor, create_openai_tools_agent from langchain.tools import BaseTool from typing import Any class ResearchAgentTool(BaseTool): name Call_Researcher description Calls a specialized research agent to gather information on a topic. def _run(self, query: str) - str: # 这里可以实例化并运行一个专门的研究智能体 research_result specialized_research_agent.run(query) return research_result # 主管智能体拥有调用其他专家的工具 supervisor_tools [ResearchAgentTool(), WritingAgentTool(), ...] supervisor_agent create_openai_tools_agent(llm, supervisor_tools, supervisor_prompt)6.3 性能优化与成本控制实战智能体应用从原型到生产必须考虑性能和成本。1. 减少不必要的LLM调用缓存对相同的输入使用LangChains Cache或Redis缓存LLM响应和嵌入结果。精简上下文使用ConversationSummaryMemory或ConversationBufferWindowMemory替代完整的缓冲区记忆。前者会定期用LLM总结长对话后者只保留最近N轮对话。优化提示词清晰、简洁的提示词能减少Token消耗并提高响应质量。2. 提升工具调用可靠性结构化工具输出确保工具返回的是结构清晰、简洁的字符串或JSON。杂乱的HTML或日志信息会干扰LLM解析。错误处理与重试在工具调用层和智能体执行层都要添加重试逻辑。网络波动、API限流是常事。超时设置为每个工具调用和整个智能体执行设置合理的超时时间。3. 监控与评估记录日志详细记录每次智能体运行的完整链Thought, Action, Observation这是调试和优化的黄金数据。定义评估指标对于你的应用什么是“好”是任务完成率、回答准确率、还是用户满意度建立评估体系才能持续改进。A/B测试提示词微调系统提示词system message对智能体行为影响巨大。可以并行测试不同版本。踩坑实录我曾部署过一个客服智能体初期没有设置max_iterations。结果有一次用户问了一个模糊的问题智能体陷入“搜索-得到不相关结果-换个关键词再搜索”的死循环跑了20多轮消耗了大量API费用才发现。教训生产环境的智能体必须加上“安全阀”——严格的迭代次数和超时限制。7. 常见问题排查与调试技巧即使按照指南操作你也可能会遇到智能体“犯傻”的情况。以下是几个典型问题及排查思路。问题1智能体不调用工具总是试图自己回答。可能原因提示词中未明确鼓励使用工具工具描述不够清晰或相关LLM的temperature参数过高导致随机性太大。排查检查系统提示词system是否包含了类似“You have access to the following tools:”、“When you need to... use the X tool.”的指令。检查工具描述description是否准确描述了适用场景让一个同事看描述猜这个工具是干嘛的看他能否猜对。将temperature设为0或一个较低的值如0.1增加输出的确定性。打开verboseTrue看它的思考链Thought。如果Thought里根本没考虑工具那就是提示词或描述的问题如果考虑了但选错了是描述区分度问题。问题2智能体陷入循环反复调用同一个工具。可能原因工具返回的结果无法让智能体做出决策任务本身模糊或无法完成缺少终止条件。排查观察Observation工具返回的结果。这个结果是否清晰、有用如果工具返回的是“未找到结果”或一堆无关信息LLM可能不知道如何是好只能再试一次。需要改进工具本身或处理其返回结果。检查任务指令。是否要求了不存在或模糊的信息给智能体的指令应尽可能明确。强制设置max_iterations如3-5次避免无限循环。问题3工具调用参数格式错误。可能原因LLM没有按照工具期望的格式生成输入。这在处理复杂参数如嵌套JSON时常见。排查在工具描述中用示例明确指定输入格式。例如Input should be a JSON string like {city: Beijing, days: 3}。使用StructuredTool而不是普通的Tool。StructuredTool可以定义严格的输入参数模式Pydantic模型LangChain会据此生成更规范的调用格式。在工具函数内部增加更健壮的输入解析和错误处理返回友好的错误信息给智能体让它有机会修正。问题4智能体“幻觉”基于错误记忆或推理编造答案。可能原因过度依赖了长期记忆中的错误信息或者在规划时没有充分依赖工具返回的事实。排查审视你的记忆检索机制。检索到的记忆是否相关且准确可以增加检索的相似度阈值或对检索结果进行二次筛选。强化提示词在系统指令中强调“基于工具返回的事实信息进行回答如果信息不足请明确说明不知道不要编造”。对于关键事实设计流程让智能体必须通过搜索等可靠工具进行验证而不是直接相信记忆。调试工具箱verboseTrue这是最重要的调试手段没有之一。仔细阅读思考链。LangSmithLangChain官方提供的追踪和调试平台。它能可视化整个智能体的调用链查看每一步的输入输出、耗时和Token消耗是进行深度调试和性能分析的利器。单元测试为你的智能体编写测试用例模拟各种用户输入确保核心功能稳定。构建AI智能体的过程是一个不断与模型“沟通”、调试和迭代的过程。它不像传统编程那样有绝对的确定性但正是这种不确定性带来了巨大的灵活性和潜力。从今天你搭建的第一个能搜索的小智能体开始逐步为它添加新的感官图像识别、语音输入、新的技能写邮件、操作软件、新的记忆方式你会发现一个真正能帮你处理复杂工作的数字伙伴正在慢慢成型。