第 12 期 | Gemma + Hermes Agent:本地开源 Agent 全栈

更新于 2026/4/8

好的,作为一名资深技术教育内容作者,我将为您撰写这期关于 Gemma 与 Hermes Agent 的完整 Markdown 教学文章。


副标题:将微调后的 Gemma 接入 Hermes Agent 作为后端 LLM,实现完全本地化、零 API 费用的 AI Agent 体验。

🎯 学习目标

  • 理解本地开源 AI Agent 的核心价值、优势及其在实际应用中的潜力。
  • 掌握 Hermes Agent 框架的基本架构,包括其 LLM 连接器、工具注册与任务编排机制。
  • 学习如何通过 Ollama 将 Google Gemma 模型部署到本地,并将其无缝集成到 Hermes Agent 作为后端语言模型。
  • 实践构建一个端到端、完全本地化、零 API 费用的 AI Agent 应用,能够执行多步骤、需要工具协作的任务。

📖 核心概念讲解

### 12.1 本地 AI Agent 的崛起与优势

随着大型语言模型(LLM)能力的飞速发展,AI Agent 已成为构建智能应用的关键范式。AI Agent 不仅仅是调用 LLM 进行一次性问答,它具备规划、反思、记忆和使用外部工具的能力,以实现更复杂、多步骤的目标。传统上,许多 Agent 框架依赖于云端 LLM 服务(如 OpenAI GPT 系列),这带来了几个挑战:

  1. API 费用:每次与云端 LLM 交互都会产生费用,对于高频或大规模应用来说,成本可能迅速累积。
  2. 数据隐私:敏感数据传输到第三方云服务进行处理,可能面临隐私泄露的风险。
  3. 网络延迟与稳定性:依赖外部网络服务可能导致不稳定的性能和更高的延迟。
  4. 定制化限制:虽然可以通过 Prompt Engineering 进行定制,但无法直接控制 LLM 的底层行为或进行深度微调后的本地部署。

本地 AI Agent 旨在解决这些问题。通过在本地部署开源 LLM(如 Gemma),并结合本地 Agent 框架,我们可以实现:

  • 零 API 费用:一旦模型部署,后续推理不再产生额外费用。
  • 数据主权与隐私:所有数据处理均在本地进行,确保数据不离开您的控制环境。
  • 低延迟与高稳定性:摆脱网络依赖,提供更快的响应速度和更稳定的服务。
  • 深度定制化:能够将经过微调的私有模型接入 Agent,实现特定领域或业务需求的高精度任务。

本地 AI Agent 的架构通常包含以下核心组件:

  • 用户接口 (User Interface):接收用户指令并展示 Agent 输出。
  • Agent 核心 (Agent Core):负责任务规划、决策、反思和流程控制。
  • LLM 连接器 (LLM Connector):与本地部署的 LLM 进行通信的接口。
  • 本地 LLM (Local LLM):在本地运行的大语言模型,负责理解、生成文本和推理。
  • 工具注册与执行 (Tool Registry & Executor):管理可用的外部工具(如计算器、代码解释器、网络搜索、数据库查询等),并根据 Agent 的决策执行相应工具。
  • 记忆模块 (Memory Module):存储 Agent 的历史对话、观察结果和学习到的知识。

以下是本地 AI Agent 的一个简化交互流程图:

graph TD
    A[用户输入] --> B(Agent 核心: 任务规划/决策)
    B --> C{需要 LLM 协助?}
    C -- 是 --> D[LLM 连接器]
    D --> E(本地 Gemma LLM)
    E --> B
    C -- 否 --> F{需要外部工具?}
    F -- 是 --> G[工具执行器]
    G --> H(外部工具: 搜索/计算/API等)
    H --> B
    B --> I(Agent 输出)
    I --> A

### 12.2 Google Gemma:本地大模型基石

Google DeepMind 推出的 Gemma 家族是开源 LLM 领域的里程碑。基于 Gemini 技术,Gemma 提供了从 1B 到 27B 等不同规模的模型,支持多模态(在 Gemma 2/3 中),拥有 128K 的超长上下文窗口,并且可以通过 LoRA/QLoRA 等技术进行高效微调。

为什么选择 Gemma 作为本地 Agent 的 LLM 基石?

  • 开源与免费:Gemma 完全开源,可在本地免费部署和使用,非常适合个人开发者和中小型企业。
  • 性能优异:尽管是开源模型,Gemma 在多项基准测试中展现出与闭源模型相媲美的性能,尤其在推理、代码生成和多语言支持方面表现出色。
  • 多模态能力:Gemma 2/3 版本支持图像-文本多模态,为构建更复杂的 Agent 应用提供了可能。
  • 易于部署:Gemma 可通过 Ollama、Hugging Face Transformers 等多种方式轻松在本地部署,降低了技术门槛。
  • 微调潜力:Gemma 对 LoRA/QLoRA 微调的良好支持,意味着我们可以训练出高度专业化、针对特定任务优化的 Agent LLM。

在本期教程中,我们将使用 Ollama 作为 Gemma 的本地部署平台。Ollama 简化了在本地运行各种开源模型的流程,提供了一个统一的 API 接口,非常适合与 Agent 框架集成。

### 12.3 Hermes Agent:本地 Agent 框架解析

Hermes Agent 是一个为本地化、模块化和可扩展性设计的开源 AI Agent 框架(注:本教程中的 Hermes Agent 是一个概念性框架,旨在演示本地 Agent 的集成模式。其设计灵感来源于 LangChain、CrewAI 等主流 Agent 框架,并针对本地 LLM 集成进行了优化。)。它的核心目标是让开发者能够轻松地将本地部署的 LLM(如 Gemma)与自定义工具结合,构建出功能强大的本地 Agent。

Hermes Agent 的核心组件:

  • LLMConnector 抽象层:Hermes Agent 不直接依赖于特定的 LLM 实现,而是通过 LLMConnector 接口与各种 LLM 后端进行通信。这使得我们可以轻松切换不同的本地 LLM(如 Gemma、Llama 3、Mistral 等),甚至是兼容 OpenAI API 的本地服务。
  • Tool 注册与封装:Agent 的能力很大程度上取决于其能使用的工具。Hermes Agent 提供了简洁的 Tool 封装机制,允许开发者将任何 Python 函数或外部服务包装成 Agent 可调用的工具。每个工具都包含名称、描述和执行逻辑。
  • AgentCore 编排引擎:这是 Agent 的“大脑”,负责:
    • 任务规划 (Planning):根据用户输入和当前状态,制定实现目标的步骤。
    • 工具选择 (Tool Selection):决定在哪个步骤使用哪个工具。
    • 推理 (Reasoning):利用 LLM 的能力进行逻辑推理、理解上下文。
    • 反思与纠错 (Reflection & Correction):根据工具执行结果调整计划。
    • 记忆管理 (Memory Management):维护对话历史和状态。
  • Prompt 模板:Agent 与 LLM 的交互是基于精心设计的 Prompt 模板,通常采用 ReAct (Reasoning and Acting) 或 Chain-of-Thought (CoT) 模式,引导 LLM 思考并决定下一步动作。

以下是 Hermes Agent 的内部工作流示意图:

+---------------------+
|    User Query       |
+---------------------+
          |
          v
+---------------------+
|    Hermes Agent     |
|   (AgentCore)       |
+---------------------+
| 1. 构建 LLM Prompt  | <---+ (历史对话/工具描述)
|    - 任务指令       |
|    - 可用工具描述   |
|    - 当前思考/观察  |
+---------------------+
          |
          v
+---------------------+
|    LLM Connector    |
|  (GemmaOllamaLLM)   |
+---------------------+
          |
          v
+---------------------+
|     Gemma Model     |
|  (本地 Ollama 服务) |
+---------------------+
          |
          v
+---------------------+
|    LLM 输出解析     |
| (识别 Thought, Action, Action Input, Final Answer) |
+---------------------+
          |
          v
+---------------------+
| 2. 决策与执行       |
|    - 若是 Final Answer: 返回结果 |
|    - 若是 Action:      |
|      - 查找对应工具   |
|      - 执行工具       |
|      - 获取 Observation |
|      - 更新 Agent 状态 |
+---------------------+
          |
          v
+---------------------+
|    Agent Output     |
+---------------------+

### 12.4 Gemma + Hermes Agent:全栈集成原理

将 Gemma 与 Hermes Agent 集成的关键在于 LLMConnector。Ollama 在本地启动后,会提供一个 RESTful API 接口(默认端口 11434),允许外部程序通过 HTTP 请求与模型进行交互。Hermes Agent 的 GemmaOllamaLLM 连接器就是利用 Python 的 ollama 客户端库(或直接 requests 库)向这个 API 发送请求。

集成步骤概述:

  1. Ollama 服务启动:确保您的机器上安装并运行了 Ollama 服务。
  2. 拉取 Gemma 模型:使用 ollama pull 命令下载所需的 Gemma 模型。
  3. Hermes GemmaOllamaLLM 实现:创建一个 Python 类,封装 ollama.Client,提供一个 generate 方法,接收 Prompt 并返回 Gemma 的响应。
  4. 工具定义:将 Agent 需要使用的功能(如网络搜索、计算、文件读写等)封装成 Tool 对象,包含名称、详细描述和执行函数。
  5. Agent 编排:在 AgentCore 中,将 GemmaOllamaLLM 实例和 Tool 列表传入,并定义 Agent 的 Prompt 模板,使其能够理解何时调用工具、如何解析工具输出,并最终给出答案。

通过这种方式,Hermes Agent 将 Gemma 作为一个强大的推理引擎,结合外部工具,实现了复杂的任务自动化。整个过程完全在本地完成,确保了数据安全和零成本运行。


💻 实战演示

我们将通过一个具体的例子来演示如何搭建一个本地的 Gemma + Hermes Agent。这个 Agent 将具备:

  1. 通用知识问答能力 (由 Gemma 提供)。
  2. 网络搜索能力 (通过 wikipedia 库模拟,实际可替换为 Google Search API 或自定义爬虫)。
  3. 数学计算能力 (通过 Python eval 函数实现)。

### 12.4.1 环境准备与 Gemma 本地部署

首先,确保您的系统满足运行 Ollama 和 Gemma 的基本要求(足够的 RAM 和 GPU,如果可用)。

  1. 安装 Ollama 访问 Ollama 官方网站,下载并安装适合您操作系统的版本。安装完成后,Ollama 服务会在后台自动运行。

  2. 拉取 Gemma 模型 打开终端或命令行,执行以下命令拉取 Gemma 7B 模型。如果您资源有限,可以选择 gemma:2b

    ollama pull gemma:7b
    # 或
    # ollama pull gemma:2b
    

    等待模型下载完成。您可以通过 ollama list 查看已下载的模型。

  3. 验证 Gemma 运行 您可以在终端尝试与 Gemma 交互,确保其正常工作:

    ollama run gemma:7b "Hello, Gemma! How are you?"
    

    如果 Gemma 返回了响应,说明本地部署成功。

  4. 创建项目目录并安装 Python 依赖

    mkdir hermes_gemma_agent
    cd hermes_gemma_agent
    python -m venv venv
    source venv/bin/activate # macOS/Linux
    # 或 venv\Scripts\activate # Windows
    
    pip install ollama wikipedia
    

    ollama 库用于与 Ollama 服务通信,wikipedia 库将作为我们的一个外部工具。

### 12.4.2 Hermes Agent 框架搭建与配置

我们将创建几个 Python 文件来构建 Hermes Agent 的核心组件。

1. llm_connector.py:Gemma LLM 连接器

# llm_connector.py
import ollama
import json

class GemmaOllamaLLM:
    """
    通过 Ollama 服务连接本地 Gemma 模型的 LLM 连接器。
    """
    def __init__(self, model_name: str = "gemma:7b", base_url: str = "http://localhost:11434"):
        self.model_name = model_name
        self.client = ollama.Client(host=base_url)
        print(f"Initialized GemmaOllamaLLM with model: {self.model_name} at {base_url}")

    def generate(self, prompt: str, temperature: float = 0.0, **kwargs) -> str:
        """
        向 Gemma 模型发送 Prompt 并获取响应。
        """
        try:
            # 使用 ollama.Client 的 generate 方法
            # stream=False 表示一次性获取完整响应
            response = self.client.generate(
                model=self.model_name,
                prompt=prompt,
                stream=False,
                options={
                    "temperature": temperature,
                    "num_gpu": -1 # 尝试使用所有可用GPU加速
                },
                **kwargs
            )
            return response['response'].strip()
        except ollama.ResponseError as e:
            print(f"Ollama Response Error: {e}")
            return f"Error: Failed to get response from Gemma. {e}"
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
            return f"Error: An unexpected error occurred. {e}"

    def __call__(self, prompt: str, temperature: float = 0.0, **kwargs) -> str:
        return self.generate(prompt, temperature, **kwargs)

if __name__ == "__main__":
    # 简单的测试
    gemma_llm = GemmaOllamaLLM(model_name="gemma:2b") # 可以用2b进行快速测试
    test_prompt = "介绍一下 Python 语言。"
    print(f"\n--- Testing GemmaOllamaLLM with prompt: '{test_prompt}' ---")
    response = gemma_llm.generate(test_prompt)
    print(response)

    test_prompt_2 = "给我讲个笑话。"
    print(f"\n--- Testing GemmaOllamaLLM with prompt: '{test_prompt_2}' ---")
    response_2 = gemma_llm(test_prompt_2, temperature=0.0) # 使用 __call__ 方法
    print(response_2)

2. tools.py:Agent 可用的工具定义

# tools.py
import wikipedia
import math
import re

class Tool:
    """
    Agent 可调用的工具的抽象基类。
    """
    def __init__(self, name: str, description: str, func):
        self.name = name
        self.description = description
        self.func = func

    def run(self, *args, **kwargs):
        """执行工具函数并返回结果。"""
        try:
            return self.func(*args, **kwargs)
        except Exception as e:
            return f"Error executing tool '{self.name}': {e}"

# --- 具体工具实现 ---

def search_wikipedia_func(query: str) -> str:
    """
    使用 Wikipedia 进行搜索并返回摘要。
    输入应为搜索查询字符串。
    """
    try:
        # 尝试获取第一句摘要
        summary = wikipedia.summary(query, sentences=2)
        return f"Wikipedia search result for '{query}': {summary}"
    except wikipedia.exceptions.PageError:
        return f"No Wikipedia page found for '{query}'."
    except wikipedia.exceptions.DisambiguationError as e:
        return f"Wikipedia disambiguation error for '{query}'. Options: {e.options[:3]}..."
    except Exception as e:
        return f"An error occurred during Wikipedia search: {e}"

def safe_calculator_func(expression: str) -> str:
    """
    执行安全的数学表达式计算。
    只允许数字、基本运算符、括号和常用数学函数。
    """
    # 限制允许的字符和函数,防止任意代码执行
    allowed_chars = "0123456789.+-*/()% "
    allowed_funcs = {"abs": abs, "round": round, "max": max, "min": min, "sum": sum,
                     "sqrt": math.sqrt, "pow": math.pow, "log": math.log, "exp": math.exp}

    # 检查表达式是否只包含允许的字符
    if not all(c in allowed_chars or c.isalpha() for c in