news 2026/5/10 12:04:51

Airweave:构建AI智能体统一上下文检索层的开源解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Airweave:构建AI智能体统一上下文检索层的开源解决方案

1. 项目概述:为什么我们需要一个统一的上下文检索层?

如果你正在构建或使用AI智能体,无论是客服机器人、代码助手还是内部知识库问答系统,一个核心的挑战始终是:如何让AI准确、高效地获取到它回答问题所需的最新、最相关的信息?传统的做法是,为每一个智能体、每一个应用场景,都单独搭建一套数据管道——连接数据源、编写同步脚本、构建向量索引、设计检索接口。这不仅重复造轮子,而且随着数据源增多、业务逻辑复杂化,整个系统会变得异常脆弱和难以维护。

Airweave的出现,正是为了解决这个痛点。它将自己定位为“AI智能体和RAG系统的开源上下文检索层”。你可以把它想象成一个智能的、统一的数据枢纽。它的核心工作不是生成内容,而是专门负责“找东西”——把你散落在各个SaaS应用(如Notion、Slack、Google Drive)、数据库和文档中的信息,持续地同步、索引,并通过一个对LLM友好的统一搜索接口暴露出来。当你的智能体需要上下文时,不再需要直接去敲几十个不同API的门,只需要问Airweave要,它就能从所有已连接的数据源中,一次性找到最相关的片段。

这带来的价值是显而易见的。对于开发者而言,你无需再为每个项目重建脆弱的数据管道,可以专注于智能体本身的逻辑和体验。对于企业而言,这意味着可以安全、可控地将内部数据赋能给AI,而不必担心数据泄露或同步混乱。Airweave既提供了开箱即用的云服务,也支持完全自托管,给了团队充分的灵活性和控制权。接下来,我将从架构设计、核心功能、实战部署到高级用法,为你完整拆解这个项目。

2. 核心架构解析:Airweave如何实现统一检索?

要理解Airweave的威力,必须深入其架构。它不是一个简单的爬虫加向量数据库,而是一个为生产环境设计的、模块化的检索基础设施。其设计哲学是“各司其职,通过工作流串联”。

2.1 分层架构与数据流

Airweave的架构可以清晰地分为四层:连接层、处理层、服务层和消费层。

连接层是项目的基石,目前支持超过50种数据源连接器。这些不是简单的API封装,而是具备增量同步、变更捕获和认证管理的成熟连接器。例如,对于Notion,连接器会利用其官方API的增量更新特性,只拉取自上次同步以来修改过的页面;对于数据库(如PostgreSQL),则可能通过监听WAL日志或使用时间戳字段来高效获取增量数据。这一层确保了数据能以最小的延迟和资源消耗流入系统。

处理层是数据加工的车间。原始数据(可能是JSON、HTML、PDF文本)进入后,会经过一系列标准化处理:

  1. 内容提取与清洗:从原始格式中提取纯文本和关键元数据(如作者、更新时间、来源URL)。
  2. 分块:根据文档类型(代码、长文、表格)采用不同的分块策略,平衡上下文完整性与检索精度。
  3. 向量化:使用集成的嵌入模型(如OpenAI的text-embedding-3-small、本地运行的BGE模型)将文本块转换为向量。Airweave支持多向量索引,允许你为同一段文本生成不同维度的向量,以优化不同查询类型的召回率。
  4. 索引与存储:处理后的文本块、元数据和向量被分别存储。Airweave使用PostgreSQL存储所有元数据和关系,使用Vespa作为向量搜索引擎。选择Vespa而非单纯的向量数据库(如Pinecone)是一个关键设计,因为Vespa支持复杂的多模态搜索,可以轻松融合关键词匹配(BM25)、向量相似度过滤和元数据过滤,实现混合搜索。

服务层以FastAPI构建的RESTful API为核心,对外提供所有功能:数据源管理、同步触发、集合(Collection)查询等。更重要的是,它集成了Temporal工作流引擎。每一个数据同步任务、一个复杂的ETL流程,都被建模为一个Temporal工作流。这带来了巨大的可靠性优势:工作流状态持久化,任何中断(如网络抖动、服务重启)都可以从中断点自动恢复,绝不会丢失数据或造成重复处理。

消费层提供了多种接入方式。除了标准的Python/TypeScript SDK和REST API,Airweave还原生支持MCP(Model Context Protocol)。这意味着它可以无缝接入像Cursor、Claude Desktop这样支持MCP的AI智能体平台,智能体无需任何额外配置就能直接查询Airweave。CLI工具则满足了运维和脚本化操作的需求。

2.2 技术选型背后的考量

这个技术栈的选择体现了对生产级可靠性和性能的追求。

  • FastAPI:提供了高性能、自动化的API文档(OpenAPI),非常适合构建需要与多种客户端交互的开发者工具。
  • PostgreSQL + Vespa:这是一个“黄金组合”。PostgreSQL负责需要强一致性和复杂关联查询的元数据;Vespa作为搜索引擎,负责需要低延迟、高并发的向量和全文检索。两者结合,兼顾了ACID事务与搜索性能。
  • Temporal:将复杂的异步任务(如同步50个数据源)从脆弱的Cron作业和自定义队列中解放出来。Temporal保证了工作流的可观测性、可重试性和持久性,这是企业级数据管道不可或缺的特性。
  • Docker Compose / Kubernetes:分别针对开发和生产环境。一键式的docker-compose让开发者能在几分钟内搭建起完整环境;K8s配置则为大规模、高可用的生产部署铺平了道路。

注意:自托管部署时,这套技术栈对机器资源有一定要求。尤其是Vespa和Temporal,都是相对重量的服务。建议准备至少4核CPU、8GB内存的服务器进行体验,生产环境则需要根据数据量和查询QPS进行规划。

3. 从零开始:本地部署与核心功能实战

理论说得再多,不如亲手跑起来。Airweave的本地部署体验做得非常流畅,我们一步步来。

3.1 环境准备与一键启动

前提条件很简单:安装好Docker和Docker Compose。之后的操作就是标准的克隆仓库和启动脚本。

git clone https://github.com/airweave-ai/airweave.git cd airweave ./start.sh

这个start.sh脚本是个“智能管家”,它帮你做了以下几件关键事:

  1. 环境配置:复制.env.example.env,并自动生成用于数据加密的ENCRYPTION_KEY和会话管理的STATE_SECRET。这是安全性的第一步,确保本地数据也是加密存储的。
  2. 依赖检查:确保Docker守护进程正在运行,并检查必要的端口(如8080、5432、6333等)是否被占用。
  3. 服务启动:通过docker-compose up -d启动所有服务容器。包括前端(React)、后端(FastAPI)、PostgreSQL、Vespa、Temporal、Redis等。
  4. 健康检查:脚本会持续轮询各服务的健康接口,直到所有服务都报告“健康”状态。首次启动因为要初始化数据库和下载镜像,可能需要2-3分钟,请耐心等待。
  5. 密钥引导(可选):如果检测到没有配置AI模型API密钥,脚本会交互式地询问你是否要输入OpenAI或Mistral的API密钥。这对于后续使用嵌入模型和LLM进行内容处理是必需的。

当脚本提示所有服务健康后,打开浏览器访问http://localhost:8080,你就看到了Airweave的Web管理界面。

3.2 连接第一个数据源:以GitHub为例

登录后(首次使用需要注册一个本地管理员账户),我们尝试连接一个数据源。这里以GitHub为例,因为它非常普遍。

  1. 添加连接器:在控制台点击“Add Source”,在列表中找到GitHub图标。
  2. OAuth授权:点击后,你会被引导至GitHub进行OAuth授权。这里体现了Airweave的一个设计亮点:它通常使用OAuth而非个人访问令牌(PAT)来建立连接。这样做更安全(权限可管理、可撤销),并且能自动处理令牌刷新。
  3. 配置同步范围:授权成功后,你需要配置同步哪些数据。例如,是同步整个组织(Organization)下的所有仓库,还是特定仓库?是同步Issues、Pull Requests还是代码文件?你可以根据智能体需要的信息范围进行勾选。
  4. 触发首次全量同步:保存配置后,Airweave会立即触发一次全量同步。你可以在“Activity”页面看到Temporal工作流的执行详情,包括拉取、分块、向量化的进度。

实操心得:在配置数据源时,务必利用好“Sync Rules”(同步规则)和“Selective Sync”(选择性同步)功能。比如,你只关心某个仓库下docs/目录的Markdown文件,或者只同步最近一年的Issues。在一开始就做好过滤,能极大减少不必要的索引数据量,提升后续检索速度和精度。

3.3 创建集合与进行首次检索

数据同步完成后,它们还只是原始素材。我们需要创建一个“集合”(Collection)来组织它们。集合是Airweave的核心抽象,你可以把它理解为一个虚拟的、跨数据源的“知识库”。

  1. 创建集合:在“Collections”页面,点击“Create Collection”。给它起个名字,比如engineering-knowledge
  2. 添加数据源:在集合的配置中,将刚才同步的GitHub数据源添加进来。你还可以继续添加其他数据源,比如团队的Confluence空间、Slack的某个频道。这样,一个集合就聚合了多个来源的数据。
  3. 进行搜索测试:集合创建并索引完成后(Vespa构建索引需要一点时间),你就可以在Web界面的搜索框进行测试了。尝试搜索“如何部署微服务?” Airweave会从你关联的GitHub仓库的README、Confluence的运维文档中,返回相关的代码片段和文档段落。

关键概念解析:统一搜索接口这个搜索框的背后,是Airweave提供的统一搜索API。无论底层数据来自GitHub的Markdown、Confluence的HTML还是PDF的扫描文本,经过处理后,它们都以统一的文本块(附带来源、更新时间等元数据)格式被索引。你的查询会同时触发:

  • 关键词搜索:在文本块中匹配关键词。
  • 向量语义搜索:计算查询语句与文本块向量的余弦相似度。
  • 元数据过滤:例如,可以限定只搜索“最近一个月更新的”、“来自Confluence的”内容。

最终结果是一个按相关性排序的混合列表。你可以通过SDK,以完全相同的接口和查询逻辑来获取这些结果。

4. 深度集成:在AI智能体中使用Airweave SDK

Web界面适合管理和探索,但Airweave真正的威力在于通过代码集成。我们来看看如何在你自己的AI应用中使用它。

4.1 Python SDK基础用法

首先安装SDK:pip install airweave-sdk。假设你已经有一个智能体,需要在回答用户关于项目进度的问题时,获取最新的任务信息。

from airweave import AirweaveSDK import os # 初始化客户端,API Key可以从环境变量或管理界面获取 client = AirweaveSDK(api_key=os.getenv("AIRWEAVE_API_KEY")) def get_project_context(user_query: str) -> list: """ 从Airweave检索与项目相关的上下文。 """ try: # 搜索名为“project-x”的集合 search_results = client.collections.search.instant( readable_id="project-x", # 你创建的集合名称 query=user_query, limit=5, # 返回最相关的5条结果 # 可以添加过滤器,例如只取最近7天的数据 filters={ "updated_at": {"gte": "now-7d"} } ) # search_results 是一个包含多个“命中项”的列表 contexts = [] for hit in search_results.hits: # 每个hit包含文本内容、来源、相关性分数等 context_text = f"来源:{hit.source_name} ({hit.document_title})\n内容:{hit.text}\n" contexts.append(context_text) return contexts except Exception as e: print(f"检索上下文时出错:{e}") return []

4.2 构建一个基于上下文的问答智能体

现在,我们将检索到的上下文注入到大语言模型(如OpenAI GPT)的提示词中,构建一个完整的RAG流程。

from openai import OpenAI from airweave import AirweaveSDK import os # 初始化客户端 airweave_client = AirweaveSDK(api_key=os.getenv("AIRWEAVE_API_KEY")) openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) def answer_with_airweave(question: str, collection_id: str) -> str: """ 1. 从Airweave检索相关上下文。 2. 将上下文与问题组合成提示词。 3. 调用LLM生成答案。 """ # 步骤1:检索 search_results = airweave_client.collections.search.instant( readable_id=collection_id, query=question, limit=3 ) if not search_results.hits: return "抱歉,在现有知识库中没有找到相关信息。" # 构建上下文字符串 context_str = "\n---\n".join([ f"【来源:{hit.source_name} - {hit.document_title}】\n{hit.text}" for hit in search_results.hits ]) # 步骤2:构建提示词(采用常见的RAG提示模板) system_prompt = """你是一个专业的助手,请严格根据提供的上下文信息来回答问题。如果上下文中的信息不足以回答问题,请直接说明“根据现有资料无法回答”。不要编造信息。""" user_prompt = f"""请基于以下上下文信息回答问题。 上下文信息: {context_str} 问题:{question} 请给出答案:""" # 步骤3:调用LLM response = openai_client.chat.completions.create( model="gpt-4-turbo-preview", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], temperature=0.1 # 低温度,让答案更贴近上下文 ) return response.choices[0].message.content # 使用示例 question = "我们项目下一阶段的主要目标是什么?" answer = answer_with_airweave(question, "project-x-roadmap") print(answer)

这个简单的流程展示了Airweave的核心价值:它将复杂的多源数据检索,简化成了一个简单的函数调用。你的智能体无需关心数据在哪里、格式是什么,只需要提问。

4.3 高级功能:异步搜索与流式响应

对于需要更复杂交互或实时性的场景,SDK提供了更多高级功能。

异步搜索:当你的查询可能涉及大量数据或复杂过滤时,可以使用异步搜索,避免阻塞主线程。

# 发起一个异步搜索任务 async_search = client.collections.search.create( readable_id="large-collection", query="分析去年的销售数据趋势", filters={"year": 2023} ) task_id = async_search.task_id # 稍后通过任务ID获取结果 results = client.tasks.retrieve(task_id)

流式响应(实验性):对于需要逐段生成答案的对话场景,你可以结合Airweave的检索和LLM的流式输出。

# 伪代码,展示思路 contexts = retrieve_from_airweave(query) for chunk in stream_llm_response_with_context(contexts, query): # 逐块向客户端输出 yield chunk

5. 生产环境考量:部署、监控与安全

将Airweave用于内部工具原型是一回事,将其部署为支撑关键业务智能体的生产级服务则是另一回事。这里有几个必须考虑的重点。

5.1 部署模式选择

  • Airweave Cloud(SaaS):最省心的方式。你无需管理服务器、数据库和运维。数据通过安全的OAuth连接和端到端加密传输到云端。适合初创团队、快速验证场景或数据安全要求可接受SaaS模式的场景。
  • 自托管(推荐用于企业):这是很多企业的选择,因为数据可以完全留在自己的VPC内。Airweave提供了完整的Docker Compose和Kubernetes(K8s)部署清单。
    • Docker Compose:适合单节点部署和测试。你需要自行处理数据备份、服务监控和更新。
    • Kubernetes:适合高可用、可扩展的生产环境。你需要配置Ingress、持久化存储卷(Persistent Volumes)、资源限制(Resources Limits)和水平Pod自动伸缩(HPA)。项目仓库中的k8s/目录提供了基础的配置示例,但你需要根据自身的K8s环境进行调整。

5.2 数据同步与索引策略

在生产中,你需要精心设计同步策略,平衡数据的实时性和系统负载。

  • 同步频率:不是所有数据都需要实时同步。对于频繁变动的数据源(如客服聊天记录),可以设置较高的同步频率(如每5分钟)。对于相对静态的知识库(如产品手册),可以设置为每天一次。
  • 增量同步与全量同步:Airweave的连接器大多支持增量同步。务必启用此功能。定期(如每周)安排一次全量同步,以修正可能因增量逻辑漏洞导致的数据不一致。
  • 索引优化:Vespa的索引性能与资源配置和索引结构有关。对于海量数据(数千万文档),需要考虑分片(partitioning)和副本(replication)。在docker-compose.yml或K8s配置中,为Vespa容器分配足够的内存和CPU。

5.3 安全与权限控制

这是企业级应用的生命线。

  • 网络隔离:在自托管部署中,确保Airweave的后端服务(特别是PostgreSQL和Vespa)不直接暴露在公网。通过内部网络或VPN访问。
  • API密钥管理:为不同的客户端应用(如不同的智能体)创建不同的API密钥,并设置适当的权限范围(Scope)。定期轮换密钥。
  • 数据源访问控制:当连接像Google Workspace或GitHub Enterprise这样的数据源时,在OAuth授权环节,遵循“最小权限原则”,只授予Airweave所需的最小范围权限(如只读权限)。
  • 内容审查与脱敏:在将数据源接入Airweave之前,考虑是否需要对某些敏感信息(如个人身份证号、内部财务数据)进行脱敏处理。可以在数据进入Airweave之前通过预处理脚本完成,或者利用Airweave未来可能提供的插件机制在索引前处理。

5.4 监控与日志

一个健康的系统离不开可观测性。

  • 服务健康度:利用/health端点监控所有微服务的状态。集成到Prometheus + Grafana中。
  • 业务指标:监控关键指标,如:各数据源同步成功率、平均同步延迟、查询QPS、查询延迟P99、向量索引大小增长情况。
  • 日志聚合:将所有容器的日志收集到ELK(Elasticsearch, Logstash, Kibana)或Loki中。特别关注Temporal工作流的错误日志和Vespa的慢查询日志。

6. 常见问题与故障排查实录

在实际部署和使用中,你肯定会遇到各种问题。这里记录了一些典型场景和解决思路。

6.1 部署与启动问题

问题1:运行./start.sh时,某个服务(如Vespa)一直无法健康启动。

  • 排查:首先查看该服务的日志:docker logs airweave-vespa。常见原因是端口冲突或资源不足。
  • 解决
    • 端口冲突:检查docker-compose.yml中定义的服务端口(如6333, 8081)是否被本机其他程序占用。修改docker-compose.yml中的端口映射或关闭冲突程序。
    • 内存不足:Vespa和PostgreSQL对内存有一定要求。确保你的Docker Desktop或宿主机分配了足够的内存(建议至少4GB)。可以在docker-compose.yml中为服务设置mem_limit
    • 初始化超时:首次启动Vespa需要下载模型和初始化索引,在慢速网络下可能超时。可以尝试增加start.sh脚本中的健康检查超时时间,或手动拉取镜像docker pull vespaengine/vespa

问题2:前端(localhost:8080)可以访问,但无法登录或添加数据源。

  • 排查:检查后端API服务是否正常。打开浏览器开发者工具(F12),查看网络(Network)选项卡,尝试登录时哪个API请求失败了(通常是/api/v1/下的接口)。
  • 解决
    • 查看后端日志:docker logs airweave-backend。常见问题是数据库连接失败或环境变量未正确加载。
    • 确认.env文件中的DATABASE_URL等配置是否正确指向了正在运行的PostgreSQL容器。
    • 尝试重启后端服务:docker-compose restart backend

6.2 数据同步问题

问题3:GitHub/Notion数据源同步一直显示“Pending”或失败。

  • 排查:前往Web界面的“Activity”页面,查看该同步任务的具体工作流执行详情。Temporal的UI(通常运行在localhost:7233)提供了更详细的任务历史和错误堆栈。
  • 解决
    • OAuth令牌失效:这是最常见的原因。去数据源配置页面,尝试“重新授权”(Re-authorize)。
    • 权限不足:确认OAuth授权时授予了足够的权限(Scope)。例如,同步GitHub私有仓库需要repo权限。
    • 速率限制:某些API(如GitHub)有严格的速率限制。Airweave的连接器通常会处理,但如果同步量巨大,仍可能触发。解决方案是降低同步频率,或在数据源配置中减少同步范围。

问题4:同步成功,但搜索不到内容。

  • 排查
    1. 确认数据是否真的进入了集合。在集合详情页,查看“Documents”计数是否增加。
    2. 确认Vespa索引是否构建完成。检查Vespa服务日志,或通过Vespa的查询API(http://localhost:8081/search/)直接测试。
    3. 检查搜索查询语句。尝试一个非常简单的、肯定存在于文档中的关键词。
  • 解决
    • 如果是索引延迟,等待几分钟再试。Vespa处理大批量文档需要时间。
    • 检查文档的分块和向量化是否正常。对于某些特殊格式文件(如加密PDF、损坏的图片),处理可能会失败。查看后端处理Worker的日志。

6.3 搜索与性能问题

问题5:搜索响应慢,尤其是返回大量结果时。

  • 排查:通过SDK或API发起搜索时,记录响应时间。同时监控服务器资源(CPU、内存、磁盘IO)。
  • 解决
    • 优化查询:避免过于宽泛的查询。使用过滤器(filters)缩小范围,例如按时间、按数据源类型过滤。
    • 限制返回数量:合理设置limit参数,不要一次性请求过多结果。
    • 升级资源配置:如果数据量持续增长,考虑为Vespa节点增加CPU和内存。对于自托管部署,可以研究Vespa的索引调优参数。
    • 启用缓存:对于热点查询,可以在应用层(如使用Redis)对搜索结果进行缓存。

问题6:搜索结果相关性不高,总是返回不匹配的内容。

  • 排查:分析返回的结果。是关键词匹配错了,还是语义理解有偏差?
  • 解决
    • 调整搜索策略:Airweave的搜索API通常支持设置search_mode,可以在hybrid(混合)、vector(向量)、keyword(关键词)之间调整。对于事实性强的查询,可以增加关键词搜索的权重。
    • 优化分块策略:默认的分块大小和重叠窗口可能不适合你的文档类型。如果文档是短小的代码片段,可以减小分块大小;如果是长技术文档,可以增加重叠窗口以避免上下文断裂。这通常需要在数据源或集合级别进行配置。
    • 微调嵌入模型:如果使用的是可自托管的嵌入模型(如BGE),可以考虑用你自己的领域数据对模型进行微调,以提升语义理解的准确性。

6.4 集成与开发问题

问题7:在Python代码中调用SDK时,出现SSL证书验证错误。

  • 解决:如果你在自签名证书的环境中使用自托管的Airweave,需要在初始化SDK时禁用SSL验证(仅限测试环境!)。
    client = AirweaveSDK( api_key="your_key", base_url="https://your-selfhosted-airweave.com", verify_ssl=False # 生产环境切勿使用! )
    生产环境应配置正确的CA证书。

问题8:如何清理测试数据或重置整个环境?

  • 解决:使用项目提供的清理脚本。
    ./start.sh --destroy # 停止并删除所有容器、卷、网络 docker system prune -af # 清理所有Docker缓存(谨慎使用)
    注意,--destroy会删除所有数据库数据,不可恢复。

最后,一个最实用的建议:加入Airweave的Discord社区。开发团队和活跃用户都在那里,你遇到的绝大多数问题,很可能已经有人遇到过并找到了解决方案。开源项目的魅力就在于这种共同成长的生态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/10 12:04:50

AI编程助手外脑:用Gemini CLI与MCP协议优化代码库分析

1. 项目概述:一个为AI开发助手减负的“外脑” 如果你和我一样,日常重度依赖 Claude Code、Cursor 或者 GitHub Copilot 这类 AI 编程助手,那你肯定也遇到过这个头疼的问题:想让 AI 帮你分析一个庞大的代码库,比如理清整…

作者头像 李华
网站建设 2026/5/10 12:02:52

TikTok评论采集终极指南:3分钟获取完整评论数据的简单方法

TikTok评论采集终极指南:3分钟获取完整评论数据的简单方法 【免费下载链接】TikTokCommentScraper 项目地址: https://gitcode.com/gh_mirrors/ti/TikTokCommentScraper 还在为手动复制抖音评论而烦恼吗?TikTokCommentScraper是你的零代码解决方…

作者头像 李华
网站建设 2026/5/10 12:01:03

5分钟构建拼多多数据采集系统:Scrapy-Pinduoduo架构解析与实战应用

5分钟构建拼多多数据采集系统:Scrapy-Pinduoduo架构解析与实战应用 【免费下载链接】scrapy-pinduoduo 拼多多爬虫,抓取拼多多热销商品信息和评论 项目地址: https://gitcode.com/gh_mirrors/sc/scrapy-pinduoduo 在电商数据驱动决策的时代&#…

作者头像 李华
网站建设 2026/5/10 12:01:01

通过Taotoken CLI工具一键配置多开发环境提升团队协作效率

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过Taotoken CLI工具一键配置多开发环境提升团队协作效率 当团队需要统一接入多个大模型服务时,确保每位成员使用相同…

作者头像 李华