codex写入文件底层揭秘一、shell_command.rs
前情提要:
使用codex生成dify工作流的一次记录(模型GPT-5.5 High)
当时我认为,直接在powerShell的终端执行写入文件的命令,它就会出现乱码,于是我就直接执行这个命令了:
Set-Content-Path"test.txt"-Value"你好,PowerShell"没有设置编码方式,直接写入(默认为ANSI/GBK的编码模式写入)
但是它是正常的
于是我问他:
他给我这样的答案:
意思就是:它通过API工具把命令发给window shell,经过层层解析,将解析之后的内容写入文件。
于是继续追问:
它是这样解释的:
(其中这里的shell表示一个总类别)
意思就是:codex调用了API工具
它给了4种验证方式,分别是:
实验 1:直接 PowerShell 输入
Set-Content -Path “direct.txt” -Value "你好,
PowerShell" Get-Content -Path "direct.txt"这个大概率正常,因为你是直接在 PowerShell 里输入。
实验 2:PowerShell 调 Python 内联中文
python-c"from pathlib import Path; Path('py-inline.txt').write_text('你好,Python', encoding='utf-8')"如果正常,说明你当前终端到 Python 的传参链路没问题。
实验 3:用 here-string 传中文给 Python
@' from pathlib import Path Path("py-heredoc.txt").write_text("你好,HereString", encoding="utf-8") '@|python -如果这里也正常,说明你本机 PowerShell 管道传输也没问题。
实验 4:强制看 Python 收到的真实字符串
python-c"import sys; print(repr('你好,Python'))"正常应该输出类似:
'你好,Python'如果输出:
'?????Python'说明中文在进入 Python 前已经坏了。
到这里,就又多了很多个抽象的概念:
HereString是什么,Shell是什么,它和powerShell有什么关联。
搞清楚这些概念之后,又萌生了一个问题,图中的tool call指的是什么,于是针对这个问题,对codex进行了提问
他给我进行了解答
解释的非常清楚:调用codex提供的工具
然后举了一些例子等等。
于是继续追问:
github地址:
https://github.com/openai/codex
并且给了查找指引:
访问codex的document被拒绝了,按照关键词搜索了一轮,文件非常繁多,还是借助了codex帮我筛查了,主要集中在这些文件:
core/src/tools/handlers/shell/shell_command.rs ->core/src/shell.rs ->shell-command/src/powershell.rs ->core/src/exec.rs ->sandboxing/src/manager.rs ->tui/src/approval_events.rs看的出是用rust语言编写的
我们看一下其中一处
shell_command.rs
捕捉到一些关键的代码
// 导入模块# codex原型(shell命令调用参数结构体)usecodex_protocol::models::ShellCommandToolCallParams;# codex工具(codex_tools)usecodex_tools::ShellCommandBackendConfig;# 创建shell命令工具类usesuper::super::shell_spec::create_shell_command_tool;shellCommandHandler实现类:
# shellCommandHandler实现类implShellCommandHandler实现类里是一些构造方法之类的,其中有一个感觉像是比较关键的方法:to_exec_params
pub(super)fnto_exec_params(params:&ShellCommandToolCallParams,session:&crate::session::session::Session,turn_context:&TurnContext,turn_environment:&TurnEnvironment,cwd:AbsolutePathBuf,allow_login_shell:bool,)->Result<ExecParams,FunctionCallError>{// 获取session_shell对象letsession_shell=session.user_shell();// 拿到从session_shell对象中拿到shellletshell=turn_environment.shell.as_ref().unwrap_or(session_shell.as_ref());letuse_login_shell=Self::resolve_use_login_shell(params.login,allow_login_shell)?;// 指令letcommand=Self::base_command(shell,¶ms.command,use_login_shell);// env对象letmutenv=create_env(&turn_context.config.permissions.shell_environment_policy,Some(session.thread_id),);letactive_permission_profile=turn_context.config.permissions.active_permission_profile();inject_permission_profile_env(&mutenv,active_permission_profile.as_ref());// result特定写法,执行OK返回成功时的执行结果Ok(ExecParams{command,cwd,expiration:params.timeout_ms.into(),capture_policy:ExecCapturePolicy::ShellTool,env,network:turn_context.network.clone(),network_environment_id:Some(turn_environment.environment_id.clone()),sandbox_permissions:params.sandbox_permissions.unwrap_or_default(),windows_sandbox_level:turn_context.windows_sandbox_level,windows_sandbox_private_desktop:turn_context.config.permissions.windows_sandbox_private_desktop,justification:params.justification.clone(),arg0:None,})}大致理解:
执行方法,将env、command指令等对象当作执行参数进行返回。
ToolExecutor实现类:
# 特质实现类ToolExecutorimplToolExecutor<ToolInvocation>forShellCommandHandler{// tool工具名称fntool_name(&self)->ToolName{ToolName::plain("shell_command")}// 创建shell_command_tool工具类fnspec(&self)->ToolSpec{create_shell_command_tool(CommandToolOptions{allow_login_shell:self.options.allow_login_shell,exec_permission_approvals_enabled:self.options.exec_permission_approvals_enabled,})}fnsupports_parallel_tool_calls(&self)->bool{true}// 执行fnhandle(&self,invocation:ToolInvocation)->codex_tools::ToolExecutorFuture<'_>{Box::pin(self.handle_call(invocation))}}捕捉到一个很像的:
create_shell_command_tool
创建shell_command工具方法
它在shell_spec.rs里:
pubfncreate_shell_command_tool(options:CommandToolOptions)->ToolSpec{pubfncreate_shell_command_tool(options:CommandToolOptions)->ToolSpec{// 定义propertiesletmutproperties=BTreeMap::from([("command".to_string(),JsonSchema::string(Some("Shell script to run in the user's default shell.".to_string(),)),),("workdir".to_string(),JsonSchema::string(Some("Working directory for the command. Defaults to the turn cwd.".to_string(),)),),("timeout_ms".to_string(),JsonSchema::number(Some("Maximum command runtime. Defaults to 10000 ms.".to_string(),)),),]);// 如果为allow_login_shell命令则执行:ifoptions.allow_login_shell{properties.insert("login".to_string(),JsonSchema::boolean(Some("True runs with login shell semantics; false disables them. Defaults to true.".to_string(),)),);}//properties.extend(create_approval_parameters(options.exec_permission_approvals_enabled,));// 如果是windows的指令则执行powershell指令否则执行普通shell指令letdescription=ifcfg!(windows){format!(r#"Runs a Powershell command (Windows) and returns its output. Examples of valid command strings: - ls -a (show hidden): "Get-ChildItem -Force" - recursive find by name: "Get-ChildItem -Recurse -Filter *.py" - recursive grep: "Get-ChildItem -Path C:\\myrepo -Recurse | Select-String -Pattern 'TODO' -CaseSensitive" - ps aux | grep python: "Get-Process | Where-Object {{ $_.ProcessName -like '*python*' }}" - setting an env var: "$env:FOO='bar'; echo $env:FOO" - running an inline Python script: "@'\\nprint('Hello, world!')\\n'@ | python -" {}"#,windows_shell_guidance())}else{r#"Runs a shell command and returns its output. - Always set the `workdir` param when using the shell_command function. Do not use `cd` unless absolutely necessary."#.to_string()};ToolSpec::Function(ResponsesApiTool{name:"shell_command".to_string(),description,strict:false,defer_loading:None,parameters:JsonSchema::object(properties,Some(vec!["command".to_string()]),Some(false.into()),),output_schema:None,})}看起来比较关键的就是定义description,若为windows则执行powershell指令,否则执行普通shell指令。
后面还有一个CoreToolRuntime,也是ShellCommandHandler的特质实现类。
总结一下看起来比较关键的部分:
1、to_exec_params,执行方法,返回指令相关参数。
2、shellCommandHandler,shell指令Handler
3、create_shell_command_tool:里面有执行powershell相关的语法,若为windows,则执行powershell指令,否则执行普通指令。
shell_command.rs大致这些内容,第一次解读codex源码,欢迎各位进行评论、鼓励、批评指正。
附加tips:
我在原来的4个实验中执行了到第三个实验的时候出现异常:
图中清晰的展示:
???HereString
说明字符通过HereString管道传输给python的时候就已经断了。