写点什么

Jarvis Agent 功能扩展

作者:SkyFire
  • 2025-07-28
    中国香港
  • 本文字数:5880 字

    阅读完需:约 19 分钟

Jarvis Agent功能扩展

项目地址:https://github.com/skyfireitdiy/Jarvis

5. 功能扩展

Jarvis 的强大之处在于其高度的可扩展性。通过创建自己的工具和平台,您可以让 Jarvis 与您的私有 API、内部数据库、专有系统或任何您需要的服务进行交互。


本章将指导您完成创建、测试和部署自定义功能的全过程。Jarvis 支持三种主要的功能扩展方式:


  1. 标准工具开发:通过创建符合规范的 Python 类来定义新工具。

  2. MCP 工具集成:通过配置文件连接到外部的“模型上下文协议”(Model Context Protocol),快速集成用任何语言编写的现有工具集。

  3. 平台扩展:通过创建 Python 类来集成新的大语言模型平台。



5.1 标准工具开发 (Python 类)

这是最常用、最直接的工具开发方式。

工具加载位置

Jarvis 启动时会从以下位置自动扫描并加载工具文件 (.py):


  1. 内置工具: src/jarvis/jarvis_tools/ (Jarvis 源码内部)

  2. 用户自定义工具: ~/.jarvis/tools/ (推荐存放您个人工具的地方)

  3. 额外工具目录: 由 JARVIS_TOOL_LOAD_DIRS 环境变量指定的目录列表。

通过 Git 共享和同步工具

与方法论类似,JARVIS_TOOL_LOAD_DIRS 的设计也考虑到了团队协作和版本控制。您可以将一个或多个包含 Jarvis 工具的 Git 仓库作为共享的工具库。


  1. 创建工具仓库: 创建一个 Git 仓库,用于存放团队共享的工具 (.py 文件)。

  2. 克隆仓库: 团队成员将此仓库克隆到本地。

  3. 设置环境变量: 设置 JARVIS_TOOL_LOAD_DIRS 指向该仓库的本地路径。可以指定多个路径,用逗号分隔。


# 例如,将团队的工具仓库克隆到 ~/git/team-toolsgit clone https://github.com/your-org/team-tools.git ~/git/team-tools
# 在 ~/.bashrc 或 ~/.zshrc 中设置环境变量export JARVIS_TOOL_LOAD_DIRS="~/git/team-tools"
复制代码


设置完成后,Jarvis 不仅会从该目录加载工具,还会**每日自动执行 git pull**,拉取最新的工具代码,确保团队成员的工具集保持同步。

工具类的核心规范

一个标准的工具类必须遵循以下规范:


  1. 文件名与工具名匹配 (关键): 工具文件名 (不含 .py 后缀) 必须与工具类内部的 name 属性完全一致。这是 Jarvis 能够找到并注册工具的硬性约定。

  2. 正确: 文件 my_tool.py -> 类属性 name = "my_tool"

  3. 错误: 文件 my_tool.py -> 类属性 name = "MyTool"name = "some_other_name"

  4. 类属性:

  5. name: (字符串) 工具的唯一名称,与文件名匹配。

  6. description: (字符串) 工具功能的清晰描述。AI 会根据这个描述来判断何时使用该工具。

  7. parameters: (字典) 定义工具所需参数的 JSON Schema。

  8. 类方法:

  9. execute(self, args): (方法) 工具的实际执行逻辑。

  10. check(): (可选的 @staticmethod) 一个静态方法,用于在加载工具前进行前置检查。如果 check() 方法存在并且返回 False,该工具将被跳过,不会加载。这可以用于检查依赖项是否存在、API 密钥是否配置等。

开发步骤

让我们创建一个新工具,它能读取 .ini 配置文件。

第 1 步:创建工具文件

根据命名约定,我们想创建一个名为 read_ini_section 的工具,所以文件名必须是 read_ini_section.py


~/.jarvis/tools/ 目录下创建 read_ini_section.py 文件。

第 2 步:编写工具类

read_ini_section.py 文件中,写入以下代码:


import configparserfrom typing import Dict, Any
class ReadIniSectionTool: # 1. 工具名称 (必须与文件名 "read_ini_section" 匹配) name = "read_ini_section" # 2. 工具描述 (给AI看,必须清晰) description = "读取 .ini 格式的配置文件中指定 section 的所有内容。" # 3. 参数定义 (使用JSON Schema) parameters = { "type": "object", "properties": { "file_path": { "type": "string", "description": "要读取的 .ini 文件的完整路径。" }, "section_name": { "type": "string", "description": "要读取的配置项所在的 section 名称。" } }, "required": ["file_path", "section_name"] }
# 4. (可选) 前置检查 @staticmethod def check() -> bool: # 这里的依赖 'configparser' 是标准库,所以我们直接返回 True。 # 如果你的工具依赖第三方库,可以在这里检查。 # try: # import some_dependency # except ImportError: # print("警告: 'some_dependency' 未安装,'read_ini_section' 工具已禁用。") # return False return True
# 5. 执行逻辑 def execute(self, args: Dict[str, Any]) -> Dict[str, Any]: file_path = args.get("file_path") section_name = args.get("section_name")
try: if not file_path or not section_name: raise ValueError("错误:必须同时提供 file_path 和 section_name。")
config = configparser.ConfigParser() if not config.read(file_path): raise FileNotFoundError(f"错误:配置文件 '{file_path}' 不存在或无法读取。")
if not config.has_section(section_name): raise ValueError(f"错误:在配置文件中未找到名为 '{section_name}' 的 section。")
section_content = dict(config.items(section_name)) return { "success": True, "stdout": str(section_content), "stderr": "" } except Exception as e: return { "success": False, "stdout": "", "stderr": f"执行工具时发生错误: {str(e)}" }
复制代码

第 3 步:测试工具

无需额外注册,只需重新启动 jarvis (jvs),新工具就会被自动加载。


  1. 检查加载: 输入 'ToolUsage' 查看工具列表,确认 read_ini_section 已在其中。

  2. 自然语言测试: 给 Jarvis 一个任务,例如:请帮我读取 /path/to/my/app.ini 文件中 [database] section 的内容。



5.2 MCP 工具集成

MCP (Model Context Protocol) 是一种强大的机制,允许 Jarvis 通过标准输入/输出 (stdio) 或 HTTP 等方式与外部工具集进行通信。这意味着您可以用任何语言(如 Go, Rust, Node.js)编写工具,并将它们作为一个整体接入 Jarvis。

配置方法

MCP 工具通过 config.yaml 文件中的 JARVIS_MCP 列表进行配置。每个列表项都定义了一个 MCP 客户端。


Jarvis 支持以下三种类型的 MCP 客户端:

1. stdio (标准输入输出)

用于集成在本地运行的、通过标准输入输出进行通信的命令行程序。


  • 工作流程: Jarvis 启动时会执行您指定的 command,然后通过 stdin 发送 JSON-RPC 请求,并通过 stdout 接收响应。

  • 适用场景: 将现有的、独立的命令行工具快速封装为 Jarvis 可用的工具集。


配置参数:


  • type: "stdio" (必须)

  • name: 为此工具集指定的唯一名称 (必须)

  • command: 启动工具程序的完整命令 (必须)

  • enable: 是否启用此配置 (可选, 默认为 true)


示例:


JARVIS_MCP:  - type: "stdio"    name: "my_cli_tools"    command: "/path/to/your/tool_program --json-rpc"    enable: true
复制代码

2. sse (Server-Sent Events)

用于连接支持 Server-Sent Events 的远程 HTTP 服务器。SSE 允许服务器向客户端推送事件流。


  • 工作流程: Jarvis 会向 base_url 发起一个持久的 HTTP 连接,并通过这个连接接收来自服务器的事件流。

  • 适用场景: 需要与远程服务进行实时、长连接通信的场景。


配置参数:


  • type: "sse" (必须)

  • name: 唯一的名称 (必须)

  • base_url: 远程 MCP 服务器的基础 URL (必须)

  • api_key: 用于认证的 API 密钥 (可选)

  • api_key_header: 发送 API 密钥时使用的 HTTP 请求头名称 (可选, 默认为 X-API-Key)

  • enable: 是否启用 (可选, 默认为 true)


示例:


JARVIS_MCP:  - type: "sse"    name: "realtime_data_service"    base_url: "https://api.example.com/mcp"    api_key: "your-secret-api-key"    api_key_header: "Authorization" # 例如,如果需要 "Authorization: your-secret-api-key"    enable: true
复制代码

3. streamable (流式 HTTP)

用于连接支持标准流式 HTTP 响应的远程服务器。


  • 工作流程: Jarvis 发送一个标准的 HTTP POST 请求,服务器以流的形式返回响应体。这比 SSE 更简单,但只能单向(服务器到客户端)流式传输。

  • 适用场景: 调用返回大量数据、适合分块读取的远程 API。


配置参数:


  • type: "streamable" (必须)

  • name: 唯一的名称 (必须)

  • base_url: 远程 MCP 服务器的基础 URL (必须)

  • enable: 是否启用 (可选, 默认为 true)


示例:


JARVIS_MCP:  - type: "streamable"    name: "large_file_processor"    base_url: "https://stream.example.com/api"    enable: true
复制代码



5.3 平台扩展 (Python 类)

除了工具和 MCP,Jarvis 还允许您集成新的大语言模型平台。这使您可以连接到专有模型、内部部署的 LLM 服务或任何未被官方支持的 API。

平台加载位置

Jarvis 会从以下位置自动加载平台适配器 (.py):


  1. 内置平台: src/jarvis/jarvis_platform/ (Jarvis 源码内部)

  2. 用户自定义平台: ~/.jarvis/platforms/ (推荐存放您个人平台适配器的地方)

平台类的核心规范

一个标准的平台类必须遵循以下规范:


  1. 继承基类: 必须继承自 jarvis.jarvis_platform.base.BasePlatform

  2. 实现抽象方法: 必须实现基类中所有的抽象方法。PlatformRegistry 在加载时会进行严格检查。


根据 src/jarvis/jarvis_platform/registry.pyREQUIRED_METHODSbase.py 的定义,一个平台类必须实现以下方法:


  • chat(self, message: str) -> Generator[str, None, None]: 核心对话方法,以流式生成器方式返回模型的响应。

  • name(self) -> str: 返回当前平台的名称。

  • platform_name(cls) -> str (类方法): 返回平台的唯一标识符,用于在配置中指定平台。

  • set_model_name(self, model_name: str): 设置当前对话使用的具体模型名称。

  • get_model_list(self) -> List[Tuple[str, str]]: 返回平台支持的模型列表,格式为 (模型ID, 模型描述)

  • delete_chat(self) -> bool: 清除当前对话上下文。

  • set_system_prompt(self, message: str): 设置系统级提示词。

  • upload_files(self, file_list: List[str]) -> bool: (如果支持) 实现文件上传逻辑。

  • support_upload_files(self) -> bool: 返回平台是否支持文件上传。

  • save(self, file_path: str) -> bool: (可选) 保存对话状态。

  • restore(self, file_path: str) -> bool: (可选) 恢复对话状态。

  • support_web(self) -> bool: 返回平台是否支持联网搜索。

开发步骤

假设我们要集成一个名为 MyLLM 的新平台。

第 1 步:创建平台文件

~/.jarvis/platforms/ 目录下创建 my_llm.py 文件。

第 2 步:编写平台类

my_llm.py 文件中,写入以下代码骨架:


import osfrom typing import Generator, List, Tuplefrom jarvis.jarvis_platform.base import BasePlatform
class MyLLMPlatform(BasePlatform):
def __init__(self): super().__init__() # 在这里初始化API客户端、会话等 self.api_key = os.getenv("MYLLM_API_KEY") if not self.api_key: raise ValueError("环境变量 MYLLM_API_KEY 未设置") self._current_model = "default-model" self._conversation_history = []
@classmethod def platform_name(cls) -> str: # 这个名称将用于 config.yaml 中的 JARVIS_PLATFORM return "my_llm"
def name(self) -> str: return f"MyLLMPlatform ({self._current_model})"
def set_model_name(self, model_name: str): self._current_model = model_name
def get_model_list(self) -> List[Tuple[str, str]]: # 返回你的平台支持的模型列表 return [("default-model", "默认模型"), ("pro-model", "专业模型")]
def chat(self, message: str) -> Generator[str, None, None]: # 在这里实现与你的LLM API的交互逻辑 # ... 调用API ... # 假设API返回一个完整的字符串 # for char in api_response: # yield char self._conversation_history.append({"role": "user", "content": message}) # 模拟流式响应 response_text = f"模拟回复 {message}" self._conversation_history.append({"role": "assistant", "content": response_text}) yield response_text
def set_system_prompt(self, message: str): # 实现设置系统提示词的逻辑 self._conversation_history.insert(0, {"role": "system", "content": message})
def delete_chat(self) -> bool: # 清空对话历史 self._conversation_history = [] return True
def upload_files(self, file_list: List[str]) -> bool: # 如果不支持,直接返回False print("MyLLM 平台不支持文件上传。") return False
def support_upload_files(self) -> bool: return False def support_web(self) -> bool: return False
def save(self, file_path: str) -> bool: # 可选实现 return False
def restore(self, file_path: str) -> bool: # 可选实现 return False
复制代码

第 3 步:配置并使用

  1. 设置环境变量: export MYLLM_API_KEY="YOUR_API_KEY"

  2. 修改 Jarvis 配置: 在 ~/.jarvis/config.yaml 中,将平台设置为 my_llm


JARVIS_PLATFORM: my_llmJARVIS_MODEL: pro-model # 可选,根据你的实现
复制代码


  1. 启动 Jarvis: 重新启动 jarvis,它会自动加载并使用你的新平台。



5.4 最佳实践

  • 命名约定是关键: 务必遵守文件名与 name 属性匹配的规则,这是最常见的工具加载失败原因。

  • 保持原子性: 每个工具应该只做一件事,并把它做好。复杂的逻辑应该拆分为多个工具。

  • 描述要清晰: description 是写给 AI 看的,是决定工具能否被正确使用的最关键因素。

  • 健壮的错误处理: execute 方法必须能妥善处理错误,并通过 stderr 返回有意义的错误信息。

  • 精确的参数定义: parameters 定义要尽可能精确。使用 description 字段详细解释每个参数。

  • 返回值为字符串: stdoutstderr 字段应该是字符串。如果结果是复杂数据结构,请将其序列化为字符串(如 json.dumps())。


发布于: 刚刚阅读数: 4
用户头像

SkyFire

关注

这个cpper很懒,什么都没留下 2018-10-13 加入

会一点点cpp的苦逼码农

评论

发布
暂无评论
Jarvis Agent功能扩展_agent_SkyFire_InfoQ写作社区