deep-agent-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deep_agent_cli-0.1.0/.gitignore +15 -0
- deep_agent_cli-0.1.0/LICENSE +21 -0
- deep_agent_cli-0.1.0/PKG-INFO +408 -0
- deep_agent_cli-0.1.0/README.md +381 -0
- deep_agent_cli-0.1.0/agent/__init__.py +42 -0
- deep_agent_cli-0.1.0/agent/attachments.py +303 -0
- deep_agent_cli-0.1.0/agent/bootstrap.py +44 -0
- deep_agent_cli-0.1.0/agent/cancel.py +107 -0
- deep_agent_cli-0.1.0/agent/cli/__init__.py +5 -0
- deep_agent_cli-0.1.0/agent/cli/app.py +1768 -0
- deep_agent_cli-0.1.0/agent/cli/clipboard.py +224 -0
- deep_agent_cli-0.1.0/agent/cli/commands.py +94 -0
- deep_agent_cli-0.1.0/agent/cli/gitinfo.py +84 -0
- deep_agent_cli-0.1.0/agent/cli/input.py +65 -0
- deep_agent_cli-0.1.0/agent/cli/interactions.py +187 -0
- deep_agent_cli-0.1.0/agent/cli/main.py +124 -0
- deep_agent_cli-0.1.0/agent/cli/previews.py +710 -0
- deep_agent_cli-0.1.0/agent/cli/rendering.py +770 -0
- deep_agent_cli-0.1.0/agent/cli/session_controller.py +221 -0
- deep_agent_cli-0.1.0/agent/cli/state.py +326 -0
- deep_agent_cli-0.1.0/agent/config.example.yaml +76 -0
- deep_agent_cli-0.1.0/agent/config.py +528 -0
- deep_agent_cli-0.1.0/agent/control.py +171 -0
- deep_agent_cli-0.1.0/agent/factory.py +232 -0
- deep_agent_cli-0.1.0/agent/file_mutation.py +5 -0
- deep_agent_cli-0.1.0/agent/llm.py +339 -0
- deep_agent_cli-0.1.0/agent/middleware/__init__.py +9 -0
- deep_agent_cli-0.1.0/agent/middleware/attachments.py +31 -0
- deep_agent_cli-0.1.0/agent/middleware/cancel_tools.py +39 -0
- deep_agent_cli-0.1.0/agent/middleware/pause.py +18 -0
- deep_agent_cli-0.1.0/agent/middleware/recovery.py +65 -0
- deep_agent_cli-0.1.0/agent/middleware/steering.py +35 -0
- deep_agent_cli-0.1.0/agent/middleware/tool_arg_hints.py +128 -0
- deep_agent_cli-0.1.0/agent/middleware/workspace_filesystem.py +38 -0
- deep_agent_cli-0.1.0/agent/middleware/write_operation.py +60 -0
- deep_agent_cli-0.1.0/agent/network.py +30 -0
- deep_agent_cli-0.1.0/agent/permission.py +80 -0
- deep_agent_cli-0.1.0/agent/runner.py +1393 -0
- deep_agent_cli-0.1.0/agent/sandbox.py +699 -0
- deep_agent_cli-0.1.0/agent/session.py +431 -0
- deep_agent_cli-0.1.0/agent/session_lock.py +223 -0
- deep_agent_cli-0.1.0/agent/session_runtime.py +209 -0
- deep_agent_cli-0.1.0/agent/stream.py +168 -0
- deep_agent_cli-0.1.0/agent/tools/__init__.py +9 -0
- deep_agent_cli-0.1.0/agent/tools/examples.py +30 -0
- deep_agent_cli-0.1.0/agent/tools/execute.py +73 -0
- deep_agent_cli-0.1.0/agent/tools/human_input.py +170 -0
- deep_agent_cli-0.1.0/agent/tools/human_interaction.py +101 -0
- deep_agent_cli-0.1.0/agent/tools/web_search.py +131 -0
- deep_agent_cli-0.1.0/pyproject.toml +44 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 deep-agent-cli contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deep-agent-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A sandboxed coding agent CLI for Linux and WSL2
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Environment :: Console
|
|
8
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Topic :: Software Development
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Requires-Dist: deepagents<0.8.0,>=0.7.17
|
|
13
|
+
Requires-Dist: httpx<1,>=0.27
|
|
14
|
+
Requires-Dist: langchain-core<2.0,>=1.0
|
|
15
|
+
Requires-Dist: langchain-openai<1.7.0,>=1.6.2
|
|
16
|
+
Requires-Dist: langchain<2.0,>=1.0
|
|
17
|
+
Requires-Dist: langgraph-checkpoint-sqlite<4,>=3.1.1
|
|
18
|
+
Requires-Dist: langgraph<2.0,>=1.0
|
|
19
|
+
Requires-Dist: prompt-toolkit<4.0,>=3.0.48
|
|
20
|
+
Requires-Dist: pyyaml<7.0,>=6.0
|
|
21
|
+
Requires-Dist: rich<15.0,>=13.9
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: build<2,>=1.2; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest<9.0,>=8.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: twine<7,>=6; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# deep-agent-cli
|
|
29
|
+
|
|
30
|
+
面向 Linux / WSL2 的本地沙箱 Coding Agent,安装后运行 `deep-agent` 即可使用。
|
|
31
|
+
|
|
32
|
+
在终端里打开任意项目,就能读改工作区文件、在沙箱里执行命令、按需搜索公开 Web、流式输出思考与回答,并按工作区记住会话。命令默认无网;在默认的 ask 模式下,写文件、删文件和运行命令前会请求审批;allow 模式自动放行全部工具并默认开放沙箱网络。自定义副作用工具需要显式配置审批规则。
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
它要解决的问题很具体:多数 Coding Agent 要么绑云端 IDE,要么绑一整套控制面。Deep Agent 把 Agent 放进当前目录,用本地 OpenAI 兼容模型跑起来,权限和会话都在你机器上。
|
|
39
|
+
|
|
40
|
+
和 Claude Code / Codex / 平台托管 Runtime 的差别:
|
|
41
|
+
|
|
42
|
+
- **本地优先** — 不依赖 Control Plane、Redis、Docker、ToolGateway
|
|
43
|
+
- **物理隔离** — Linux Bubblewrap,默认 `--unshare-net`,不是只靠模型“答应不乱来”
|
|
44
|
+
- **Runtime 与 TUI 分离** — 终端只是一层皮,同一套 `AgentRunner` 可以接到 SSE 或自己的 UI
|
|
45
|
+
- **不 Fork Deep Agents** — 装配入口只有 `create_deep_agent()`
|
|
46
|
+
|
|
47
|
+
## 核心理念
|
|
48
|
+
|
|
49
|
+
`deep_agent_cli` 的目标是做一个**简单、可控、可恢复的本地 Coding Agent**,而不是一个构建 Agent 的框架。
|
|
50
|
+
|
|
51
|
+
- **少造抽象**:优先复用 Deep Agents / LangGraph 已有能力。
|
|
52
|
+
- **单一状态源**:项目文件归文件系统,会话上下文与计划归 checkpoint,避免重复状态。
|
|
53
|
+
- **安全边界明确**:Bubblewrap 限制能力范围,Permission 控制用户授权,两者互不混淆。
|
|
54
|
+
- **恢复不等于继续**:`/resume` 只恢复会话状态,不自动执行未完成任务。F2 只重开 pending overlay。
|
|
55
|
+
- **默认安全,显式扩权**:workspace 外资源、网络及高风险操作必须由用户明确开放。
|
|
56
|
+
- **按真实需求演进**:没有实际问题,就不提前构建复杂机制。
|
|
57
|
+
|
|
58
|
+
> 保持它是一个 Agent,而不是一个构建 Agent 的框架。
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- **工作区工具** — `ls` / `read` / `write` / `edit` / `glob` / `grep` / `delete` 访问 `/workspace`;用户 Skills 可在只读 `/skills` 下读取
|
|
63
|
+
- **公开 Web 搜索** — 配置 Tavily 密钥后提供 `web_search`;只返回带 URL 的相关摘要,不授予沙箱命令联网权限
|
|
64
|
+
- **沙箱执行** — 默认隔离网络;`execute(network=true)` 使用宿主网络,可访问互联网、localhost 和局域网;allow 模式下所有 `execute` 默认使用宿主网络
|
|
65
|
+
- **权限模式** — `ask` 审批 `execute` 和内置文件写入、删除工具;`allow` 仅 SANDBOXED 可启用(需输入 `ALLOW`),开启后 `execute` 默认开放宿主网络
|
|
66
|
+
- **持久会话** — 每个工作区一份 SQLite,`/resume` 恢复最近线程,checkpoint 是恢复依据
|
|
67
|
+
- **流式输出** — 思考和回答按增量刷新;`execute` 的 stdout/stderr 原地更新同一个 Tool 块
|
|
68
|
+
- **转录区跟随** — 停留在底部时持续显示新输出;上滚后保留阅读位置,并提供可点击的回到底部提示
|
|
69
|
+
- **运行中转向** — `Enter` 注入下一条指令,`Esc` 取消并把未发送的内容还原到输入框
|
|
70
|
+
- **后续任务** — `Alt+Enter` 排入 Runtime 队列,当前任务完成后由 `AgentRunner` 接续执行
|
|
71
|
+
- **多模型** — YAML 按来源分组;`/model` 先选来源再选模型,`Ctrl+P` 循环切换。切换会重建 graph,会话保留
|
|
72
|
+
- **图片附件** — Ctrl+V / 路径 / `/image`;checkpoint 只存引用,请求模型时才编码
|
|
73
|
+
- **自动上下文压缩** — `create_deep_agent()` 默认带 `SummarizationMiddleware`,上下文接近上限时自动摘要;被挤掉的历史落到工作区,需要时还能再读
|
|
74
|
+
- **人工交互** — Agent 缺判断时弹出单选、多选、布尔、单行、多行,不绑特定 UI
|
|
75
|
+
- **任务规划** — `write_todos` 使用上游 TodoListMiddleware 更新 LangGraph 的 `todos` state;TUI 从 `values` 状态流替换当前计划,从 checkpoint 恢复计划,不保留历史版本的工具块
|
|
76
|
+
- **Skills** — 只读加载 `~/.deep-agent/skills/*/SKILL.md`
|
|
77
|
+
|
|
78
|
+
自动压缩已由 Deep Agents 的 `SummarizationMiddleware` 处理,不需要额外叠加一层。
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
### Requirements
|
|
83
|
+
|
|
84
|
+
- Python 3.12+
|
|
85
|
+
- Linux 或 WSL2
|
|
86
|
+
- Bubblewrap(Debian / Ubuntu:`sudo apt install bubblewrap`)
|
|
87
|
+
- OpenAI 兼容推理服务(默认 `http://localhost:8000/v1`)
|
|
88
|
+
|
|
89
|
+
### Install
|
|
90
|
+
|
|
91
|
+
推荐使用独立环境安装:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pipx install deep-agent-cli
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
也可以:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install deep-agent-cli
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Start
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
cd ~/projects/my-app
|
|
107
|
+
deep-agent
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
首次运行会生成配置模板并退出。编辑 `~/.deep-agent/config.yaml` 的模型端点和 API key 后,再运行 `deep-agent`。当前不支持原生 Windows 或 macOS 沙箱。
|
|
111
|
+
|
|
112
|
+
### Configure
|
|
113
|
+
|
|
114
|
+
首次运行 `deep-agent` 会生成 `~/.deep-agent/config.yaml` 和空的 `~/.deep-agent/skills/`,提示编辑配置后退出。修改 API Key 和端点后,再次运行。配置示例:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
ui:
|
|
118
|
+
timezone: Asia/Shanghai
|
|
119
|
+
llm:
|
|
120
|
+
default: local/qwen-plus
|
|
121
|
+
models:
|
|
122
|
+
local:
|
|
123
|
+
api_key: sk-your-key
|
|
124
|
+
base_url: http://localhost:8000/v1
|
|
125
|
+
provider: qwen-responses
|
|
126
|
+
models:
|
|
127
|
+
qwen-plus:
|
|
128
|
+
model: qwen3.5-plus
|
|
129
|
+
input: [text, image]
|
|
130
|
+
context_window: 1m
|
|
131
|
+
qwen3.6-flash:
|
|
132
|
+
input: [text, image]
|
|
133
|
+
context_window: 1m
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
每轮回答结束后,转录区显示耗时与结束时间,例如 `Worked for 7m 56s · 10:24`。时间默认按北京时间显示;`ui.timezone` 接受 IANA 时区名称。恢复会话时不会重建此前回合的耗时行。
|
|
137
|
+
|
|
138
|
+
也可以不改文件,启动时指定:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
export DEEP_AGENT_CONFIG=/path/outside/workspace/config.yaml
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
接阿里云百炼 **Qwen Token Plan** 时用 `compatible-mode` 端点加 `provider: openai-compatible`,并打开 `stream_usage`,否则流式响应不回报 usage,底栏的上下文占用百分比会一直显示未知。下例可单独使用;与本地网关并用时,把 `token-plan` 组并入已有的 `llm.models`,再按需要修改 `llm.default`。来源下的模型共用端点和密钥,模型级字段可以覆盖来源级字段:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
llm:
|
|
148
|
+
default: token-plan/auto
|
|
149
|
+
models:
|
|
150
|
+
token-plan:
|
|
151
|
+
api_key: sk-your-token-plan-key
|
|
152
|
+
base_url: https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
|
|
153
|
+
provider: openai-compatible
|
|
154
|
+
stream_usage: true
|
|
155
|
+
context_window: 1m
|
|
156
|
+
models:
|
|
157
|
+
auto: # 由 Token Plan 侧自动路由
|
|
158
|
+
input: [text]
|
|
159
|
+
qwen3.8-max:
|
|
160
|
+
input: [text, image]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`llm.default` 必须明确指定已配置的 `来源/模型`,例如 `token-plan/auto`。`local`、`token-plan` 和模型键都是自定义名称;模型键默认也是发给 API 的模型名,需要别名时在模型项内写 `model:`。配置只接受 `llm.models.<来源>.models.<模型>` 结构;旧版 `llm.model`、扁平 `llm.models.<模型>` 以及放在 `llm` 顶层的端点或密钥字段都会报错。缺少默认模型、字段无效或 YAML 语法错误时,启动会指出配置文件及错误位置,不会默默切换模型。
|
|
164
|
+
|
|
165
|
+
`/model` 先选来源,再选来源下的模型;在模型列表按 `Esc` 返回来源列表。`/model token-plan` 可直接打开该来源,`/model token-plan/auto` 可直接切换。`context_window` 决定自动压缩阈值和占用百分比分母。区域按自己的开通情况替换 `cn-beijing`。密钥只写在 workspace 外的配置里,不要提交进仓库。
|
|
166
|
+
|
|
167
|
+
### Resume
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
deep-agent resume # 选择已有会话
|
|
171
|
+
deep-agent resume 01a08aae-... # 按 id 恢复
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
源码仓库中也可运行 `python -m agent.cli.main`。没有 `bwrap` 时默认拒绝启动;交互式终端只有输入 `UNSANDBOXED` 才会降级到宿主执行。
|
|
175
|
+
|
|
176
|
+
## Usage
|
|
177
|
+
|
|
178
|
+
进入交互会话后,普通文本就是任务。以 `/` 开头是命令。
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
/help 命令与快捷键
|
|
182
|
+
/status 工作区、沙箱、权限、session
|
|
183
|
+
/session 当前线程、创建/更新时间与 SQLite 路径
|
|
184
|
+
/new 开一条新线程
|
|
185
|
+
/resume 列出有内容的会话并选择恢复
|
|
186
|
+
/resume a1b2 按 id 前缀切换 session
|
|
187
|
+
/model 先选来源,再选模型
|
|
188
|
+
/model token-plan 打开 Token Plan 模型列表
|
|
189
|
+
/model token-plan/auto 直接切换模型
|
|
190
|
+
/compact 达到手动压缩门槛后摘要旧对话;未达到时显示当前占用百分比
|
|
191
|
+
/permission ask|allow # allow 仅 SANDBOXED 且开放沙箱网络;UNSANDBOXED / CUSTOM 只有 ask
|
|
192
|
+
/image clipboard | <path> | clear
|
|
193
|
+
/pause 在下一个模型安全点暂停
|
|
194
|
+
/quit
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
常用按键:
|
|
198
|
+
|
|
199
|
+
| 操作 | 按键 |
|
|
200
|
+
|------|------|
|
|
201
|
+
| 提交;运行中注入 steering | `Enter` |
|
|
202
|
+
| 命令候选 | 输入 `/` 后用方向键选择,`Tab` / `Enter` 填入,再按 `Enter` 执行 |
|
|
203
|
+
| 换行 | `Ctrl+J` |
|
|
204
|
+
| 排到本轮结束后再问 | `Alt+Enter` |
|
|
205
|
+
| 上滚时回到底部;再次按下执行当前界面的取消操作 | `Esc`;也可点击 `↓ Back to bottom · esc` |
|
|
206
|
+
| 清空输入;0.5 秒内再次按下退出 | `Ctrl+C` |
|
|
207
|
+
| 切换模型 | `Ctrl+P` / `Alt+P` |
|
|
208
|
+
| 粘贴图片 | `Ctrl+V` / `Alt+V` |
|
|
209
|
+
| 展开或收起工具详情 / Thinking | `Ctrl+O` / `Ctrl+T` |
|
|
210
|
+
| 查看 edit/write/delete 的完整改动 | `Ctrl+R` |
|
|
211
|
+
| 重开 pending 交互 | `F2` |
|
|
212
|
+
| 取回未应用的 steering | `Alt+Up` |
|
|
213
|
+
| 回看历史输出 | 滚轮 / `PgUp` / `PgDn`,`Ctrl+Home` 到顶,`Ctrl+End` 回到底部跟随 |
|
|
214
|
+
|
|
215
|
+
输入 `/` 时显示命令候选,按 `Esc` 可直接清空尚未执行的命令。执行 `/model` 等交互式命令时,蓝色分隔线将选择界面与对话区隔开;如果对话已上滚,居中的 `↓ Back to bottom · esc` 提示显示在分隔线正上方。转录区隐藏右侧滚动条,可用滚轮、翻页键或 `Ctrl+Home` / `Ctrl+End` 滚动。
|
|
216
|
+
|
|
217
|
+
探索类工具调用合并显示为 `Explored N items`,只预览最后五项;省略项数量显示在预览上方,底部灰色的 `Ctrl+O to expand` 提示可展开完整工具详情。再次按 `Ctrl+O` 可收起。
|
|
218
|
+
|
|
219
|
+
`web_search` 单独显示查询和结果数,按 `Ctrl+O` 展开结果摘要。`write_file` 根据执行前的文件状态显示 `Create /path` 或 `Wrote /path`;连续创建多个文件合并为 `Create N files`,按 `Ctrl+O` 展开完整文件列表。覆盖写入预览显示写入行数和前六行内容,省略的行数在下方提示;按 `Ctrl+R` 查看完整内容,不在写入预览或审阅中显示 `/dev/null`、`+++`、`@@` 等 diff 头。恢复已中断会话时,没有保存工具结果的历史调用显示灰色 `interrupted (completion unconfirmed)`,不会继续转圈;仍待审批的调用显示等待状态。
|
|
220
|
+
|
|
221
|
+
工具失败时显示灰色圆点、`Failed (exit N)`、具体命令或工具目标,以及简短错误;`Ctrl+O` 展开完整输出。探索组在标题中统计失败数,并额外列出最近的失败项。`execute` 显示命令的真实退出码;没有进程退出码的工具错误以约定的 `exit 1` 显示。搜索超时但返回部分结果时只提示结果不完整,不计为失败。
|
|
222
|
+
|
|
223
|
+
底栏固定两行:首行左侧按 `工作区路径 · 当前模型 · resume id` 排列,分别使用终端标准绿、黄、青色,右侧显示 Git 分支与改动文件数(非 Git 目录显示 `⎇ no git`);第二行左侧显示运行状态、执行模式、权限,右侧显示上下文占用。Git 状态每 5 秒后台刷新一次。窄终端优先保留右侧 Git 与上下文信息,左侧路径与模型先被截断。占用百分比以 `context_window` 为分母,使用模型最近一次返回的 token usage;没有用量报告时只显示窗口大小。
|
|
224
|
+
|
|
225
|
+
作为库使用:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from agent import AgentRunner, create_agent
|
|
229
|
+
|
|
230
|
+
prepared = create_agent()
|
|
231
|
+
runner = AgentRunner(prepared=prepared, on_delta=print, on_event=print)
|
|
232
|
+
result = runner.invoke("列出 /workspace 下的文件")
|
|
233
|
+
# result.status: completed | waiting_confirmation | waiting_human | paused | failed
|
|
234
|
+
# runner.continue_run() / approve_tool(id) / reject_tool(id) / submit_human_input(values)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`on_event` 不含 ANSI 和终端宽度,HTTP / SSE 可以复用同一条 Runtime。
|
|
238
|
+
`execute` 的 `tool_completed` 事件在 `result` 中携带 `exit_code`、`truncated`、`host_log_path`、`agent_log_path` 和 `termination_reason`;`content` 仍是发给模型的可读结果。
|
|
239
|
+
|
|
240
|
+
默认工具包括文件、沙箱命令、人工输入和 `write_todos`;配置 Tavily 密钥后另行注册 `web_search`。`agent.tools.examples` 中的文档查询示例需要作为 `extra_tools` 显式加入;自定义副作用工具需要通过 `interrupt_on` 显式追加审批规则,内置审批规则不能被覆盖。工具异常不会自动重试,以免超时后重复执行副作用;模型传输错误由 OpenAI SDK 最多重试两次。
|
|
241
|
+
人工输入只使用 `request_human_input`:可传 `fields` 描述文本、布尔或选择题;恢复时用 `runner.submit_human_input({"text": "..."})`。审批用 `approve_tool` / `reject_tool`,暂停用 `continue_run`。旧工具 `handoff_to_human` 已移除,停在该工具调用上的旧会话恢复时会给出迁移错误。
|
|
242
|
+
|
|
243
|
+
传入 `create_deep_agent()` 的 `middleware=[...]` 是附加到默认栈,不会整表替换。Deep Agents 0.7.17 会自动加入 `create_summarization_middleware(model, backend)`。本项目只禁用了默认 general-purpose subagent,没有 `excluded_middleware`,因此自动压缩是开着的。
|
|
244
|
+
|
|
245
|
+
配置正数 `context_window` 时会将其作为模型的 `max_input_tokens` 传给 Deep Agents:约 85% 触发压缩,保留最近 10% 的上下文。未配置且模型自身也没有 `max_input_tokens` 时使用保守默认:约 170,000 tokens 触发、保留最近 6 条消息、旧工具参数在约 20 条消息时预裁剪。被挤掉的对话会写到 backend 上的会话历史文件,而不是直接丢掉。
|
|
246
|
+
|
|
247
|
+
`/compact` 调用 Deep Agents 的 `compact_conversation` 工具手动压缩,要求最近一次模型 usage 达到自动阈值的一半;配置了上下文窗口时约为 42.5%。未达到门槛时,CLI 显示最近一次模型报告的占用百分比;模型未报告 usage 时显示未知。
|
|
248
|
+
|
|
249
|
+
## Architecture
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
你的终端
|
|
253
|
+
│
|
|
254
|
+
▼
|
|
255
|
+
cli/ 输入、渲染、按键、人工确认
|
|
256
|
+
│ RunEvent
|
|
257
|
+
▼
|
|
258
|
+
AgentRunner 流式、中断、恢复、附件
|
|
259
|
+
│ SessionRuntime thread 独占租约与切换
|
|
260
|
+
▼
|
|
261
|
+
create_deep_agent() LangGraph 图(不 Fork 上游)
|
|
262
|
+
│
|
|
263
|
+
├── Model OpenAI 兼容 / Qwen Responses
|
|
264
|
+
├── Tools 文件、execute、人工输入、write_todos
|
|
265
|
+
├── Middleware 暂停、转向、取消
|
|
266
|
+
│ + Deep Agents 默认栈(含自动摘要)
|
|
267
|
+
└── Storage SQLite checkpoint + session catalog
|
|
268
|
+
│
|
|
269
|
+
▼
|
|
270
|
+
Bubblewrap /workspace
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
四层各做一件事:
|
|
274
|
+
|
|
275
|
+
| 层 | 职责 |
|
|
276
|
+
|----|------|
|
|
277
|
+
| `agent/cli/` | 终端交互。换 UI 不改 Runtime |
|
|
278
|
+
| `agent/runner.py` | 一次 run 的生命周期:invoke / continue_run / approve_tool / reject_tool / submit_human_input / steer / cancel |
|
|
279
|
+
| `agent/factory.py` | `AgentSpec` 静态装配和 `create_deep_agent()` 构图 |
|
|
280
|
+
| `agent/sandbox.py` | Bubblewrap 策略与降级 |
|
|
281
|
+
|
|
282
|
+
更细的中间件顺序、挂载策略和 Qwen 事件适配在源码注释里,不在 README 展开。
|
|
283
|
+
|
|
284
|
+
### Project Structure
|
|
285
|
+
|
|
286
|
+
```text
|
|
287
|
+
agent/
|
|
288
|
+
├── cli/ # TUI(含 session_controller 会话工作流)
|
|
289
|
+
├── tools/ # 文件、execute、人工输入
|
|
290
|
+
├── middleware/ # 暂停、转向、取消、联网
|
|
291
|
+
├── factory.py # 装配入口
|
|
292
|
+
├── runner.py # 运行时
|
|
293
|
+
├── sandbox.py # Bubblewrap
|
|
294
|
+
├── session.py # SQLite session
|
|
295
|
+
├── session_lock.py # thread 独占锁
|
|
296
|
+
├── session_runtime.py # 租约与会话生命周期
|
|
297
|
+
└── llm.py # 模型适配
|
|
298
|
+
agent/cli/main.py # pip 安装后的 deep-agent 入口
|
|
299
|
+
examples/ # TUI 与流式冒烟
|
|
300
|
+
skills/ # SKILL.md 示例
|
|
301
|
+
tests/
|
|
302
|
+
agent/config.example.yaml # 首次启动时使用的配置模板
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Configuration
|
|
306
|
+
|
|
307
|
+
CLI 使用 `DEEP_AGENT_CONFIG` 指定的现有配置,否则读取 `~/.deep-agent/config.yaml`;默认配置缺失时会生成模板并退出。项目内的配置文件不会自动加载。配置文件必须包含分组模型及有效的 `llm.default`;解析失败时 CLI 输出 `Configuration error` 并以状态码 2 退出。作为库使用时可显式传入 `Settings`。
|
|
308
|
+
|
|
309
|
+
主配置和按键配置必须位于 workspace 外;如果将 home 目录作为 workspace,启动时会拒绝位于 `~/.deep-agent` 的配置。已有项目内的 `config.yaml` 可一次性迁到 `~/.deep-agent/config.yaml`,确认内容后删除旧文件。配置中的相对路径(除 `sandbox.workspace: .`)仍相对于配置文件所在目录解析,迁移时需检查这些路径。
|
|
310
|
+
|
|
311
|
+
旧配置中的 `sandbox.protected_workspace_paths` 已移除,启动时会提示删除。旧版扁平模型配置需要按上面的 YAML 示例手动改成来源分组;程序不会自动迁移。将原工作区 `skills/` 中需要保留的目录手动复制到 `~/.deep-agent/skills/`;项目内的 `skills/` 不再自动加载。Agent 通过只读 `/skills` 路径读取用户 Skills;其他外部只读资源可使用 `extra_read_only_mounts` 显式开放。
|
|
312
|
+
|
|
313
|
+
| 配置项 | 默认 | 说明 |
|
|
314
|
+
|--------|------|------|
|
|
315
|
+
| `agent.instructions` | `null` | 追加到默认身份之后的长期说明;支持 YAML 多行文本 |
|
|
316
|
+
| `ui.timezone` | `Asia/Shanghai` | 回合结束时间使用的 IANA 时区;无效名称会报配置错误 |
|
|
317
|
+
| `llm.default` | 必填 | 启动模型,格式为 `来源/模型` |
|
|
318
|
+
| `llm.models.<source>.models` | 必填 | 来源下的模型列表;来源名和模型名由用户自定义 |
|
|
319
|
+
| `llm.models.<source>.models.<name>.model` | `<name>` | 实际 API 模型名;仅在模型键是别名时需要填写 |
|
|
320
|
+
| `llm.models.<source>.api_key` | `sk-local` | 来源共用密钥;模型级可以覆盖 |
|
|
321
|
+
| `web_search.tavily_api_key` | `null` | 私有配置中的 Tavily 密钥;`TAVILY_API_KEY` 环境变量优先 |
|
|
322
|
+
| `llm.models.<source>.provider` | `qwen-responses` | 来源共用 `qwen-responses` 或 `openai-compatible`;模型级可以覆盖 |
|
|
323
|
+
| `llm.models.<source>.stream_usage` | `false` | 流式 Chat Completions 请求附带 `stream_options.include_usage`;模型级可以覆盖 |
|
|
324
|
+
| `llm.models.<source>.base_url` | `http://localhost:8000/v1` | 来源共用推理端点;模型级可以覆盖 |
|
|
325
|
+
| `llm.models.<source>.models.<name>.input` | `[text]` | 图片模型写成 `[text, image]` |
|
|
326
|
+
| `llm.models.<source>.context_window` | `0` | 模型输入窗口,可在模型级覆盖;可写 `1000000`、`128k` 或 `1m`;`0` 隐藏占用百分比 |
|
|
327
|
+
| `sandbox.workspace` | `.` | 映射到 `/workspace`;`.` = 启动时的 cwd |
|
|
328
|
+
| `sandbox.allow_unsandboxed` | `false` | 无 bwrap 时是否允许宿主机执行 |
|
|
329
|
+
| `sandbox.timeout_seconds` | `null`(无限制) | 可选的单次命令超时上限,单位秒;工具可请求更短时间 |
|
|
330
|
+
| `sandbox.max_output_bytes` | `100000` | TUI 实时显示开头、最终结果保留尾部的字节上限;超出时保存完整日志 |
|
|
331
|
+
| `paths.state_path` | `null` | Session DB;默认 `~/.deep-agent/sessions/<hash>.sqlite3` |
|
|
332
|
+
| `paths.config_dir` | `null` | 按键配置;默认 `~/.deep-agent` |
|
|
333
|
+
|
|
334
|
+
按键覆盖:`~/.deep-agent/keybindings.json`。
|
|
335
|
+
|
|
336
|
+
模型来源或模型级的 `api_key` 以及 `web_search.tavily_api_key` 都支持完整的 `${变量名}` 引用。引用的环境变量缺失时启动会报告配置错误;其他配置字段不做环境变量替换。真实密钥应放在本机私有配置或环境变量中,不要提交到仓库。
|
|
337
|
+
|
|
338
|
+
设置 Tavily 密钥后,Agent 会获得 `web_search`,用于查找公开网页上的最新资料、新闻及外部事实;未设置时不会注册该工具。工具通过宿主机向固定的 Tavily Search 接口发起请求,20 秒超时,返回排名摘要和 URL,不抓取完整网页,也不会给 `execute` 开放网络。可在 `~/.deep-agent/config.yaml` 中加入:
|
|
339
|
+
|
|
340
|
+
```yaml
|
|
341
|
+
web_search:
|
|
342
|
+
tavily_api_key: ${TAVILY_API_KEY}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
也可以只设置 `TAVILY_API_KEY`,无需添加 YAML 字段。若两者均有值,直接设置的 `TAVILY_API_KEY` 优先;使用上面的 `${TAVILY_API_KEY}` 写法时必须设置该环境变量。`web_search` 的参数为 `query`、`max_results`(1–20)、`topic`(`general` 或 `news`)、`time_range`(`day`、`week`、`month`、`year`)和 `include_domains`。结果仅是摘要;未搜到不代表资料不存在。
|
|
346
|
+
|
|
347
|
+
`execute` 输出超限时,完整日志保存到当前工作区的 `.deep-agent/logs/exec/`。最终工具结果同时给出宿主机真实路径和 Agent 可用文件工具读取的 `/workspace/.deep-agent/logs/exec/...` 路径。建议在自己的项目 `.gitignore` 中加入 `.deep-agent/`;程序不会修改项目的忽略规则。
|
|
348
|
+
父 shell 退出后,如果后台进程仍占有输出管道,`execute` 会继续收集数据,直到管道关闭或连续 100 毫秒没有新输出;后台进程在此后写出的内容不会进入本次工具结果。
|
|
349
|
+
|
|
350
|
+
`qwen-responses` 使用 `QwenChatOpenAI` 和 Responses API;`openai-compatible` 使用普通 `ChatOpenAI`,固定走 Chat Completions,百炼 Token Plan 的 compatible-mode 端点走这一条。两者都可通过 `AttachmentStore` 引用发送图片,前提是模型 profile 声明 `input: [text, image]` 且端点支持图片。
|
|
351
|
+
|
|
352
|
+
每次构图都会按默认身份、`agent.instructions`、工作区根目录 `AGENTS.md` 的顺序组成 system prompt;`AGENTS.md` 映射到 Agent 内的 `/workspace/AGENTS.md`。切换模型或权限会重新读取它。作为库调用时,`create_agent(instructions="...")` 可覆盖配置中的长期说明。
|
|
353
|
+
|
|
354
|
+
LangGraph checkpoint 保存消息、中断和图状态;同一 SQLite 的 `session_catalog` 另存 thread 的 model、permission 和上一轮运行原因,展示状态按运行原因计算。旧 catalog 在打开时迁移。旧会话若保存了已不存在的扁平模型 ID,恢复时会提示该模型不可用,并改用 YAML 指定的默认模型;旧 ID 不会被当作新模型别名。模型的 `finish_reason` 保留在 checkpoint 消息中。`/resume` 只恢复状态,不调用模型。若上一轮是 `pending` 且已有 checkpoint,或上一轮是 `aborted`、`error`,下一次用户输入保持原文写入 checkpoint,恢复说明仅临时加入首次模型请求。空白新 thread 的 `pending` 不触发恢复说明。明确的审批或暂停中断仍按 checkpoint 恢复,不自动重跑工具。切换或新建会话时,TUI 会把未执行的 steering / follow-up 退回输入框;库调用者需先取回队列,才能切换会话。
|
|
355
|
+
|
|
356
|
+
同一 thread 同时只能被一个进程写入:`SessionStore` 在数据库旁的 `<db>.locks/` 目录用 `flock` 实现 thread 独占,锁文件不删除,进程退出自动释放,Agent 子进程不继承锁描述符。目标会话正被其他窗口持有时,`/resume` 和 `deep-agent resume <id>` 启动恢复都会先释放当前会话并进入等待提示,`Esc` 取消等待回到会话选择器;库调用者同步调用 `switch_session` 则直接抛出 `SessionLockBusyError`。会话切换的提交点在完成全部图与 catalog 读取之后,中途失败会回滚到原会话;`steer` / `follow_up` 在脱离会话或 Runner 关闭后会被拒绝。没有持久化存储的 Runner 只在进程内通过 checkpointer 对象互斥。
|
|
357
|
+
|
|
358
|
+
沙箱默认挂载当前工作区,并将 `~/.deep-agent/skills/` 只读挂载为 `/skills`;宿主家目录的其他内容不可见。`network=true` 取消网络命名空间隔离,可访问宿主网络,包括 localhost、局域网和内网。Bubblewrap 不管 CPU / 内存配额。`UNSANDBOXED` 和显式传入的 `CUSTOM` backend 只有 ask:所有 `execute` 都要审批,不能切到 allow。allow 模式下每个 `execute` 都默认使用宿主网络,单次调用无法关闭。
|
|
359
|
+
|
|
360
|
+
## Development
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
python3.12 -m venv .venv
|
|
364
|
+
source .venv/bin/activate
|
|
365
|
+
pip install -e ".[dev]"
|
|
366
|
+
pytest -q
|
|
367
|
+
python -m build
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
`pyproject.toml` 是依赖声明的唯一维护入口。`requirements.lock` 是目前 Python 3.12 开发环境的冻结快照;升级依赖后在干净环境安装 `.[dev]`,再更新快照并运行测试。
|
|
371
|
+
|
|
372
|
+
使用 pipx 安装过本项目时,修改源码后在项目根目录运行 `pipx install --force .`,再重启 CLI;已有进程不会自动加载新工具。
|
|
373
|
+
|
|
374
|
+
测试不打真模型。对着本地端点做流式冒烟:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
python examples/stream_smoke.py
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Roadmap
|
|
381
|
+
|
|
382
|
+
- [x] 交互式 TUI
|
|
383
|
+
- [x] 按工作区持久化 session
|
|
384
|
+
- [x] 工作区文件工具
|
|
385
|
+
- [x] Bubblewrap 沙箱(默认无网)
|
|
386
|
+
- [x] 权限 ask / 仅沙箱 allow
|
|
387
|
+
- [x] 流式思考与回答
|
|
388
|
+
- [x] 多模型切换
|
|
389
|
+
- [x] 图片附件
|
|
390
|
+
- [x] 运行中 steering / 取消
|
|
391
|
+
- [x] 语义化人工输入
|
|
392
|
+
- [x] 自动上下文压缩(Deep Agents 默认 `SummarizationMiddleware`)
|
|
393
|
+
- [x] 可安装的 Python 包
|
|
394
|
+
- [ ] MCP
|
|
395
|
+
|
|
396
|
+
## Project Status
|
|
397
|
+
|
|
398
|
+
项目在活跃开发中。
|
|
399
|
+
|
|
400
|
+
配置格式、session schema、工具集合和公开 API 在第一个稳定版之前都可能变。请先当本地工具用,不要当生产 SDK 依赖。
|
|
401
|
+
|
|
402
|
+
## Contributing
|
|
403
|
+
|
|
404
|
+
Issue 和 Pull Request 都欢迎。改行为请带测试;不要在 PR 里提交 `config.yaml`、`.venv` 或 session 数据库。
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
MIT,详见 [LICENSE](LICENSE)。
|