当前位置: 首页 > news >正文

一小时内使用NVIDIA Nemotron创建你自己的Bash计算机使用智能体

一小时内使用NVIDIA Nemotron创建你自己的Bash计算机使用智能体

在这里插入图片描述

想象一下,如果你能和你的电脑对话,让它通过Bash终端执行任务,而你却无需编写任何一条命令,那会是怎样一种体验?借助NVIDIA Nemotron Nano v2,你可以在一小时内,用大约200行Python代码,从零开始轻松构建一个自然语言Bash智能体,并且依赖极少。

本文将带你一步步了解其核心组件和关键考量,让你轻松上手。我们将首先从零开始构建所有内容,然后,作为额外福利,本文还将展示如何使用LangGraph进一步简化设计。

让我们开始吧!

核心架构:两大组件

该系统的架构非常简单,主要由两个组件构成:

  1. Bash类:一个围绕Python subprocess模块的轻量级包装器,负责管理工作目录、执行命令白名单、执行命令,并将结果(或错误)返回给智能体。
  2. 智能体:使用NVIDIA Nemotron模型来理解用户意图并决定如何行动,同时在多轮对话中保持上下文。智能体的行为由一个精心设计的系统提示来引导,该提示设定了边界、定义了其作为Bash助理的角色,并提醒它允许使用的命令。

在这里插入图片描述

图1. 智能体工作流图

工作流程如下:

  1. 用户发出高级指令,例如更改目录、复制文件或检查文档内容。
  2. Nemotron解释请求,将其分解为具体步骤,并在需要执行命令时使用Bash类。有些任务可能根本不需要执行,而其他任务可能跨越多个命令。每次运行后,模型都会接收输出并决定下一步或何时停止。
  3. 任务完成后,无论是成功还是因错误而停止,智能体都会将结果返回给用户,并等待下一条指令。

代码实现:从零构建Bash智能体

下面,我们将通过详细的代码示例,展示如何从零开始构建这个强大的Bash智能体。

1. Bash工具类

我们首先创建一个简单的Bash类,它存储了允许的命令列表和当前工作目录。这个类将作为我们智能体可以调用的“工具”。

# bash_tool.py: Bash命令执行工具
import subprocess
from typing import List, Dict, Anyclass Bash:"""一个执行Bash命令的工具实现。它负责管理工作目录、执行命令白名单和实际的命令执行。"""def __init__(self, cwd: str, allowed_commands: List[str]):"""初始化Bash工具。Args:cwd (str): 初始当前工作目录。allowed_commands (List[str]): 允许执行的命令列表。"""self.cwd = cwd  # 当前工作目录self._allowed_commands = allowed_commands  # 允许的命令列表def _run_bash_command(self, cmd: str) -> Dict[str, str]:"""运行Bash命令并捕获异常(如果有)。这是实际执行命令的私有方法。Args:cmd (str): 要执行的命令字符串。Returns:Dict[str, str]: 包含stdout、stderr和新工作目录的字典。"""stdout = ""stderr = ""new_cwd = self.cwdtry:# 包装命令,以便我们能跟踪工作目录的变化。# 我们在命令后附加一个唯一的分隔符和`pwd`命令。wrapped_cmd = f"{cmd}; echo __END__; pwd"result = subprocess.run(wrapped_cmd, shell=True, cwd=self.cwd, capture_output=True, text=True,executable="/bin/bash")stderr = result.stderr# 找到分隔符标记split_output = result.stdout.split("__END__")stdout = split_output[0].strip()# 如果完全没有输出/错误,则通知调用成功。if not stdout and not stderr:stdout = "命令执行成功,没有任何输出。"# 获取新的工作目录,并更新它new_cwd = split_output[-1].strip()self.cwd = new_cwdexcept Exception as e:stdout = ""stderr = str(e)return {"stdout": stdout, "stderr": stderr, "cwd": new_cwd}def exec_bash_command(self, cmd: str) -> Dict[str, str]:"""在获得用户确认后执行bash命令。这是暴露给智能体调用的公共方法。Args:cmd (str): 要执行的命令。Returns:Dict[str, str]: 命令执行结果。"""if cmd:# 检查命令是否在白名单中# 为了简单起见,我们只检查命令的主要部分command_part = cmd.strip().split()[0]if command_part not in self._allowed_commands:return {"error": f"命令 [1m{command_part}[0m 不在允许的命令列表中。"}# 在实际执行前获取用户确认if input(f"    ▶️   是否执行 [1m'{cmd}'[0m? [y/N]: ").strip().lower() == "y":return self._run_bash_command(cmd)else:return {"error": "用户拒绝执行此命令。"}return {"error": "没有提供命令。"}def to_json_schema(self) -> Dict[str, Any]:"""将函数签名转换为JSON Schema,用于LLM的工具调用。这告诉LLM如何以及何时使用这个工具。Returns:Dict[str, Any]: 工具的JSON Schema表示。"""return {"type": "function","function": {"name": "exec_bash_command","description": "执行一个bash命令并返回stdout/stderr和工作目录","parameters": {"type": "object","properties": {"cmd": {"type": "string","description": "要执行的bash命令"}},"required": ["cmd"],},},}

这个类暴露了两个公共函数:

  • exec_bash_command():智能体可以调用它来执行命令。它会返回一个包含stdoutstderr和更新后工作目录的字典,或者在命令无效或不被允许时返回错误。这些信号让智能体在出现问题时能够做出调整。
  • to_json_schema():用于告诉LLM如何使用这个工具。

2. 定义系统提示

对于智能体,我们将Nemotron初始化为推理引擎,并将exec_bash_command()注册为可调用的工具。模型的行为由一个系统提示来塑造,该提示定义了其作为Bash助理的角色,列出了允许的命令,并指导它何时以及如何协助用户或调用工具。

# system_prompt.pydef get_system_prompt(allowed_commands: List[str]) -> str:"""生成系统提示,指导LLM的行为。Args:allowed_commands (List[str]): 允许执行的命令列表。Returns:str: 格式化后的系统提示字符串。"""# 将命令列表格式化为易于阅读的字符串command_list_str = "- " + "\n- ".join(allowed_commands)return f"""
你是一个乐于助人的Bash助手,有能力在shell中执行命令。
你与用户互动,帮助回答关于bash命令的问题,或执行他们的意图。
如果用户意图不明确,继续与他们互动,找出他们需要什么以及如何最好地帮助他们。
如果他们问与bash或计算机使用无关的问题,请拒绝回答。当一个命令被执行时,你将得到该命令的输出和任何错误。基于此,要么采取进一步的行动,要么将控制权交还给用户。
每次执行命令时,你都会得到bash解释器的输出和当前工作目录。在下一次对话中要考虑到这一点。
如果执行过程中出现错误,要准确地告诉用户错误是什么。你只允许执行以下命令:
{command_list_str}**永远不要**尝试执行不在此列表中的命令。**永远不要**尝试执行危险的命令,如`rm`、`mv`、`rmdir`、`sudo`等。如果用户要求你这样做,请礼貌地拒绝。
当你切换到新目录时,总是列出文件,以便获得更多上下文。思考过程请使用<think>和</think>标签包裹。
"""

3. 构建智能体循环(从零开始)

构建智能体循环非常直接。我们初始化OpenAI客户端,并保留一个对话历史记录,作为我们的内存/状态。在循环内部:

  1. 获取用户输入,并与系统提示一起发送给模型。
  2. 获取并存储模型的响应到对话历史中,然后检查是否有工具调用:
    • 如果存在工具调用,则运行exec_bash_command()(其中包含了用户确认步骤),返回结果,并获取下一个响应。
    • 如果没有工具调用,则显示模型的回复并将控制权交还给用户。
  3. 这个循环会一直重复,直到应用程序终止。
# agent_from_scratch.py
import os
import json
from openai import OpenAI
from typing import List, Dict# (此处应导入上面定义的Bash类和get_system_prompt函数)# --- 辅助类 --- 
class Messages:# ... (一个简单的类来管理对话历史)class LLM:# ... (一个包装OpenAI客户端的类)# --- 主程序 ---
if __name__ == "__main__":# 允许的命令列表ALLOWED_COMMANDS = ["ls", "cat", "grep", "cd", "pwd", "mkdir", "touch", "echo", "find", "wc", "head", "tail"]# 1. 初始化Bash工具bash = Bash(cwd=os.path.expanduser("~"), allowed_commands=ALLOWED_COMMANDS)# 2. 初始化LLM客户端# 假设你已经设置了OPENAI_API_KEY和OPENAI_BASE_URL环境变量llm = LLM(client=OpenAI())# 3. 创建对话历史,并设置系统提示system_prompt = get_system_prompt(ALLOWED_COMMANDS)messages = Messages(system_prompt)print("Bash智能体已准备就绪。输入 'exit' 退出。")# 4. 主智能体循环while True:# 获取用户消息user_input = input(f"[[34m{bash.cwd}[0m 🙂] ").strip()if user_input.lower() == 'exit':breakmessages.add_user_message(user_input)# 5. 工具调用/响应循环while True:# 查询LLMresponse, tool_calls = llm.query(messages, [bash.to_json_schema()])messages.add_assistant_message(response, tool_calls)# 处理工具调用if tool_calls:for tc in tool_calls:function_name = tc.function.namefunction_args = json.loads(tc.function.arguments)# 确保调用的是正确的工具if function_name != "exec_bash_command" or "cmd" not in function_args:tool_call_result = json.dumps({"error": "错误的工具或函数参数"})else:# 执行命令tool_call_result = bash.exec_bash_command(function_args["cmd"])# 将工具调用的结果添加回消息历史messages.add_tool_message(tool_call_result, tc.id)else:# 如果没有工具调用,向用户显示助手的消息# (去除<think>标签)display_response = response.split("</think>")[-1].strip()if display_response:print(f"\n[🤖] {display_response}")break # 结束内部循环,等待下一个用户输入

额外福利:使用LangGraph的智能体循环

使用LangGraph,智能体循环变得更加简单。create_react_agent()可以管理循环、连接模型、工具和对话状态,并让库自动处理工具调用和结果传递。

# agent_with_langgraph.py
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import InMemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool# (此处应导入上面定义的Bash类和get_system_prompt函数)# 1. 实例化Bash类
bash = Bash(cwd=os.path.expanduser("~"), allowed_commands=ALLOWED_COMMANDS)# 2. 使用@tool装饰器包装我们的函数,以便LangGraph可以识别
@tool
def exec_bash_command(cmd: str) -> str:"""在获得用户确认后执行bash命令。"""# ... (这里的逻辑与Bash.exec_bash_command类似,但作为独立函数)# 为了简洁,我们直接调用已有的bash实例if input(f"    ▶️   是否执行 [1m'{cmd}'[0m? [y/N]: ").strip().lower() == "y":# 检查白名单command_part = cmd.strip().split()[0]if command_part not in bash._allowed_commands:return json.dumps({"error": f"命令 [1m{command_part}[0m 不在允许的命令列表中。"})return json.dumps(bash._run_bash_command(cmd))return json.dumps({"error": "用户拒绝执行此命令。"})# 3. 创建智能体
agent_executor = create_react_agent(model=ChatOpenAI(model="nvidia/nemotron-4-9b-v2", ...),tools=[exec_bash_command],messages_modifier=get_system_prompt(ALLOWED_COMMANDS),checkpointer=InMemorySaver(),
)# 4. 创建用户/智能体交互循环
while True:user_input = input(f"[🙂] ").strip()if user_input.lower() == "exit":break# 调用智能体config = {"configurable": {"thread_id": "user"}}events = agent_executor.stream({"messages": [("user", user_input)]}, config, stream_mode="values")for event in events:event["messages"][-1].pretty_print()

总结与下一步

你现在已经用几行代码构建了自己的计算机使用智能体。从这里开始,进行实验:尝试添加你自己的命令,调整系统提示,看看智能体如何适应。一旦你探索了一番,你会发现同样的原则可以自然地扩展到更高级的多智能体系统中。

http://www.dtcms.com/a/519606.html

相关文章:

  • Chrome开发者工具
  • 虚拟机 Ubuntu 中安装 Google Chrome 浏览器
  • Docker/K8s部署MySQL的创新实践与优化技巧大纲
  • 网站建设管理流程避免网站侵权
  • 如何在Visual Studio中配置C++环境?
  • 珠海翻译公司高效翻译服务 2025年10月
  • 网站后台管理系统怎么登陆鄂州网站建设与设计
  • 建设系统网站企业密信下载app下载官网
  • 算法面经常考题整理(1)机器学习
  • 使用java如何进行接口测试
  • 机器学习-方差与偏差
  • 甘肃省网站建设咨询seo最好的网站源码
  • 3.序列式容器-heap
  • Module JDK is not defined 警告解决
  • 柞水县住房和城乡建设局网站网站建设客户分析调查表文档
  • html`contenteditable`
  • 【语音识别】语音识别的发展历程
  • 【C++ 类与对象 (下)】:进阶特性与编译器优化的深度实战
  • 加速智能体开发:从 Serverless 运行时到 Serverless AI 运行时
  • 怎么在服务器建立网站wordpress getcategorylink
  • uniapp textarea标签 在ios真机上出现高度拉长问题
  • cpp language 语法
  • uni-app 入门学习教程,从入门到精通,uni-app 企业项目实战:鲁嗑瓜子项目开发知识点(9)
  • uni-app小程序往飞书多维表格写入内容(包含图片)
  • 【uniapp】App平台展示pdf文件
  • Jenkins Pipeline 中使用 GitLab Webhook 触发任务执行
  • 【课堂笔记】概率论-2
  • 自建企业级演示中心:不用租Office,PPTist+cpolar方案实测
  • ubuntu22+postgresql18启动报错
  • 如何做好电商网站平面设计wordpress接入翼支付宝