@9000ai/cli 0.6.4 → 0.7.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/commands/content.d.ts +2 -0
- package/dist/commands/content.js +66 -0
- package/dist/commands/feedback.js +4 -1
- package/dist/commands/init.js +14 -6
- package/dist/commands/monitor.js +7 -4
- package/dist/commands/search.js +2 -2
- package/dist/commands/task.js +5 -3
- package/dist/index.js +2 -0
- package/package.json +1 -1
- package/skills/9000AI-hub/SKILL.md +14 -1
- package/skills/9000AI-hub/init/templates/agents/content-agent.md +163 -35
- package/skills/9000AI-hub/init/templates/assets/README.md +56 -0
- package/skills/9000AI-hub/init/templates/assets/catalog.json +1 -0
- package/skills/9000AI-hub/init/templates/benchmarks/README.md +56 -11
- package/skills/9000AI-hub/init/templates/benchmarks/index.json +1 -0
- package/skills/9000AI-hub/init/templates/claims/README.md +5 -2
- package/skills/9000AI-hub/init/templates/guide.md +55 -27
- package/skills/9000AI-hub/init/templates/workflows/README.md +26 -0
- package/skills/content-processing/SKILL.md +119 -0
- package/skills/content-processing/references/endpoints.md +72 -0
- package/skills/douyin-topic-discovery/SKILL.md +29 -125
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { request, printJson, pollUntilDone } from "../client.js";
|
|
2
|
+
import { loadJsonFile } from "../utils/format.js";
|
|
3
|
+
export function registerContentCommands(parent) {
|
|
4
|
+
const cmd = parent.command("content").description("LLM content processing (analyze, rewrite, summarize)");
|
|
5
|
+
cmd
|
|
6
|
+
.command("analyze")
|
|
7
|
+
.description("Submit batch LLM processing tasks")
|
|
8
|
+
.option("--preset <name>", "Built-in preset: analyze, summarize, rewrite")
|
|
9
|
+
.option("--system-prompt <prompt>", "Custom system prompt (alternative to --preset)")
|
|
10
|
+
.option("--texts-file <path>", "JSON file: [{\"id\": \"...\", \"text\": \"...\"}]")
|
|
11
|
+
.option("--model <model>", "Override LLM model")
|
|
12
|
+
.option("--group-label <label>", "Batch group label")
|
|
13
|
+
.option("--webhook <url>", "Webhook for completion callback")
|
|
14
|
+
.option("--wait", "Poll until all tasks complete")
|
|
15
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
16
|
+
.option("--compact", "One JSON object per line")
|
|
17
|
+
.action(async (opts) => {
|
|
18
|
+
if (!opts.textsFile) {
|
|
19
|
+
console.error("Error: --texts-file is required");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (!opts.preset && !opts.systemPrompt) {
|
|
23
|
+
console.error("Error: must provide --preset or --system-prompt");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const textsData = loadJsonFile(opts.textsFile);
|
|
27
|
+
const texts = Array.isArray(textsData) ? textsData : textsData.texts;
|
|
28
|
+
const payload = { texts };
|
|
29
|
+
if (opts.preset)
|
|
30
|
+
payload.preset = opts.preset;
|
|
31
|
+
if (opts.systemPrompt)
|
|
32
|
+
payload.system_prompt = opts.systemPrompt;
|
|
33
|
+
if (opts.model)
|
|
34
|
+
payload.model = opts.model;
|
|
35
|
+
if (opts.groupLabel)
|
|
36
|
+
payload.group_label = opts.groupLabel;
|
|
37
|
+
if (opts.webhook)
|
|
38
|
+
payload.webhook = opts.webhook;
|
|
39
|
+
const data = await request({
|
|
40
|
+
method: "POST",
|
|
41
|
+
path: "/api/v1/content/batch-analyze",
|
|
42
|
+
payload,
|
|
43
|
+
});
|
|
44
|
+
const resp = data;
|
|
45
|
+
const inner = resp.data;
|
|
46
|
+
const batchId = inner?.batch_id;
|
|
47
|
+
if (!opts.wait || !batchId) {
|
|
48
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.error(`Batch submitted → ${batchId}, waiting...`);
|
|
52
|
+
await pollUntilDone(`/api/v1/tasks/batch/${batchId}`);
|
|
53
|
+
const resultData = await request({
|
|
54
|
+
method: "GET",
|
|
55
|
+
path: `/api/v1/tasks/batch/${batchId}`,
|
|
56
|
+
});
|
|
57
|
+
printJson(resultData, { fields: opts.fields, compact: opts.compact });
|
|
58
|
+
});
|
|
59
|
+
cmd
|
|
60
|
+
.command("presets")
|
|
61
|
+
.description("List available LLM presets")
|
|
62
|
+
.action(async () => {
|
|
63
|
+
const data = await request({ method: "GET", path: "/api/v1/content/presets" });
|
|
64
|
+
printJson(data);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -38,11 +38,14 @@ export function registerFeedbackCommands(parent) {
|
|
|
38
38
|
.description("List my submitted feedback")
|
|
39
39
|
.option("--page <n>", "Page number", "1")
|
|
40
40
|
.option("--page-size <n>", "Items per page", "20")
|
|
41
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
42
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
43
|
+
.option("--compact", "One JSON object per line")
|
|
41
44
|
.action(async (opts) => {
|
|
42
45
|
const data = await request({
|
|
43
46
|
method: "GET",
|
|
44
47
|
path: `/api/v1/feedback?page=${opts.page}&page_size=${opts.pageSize}`,
|
|
45
48
|
});
|
|
46
|
-
printJson(data);
|
|
49
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
47
50
|
});
|
|
48
51
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, copyFileSync, readFileSync, readdirSync, statSyn
|
|
|
2
2
|
import { join, relative } from "path";
|
|
3
3
|
import { resolveSkillsPath } from "../config.js";
|
|
4
4
|
/** User data directories — never overwrite even with --force */
|
|
5
|
-
const USER_DATA_DIRS = ["profile", "claims", "inbox", "projects", "benchmarks"];
|
|
5
|
+
const USER_DATA_DIRS = ["profile", "claims", "inbox", "projects", "benchmarks", "assets", "workflows"];
|
|
6
6
|
function filesEqual(a, b) {
|
|
7
7
|
try {
|
|
8
8
|
return readFileSync(a).equals(readFileSync(b));
|
|
@@ -84,6 +84,8 @@ export function registerInitCommand(program) {
|
|
|
84
84
|
}
|
|
85
85
|
const { created, updated, skipped, outdated } = copyTemplates(templatesDir, dest, dest, !!opts.force);
|
|
86
86
|
// Output summary
|
|
87
|
+
const isFirstTime = created.length > 0 && updated.length === 0 && outdated.length === 0;
|
|
88
|
+
const isUpdate = updated.length > 0;
|
|
87
89
|
if (created.length > 0) {
|
|
88
90
|
console.log(`Created ${created.length} files:`);
|
|
89
91
|
created.forEach((f) => console.log(` + ${f}`));
|
|
@@ -102,11 +104,17 @@ export function registerInitCommand(program) {
|
|
|
102
104
|
return;
|
|
103
105
|
}
|
|
104
106
|
console.log(`\nWorkspace ready: ${dest}`);
|
|
105
|
-
if (
|
|
106
|
-
console.log(`\
|
|
107
|
-
console.log(`
|
|
108
|
-
console.log(
|
|
109
|
-
console.log(`
|
|
107
|
+
if (isFirstTime) {
|
|
108
|
+
console.log(`\nThis is a content creator workspace powered by 9000AI.`);
|
|
109
|
+
console.log(`Capabilities: trending topics, keyword search, creator monitoring, video transcription.`);
|
|
110
|
+
console.log(`\nRead guide.md for full walkthrough, or get started now:`);
|
|
111
|
+
console.log(` 1. Load 9000AI hub: 9000ai skill load 9000AI-hub`);
|
|
112
|
+
console.log(` 2. Or tell your AI: /9000AI`);
|
|
113
|
+
console.log(` The AI will guide you through profile setup and available capabilities.`);
|
|
114
|
+
}
|
|
115
|
+
else if (isUpdate) {
|
|
116
|
+
console.log(`\nWhat's new: 9000ai skill load 9000AI-hub to see latest capabilities.`);
|
|
117
|
+
console.log(`Your data (profile/, claims/, assets/, workflows/, inbox/, projects/) was not modified.`);
|
|
110
118
|
}
|
|
111
119
|
});
|
|
112
120
|
}
|
package/dist/commands/monitor.js
CHANGED
|
@@ -5,11 +5,12 @@ export function registerMonitorCommands(parent) {
|
|
|
5
5
|
cmd
|
|
6
6
|
.command("list-creators")
|
|
7
7
|
.description("List all monitoring targets")
|
|
8
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
8
9
|
.option("--fields <list>", "Comma-separated fields to extract")
|
|
9
10
|
.option("--compact", "One JSON object per line")
|
|
10
11
|
.action(async (opts) => {
|
|
11
12
|
const data = await request({ method: "GET", path: "/api/v1/douyin/monitor/creators" });
|
|
12
|
-
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
13
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
13
14
|
});
|
|
14
15
|
cmd
|
|
15
16
|
.command("create-creator")
|
|
@@ -85,6 +86,7 @@ export function registerMonitorCommands(parent) {
|
|
|
85
86
|
.option("--page <n>", "Page number", "1")
|
|
86
87
|
.option("--page-size <n>", "Items per page", "20")
|
|
87
88
|
.option("--status <status>", "Filter by status")
|
|
89
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
88
90
|
.option("--fields <list>", "Comma-separated fields to extract")
|
|
89
91
|
.option("--compact", "One JSON object per line")
|
|
90
92
|
.action(async (opts) => {
|
|
@@ -92,17 +94,18 @@ export function registerMonitorCommands(parent) {
|
|
|
92
94
|
if (opts.status)
|
|
93
95
|
params.set("status", opts.status);
|
|
94
96
|
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/runs?${params}` });
|
|
95
|
-
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
97
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
96
98
|
});
|
|
97
99
|
cmd
|
|
98
100
|
.command("run-detail")
|
|
99
101
|
.description("View single run details")
|
|
100
102
|
.requiredOption("--run-id <id>", "Run ID")
|
|
103
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
101
104
|
.option("--fields <list>", "Comma-separated fields to extract")
|
|
102
105
|
.option("--compact", "One JSON object per line")
|
|
103
106
|
.action(async (opts) => {
|
|
104
107
|
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/runs/${opts.runId}` });
|
|
105
|
-
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
108
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
106
109
|
});
|
|
107
110
|
cmd
|
|
108
111
|
.command("fetch")
|
|
@@ -110,7 +113,7 @@ export function registerMonitorCommands(parent) {
|
|
|
110
113
|
.requiredOption("--sec-user <id>", "User sec_user_id or homepage URL")
|
|
111
114
|
.option("--count <n>", "Number of videos to fetch", "20")
|
|
112
115
|
.option("--sort <n>", "Sort: 0=newest 1=hottest", "0")
|
|
113
|
-
.option("--format <fmt>", "Output format: csv|json", "
|
|
116
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
114
117
|
.option("--fields <list>", "Comma-separated fields to extract")
|
|
115
118
|
.option("--compact", "One JSON object per line")
|
|
116
119
|
.action(async (opts) => {
|
package/dist/commands/search.js
CHANGED
|
@@ -68,7 +68,7 @@ export function registerSearchCommands(parent) {
|
|
|
68
68
|
.option("--min-likes <n>", "Minimum likes", "0")
|
|
69
69
|
.option("--min-comments <n>", "Minimum comments", "0")
|
|
70
70
|
.option("--wait", "Poll until batch completes then print results")
|
|
71
|
-
.option("--format <fmt>", "Output format: csv|json", "
|
|
71
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
72
72
|
.option("--fields <list>", "Comma-separated fields to extract (used with --wait)")
|
|
73
73
|
.option("--compact", "One JSON object per line (used with --wait)")
|
|
74
74
|
.action(async (keywords, opts) => {
|
|
@@ -121,7 +121,7 @@ export function registerSearchCommands(parent) {
|
|
|
121
121
|
.command("batch-result")
|
|
122
122
|
.description("Query async search batch results")
|
|
123
123
|
.requiredOption("--batch-id <id>", "Batch ID")
|
|
124
|
-
.option("--format <fmt>", "Output format: csv|json", "
|
|
124
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
125
125
|
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,author_name,likes,video_url)")
|
|
126
126
|
.option("--compact", "One JSON object per line, no indentation")
|
|
127
127
|
.action(async (opts) => {
|
package/dist/commands/task.js
CHANGED
|
@@ -6,6 +6,7 @@ export function registerTaskCommands(parent) {
|
|
|
6
6
|
.description("Check task or batch status")
|
|
7
7
|
.option("--task-id <id>", "Task ID")
|
|
8
8
|
.option("--batch-id <id>", "Batch ID (shows batch progress + task list)")
|
|
9
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
9
10
|
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,video_url,likes)")
|
|
10
11
|
.option("--compact", "One JSON object per line, no indentation")
|
|
11
12
|
.action(async (opts) => {
|
|
@@ -15,20 +16,21 @@ export function registerTaskCommands(parent) {
|
|
|
15
16
|
}
|
|
16
17
|
if (opts.batchId) {
|
|
17
18
|
const data = await request({ method: "GET", path: `/api/v1/tasks/batch/${opts.batchId}` });
|
|
18
|
-
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
19
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
22
|
const data = await request({ method: "GET", path: `/api/v1/tasks/${opts.taskId}` });
|
|
22
|
-
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
23
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
23
24
|
});
|
|
24
25
|
cmd
|
|
25
26
|
.command("results")
|
|
26
27
|
.description("Get task results")
|
|
27
28
|
.requiredOption("--task-id <id>", "Task ID")
|
|
29
|
+
.option("--format <fmt>", "Output format: csv|json", "csv")
|
|
28
30
|
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,video_url,likes)")
|
|
29
31
|
.option("--compact", "One JSON object per line, no indentation")
|
|
30
32
|
.action(async (opts) => {
|
|
31
33
|
const data = await request({ method: "GET", path: `/api/v1/tasks/${opts.taskId}/results` });
|
|
32
|
-
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
34
|
+
printJson(data, { fields: opts.fields, compact: opts.compact, format: opts.format });
|
|
33
35
|
});
|
|
34
36
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerFeedbackCommands } from "./commands/feedback.js";
|
|
|
10
10
|
import { registerSkillCommands } from "./commands/skill.js";
|
|
11
11
|
import { registerInitCommand } from "./commands/init.js";
|
|
12
12
|
import { registerUpdateCommand } from "./commands/update.js";
|
|
13
|
+
import { registerContentCommands } from "./commands/content.js";
|
|
13
14
|
const program = new Command();
|
|
14
15
|
program
|
|
15
16
|
.name("9000ai")
|
|
@@ -25,6 +26,7 @@ registerFeedbackCommands(program);
|
|
|
25
26
|
registerSkillCommands(program);
|
|
26
27
|
registerInitCommand(program);
|
|
27
28
|
registerUpdateCommand(program);
|
|
29
|
+
registerContentCommands(program);
|
|
28
30
|
program.parseAsync(process.argv).catch((err) => {
|
|
29
31
|
console.error(err);
|
|
30
32
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -57,6 +57,10 @@ output-format: routing-only
|
|
|
57
57
|
- 命令: `9000ai transcribe`
|
|
58
58
|
- 场景: 批量视频转文字、查看转写结果
|
|
59
59
|
|
|
60
|
+
### 内容处理
|
|
61
|
+
- 命令: `9000ai content`
|
|
62
|
+
- 场景: 批量文案拆解、改写、摘要,或自定义 prompt 处理
|
|
63
|
+
|
|
60
64
|
### 反馈提交
|
|
61
65
|
- 命令: `9000ai feedback`
|
|
62
66
|
- 场景: 提交反馈、查看记录。接口报错或用户不满时主动提议反馈
|
|
@@ -69,6 +73,7 @@ output-format: routing-only
|
|
|
69
73
|
| 查热榜 / 搜抖音 / 找选题 | `9000ai search` |
|
|
70
74
|
| 看某人主页 / 监控抖音号 | `9000ai monitor` |
|
|
71
75
|
| 视频转文字 / 转写 | `9000ai transcribe` |
|
|
76
|
+
| 拆解这篇文案 / 分析内容 / 改写 / 摘要 | `9000ai content` |
|
|
72
77
|
| 反馈 / 建议 / 报 bug | `9000ai feedback` |
|
|
73
78
|
|
|
74
79
|
## 首次使用流程
|
|
@@ -114,7 +119,15 @@ output-format: routing-only
|
|
|
114
119
|
|
|
115
120
|
完成后:
|
|
116
121
|
|
|
117
|
-
>
|
|
122
|
+
> 画像建好了。你的工作空间包含:
|
|
123
|
+
> - `profile/` — 你的画像(刚填好的)
|
|
124
|
+
> - `claims/` — 主张库,所有内容围绕主张展开
|
|
125
|
+
> - `assets/` — 内容弹药库,所有素材统一存放,AI 通过三层检索找素材
|
|
126
|
+
> - `workflows/` — 工作流,会在使用中不断进化
|
|
127
|
+
> - `inbox/` — 素材投喂入口
|
|
128
|
+
> - `projects/` — 选题项目
|
|
129
|
+
>
|
|
130
|
+
> 现在可以开始——比如"帮我做选题调研"、"看看最近热榜"、"转写这个视频"。
|
|
118
131
|
|
|
119
132
|
## 更新系统
|
|
120
133
|
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
| 目录 | 用途 | 你的权限 |
|
|
12
12
|
|------|------|---------|
|
|
13
13
|
| profile/ | 了解用户是谁——身份、风格、领域 | 只读(用户维护) |
|
|
14
|
+
| claims/ | 主张库——主张定义、角度、产出记录 | 读写(匹配、回写角度和产出) |
|
|
15
|
+
| assets/ | 内容弹药库——所有素材统一存放 | 读写(入库、更新索引、标记使用) |
|
|
16
|
+
| workflows/ | 工作流——会在使用中不断进化 | 读写(追加已验证规则) |
|
|
14
17
|
| inbox/ | 接收用户投喂的语料——会议记录、视频链接、文章、随笔 | 读取 + 处理后归档 |
|
|
15
|
-
| claims/ | 主张库——核心资产,所有内容围绕主张展开 | 读写(提取、匹配、回写) |
|
|
16
18
|
| projects/ | 选题项目——一个选题一个项目 | 读写 |
|
|
17
19
|
|
|
18
20
|
## 3. 可用工具能力
|
|
@@ -24,11 +26,136 @@
|
|
|
24
26
|
| 对标账号监控 | 监控指定博主的内容更新 | `9000ai monitor` |
|
|
25
27
|
| 视频转文字 | 批量将视频转为文字稿 | `9000ai transcribe` |
|
|
26
28
|
|
|
27
|
-
## 4.
|
|
29
|
+
## 4. 内容弹药库(assets/)
|
|
30
|
+
|
|
31
|
+
弹药库是整个系统的核心。所有素材——对标博主的文案、拆解出来的案例、你自己的金句——全部存在这里。
|
|
32
|
+
|
|
33
|
+
### 4.1 三层检索
|
|
34
|
+
|
|
35
|
+
AI 找素材不能一次性读全量,必须分层:
|
|
36
|
+
|
|
37
|
+
**第一层:仓库总览 `assets/catalog.json`**
|
|
38
|
+
|
|
39
|
+
AI 第一个读的文件。记录有哪些主题、每个主题多少素材、什么标签、关联哪些主张。
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
[
|
|
43
|
+
{
|
|
44
|
+
"topic": "知识付费",
|
|
45
|
+
"count": 326,
|
|
46
|
+
"claim_ids": ["cl001"],
|
|
47
|
+
"top_tags": ["割韭菜", "教育", "焦虑", "课程"],
|
|
48
|
+
"top_ips": ["栋哥", "曲曲大女人"]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"topic": "_自己的",
|
|
52
|
+
"count": 89,
|
|
53
|
+
"claim_ids": ["cl001", "cl002", "cl003"],
|
|
54
|
+
"top_tags": ["金句", "开头", "亲身经历"],
|
|
55
|
+
"top_ips": []
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**第二层:主题索引 `assets/_index/{主题}.jsonl`**
|
|
61
|
+
|
|
62
|
+
每个主题一个索引文件,每行一条素材摘要。AI 按标签过滤,筛出十几条相关的。
|
|
63
|
+
|
|
64
|
+
```jsonl
|
|
65
|
+
{"id":"a001","type":"transcription","title":"栋哥讲知识付费真相","tags":["知识付费","割韭菜","教育"],"source_ip":"栋哥","claim_ids":["cl001"],"hook":"知识付费就是当代最大的骗局","structure":"递进","date":"2026-04-04","reuse_count":2,"file":"知识付费/栋哥_知识付费真相.md"}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**第三层:具体文件 `assets/{主题}/{文件}`**
|
|
69
|
+
|
|
70
|
+
只有被第二层命中了才读。转写原文、拆解报告、自己的片段都在这里。
|
|
71
|
+
|
|
72
|
+
### 4.2 检索规则
|
|
73
|
+
|
|
74
|
+
1. **必须从第一层开始**,不允许跳过总览直接扫索引
|
|
75
|
+
2. 读 catalog.json → 判断该读哪几个主题的索引 → 读对应 _index/*.jsonl → 按标签筛 → 只读命中的具体文件
|
|
76
|
+
3. `_自己的` 主题里的素材优先级高于对标素材——用户自己验证过的表述优先复用
|
|
77
|
+
4. 每次使用某条素材后,更新该条记录的 `reuse_count`,避免过度复用同一个案例
|
|
78
|
+
|
|
79
|
+
### 4.3 素材入库规则
|
|
80
|
+
|
|
81
|
+
素材进来时,AI 做三件事:
|
|
82
|
+
1. 具体文件存到 `assets/{主题}/`,文件名格式:`{作者}_{标题}.md`
|
|
83
|
+
2. 在 `assets/_index/{主题}.jsonl` 追加一行索引记录
|
|
84
|
+
3. 更新 `assets/catalog.json` 里对应主题的 count 和 top_tags
|
|
85
|
+
|
|
86
|
+
新主题第一次出现时,自动建文件夹和索引文件,同时在 catalog.json 追加条目。
|
|
87
|
+
|
|
88
|
+
还没想好放哪个主题的素材,先存到 `_待分类/`。
|
|
89
|
+
|
|
90
|
+
### 4.4 拆解报告
|
|
91
|
+
|
|
92
|
+
对标文案转写后,如果经过后端拆解,拆解报告存在原文旁边:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
assets/知识付费/
|
|
96
|
+
├── 栋哥_知识付费真相.md ← 转写原文
|
|
97
|
+
└── 栋哥_知识付费真相.analysis.json ← 拆解报告
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
拆解报告包含:开头钩子、核心观点、案例素材、金句、结构模式、转化钩子。AI 做选题时可以直接读拆解报告,不需要重新分析原文。
|
|
101
|
+
|
|
102
|
+
### 4.5 `_自己的` 目录
|
|
103
|
+
|
|
104
|
+
用户自己产出的好内容——录过的文案、想到的金句、亲身经历——存在这里。
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
assets/_自己的/
|
|
108
|
+
├── 片段/
|
|
109
|
+
│ ├── 知识付费就是欺诈_完整文案.md
|
|
110
|
+
│ └── 劳动仲裁_口述观点.md
|
|
111
|
+
└── ...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
这里的素材和对标素材走同样的索引机制,但 `source_ip` 标记为用户自己(从 profile/identity.md 读名字),AI 检索时可以区分"我自己说过的"和"别人说的"。
|
|
115
|
+
|
|
116
|
+
## 5. 工作流进化(workflows/)
|
|
117
|
+
|
|
118
|
+
工作流文件定义内容生产的步骤。关键特性:**工作流不是写死的,在使用中不断固化标准。**
|
|
119
|
+
|
|
120
|
+
### 5.1 进化机制
|
|
121
|
+
|
|
122
|
+
用户每次纠正 AI 的判断,AI 把验证过的标准追加到对应工作流文件的"已验证规则"区域。
|
|
123
|
+
|
|
124
|
+
例子:
|
|
125
|
+
|
|
126
|
+
```markdown
|
|
127
|
+
## 已验证规则
|
|
128
|
+
|
|
129
|
+
- 案例筛选:点赞 ≥ 500,时长 > 60s 的要点赞 ≥ 1000
|
|
130
|
+
- 角度偏好:优先选有冲突感的,不要中性表述
|
|
131
|
+
- 开头风格:直接亮观点,不铺垫
|
|
132
|
+
- 补素材:弹药库里相关案例 < 3 条时自动搜索补充,不用问
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5.2 规则追加原则
|
|
136
|
+
|
|
137
|
+
1. **只记真正的规律**,单次偶发纠正不写入
|
|
138
|
+
2. **记标准不记操作**,写"点赞 ≥ 500"而不是"上次你删了那条 200 赞的"
|
|
139
|
+
3. **记原因**,不只是结论,要写为什么——方便判断边界情况
|
|
140
|
+
4. **用户确认后才追加**,AI 先提议,用户同意了再写入
|
|
141
|
+
|
|
142
|
+
### 5.3 工作流与主张的关系
|
|
143
|
+
|
|
144
|
+
工作流是通用的流程模板,主张是个人化的内容资产。工作流里不硬编码具体主张,而是引用 claims/claims.json。这样同一套工作流可以服务不同主张。
|
|
145
|
+
|
|
146
|
+
## 6. 对标 IP 管理
|
|
147
|
+
|
|
148
|
+
不新建文件,复用 monitor 的 creator 列表。
|
|
149
|
+
|
|
150
|
+
- 用户说"盯住这几个 IP" → 调 `9000ai monitor create-creator` 添加监控
|
|
151
|
+
- 对标 IP 的内容转写后入库时,`source_ip` 字段自动标记博主名
|
|
152
|
+
- 做选题时可以按 `source_ip` 检索某个博主的所有素材
|
|
153
|
+
|
|
154
|
+
## 7. inbox 处理规则
|
|
28
155
|
|
|
29
156
|
当用户往 inbox/ 丢入新文件时:
|
|
30
157
|
|
|
31
|
-
###
|
|
158
|
+
### 7.1 判断文件类型
|
|
32
159
|
|
|
33
160
|
| 类型 | 特征 |
|
|
34
161
|
|------|------|
|
|
@@ -38,20 +165,20 @@
|
|
|
38
165
|
| 观点 / 随笔 | .txt / .md,短篇,非结构化 |
|
|
39
166
|
| 热点事件描述 | 含事件名称、时间、平台来源 |
|
|
40
167
|
|
|
41
|
-
###
|
|
168
|
+
### 7.2 内容分类分发
|
|
42
169
|
|
|
43
170
|
inbox 里的内容不全是案例素材,提取前先判断类型,分发到正确的位置:
|
|
44
171
|
|
|
45
172
|
| 内容类型 | 判断特征 | 去哪 |
|
|
46
173
|
|---------|---------|------|
|
|
47
|
-
|
|
|
174
|
+
| 对标文案、案例、观点、热点事件 | 可以作为内容素材 | `assets/` 对应主题 |
|
|
175
|
+
| 用户自己的好内容 | 第一人称表述、自己的经历 | `assets/_自己的/` |
|
|
48
176
|
| 个人身份、背景、目标受众 | 关于"你是谁"的信息 | 提示用户更新 `profile/identity.md` |
|
|
49
177
|
| 用户自己的表达风格、语气偏好 | 用户描述自己说话方式的信息 | 提示用户更新 `profile/voice.md` |
|
|
50
178
|
| 关注领域、赛道、不碰的方向 | 关于"你做什么内容"的信息 | 提示用户更新 `profile/topics.md` |
|
|
51
179
|
| 产品细节、交付形式、定价、卖点 | 关于"你卖什么"的信息 | 提示用户更新 `profile/product.md` |
|
|
52
|
-
| 技术实现、系统设计、内部流程 | 关于"你怎么做"的内部信息 | 提示用户更新 `profile/product.md` |
|
|
53
180
|
|
|
54
|
-
**原则**:没有内容会被丢弃,只是放对位置。profile
|
|
181
|
+
**原则**:没有内容会被丢弃,只是放对位置。profile 类内容不入弹药库。
|
|
55
182
|
|
|
56
183
|
#### profile/voice.md 写入规则
|
|
57
184
|
|
|
@@ -69,8 +196,8 @@ inbox 里的内容不全是案例素材,提取前先判断类型,分发到
|
|
|
69
196
|
|
|
70
197
|
| 情况 | 处理 |
|
|
71
198
|
|------|------|
|
|
72
|
-
| 提到对标博主(曲曲、良策文化等) | 存
|
|
73
|
-
| 投喂的是别人的文案(用于分析) |
|
|
199
|
+
| 提到对标博主(曲曲、良策文化等) | 存 assets/ 作为素材,不碰 voice.md |
|
|
200
|
+
| 投喂的是别人的文案(用于分析) | 只入弹药库,不碰 voice.md |
|
|
74
201
|
| 会议里描述第三方账号的风格 | 忽略风格部分,不碰 voice.md |
|
|
75
202
|
| 用户说"我想学 XX 的风格" | 见下方特殊情况处理 |
|
|
76
203
|
|
|
@@ -92,47 +219,46 @@ inbox 里的内容不全是案例素材,提取前先判断类型,分发到
|
|
|
92
219
|
> 「你是想把 XX 加为风格模仿对象(写入 voice.md),还是只是选题对标?」
|
|
93
220
|
|
|
94
221
|
- 选风格模仿 → 写入 voice.md 的"参考博主风格"字段,并标注"待学习"
|
|
95
|
-
- 选选题对标 → 存
|
|
222
|
+
- 选选题对标 → 存 assets/,不碰 voice.md
|
|
96
223
|
|
|
97
|
-
###
|
|
224
|
+
### 7.3 并行调度 subagent
|
|
98
225
|
|
|
99
|
-
-
|
|
226
|
+
- **入库 subagent**:将素材分类后存入 assets/ 对应主题,更新索引
|
|
100
227
|
- **转写 subagent**:如果有视频链接 → 调用视频转文字能力
|
|
101
228
|
- **搜索 subagent**:如果需要佐证或补充素材 → 调用关键词搜索能力
|
|
102
229
|
- **profile subagent**:如果语料中有用户个人信息 → 提醒用户更新 profile/
|
|
103
230
|
|
|
104
|
-
###
|
|
231
|
+
### 7.4 处理完成后
|
|
105
232
|
|
|
106
233
|
1. 将原始文件从 inbox/ 移入 inbox/processed/
|
|
107
|
-
2.
|
|
234
|
+
2. 向用户汇报:提取了什么、存入了哪些主题、关联了哪些主张
|
|
108
235
|
|
|
109
|
-
##
|
|
236
|
+
## 8. 匹配流程
|
|
110
237
|
|
|
111
238
|
```
|
|
112
239
|
输入(热点/案例/事件)
|
|
113
240
|
→ AI 提取关键词
|
|
114
|
-
→
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
→ 用户录制完成后 → 记录到 outputs.jsonl
|
|
241
|
+
→ 读 assets/catalog.json 定位相关主题
|
|
242
|
+
→ 读对应 _index/*.jsonl 按标签匹配
|
|
243
|
+
→ 命中已有素材 → 推荐角度 + 拉历史案例
|
|
244
|
+
→ 未命中 → 读 claims/claims.json 中对应主张
|
|
245
|
+
→ 基于主张现推 2-3 个新角度
|
|
246
|
+
→ 用户选定后 → 新角度写回 claims/angles.jsonl
|
|
247
|
+
→ 用户录制完成后 → 记录到 claims/outputs.jsonl
|
|
122
248
|
```
|
|
123
249
|
|
|
124
|
-
##
|
|
250
|
+
## 9. subagent 调度规则
|
|
125
251
|
|
|
126
252
|
- 独立任务应并行执行,不串行等待
|
|
127
253
|
- 每个 subagent 只做一件事,做完汇报
|
|
128
254
|
- subagent 不直接修改 claims/ 数据,汇报给主 agent,由主 agent 统一写入
|
|
129
255
|
- 大量数据不塞上下文,用文件路径传递
|
|
130
256
|
|
|
131
|
-
##
|
|
257
|
+
## 10. 自我进化准则
|
|
132
258
|
|
|
133
259
|
这个系统应该越用越好用、越用越个性化。用户每一次纠正都是信号,不能丢掉。
|
|
134
260
|
|
|
135
|
-
###
|
|
261
|
+
### 10.1 用户纠正时的行为链
|
|
136
262
|
|
|
137
263
|
当用户纠正 agent 的判断、输出或行为时,按以下顺序处理:
|
|
138
264
|
|
|
@@ -149,7 +275,8 @@ inbox 里的内容不全是案例素材,提取前先判断类型,分发到
|
|
|
149
275
|
| 纠正性质 | 处理方式 |
|
|
150
276
|
|---------|---------|
|
|
151
277
|
| 用户画像不准(AI 不了解用户) | 询问用户是否更新对应 profile/ 文件 |
|
|
152
|
-
|
|
|
278
|
+
| 工作流判断错误(可固化的标准) | 提议追加到对应 workflows/ 文件的"已验证规则" |
|
|
279
|
+
| 工作流系统级问题 | 提议提交 feedback |
|
|
153
280
|
| 单次误操作(偶发,不代表规律) | 当场纠正,不更新任何文件 |
|
|
154
281
|
|
|
155
282
|
**第三步:更新 profile(如适用)**
|
|
@@ -164,18 +291,19 @@ inbox 里的内容不全是案例素材,提取前先判断类型,分发到
|
|
|
164
291
|
|
|
165
292
|
如果纠正属于工作流或系统级问题,主动提议:
|
|
166
293
|
|
|
167
|
-
> 「这个问题可能不只影响你,要不要提交一条 feedback
|
|
294
|
+
> 「这个问题可能不只影响你,要不要提交一条 feedback 让团队知道?」
|
|
168
295
|
|
|
169
|
-
###
|
|
296
|
+
### 10.2 主动提议更新的时机
|
|
170
297
|
|
|
171
|
-
|
|
298
|
+
不只是纠正时,以下情况也应主动询问:
|
|
172
299
|
|
|
173
|
-
- inbox 处理中发现了用户的第一人称风格自描述(见
|
|
300
|
+
- inbox 处理中发现了用户的第一人称风格自描述(见 7.2)
|
|
174
301
|
- 用户在对话中透露了新的业务信息、受众变化、领域调整
|
|
175
|
-
-
|
|
302
|
+
- 用户多次对同一类输出表示满意(正向信号,可提炼为工作流规则)
|
|
303
|
+
- 用户反复做同样的手动操作(可以固化为工作流步骤)
|
|
176
304
|
|
|
177
|
-
###
|
|
305
|
+
### 10.3 不该做的事
|
|
178
306
|
|
|
179
307
|
- 不要在用户没确认的情况下自动更新任何 profile 文件
|
|
180
|
-
-
|
|
181
|
-
- 不要每次都问"要不要更新
|
|
308
|
+
- 不要把单次纠正当成规律写入工作流
|
|
309
|
+
- 不要每次都问"要不要更新",只在真正有信息量的时候问
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 内容弹药库
|
|
2
|
+
|
|
3
|
+
所有内容素材统一存放在这里。按主题分文件夹,AI 通过三层检索找素材。
|
|
4
|
+
|
|
5
|
+
## 目录结构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
assets/
|
|
9
|
+
├── catalog.json ← 仓库总览(AI 第一个读的文件)
|
|
10
|
+
├── _index/ ← 每个主题的素材索引
|
|
11
|
+
│ ├── 知识付费.jsonl
|
|
12
|
+
│ ├── AI就业.jsonl
|
|
13
|
+
│ └── _自己的.jsonl
|
|
14
|
+
├── 知识付费/ ← 按主题存素材
|
|
15
|
+
│ ├── 栋哥_知识付费真相.md
|
|
16
|
+
│ └── 栋哥_知识付费真相.analysis.json
|
|
17
|
+
├── _自己的/ ← 你自己的好内容
|
|
18
|
+
│ └── 片段/
|
|
19
|
+
├── _待分类/ ← 还没归类的素材
|
|
20
|
+
└── README.md
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## AI 怎么找素材
|
|
24
|
+
|
|
25
|
+
1. 读 catalog.json → 判断该看哪几个主题
|
|
26
|
+
2. 读 _index/{主题}.jsonl → 按标签筛出相关条目
|
|
27
|
+
3. 只读命中的具体文件
|
|
28
|
+
|
|
29
|
+
**不允许跳过第一层直接扫全部索引。**
|
|
30
|
+
|
|
31
|
+
## 素材入库
|
|
32
|
+
|
|
33
|
+
每次存入新素材:
|
|
34
|
+
1. 文件存到 `assets/{主题}/`,文件名:`{作者}_{标题}.md`
|
|
35
|
+
2. `_index/{主题}.jsonl` 追加一行
|
|
36
|
+
3. `catalog.json` 更新对应主题的 count 和 top_tags
|
|
37
|
+
|
|
38
|
+
## 索引记录格式
|
|
39
|
+
|
|
40
|
+
```jsonl
|
|
41
|
+
{"id":"a001","type":"transcription","title":"标题","tags":["标签1","标签2"],"source_ip":"作者","claim_ids":["cl001"],"hook":"开头第一句","structure":"递进","date":"2026-04-04","reuse_count":0,"file":"主题/作者_标题.md"}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| 字段 | 说明 |
|
|
45
|
+
|------|------|
|
|
46
|
+
| id | 递增编号 a001, a002... |
|
|
47
|
+
| type | transcription(转写) / analysis(拆解) / quote(金句) / experience(亲身经历) / fragment(片段) |
|
|
48
|
+
| title | 素材标题 |
|
|
49
|
+
| tags | 关键词标签(行业+情绪+场景+人物) |
|
|
50
|
+
| source_ip | 来源博主名,自己的内容标记用户名 |
|
|
51
|
+
| claim_ids | 关联的主张 ID 列表,未关联时为 [] |
|
|
52
|
+
| hook | 开头钩子(第一句话),可选 |
|
|
53
|
+
| structure | 结构模式(递进/多角度/对比),可选 |
|
|
54
|
+
| date | 入库日期 |
|
|
55
|
+
| reuse_count | 被引用次数,初始 0 |
|
|
56
|
+
| file | 相对路径 |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|