news 2026/5/8 15:40:15

Vidura框架:为本地大模型构建规划与执行分离的智能体系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vidura框架:为本地大模型构建规划与执行分离的智能体系统

1. 项目概述:一个为本地大模型量身打造的智能体框架

最近在折腾本地部署的大语言模型(LLM),比如Llama、Qwen这些,总感觉少了点什么。模型本身能力很强,但让它干点具体的事,比如“帮我分析一下这个PDF”、“去网上查查最新的新闻然后总结给我”,就显得有点笨拙。你需要自己写脚本去调用各种API,处理不同格式的返回,整个过程非常割裂。直到我遇到了Vidura,一个专门为本地LLM设计的智能体(Agent)框架,它就像给这些“大块头”模型装上了手脚和眼睛,让它们能真正动起来,去执行复杂的任务链。

简单来说,Vidura是一个开源的、模块化的智能体系统。它的核心目标,是让你能在自己的电脑或服务器上,用完全本地化的方式,构建一个能理解你复杂指令、并自动调用各种工具(Tools)来完成任务的AI助手。这些工具可以是读取本地文件、搜索网页、执行计算、调用其他API等等。你不再需要把数据上传到云端,也不需要依赖任何闭源的服务,所有计算和决策都在你的掌控之中。这对于注重隐私、有定制化需求,或者单纯想深入研究Agent机制的开发者来说,吸引力巨大。

我最初被它吸引,是因为它的设计非常“务实”。它没有追求花哨的单一全能模型,而是采用了清晰的“规划-执行”架构。一个专门的“规划器(Planner)”模型(通常是较小、较快的模型)负责拆解你的复杂指令,生成一步步的执行计划;另一个“执行器(Executor)”模型(可以是你的主力大模型)则负责具体执行每一步,调用相应的工具。这种分工不仅效率更高,而且让整个系统的行为更可控、更易调试。接下来,我就结合自己的搭建和踩坑经历,带你彻底拆解Vidura,看看它如何让本地LLM真正“活”起来。

2. 核心架构与设计哲学:为什么是“规划器”与“执行器”分离?

2.1 主流智能体模式的困境与Vidura的解法

在深入Vidura之前,我们先看看常见的智能体实现方式。很多方案是让同一个大模型既做规划,又做执行。你给模型一个提示(Prompt),比如“请总结这篇长文章,并找出其中提到的三个主要产品,然后为每个产品写一句广告语”,模型需要自己在内部思考:“第一步,我得先读懂文章;第二步,提取产品信息;第三步,创作广告语”。然后它再一步步输出结果。

这种方式的问题在于:

  1. 效率低下:让一个动辄70亿、130亿参数的大模型去思考“下一步该做什么”这种元问题,是对算力的浪费。规划任务本身不需要那么强的语言生成能力。
  2. 容易迷失:在复杂的多步任务中,模型可能会“忘记”最初的指令,或者在某一步陷入循环。
  3. 难以控制和调试:整个思考过程是一个黑盒,如果任务失败了,你很难定位是规划阶段出了问题,还是某一步执行出了问题。

Vidura的“规划器-执行器”双模型架构,正是为了系统性地解决这些问题。它将智能体的认知过程进行了清晰的解耦:

  • 规划器(Planner):通常选用一个较小、推理速度快的模型(例如Phi-3-mini, Gemma-2B)。它的唯一职责是理解用户的高层目标,并将其分解成一个线性的、可执行的任务列表(Task List)。规划器不关心具体怎么操作文件、怎么调用搜索引擎,它只输出类似[“读取文件:report.pdf”, “提取关键数据点”, “生成摘要报告”]这样的抽象步骤。
  • 执行器(Executor):这是你的主力大模型(如Llama 3 70B, Qwen2.5 72B)。它接收规划器给出的当前任务,以及相关的上下文(比如上一步的执行结果),然后专注于完成这一个具体任务。执行器知道如何调用具体的工具,因为它被赋予了工具的描述和使用方法。

注意:这种架构的一个巨大优势是灵活性。你可以为规划器和执行器分别选择最适合的模型。规划器追求快和准,执行器追求强和稳。你甚至可以用同一个系列的较小模型做规划器,较大模型做执行器,实现性能和效果的平衡。

2.2 Vidura的核心组件与数据流

理解了双模型架构,我们再看Vidura的具体组件,就一目了然了。其核心数据流如下图所示(概念示意):

  1. 用户接口:你通过Web界面或API发送一个自然语言请求,例如:“基于我/data文件夹下的销售数据CSV文件,分析本季度趋势,并生成一份包含图表的Markdown报告。”
  2. 规划器(Planner):Vidura将这个请求发送给规划器模型。规划器分析后,可能生成如下任务链:
    1. 任务:定位并读取/data目录下的所有CSV文件。 2. 任务:解析CSV文件,识别“日期”和“销售额”列。 3. 任务:计算季度总销售额和月度增长趋势。 4. 任务:使用数据分析库(如pandas)生成销售额趋势图表。 5. 任务:将分析结果和图表路径整合,撰写Markdown报告。
  3. 任务队列与状态机:Vidura内部维护一个任务队列。它从队列中取出第一个任务(“定位并读取CSV文件”),连同必要的上下文(如用户指定的/data路径)一起,准备交给执行器。
  4. 执行器(Executor)与工具调用:执行器模型收到具体任务。它知道自己可以调用的工具,比如list_files(列出文件)、read_file(读取文件)、python_executor(执行Python代码)。它会判断:“要完成‘定位并读取CSV文件’,我需要先调用list_files工具查看/data目录,然后对每个CSV文件调用read_file工具。” 模型会生成一个结构化的工具调用请求。
  5. 工具层(Tools):这是Vidura真正发挥能力的地方。工具是一系列预定义或自定义的函数。当执行器决定调用read_file工具时,Vidura的框架会实际执行对应的Python函数,读取文件内容,并将结果返回给执行器。
  6. 结果聚合与循环:执行器将工具返回的结果(文件内容)作为当前任务的输出。Vidura将这个结果更新到任务上下文中,然后将任务标记为完成,并从队列中取出下一个任务(“解析CSV文件…”),重复步骤4和5,直到所有任务完成。
  7. 最终输出:所有任务执行完毕后,Vidura将最终结果(生成的Markdown报告文本和图表文件路径)返回给用户。

这个流程的关键在于,规划是一次性的,执行是迭代的。规划器只工作一次,生成蓝图;执行器则在一个受控的循环中,一步步地、可靠地实现这个蓝图,并且每一步都有明确的输入输出,极易追踪和调试。

3. 环境部署与核心配置详解

Vidura提供了多种部署方式,包括Docker、本地Python安装等。这里我以最灵活、最便于深度定制的本地Python部署为例,带你走通全流程,并解释每一个关键配置项的意义。

3.1 基础环境搭建与依赖安装

首先,确保你的系统满足基本条件:Python 3.10+,以及足够的磁盘空间存放模型。强烈建议使用虚拟环境。

# 1. 克隆代码仓库 git clone https://github.com/narenaryan/Vidura.git cd Vidura # 2. 创建并激活虚拟环境(以conda为例) conda create -n vidura python=3.10 conda activate vidura # 3. 安装核心依赖 pip install -r requirements.txt

安装过程中,你可能会遇到一些依赖冲突,特别是与PyTorch版本相关的。这里有个实操心得:Vidura的requirements.txt可能指定了某个范围的库版本,但为了与你本地的CUDA驱动兼容,你可能需要手动调整PyTorch的版本。一个稳妥的做法是先安装PyTorch,再安装其他依赖。

# 根据你的CUDA版本,从PyTorch官网获取安装命令,例如对于CUDA 11.8: pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 然后再安装Vidura的其他依赖,有时需要忽略已安装的包 pip install -r requirements.txt --no-deps # 谨慎使用,或手动处理冲突

3.2 模型配置:规划器与执行器的选型与部署

这是Vidura的核心配置。你需要准备两个模型:一个给规划器,一个给执行器。模型需要通过Ollama或vLLM等推理服务器来提供API服务。我推荐使用Ollama,因为它对本地模型的支持最友好,拉取和运行模型非常简单。

1. 部署Ollama并拉取模型:

# 安装Ollama (Linux/macOS) curl -fsSL https://ollama.com/install.sh | sh # 拉取规划器模型(例如,小巧高效的Qwen2.5-Coder-1.5B) ollama pull qwen2.5-coder:1.5b # 拉取执行器模型(例如,能力均衡的Llama 3.1 8B) ollama pull llama3.1:8b

2. 配置Vidura的模型端点:Vidura的配置文件通常是config.yaml或通过环境变量设置。你需要告诉它规划器和执行器的API地址。

# config.yaml 示例 models: planner: model_name: "qwen2.5-coder:1.5b" # 与Ollama拉取的名称一致 base_url: "http://localhost:11434" # Ollama默认地址 api_type: "ollama" executor: model_name: "llama3.1:8b" base_url: "http://localhost:11434" api_type: "ollama" # 工具配置 tools: - name: "web_search" enabled: true config: api_key: ${SERPER_API_KEY} # 建议使用环境变量 - name: "python_executor" enabled: true - name: "file_operations" enabled: true config: allowed_directories: ["/tmp", "/home/user/documents"] # 安全限制!必须设置

关键安全提示file_operations(文件操作)工具非常强大,但也极其危险。务必通过allowed_directories严格限制其可以访问的目录范围,绝对不要设置为根目录/或你的家目录。这是防止恶意指令或模型幻觉导致系统文件被误删改的第一道防线。

3. 模型选型的经验之谈:

  • 规划器:首选代码能力较强的小模型。因为规划任务本质上是将自然语言指令“编译”成一系列结构化步骤,这与代码生成任务相似。Qwen2.5-Coder、Phi-3-mini、DeepSeek-Coder-V2-Lite等都是绝佳选择。参数在3B以下,响应速度极快。
  • 执行器:根据你的任务类型选择。
    • 通用任务:Llama 3/3.1 8B、Qwen2.5 7B/14B,在理解力和工具调用上有很好的平衡。
    • 代码/逻辑密集型任务:继续使用代码模型,如Qwen2.5-Coder-7B、DeepSeek-Coder。
    • 追求极致效果:可以上70B级别的模型,但需要足够的GPU内存(通常需要2张以上24G显存的卡)。

3.3 工具系统的配置与自定义

Vidura自带了一些基础工具,但真正的威力在于自定义工具。工具在代码中通常是一个Python类,继承自基础工具类,并实现_run方法。

示例:创建一个获取天气的自定义工具

# custom_tools/weather_tool.py import requests from vidura.tools import BaseTool from typing import Optional class WeatherTool(BaseTool): name = "get_weather" description = "获取指定城市的当前天气情况。" parameters = { "city": { "type": "string", "description": "城市名称,例如:Beijing, Shanghai", "required": True } } def _run(self, city: str) -> str: """调用公开的天气API查询天气""" # 这里使用一个假设的免费API,实际使用时请替换为真实API(如OpenWeatherMap) # 并注意处理API Key(建议从环境变量读取) try: # 示例URL,仅作演示 url = f"https://api.weather.example.com/current?city={city}" response = requests.get(url, timeout=10) data = response.json() # 解析返回的JSON数据,格式化成自然语言 temp = data.get('temperature', 'N/A') condition = data.get('condition', 'N/A') return f"{city}的当前天气是{condition},气温{temp}摄氏度。" except Exception as e: return f"获取{city}天气失败:{str(e)}"

配置Vidura加载自定义工具:在配置文件中,你需要指定自定义工具的路径。

tool_manager: custom_tool_paths: ["./custom_tools"] # 你的自定义工具目录

编写自定义工具的注意事项:

  1. 描述要清晰准确descriptionparameters的描述是执行器模型决定是否、以及如何调用该工具的依据。务必用自然语言写清楚工具的功能和每个参数的意义。
  2. 错误处理要健壮:在_run方法中,一定要用try-except包裹核心逻辑,并返回友好的错误信息。不能让工具异常导致整个Agent崩溃。
  3. 考虑安全性:如果工具涉及外部API调用、系统命令执行或文件操作,必须进行输入验证和权限控制。例如,上面的天气工具虽然简单,但如果API URL是用户可配置的,就可能存在SSRF(服务器端请求伪造)风险。

4. 实战:构建一个数据分析与报告生成智能体

现在,让我们把上面所有的部分组合起来,实现开头的那个例子:一个能自动分析CSV数据并生成报告的智能体。我们将配置工具、编写提示词,并观察整个执行过程。

4.1 定义任务与工具链

我们的目标是:用户说“分析./sales_data文件夹下本季度的销售数据,并总结趋势”,智能体能自动完成。 我们需要以下工具:

  1. list_files(Vidura内置):列出目录下的文件。
  2. read_file(Vidura内置):读取文件内容。
  3. python_executor(Vidura内置):执行Python代码。这是我们进行数据分析的核心。
  4. (可选)generate_chart(自定义工具):调用matplotlib生成图表。

4.2 配置与提示词工程

1. 规划器提示词(System Prompt)优化:默认的规划器提示词可能不够精准。我们需要微调它,使其更擅长生成与现有工具匹配的任务列表。关键是在系统提示词中清晰列出可用的工具及其功能。

你是一个任务规划专家。你的目标是将用户的复杂请求分解成一个线性的、可执行的任务序列。 可用的工具包括: - list_files(directory_path): 列出指定目录下的文件。 - read_file(file_path): 读取指定文件的内容。 - python_executor(code): 执行一段Python代码,并返回结果。可用于数据分析、计算等。 - generate_chart(data, chart_type): 根据提供的数据和图表类型生成图表。 请遵循以下规则生成任务列表: 1. 每个任务必须对应一个工具的直接应用,或一个简单的、无需工具的步骤(如“汇总结果”)。 2. 任务描述应简洁、明确,包含必要的输入信息(如文件路径)。 3. 考虑任务之间的依赖关系,后一个任务可能需要前一个任务的输出。 4. 如果用户请求涉及数据分析,任务链应包括:定位数据、加载数据、分析数据、呈现结果。 用户请求:{user_input} 请生成任务列表:

2. 执行器提示词(System Prompt)优化:执行器需要精确地调用工具。它的系统提示词需要包含详细的工具使用规范。

你是一个任务执行AI。你拥有调用工具的能力。你的职责是严格根据当前任务描述和上下文,选择并调用正确的工具来完成任务。 工具调用规范: 当你决定调用一个工具时,你必须严格按照以下JSON格式输出,且只输出这个JSON对象: { "tool": "工具名称", "input": { "参数1": "值1", "参数2": "值2" } } 当前任务:{current_task} 历史上下文(上一步结果):{context} 请思考并输出你的行动(直接输出JSON,无需额外解释)。

4.3 完整执行流程与调试观察

启动Vidura服务后,我们通过其Web界面或API发送请求。Vidura的日志会清晰展示每一步,这是我们调试的黄金信息。

假设./sales_data下有Q1_sales.csv

  1. 用户输入:“分析./sales_data文件夹下本季度的销售数据,总结趋势,并生成一个简要报告。”
  2. 规划器输出(日志):
    [Planner] Generated task list: 1. 使用 list_files 工具查看 ./sales_data 目录内容。 2. 使用 read_file 工具读取发现的CSV文件。 3. 使用 python_executor 工具,编写代码解析CSV,计算季度总销售额、月度平均等。 4. 使用 python_executor 工具,编写代码生成趋势摘要文本。 5. 任务:格式化最终报告。
  3. 执行器逐步执行(日志节选):
    [Executor] Processing Task 1: 使用 list_files 工具查看 ./sales_data 目录内容。 [Executor] Tool Call: {"tool": "list_files", "input": {"directory_path": "./sales_data"}} [Tool] list_files returned: ["Q1_sales.csv"] [Executor] Task 1 Result: 目录下找到文件:Q1_sales.csv。 [Executor] Processing Task 2: 使用 read_file 工具读取发现的CSV文件。 [Executor] Tool Call: {"tool": "read_file", "input": {"file_path": "./sales_data/Q1_sales.csv"}} [Tool] read_file returned: (CSV文件内容,此处省略) [Executor] Task 2 Result: 文件内容已加载。 [Executor] Processing Task 3: 使用 python_executor 工具,编写代码解析CSV... [Executor] Tool Call: {"tool": "python_executor", "input": {"code": "import pandas as pd\nfrom io import StringIO\ncsv_data = '''{context}'''\ndf = pd.read_csv(StringIO(csv_data))\ntotal_sales = df['Sales'].sum()\nmonthly_avg = df.groupby('Month')['Sales'].mean().to_dict()\nresult = {'total_sales': total_sales, 'monthly_avg': monthly_avg}\nprint(result)"}} [Tool] python_executor returned: {'total_sales': 150000, 'monthly_avg': {'Jan': 48000, 'Feb': 52000, 'Mar': 50000}}
  4. 最终输出:Vidura将步骤3、4、5的结果整合,返回给用户一份文本报告:“2024年第一季度总销售额为150,000元。其中一月平均48,000元,二月平均52,000元,三月平均50,000元,销售趋势较为平稳,二月略有峰值。”

通过观察这个流程,你可以非常容易地定位问题。如果报告数据不对,你可以检查是步骤3的Python代码写错了,还是步骤2读取的文件不对。这种透明性是单模型Agent难以提供的。

5. 高级技巧、常见问题与性能优化

5.1 提升规划可靠性的技巧

规划器是智能体的“大脑”,它的可靠性直接决定任务能否成功启动。

  • 提供工具上下文:如前所述,在规划器的系统提示词中详细描述工具。你甚至可以提供几个示例(Few-shot Learning)。
  • 任务格式约束:要求规划器以特定格式(如JSON、带编号的列表)输出,便于程序解析。例如:“请以JSON数组格式输出任务列表,每个任务包含iddescriptiondepends_on字段。”
  • 后处理校验:编写简单的规则对规划器输出进行校验。例如,检查任务描述中是否包含了必要的参数(如文件路径),或者任务序列是否存在循环依赖。

5.2 执行器工具调用的精准控制

执行器有时会“幻觉”出不存在工具的参数,或调用格式错误。

  • 结构化输出强制:使用支持JSON模式(JSON Schema)或函数调用(Function Calling)的模型作为执行器。在调用API时,直接传入定义好的工具JSON Schema,让模型严格按格式生成。Ollama和vLLM的最新版本都支持此功能。
  • 输入验证与重试:在Vidura的工具调用层,对执行器输出的“工具调用请求”进行验证。如果格式错误或参数缺失,可以尝试将错误信息连同原任务重新发送给执行器,让其修正。但需设置重试上限(如3次),避免死循环。

5.3 性能优化实战

  1. 缓存:对于频繁读取且不常变动的文件(如配置文件、知识库),可以引入缓存机制。在read_file工具中,先检查文件MD5是否变化,无变化则返回缓存内容。
  2. 并行执行:如果任务链中有多个独立的任务(例如,同时分析A文件和B文件),可以修改调度逻辑,让它们并行执行。Vidura本身是顺序执行,但你可以通过自定义任务调度器来实现。
  3. 模型量化与推理优化
    • 对于执行器模型,使用GPTQ、AWQ或GGUF量化技术,可以大幅减少显存占用,从而在消费级显卡上运行更大模型。
    • 使用vLLM作为推理后端替代Ollama,其连续批处理(Continuous Batching)和PagedAttention技术能极大提高高并发下的吞吐量。将Vidura的executor配置指向vLLM服务器即可。
    executor: model_name: "meta-llama/Llama-3.1-8B" # Hugging Face模型ID base_url: "http://localhost:8000" # vLLM服务器地址 api_type: "openai" # vLLM兼容OpenAI API协议

5.4 常见问题排查清单

问题现象可能原因排查步骤与解决方案
规划器返回“无法分解”或胡言乱语1. 规划器模型能力太弱。
2. 系统提示词不清晰。
3. 输入请求过于模糊。
1. 换用更强的代码小模型(如Phi-3-mini)。
2. 重写系统提示词,加入具体示例。
3. 引导用户给出更具体的指令。
执行器不调用工具,而是用文本回答1. 执行器未正确理解工具调用格式。
2. 系统提示词未强调必须输出JSON。
3. 模型本身不擅长函数调用。
1. 检查执行器API调用参数,确保启用了“JSON模式”或“函数调用”。
2. 强化系统提示词,使用“你必须输出JSON”等强制语句。
3. 换用已知工具调用能力强的模型(如GPT-4o-mini、Claude 3.5 Sonnet的API,或本地微调过的模型)。
工具调用失败(如文件不存在)1. 规划器生成的文件路径错误。
2. 执行器幻觉了不存在的参数。
3. 权限不足。
1. 查看规划器输出日志,确认路径。
2. 在执行器提示词中明确工具的参数结构。
3. 检查Vidura进程的文件系统权限。
任务陷入循环1. 规划器生成的任务存在循环依赖。
2. 某个工具执行失败,但未正确处理错误,导致任务重试卡住。
1. 在规划器输出后添加依赖关系检查逻辑。
2. 完善工具的错误处理,确保任何情况下都返回明确结果(包括错误信息)。在任务调度中,对失败任务进行标记和跳过。
整体响应速度慢1. 模型推理速度慢。
2. 网络延迟(如果模型在远程服务器)。
3. 工具本身执行慢(如网络请求)。
1. 对模型进行量化,或使用更快的推理后端(vLLM)。
2. 将模型部署在本地或同一内网。
3. 为慢速工具设置超时,或考虑异步调用。

5.5 安全加固建议

将强大的工具调用能力开放给LLM,安全是重中之重。

  1. 沙箱化Python执行python_executor是最高风险的工具。务必使用安全的沙箱环境,如Docker容器、seccomp沙箱,或使用RestrictedPython等库限制可用的模块和函数(禁止os,subprocess,sys等)。
  2. 严格的输入过滤:对所有从LLM生成并传递给工具的参数进行过滤和转义,防止命令注入、路径遍历等攻击。
  3. 权限最小化:如之前所述,文件、网络等工具的权限必须控制在最小必要范围。
  4. 审计日志:记录每一个用户请求、规划结果、工具调用详情和执行结果。这不仅是安全审计的需要,也是后期分析和优化Agent表现的宝贵数据。

经过以上配置和优化,你就能获得一个既强大又可控的本地AI智能体。它不再是一个只会对话的模型,而是一个能真正为你自动化处理复杂工作流的智能助手。从自动整理文档、监控日志、生成周报,到处理数据分析流水线,Vidura提供的这套框架,让我们看到了本地大模型应用落地的清晰路径。

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

私募资本如何重塑半导体巨头:从瑞萨电子看产业重组与资本博弈

1. 瑞萨电子的十字路口:当私募股权敲响半导体巨头的大门 在半导体这个周期性极强的行业里,企业的命运往往与资本浪潮紧密相连。2012年夏天,一则来自日本的消息震动了全球半导体产业圈:深陷困境的日本芯片巨头瑞萨电子,…

作者头像 李华
网站建设 2026/5/8 15:39:58

MetaClaw Setup Architect:用自然语言快速构建多智能体系统的实践指南

1. 项目概述:从零到一,用自然语言构建你的多智能体军团如果你和我一样,是个技术出身的创始人或者早期团队的操盘手,每天脑子里塞满了各种产品想法和自动化需求,但一想到要为一个新点子去手动配置一堆AI智能体、编写复杂…

作者头像 李华
网站建设 2026/5/8 15:39:55

零配置代码质量工具链Ultracite:Biome、ESLint、Oxlint一键集成与AI协同

1. 项目概述:为什么我们需要一个“零配置”的代码质量工具链? 如果你和我一样,常年泡在 JavaScript 和 TypeScript 的项目里,那你一定经历过这个循环:新项目启动,信心满满地要搞一套完美的代码规范。于是&…

作者头像 李华
网站建设 2026/5/8 15:39:45

NI与TechShop合作:专业工具民主化如何重塑创客生态

1. 从一次展会到一场运动:当NI遇见TechShop,创客社区的新篇章2013年4月,加州的阳光一如既往地明媚,我站在圣何塞麦肯瑞会议中心门口,刚从Design West展会的人潮中挤出来,脑子里还塞满了各种新奇的芯片和开发…

作者头像 李华
网站建设 2026/5/8 15:39:43

技巧分享:2345清理王的实用使用技巧

2345清理王功能丰富,但是想要用得更顺手,还需要一些实用的小技巧。今天就给大家分享几个使用技巧,让你用起来更顺手,效率更高。 技巧一:定期清理,养成习惯。手机垃圾是每天都会产生的,定期清理…

作者头像 李华