mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
docs(sdk): add minimal Python example
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-10-minimal-preset-owns-rl-composition.md
|
||||
2026-08-10-minimal-preset-owns-rl-composition.md: 043f2e45e3fe4fbb92aa6652ce099ebfde09de55
|
||||
2026-08-10-minimal-preset-owns-rl-composition.zh.md: 83f243b56b25237f19fa288f87e15eee6a264c94
|
||||
2026-08-10-minimal-preset-owns-rl-composition.md: 002cad0827e969b322997821dc978db85e2955f3
|
||||
2026-08-10-minimal-preset-owns-rl-composition.zh.md: e957b57395c68b336695bdae07ea15a54ca1ea4e
|
||||
|
||||
@@ -12,7 +12,7 @@ The split also hid other drift. The preset mounted one-shot Bash rather than the
|
||||
|
||||
## Decision
|
||||
|
||||
The shipped `minimal` preset is the sole RL agent composition. It declares an entry-local PTY registry and local backend, persistent `bash` with the RL environment description and 300-second timeout, `str_replace_editor`, and an entry-local compaction backend. Tool presentation remains a deployment choice. The compaction policy keeps the RL threshold, absolute retention, generation cap, and retry count; model capacity comes from routed adapter metadata because `contextWindow` is no longer a compact-basic config field. The editor accepts no `requireAbsolutePath` setting because absolute paths are its unconditional contract.
|
||||
The shipped Web `minimal` preset is the sole Web owner of the RL agent composition. It declares an entry-local PTY registry and local backend, persistent `bash` with the RL environment description and 300-second timeout, `str_replace_editor`, and an entry-local compaction backend. Tool presentation remains a deployment choice. The compaction policy keeps the RL threshold, absolute retention, generation cap, and retry count; model capacity comes from routed adapter metadata because `contextWindow` is no longer a compact-basic config field. The editor accepts no `requireAbsolutePath` setting because absolute paths are its unconditional contract.
|
||||
|
||||
The preset persona is exactly `You are a helpful software engineer assistant.` and sets `complete: true`. A complete `PromptSection` participates in ordinary assembly so tools, contexts, variables, and cooperative listeners still resolve; after the `system-prompt/assemble` waterfall, the prompt registry restores a detached copy of that section as the sole system-prompt section. Multiple effective complete sections reject assembly. This final registry constraint prevents harness identity, Web orientation, tool guidance, or an assembly listener from appending prompt text.
|
||||
|
||||
@@ -22,6 +22,8 @@ The process-wide `core-web.cordis.yml` patch is absent. Browser UI, workspace at
|
||||
|
||||
System-prompt and persona package tests prove final complete-section enforcement, including waterfall mutation and duplicate rejection. The shipped-preset composition test asserts the exact prompt, Bash description, absolute editor schema, and two-tool catalog under the default native presentation. The keyless Web replay sends a real request through a `minimal` agent while global identity, Web surface text, and a test section are registered, then executes two persistent Bash calls to prove environment and cwd state survive and executes the editor through an absolute path.
|
||||
|
||||
The standalone [`minimal.cordis.yml`](../../../../examples/jsonrpc-agent/minimal.cordis.yml) mirrors the same prompt, tools, timeouts, and compaction policy for the bundled JSON-RPC runtime. Its keyless SDK replay asserts the assembled system prompt and two-tool catalog, executes persistent Bash across calls, and exercises the editor; the Python SDK tutorial provides the runnable entry point.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep `core-web.cordis.yml` as a compatibility patch.** Rejected because a process patch and a session preset are two independent owners for one agent contract; precedence makes either one capable of silently undoing the other.
|
||||
@@ -34,4 +36,4 @@ System-prompt and persona package tests prove final complete-section enforcement
|
||||
|
||||
## Consequences
|
||||
|
||||
The RL prompt is fixed rather than environment-overridable, and `minimal` is the only shipped place that states it. The model sees only persistent `bash` and `str_replace_editor`; shell state is per agent and disappears with that agent. The preset pays for its own PTY and compaction service instances, while other presets pay nothing for them. The local persistent-shell backend requires the supported POSIX terminal substrate, so this preset is not a Windows agent surface.
|
||||
The RL prompt is fixed rather than environment-overridable. The Web preset and standalone JSON-RPC example state the same contract for their respective launch surfaces. The model sees only persistent `bash` and `str_replace_editor`; shell state is per agent and disappears with that agent. The preset pays for its own PTY and compaction service instances, while other presets pay nothing for them. The local persistent-shell backend requires the supported POSIX terminal substrate, so this preset is not a Windows agent surface.
|
||||
|
||||
@@ -12,7 +12,7 @@ Web surface 同时由两个位置定义与 Claude SWE 兼容的 RL agent(智
|
||||
|
||||
## 决策
|
||||
|
||||
随附的 `minimal` preset 是 RL agent 组合的唯一所有者。它声明 entry 本地的 PTY 注册表与本地后端、带 RL 环境描述且超时为 300 秒的持久 `bash`、`str_replace_editor`,以及 entry 本地的压缩后端。工具呈现仍由部署选择。压缩策略保留 RL 的阈值、绝对保留量、生成上限和重试次数;模型容量来自经路由选定的适配器元数据,因为 `contextWindow` 已不再是 compact-basic 的配置字段。编辑器不接受 `requireAbsolutePath` 设置,因为要求绝对路径是它的无条件约定。
|
||||
随附的 Web `minimal` preset 是 RL agent 组合在 Web 中的唯一所有者。它声明 entry 本地的 PTY 注册表与本地后端、带 RL 环境描述且超时为 300 秒的持久 `bash`、`str_replace_editor`,以及 entry 本地的压缩后端。工具呈现仍由部署选择。压缩策略保留 RL 的阈值、绝对保留量、生成上限和重试次数;模型容量来自经路由选定的适配器元数据,因为 `contextWindow` 已不再是 compact-basic 的配置字段。编辑器不接受 `requireAbsolutePath` 设置,因为要求绝对路径是它的无条件约定。
|
||||
|
||||
preset persona 恰好是 `You are a helpful software engineer assistant.`,并设置 `complete: true`。complete `PromptSection` 参与常规组装,因此工具、上下文、变量和协作式监听器仍会解析;`system-prompt/assemble` waterfall(瀑布式事件)结束后,提示词注册表会将该段落的独立副本恢复为唯一的系统提示词段落。存在多个有效 complete 段时,组装会被拒绝。这项最终注册表约束可防止 harness 身份、Web 定位、工具引导或组装监听器追加提示词文本。
|
||||
|
||||
@@ -22,6 +22,8 @@ preset persona 恰好是 `You are a helpful software engineer assistant.`,并
|
||||
|
||||
系统提示词与 persona 包测试证明了 complete 段的最终约束,包括 waterfall 修改与重复项拒绝。交付 preset 组合测试在默认原生呈现下断言精确的提示词、Bash 描述、要求绝对路径的编辑器 schema 和双工具目录。无密钥 Web 回放通过 `minimal` agent 发送一个真实请求,同时注册全局身份、Web surface 文本和一个测试段落;随后执行两次持久 Bash 调用,证明环境与 cwd 状态能够保留,并通过绝对路径执行编辑器。
|
||||
|
||||
独立的 [`minimal.cordis.yml`](../../../../examples/jsonrpc-agent/minimal.cordis.yml) 为内置 JSON-RPC 运行时复现相同的提示词、工具、超时和压缩策略。其无密钥 SDK 回放会断言组装后的系统提示词与双工具目录,跨调用执行持久 Bash,并使用编辑器;Python SDK 教程提供可运行的入口。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**将 `core-web.cordis.yml` 保留为兼容 patch。** 被拒绝,因为进程 patch 与会话 preset 是同一 agent 约定的两个独立所有者;优先级会使任意一方都能静默撤销另一方的配置。
|
||||
@@ -34,4 +36,4 @@ preset persona 恰好是 `You are a helpful software engineer assistant.`,并
|
||||
|
||||
## 后果
|
||||
|
||||
RL 提示词固定不变,不能通过环境覆盖,且 `minimal` 是交付内容中唯一声明该提示词的位置。模型只看到持久 `bash` 与 `str_replace_editor`;shell 状态按 agent 隔离,并随该 agent 一并消失。preset 为自身的 PTY 与压缩服务实例承担开销,其他 preset 无需承担。持久 shell 的本地后端需要受支持的 POSIX 终端基础环境,因此该 preset 不适用于 Windows agent surface。
|
||||
RL 提示词固定不变,不能通过环境覆盖。Web preset 与独立 JSON-RPC 示例分别在各自的启动界面声明相同的约定。模型只看到持久 `bash` 与 `str_replace_editor`;shell 状态按 agent 隔离,并随该 agent 一并消失。preset 为自身的 PTY 与压缩服务实例承担开销,其他 preset 无需承担。持久 shell 的本地后端需要受支持的 POSIX 终端基础环境,因此该 preset 不适用于 Windows agent surface。
|
||||
|
||||
6
docs/user/guide/python-sdk-minimal.i18n.yaml
Normal file
6
docs/user/guide/python-sdk-minimal.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/guide/python-sdk-minimal.md
|
||||
python-sdk-minimal.md: 9d46278aeec625afdf30678806bc90104be00b66
|
||||
python-sdk-minimal.zh.md: ec06a205c680c1d7a83be5f949c7ff4d719defe4
|
||||
95
docs/user/guide/python-sdk-minimal.md
Normal file
95
docs/user/guide/python-sdk-minimal.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Run the minimal agent with the Python SDK
|
||||
|
||||
English | [中文](python-sdk-minimal.zh.md)
|
||||
|
||||
This tutorial runs the minimal agent without the Web UI. The checked-in Cordis composition fixes the system prompt, tool catalog, persistent-shell behavior, and compaction policy so SDK runs use the same model-facing contract as the Web `minimal` preset.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10 or newer
|
||||
- Linux x64, Linux arm64, or macOS arm64
|
||||
- A DeepSeek-compatible API endpoint and credential
|
||||
- An isolated workspace that the agent may modify
|
||||
|
||||
Create a virtual environment and install the SDK with its same-version bundled runtime:
|
||||
|
||||
```sh
|
||||
python -m venv .venv
|
||||
. .venv/bin/activate
|
||||
python -m pip install deepseek-harness
|
||||
```
|
||||
|
||||
The runtime wheel contains the JSON-RPC executable and every plugin used by the complete [`minimal.cordis.yml`](../../../examples/jsonrpc-agent/minimal.cordis.yml), so an installed SDK does not need Node.js.
|
||||
|
||||
## Run the checked-in example
|
||||
|
||||
Set the credential in the environment. Set `DEEPSEEK_BASE_URL` as well when the model is served by an OpenAI-compatible proxy rather than the default DeepSeek endpoint.
|
||||
|
||||
```sh
|
||||
export DEEPSEEK_API_KEY=sk-your-key-here
|
||||
# export DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1
|
||||
```
|
||||
|
||||
Run one task from the repository checkout:
|
||||
|
||||
```sh
|
||||
python examples/jsonrpc-agent/minimal.py \
|
||||
--workspace /absolute/path/to/workspace \
|
||||
--session-root /absolute/path/to/trajectories \
|
||||
--session-id example-001 \
|
||||
"Inspect the repository and fix the failing tests."
|
||||
```
|
||||
|
||||
The script prints the final assistant response. The session root receives the JSONL trajectory, including the assembled model request and every tool call.
|
||||
|
||||
## Use the SDK in your own program
|
||||
|
||||
The example is a thin wrapper around this SDK call:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
from deepseek_harness import DeepSeekHarness
|
||||
|
||||
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
|
||||
workspace = Path("/absolute/path/to/workspace").resolve()
|
||||
sessions = Path("/absolute/path/to/trajectories").resolve()
|
||||
|
||||
with DeepSeekHarness(
|
||||
provider="deepseek-official",
|
||||
model="deepseek-v4-flash",
|
||||
max_tokens=49_152,
|
||||
cwd=str(workspace),
|
||||
session_root=str(sessions),
|
||||
cordis=str(config),
|
||||
) as harness:
|
||||
result = harness.run(
|
||||
"Inspect the repository and fix the failing tests.",
|
||||
session_id="example-001",
|
||||
)
|
||||
|
||||
print(result.final_response)
|
||||
```
|
||||
|
||||
`DeepSeekHarness` starts the bundled JSON-RPC runtime lazily and reuses it until the context manager exits. Reusing the same harness and session id across calls also preserves the session-owned Bash process, including its working directory, exported variables, and shell functions.
|
||||
|
||||
## Contract reproduced by the configuration
|
||||
|
||||
| Surface | Fixed value |
|
||||
|---|---|
|
||||
| System prompt | `You are a helpful software engineer assistant.` |
|
||||
| Model-facing tools | Persistent `bash` and `str_replace_editor` only |
|
||||
| Bash timeout | 300 seconds |
|
||||
| Editor output limit | 16,000 characters |
|
||||
| Compaction | Trigger ratio `0.8`, retain `20,480` tokens, summary cap `8,192` tokens, one retry |
|
||||
| Session persistence | Uncompressed JSONL under `DSH_SESSION_ROOT` |
|
||||
|
||||
The configuration omits harness identity, workspace prompt text, skills, one-shot Bash, task tools, and every other model-facing plugin. Filesystem policy facts are logged as runtime user context rather than appended to the system prompt. The editor requires absolute paths as an unconditional current contract, so the obsolete `requireAbsolutePath` option is absent.
|
||||
|
||||
## Keep runs reproducible
|
||||
|
||||
For comparable trajectories, pin the Harness commit and Python package version together, retain the exact Cordis file, and record the provider, model, endpoint, `max_tokens`, task input, workspace state, and session id for every run. Start independent runs with a clean workspace and a fresh session id; reuse a session only when multi-turn state is intentional.
|
||||
|
||||
The composition uses `danger-full-access`. Run it only inside a disposable checkout or container: Bash and the editor can modify any path allowed to the runtime process. The persistent PTY backend requires a POSIX terminal substrate and is not a Windows agent surface.
|
||||
|
||||
For the complete SDK lifecycle and result contract, see the [Python SDK reference](../../../python/sdk/README.md). For Cordis composition syntax, see [Configuration](./config.md).
|
||||
95
docs/user/guide/python-sdk-minimal.zh.md
Normal file
95
docs/user/guide/python-sdk-minimal.zh.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# 使用 Python SDK 运行极简 agent(智能体)
|
||||
|
||||
[English](python-sdk-minimal.md) | 中文
|
||||
|
||||
本教程介绍如何在不使用 Web UI 的情况下运行极简 agent。仓库内置的 Cordis 组合固定了系统提示词、工具目录、持久 shell 行为和压缩(compaction)策略,因此 SDK 运行与 Web `minimal` preset 使用相同的面向模型约定。
|
||||
|
||||
## 前置要求
|
||||
|
||||
- Python 3.10 或更高版本
|
||||
- Linux x64、Linux arm64 或 macOS arm64
|
||||
- DeepSeek 兼容的 API 端点与凭据
|
||||
- agent 可以修改的隔离 workspace
|
||||
|
||||
请创建虚拟环境,并安装 SDK 及其同版本内置运行时:
|
||||
|
||||
```sh
|
||||
python -m venv .venv
|
||||
. .venv/bin/activate
|
||||
python -m pip install deepseek-harness
|
||||
```
|
||||
|
||||
运行时 wheel 包含 JSON-RPC 可执行文件,以及完整 [`minimal.cordis.yml`](../../../examples/jsonrpc-agent/minimal.cordis.yml) 使用的每个插件,因此安装后的 SDK 不需要 Node.js。
|
||||
|
||||
## 运行仓库内置示例
|
||||
|
||||
请在环境中设置凭据。如果模型不是由默认 DeepSeek 端点提供,而是通过 OpenAI 兼容代理提供,还需要设置 `DEEPSEEK_BASE_URL`。
|
||||
|
||||
```sh
|
||||
export DEEPSEEK_API_KEY=sk-your-key-here
|
||||
# export DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1
|
||||
```
|
||||
|
||||
从仓库 checkout 运行一个任务:
|
||||
|
||||
```sh
|
||||
python examples/jsonrpc-agent/minimal.py \
|
||||
--workspace /absolute/path/to/workspace \
|
||||
--session-root /absolute/path/to/trajectories \
|
||||
--session-id example-001 \
|
||||
"Inspect the repository and fix the failing tests."
|
||||
```
|
||||
|
||||
脚本会打印 assistant 的最终回复。会话根目录会收到 JSONL 运行轨迹,其中包含组装后的模型请求与每次工具调用。
|
||||
|
||||
## 在自己的程序中使用 SDK
|
||||
|
||||
该示例是以下 SDK 调用的轻量包装层:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
from deepseek_harness import DeepSeekHarness
|
||||
|
||||
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
|
||||
workspace = Path("/absolute/path/to/workspace").resolve()
|
||||
sessions = Path("/absolute/path/to/trajectories").resolve()
|
||||
|
||||
with DeepSeekHarness(
|
||||
provider="deepseek-official",
|
||||
model="deepseek-v4-flash",
|
||||
max_tokens=49_152,
|
||||
cwd=str(workspace),
|
||||
session_root=str(sessions),
|
||||
cordis=str(config),
|
||||
) as harness:
|
||||
result = harness.run(
|
||||
"Inspect the repository and fix the failing tests.",
|
||||
session_id="example-001",
|
||||
)
|
||||
|
||||
print(result.final_response)
|
||||
```
|
||||
|
||||
`DeepSeekHarness` 会延迟启动内置 JSON-RPC 运行时,并持续复用,直至退出上下文管理器。在多次调用中复用同一个 harness 和 session id,还会保留该会话拥有的 Bash 进程,包括其工作目录、已导出的变量与 shell 函数。
|
||||
|
||||
## 配置复现的约定
|
||||
|
||||
| 方面 | 固定值 |
|
||||
|---|---|
|
||||
| 系统提示词 | `You are a helpful software engineer assistant.` |
|
||||
| 面向模型的工具 | 仅持久 `bash` 与 `str_replace_editor` |
|
||||
| Bash 超时 | 300 秒 |
|
||||
| 编辑器输出上限 | 16,000 个字符 |
|
||||
| 压缩 | 触发比例 `0.8`、保留 `20,480` 个 token、摘要上限 `8,192` 个 token、重试 1 次 |
|
||||
| 会话持久化 | `DSH_SESSION_ROOT` 下未压缩的 JSONL |
|
||||
|
||||
该配置省略了 harness 身份、workspace 提示词文本、skill(技能)、一次性 Bash、任务工具和其他所有面向模型的插件。文件系统策略事实记录为运行时用户上下文,而不会追加到系统提示词中。编辑器无条件要求绝对路径,因此配置中没有已经废弃的 `requireAbsolutePath` 选项。
|
||||
|
||||
## 保持运行可复现
|
||||
|
||||
为了让运行轨迹可复现且便于比较,请配套固定 Harness commit 与 Python 包版本,保留确切的 Cordis 文件,并为每次运行记录提供方、模型、端点、`max_tokens`、任务输入、workspace 状态和 session id。独立运行应使用干净的 workspace 和新的 session id;只有有意保留多轮状态时才复用会话。
|
||||
|
||||
该组合使用 `danger-full-access`。只能在可丢弃的 checkout 或容器内运行:Bash 与编辑器可以修改运行时进程有权访问的任何路径。持久 PTY 后端需要 POSIX 终端环境,因此该模式不适用于 Windows agent。
|
||||
|
||||
完整的 SDK 生命周期与结果约定见 [Python SDK 参考](../../../python/sdk/README.md)。Cordis 组合语法见[配置](./config.md)。
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/guide/quickstart.md
|
||||
quickstart.md: ce196641f205324334533c025b4ac1dc791f857d
|
||||
quickstart.zh.md: 3a5d6d0748ec0c7ec83c74570d0fad1e8d66a97c
|
||||
quickstart.md: 13d5b2196282394619747e36ecddfa1d87f61c8d
|
||||
quickstart.zh.md: 3d775db9280b43bbee2de37f7327fe8d2bd3a121
|
||||
|
||||
@@ -57,6 +57,7 @@ Open `http://127.0.0.1:3080`. The agent can read and write files, run commands,
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Run the minimal agent with Python](./python-sdk-minimal.md) — use the fixed two-tool composition without the Web UI
|
||||
- [Configure models](./providers.md) — reach providers beyond DeepSeek, and custom gateways
|
||||
- [Configuration](./config.md) — understand the `cordis.yml` format
|
||||
- [Develop a plugin](../develop/basic/) — build your own tool or backend
|
||||
|
||||
@@ -57,6 +57,7 @@ pnpm run dsh web
|
||||
|
||||
## 下一步
|
||||
|
||||
- [使用 Python 运行极简 agent](./python-sdk-minimal.md) — 无需 Web UI,即可使用固定的双工具组合
|
||||
- [配置模型](./providers.md) — 接入 DeepSeek 之外的提供方与自定义网关
|
||||
- [配置文件](./config.md) — 了解 `cordis.yml` 的格式
|
||||
- [开发插件](../develop/basic/) — 编写自己的工具或后端
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write examples/jsonrpc-agent/README.md
|
||||
README.md: bcc1027d2edb30ab374dfa2ed13ad8e6360d923b
|
||||
README.zh.md: ce255e4dd70bf8c5c6edc51afbe03bb4c66560a0
|
||||
README.md: 863b39eb9c7c65d36ceca77e945379fdd1d5fe22
|
||||
README.zh.md: a8334320896d2b16e166ef0f0ec300ab1cb0ca93
|
||||
|
||||
@@ -26,11 +26,11 @@ The surrounding runtime also loads JSONL session persistence and automatic conte
|
||||
|
||||
Pass the config path through the Python SDK's `cordis` option or `DSH_CORDIS_CONFIG`. The bundled executable already carries every plugin named by this file; the target machine does not need Node.js.
|
||||
|
||||
## Persistent tools variant
|
||||
## Minimal variant
|
||||
|
||||
[`persistent-tools.cordis.yml`](persistent-tools.cordis.yml) is a minimal runnable variant whose model-facing surface is exactly:
|
||||
[`minimal.cordis.yml`](minimal.cordis.yml) is the complete standalone counterpart of the Web `minimal` preset. It fixes the system prompt and compaction policy, and its model-facing surface is exactly:
|
||||
|
||||
- owner-scoped persistent `bash`
|
||||
- `str_replace_editor` with `view`, `create`, `str_replace`, and `insert`
|
||||
|
||||
It composes the local PTY, filesystem intent policy, and session sandbox policy.
|
||||
It composes the local PTY, filesystem intent policy, session sandbox policy, and JSONL persistence needed by the bundled runtime. [`minimal.py`](minimal.py) runs it through the Python SDK; the [minimal Python SDK tutorial](../../docs/user/guide/python-sdk-minimal.md) covers setup, repeatable runs, and the security boundary.
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
|
||||
通过 Python SDK 的 `cordis` 选项或 `DSH_CORDIS_CONFIG` 传入配置路径。内置可执行文件已携带此文件中指定的每个插件;目标机器无需 Node.js。
|
||||
|
||||
## 持久化工具变体
|
||||
## 极简变体
|
||||
|
||||
[`persistent-tools.cordis.yml`](persistent-tools.cordis.yml) 是一个最小可运行变体,面向模型的能力严格只有:
|
||||
[`minimal.cordis.yml`](minimal.cordis.yml) 是 Web `minimal` preset 的完整独立版本。它固定系统提示词与压缩策略,面向模型的能力严格只有:
|
||||
|
||||
- 所有者作用域内持久化的 `bash`
|
||||
- 提供 `view`、`create`、`str_replace` 与 `insert` 的 `str_replace_editor`
|
||||
|
||||
它组合了本地 PTY、文件系统意图策略与会话沙箱策略。
|
||||
它组合了内置运行时所需的本地 PTY、文件系统意图策略、会话沙箱策略与 JSONL 持久化。[`minimal.py`](minimal.py) 通过 Python SDK 运行该配置;[极简 Python SDK 教程](../../docs/user/guide/python-sdk-minimal.md)介绍设置方式、可重复运行与安全边界。
|
||||
|
||||
91
examples/jsonrpc-agent/minimal.cordis.yml
Normal file
91
examples/jsonrpc-agent/minimal.cordis.yml
Normal file
@@ -0,0 +1,91 @@
|
||||
# Complete unattended minimal-agent composition for the Python SDK. The model
|
||||
# sees one fixed system prompt and only the owner-scoped persistent Bash and
|
||||
# string-replace editor tools.
|
||||
|
||||
- id: jsonrpc
|
||||
name: '@deepseek-ai/dsh-jsonrpc'
|
||||
config:
|
||||
maxTokensAsSuccess: false
|
||||
|
||||
- id: llm-deepseek
|
||||
name: '@deepseek-ai/dsh-llm-deepseek'
|
||||
|
||||
- id: sandbox
|
||||
name: '@deepseek-ai/dsh-sandbox-local'
|
||||
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: danger-full-access
|
||||
workspaceRoot: !!js process.env.DSH_CWD ?? process.cwd()
|
||||
|
||||
- id: subprocess
|
||||
name: '@deepseek-ai/dsh-subprocess-local'
|
||||
|
||||
- id: pty
|
||||
name: '@deepseek-ai/dsh-pty'
|
||||
|
||||
- id: pty-local
|
||||
name: '@deepseek-ai/dsh-pty-local'
|
||||
config:
|
||||
timeoutMs: 300000
|
||||
|
||||
# The sandbox-aware filesystem backend applies the same per-session policy as
|
||||
# Bash. danger-full-access permits unrestricted workspace behavior while
|
||||
# keeping one policy boundary for both tools.
|
||||
- id: fs-sandbox
|
||||
name: '@deepseek-ai/dsh-fs-sandbox'
|
||||
config:
|
||||
cwd: !!js process.env.DSH_CWD ?? process.cwd()
|
||||
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
|
||||
- id: agent-spine
|
||||
name: '@deepseek-ai/dsh-agent-spine-demo'
|
||||
config:
|
||||
includeHarnessIdentity: false
|
||||
persona: You are a helpful software engineer assistant.
|
||||
workspaceContext: false
|
||||
skills:
|
||||
enabled: false
|
||||
toolBash: false
|
||||
toolTasks: false
|
||||
|
||||
- id: persistent-bash
|
||||
name: '@deepseek-ai/dsh-tool-bash-persistent'
|
||||
config:
|
||||
timeoutMs: 300000
|
||||
description: |-
|
||||
Run commands in a bash shell
|
||||
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
|
||||
* You don't have access to the internet via this tool.
|
||||
* You do have access to a mirror of common linux and python packages via apt and pip.
|
||||
* State is persistent across command calls and discussions with the user.
|
||||
* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.
|
||||
* Please avoid commands that may produce a very large amount of output.
|
||||
* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.
|
||||
|
||||
- id: str-replace-editor
|
||||
name: '@deepseek-ai/dsh-tool-str-replace-editor'
|
||||
config:
|
||||
maxOutputChars: 16000
|
||||
|
||||
- id: sessions
|
||||
name: '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
config:
|
||||
root: !!js process.env.DSH_SESSION_ROOT ?? './.sessions'
|
||||
compression: none
|
||||
|
||||
- id: token-meter
|
||||
name: '@deepseek-ai/dsh-token-meter'
|
||||
|
||||
- id: compact-basic
|
||||
name: '@deepseek-ai/dsh-compact-basic'
|
||||
config:
|
||||
thresholdRatio: 0.8
|
||||
retainTokens: 20480
|
||||
summarizationProvider: ''
|
||||
summarizationModel: ''
|
||||
maxTokens: 8192
|
||||
compactionRetries: 1
|
||||
42
examples/jsonrpc-agent/minimal.py
Normal file
42
examples/jsonrpc-agent/minimal.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run one minimal-agent turn through the bundled Python SDK runtime."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from deepseek_harness import DeepSeekHarness
|
||||
|
||||
|
||||
CONFIG = Path(__file__).with_name("minimal.cordis.yml")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Parse one task and print the agent's final response."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("prompt", help="Task for the minimal agent")
|
||||
parser.add_argument("--workspace", type=Path, default=Path.cwd())
|
||||
parser.add_argument("--session-root", type=Path, default=Path(".dsh-sessions"))
|
||||
parser.add_argument("--session-id")
|
||||
parser.add_argument("--provider", default="deepseek-official")
|
||||
parser.add_argument("--model", default="deepseek-v4-flash")
|
||||
parser.add_argument("--max-tokens", type=int)
|
||||
args = parser.parse_args()
|
||||
|
||||
workspace = args.workspace.resolve()
|
||||
session_root = args.session_root.resolve()
|
||||
with DeepSeekHarness(
|
||||
provider=args.provider,
|
||||
model=args.model,
|
||||
max_tokens=args.max_tokens,
|
||||
cwd=str(workspace),
|
||||
session_root=str(session_root),
|
||||
cordis=str(CONFIG.resolve()),
|
||||
) as harness:
|
||||
result = harness.run(args.prompt, session_id=args.session_id)
|
||||
print(result.final_response)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,12 +1,10 @@
|
||||
# Keyless replay keeps the persistent-tool composition intact and replaces
|
||||
# only its live DeepSeek adapter with the fixture-backed provider. The catalog
|
||||
# below claims the same `deepseek-official` route the agent asks for: an
|
||||
# unowned route makes the SDK server mount the real adapter, which then demands
|
||||
# a key this keyless lane has no way to supply.
|
||||
# Keyless replay keeps the complete minimal composition intact and replaces
|
||||
# only its live DeepSeek adapter with the fixture-backed provider. The replay
|
||||
# catalog claims the same route initialized by the SDK.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ./persistent-tools.cordis.yml
|
||||
path: ./minimal.cordis.yml
|
||||
patches:
|
||||
- id: llm-deepseek
|
||||
name: '@deepseek-ai/dsh-llm-deepseek'
|
||||
@@ -1,59 +0,0 @@
|
||||
# Minimal unattended composition for the persistent Bash and string-replace
|
||||
# editor. It is runnable through the JSON-RPC example runtime and intentionally
|
||||
# keeps the model-facing surface to exactly these two tools.
|
||||
|
||||
- id: jsonrpc
|
||||
name: '@deepseek-ai/dsh-jsonrpc'
|
||||
|
||||
- id: llm-deepseek
|
||||
name: '@deepseek-ai/dsh-llm-deepseek'
|
||||
config:
|
||||
|
||||
- id: sandbox
|
||||
name: '@deepseek-ai/dsh-sandbox-local'
|
||||
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: danger-full-access
|
||||
workspaceRoot: !!js process.env.DSH_CWD ?? process.cwd()
|
||||
|
||||
- id: subprocess
|
||||
name: '@deepseek-ai/dsh-subprocess-local'
|
||||
|
||||
- id: pty
|
||||
name: '@deepseek-ai/dsh-pty'
|
||||
|
||||
- id: pty-local
|
||||
name: '@deepseek-ai/dsh-pty-local'
|
||||
|
||||
- id: fs-sandbox
|
||||
name: '@deepseek-ai/dsh-fs-sandbox'
|
||||
config:
|
||||
cwd: !!js process.env.DSH_CWD ?? process.cwd()
|
||||
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
|
||||
- id: agent-spine
|
||||
name: '@deepseek-ai/dsh-agent-spine-demo'
|
||||
config:
|
||||
includeHarnessIdentity: false
|
||||
persona: 'You are a helpful software engineer assistant.'
|
||||
workspaceContext: false
|
||||
skills:
|
||||
enabled: false
|
||||
toolBash: false
|
||||
toolTasks: false
|
||||
|
||||
- id: persistent-bash
|
||||
name: '@deepseek-ai/dsh-tool-bash-persistent'
|
||||
|
||||
- id: str-replace-editor
|
||||
name: '@deepseek-ai/dsh-tool-str-replace-editor'
|
||||
|
||||
- id: sessions
|
||||
name: '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
config:
|
||||
root: !!js process.env.DSH_SESSION_ROOT ?? './.sessions'
|
||||
compression: none
|
||||
@@ -33,11 +33,21 @@ const testsDir = dirOf(import.meta.url)
|
||||
const snapshotsDir = join(testsDir, 'snapshots')
|
||||
const liveConfig = join(testsDir, '..', 'cordis.yml')
|
||||
const replayConfig = join(testsDir, '..', 'cordis.snapshot.yml')
|
||||
const persistentToolsLiveConfig = join(testsDir, '..', 'persistent-tools.cordis.yml')
|
||||
const persistentToolsReplayConfig = join(testsDir, '..', 'persistent-tools.snapshot.cordis.yml')
|
||||
const minimalLiveConfig = join(testsDir, '..', 'minimal.cordis.yml')
|
||||
const minimalReplayConfig = join(testsDir, '..', 'minimal.snapshot.cordis.yml')
|
||||
const runtimeBin = fileURLToPath(new URL('../../../packages/examples/jsonrpc-demo/src/bin.ts', import.meta.url))
|
||||
const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
|
||||
|
||||
const MINIMAL_SYSTEM_PROMPT = 'You are a helpful software engineer assistant.'
|
||||
const MINIMAL_BASH_DESCRIPTION = `Run commands in a bash shell
|
||||
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
|
||||
* You don't have access to the internet via this tool.
|
||||
* You do have access to a mirror of common linux and python packages via apt and pip.
|
||||
* State is persistent across command calls and discussions with the user.
|
||||
* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.
|
||||
* Please avoid commands that may produce a very large amount of output.
|
||||
* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.`
|
||||
|
||||
const mode = process.env.DSH_SNAPSHOT ?? 'replay'
|
||||
const recording = mode === 'record'
|
||||
const refreshing = mode === 'refresh'
|
||||
@@ -61,6 +71,10 @@ interface SdkScenario {
|
||||
expectedFiles?: Readonly<Record<string, string>>
|
||||
/** Assembled model-facing tool names and required argument keys. */
|
||||
expectedTools?: Readonly<Record<string, readonly string[]>>
|
||||
/** Exact assembled system prompt for the root request. */
|
||||
expectedSystem?: string
|
||||
/** Exact model-facing descriptions for selected tools. */
|
||||
expectedToolDescriptions?: Readonly<Record<string, string>>
|
||||
/** Stable policy-context clauses the real assembled request must include or omit. */
|
||||
policyContext?: { includes: readonly string[]; excludes: readonly string[] }
|
||||
}
|
||||
@@ -89,9 +103,11 @@ const SCENARIOS: SdkScenario[] = [
|
||||
prompt: 'Prove that bash state persists. Then create {{cwd}}/note.txt with a tab-indented line, view it, replace that literal tab-indented line, and make the persistent shell exit with code 9.',
|
||||
sessionId: 'persistent-tools-snapshot',
|
||||
children: 0,
|
||||
configs: { live: persistentToolsLiveConfig, replay: persistentToolsReplayConfig },
|
||||
configs: { live: minimalLiveConfig, replay: minimalReplayConfig },
|
||||
expectedFiles: { 'note.txt': 'target:\n\tnew\n' },
|
||||
expectedTools: { bash: ['command'], str_replace_editor: ['command', 'path'] },
|
||||
expectedSystem: MINIMAL_SYSTEM_PROMPT,
|
||||
expectedToolDescriptions: { bash: MINIMAL_BASH_DESCRIPTION },
|
||||
policyContext: {
|
||||
includes: ['Current DSH file policy: danger-full-access.', 'file modifications by available operations'],
|
||||
excludes: ['write and edit tools', 'terminal sessions', 'one-shot bash commands'],
|
||||
@@ -125,16 +141,33 @@ async function persistedLogs(sessionsRoot: string): Promise<PersistedLog[]> {
|
||||
|
||||
interface LoggedRequestHeader {
|
||||
type?: string
|
||||
data?: { header?: { system?: unknown; tools?: Array<{ name: string; parameters: { required?: string[] } }> } }
|
||||
data?: { header?: { system?: unknown; tools?: LoggedTool[] } }
|
||||
}
|
||||
|
||||
function assembledToolRequirements(log: PersistedLog): Record<string, string[]> {
|
||||
interface LoggedTool {
|
||||
readonly name: string
|
||||
readonly description?: unknown
|
||||
readonly parameters: { readonly required?: string[] }
|
||||
}
|
||||
|
||||
function assembledTools(log: PersistedLog): LoggedTool[] {
|
||||
const event = log.content.trimEnd().split('\n')
|
||||
.map(line => JSON.parse(line) as LoggedRequestHeader)
|
||||
.find(candidate => candidate.type === 'request/header')
|
||||
const tools = event?.data?.header?.tools
|
||||
if (tools === undefined) throw new Error('session log has no request/header tools')
|
||||
return Object.fromEntries(tools.map(tool => [tool.name, tool.parameters.required ?? []]))
|
||||
return tools
|
||||
}
|
||||
|
||||
function assembledToolRequirements(log: PersistedLog): Record<string, string[]> {
|
||||
return Object.fromEntries(assembledTools(log).map(tool => [tool.name, tool.parameters.required ?? []]))
|
||||
}
|
||||
|
||||
function assembledToolDescriptions(log: PersistedLog): Record<string, string> {
|
||||
return Object.fromEntries(assembledTools(log).map((tool) => {
|
||||
if (typeof tool.description !== 'string') throw new Error(`tool ${tool.name} has no description`)
|
||||
return [tool.name, tool.description]
|
||||
}))
|
||||
}
|
||||
|
||||
function assembledSystem(log: PersistedLog): string {
|
||||
@@ -400,6 +433,16 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => {
|
||||
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
|
||||
expect(assembledToolRequirements(parent)).toEqual(scenario.expectedTools)
|
||||
}
|
||||
if (scenario.expectedSystem !== undefined) {
|
||||
const parent = ordered[0]
|
||||
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
|
||||
expect(assembledSystem(parent)).toBe(scenario.expectedSystem)
|
||||
}
|
||||
if (scenario.expectedToolDescriptions !== undefined) {
|
||||
const parent = ordered[0]
|
||||
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
|
||||
expect(assembledToolDescriptions(parent)).toMatchObject(scenario.expectedToolDescriptions)
|
||||
}
|
||||
if (scenario.policyContext !== undefined) {
|
||||
const parent = ordered[0]
|
||||
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write python/sdk/README.md
|
||||
README.md: 2d545688c58a2f1b755e647d7cda9555249e41c4
|
||||
README.zh.md: b335d75aedc3a145771b23ea5d408315cae9a3e3
|
||||
README.md: f2cd6b9f1a978fe5519df3965fbe6679a72d56f5
|
||||
README.zh.md: dfa25d1d09d6edf24ebf1df3faa925493aeef7de
|
||||
|
||||
@@ -5,8 +5,7 @@ English | [中文](README.zh.md)
|
||||
Python subprocess SDK for driving DeepSeek Harness over JSON-RPC stdio. The
|
||||
runtime inherits normal DeepSeek Harness environment variables such as
|
||||
`DEEPSEEK_BASE_URL` and `DEEPSEEK_API_KEY`, so callers can use real model
|
||||
endpoints directly or point those variables at a local proxy during
|
||||
benchmark runs.
|
||||
endpoints directly or point those variables at a local proxy.
|
||||
|
||||
Installing `deepseek-harness` installs the exact same-version `deepseek-harness-runtime-bin` platform wheel. The normal entry point therefore needs no executable argument:
|
||||
|
||||
@@ -35,6 +34,8 @@ with DeepSeekHarness(
|
||||
|
||||
`provider` selects a provider route registered by the chosen Cordis composition; `model` is the model id resolved by that adapter. `max_tokens` is an optional positive per-request output-token cap for the root agent and its in-process descendants; omission leaves the provider default in control. Compaction summaries keep the separate limit configured by their compaction plugin. The bundled default composition registers `deepseek-official`. A custom composition can mount `llm-pi-ai`, configure provider-specific credentials/endpoints there, and select any provider/model present in pi-ai's installed catalog.
|
||||
|
||||
The [minimal-agent tutorial](../../docs/user/guide/python-sdk-minimal.md) provides a complete standalone Cordis file and runnable SDK example for using the two-tool minimal mode without the Web UI.
|
||||
|
||||
`Session.run()` owns an activity interval from its prompt's durable inbox receipt through the next whole-agent idle and returns `RunResult(session_id, final_response, events, notifications, session_root)`. The result has no prompt-level status or turn reason: `final_response` is the last committed root-session assistant text in the interval, not an output causally assigned to the prompt. Steering, injected context, and other queued work may contribute before idle.
|
||||
|
||||
`HarnessClient` retains discovered subagent ancestry for the lifetime of the runtime process. During each `Session.run()`, `RunResult.notifications` and `on_notification` receive the root session and all known descendant notifications in wire order, including nested subagent lifecycle and session events. `RunResult.events` contains root-session events only, so descendant messages cannot replace the root response. The low-level `session_prompt()` returns the queued `MessageId` immediately; callers that bypass `Session.run()` own any later activity boundary themselves.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
通过 JSON-RPC stdio 驱动 DeepSeek Harness 的 Python 子进程 SDK。运行时继承常规的 DeepSeek Harness 环境变量(如 `DEEPSEEK_BASE_URL` 与 `DEEPSEEK_API_KEY`),调用方可以直接用真实模型端点,也可以在跑基准测试时把它们指向本地代理。
|
||||
通过 JSON-RPC stdio 驱动 DeepSeek Harness 的 Python 子进程 SDK。运行时继承常规的 DeepSeek Harness 环境变量(如 `DEEPSEEK_BASE_URL` 与 `DEEPSEEK_API_KEY`),调用方可以直接使用真实模型端点,也可以把这些变量指向本地代理。
|
||||
|
||||
安装 `deepseek-harness` 会同时安装版本完全相同的 `deepseek-harness-runtime-bin` 平台 wheel 包。因此常规入口不需要传可执行文件参数:
|
||||
|
||||
@@ -31,6 +31,8 @@ with DeepSeekHarness(
|
||||
|
||||
`provider` 用于选择当前 Cordis 组合已注册的提供方路由;`model` 是该适配器解析的模型 ID。`max_tokens` 是可选的正整数,用于限制根 agent(智能体)及其进程内后代每次请求的输出 token;省略时由提供方默认值控制。压缩摘要继续使用压缩插件单独配置的上限。内置默认组合注册 `deepseek-official`。自定义组合可以挂载 `llm-pi-ai`,在其中配置各提供方的凭据与端点,再选择 pi-ai 已安装目录中的任意提供方/模型组合。
|
||||
|
||||
[极简 agent 教程](../../docs/user/guide/python-sdk-minimal.md)提供完整的独立 Cordis 文件与可运行的 SDK 示例,用于在不使用 Web UI 的情况下使用双工具极简模式。
|
||||
|
||||
`Session.run()` 拥有一个从提示词进入持久 inbox 时开始、到整个 agent 下一次进入空闲状态为止的活动区间,并返回 `RunResult(session_id, final_response, events, notifications, session_root)`。结果不携带提示词级状态或轮次原因:`final_response` 是该区间内根会话最后提交的助手文本,并非因果上归属于该提示词的输出。steering(中途引导)、注入的上下文和其他排队工作都可能在进入空闲状态前参与其中。
|
||||
|
||||
`HarnessClient` 会在运行时进程的生命周期内保留已发现的 subagent(子 agent)祖先关系。每次执行 `Session.run()` 时,`RunResult.notifications` 与 `on_notification` 会按协议传输顺序收到根会话及所有已知后代的通知,其中包括嵌套 subagent 的生命周期事件与会话事件。`RunResult.events` 只包含根会话事件,因此后代消息不会覆盖根会话回复。底层 `session_prompt()` 会立即返回已排队消息的 `MessageId`;绕过 `Session.run()` 的调用方必须自行负责后续的活动边界。
|
||||
|
||||
@@ -138,13 +138,21 @@ const homeAndGuide = pairedPages([
|
||||
section: { root: '入门', en: 'Guide' },
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/guide/python-sdk-minimal.md',
|
||||
route: 'guide/python-sdk-minimal.md',
|
||||
label: { root: 'Python SDK 极简模式', en: 'Minimal mode with Python' },
|
||||
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
||||
section: { root: '入门', en: 'Guide' },
|
||||
order: 4,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/guide/config.md',
|
||||
route: 'guide/config.md',
|
||||
label: { root: '配置文件', en: 'Configuration' },
|
||||
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
||||
section: { root: '入门', en: 'Guide' },
|
||||
order: 4,
|
||||
order: 5,
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user