Skip to content

Commit 95cb839

Browse files
alcholiclgalcholiclg
andauthored
Feat/readme code tool (#789)
Co-authored-by: alcholiclg <ligongshengzju@foxmail.com>
1 parent 7c65c55 commit 95cb839

File tree

7 files changed

+329
-31
lines changed

7 files changed

+329
-31
lines changed

docs/en/Components/Tools.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,72 @@ Parameters:
5353

5454
- path: `str`, relative directory based on the `output` in yaml configuration. If empty, lists all files in the root directory.
5555

56+
### code_executor
57+
58+
Code execution tool that can run Python code either in a sandboxed environment or directly in the local Python environment. The behavior is controlled by the `tools.code_executor.implementation` field.
59+
60+
- When omitted or set to `sandbox`:
61+
- Uses an [ms-enclave](https://github.com/modelscope/ms-enclave) based sandbox. The sandbox can be created locally with Docker or via a remote HTTP service.
62+
- Currently supports two sandbox types: `docker` and `docker_notebook`. The former is suitable for non-interactive/stateless execution; the latter maintains notebook-style state across calls.
63+
- The configured `output_dir` on the host is mounted into the sandbox at `/data` so code can read and write persistent artifacts there.
64+
65+
- When set to `python_env`:
66+
- Runs code in the local Python environment. The tool API is aligned with the sandbox version and supports both Jupyter-kernel based execution and plain Python interpreter execution.
67+
- Required dependencies should be installed locally; on the first run, common data-analysis and execution dependencies (such as `numpy`, `pandas`, etc.) will be installed automatically when missing.
68+
69+
#### notebook_executor
70+
71+
- **Sandbox mode**: Executes code inside a `docker_notebook` sandbox, preserving state (variables, imports, dataframes, etc.) across calls. Files under the mounted data directory are available at `/data/...`, and you can also run simple shell commands from code cells using the standard `!` prefix.
72+
- **Local mode**: Executes code in a local Jupyter kernel, with environment isolation and state persistence across calls. In the notebook environment you can also use simple shell commands via the standard `!` syntax.
73+
74+
**Parameters**:
75+
76+
- **code**: `string` – Python code to execute.
77+
- **description**: `string` – Short description of what the code is doing.
78+
- **timeout**: `integer` – Optional execution timeout in seconds; if omitted, the tool-level default is used.
79+
80+
#### python_executor
81+
82+
- **Sandbox mode**: Executes Python code in a `docker`-type sandbox using the sandbox’s Python interpreter, typically used when you do not need full notebook-style interaction.
83+
- **Local mode**: Executes code with the local Python interpreter in a stateless fashion; each call has its own execution context and does not share variables with previous calls.
84+
85+
**Parameters**:
86+
87+
- **code**: `string` – Python code to execute.
88+
- **description**: `string` – Short description of what the code is doing.
89+
- **timeout**: `integer` – Optional execution timeout in seconds; if omitted, the tool-level default is used.
90+
91+
#### shell_executor
92+
93+
- **Sandbox mode**: Dedicated to `docker`-type sandboxes and executes shell commands inside the sandbox using `bash`, supporting basic operations like `ls`, `cd`, `mkdir`, `rm`, etc., and access to files under `/data`.
94+
- **Local mode**: Executes shell commands using the local `bash` interpreter with the working directory set to `output_dir`; this is convenient for development but generally not recommended for production.
95+
96+
**Parameters**:
97+
98+
- **command**: `string` – Shell command to execute.
99+
- **timeout**: `integer` – Optional execution timeout in seconds; if omitted, the tool-level default is used.
100+
101+
#### file_operation
102+
103+
- **Sandbox mode**: Dedicated to `docker`-type sandboxes and performs basic file operations inside the sandbox (create, read, write, delete, list, exists). Paths are interpreted as sandbox-internal paths; in most cases you should work under `/data/...`.
104+
- **Local mode**: Performs the same basic file operations on the local filesystem but always constrained under `output_dir` to prevent accessing arbitrary locations.
105+
106+
**Parameters**:
107+
108+
- **operation**: `string` – Type of file operation to perform; one of `'create'`, `'read'`, `'write'`, `'delete'`, `'list'`, `'exists'`.
109+
- **file_path**: `string` – File or directory path (sandbox-internal in sandbox mode; relative to or under `output_dir` in local mode).
110+
- **content**: `string` – Optional, content to write when `operation` is `'write'`.
111+
- **encoding**: `string` – Optional file encoding, default `utf-8`.
112+
113+
#### reset_executor
114+
115+
- **Sandbox mode**: Recreates the sandbox (or restarts the notebook kernel) to clear all variables and session state when the environment becomes unstable.
116+
- **Local mode**: Restarts the local Jupyter kernel used by `notebook_executor`, dropping all in-memory state.
117+
118+
#### get_executor_info
119+
120+
- **Sandbox mode**: Returns the current sandbox status and configuration summary (such as memory/CPU limits, available tools, etc.).
121+
- **Local mode**: Returns basic information about the local execution environment (working directory, whether it is initialized, current execution count, uptime, etc.).
56122

57123
### MCP Tools
58124

docs/en/Projects/FinResearch.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,42 @@ pip install akshare baostock
7373

7474
### Sandbox Environment
7575

76+
By default, the Collector and Analyst agents use a Docker-based sandbox to safely execute code:
77+
7678
```bash
7779
pip install ms-enclave docker websocket-client # https://github.com/modelscope/ms-enclave
7880
bash projects/fin_research/tools/build_jupyter_image.sh
7981
```
8082

83+
If you prefer not to install Docker and related dependencies, you can configure a local code execution tool instead. In both `analyst.yaml` and `collector.yaml`, change the default `tools` configuration to:
84+
85+
```yaml
86+
tools:
87+
code_executor:
88+
mcp: false
89+
implementation: python_env
90+
exclude:
91+
- python_executor
92+
- shell_executor
93+
- file_operation
94+
```
95+
96+
With this configuration, code is executed via a Jupyter kernel–based notebook executor that isolates environment variables and supports shell command execution; the necessary dependencies (for data analysis and code execution) will be installed automatically on the first run.
97+
98+
If you only need a lighter-weight Python execution environment and do not want to introduce notebook-related dependencies, you can instead use:
99+
100+
```yaml
101+
tools:
102+
code_executor:
103+
mcp: false
104+
implementation: python_env
105+
exclude:
106+
- notebook_executor
107+
- file_operation
108+
```
109+
110+
This configuration uses an independent Python executor together with a shell command executor and is suitable for lightweight code execution scenarios.
111+
81112
### Environment Variables
82113
83114
Configure API keys in your system environment or in YAML.

docs/zh/Components/工具.md

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,44 +53,78 @@ MS-Agent支持很多内部工具:
5353

5454
- path: `str`, 基于yaml配置中的`output`的相对目录。如果为空,则列出根目录下的所有文件。
5555

56-
### code_execution
56+
### code_executor
5757

58-
代码执行工具,基于沙箱环境运行代码,支持基于HTTP或本地建立沙箱运行环境,主要支持docker和docker-notebook两种环境类型,分别适合于无状态的代码运行和需要在对话内保持上下文状态的代码运行。
59-
工具基于ms-enclave实现,依赖于本地的docker环境,如果代码执行需要的依赖较多,需要预先构建包含所需依赖的镜像。准备好基础镜像后,需要完善本次启动容器的基础配置,如配置所选择的执行环境类型、可用的工具、容器需要挂载的目录等等,默认的挂载目录基于yaml配置中的`output`字段,在沙箱内挂载为`/data`
58+
代码执行工具,支持基于本地 Python 环境执行代码或基于沙箱环境执行代码。可以在 `tools.code_executor` 下通过配置 `implementation` 字段选择执行环境。
59+
60+
- 默认或 `implementation: sandbox` 启动沙箱模式:
61+
- 基于沙箱环境运行代码,支持通过本地 Docker 或远程 HTTP 服务建立沙箱运行环境,主要支持 `docker``docker_notebook` 两种环境类型,分别适合于无状态的代码运行和需要在对话内保持上下文状态的代码运行。
62+
- 工具基于 [ms-enclave](https://github.com/modelscope/ms-enclave) 实现,依赖于本地可用的 Docker 环境。如果代码执行需要的依赖较多,建议预先构建包含所需依赖的镜像。准备好基础镜像后,需要完善本次启动容器的基础配置,如配置所选择的执行环境类型、可用的工具、容器需要挂载的目录等。
63+
- 默认会将配置中的 `output_dir` 目录挂载到沙箱内的 `/data`,用于读写持久化文件。
64+
65+
- `implementation: python_env` 时启动本地模式:
66+
- 基于本地 Python 环境执行代码,在工具设计上与沙箱环境保持一致,支持 Jupyter Notebook 和 Python 解释器两种执行方式,分别适合于需要在对话内保持上下文状态的代码运行和无状态的代码运行。
67+
- 所需的依赖需要在本地进行配置,第一次运行时会自动安装常用的数据分析和代码执行基础依赖(例如 `numpy``pandas` 等)。
6068

6169
#### notebook_executor
6270

63-
该方法专用于docker-notebook类型沙箱环境,可以在notebook内执行代码并保持对话内的上下文,同时支持使用shell命令完成沙箱环境下的各种操作。
71+
沙箱模式下,该方法对应 `docker_notebook` 类型沙箱环境,可以在 Notebook 内执行代码并保持对话内的上下文;代码可以访问挂载在 `/data` 下的文件,支持在代码中通过`!`前缀执行简单的shell命令。
72+
73+
本地模式下,该方法基于本地 Jupyter Kernel 执行代码,提供对环境变量的隔离,支持对话内的上下文保持,并支持在代码中通过 `!` 前缀执行简单的 shell 命令。相应依赖会在首次运行时自动安装(包括数据分析和代码执行需要的依赖)。
74+
75+
参数:
6476

6577
- code: `str`, 需要执行的代码。
66-
- description: `str`, 代码工作内容的描述。
78+
- description: `str`, 代码工作内容的简要描述。
79+
- timeout: `int`, 可选,执行超时时间(秒),不配置时使用工具默认值。
6780

6881
#### python_executor
6982

70-
该方法专用于docker类型沙箱环境,可以使用沙箱内的本地代码解释器运行代码。
83+
沙箱模式下,该方法专用于 `docker` 类型沙箱环境,可以使用沙箱内的 Python 解释器运行代码,适合不需要完整 Notebook 交互能力的执行场景。
84+
85+
本地模式下,该方法基于本地 Python 解释器执行代码,每次调用互相独立,不保留上下文。
86+
87+
参数:
7188

7289
- code: `str`, 需要执行的代码。
73-
- description: `str`, 代码工作内容的描述。
90+
- description: `str`, 代码工作内容的简要描述。
91+
- timeout: `int`, 可选,执行超时时间(秒),不配置时使用工具默认值。
7492

7593
#### shell_executor
7694

77-
该方法专用于docker类型沙箱环境,可以使用bash在沙箱内执行shell命令,支持基本的shell操作例如ls、cd、mkdir、rm等等
95+
沙箱模式下,该方法专用于 `docker` 类型沙箱环境,该方法使用 bash 在沙箱内执行 shell 命令,支持基本的 shell 操作例如 `ls``cd``mkdir``rm` 等,并可以访问 `/data` 目录下的文件
7896

79-
- command: `str`, 需要执行的shell命令。
97+
本地模式下,该方法使用本地的 bash 解释器执行 shell 命令,工作目录为配置中的 `output_dir`,支持基本的 shell 操作,但不建议在生产环境中使用。
98+
99+
参数:
100+
101+
- command: `str`, 需要执行的 shell 命令。
102+
- timeout: `int`, 可选,执行超时时间(秒),不配置时使用工具默认值。
80103

81104
#### file_operation
82105

83-
该方法专用于docker类型沙箱环境,可以在沙箱内执行基本的文件操作,包括创建、读、写、删除、列出、判断是否存在等。
106+
沙箱模式下,该方法专用于 `docker` 类型沙箱环境,用于在沙箱内执行基本的文件操作,包括创建、读、写、删除、列出、判断是否存在等。文件路径基于沙箱内部路径,通常建议在挂载目录 `/data` 下进行读写。
107+
108+
本地模式下,该方法用于直接对本地文件系统进行基础操作,但所有路径都会被约束在 `output_dir` 目录内部,以避免越权访问。
109+
110+
参数:
111+
112+
- operation: `str`, 要执行的文件操作类型,可选值为 `'create'``'read'``'write'``'delete'``'list'``'exists'`
113+
- file_path: `str`, 要操作的文件或目录路径(沙箱模式下为容器内路径,本地模式下通常为相对于 `output_dir` 的路径,也可以传入受限的绝对路径)。
114+
- content: `str`, 可选,仅在 `write` 操作时需要,写入文件的内容。
115+
- encoding: `str`, 可选,文件编码,默认为 `utf-8`
116+
117+
#### reset_executor
84118

85-
- operation: `str`, 要执行的文件操作类型,可选值为'create'、'read'、'write'、'delete'、'list'、'exists'
119+
沙箱模式下,用于在沙箱环境崩溃或 Notebook 内变量状态混乱时重启沙箱环境或重建内核,清空所有状态
86120

87-
#### reset_sandbox
121+
本地模式下,专用于在 Notebook 执行出现问题时对本地 Jupyter Kernel 进行重启,清空当前会话的所有状态。
88122

89-
用于在沙箱环境崩溃时重启沙箱环境,例如notebook内变量状态混乱时重置所有状态。
123+
#### get_executor_info
90124

91-
#### get_sandbox_info
125+
沙箱模式下,获取当前沙箱状态与环境的基础信息,例如沙箱 ID、运行状态、资源限制配置和可用工具列表等。
92126

93-
获取当前沙箱状态与环境信息
127+
本地模式下,用于获取当前本地 Python 执行环境的基本信息,例如工作目录、是否已初始化、当前执行次数以及运行时长等
94128

95129
### MCP工具
96130

docs/zh/Projects/金融深度研究.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pip install akshare baostock
7373

7474
### 沙箱环境
7575

76+
Collector 与 Analyst 默认使用 Docker 沙箱以安全执行代码(可选):
77+
7678
```bash
7779
# 安装 ms-enclave(https://github.com/modelscope/ms-enclave)
7880
pip install ms-enclave docker websocket-client
@@ -81,6 +83,33 @@ pip install ms-enclave docker websocket-client
8183
bash projects/fin_research/tools/build_jupyter_image.sh
8284
```
8385

86+
如果不希望安装 Docker 等依赖,也可以选择配置本地代码执行工具,推荐将 `analyst.yaml``collector.yaml` 中默认的 `tools` 配置修改为:
87+
88+
```yaml
89+
tools:
90+
code_executor:
91+
mcp: false
92+
implementation: python_env
93+
exclude:
94+
- python_executor
95+
- shell_executor
96+
- file_operation
97+
```
98+
99+
该配置下默认依赖 Jupyter Kernel 执行代码,提供对环境变量的隔离,并支持 shell 命令执行,相应的依赖将在第一次运行代码时自动安装(包括数据分析和代码执行需要的依赖)。如果希望只使用更轻量的 Python 执行环境而不引入其他依赖,可以修改为:
100+
101+
```yaml
102+
tools:
103+
code_executor:
104+
mcp: false
105+
implementation: python_env
106+
exclude:
107+
- notebook_executor
108+
- file_operation
109+
```
110+
111+
该配置使用独立的 Python 执行器和 Shell 命令执行器,适合轻量级代码执行场景。
112+
84113
### 环境变量
85114
86115
在系统环境或 YAML 中配置 API Key:

ms_agent/app/fin_research.py

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,93 @@ def replace_image(match):
538538
return re.sub(pattern, replace_image, markdown_content)
539539

540540

541+
MARKDOWN_TABLE_STYLE_BLOCK = """
542+
.markdown-html-content .fin-table-wrapper {
543+
margin: 1.5rem 0;
544+
border: 1px solid rgba(15, 23, 42, 0.12);
545+
border-radius: 18px;
546+
overflow-x: auto;
547+
background: #ffffff;
548+
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08);
549+
}
550+
.markdown-html-content .fin-table-wrapper table {
551+
width: 100%;
552+
border-collapse: collapse;
553+
min-width: 640px;
554+
}
555+
.markdown-html-content .fin-table-wrapper table th,
556+
.markdown-html-content .fin-table-wrapper table td {
557+
border: 1px solid rgba(15, 23, 42, 0.12);
558+
padding: 12px 16px;
559+
text-align: left;
560+
font-size: 0.95rem;
561+
}
562+
.markdown-html-content .fin-table-wrapper table thead {
563+
background: rgba(59, 130, 246, 0.08);
564+
font-weight: 600;
565+
color: #0f172a;
566+
}
567+
.markdown-html-content .fin-table-wrapper table tbody tr:nth-child(even) {
568+
background: rgba(15, 23, 42, 0.03);
569+
}
570+
.markdown-html-content .fin-table-wrapper table caption {
571+
caption-side: bottom;
572+
padding: 0.75rem 0.5rem 0;
573+
color: #475569;
574+
font-size: 0.9rem;
575+
}
576+
.markdown-html-content .fin-table-wrapper table code {
577+
background: rgba(99, 102, 241, 0.12);
578+
color: #4c1d95;
579+
padding: 2px 6px;
580+
border-radius: 6px;
581+
}
582+
@media (max-width: 640px) {
583+
.markdown-html-content .fin-table-wrapper table {
584+
min-width: 520px;
585+
}
586+
}
587+
.dark .markdown-html-content .fin-table-wrapper {
588+
background: #0f172a;
589+
border-color: #1e293b;
590+
box-shadow: 0 12px 30px rgba(2, 6, 23, 0.45);
591+
}
592+
.dark .markdown-html-content .fin-table-wrapper table th,
593+
.dark .markdown-html-content .fin-table-wrapper table td {
594+
border-color: rgba(148, 163, 184, 0.35);
595+
color: #e2e8f0;
596+
}
597+
.dark .markdown-html-content .fin-table-wrapper table thead {
598+
background: rgba(59, 130, 246, 0.25);
599+
color: #f1f5f9;
600+
}
601+
.dark .markdown-html-content .fin-table-wrapper table tbody tr:nth-child(even) {
602+
background: rgba(15, 23, 42, 0.8);
603+
}
604+
.dark .markdown-html-content .fin-table-wrapper table caption {
605+
color: #cbd5f5;
606+
}
607+
.dark .markdown-html-content .fin-table-wrapper table code {
608+
background: #020617 !important;
609+
color: #fef9c3 !important;
610+
}
611+
"""
612+
613+
_TABLE_WRAPPER_PATTERN = re.compile(r'(<table\b.*?</table>)',
614+
flags=re.IGNORECASE | re.DOTALL)
615+
616+
617+
def _wrap_tables_with_container(html_content: str) -> str:
618+
"""Ensure markdown tables are wrapped for styling/scrolling."""
619+
def _inject_wrapper(match: re.Match) -> str:
620+
table_html = match.group(1)
621+
if 'fin-table-wrapper' in table_html:
622+
return table_html
623+
return f'<div class="fin-table-wrapper">{table_html}</div>'
624+
625+
return _TABLE_WRAPPER_PATTERN.sub(_inject_wrapper, html_content)
626+
627+
541628
def _render_markdown_html_core(markdown_content: str,
542629
add_permalink: bool = True) -> Tuple[str, str]:
543630
latex_placeholders = {}
@@ -580,6 +667,7 @@ def protect_latex(match):
580667
html_content = md.convert(protected_content)
581668
for placeholder, latex_formula in latex_placeholders.items():
582669
html_content = html_content.replace(placeholder, latex_formula)
670+
html_content = _wrap_tables_with_container(html_content)
583671
container_id = f'katex-content-{int(time.time() * 1_000_000)}'
584672
return html_content, container_id
585673

@@ -591,6 +679,9 @@ def _build_inline_markdown_html(html_content: str, container_id: str) -> str:
591679
href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"
592680
integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV"
593681
crossorigin="anonymous">
682+
<style>
683+
{MARKDOWN_TABLE_STYLE_BLOCK}
684+
</style>
594685
<div class="content-area">
595686
{html_content}
596687
</div>
@@ -714,18 +805,9 @@ def build_exportable_report_html(markdown_content: str,
714805
border-radius: 16px;
715806
box-shadow: 0 20px 40px rgba(15, 23, 42, 0.12);
716807
}
717-
.markdown-html-content table {
718-
width: 100%;
719-
border-collapse: collapse;
720-
margin: 1.5rem 0;
721-
font-size: 0.95rem;
722-
}
723-
.markdown-html-content table th,
724-
.markdown-html-content table td {
725-
border: 1px solid rgba(15, 23, 42, 0.15);
726-
padding: 12px 16px;
727-
text-align: left;
728-
}
808+
"""
809+
base_css += MARKDOWN_TABLE_STYLE_BLOCK
810+
base_css += """
729811
.markdown-html-content blockquote {
730812
border-left: 4px solid #6366f1;
731813
padding: 0.5rem 1.5rem;

0 commit comments

Comments
 (0)