@9000ai/cli 0.4.0 → 0.5.0
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.
- package/dist/client.d.ts +6 -0
- package/dist/client.js +22 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +83 -0
- package/dist/commands/monitor.js +17 -0
- package/dist/commands/search.js +30 -4
- package/dist/commands/transcribe.js +19 -2
- package/dist/index.js +3 -1
- package/dist/postinstall.js +6 -1
- package/package.json +1 -1
- package/skills/9000AI-hub/SKILL.md +75 -190
- package/skills/9000AI-hub/references/usage-guidelines.md +75 -0
- package/skills/douyin-monitor/SKILL.md +12 -1
- package/skills/9000AI-hub/configure.py +0 -56
- package/skills/9000AI-hub/init/SKILL.md +0 -130
- package/skills/9000AI-hub/init/templates/CLAUDE.md +0 -24
- package/skills/9000AI-hub/shared/__init__.py +0 -1
- package/skills/9000AI-hub/shared/runner.py +0 -135
- package/skills/douyin-monitor/agents/openai.yaml +0 -3
- package/skills/douyin-monitor/scripts/douyin_monitor_api.py +0 -273
- package/skills/douyin-topic-discovery/agents/openai.yaml +0 -3
- package/skills/douyin-topic-discovery/scripts/douyin_topic_discovery_api.py +0 -497
- package/skills/feedback/scripts/feedback_api.py +0 -93
- package/skills/video-transcription/agents/openai.yaml +0 -3
- package/skills/video-transcription/scripts/video_transcription_api.py +0 -183
package/dist/client.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface RequestOptions {
|
|
|
7
7
|
timeout?: number;
|
|
8
8
|
}
|
|
9
9
|
export declare function request(opts: RequestOptions): Promise<unknown>;
|
|
10
|
+
/** Poll a task/batch until terminal status. Returns final response data. */
|
|
11
|
+
export declare function pollUntilDone(path: string, opts?: {
|
|
12
|
+
interval?: number;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
quiet?: boolean;
|
|
15
|
+
}): Promise<unknown>;
|
|
10
16
|
export declare function printJson(data: unknown, opts?: {
|
|
11
17
|
fields?: string;
|
|
12
18
|
compact?: boolean;
|
package/dist/client.js
CHANGED
|
@@ -40,6 +40,28 @@ export async function request(opts) {
|
|
|
40
40
|
clearTimeout(timer);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
/** Poll a task/batch until terminal status. Returns final response data. */
|
|
44
|
+
export async function pollUntilDone(path, opts) {
|
|
45
|
+
const interval = opts?.interval ?? 3_000;
|
|
46
|
+
const timeout = opts?.timeout ?? 600_000;
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
while (Date.now() - start < timeout) {
|
|
49
|
+
const data = await request({ method: "GET", path });
|
|
50
|
+
const resp = data;
|
|
51
|
+
const inner = (resp.data ?? resp);
|
|
52
|
+
const status = (inner.status ?? "").toUpperCase();
|
|
53
|
+
if (["SUCCESS", "COMPLETED", "FAILED", "ERROR"].includes(status)) {
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
if (!opts?.quiet) {
|
|
57
|
+
const elapsed = Math.round((Date.now() - start) / 1000);
|
|
58
|
+
console.error(`[${elapsed}s] status: ${status || "PENDING"} — polling...`);
|
|
59
|
+
}
|
|
60
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
61
|
+
}
|
|
62
|
+
console.error("Error: poll timed out");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
43
65
|
export function printJson(data, opts) {
|
|
44
66
|
let output = data;
|
|
45
67
|
// --fields: extract specified fields from items/data
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join, relative } from "path";
|
|
4
|
+
const TEMPLATES_DIR = join(homedir(), ".9000ai", "skills", "9000AI-hub", "init", "templates");
|
|
5
|
+
/** Files under profile/ are never overwritten — user may have edited them. */
|
|
6
|
+
const NEVER_OVERWRITE_DIRS = ["profile"];
|
|
7
|
+
function copyTemplates(src, dest, relBase) {
|
|
8
|
+
const created = [];
|
|
9
|
+
const skipped = [];
|
|
10
|
+
if (!existsSync(src))
|
|
11
|
+
return { created, skipped };
|
|
12
|
+
const entries = readdirSync(src);
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const srcPath = join(src, entry);
|
|
15
|
+
const destPath = join(dest, entry);
|
|
16
|
+
const rel = relative(relBase, destPath);
|
|
17
|
+
const stat = statSync(srcPath);
|
|
18
|
+
if (stat.isDirectory()) {
|
|
19
|
+
if (!existsSync(destPath)) {
|
|
20
|
+
mkdirSync(destPath, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
const sub = copyTemplates(srcPath, destPath, relBase);
|
|
23
|
+
created.push(...sub.created);
|
|
24
|
+
skipped.push(...sub.skipped);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
if (existsSync(destPath)) {
|
|
28
|
+
// Never overwrite profile files
|
|
29
|
+
const topDir = rel.split(/[/\\]/)[0];
|
|
30
|
+
if (NEVER_OVERWRITE_DIRS.includes(topDir)) {
|
|
31
|
+
skipped.push(rel);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// Other existing files: also skip (idempotent)
|
|
35
|
+
skipped.push(rel);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
mkdirSync(join(destPath, ".."), { recursive: true });
|
|
39
|
+
copyFileSync(srcPath, destPath);
|
|
40
|
+
created.push(rel);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { created, skipped };
|
|
45
|
+
}
|
|
46
|
+
export function registerInitCommand(program) {
|
|
47
|
+
program
|
|
48
|
+
.command("init")
|
|
49
|
+
.description("Initialize content workspace — copy templates to project directory")
|
|
50
|
+
.requiredOption("--path <dir>", "Project directory to initialize")
|
|
51
|
+
.action((opts) => {
|
|
52
|
+
const dest = opts.path;
|
|
53
|
+
// Check templates exist
|
|
54
|
+
if (!existsSync(TEMPLATES_DIR)) {
|
|
55
|
+
console.error(`Error: Templates not found at ${TEMPLATES_DIR}\n` +
|
|
56
|
+
`Run: npm install -g @9000ai/cli`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
// Create dest if needed
|
|
60
|
+
if (!existsSync(dest)) {
|
|
61
|
+
mkdirSync(dest, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
const { created, skipped } = copyTemplates(TEMPLATES_DIR, dest, dest);
|
|
64
|
+
// Output summary
|
|
65
|
+
if (created.length > 0) {
|
|
66
|
+
console.log(`Created ${created.length} files:`);
|
|
67
|
+
created.forEach((f) => console.log(` + ${f}`));
|
|
68
|
+
}
|
|
69
|
+
if (skipped.length > 0) {
|
|
70
|
+
console.log(`Skipped ${skipped.length} existing files:`);
|
|
71
|
+
skipped.forEach((f) => console.log(` - ${f}`));
|
|
72
|
+
}
|
|
73
|
+
if (created.length === 0 && skipped.length === 0) {
|
|
74
|
+
console.log("No template files found.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log(`\nWorkspace ready: ${dest}`);
|
|
78
|
+
console.log(`\nNext steps:`);
|
|
79
|
+
console.log(` 1. Fill in profile/ (identity.md, voice.md, topics.md, product.md)`);
|
|
80
|
+
console.log(` 2. cd ${dest}`);
|
|
81
|
+
console.log(` 3. Open Claude Code and start working`);
|
|
82
|
+
});
|
|
83
|
+
}
|
package/dist/commands/monitor.js
CHANGED
|
@@ -104,4 +104,21 @@ export function registerMonitorCommands(parent) {
|
|
|
104
104
|
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/runs/${opts.runId}` });
|
|
105
105
|
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
106
106
|
});
|
|
107
|
+
cmd
|
|
108
|
+
.command("fetch")
|
|
109
|
+
.description("Fetch user homepage videos directly (sync, no queue)")
|
|
110
|
+
.requiredOption("--sec-user <id>", "User sec_user_id or homepage URL")
|
|
111
|
+
.option("--count <n>", "Number of videos to fetch", "20")
|
|
112
|
+
.option("--sort <n>", "Sort: 0=newest 1=hottest", "0")
|
|
113
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
114
|
+
.option("--compact", "One JSON object per line")
|
|
115
|
+
.action(async (opts) => {
|
|
116
|
+
const params = new URLSearchParams({
|
|
117
|
+
sec_user_id: opts.secUser,
|
|
118
|
+
count: opts.count,
|
|
119
|
+
sort_type: opts.sort,
|
|
120
|
+
});
|
|
121
|
+
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/user-posts?${params}` });
|
|
122
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
123
|
+
});
|
|
107
124
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request, printJson } from "../client.js";
|
|
1
|
+
import { request, printJson, pollUntilDone } from "../client.js";
|
|
2
2
|
import { writeJson, writeTsv, listOutputFiles, readOutputJson, timestampSlug } from "../output.js";
|
|
3
3
|
export function registerSearchCommands(parent) {
|
|
4
4
|
const cmd = parent.command("search").description("Douyin topic discovery — trending boards and keyword search");
|
|
@@ -36,6 +36,9 @@ export function registerSearchCommands(parent) {
|
|
|
36
36
|
.option("--filter-duration <v>", "Duration filter", "")
|
|
37
37
|
.option("--min-likes <n>", "Minimum likes", "0")
|
|
38
38
|
.option("--min-comments <n>", "Minimum comments", "0")
|
|
39
|
+
.option("--wait", "Poll until batch completes then print results")
|
|
40
|
+
.option("--fields <list>", "Comma-separated fields to extract (used with --wait)")
|
|
41
|
+
.option("--compact", "One JSON object per line (used with --wait)")
|
|
39
42
|
.action(async (keywords, opts) => {
|
|
40
43
|
const payload = {
|
|
41
44
|
keywords,
|
|
@@ -55,9 +58,32 @@ export function registerSearchCommands(parent) {
|
|
|
55
58
|
});
|
|
56
59
|
const resp = data;
|
|
57
60
|
const inner = resp.data;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const batchId = inner?.batch_id;
|
|
62
|
+
if (!opts.wait || !batchId) {
|
|
63
|
+
console.log(`Batch submitted → batch_id: ${batchId}`);
|
|
64
|
+
console.log(`Use: 9000ai search batch-result --batch-id ${batchId}`);
|
|
65
|
+
printJson(inner);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// --wait: poll then fetch results
|
|
69
|
+
console.error(`Batch submitted → ${batchId}, waiting...`);
|
|
70
|
+
await pollUntilDone(`/api/v1/tasks/batch/${batchId}`);
|
|
71
|
+
const resultData = await request({
|
|
72
|
+
method: "GET",
|
|
73
|
+
path: `/api/v1/douyin/discovery/search-videos/batch/${batchId}/results`,
|
|
74
|
+
});
|
|
75
|
+
const resultResp = resultData;
|
|
76
|
+
const resultInner = resultResp.data;
|
|
77
|
+
const items = (resultInner?.items ?? []);
|
|
78
|
+
if (items.length > 0) {
|
|
79
|
+
const slug = timestampSlug();
|
|
80
|
+
writeJson("latest_search.json", resultInner);
|
|
81
|
+
writeJson(`latest_search_${slug}.json`, resultInner);
|
|
82
|
+
writeTsv("latest_search.tsv", items);
|
|
83
|
+
writeTsv(`latest_search_${slug}.tsv`, items);
|
|
84
|
+
console.error(`${items.length} items → output/latest_search.json`);
|
|
85
|
+
}
|
|
86
|
+
printJson(resultInner, { fields: opts.fields, compact: opts.compact });
|
|
61
87
|
});
|
|
62
88
|
cmd
|
|
63
89
|
.command("batch-result")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request, printJson } from "../client.js";
|
|
1
|
+
import { request, printJson, pollUntilDone } from "../client.js";
|
|
2
2
|
import { loadJsonFile } from "../utils/format.js";
|
|
3
3
|
export function registerTranscribeCommands(parent) {
|
|
4
4
|
const cmd = parent.command("transcribe").description("Video / audio transcription");
|
|
@@ -6,6 +6,9 @@ export function registerTranscribeCommands(parent) {
|
|
|
6
6
|
.command("submit")
|
|
7
7
|
.description("Submit batch video-to-text task")
|
|
8
8
|
.requiredOption("--json-file <path>", "JSON file with video task details")
|
|
9
|
+
.option("--wait", "Poll until task completes then print results")
|
|
10
|
+
.option("--fields <list>", "Comma-separated fields to extract (used with --wait)")
|
|
11
|
+
.option("--compact", "One JSON object per line (used with --wait)")
|
|
9
12
|
.action(async (opts) => {
|
|
10
13
|
const payload = loadJsonFile(opts.jsonFile);
|
|
11
14
|
const data = await request({
|
|
@@ -13,7 +16,21 @@ export function registerTranscribeCommands(parent) {
|
|
|
13
16
|
path: "/api/v1/media/batch-video-to-text",
|
|
14
17
|
payload,
|
|
15
18
|
});
|
|
16
|
-
|
|
19
|
+
const resp = data;
|
|
20
|
+
const inner = resp.data;
|
|
21
|
+
const batchId = inner?.batch_id;
|
|
22
|
+
if (!opts.wait || !batchId) {
|
|
23
|
+
printJson(data);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// --wait: poll then fetch results
|
|
27
|
+
console.error(`Batch submitted → ${batchId}, waiting...`);
|
|
28
|
+
await pollUntilDone(`/api/v1/tasks/batch/${batchId}`);
|
|
29
|
+
const resultData = await request({
|
|
30
|
+
method: "GET",
|
|
31
|
+
path: `/api/v1/tasks/batch/${batchId}`,
|
|
32
|
+
});
|
|
33
|
+
printJson(resultData, { fields: opts.fields, compact: opts.compact });
|
|
17
34
|
});
|
|
18
35
|
cmd
|
|
19
36
|
.command("text")
|
package/dist/index.js
CHANGED
|
@@ -8,11 +8,12 @@ import { registerTranscribeCommands } from "./commands/transcribe.js";
|
|
|
8
8
|
import { registerTaskCommands } from "./commands/task.js";
|
|
9
9
|
import { registerFeedbackCommands } from "./commands/feedback.js";
|
|
10
10
|
import { registerSkillCommands } from "./commands/skill.js";
|
|
11
|
+
import { registerInitCommand } from "./commands/init.js";
|
|
11
12
|
const program = new Command();
|
|
12
13
|
program
|
|
13
14
|
.name("9000ai")
|
|
14
15
|
.description("9000AI Toolbox CLI — unified interface for 9000AI platform")
|
|
15
|
-
.version("0.
|
|
16
|
+
.version("0.5.0");
|
|
16
17
|
registerConfigCommands(program);
|
|
17
18
|
registerAuthCommands(program);
|
|
18
19
|
registerSearchCommands(program);
|
|
@@ -21,6 +22,7 @@ registerTranscribeCommands(program);
|
|
|
21
22
|
registerTaskCommands(program);
|
|
22
23
|
registerFeedbackCommands(program);
|
|
23
24
|
registerSkillCommands(program);
|
|
25
|
+
registerInitCommand(program);
|
|
24
26
|
program.parseAsync(process.argv).catch((err) => {
|
|
25
27
|
console.error(err);
|
|
26
28
|
process.exit(1);
|
package/dist/postinstall.js
CHANGED
|
@@ -14,4 +14,9 @@ if (!existsSync(src)) {
|
|
|
14
14
|
}
|
|
15
15
|
mkdirSync(dest, { recursive: true });
|
|
16
16
|
cpSync(src, dest, { recursive: true, force: true });
|
|
17
|
-
console.log(
|
|
17
|
+
console.log(`\n9000AI CLI installed successfully!\n`);
|
|
18
|
+
console.log(`Skills installed: ${dest}\n`);
|
|
19
|
+
console.log(`Get started:`);
|
|
20
|
+
console.log(` 1. 9000ai config set --base-url <server-url> --api-key <key>`);
|
|
21
|
+
console.log(` 2. 9000ai init --path <your-project-dir>`);
|
|
22
|
+
console.log(` 3. cd <your-project-dir> → open Claude Code → start working\n`);
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: 9000AI-hub
|
|
3
|
-
description: 9000AI
|
|
2
|
+
name: 9000AI-hub
|
|
3
|
+
description: 9000AI 中台总入口。识别用户意图,路由到对应模块。首次使用时引导初始化。
|
|
4
4
|
triggers:
|
|
5
5
|
- /9000AI
|
|
6
6
|
- /9000AI中台
|
|
7
|
-
-
|
|
7
|
+
- /content-init
|
|
8
|
+
- /初始化工作空间
|
|
9
|
+
- 初始化
|
|
10
|
+
- 第一次用
|
|
8
11
|
role: router
|
|
9
12
|
scope: routing
|
|
10
13
|
output-format: routing-only
|
|
@@ -12,227 +15,109 @@ output-format: routing-only
|
|
|
12
15
|
|
|
13
16
|
# 9000AI 中台
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
9000AI 是一个内容创作者中台。它把抖音热榜、关键词搜索、主页监控、视频转写这些能力整合在一起,通过 `9000ai` CLI 统一调用,后端异步执行。
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
你的职责不是直接执行业务,而是:
|
|
19
|
-
1. 识别用户当前属于哪个模块
|
|
20
|
-
2. 告诉系统应该进入哪个 skill
|
|
21
|
-
3. 在必要时提示下一步怎么继续
|
|
20
|
+
你作为 AI 助手,通过这个中台帮用户完成选题发现、竞品分析、素材采集等内容创作工作流。中台只是工具层——你负责理解用户意图、编排调用、输出结果。
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
## 调用原则
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
1. **无依赖的操作必须并行。** 热榜+搜索、多个关键词——没有数据依赖就同时发出
|
|
25
|
+
2. **异步任务用 `--wait`。** 搜索和转写加 `--wait`,一条命令拿到最终结果
|
|
26
|
+
3. **有依赖的操作串行。** 上一步输出是下一步输入时,必须等
|
|
27
|
+
4. **用 `--fields` 只取需要的字段。** 永远不要全量读取
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
```bash
|
|
30
|
+
# 单任务
|
|
31
|
+
9000ai search keyword "美食探店" --wait --fields desc,likes,author_name
|
|
32
|
+
|
|
33
|
+
# 多数据源并行
|
|
34
|
+
并行 {
|
|
35
|
+
9000ai search hot
|
|
36
|
+
9000ai search keyword "关键词A" --wait --fields desc,likes,author_name
|
|
37
|
+
9000ai search keyword "关键词B" --wait --fields desc,likes,author_name
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# 有依赖,串行
|
|
41
|
+
9000ai search keyword "xxx" --wait --fields desc,video_url,likes
|
|
42
|
+
→ 拿 video_url → 9000ai transcribe submit --json-file ... --wait
|
|
43
|
+
```
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
## 模块目录
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
### 抖音选题发现
|
|
48
|
+
- 命令: `9000ai search`
|
|
49
|
+
- 场景: 热榜、关键词搜索、候选视频发现
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
| `--api-key` | 你的身份钥匙 | 在中台后端的 `.env` 文件里配置,找管理员要 |
|
|
51
|
+
### 抖音主页监控
|
|
52
|
+
- 命令: `9000ai monitor`
|
|
53
|
+
- 场景: 直接获取主页视频(`monitor fetch`)、加入监控队列(`monitor submit`)
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
> 如果你就在跑服务的这台电脑上用,填 `http://127.0.0.1:8025` 就行。
|
|
55
|
+
### 视频转写
|
|
56
|
+
- 命令: `9000ai transcribe`
|
|
57
|
+
- 场景: 批量视频转文字、查看转写结果
|
|
53
58
|
|
|
54
|
-
###
|
|
59
|
+
### 反馈提交
|
|
60
|
+
- 命令: `9000ai feedback`
|
|
61
|
+
- 场景: 提交反馈、查看记录。接口报错或用户不满时主动提议反馈
|
|
55
62
|
|
|
56
|
-
|
|
63
|
+
## 路由规则
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
| 用户意图 | 路由 |
|
|
66
|
+
|---------|------|
|
|
67
|
+
| 初始化 / 第一次用 / 搭建工作空间 | 执行下方"首次使用流程" |
|
|
68
|
+
| 查热榜 / 搜抖音 / 找选题 | `9000ai search` |
|
|
69
|
+
| 看某人主页 / 监控抖音号 | `9000ai monitor` |
|
|
70
|
+
| 视频转文字 / 转写 | `9000ai transcribe` |
|
|
71
|
+
| 反馈 / 建议 / 报 bug | `9000ai feedback` |
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
## 首次使用流程
|
|
63
74
|
|
|
64
|
-
###
|
|
75
|
+
### 1. 确认中台连接
|
|
65
76
|
|
|
66
77
|
```bash
|
|
67
78
|
9000ai config show
|
|
68
79
|
```
|
|
69
80
|
|
|
70
|
-
|
|
81
|
+
未配置 → 问用户要地址和 key:
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### 配置存在哪?
|
|
75
|
-
|
|
76
|
-
保存在 `9000AI-hub-9000AI/local/config.json`(这个文件不入 git,不用担心泄露)。
|
|
77
|
-
各 skill 不需要单独配置,都从这里读。
|
|
78
|
-
|
|
79
|
-
## PowerShell 编码准则
|
|
80
|
-
|
|
81
|
-
在 Windows PowerShell 里直接跑脚本时,默认编码容易把中文参数和输出搞乱码。
|
|
82
|
-
进入任何 9000AI skill 之前,先按 UTF-8 设好控制台:
|
|
83
|
-
|
|
84
|
-
```powershell
|
|
85
|
-
$env:PYTHONIOENCODING = 'utf-8'
|
|
86
|
-
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
87
|
-
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
83
|
+
```bash
|
|
84
|
+
9000ai config set --base-url <地址> --api-key <key>
|
|
88
85
|
```
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
- 中文关键词被打坏
|
|
92
|
-
- 返回 JSON 中文发花
|
|
93
|
-
- 复制到后续命令里的参数失真
|
|
94
|
-
|
|
95
|
-
## 模块目录
|
|
96
|
-
|
|
97
|
-
### 0. 内容工作空间初始化
|
|
98
|
-
|
|
99
|
-
- skill: `content-init`(位于 `9000AI-hub-9000AI/init/SKILL.md`)
|
|
100
|
-
- 主调用命令: `/content-init`
|
|
101
|
-
- 适用场景:
|
|
102
|
-
- 首次使用 9000AI 内容中台
|
|
103
|
-
- 初始化项目工作空间目录结构
|
|
104
|
-
- 创建 agent 人格、主张库模板、用户画像模板
|
|
105
|
-
- 查看使用指南
|
|
106
|
-
|
|
107
|
-
### 1. 抖音主页监控
|
|
108
|
-
|
|
109
|
-
- skill: `douyin-monitor-9000AI`
|
|
110
|
-
- 主调用命令: `/douyin-monitor-9000AI`
|
|
111
|
-
- 适用场景:
|
|
112
|
-
- 添加监控账号
|
|
113
|
-
- 发起主页监控
|
|
114
|
-
- 查看监控运行状态
|
|
115
|
-
- 回看监控结果
|
|
116
|
-
|
|
117
|
-
### 2. 抖音选题发现
|
|
118
|
-
|
|
119
|
-
- skill: `douyin-topic-discovery-9000AI`
|
|
120
|
-
- 主调用命令: `/douyin-topic-discovery-9000AI`
|
|
121
|
-
- 适用场景:
|
|
122
|
-
- 抖音热榜
|
|
123
|
-
- 抖音搜索流
|
|
124
|
-
- 候选视频发现
|
|
125
|
-
- 搜索结果落本地 output 文件
|
|
126
|
-
|
|
127
|
-
### 3. 视频转写
|
|
128
|
-
|
|
129
|
-
- skill: `video-transcription`
|
|
130
|
-
- 主调用命令: `/video-transcription`
|
|
131
|
-
- 适用场景:
|
|
132
|
-
- 批量视频转文字
|
|
133
|
-
- 查看转写任务状态
|
|
134
|
-
- 回看转写结果
|
|
135
|
-
|
|
136
|
-
### 4. 反馈提交
|
|
137
|
-
|
|
138
|
-
- skill: `feedback-9000AI`
|
|
139
|
-
- 主调用命令: `/feedback-9000AI`
|
|
140
|
-
- 适用场景:
|
|
141
|
-
- 提交使用反馈(工作流串联建议、Bug、功能请求等)
|
|
142
|
-
- 查看自己的反馈记录
|
|
143
|
-
|
|
144
|
-
## 路由规则
|
|
145
|
-
|
|
146
|
-
- 用户要”初始化 / 开始使用 / 搭建工作空间 / 第一次用 / 初始化内容工作空间”
|
|
147
|
-
- 路由到 `content-init`(`9000AI-hub-9000AI/init/SKILL.md`)
|
|
148
|
-
|
|
149
|
-
- 用户要”监控抖音号 / 看主页更新 / 查主页监控结果”
|
|
150
|
-
- 路由到 `douyin-monitor-9000AI`
|
|
151
|
-
|
|
152
|
-
- 用户要“查热榜 / 搜抖音内容 / 找选题 / 找候选视频”
|
|
153
|
-
- 路由到 `douyin-topic-discovery-9000AI`
|
|
154
|
-
|
|
155
|
-
- 用户要”把视频转成文字 / 提交转写 / 看转写结果”
|
|
156
|
-
- 路由到 `video-transcription`
|
|
157
|
-
|
|
158
|
-
- 用户要”提交反馈 / 有建议 / 报 bug / 反馈意见”
|
|
159
|
-
- 路由到 `feedback-9000AI`
|
|
160
|
-
|
|
161
|
-
## 串联规则
|
|
162
|
-
|
|
163
|
-
只有在用户明确要求多步串联时,才跨模块:
|
|
164
|
-
|
|
165
|
-
- 先搜索,再转写
|
|
166
|
-
- 先监控,再分析结果
|
|
167
|
-
|
|
168
|
-
如果需要串联:
|
|
169
|
-
1. 先进入第一个模块完成提交
|
|
170
|
-
2. 拿到任务结果或结果引用
|
|
171
|
-
3. 再进入下一个模块继续执行
|
|
172
|
-
|
|
173
|
-
## 统一使用规范
|
|
174
|
-
|
|
175
|
-
1. 不把大结果直接塞进上下文
|
|
176
|
-
2. 优先使用子 skill 的 `output` 文件、`task_id`、`batch_id`、`row_no` 继续操作
|
|
177
|
-
3. 不在中台里展开子 skill 的全部脚本命令
|
|
178
|
-
4. 如果用户问”该用哪个模块”,中台负责判断
|
|
179
|
-
5. 如果用户已经明确说了模块名,直接进入对应 skill
|
|
180
|
-
6. 共享执行底座以 `9000AI-hub-9000AI/shared/runner.py` 为准
|
|
181
|
-
7. 统一 runner 规范以 `9000AI-hub-9000AI/references/runner-spec-v1.md` 为准
|
|
182
|
-
8. 以下情况主动提议进入 `feedback-9000AI` 提交反馈,不要等用户说:
|
|
183
|
-
- 任何 skill 出现接口报错、任务失败、结果异常
|
|
184
|
-
- 发现某个工作流可以改进或串联自动化
|
|
185
|
-
- 用户表达了不满、困惑或明确的改进意见
|
|
186
|
-
|
|
187
|
-
## 性能准则(必须遵守)
|
|
188
|
-
|
|
189
|
-
### 数据精简
|
|
190
|
-
|
|
191
|
-
所有查询命令都支持 `--fields` 和 `--compact` 参数。**默认必须用 `--fields` 只取需要的字段,禁止全量读取。**
|
|
87
|
+
### 2. 初始化工作空间
|
|
192
88
|
|
|
193
89
|
```bash
|
|
194
|
-
|
|
195
|
-
9000ai task results --task-id <id>
|
|
196
|
-
|
|
197
|
-
# 正确 — 只取需要的 4 个字段
|
|
198
|
-
9000ai task results --task-id <id> --fields desc,video_url,likes,author_name
|
|
199
|
-
|
|
200
|
-
# 更紧凑 — 每条一行,适合批量处理
|
|
201
|
-
9000ai task results --task-id <id> --fields desc,video_url,likes --compact
|
|
90
|
+
9000ai init --path <项目目录>
|
|
202
91
|
```
|
|
203
92
|
|
|
204
|
-
|
|
205
|
-
- 搜索结果概览:`--fields desc,author_name,likes,duration`
|
|
206
|
-
- 提取转写链接:`--fields video_url,download_url,play_url,desc`
|
|
207
|
-
- 监控结果摘要:`--fields desc,author_name,likes,comments,create_time`
|
|
93
|
+
### 3. 载入角色
|
|
208
94
|
|
|
209
|
-
|
|
95
|
+
初始化完成后,读取项目目录下的 `agents/content-agent.md` 作为你的角色设定。这个文件定义了你的工作方式、可用能力、inbox 处理规则等。
|
|
210
96
|
|
|
211
|
-
|
|
97
|
+
**如果用户还没初始化**,你只有中台调度能力。**初始化后**,你才是完整的内容创作执行助手。
|
|
212
98
|
|
|
213
|
-
|
|
214
|
-
- 写 inbox 文件 + 提交转写任务
|
|
215
|
-
- 多个关键词的搜索结果查询
|
|
216
|
-
- 查多个 task_id 的状态
|
|
99
|
+
### 4. 了解用户
|
|
217
100
|
|
|
218
|
-
|
|
219
|
-
- 搜索 → 等结果 → 筛选(有依赖关系)
|
|
220
|
-
- 创建监控对象 → 提交监控任务(需要 creator_id)
|
|
101
|
+
主动发起对话:
|
|
221
102
|
|
|
222
|
-
|
|
103
|
+
> 工作空间搭好了。我想先了解一下你,这样后面做选题会更精准。
|
|
104
|
+
> 我们可以聊几个问题,也可以你把资料丢进 inbox/(支持 .txt),我来提取。
|
|
223
105
|
|
|
224
|
-
|
|
106
|
+
**对话模式** — 依次引导:
|
|
107
|
+
1. **identity.md** — 你是谁?做什么业务?目标受众?
|
|
108
|
+
2. **voice.md** — 内容风格?参考博主?什么话不说?
|
|
109
|
+
3. **topics.md** — 主攻方向?不碰的领域?
|
|
110
|
+
4. **product.md** — 产品/服务?定价?核心卖点?
|
|
225
111
|
|
|
226
|
-
|
|
112
|
+
**inbox 模式** — 读取 inbox/ 的 .txt,提取信息填充 profile/。
|
|
227
113
|
|
|
228
|
-
|
|
114
|
+
完成后:
|
|
229
115
|
|
|
230
|
-
|
|
116
|
+
> 画像建好了。现在可以开始——比如"帮我做选题调研"、"看看最近热榜"。
|
|
231
117
|
|
|
232
|
-
|
|
118
|
+
## 参考文档
|
|
233
119
|
|
|
234
|
-
|
|
235
|
-
- 告诉系统该去哪个 skill
|
|
236
|
-
- 给出主调用命令
|
|
120
|
+
需要更详细的使用规范时查阅:
|
|
237
121
|
|
|
238
|
-
|
|
122
|
+
- `references/usage-guidelines.md` — 性能准则、字段组合、串联规则、PowerShell 编码
|
|
123
|
+
- `references/env.example` — 环境变量示例
|