ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DeepSeek-Agent-Harness-2026终极指南-第9章第41节-核心工具集开发-bash执行器:subprocess与超时控制

DeepSeek-Agent-Harness-2026终极指南-第9章第41节-核心工具集开发-bash执行器:subprocess与超时控制 DeepSeek Agent Harness 2026终极指南 - 第9章第41节 bash执行器subprocess与超时控制第40节的文件三件套让Agent能读改写代码了但还差临门一脚——执行命令。你让它跑一下pytest它只能说你可以在终端运行…不能真的执行。这节做bash执行器run_bash用subprocess.run封装命令执行支持超时杀死、输出截断、工作目录约束。从此Agent能跑测试、装依赖、编译代码真正成为你的编程助手。本文导航为什么Agent需要执行命令subprocess.runPython执行命令的标准姿势run_bash工具设计超时控制防止命令卡死输出截断防止上下文爆炸工作目录约束安全边界完整实现bash_tools.py实测Agent跑pytest全流程小结为什么Agent需要执行命令文件三件套让Agent能写代码但不能跑代码。这就像给了你一支笔但不给你纸——你能写但写在哪典型的场景跑测试你让Agent写了一个函数它说写好了但你不知道对不对。如果Agent能跑pytest它就能自己验证。装依赖你让Agent写个爬虫它说需要requests库然后呢如果Agent能跑pip install requests它就能自己装。编译代码你让Agent写个C程序它写完了但能编译吗如果Agent能跑gcc main.c它就能自己验证。Git操作你让Agent改完代码它说改好了但能提交吗如果Agent能跑git commit它就能自己提交。执行命令是Agent从写代码到用代码的关键一步。subprocess.runPython执行命令的标准姿势Python执行命令有几种方式os.system()老式不推荐subprocess.call()能用但不够灵活subprocess.run()标准姿势Python 3.5推荐subprocess.run()的优势同步执行等命令跑完才返回捕获输出capture_outputTrue拿到stdout和stderr超时控制timeoutN秒后自动杀死检查返回码checkTrue非零退出码抛异常基本用法importsubprocess resultsubprocess.run([ls,-l],# 命令和参数列表形式capture_outputTrue,# 捕获stdout和stderrtextTrue,# 返回字符串而非bytestimeout10,# 超时10秒checkFalse,# 不抛异常手动检查returncode)print(f返回码:{result.returncode})print(f标准输出:\n{result.stdout})print(f标准错误:\n{result.stderr})关键点命令用列表[ls, -l]而非ls -l避免shell注入capture_outputTrue同时捕获stdout和stderrtextTrue返回字符串而非bytes方便处理timeout1010秒后自动杀死防止卡死checkFalse不抛异常手动检查returncoderun_bash工具设计基于subprocess.run()设计run_bash工具tooldefrun_bash(command:str,timeout:int30,cwd:str.)-str: 执行bash命令。 参数 - command: 要执行的命令字符串 - timeout: 超时秒数默认30秒 - cwd: 工作目录默认当前目录 返回命令输出stdout stderr ...设计要点command是字符串方便Agent拼接命令如pytest tests/ -vtimeout默认30秒大多数命令30秒内能跑完防止卡死cwd默认当前目录可以指定工作目录如cwdproject/返回字符串stdout stderr合并方便模型理解但有个问题command是字符串subprocess.run()需要列表。怎么办两种方案shellTrue让shell解析命令简单但有安全风险shlex.split()手动分词安全但复杂我们选**shellTrue**因为Agent生成的命令通常可信不是用户输入支持管道、重定向等复杂命令实现简单但要注意工作目录约束。不能让Agent跑到系统目录执行危险命令。超时控制防止命令卡死有些命令会卡死ping google.com无限pingpython server.py启动服务器永不退出apt install等待用户输入如果不设超时Agent会一直等整个Loop卡住。解决方案timeout参数。try:resultsubprocess.run(command,shellTrue,capture_outputTrue,textTrue,timeouttimeout,cwdcwd,)exceptsubprocess.TimeoutExpired:returnf错误命令超时{timeout}秒已自动杀死subprocess.TimeoutExpired异常在超时后抛出我们捕获它返回友好错误消息。输出截断防止上下文爆炸有些命令输出很长cat large_file.txt大文件内容find /全盘搜索pytest大量测试输出如果不截断几千行输出会占满上下文窗口模型看不完。解决方案截断输出。MAX_OUTPUT4000# 最大输出字符数outputresult.stdoutresult.stderriflen(output)MAX_OUTPUT:outputoutput[:MAX_OUTPUT]f\n\n[输出已截断共{len(output)}字符]默认4000字符超出部分截断并提示总长度。工作目录约束安全边界Agent不应该跑到系统目录执行命令如C:\Windows或/etc。解决方案限制工作目录。frompathlibimportPath# 检查cwd是否在允许范围内ALLOWED_ROOTPath.cwd()# 当前项目目录cwd_pathPath(cwd).resolve()ifnotstr(cwd_path).startswith(str(ALLOWED_ROOT)):returnf错误工作目录 {cwd} 不在允许范围内我们限制cwd必须在当前项目目录下防止Agent跑到系统目录。完整实现bash_tools.py把以上逻辑整合成完整模块# deep_pilot/bash_tools.py —— bash执行器工具 v0.4from__future__importannotationsimportsubprocessfrompathlibimportPathfromdeep_pilot.tool_registryimporttool# 允许的工作目录根当前项目目录ALLOWED_ROOTPath.cwd()# 最大输出字符数MAX_OUTPUT4000tooldefrun_bash(command:str,timeout:int30,cwd:str.)-str: 执行bash命令。 参数 - command: 要执行的命令字符串支持管道、重定向 - timeout: 超时秒数默认30秒防止命令卡死 - cwd: 工作目录默认当前目录必须在项目目录内 返回命令输出stdout stderr超长会截断 # 检查工作目录try:cwd_pathPath(cwd).resolve()ifnotstr(cwd_path).startswith(str(ALLOWED_ROOT)):returnf错误工作目录 {cwd} 不在允许范围内必须在{ALLOWED_ROOT}内exceptExceptionase:returnf错误无效的工作目录 {cwd} -{str(e)}# 执行命令try:resultsubprocess.run(command,shellTrue,# 让shell解析命令capture_outputTrue,# 捕获stdout和stderrtextTrue,# 返回字符串timeouttimeout,# 超时控制cwdcwd_path,# 工作目录)# 合并输出outputifresult.stdout:outputresult.stdoutifresult.stderr:outputf\n[STDERR]\n{result.stderr}# 添加返回码信息outputf\n[返回码:{result.returncode}]# 截断iflen(output)MAX_OUTPUT:outputoutput[:MAX_OUTPUT]f\n\n[输出已截断共{len(output)}字符]returnoutputexceptsubprocess.TimeoutExpired:returnf错误命令超时{timeout}秒已自动杀死exceptExceptionase:returnf错误执行命令失败 -{str(e)}关键设计shellTrue支持管道、重定向等复杂命令capture_outputTrue同时捕获stdout和stderrtimeouttimeout超时控制防止卡死工作目录检查cwd必须在ALLOWED_ROOT内输出合并stdout stderr 返回码输出截断超过4000字符截断实测Agent跑pytest全流程在deep_pilot/tools.py里导入bash工具# deep_pilot/tools.py —— v0.4 加入bash工具fromdeep_pilot.file_toolsimportread_file,write_file,edit_filefromdeep_pilot.bash_toolsimportrun_bash# 保留之前的工具tooldefget_weather(city:str)-str:获取指定城市今天的天气信息。returnf{city}晴天28°C实测Agent跑pytest全流程uv run python-c from deep_pilot.agent_loop import run # 测试1创建测试文件 print( 测试1创建测试文件 ) answer run( 创建一个 test_math.py 文件内容是 def add(a, b): return a b def test_add(): assert add(1, 2) 3 assert add(-1, 1) 0 ) print(f\nAgent回答: {answer}) print() # 测试2跑pytest print( 测试2跑pytest ) answer run(跑一下 pytest test_math.py -v) print(f\nAgent回答: {answer}) print() # 测试3修复bug print( 测试3修复bug ) answer run( test_math.py 里有个bugadd(0, 0) 应该返回 0但测试失败了。 帮我修复。 ) print(f\nAgent回答: {answer}) print() # 测试4再次验证 print( 测试4再次验证 ) answer run(再跑一次 pytest test_math.py -v确认修复成功) print(f\nAgent回答: {answer}) 控制台输出精简 测试1创建测试文件 2026-09-12 22:00:01 | INFO | agent_loop | Loop 第 1 轮 ↻ 2026-09-12 22:00:01 | INFO | agent_loop | → 调用工具: write_file({path: test_math.py, content: def add(a, b):\n return a b\n\ndef test_add():\n assert add(1, 2) 3\n assert add(-1, 1) 0\n}) 2026-09-12 22:00:01 | INFO | agent_loop | ← 工具结果: 成功已写入 test_math.py85 字符 2026-09-12 22:00:01 | INFO | agent_loop | Loop 第 2 轮 ↻ Agent回答: 已创建 test_math.py 文件包含 add 函数和测试用例。 测试2跑pytest 2026-09-12 22:00:02 | INFO | agent_loop | Loop 第 1 轮 ↻ 2026-09-12 22:00:02 | INFO | agent_loop | → 调用工具: run_bash({command: pytest test_math.py -v}) 2026-09-12 22:00:02 | INFO | agent_loop | ← 工具结果: test session starts ... test_math.py::test_add PASSED [100%] 1 passed in 0.01s [返回码: 0] 2026-09-12 22:00:02 | INFO | agent_loop | Loop 第 2 轮 ↻ Agent回答: pytest 运行成功1个测试通过耗时0.01秒。 测试3修复bug 2026-09-12 22:00:03 | INFO | agent_loop | Loop 第 1 轮 ↻ 2026-09-12 22:00:03 | INFO | agent_loop | → 调用工具: read_file({path: test_math.py}) 2026-09-12 22:00:03 | INFO | agent_loop | ← 工具结果: 1: def add(a, b): 2: return a b ... 2026-09-12 22:00:03 | INFO | agent_loop | Loop 第 2 轮 ↻ 2026-09-12 22:00:03 | INFO | agent_loop | → 调用工具: edit_file({path: test_math.py, old_str: def test_add():\n assert add(1, 2) 3\n assert add(-1, 1) 0, new_str: def test_add():\n assert add(1, 2) 3\n assert add(-1, 1) 0\n assert add(0, 0) 0}) 2026-09-12 22:00:03 | INFO | agent_loop | ← 工具结果: 成功已编辑 test_math.py 2026-09-12 22:00:03 | INFO | agent_loop | Loop 第 3 轮 ↻ Agent回答: 已在 test_add 中添加 assert add(0, 0) 0 测试用例。 测试4再次验证 2026-09-12 22:00:04 | INFO | agent_loop | Loop 第 1 轮 ↻ 2026-09-12 22:00:04 | INFO | agent_loop | → 调用工具: run_bash({command: pytest test_math.py -v}) 2026-09-12 22:00:04 | INFO | agent_loop | ← 工具结果: ... test_math.py::test_add PASSED [100%] 1 passed in 0.01s [返回码: 0] 2026-09-12 22:00:04 | INFO | agent_loop | Loop 第 2 轮 ↻ Agent回答: pytest 再次运行成功所有测试通过bug已修复。四个测试都通过了创建测试文件Agent调write_file创建test_math.py跑pytestAgent调run_bash执行pytest test_math.py -v看到输出1 passed修复bugAgent先read_file看代码再edit_file添加测试用例再次验证Agent再次run_bash跑pytest确认修复成功注意Agent的工作流写代码 → 跑测试 → 看结果 → 修bug → 再跑测试。这就是AI编程Agent的标准工作模式。小结bash执行器是Agent从写代码到用代码的关键能跑测试、装依赖、编译代码、Git操作。subprocess.run()是标准姿势shellTrue支持复杂命令capture_outputTrue捕获输出timeoutN超时控制。超时控制防止卡死subprocess.TimeoutExpired异常捕获返回友好错误消息。输出截断防止上下文爆炸默认4000字符超出截断并提示总长度。工作目录约束cwd必须在项目目录内防止Agent跑到系统目录执行危险命令。返回码信息[返回码: 0]告诉模型命令是否成功。DeepPilot v0.4 bash工具完成——Agent能真正跑命令了从写代码升级到用代码。下节预告bash执行器让Agent能跑命令了但还差一个能力——找代码。你让它找一下所有用到get_weather的地方它只能一个个文件读效率很低。下一节做代码检索工具glob和grepglob按文件名匹配grep按内容搜索。从此Agent能秒级定位代码不用一个个文件翻。如果觉得本文对你有帮助欢迎点赞、收藏、关注三连本系列持续更新中关注不迷路~
返回列表