qi-coding-agent 0.1.0__py3-none-any.whl
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.
- qi_agent/__init__.py +18 -0
- qi_agent/abort.py +40 -0
- qi_agent/auth.py +216 -0
- qi_agent/cli.py +2178 -0
- qi_agent/compaction.py +475 -0
- qi_agent/config.py +409 -0
- qi_agent/docs/cli.md +188 -0
- qi_agent/docs/compaction.md +148 -0
- qi_agent/docs/configuration.md +89 -0
- qi_agent/docs/containerization.md +80 -0
- qi_agent/docs/docs.json +1 -0
- qi_agent/docs/environment-variables.md +47 -0
- qi_agent/docs/extensions.md +382 -0
- qi_agent/docs/how-qi-works.md +93 -0
- qi_agent/docs/index.md +61 -0
- qi_agent/docs/json.md +112 -0
- qi_agent/docs/keybindings.md +83 -0
- qi_agent/docs/models.md +138 -0
- qi_agent/docs/packages.md +155 -0
- qi_agent/docs/providers.md +288 -0
- qi_agent/docs/quickstart.md +109 -0
- qi_agent/docs/security.md +69 -0
- qi_agent/docs/session-format.md +299 -0
- qi_agent/docs/sessions.md +127 -0
- qi_agent/docs/settings.md +138 -0
- qi_agent/docs/skills.md +124 -0
- qi_agent/docs/slash-commands.md +55 -0
- qi_agent/docs/themes.md +78 -0
- qi_agent/docs/tui.md +109 -0
- qi_agent/docs/usage.md +112 -0
- qi_agent/export_html.py +95 -0
- qi_agent/extensions.py +2267 -0
- qi_agent/llm.py +609 -0
- qi_agent/loader.py +299 -0
- qi_agent/model_catalog.py +108 -0
- qi_agent/models.py +103 -0
- qi_agent/packages.py +430 -0
- qi_agent/paths.py +242 -0
- qi_agent/pep723.py +74 -0
- qi_agent/presets.py +293 -0
- qi_agent/prompt.py +150 -0
- qi_agent/registry.py +485 -0
- qi_agent/runner.py +584 -0
- qi_agent/runtime.py +1940 -0
- qi_agent/session.py +739 -0
- qi_agent/settings.py +501 -0
- qi_agent/share.py +111 -0
- qi_agent/system_prompt.py +278 -0
- qi_agent/theme.py +389 -0
- qi_agent/themes/dark.json +90 -0
- qi_agent/themes/light.json +89 -0
- qi_agent/titling.py +90 -0
- qi_agent/tools/__init__.py +349 -0
- qi_agent/tools/shell.py +276 -0
- qi_agent/trust.py +106 -0
- qi_agent/tui.py +6224 -0
- qi_agent/workspaces.py +116 -0
- qi_coding_agent-0.1.0.dist-info/METADATA +151 -0
- qi_coding_agent-0.1.0.dist-info/RECORD +62 -0
- qi_coding_agent-0.1.0.dist-info/WHEEL +4 -0
- qi_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
- qi_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# 供应商与凭证
|
|
2
|
+
|
|
3
|
+
qi 通过 litellm 调各家模型。**provider 的声明**(baseUrl / api / 模型清单)写在 `models.json`,格式与
|
|
4
|
+
分层见 [models.md](models.md);本文讲**凭证**:从哪来、怎么解析、怎么排查。
|
|
5
|
+
|
|
6
|
+
**事实源:`src/qi_agent/auth.py` + `config.py` + `paths.py`。**
|
|
7
|
+
|
|
8
|
+
## 1. 凭证解析顺序
|
|
9
|
+
|
|
10
|
+
对某个 provider,按这个顺序找密钥(`auth.resolve_key`),**先命中先用**:
|
|
11
|
+
|
|
12
|
+
| 顺序 | 来源 | 说明 |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| 1 | **auth store** `~/.qi/agent/auth.json` | 按 provider 名存;`qi auth login` 写入 |
|
|
15
|
+
| 2 | **约定环境变量** | 见 §2 的表(如 `deepseek` → `DEEPSEEK_API_KEY`) |
|
|
16
|
+
| 3 | **`models.json` 里该 provider 的 `apiKey`** | 支持字面量 / `$ENV` / `!command`,见 §3 |
|
|
17
|
+
| 4 | — | 都没有 → 该 provider 报"缺密钥" |
|
|
18
|
+
|
|
19
|
+
**免密钥的 provider**:`ollama`(本地)—— 它 `ok=True`,显示为"无需密钥(本地 provider)"。
|
|
20
|
+
|
|
21
|
+
排查时会看到的几种状态(`ResolvedKey.describe()`,出现在 `qi --list-models` 与 `qi doctor`):
|
|
22
|
+
|
|
23
|
+
| 显示 | 含义 |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `auth(ab12cd…)` | 命中了 auth store(只显示前 6 位) |
|
|
26
|
+
| `env:DEEPSEEK_API_KEY` | 命中了约定环境变量 |
|
|
27
|
+
| `config:$MY_KEY` | 命中了 `models.json` 的 `apiKey` 引用(原样回显写法) |
|
|
28
|
+
| `缺失(env:OPENAI_API_KEY(未设置))` | 找到了来源但值为空 —— 这一类最容易被忽略 |
|
|
29
|
+
| `缺失(config:…(未解析))` | `apiKey` 写法的语法不成立(命令失败 / 变量缺失 / 引号不配对) |
|
|
30
|
+
| `缺失(无密钥来源)` | 既没有约定环境变量,`models.json` 里也没写 `apiKey` |
|
|
31
|
+
|
|
32
|
+
## 2. 约定环境变量
|
|
33
|
+
|
|
34
|
+
provider 名 → 自动识别的环境变量(`DEFAULT_API_KEY_ENV`):
|
|
35
|
+
|
|
36
|
+
| provider | 环境变量 |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `openai` | `OPENAI_API_KEY` |
|
|
39
|
+
| `anthropic` | `ANTHROPIC_API_KEY` |
|
|
40
|
+
| `deepseek` | `DEEPSEEK_API_KEY` |
|
|
41
|
+
| `google` / `gemini` | `GEMINI_API_KEY` |
|
|
42
|
+
| `openrouter` | `OPENROUTER_API_KEY` |
|
|
43
|
+
| `groq` | `GROQ_API_KEY` |
|
|
44
|
+
| `mistral` | `MISTRAL_API_KEY` |
|
|
45
|
+
| `xai` | `XAI_API_KEY` |
|
|
46
|
+
| `dashscope`(阿里云百炼) | `DASHSCOPE_API_KEY` |
|
|
47
|
+
| `moonshot`(月之暗面 Kimi) | `MOONSHOT_API_KEY` |
|
|
48
|
+
| `zhipu`(智谱 GLM) | `ZHIPUAI_API_KEY` |
|
|
49
|
+
| `minimax` | `MINIMAX_API_KEY` |
|
|
50
|
+
| `siliconflow`(硅基流动) | `SILICONFLOW_API_KEY` |
|
|
51
|
+
| `stepfun`(阶跃星辰) | `STEP_API_KEY` |
|
|
52
|
+
| `hunyuan`(腾讯混元) | `HUNYUAN_API_KEY` |
|
|
53
|
+
| `volcengine`(火山方舟) | `ARK_API_KEY` |
|
|
54
|
+
| `qianfan`(百度千帆) | `QIANFAN_API_KEY` |
|
|
55
|
+
|
|
56
|
+
下面那批是**预置 provider**(见 §2.1);表里的名字与 `presets.py` 一一对应,有测试锁住。
|
|
57
|
+
|
|
58
|
+
**这张表只对"名字对得上"的 provider 生效。** 你自己起的 provider 名(如 `my-proxy`)不在表里,要么在
|
|
59
|
+
`models.json` 里写 `apiKey`,要么用 `qi auth login my-proxy` —— 不会去猜你的变量名。
|
|
60
|
+
|
|
61
|
+
### 2.1 预置 provider(国产为主)
|
|
62
|
+
|
|
63
|
+
qi 自带一份预置表(`src/qi_agent/presets.py`),**两种用法、一套数据**:
|
|
64
|
+
|
|
65
|
+
**① 兜底(默认行为)** —— `models.json` 里**没写**的预置 provider,`load_config` 会自动补上。
|
|
66
|
+
所以零配置也能直接用:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
export DEEPSEEK_API_KEY=sk-… # 或 qi auth login deepseek
|
|
70
|
+
qi config --set defaultProvider=deepseek --set defaultModel=deepseek-v4-pro
|
|
71
|
+
qi # 直接跑
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
TUI 里 `/login` 的 provider 选择器列出**全部**预置(带 `(无凭证 · 预置)` 标记)—— 那是登录入口。
|
|
75
|
+
|
|
76
|
+
**但 `/model`、`ctrl+l/p` 轮换、`/scoped-models`、web 的模型菜单、`qi --list-models` 只列你能用的**:
|
|
77
|
+
一个 provider 要**解析得出凭证**(auth store / 约定环境变量 / `models.json` 里 `apiKey` 的字面值
|
|
78
|
+
或 `$VAR`·`$(命令)` 引用 / 免密钥的 `ollama`)才会进清单 —— 没登录就不该在切换列表里看到它
|
|
79
|
+
(想登录就去 `/login`)。`models.json` 里**显式写过**的 provider **没有豁免**:写了 `apiKey`
|
|
80
|
+
却解析不出来(环境变量没导出、命令跑不通),就是“用不了”—— “我刚把 mimo logout 了,
|
|
81
|
+
它怎么还在 `/scoped-models` 里”就是这个豁免造成的。**清单口径**:没凭证的 provider 一个模型都不在里面。
|
|
82
|
+
|
|
83
|
+
`qi doctor` 与 `qi --list-models` 是**诊断面**,照列全量并标 `缺密钥`(唯一例外:`--list-models`
|
|
84
|
+
仍列**当前默认模型**那个 provider —— 否则你看不出自己缺的是哪把钥匙)。
|
|
85
|
+
|
|
86
|
+
本地**无鉴权**端点(自建代理 / llama.cpp 之类)想进清单,写一个**字面** `apiKey` 就行
|
|
87
|
+
(`"apiKey": "none"`)—— `resolve_key` 认它,请求只是多一个 Authorization 头。
|
|
88
|
+
|
|
89
|
+
**`models.json` 里写了同名 provider 就以你写的为准** —— 预置从不覆盖用户的定义(baseUrl /
|
|
90
|
+
apiKey / 模型清单全是)。
|
|
91
|
+
|
|
92
|
+
**② 物化(想改的时候)** —— 把预置**写进** `models.json`,之后这个 provider 就归你说了算:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
qi init --list-presets # 看有哪些(名字 / baseUrl / 约定环境变量 / 模型 / 注意项)
|
|
96
|
+
qi init --preset deepseek # 写进 models.json,并把它的第一个模型设为默认
|
|
97
|
+
qi init --preset deepseek,moonshot --local # 多个 / 写进项目 .qi/models.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**交互流里也看得到这批**:`qi init` 的 provider 选择器把**没物化的预置**全列出来
|
|
101
|
+
(标 `[预置]`,排在已有 provider 之后、`+ 新建 provider` 之前),选中即走 `apply_presets`
|
|
102
|
+
物化(baseUrl / apiKey 引用 / 模型清单先备好,下面几问回车即保留);没选中的预置不写盘。
|
|
103
|
+
新建时敲个预置名、或 `-y --provider deepseek`,同样先物化 —— 否则写出的 provider 没有 baseUrl。
|
|
104
|
+
|
|
105
|
+
“缺凭证”的告警口径只算**在用**的 provider(`models.json` 里写过的 + 默认/路由模型那个) ——
|
|
106
|
+
预置那十家只是目录,不会让“凭证全部就绪”永远不成立。这条**不管**清单:没凭证的 provider
|
|
107
|
+
既不在 `/model` 里,又可能在 `qi doctor` 里被点名 —— 那正是它该干的事(说清缺什么)。
|
|
108
|
+
|
|
109
|
+
预置了什么:**baseUrl + api + 约定环境变量名 + 模型清单**,每个模型都带两个数 ——
|
|
110
|
+
|
|
111
|
+
- `contextWindow` = 上下文窗口(最大**输入**);
|
|
112
|
+
- `maxTokens` = 该模型的最大**输出** tokens(qi 会把它作为请求的 `max_tokens` 上限发出去)。
|
|
113
|
+
|
|
114
|
+
**两个数都从厂商自己的文档/模型页核过**(表中括号里就是),核不到的模型**干脆不收** ——
|
|
115
|
+
猜一个 `maxTokens` 会让请求要么被拒要么悄悄截断,比没有这一条更糟。想补别的模型:
|
|
116
|
+
`qi init --preset <名>` 先物化,再按厂商文档往 `models.json` 里加一条。
|
|
117
|
+
|
|
118
|
+
`reasoning` **故意不预置**:这些模型多数**默认就带思考**,而 `reasoning_effort` 是否被各家
|
|
119
|
+
OpenAI 兼容端点接受并不一致。默认不带参最不容易 400;想开就给该模型加 `"reasoning": true`
|
|
120
|
+
(provider 拒收时 qi 会自动去掉并提示一次,见 [slash-commands.md](slash-commands.md))。
|
|
121
|
+
|
|
122
|
+
覆盖规矩(**不静默改用户写过的东西**):provider 段缺什么补什么(`api` / `baseUrl` / `apiKey`),
|
|
123
|
+
已有值不动;模型按 id 追加缺的,同 id 已存在则原样保留(你手调过的 `reasoning` / `contextWindow`
|
|
124
|
+
以你为准)。所以再跑一次是幂等的 —— 想拉新模型时更新 qi 后再跑一次即可。
|
|
125
|
+
|
|
126
|
+
| 预置 | baseUrl | 预置模型(id · ctx / max;**第一个 = 物化后的默认**) |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `deepseek` | `https://api.deepseek.com` | **`deepseek-flash`**(= V4.1 Flash)、`deepseek-v4-pro`(均 1M / 384K) |
|
|
129
|
+
| `dashscope` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | **`qwen3.7-plus`**(官方推荐:均衡 + 完整工具调用)、`qwen3.8-max`、`qwen3.8-flash`(均 1M / 128K) |
|
|
130
|
+
| `mimo` | `https://api.xiaomimimo.com/v1` | **`mimo-v2.6-flash`**(官方定位:高智能、低成本,排第一)、`mimo-v2.6-pro`、`mimo-v2.6-pro-ultraspeed`(速度档;均 1M / 128K;旧的 `mimo-v2.5*` **2026-10-21 下线**) |
|
|
131
|
+
| `moonshot` | `https://api.moonshot.cn/v1` | **`kimi-k3`**(1M / 128K)、`kimi-k2.7-code`(256K / 32K) |
|
|
132
|
+
| `zhipu` | `https://open.bigmodel.cn/api/paas/v4` | **`glm-5.2`**(1M / 128K)、`glm-4.7`(200K / 128K) |
|
|
133
|
+
| `minimax` | `https://api.minimax.cn/v1` | **`MiniMax-M3`**(1M / 128K) |
|
|
134
|
+
| `siliconflow` | `https://api.siliconflow.cn/v1` | **`deepseek-ai/DeepSeek-V4-Flash`**(1M / 384K) |
|
|
135
|
+
| `stepfun` | `https://api.stepfun.com/v1` | **`step-3.7-flash`**(256K / 256K) |
|
|
136
|
+
| `hunyuan` | `https://tokenhub.tencentmaas.com/v1` | **`hy4-preview`**(1M / 64K)、`hy3`(256K / 128K) |
|
|
137
|
+
| `volcengine` | `https://ark.cn-beijing.volces.com/api/v3` | **`doubao-seed-evolving`**(统一 id,周更自动迭代)、`doubao-seed-2-1-pro-260628`(均 256K / 128K) |
|
|
138
|
+
| `qianfan` | `https://qianfan.baidubce.com/v2` | **`ernie-5.1`**、`ernie-5.0`(均 128K / 64K) |
|
|
139
|
+
|
|
140
|
+
这张表是 **2026-09 逐个厂商模型页核过的快照**(各条注释里写了出处)。换世代时它会过时 ——
|
|
141
|
+
`qi init --refresh <名>` 就是为此准备的;`tests/test_presets.py` 里有一条快照测试,
|
|
142
|
+
厂商换线时会红,提醒去核对而不是把断言改宽。
|
|
143
|
+
|
|
144
|
+
**③ 刷新(以厂商接口为准)** —— 种子会过时,接口不会:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
qi init --refresh deepseek # GET {baseUrl}/models → 并进 models.json
|
|
148
|
+
qi init --refresh deepseek,moonshot
|
|
149
|
+
qi init --refresh-all # 所有已配置(有凭证)的 provider
|
|
150
|
+
qi init --refresh deepseek --local # 写项目 .qi/models.json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
交互流里有同一条入口:`qi init` → `--- Add Models ---` 菜单选
|
|
154
|
+
**`↻ Refresh model list (GET /models)`**(与 `+ Add a model` 同级,共用
|
|
155
|
+
`_refresh_entry_models`):失败只报错、回菜单不中断 init。
|
|
156
|
+
|
|
157
|
+
> 为什么挂在 `qi init` 下:`qi models` 子命令在本仓是**故意删掉的**(列清单用
|
|
158
|
+
> `--list-models`),所以“刷新模型清单”跟 `--preset` 一样留在 `qi init` 这个“把 provider / 模型
|
|
159
|
+
> 写进 `models.json`”的家族里。`qi doctor` 与 `--list-presets` 的输出里都会提示这条。
|
|
160
|
+
|
|
161
|
+
为什么需要它(实测):DeepSeek 把当前模型名从 `deepseek-v4-flash` 换成了 **`deepseek-flash`**
|
|
162
|
+
(V4.1 Flash,旧名仍存在但请求被路由到新模型),而 `deepseek-v4-pro` 官方计划下线 ——
|
|
163
|
+
这种变更只能靠公告或接口,写死的表迟早落后一拍。
|
|
164
|
+
|
|
165
|
+
刷新规矩:
|
|
166
|
+
|
|
167
|
+
- **只加不删**:接口回来的新 id 追加进去;本地有、接口没返回的**保留并打印出来**
|
|
168
|
+
(厂商的 `/models` 有可能只列有权限的模型、或者列不全,不该替用户删配置);
|
|
169
|
+
- **新加的条目只写 `id`** —— `/models` 不返回 `contextWindow` / `maxTokens`,先用 qi 的默认值,
|
|
170
|
+
要精确就照厂商文档在 `models.json` 里补(种子里的那两个数在刷新时**不会被覆盖**);
|
|
171
|
+
- **写回「定义它的那个文件」**:provider 写在项目 `.qi/models.json` 就刷到那儿、写在用户级就刷到
|
|
172
|
+
用户级;只有“预置兜底来的”(哪个文件都没定义)才物化到用户级 —— 否则会在别处长出一份同名定义,
|
|
173
|
+
而合并取高优先级的那个,于是“刷新了却看不到效果”;
|
|
174
|
+
- 鉴权用**同一个 API key**(auth store / 约定环境变量 / `apiKey` 引用);拿不到 key 就报错、**不写文件**;
|
|
175
|
+
- 只认 `http(s)` 的 `baseUrl`(自建代理照常),网络失败/HTTP 错误都报清楚并且**不落盘**。
|
|
176
|
+
|
|
177
|
+
数过时的注意项(与 `--list-presets` 里打印的一致):
|
|
178
|
+
|
|
179
|
+
- `dashscope`:北京新账号可能要换成工作空间专属域名
|
|
180
|
+
`https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1`;
|
|
181
|
+
- `volcengine`:方舟的模型 id 带日期后缀(会变),自定义部署还得用 Endpoint ID ——
|
|
182
|
+
以控制台“模型列表”里的调用名为准;
|
|
183
|
+
- `hunyuan`:腾讯正把混元迁到 TokenHub(新 id 形如 `hy3` / `hy4-preview`),表里是旧平台的
|
|
184
|
+
兼容端点;
|
|
185
|
+
- `siliconflow` 是聚合平台,模型 id 带组织前缀(`zai-org/GLM-5.2`、`Qwen/…`、`Pro/…`),
|
|
186
|
+
与各家的官方 id 不同;
|
|
187
|
+
- `mimo`:按量付费用 `api.xiaomimimo.com`,订阅套餐(Token Plan)是
|
|
188
|
+
`https://token-plan-cn.xiaomimimo.com/v1` 且 Key 前缀不同 —— 两家 token 不能混用;
|
|
189
|
+
另外 `mimo-v2.5` / `mimo-v2.5-pro` 官方公告 **2026-10-21 10:00 下线**(种子已切到
|
|
190
|
+
`mimo-v2.6-flash` / `mimo-v2.6-pro` / `mimo-v2.6-pro-ultraspeed` —— ultraspeed 是速度档,
|
|
191
|
+
限流为定制服务,调它之前先问平台);
|
|
192
|
+
- `zhipu`:`GLM-5.3` 官方页面写着「API 即将上线」(Bailian 已可调),所以种子里是线上可用的
|
|
193
|
+
`GLM-5.2`;上线后 `qi init --refresh zhipu` 拉一下就有;
|
|
194
|
+
- `hunyuan`:腾讯已把混元迁到 **TokenHub**(`https://tokenhub.tencentmaas.com/v1`,Key 也从
|
|
195
|
+
TokenHub 控制台拿);旧平台 `https://api.hunyuan.cloud.tencent.com/v1` + `hunyuan-turbos-latest`
|
|
196
|
+
仍可用但**Key 不通用**,且腾讯说旧平台不再新增模型;
|
|
197
|
+
- `volcengine`:普通模型 id 带日期后缀(会变),`doubao-seed-evolving` 是**版本无关**的统一 id
|
|
198
|
+
(官方周更自动迭代、不用改配置),所以排第一。
|
|
199
|
+
|
|
200
|
+
**模型 id 会漂** —— 所以还有第三条路:**从接口拉**(见下)。表里的清单是核过厂商文档的快照。
|
|
201
|
+
|
|
202
|
+
## 3. `models.json` 里的 `apiKey` 写法
|
|
203
|
+
|
|
204
|
+
`apiKey` 的值按**前缀**解析:
|
|
205
|
+
|
|
206
|
+
| 写法 | 行为 |
|
|
207
|
+
| --- | --- |
|
|
208
|
+
| `"sk-abc123"` | 字面量 |
|
|
209
|
+
| `"$MY_KEY"` / `"${MY_KEY}"` | 环境变量插值。**变量缺失 → 整个值解析失败**(不返回半截,也不会变成空串去发请求) |
|
|
210
|
+
| `"!command -args"` | 执行命令,取 **stdout**(strip 后);非零退出 / 超时(30s)/ 引号不配对 → 解析失败 |
|
|
211
|
+
| `"$$literal"` / `"$!literal"` | 转义:得到字面 `$` / `!` 开头的字符串 |
|
|
212
|
+
|
|
213
|
+
`!command` 的三种常见写法:
|
|
214
|
+
|
|
215
|
+
```jsonc
|
|
216
|
+
"apiKey": "!security find-generic-password -ws 'anthropic'" // 引号由 shlex 处理
|
|
217
|
+
"apiKey": "!op read 'op://vault/item/credential'"
|
|
218
|
+
"apiKey": "!bash -lc 'cat /tmp/k | tr -d \\n'" // 需要管道时显式起 shell
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**安全边界**:`!command` **不走 shell** —— 它被 `shlex` 拆成参数后以 `shell=False` 执行,所以没有
|
|
222
|
+
shell 注入面。代价是**不能直接写 `!cat a | jq -r .key`**(管道会被当成普通参数);要管道就显式写
|
|
223
|
+
`!bash -lc "…"`,把那层 shell 变成一个看得见的选择。
|
|
224
|
+
|
|
225
|
+
求值时机只有一处:**解析你自己写的 `apiKey` 时**。agent 或模型内容无法触发它(与 `auth.json` 同属
|
|
226
|
+
你的配置信任域)。
|
|
227
|
+
|
|
228
|
+
## 4. auth store:`~/.qi/agent/auth.json`
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"deepseek": { "type": "api_key", "key": "sk-…" },
|
|
233
|
+
"my-proxy": { "type": "api_key", "key": "…" }
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- 写入时 `chmod 0600`(Windows 上无 posix 权限,忽略失败)。
|
|
238
|
+
- **文件损坏或不是对象 → 当作空**(`{}`),不报错、不影响启动。凭证读不出来时表现为"缺密钥",
|
|
239
|
+
而不是把 qi 挡在门外。
|
|
240
|
+
- 只存 `type: api_key` 这一种。**qi 没有 OAuth(订阅登录)** —— 凭证层只有 API key,所以用订阅
|
|
241
|
+
额度(Claude Pro/Max、Copilot、Codex 等)的用户必须换成 API key,否则跑不通。
|
|
242
|
+
|
|
243
|
+
## 5. 命令行
|
|
244
|
+
|
|
245
|
+
| 命令 | 作用 |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| `qi auth login <provider>` | **输入可见**地写入 API key(auth store);TUI 里的 `/login` 是**遮罩**输入(见 [slash-commands.md](slash-commands.md)) |
|
|
248
|
+
| `qi auth logout <provider>` | 删除该 provider 的凭证 |
|
|
249
|
+
| `qi auth list` | 列出 auth store 里**有凭证**的 provider |
|
|
250
|
+
| `qi auth print-api-key <provider>` | 打印解析到的 key(用于喂给别的工具) |
|
|
251
|
+
| `qi auth print-bearer-token <provider>` | 同上 —— qi 没有 OAuth,所以"bearer token"就是 API key |
|
|
252
|
+
| `qi auth check <provider>` | 检查该 provider 能否解析出凭证 |
|
|
253
|
+
| `qi auth rm <provider>` | 同 `logout` |
|
|
254
|
+
| `qi --list-models` | 列出 provider / 模型,并显示每个 provider 的凭证状态 |
|
|
255
|
+
| `qi doctor` | 逐个 provider 打印凭证状态(`OK` / `缺密钥`)+ 来源 |
|
|
256
|
+
|
|
257
|
+
`qi auth print-bearer-token` 接受 `--no-refresh` 参数(为兼容既有命令面),但 qi 没有 OAuth,
|
|
258
|
+
所以**它是空操作**。
|
|
259
|
+
|
|
260
|
+
## 6. 环境变量总表
|
|
261
|
+
|
|
262
|
+
qi 自己识别的变量分两类。
|
|
263
|
+
|
|
264
|
+
**配置类(4 个)**:
|
|
265
|
+
|
|
266
|
+
| 变量 | 作用 |
|
|
267
|
+
| --- | --- |
|
|
268
|
+
| `QI_CONFIG_DIR` | 名字空间根(默认 `~/.qi`)。仅用于推导默认路径与迁移 |
|
|
269
|
+
| `QI_AGENT_HOME` | **agent 目录本身**(默认 `~/.qi/agent`)。全部用户级状态的挂载点:`settings.json` / `models.json` / `auth.json` / `sessions` / `skills` / `agents` / `extensions` |
|
|
270
|
+
| `QI_AGENT_CONFIG` | **直接指定 `models.json` 文件**(优先于项目 / 全局路径) |
|
|
271
|
+
| `QI_THEME` | `dark` / `light` / `auto`,覆盖 `settings.theme`(见 [themes.md](themes.md)) |
|
|
272
|
+
|
|
273
|
+
**会话类(5 个)—— 只出现在 bash / powershell 的**子进程**里,不是给用户设的**:
|
|
274
|
+
|
|
275
|
+
| 变量 | 作用 |
|
|
276
|
+
| --- | --- |
|
|
277
|
+
| `QI_SESSION_ID` | 当前会话 id |
|
|
278
|
+
| `QI_SESSION_FILE` | 会话 JSONL 的绝对路径 |
|
|
279
|
+
| `QI_PROVIDER` / `QI_MODEL` | 当前模型 |
|
|
280
|
+
| `QI_REASONING_LEVEL` | 当前思考级别 |
|
|
281
|
+
|
|
282
|
+
它们由 `Runtime._session_env()` 生成、`merged_env()` 注入,**先删后填**所以不会从父进程继承。细节见 [environment-variables.md](environment-variables.md)。
|
|
283
|
+
|
|
284
|
+
`models.json` 的完整查找顺序是 `QI_AGENT_CONFIG` → `<项目>/.qi/models.json` → `~/.qi/agent/models.json`,
|
|
285
|
+
细节见 [models.md](models.md)。
|
|
286
|
+
|
|
287
|
+
> `QI_AGENT_HOME` 指向的是**目录本身**,不是它的父目录 —— 拿它去拼 `agent/settings.json` 会得到
|
|
288
|
+
> `…/agent/agent/settings.json`。这是这个变量最常见的误用。
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# 快速开始
|
|
2
|
+
|
|
3
|
+
从零到第一次对话:装 → 配模型 → 跑。
|
|
4
|
+
|
|
5
|
+
## 1. 装
|
|
6
|
+
|
|
7
|
+
qi 需要 **Python ≥ 3.12**。
|
|
8
|
+
|
|
9
|
+
**发行名是 `qi-coding-agent`**(PyPI 上的 `qi-agent` 属于**别的项目**,别装错;import 包名仍是
|
|
10
|
+
`qi_agent`、命令仍是 `qi`)。目前**还没发布**,所以以从源码安装为主 —— 不要假设
|
|
11
|
+
`pip install qi-coding-agent` 现在就能装到东西。
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/viewnah/qi.git
|
|
15
|
+
cd qi
|
|
16
|
+
uv sync # 或用你自己习惯的方式装进某个 venv:
|
|
17
|
+
# python -m venv .venv && .venv/bin/pip install -e .
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
装完 `qi` 命令可用(`[project.scripts]` 里的入口):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run qi --version # 或激活 venv 后直接 `qi --version`
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
首次运行会创建 `~/.qi/agent/`(用户级状态都挂在这里:配置、会话、技能、扩展)。
|
|
27
|
+
|
|
28
|
+
## 2. 配一个模型
|
|
29
|
+
|
|
30
|
+
qi 不内置任何 provider 就必须先配一个能用的模型。交互式引导:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
qi init
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
它会走三步(样式复刻 QwenPaw 的 `init`):**选 provider**(已有 / 新建 → Base URL → API 类型 → API Key)
|
|
37
|
+
→ **Add Models**(`Add a model?` 循环,含 reasoning / contextWindow / maxTokens)
|
|
38
|
+
→ **选一个默认模型**。产物是三个文件:`models.json`(provider 与模型清单)、
|
|
39
|
+
`settings.json`(默认模型)、`auth.json`(凭证,0600)。
|
|
40
|
+
|
|
41
|
+
CI / 脚本里不想交互就一行:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
qi init -y --provider deepseek --model deepseek-chat \
|
|
45
|
+
--base-url https://api.deepseek.com/v1 --api openai-completions --api-key sk-xxx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> 更细的字段与手工写法见 [models.md](models.md);凭证的解析顺序、`apiKey` 的
|
|
49
|
+
> `!command` / `$ENV` 写法见 [providers.md](providers.md)。
|
|
50
|
+
|
|
51
|
+
## 3. 验一下
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
qi doctor
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
它逐项报告:读到了哪些配置文件、装了哪些扩展、每个 provider 的**凭证状态**(`OK` / `缺密钥`,
|
|
58
|
+
以及来源)、默认模型。缺东西时它会给出可复制的修复命令。
|
|
59
|
+
|
|
60
|
+
这一步别跳过——"配了但没生效"几乎都能在这里一眼看出来(尤其是凭证:显示 `缺失(env:OPENAI_API_KEY(未设置))`
|
|
61
|
+
这种带括号说明的,就是"找到了来源但值为空")。
|
|
62
|
+
|
|
63
|
+
## 4. 给它一个任务
|
|
64
|
+
|
|
65
|
+
三种形态,按用途选:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
qi # 交互界面(TUI)。裸 `qi` 就是这个
|
|
69
|
+
qi "分析这个仓库" # 进 TUI,并把这句话作为首条消息发出
|
|
70
|
+
qi -p "分析这个仓库" # 无头一次执行,只输出答案(脚本用)
|
|
71
|
+
qi --mode json "分析这个仓库" # 事件流(一行一个 JSON 对象),给管道/上游服务用
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
两者区别:`-p` 的 stdout **只有答案**(进度走 stderr,`--verbose` 才显示);`--mode json` 输出全部
|
|
75
|
+
事件,见 [json.md](json.md)。
|
|
76
|
+
|
|
77
|
+
想让它问一句答一句、边看边改 → 用 TUI;想拿结果喂给别的程序 → 用 `-p` 或 `--mode json`。
|
|
78
|
+
|
|
79
|
+
## 5. 接着往下
|
|
80
|
+
|
|
81
|
+
| 想做 | 去哪 |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| 下次接着这条会话 | `qi -c`(最近一条)或 `qi --session <id>` —— [sessions.md](sessions.md) |
|
|
84
|
+
| 换模型 / 换思考级别 | TUI 里换,或 `--thinking <级别>` —— [tui.md](tui.md) |
|
|
85
|
+
| 加自己的技能 | 写 `SKILL.md` 放进 `~/.qi/agent/skills/` —— [skills.md](skills.md) |
|
|
86
|
+
| 改系统提示词 | 写 `.qi/SYSTEM.md`(**整体替换**默认基座,注意副作用) —— [configuration.md](configuration.md) |
|
|
87
|
+
| 写扩展 | [extensions.md](extensions.md) |
|
|
88
|
+
| 换个配色 | `qi config --set theme=dark` 或 `QI_THEME=light qi` —— [themes.md](themes.md) |
|
|
89
|
+
|
|
90
|
+
## 怎么定制 qi
|
|
91
|
+
|
|
92
|
+
按"想做的最小的事"挑:
|
|
93
|
+
|
|
94
|
+
| 想做 | 用 |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| 让它按我的偏好干活(渲染模式、压缩、思考级别…) | [设置](settings.md) / [配置](configuration.md) |
|
|
97
|
+
| 给它一份按需加载的检查清单 | [技能](skills.md) |
|
|
98
|
+
| 加自己的工具 / 命令 / 事件钩子 | [写扩展](extensions.md) |
|
|
99
|
+
| 换配色 | [主题](themes.md) |
|
|
100
|
+
| 打包分发上面这些 | [扩展的安装与声明](packages.md) |
|
|
101
|
+
|
|
102
|
+
## 6. 零配置也能跑什么
|
|
103
|
+
|
|
104
|
+
裸 core **不带角色**:它就是一个 agent,一份**代码内默认基座提示词**,加上内置工具
|
|
105
|
+
(read / ls / find / grep / write / edit / bash / powershell / clarify)。所以不装任何扩展也能正常干活 ——
|
|
106
|
+
其余能力(角色、MCP、Web UI 等)都以**扩展**形式单独提供,手册随各自的包走。
|
|
107
|
+
|
|
108
|
+
装上扩展后 `qi doctor` 的"扩展"一节会列出它们;`qi list` 会把**已装**与 `settings.packages` 里
|
|
109
|
+
**声明**的扩展做双向比对(声明了没装 / 装了没声明),见 [cli.md](cli.md)。
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# 安全地运行
|
|
2
|
+
|
|
3
|
+
qi 能执行任意命令、读写任意文件,而**装进来的扩展就是任意代码**。本文讲清**有哪些闸门、哪些地方没有闸门**,
|
|
4
|
+
以及边界应该画在哪。qi **不提供进程内沙箱** —— 真隔离见 [隔离运行](containerization.md)。
|
|
5
|
+
|
|
6
|
+
## 先分清什么会"执行"
|
|
7
|
+
|
|
8
|
+
| 种类 | 是什么 | 未信任项目时 |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| **扩展**(extension) | **可执行代码**,与 qi 同进程、同一棵依赖树、全权限 | **挡住**(不扫项目目录) |
|
|
11
|
+
| **技能**(skill) | **文本指令**:描述 + 正文,模型按需读取并照做 | **不挡** |
|
|
12
|
+
| **项目上下文**(`AGENTS.md` 等) | **文本指令**,整篇进系统提示词 | **不挡** |
|
|
13
|
+
| **`bash` 工具** | 全权子进程(无命令级过滤) | 与信任无关(它就是个工具) |
|
|
14
|
+
|
|
15
|
+
界线画在"**可执行 vs 文本**"上。所以信任门控**不能**阻止一个恶意仓库通过 `AGENTS.md` 或
|
|
16
|
+
`.agents/skills/**/SKILL.md` 影响模型行为 —— 那一侧没有任何自动闸门,只能靠你自己看。
|
|
17
|
+
|
|
18
|
+
## 项目信任
|
|
19
|
+
|
|
20
|
+
项目级 `.qi/` 可能含可执行代码,所以默认**不信任**:
|
|
21
|
+
|
|
22
|
+
| 值 / 命令 | 含义 |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `qi -a` / `--approve` | 信任本项目(**本次运行**),加载项目级扩展 |
|
|
25
|
+
| `qi -na` / `--no-approve` | 明确不信任;两个同给报错(退出码 2) |
|
|
26
|
+
| `settings.defaultProjectTrust` | `ask`(默认)/ `always` / `never`。**只从用户级读** —— 项目级写了会被忽略并提示 |
|
|
27
|
+
|
|
28
|
+
优先级:**CLI 显式表态 > `project_trust` 事件(用户级 / `-e` 扩展可投票)> `trust.json` 的按目录决定 >
|
|
29
|
+
`defaultProjectTrust`**。
|
|
30
|
+
|
|
31
|
+
- `ask` 在**没有界面**时(`qi -p`)**保守判不信任**:跳过项目级资源,并在 stderr 提示用 `-a`。CI 必须显式信任。
|
|
32
|
+
- **按目录记住决定**:TUI 里 `/trust`(连带上一层)、`/trust no`、`/trust forget`,存 `~/.qi/agent/trust.json`
|
|
33
|
+
—— **写在用户 home,不碰仓库里任何文件**(仓库不能为自己背书)。写完当前会话不重载,重启后生效。
|
|
34
|
+
- 判定结果与理由会进启动提示(`qi doctor` 也显示),例如
|
|
35
|
+
`未信任项目(defaultProjectTrust=ask(无 UI;用 -a 信任)):.qi/extensions/ 未加载`。
|
|
36
|
+
|
|
37
|
+
**未信任时不加载**:<项目>`/.qi/extensions/` 与项目级 `settings.json` 的 `extensions[]`(只挡项目那一份)。
|
|
38
|
+
**仍然生效**:项目级技能(`.agents/skills/`、`<git根>/.qi/skills/`)、项目上下文(`AGENTS.md` 等)、
|
|
39
|
+
项目级 settings 的普通字段(`defaultTools`、`theme`…)。细目见 [configuration.md](configuration.md)。
|
|
40
|
+
|
|
41
|
+
## bash 的边界
|
|
42
|
+
|
|
43
|
+
**qi 没有命令级过滤**:不筛子命令、不拦重定向、不做只读白名单 —— `bash` 能跑任何东西。这是**刻意的**,
|
|
44
|
+
理由与四种绕过写法见 [bash-allowlist.md](../design/bash-allowlist.md)。
|
|
45
|
+
|
|
46
|
+
限制手段只有两档:
|
|
47
|
+
|
|
48
|
+
1. **工具级收窄**:`-t` 严格白名单 / `-xt` 排除 / `-nt` 全禁 / `-nbt` 只去内置;`settings.defaultTools` 只挑内置那档。
|
|
49
|
+
想"连 bash 都不给"就别把 `bash` 列进白名单。见 [cli.md](cli.md)。
|
|
50
|
+
2. **把 qi 放进容器 / VM / 受限用户**(唯一真边界):见 [containerization.md](containerization.md)。
|
|
51
|
+
|
|
52
|
+
其它约定与现状:
|
|
53
|
+
|
|
54
|
+
- 文件工具的路径限制在**会话工作目录**内(`read` / `ls` / `find` / `grep` / `write` / `edit`),**bash 不受限**。
|
|
55
|
+
- `bash` / `powershell` 的子进程 stdin 是 `DEVNULL`(读不到 TUI 的按键);超时按**进程组**回收。
|
|
56
|
+
- TUI 里 `!` 手动命令不经任何过滤 —— 那是你亲手敲的,不算模型越权。
|
|
57
|
+
- **没有工具级审批弹窗**(那类 workflow 属于扩展或外部工具)。破坏性操作的交互前钩子(HITL)**未做**,
|
|
58
|
+
见 [PLAN.md](../design/PLAN.md) 的 v2 清单。
|
|
59
|
+
|
|
60
|
+
## 配置文件里的命令
|
|
61
|
+
|
|
62
|
+
`models.json` 的 `apiKey` 支持 `!command`(执行命令取 stdout)。它**不走 shell**(`shlex` 拆参 + `shell=False`),
|
|
63
|
+
只在你写的那份配置被解析时求值 —— 属于**你的配置信任域**,与 `auth.json` 同级;扩展或模型内容**无法**触发它。
|
|
64
|
+
三种写法见 [models.md](models.md)。
|
|
65
|
+
|
|
66
|
+
## 报漏洞 / 上报问题
|
|
67
|
+
|
|
68
|
+
qi 目前没有专门的安全上报渠道;设计层面的取舍记录在 [design/](../design)(尤其
|
|
69
|
+
[bash-allowlist.md](../design/bash-allowlist.md) 与 [PLAN.md](../design/PLAN.md) 的 containerization 条目)。
|