@9000ai/cli 0.4.0 → 0.5.1

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.
@@ -0,0 +1,75 @@
1
+ # 9000AI 使用规范详细参考
2
+
3
+ ## 性能准则
4
+
5
+ ### 数据精简
6
+
7
+ 所有查询命令都支持 `--fields` 和 `--compact` 参数。**默认必须用 `--fields` 只取需要的字段,禁止全量读取。**
8
+
9
+ ```bash
10
+ # 错误 — 返回 83KB 全量数据,浪费上下文
11
+ 9000ai task results --task-id <id>
12
+
13
+ # 正确 — 只取需要的字段
14
+ 9000ai task results --task-id <id> --fields desc,video_url,likes,author_name
15
+
16
+ # 更紧凑 — 每条一行,适合批量处理
17
+ 9000ai task results --task-id <id> --fields desc,video_url,likes --compact
18
+ ```
19
+
20
+ 常用字段组合:
21
+ - 搜索结果概览:`--fields desc,author_name,likes,duration`
22
+ - 提取转写链接:`--fields video_url,download_url,play_url,desc`
23
+ - 监控结果摘要:`--fields desc,author_name,likes,comments,create_time`
24
+ - 主页视频列表:`--fields description,likes,comments,plays,publish_time`
25
+
26
+ ### 并行执行
27
+
28
+ 独立的操作必须并行,不要串行等待。用 subagent 或并行工具调用。
29
+
30
+ 可以并行的场景:
31
+ - 写 inbox 文件 + 提交转写任务
32
+ - 多个关键词的搜索结果查询
33
+ - 查多个 task_id 的状态
34
+ - 热榜 + 关键词搜索同时拉
35
+
36
+ 不能并行的场景:
37
+ - 搜索 → 等结果 → 筛选(有依赖关系)
38
+ - 创建监控对象 → 提交监控任务(需要 creator_id)
39
+
40
+ ### 减少中间文件
41
+
42
+ CLI 支持 `--json-file` 也支持直接传参。简单请求直接传参,不要写中间 JSON 文件。
43
+
44
+ ### 不暴露中间过程
45
+
46
+ 用户要的是结果,不是过程。拿到数据后直接输出摘要或执行下一步,不要把原始 JSON 展示给用户。
47
+
48
+ ## 串联规则
49
+
50
+ 只有在用户明确要求多步串联时,才跨模块:
51
+ - 先搜索,再转写
52
+ - 先监控,再分析结果
53
+
54
+ 串联步骤:
55
+ 1. 先进入第一个模块完成提交
56
+ 2. 拿到任务结果或结果引用
57
+ 3. 再进入下一个模块继续执行
58
+
59
+ ## 统一使用规范
60
+
61
+ 1. 不把大结果直接塞进上下文
62
+ 2. 优先使用 `task_id`、`batch_id`、`row_no` 继续操作
63
+ 3. 如果用户问"该用哪个模块",中台负责判断
64
+ 4. 如果用户已经明确说了模块名,直接进入对应 skill
65
+ 5. 接口报错、任务失败、用户不满时,主动提议 `9000ai feedback submit` 提交反馈
66
+
67
+ ## PowerShell 编码
68
+
69
+ Windows PowerShell 中文容易乱码,先设 UTF-8:
70
+
71
+ ```powershell
72
+ $env:PYTHONIOENCODING = 'utf-8'
73
+ [Console]::InputEncoding = [System.Text.Encoding]::UTF8
74
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
75
+ ```
@@ -24,13 +24,23 @@ output-format: task-oriented
24
24
 
25
25
  用于:
26
26
  - 查看当前 key 和能力
27
+ - **直接获取某个用户的主页视频**(同步,不入队列)
27
28
  - 新建监控对象
28
29
  - 查看监控对象列表
29
30
  - 修改、启停、重置、删除监控对象
30
- - 提交主页监控任务
31
+ - 提交主页监控任务(异步,加入队列定期监控)
31
32
  - 查看运行历史和单次运行详情
32
33
  - 查看任务状态和任务结果
33
34
 
35
+ ## 两种获取模式
36
+
37
+ | 模式 | 命令 | 场景 |
38
+ |------|------|------|
39
+ | **直接获取** | `9000ai monitor fetch --sec-user <id>` | 临时看某人主页、一次性分析 |
40
+ | **加入监控** | `9000ai monitor submit --json-file ...` | 持续跟踪、定期检查更新 |
41
+
42
+ 判断标准:用户说"看一下xxx的主页/视频" → 直接获取;用户说"监控xxx/跟踪xxx" → 加入队列。
43
+
34
44
  ## 工作方式
35
45
 
36
46
  主页监控的"提交运行"是异步任务,标准动作是:
@@ -89,6 +99,7 @@ output-format: task-oriented
89
99
  9000ai config set --base-url http://127.0.0.1:8025 --api-key <key>
90
100
  9000ai auth whoami
91
101
  9000ai auth capabilities
102
+ 9000ai monitor fetch --sec-user <sec_user_id> --fields description,likes,comments,plays
92
103
  9000ai monitor list-creators
93
104
  9000ai monitor create-creator --json-file creator.json
94
105
  9000ai monitor update-creator --creator-id <creator_id> --json-file creator_update.json
@@ -1,56 +0,0 @@
1
- """9000AI 中台初始化脚本(旧版,保留兼容)。
2
-
3
- 推荐使用新的 CLI 工具:
4
- 9000ai config set --base-url http://192.168.1.100:8025 --api-key sk-xxx
5
-
6
- 查看当前配置:
7
- 9000ai config show
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import argparse
13
- import json
14
- import sys
15
- from pathlib import Path
16
-
17
- HUB_ROOT = Path(__file__).resolve().parent
18
- if str(HUB_ROOT) not in sys.path:
19
- sys.path.insert(0, str(HUB_ROOT))
20
-
21
- from shared.runner import _HUB_CONFIG_PATH, load_hub_config, save_hub_config # noqa: E402
22
-
23
-
24
- def main() -> None:
25
- parser = argparse.ArgumentParser(description="9000AI 中台连接配置(首次使用前必须运行一次)")
26
- parser.add_argument("--base-url", help="中台服务地址,例如 http://192.168.1.100:8025")
27
- parser.add_argument("--api-key", help="你的 API Key,找管理员要")
28
- parser.add_argument("--show", action="store_true", help="查看当前已保存的配置")
29
- args = parser.parse_args()
30
-
31
- if args.show:
32
- cfg = load_hub_config()
33
- if cfg:
34
- print("当前 9000AI 中台配置:")
35
- print(json.dumps(cfg, ensure_ascii=False, indent=2))
36
- else:
37
- print(f"还没有初始化过。请先运行:")
38
- print(f" 9000ai config set --base-url <中台地址> --api-key <你的key>")
39
- return
40
-
41
- if not args.base_url or not args.api_key:
42
- parser.error("--base-url 和 --api-key 都是必填项。\n"
43
- " 推荐使用: 9000ai config set "
44
- "--base-url http://192.168.1.100:8025 --api-key sk-xxx")
45
-
46
- save_hub_config(base_url=args.base_url, api_key=args.api_key)
47
- print(f"初始化完成!配置已保存。")
48
- print(f" 中台地址: {args.base_url.rstrip('/')}")
49
- print(f" API Key: {args.api_key[:8]}...")
50
- print(f" 配置文件: {_HUB_CONFIG_PATH}")
51
- print()
52
- print("现在可以使用所有 9000AI skill 了。")
53
-
54
-
55
- if __name__ == "__main__":
56
- main()
@@ -1,130 +0,0 @@
1
- ---
2
- name: content-init
3
- description: 内容创作者工作空间初始化。创建完整的目录结构、agent 人格、主张库、用户画像模板,配置中台连接,并生成使用指南。只需运行一次。
4
- triggers:
5
- - /content-init
6
- - /初始化工作空间
7
- - 初始化内容工作空间
8
- - 初始化
9
- - 第一次用
10
- role: initializer
11
- scope: workspace-setup
12
- output-format: file-creation
13
- ---
14
-
15
- # 内容工作空间初始化
16
-
17
- 你负责帮内容创作者完成工作空间的一次性初始化。完成后,用户就拥有了完整的内容创作文件系统和使用指南。
18
-
19
- ---
20
-
21
- ## 前置条件:中台连接配置
22
-
23
- 在创建文件之前,先确认中台已连上。
24
-
25
- **检查方式**:
26
-
27
- ```bash
28
- 9000ai config show
29
- ```
30
-
31
- - 输出了 `base_url` 和 `api_key` → 继续下一步
32
- - 提示未配置 → 问用户要中台地址和 API Key,然后运行:
33
-
34
- ```bash
35
- 9000ai config set --base-url <地址> --api-key <key>
36
- ```
37
-
38
- > **不知道地址和 key?**
39
- > - `base-url`:找中台管理员要,或查看你收到的 9000AI 服务开通通知。
40
- > - `api-key`:找中台管理员要,每个用户的 key 独立分配。
41
-
42
- ---
43
-
44
- ## 幂等性规则
45
-
46
- 这个初始化可以安全地多次运行,不会破坏已有数据。
47
-
48
- | 情况 | 处理 |
49
- |------|------|
50
- | 目录已存在 | 跳过,不重建 |
51
- | `profile/` 下的文件已存在 | **一律跳过**(用户可能已开始填写) |
52
- | `claims/README.md` 是旧版(含"每个主张是一个文件夹") | 备份为 `.bak`,替换为新版 |
53
- | `claims/` 数据文件已存在(json/jsonl) | 跳过 |
54
- | `agents/`、`CLAUDE.md`、`guide.md` 不存在 | 创建 |
55
- | `agents/`、`CLAUDE.md`、`guide.md` 已存在 | 跳过 |
56
-
57
- 核心原则:只创建不存在的东西,绝不覆盖用户已编辑的内容。
58
-
59
- ---
60
-
61
- ## 模板来源
62
-
63
- 所有模板文件位于本 skill 的 `templates/` 子目录中,初始化时直接复制到用户项目根目录。
64
-
65
- 模板根路径(相对于本 SKILL.md):`init/templates/`
66
-
67
- ---
68
-
69
- ## 创建清单
70
-
71
- 按以下顺序在**用户当前项目根目录**下创建文件和目录。每一步先检查是否已存在。
72
-
73
- | # | 目标路径 | 模板来源 | 跳过条件 |
74
- |---|---------|---------|---------|
75
- | 1 | `agents/` | — | 目录已存在 |
76
- | 2 | `agents/content-agent.md` | `templates/agents/content-agent.md` | 文件已存在 |
77
- | 3 | `agents/README.md` | `templates/agents/README.md` | 文件已存在 |
78
- | 4 | `profile/` | — | 目录已存在 |
79
- | 5 | `profile/identity.md` | `templates/profile/identity.md` | 文件已存在 |
80
- | 6 | `profile/voice.md` | `templates/profile/voice.md` | 文件已存在 |
81
- | 7 | `profile/topics.md` | `templates/profile/topics.md` | 文件已存在 |
82
- | 8 | `profile/product.md` | `templates/profile/product.md` | 文件已存在 |
83
- | 9 | `inbox/` | — | 目录已存在 |
84
- | 10 | `inbox/README.md` | `templates/inbox/README.md` | 文件已存在 |
85
- | 11 | `claims/` | — | 目录已存在 |
86
- | 12 | `claims/README.md` | `templates/claims/README.md` | 已存在且**不含**"每个主张是一个文件夹" |
87
- | 13 | `claims/claims.json` | `templates/claims/claims.json` | 文件已存在 |
88
- | 14 | `claims/angles.jsonl` | 空文件 | 文件已存在 |
89
- | 15 | `claims/cases.jsonl` | 空文件 | 文件已存在 |
90
- | 16 | `claims/outputs.jsonl` | 空文件 | 文件已存在 |
91
- | 17 | `projects/` | — | 目录已存在 |
92
- | 18 | `projects/README.md` | `templates/projects/README.md` | 文件已存在 |
93
- | 19 | `CLAUDE.md` | `templates/CLAUDE.md` | 文件已存在 |
94
- | 20 | `guide.md` | `templates/guide.md` | 文件已存在 |
95
-
96
- 对于第 12 项:如果 `claims/README.md` 已存在且内容含"每个主张是一个文件夹",先备份为 `claims/README.md.bak`,再用模板覆盖。
97
-
98
- ---
99
-
100
- ## 初始化完成后汇报
101
-
102
- 所有文件创建完毕后,输出以下格式的汇报:
103
-
104
- ```
105
- ## 工作空间初始化完成
106
-
107
- ### 已创建
108
- - [文件路径] — [一句话说明]
109
- - ...
110
-
111
- ### 已跳过(已存在)
112
- - [文件路径]
113
- - ...
114
-
115
- ### 下一步
116
-
117
- 1. **填写画像** → 打开 profile/ 下的文件,把"待填写"替换成真实信息
118
- 2. **建立主张** → 编辑 claims/claims.json
119
- 3. **开始使用** → 查看 guide.md 了解全部用法
120
-
121
- 如果目录中有散落的素材文件(如会议转写),可以移入 inbox/,我会帮你处理。
122
- ```
123
-
124
- ---
125
-
126
- ## 注意事项
127
-
128
- - 不要在初始化过程中自动移动任何已有用户文件
129
- - 初始化只负责搭骨架,不填内容
130
- - 如果 `claims/` 中已有按主张命名的子目录(旧结构),不要删除,在汇报中告知用户新结构的差异
@@ -1,24 +0,0 @@
1
- 请载入 agents/content-agent.md 作为你的角色设定。
2
-
3
- ## 工作空间概览
4
-
5
- | 目录 | 用途 |
6
- |------|------|
7
- | profile/ | 用户画像(只读) |
8
- | inbox/ | 素材投喂入口 |
9
- | claims/ | 主张库(核心资产) |
10
- | projects/ | 选题项目 |
11
-
12
- 详细使用指南见 guide.md。
13
-
14
- ## 平台能力映射
15
-
16
- | 能力 | CLI 命令 |
17
- |------|---------|
18
- | 配置中台连接 | `9000ai config set --base-url <url> --api-key <key>` |
19
- | 热点榜获取 | `9000ai search hot` |
20
- | 关键词搜索 | `9000ai search keyword` |
21
- | 对标账号监控 | `9000ai monitor` |
22
- | 视频转文字 | `9000ai transcribe` |
23
- | 查看任务状态 | `9000ai task status --task-id <id>` |
24
- | 身份验证 | `9000ai auth whoami` |
@@ -1 +0,0 @@
1
- """Shared execution helpers shipped with 9000AI hub."""
@@ -1,135 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- import sys
6
- from dataclasses import dataclass
7
- from pathlib import Path
8
- from typing import Any
9
-
10
- import requests
11
-
12
-
13
- DEFAULT_BASE_URL = "http://127.0.0.1:8025"
14
- DEFAULT_TIMEOUT_SECONDS = 300
15
- DEFAULT_API_KEY_HEADER = "X-API-Key"
16
- HUB_BASE_URL_ENV = "9000AI_BASE_URL"
17
- HUB_API_KEY_ENV = "9000AI_API_KEY"
18
-
19
- # hub 自身的全局配置文件,位于 9000AI-hub-9000AI/local/config.json
20
- _HUB_CONFIG_PATH = Path(__file__).resolve().parents[1] / "local" / "config.json"
21
-
22
-
23
- @dataclass(frozen=True)
24
- class ModuleSpec:
25
- module: str
26
- skill_root: Path
27
- default_base_url: str = DEFAULT_BASE_URL
28
- api_key_header: str = DEFAULT_API_KEY_HEADER
29
- timeout_seconds: int = DEFAULT_TIMEOUT_SECONDS
30
-
31
- @property
32
- def config_path(self) -> Path:
33
- return self.skill_root / "local" / "config.json"
34
-
35
- @property
36
- def output_dir(self) -> Path:
37
- return self.skill_root / "output"
38
-
39
-
40
- def configure_stdout_encoding() -> None:
41
- encoding = (sys.stdout.encoding or "").lower()
42
- if encoding != "utf-8" and hasattr(sys.stdout, "reconfigure"):
43
- sys.stdout.reconfigure(encoding="utf-8", errors="replace")
44
-
45
-
46
- def load_local_config(spec: ModuleSpec) -> dict[str, Any]:
47
- if not spec.config_path.exists():
48
- return {}
49
- return json.loads(spec.config_path.read_text(encoding="utf-8"))
50
-
51
-
52
- def load_hub_config() -> dict[str, Any]:
53
- if not _HUB_CONFIG_PATH.exists():
54
- return {}
55
- return json.loads(_HUB_CONFIG_PATH.read_text(encoding="utf-8"))
56
-
57
-
58
- def save_local_config(spec: ModuleSpec, *, base_url: str, api_key: str) -> None:
59
- spec.config_path.parent.mkdir(parents=True, exist_ok=True)
60
- spec.config_path.write_text(
61
- json.dumps({"base_url": base_url.rstrip("/"), "api_key": api_key}, ensure_ascii=False, indent=2),
62
- encoding="utf-8",
63
- )
64
-
65
-
66
- def save_hub_config(*, base_url: str, api_key: str) -> None:
67
- """保存 hub 全局配置,所有 skill 共享。"""
68
- _HUB_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
69
- _HUB_CONFIG_PATH.write_text(
70
- json.dumps({"base_url": base_url.rstrip("/"), "api_key": api_key}, ensure_ascii=False, indent=2),
71
- encoding="utf-8",
72
- )
73
-
74
-
75
- def resolve_base_url(spec: ModuleSpec, override: str | None) -> str:
76
- hub_config = load_hub_config()
77
- skill_config = load_local_config(spec)
78
- value = (
79
- override
80
- or os.getenv(HUB_BASE_URL_ENV)
81
- or hub_config.get("base_url")
82
- or skill_config.get("base_url")
83
- or spec.default_base_url
84
- )
85
- return str(value).rstrip("/")
86
-
87
-
88
- def resolve_api_key(spec: ModuleSpec, override: str | None) -> str:
89
- hub_config = load_hub_config()
90
- skill_config = load_local_config(spec)
91
- value = override or os.getenv(HUB_API_KEY_ENV) or hub_config.get("api_key") or skill_config.get("api_key")
92
- if not value:
93
- raise SystemExit(
94
- "还没连上 9000AI 中台。请先初始化:\n"
95
- " 9000ai config set --base-url <中台地址> --api-key <你的key>\n"
96
- "\n"
97
- "不知道地址和 key?找中台管理员要。"
98
- )
99
- return str(value)
100
-
101
-
102
- def load_json_file(path: str) -> Any:
103
- return json.loads(Path(path).read_text(encoding="utf-8"))
104
-
105
-
106
- def request_json(
107
- spec: ModuleSpec,
108
- *,
109
- method: str,
110
- base_url: str,
111
- api_key: str,
112
- path: str,
113
- payload: Any | None = None,
114
- timeout_seconds: int | None = None,
115
- ) -> Any:
116
- response = requests.request(
117
- method=method,
118
- url=f"{base_url}{path}",
119
- headers={spec.api_key_header: api_key, "Content-Type": "application/json"},
120
- json=payload,
121
- timeout=timeout_seconds or spec.timeout_seconds,
122
- )
123
- data = response.json()
124
- if response.status_code >= 400:
125
- raise SystemExit(json.dumps(data, ensure_ascii=False, indent=2))
126
- return data
127
-
128
-
129
- def ensure_output_dir(spec: ModuleSpec) -> Path:
130
- spec.output_dir.mkdir(parents=True, exist_ok=True)
131
- return spec.output_dir
132
-
133
-
134
- def print_json(payload: Any) -> None:
135
- print(json.dumps(payload, ensure_ascii=False, indent=2))
@@ -1,3 +0,0 @@
1
- display_name: douyin-monitor-9000AI
2
- short_description: 通过 /douyin-monitor-9000AI 提交主页监控任务并回查结果
3
- default_prompt: 帮我通过 /douyin-monitor-9000AI 提交抖音主页监控任务,拿到 task_id 后稍后回查结果。