galaxy-opc-plugin 0.2.0 → 0.2.2
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/index.ts +244 -8
- package/package.json +17 -3
- package/skills/acquisition-management/SKILL.md +83 -0
- package/skills/ai-staff/SKILL.md +89 -0
- package/skills/asset-package/SKILL.md +142 -0
- package/skills/opb-canvas/SKILL.md +88 -0
- package/src/__tests__/e2e/company-lifecycle.test.ts +399 -0
- package/src/__tests__/integration/business-workflows.test.ts +366 -0
- package/src/__tests__/test-utils.ts +316 -0
- package/src/commands/opc-command.ts +422 -0
- package/src/db/index.ts +3 -0
- package/src/db/migrations.test.ts +324 -0
- package/src/db/migrations.ts +131 -0
- package/src/db/schema.ts +211 -0
- package/src/db/sqlite-adapter.ts +5 -0
- package/src/opc/autonomy-rules.ts +132 -0
- package/src/opc/briefing-builder.ts +1331 -0
- package/src/opc/business-workflows.test.ts +535 -0
- package/src/opc/business-workflows.ts +325 -0
- package/src/opc/context-injector.ts +366 -28
- package/src/opc/event-triggers.ts +472 -0
- package/src/opc/intelligence-engine.ts +702 -0
- package/src/opc/milestone-detector.ts +251 -0
- package/src/opc/proactive-service.ts +179 -0
- package/src/opc/reminder-service.ts +4 -43
- package/src/opc/session-task-tracker.ts +60 -0
- package/src/opc/stage-detector.ts +168 -0
- package/src/opc/task-executor.ts +332 -0
- package/src/opc/task-templates.ts +179 -0
- package/src/tools/acquisition-tool.ts +8 -5
- package/src/tools/document-tool.ts +1176 -0
- package/src/tools/finance-tool.test.ts +238 -0
- package/src/tools/finance-tool.ts +922 -14
- package/src/tools/hr-tool.ts +10 -1
- package/src/tools/legal-tool.test.ts +251 -0
- package/src/tools/legal-tool.ts +26 -4
- package/src/tools/lifecycle-tool.test.ts +231 -0
- package/src/tools/media-tool.ts +156 -1
- package/src/tools/monitoring-tool.ts +135 -2
- package/src/tools/opc-tool.test.ts +250 -0
- package/src/tools/opc-tool.ts +251 -28
- package/src/tools/project-tool.test.ts +218 -0
- package/src/tools/schemas.ts +80 -0
- package/src/tools/search-tool.ts +227 -0
- package/src/tools/staff-tool.ts +395 -2
- package/src/web/config-ui.ts +299 -45
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 星环OPC中心 — 公司阶段检测器
|
|
3
|
+
*
|
|
4
|
+
* 根据数据信号自动判断公司所处发展阶段,写入 opc_company_stage 表。
|
|
5
|
+
*
|
|
6
|
+
* 阶段定义:
|
|
7
|
+
* - idea 构想阶段:无画布、无交易、无客户
|
|
8
|
+
* - validation 验证阶段:有画布或客户或项目,但无收入
|
|
9
|
+
* - early_revenue 初始营收:有收入但 < 2 个月
|
|
10
|
+
* - growth 增长阶段:收入 > 1万,有收入月份 ≥ 2
|
|
11
|
+
* - stable 稳定运营:收入 > 10万,有收入月份 ≥ 3,活跃合同 ≥ 2
|
|
12
|
+
* - scaling 规模化:收入 > 50万,有收入月份 ≥ 6,员工 ≥ 3
|
|
13
|
+
* - exit 退出阶段:公司状态为 acquired/packaged
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { OpcDatabase } from "../db/index.js";
|
|
17
|
+
|
|
18
|
+
export type CompanyStage =
|
|
19
|
+
| "idea"
|
|
20
|
+
| "validation"
|
|
21
|
+
| "early_revenue"
|
|
22
|
+
| "growth"
|
|
23
|
+
| "stable"
|
|
24
|
+
| "scaling"
|
|
25
|
+
| "exit";
|
|
26
|
+
|
|
27
|
+
export interface StageResult {
|
|
28
|
+
stage: CompanyStage;
|
|
29
|
+
stageLabel: string;
|
|
30
|
+
confidence: number;
|
|
31
|
+
factors: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const STAGE_LABELS: Record<CompanyStage, string> = {
|
|
35
|
+
idea: "构想阶段",
|
|
36
|
+
validation: "验证阶段",
|
|
37
|
+
early_revenue: "初始营收",
|
|
38
|
+
growth: "增长阶段",
|
|
39
|
+
stable: "稳定运营",
|
|
40
|
+
scaling: "规模化",
|
|
41
|
+
exit: "退出阶段",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type CountRow = { cnt: number };
|
|
45
|
+
type SumRow = { total: number };
|
|
46
|
+
type MonthRow = { month_count: number };
|
|
47
|
+
|
|
48
|
+
/** 检测单个公司的发展阶段 */
|
|
49
|
+
export function detectCompanyStage(db: OpcDatabase, companyId: string): StageResult {
|
|
50
|
+
const factors: Record<string, unknown> = {};
|
|
51
|
+
|
|
52
|
+
// 基础信号采集
|
|
53
|
+
const company = db.getCompany(companyId);
|
|
54
|
+
if (!company) {
|
|
55
|
+
return { stage: "idea", stageLabel: STAGE_LABELS.idea, confidence: 0, factors: {} };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 退出阶段判定(优先级最高)
|
|
59
|
+
const companyStatus = company.status;
|
|
60
|
+
if (companyStatus === "acquired" || companyStatus === "packaged") {
|
|
61
|
+
factors.status = companyStatus;
|
|
62
|
+
return { stage: "exit", stageLabel: STAGE_LABELS.exit, confidence: 1.0, factors };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 数据信号
|
|
66
|
+
const totalIncome = (db.queryOne(
|
|
67
|
+
"SELECT COALESCE(SUM(amount), 0) as total FROM opc_transactions WHERE company_id = ? AND type = 'income'",
|
|
68
|
+
companyId,
|
|
69
|
+
) as SumRow).total;
|
|
70
|
+
|
|
71
|
+
const revenueMonths = (db.queryOne(
|
|
72
|
+
`SELECT COUNT(DISTINCT strftime('%Y-%m', transaction_date)) as month_count
|
|
73
|
+
FROM opc_transactions WHERE company_id = ? AND type = 'income' AND amount > 0`,
|
|
74
|
+
companyId,
|
|
75
|
+
) as MonthRow).month_count;
|
|
76
|
+
|
|
77
|
+
const hasCanvas = (db.queryOne(
|
|
78
|
+
"SELECT COUNT(*) as cnt FROM opc_opb_canvas WHERE company_id = ?",
|
|
79
|
+
companyId,
|
|
80
|
+
) as CountRow).cnt > 0;
|
|
81
|
+
|
|
82
|
+
const contactCount = (db.queryOne(
|
|
83
|
+
"SELECT COUNT(*) as cnt FROM opc_contacts WHERE company_id = ?",
|
|
84
|
+
companyId,
|
|
85
|
+
) as CountRow).cnt;
|
|
86
|
+
|
|
87
|
+
const projectCount = (db.queryOne(
|
|
88
|
+
"SELECT COUNT(*) as cnt FROM opc_projects WHERE company_id = ?",
|
|
89
|
+
companyId,
|
|
90
|
+
) as CountRow).cnt;
|
|
91
|
+
|
|
92
|
+
const activeContracts = (db.queryOne(
|
|
93
|
+
"SELECT COUNT(*) as cnt FROM opc_contracts WHERE company_id = ? AND status = 'active'",
|
|
94
|
+
companyId,
|
|
95
|
+
) as CountRow).cnt;
|
|
96
|
+
|
|
97
|
+
const hrCount = (db.queryOne(
|
|
98
|
+
"SELECT COUNT(*) as cnt FROM opc_hr_records WHERE company_id = ? AND status = 'active'",
|
|
99
|
+
companyId,
|
|
100
|
+
) as CountRow).cnt;
|
|
101
|
+
|
|
102
|
+
const transactionCount = (db.queryOne(
|
|
103
|
+
"SELECT COUNT(*) as cnt FROM opc_transactions WHERE company_id = ?",
|
|
104
|
+
companyId,
|
|
105
|
+
) as CountRow).cnt;
|
|
106
|
+
|
|
107
|
+
factors.totalIncome = totalIncome;
|
|
108
|
+
factors.revenueMonths = revenueMonths;
|
|
109
|
+
factors.hasCanvas = hasCanvas;
|
|
110
|
+
factors.contactCount = contactCount;
|
|
111
|
+
factors.projectCount = projectCount;
|
|
112
|
+
factors.activeContracts = activeContracts;
|
|
113
|
+
factors.hrCount = hrCount;
|
|
114
|
+
factors.transactionCount = transactionCount;
|
|
115
|
+
|
|
116
|
+
// 阶段判定(从高到低)
|
|
117
|
+
if (totalIncome > 500_000 && revenueMonths >= 6 && hrCount >= 3) {
|
|
118
|
+
return { stage: "scaling", stageLabel: STAGE_LABELS.scaling, confidence: 0.9, factors };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (totalIncome > 100_000 && revenueMonths >= 3 && activeContracts >= 2) {
|
|
122
|
+
return { stage: "stable", stageLabel: STAGE_LABELS.stable, confidence: 0.85, factors };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (totalIncome > 10_000 && revenueMonths >= 2) {
|
|
126
|
+
return { stage: "growth", stageLabel: STAGE_LABELS.growth, confidence: 0.8, factors };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (totalIncome > 0) {
|
|
130
|
+
return { stage: "early_revenue", stageLabel: STAGE_LABELS.early_revenue, confidence: 0.8, factors };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (hasCanvas || contactCount > 0 || projectCount > 0 || transactionCount > 0) {
|
|
134
|
+
return { stage: "validation", stageLabel: STAGE_LABELS.validation, confidence: 0.7, factors };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { stage: "idea", stageLabel: STAGE_LABELS.idea, confidence: 0.6, factors };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** 更新单个公司的阶段缓存到 opc_company_stage */
|
|
141
|
+
export function updateCompanyStage(db: OpcDatabase, companyId: string): StageResult {
|
|
142
|
+
const result = detectCompanyStage(db, companyId);
|
|
143
|
+
const now = new Date().toISOString();
|
|
144
|
+
|
|
145
|
+
db.execute(
|
|
146
|
+
`INSERT INTO opc_company_stage (company_id, stage, stage_label, confidence, factors_json, updated_at)
|
|
147
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
148
|
+
ON CONFLICT(company_id) DO UPDATE SET
|
|
149
|
+
stage = excluded.stage,
|
|
150
|
+
stage_label = excluded.stage_label,
|
|
151
|
+
confidence = excluded.confidence,
|
|
152
|
+
factors_json = excluded.factors_json,
|
|
153
|
+
updated_at = excluded.updated_at`,
|
|
154
|
+
companyId, result.stage, result.stageLabel, result.confidence,
|
|
155
|
+
JSON.stringify(result.factors), now,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** 重新计算所有公司的阶段 */
|
|
162
|
+
export function recalculateAllStages(db: OpcDatabase, log: (msg: string) => void): void {
|
|
163
|
+
const companies = db.query("SELECT id FROM opc_companies") as { id: string }[];
|
|
164
|
+
for (const c of companies) {
|
|
165
|
+
const result = updateCompanyStage(db, c.id);
|
|
166
|
+
log(`opc-stage: [${c.id}] → ${result.stageLabel}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 星环OPC中心 — AI 员工任务执行引擎
|
|
3
|
+
*
|
|
4
|
+
* 负责:
|
|
5
|
+
* 1. 检查哪些定时任务到了执行时间
|
|
6
|
+
* 2. 创建 staff_task 记录
|
|
7
|
+
* 3. 构建 sessions_spawn 的 prompt(由 AI 调用 sessions_spawn 执行)
|
|
8
|
+
* 4. 跟踪任务完成状态
|
|
9
|
+
*
|
|
10
|
+
* 注意:sessions_spawn 是 OpenClaw 框架的 agent 工具,
|
|
11
|
+
* 插件不直接调用,而是将 prompt 返回给 AI,由 AI 调用 sessions_spawn。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { OpcDatabase } from "../db/index.js";
|
|
15
|
+
import { BUILTIN_TASK_TEMPLATES, type TaskTemplate, type TaskContext } from "./task-templates.js";
|
|
16
|
+
|
|
17
|
+
export interface ExecuteTaskResult {
|
|
18
|
+
taskId: string;
|
|
19
|
+
alreadyExists: boolean;
|
|
20
|
+
staffRoleName: string;
|
|
21
|
+
title: string;
|
|
22
|
+
/** 完整的 sessions_spawn task prompt,AI 用此调用 sessions_spawn */
|
|
23
|
+
spawnPrompt: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class TaskExecutor {
|
|
27
|
+
constructor(private db: OpcDatabase) {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 为一个任务模板创建任务记录并生成 spawn prompt。
|
|
31
|
+
* 如果今日已有同类任务,返回已存在标记。
|
|
32
|
+
*/
|
|
33
|
+
prepareTask(template: TaskTemplate, companyId: string): ExecuteTaskResult {
|
|
34
|
+
const company = this.db.queryOne(
|
|
35
|
+
"SELECT name, industry FROM opc_companies WHERE id = ?", companyId,
|
|
36
|
+
) as { name: string; industry: string } | null;
|
|
37
|
+
if (!company) throw new Error(`公司 ${companyId} 不存在`);
|
|
38
|
+
|
|
39
|
+
const staffConfig = this.db.queryOne(
|
|
40
|
+
"SELECT role_name, system_prompt FROM opc_staff_config WHERE company_id = ? AND role = ? AND enabled = 1",
|
|
41
|
+
companyId, template.role,
|
|
42
|
+
) as { role_name: string; system_prompt: string } | null;
|
|
43
|
+
if (!staffConfig) throw new Error(`岗位 ${template.role} 未启用`);
|
|
44
|
+
|
|
45
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
46
|
+
const agentId = `opc-${companyId}`;
|
|
47
|
+
|
|
48
|
+
// 检查今日是否已执行过同类任务(防重复)
|
|
49
|
+
const existingTask = this.db.queryOne(
|
|
50
|
+
`SELECT id FROM opc_staff_tasks
|
|
51
|
+
WHERE company_id = ? AND staff_role = ? AND task_type = ?
|
|
52
|
+
AND DATE(created_at) = ?
|
|
53
|
+
AND status != 'cancelled'`,
|
|
54
|
+
companyId, template.role, template.taskType, today,
|
|
55
|
+
) as { id: string } | null;
|
|
56
|
+
|
|
57
|
+
if (existingTask) {
|
|
58
|
+
return {
|
|
59
|
+
taskId: existingTask.id,
|
|
60
|
+
alreadyExists: true,
|
|
61
|
+
staffRoleName: staffConfig.role_name,
|
|
62
|
+
title: template.title,
|
|
63
|
+
spawnPrompt: "",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 创建任务记录
|
|
68
|
+
const taskId = this.db.genId();
|
|
69
|
+
const now = new Date().toISOString();
|
|
70
|
+
this.db.execute(
|
|
71
|
+
`INSERT INTO opc_staff_tasks (id, company_id, staff_role, title, description, status, priority, task_type, schedule, assigned_at, started_at, created_at)
|
|
72
|
+
VALUES (?, ?, ?, ?, ?, 'in_progress', 'normal', ?, ?, ?, ?, ?)`,
|
|
73
|
+
taskId, companyId, template.role, template.title, template.description,
|
|
74
|
+
template.taskType, template.schedule, now, now, now,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// 构建 prompt context
|
|
78
|
+
const ctx: TaskContext = {
|
|
79
|
+
companyId,
|
|
80
|
+
companyName: company.name,
|
|
81
|
+
industry: company.industry,
|
|
82
|
+
agentId,
|
|
83
|
+
staffRole: template.role,
|
|
84
|
+
staffRoleName: staffConfig.role_name,
|
|
85
|
+
staffSystemPrompt: staffConfig.system_prompt,
|
|
86
|
+
date: today,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const basePrompt = template.buildPrompt(ctx);
|
|
90
|
+
|
|
91
|
+
// 追加通用尾部指令
|
|
92
|
+
const spawnPrompt = `${basePrompt}
|
|
93
|
+
|
|
94
|
+
## 任务 ID
|
|
95
|
+
${taskId}
|
|
96
|
+
|
|
97
|
+
## 完成后必须执行
|
|
98
|
+
|
|
99
|
+
**【必须】更新任务状态 — 这是你交付成果的唯一方式**
|
|
100
|
+
调用 opc_staff update_task:
|
|
101
|
+
- task_id="${taskId}"
|
|
102
|
+
- status="completed"
|
|
103
|
+
- result_summary=**完整的工作报告**(不是一句话!要包含所有具体内容)。格式:
|
|
104
|
+
|
|
105
|
+
## ${template.title}(${ctx.date})
|
|
106
|
+
|
|
107
|
+
### 工作成果
|
|
108
|
+
(列出你做的所有工作的完整内容,搜到了什么、分析出了什么,要写具体内容)
|
|
109
|
+
|
|
110
|
+
### 关键发现
|
|
111
|
+
(3-5 条最重要的发现/结论,每条有具体数据)
|
|
112
|
+
|
|
113
|
+
### 建议下一步
|
|
114
|
+
(基于工作成果,建议老板接下来做什么)
|
|
115
|
+
|
|
116
|
+
- result_data=完整结果的 JSON 字符串(包含所有原始数据、搜索结果、分析明细)
|
|
117
|
+
|
|
118
|
+
⚠️ result_summary 是老板看到的唯一内容,必须详细、完整、有具体数据。
|
|
119
|
+
老板不会去翻 result_data,所以 result_summary 里要把关键信息写全。
|
|
120
|
+
|
|
121
|
+
**【可选】尝试直接汇报**
|
|
122
|
+
如果 sessions_send 工具可用,调用:
|
|
123
|
+
- sessionKey="agent:${agentId}:main"
|
|
124
|
+
- message=与 result_summary 相同的完整报告
|
|
125
|
+
(如果 sessions_send 不可用,跳过即可,老板会通过任务系统看到你的成果)`;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
taskId,
|
|
129
|
+
alreadyExists: false,
|
|
130
|
+
staffRoleName: staffConfig.role_name,
|
|
131
|
+
title: template.title,
|
|
132
|
+
spawnPrompt,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 老板按需触发:为指定员工构建自定义任务。
|
|
138
|
+
*/
|
|
139
|
+
prepareBossTask(
|
|
140
|
+
companyId: string,
|
|
141
|
+
staffRole: string,
|
|
142
|
+
title: string,
|
|
143
|
+
description: string,
|
|
144
|
+
): ExecuteTaskResult {
|
|
145
|
+
const template: TaskTemplate = {
|
|
146
|
+
role: staffRole,
|
|
147
|
+
taskType: "boss_assigned",
|
|
148
|
+
title,
|
|
149
|
+
schedule: "on_demand",
|
|
150
|
+
description,
|
|
151
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」(${ctx.industry}行业)服务。
|
|
152
|
+
${ctx.staffSystemPrompt}
|
|
153
|
+
|
|
154
|
+
## 老板交办任务
|
|
155
|
+
${title}
|
|
156
|
+
|
|
157
|
+
${description}
|
|
158
|
+
|
|
159
|
+
## 可用工具
|
|
160
|
+
- opc_search: 联网搜索
|
|
161
|
+
- opc_finance: 财务管理
|
|
162
|
+
- opc_legal: 合同法务
|
|
163
|
+
- opc_hr: 人力资源
|
|
164
|
+
- opc_media: 内容管理
|
|
165
|
+
- opc_project: 项目管理
|
|
166
|
+
- opc_staff: 更新任务状态
|
|
167
|
+
- exec: 执行脚本/命令
|
|
168
|
+
- read/write: 文件操作
|
|
169
|
+
- browser: 浏览器自动化`,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// boss_assigned 任务不做防重复检查,每次都新建
|
|
173
|
+
const company = this.db.queryOne(
|
|
174
|
+
"SELECT name, industry FROM opc_companies WHERE id = ?", companyId,
|
|
175
|
+
) as { name: string; industry: string } | null;
|
|
176
|
+
if (!company) throw new Error(`公司 ${companyId} 不存在`);
|
|
177
|
+
|
|
178
|
+
const staffConfig = this.db.queryOne(
|
|
179
|
+
"SELECT role_name, system_prompt FROM opc_staff_config WHERE company_id = ? AND role = ? AND enabled = 1",
|
|
180
|
+
companyId, staffRole,
|
|
181
|
+
) as { role_name: string; system_prompt: string } | null;
|
|
182
|
+
if (!staffConfig) throw new Error(`岗位 ${staffRole} 未启用`);
|
|
183
|
+
|
|
184
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
185
|
+
const agentId = `opc-${companyId}`;
|
|
186
|
+
const taskId = this.db.genId();
|
|
187
|
+
const now = new Date().toISOString();
|
|
188
|
+
|
|
189
|
+
this.db.execute(
|
|
190
|
+
`INSERT INTO opc_staff_tasks (id, company_id, staff_role, title, description, status, priority, task_type, schedule, assigned_at, started_at, created_at)
|
|
191
|
+
VALUES (?, ?, ?, ?, ?, 'in_progress', 'normal', ?, ?, ?, ?, ?)`,
|
|
192
|
+
taskId, companyId, staffRole, title, description,
|
|
193
|
+
"boss_assigned", "on_demand", now, now, now,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const ctx: TaskContext = {
|
|
197
|
+
companyId,
|
|
198
|
+
companyName: company.name,
|
|
199
|
+
industry: company.industry,
|
|
200
|
+
agentId,
|
|
201
|
+
staffRole,
|
|
202
|
+
staffRoleName: staffConfig.role_name,
|
|
203
|
+
staffSystemPrompt: staffConfig.system_prompt,
|
|
204
|
+
date: today,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const basePrompt = template.buildPrompt(ctx);
|
|
208
|
+
const spawnPrompt = `${basePrompt}
|
|
209
|
+
|
|
210
|
+
## 任务 ID
|
|
211
|
+
${taskId}
|
|
212
|
+
|
|
213
|
+
## 完成后必须执行
|
|
214
|
+
|
|
215
|
+
**【必须】更新任务状态 — 这是你交付成果的唯一方式**
|
|
216
|
+
调用 opc_staff update_task:
|
|
217
|
+
- task_id="${taskId}"
|
|
218
|
+
- status="completed"
|
|
219
|
+
- result_summary=**完整的工作报告**(不是一句话!要包含所有具体内容)。格式:
|
|
220
|
+
|
|
221
|
+
## ${title}(${today})
|
|
222
|
+
|
|
223
|
+
### 工作成果
|
|
224
|
+
(列出你做的所有工作的完整内容,搜到了什么、分析出了什么,要写具体内容)
|
|
225
|
+
|
|
226
|
+
### 关键发现
|
|
227
|
+
(3-5 条最重要的发现/结论,每条有具体数据)
|
|
228
|
+
|
|
229
|
+
### 建议下一步
|
|
230
|
+
(基于工作成果,建议老板接下来做什么)
|
|
231
|
+
|
|
232
|
+
- result_data=完整结果的 JSON 字符串(包含所有原始数据、搜索结果、分析明细)
|
|
233
|
+
|
|
234
|
+
⚠️ result_summary 是老板看到的唯一内容,必须详细、完整、有具体数据。
|
|
235
|
+
老板不会去翻 result_data,所以 result_summary 里要把关键信息写全。
|
|
236
|
+
|
|
237
|
+
**【可选】尝试直接汇报**
|
|
238
|
+
如果 sessions_send 工具可用,调用:
|
|
239
|
+
- sessionKey="agent:${agentId}:main"
|
|
240
|
+
- message=与 result_summary 相同的完整报告
|
|
241
|
+
(如果 sessions_send 不可用,跳过即可,老板会通过任务系统看到你的成果)`;
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
taskId,
|
|
245
|
+
alreadyExists: false,
|
|
246
|
+
staffRoleName: staffConfig.role_name,
|
|
247
|
+
title,
|
|
248
|
+
spawnPrompt,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 获取某公司在某个调度频率下所有应执行的任务模板。
|
|
254
|
+
* 检查哪些岗位已启用,返回可执行的模板列表。
|
|
255
|
+
*/
|
|
256
|
+
getScheduledTemplates(companyId: string, schedule: "daily" | "weekly" | "hourly"): TaskTemplate[] {
|
|
257
|
+
const templates = BUILTIN_TASK_TEMPLATES.filter(t => t.schedule === schedule);
|
|
258
|
+
const enabledRoles = this.db.query(
|
|
259
|
+
"SELECT role FROM opc_staff_config WHERE company_id = ? AND enabled = 1",
|
|
260
|
+
companyId,
|
|
261
|
+
) as { role: string }[];
|
|
262
|
+
const roleSet = new Set(enabledRoles.map(r => r.role));
|
|
263
|
+
return templates.filter(t => roleSet.has(t.role));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 为某公司创建所有到期的定时任务记录(不重复创建)。
|
|
268
|
+
* 返回所有新建任务的 spawn 结果。
|
|
269
|
+
*/
|
|
270
|
+
prepareScheduledTasks(companyId: string, schedule: "daily" | "weekly"): ExecuteTaskResult[] {
|
|
271
|
+
const templates = this.getScheduledTemplates(companyId, schedule);
|
|
272
|
+
const results: ExecuteTaskResult[] = [];
|
|
273
|
+
|
|
274
|
+
for (const template of templates) {
|
|
275
|
+
try {
|
|
276
|
+
const result = this.prepareTask(template, companyId);
|
|
277
|
+
if (!result.alreadyExists) {
|
|
278
|
+
results.push(result);
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
// 岗位未启用等,跳过
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return results;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 为所有活跃公司创建定时任务记录。
|
|
290
|
+
* 用于 proactive-service 后台调用。
|
|
291
|
+
*/
|
|
292
|
+
createPendingScheduledTasks(schedule: "daily" | "weekly"): number {
|
|
293
|
+
const companies = this.db.query(
|
|
294
|
+
"SELECT id FROM opc_companies WHERE status = 'active'",
|
|
295
|
+
) as { id: string }[];
|
|
296
|
+
|
|
297
|
+
let created = 0;
|
|
298
|
+
for (const company of companies) {
|
|
299
|
+
const templates = this.getScheduledTemplates(company.id, schedule);
|
|
300
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
301
|
+
|
|
302
|
+
for (const template of templates) {
|
|
303
|
+
try {
|
|
304
|
+
// 检查今日是否已有同类任务
|
|
305
|
+
const existing = this.db.queryOne(
|
|
306
|
+
`SELECT id FROM opc_staff_tasks
|
|
307
|
+
WHERE company_id = ? AND staff_role = ? AND task_type = ?
|
|
308
|
+
AND DATE(created_at) = ?
|
|
309
|
+
AND status != 'cancelled'`,
|
|
310
|
+
company.id, template.role, template.taskType, today,
|
|
311
|
+
);
|
|
312
|
+
if (existing) continue;
|
|
313
|
+
|
|
314
|
+
// 创建 pending 状态的任务(等 AI 来触发执行)
|
|
315
|
+
const taskId = this.db.genId();
|
|
316
|
+
const now = new Date().toISOString();
|
|
317
|
+
this.db.execute(
|
|
318
|
+
`INSERT INTO opc_staff_tasks (id, company_id, staff_role, title, description, status, priority, task_type, schedule, assigned_at, created_at)
|
|
319
|
+
VALUES (?, ?, ?, ?, ?, 'pending', 'normal', ?, ?, ?, ?)`,
|
|
320
|
+
taskId, company.id, template.role, template.title, template.description,
|
|
321
|
+
template.taskType, template.schedule, now, now,
|
|
322
|
+
);
|
|
323
|
+
created++;
|
|
324
|
+
} catch {
|
|
325
|
+
// 跳过失败的
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return created;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 星环OPC中心 — AI 员工内置任务模板
|
|
3
|
+
*
|
|
4
|
+
* 每种角色有预定义的日常/周常任务模板。
|
|
5
|
+
* 模板生成 sessions_spawn 的 task prompt,由 AI 调用 sessions_spawn 执行。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface TaskTemplate {
|
|
9
|
+
role: string;
|
|
10
|
+
taskType: string;
|
|
11
|
+
title: string;
|
|
12
|
+
schedule: "daily" | "weekly" | "hourly" | "on_demand";
|
|
13
|
+
description: string;
|
|
14
|
+
buildPrompt: (ctx: TaskContext) => string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TaskContext {
|
|
18
|
+
companyId: string;
|
|
19
|
+
companyName: string;
|
|
20
|
+
industry: string;
|
|
21
|
+
agentId: string;
|
|
22
|
+
staffRole: string;
|
|
23
|
+
staffRoleName: string;
|
|
24
|
+
staffSystemPrompt: string;
|
|
25
|
+
date: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const BUILTIN_TASK_TEMPLATES: TaskTemplate[] = [
|
|
29
|
+
// ── 市场推广 ──────────────────────────────────────────────────
|
|
30
|
+
{
|
|
31
|
+
role: "marketing",
|
|
32
|
+
taskType: "daily_news",
|
|
33
|
+
title: "每日行业资讯搜集",
|
|
34
|
+
schedule: "daily",
|
|
35
|
+
description: "搜集行业最新资讯,整理成简报",
|
|
36
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」(${ctx.industry}行业)服务。
|
|
37
|
+
${ctx.staffSystemPrompt}
|
|
38
|
+
|
|
39
|
+
## 今日任务:行业资讯搜集(${ctx.date})
|
|
40
|
+
|
|
41
|
+
请完成以下工作:
|
|
42
|
+
1. 使用 opc_search 搜索「${ctx.industry} 最新动态 ${ctx.date}」
|
|
43
|
+
2. 搜索「${ctx.industry} 行业新闻」获取更多信息
|
|
44
|
+
3. 从搜索结果中筛选 3-5 条最有价值的资讯
|
|
45
|
+
4. 用 opc_media create_content 将整理好的资讯写入系统,格式:
|
|
46
|
+
- title: "${ctx.date} ${ctx.industry}行业日报"
|
|
47
|
+
- content_type: "article"
|
|
48
|
+
- content: 整理好的资讯(标题+摘要+来源链接)
|
|
49
|
+
5. 调用 opc_staff update_task 更新任务状态为 completed,result_summary 填写关键发现
|
|
50
|
+
|
|
51
|
+
## 可用工具
|
|
52
|
+
- opc_search: 联网搜索
|
|
53
|
+
- opc_media: 内容管理(create_content 写入)
|
|
54
|
+
- opc_staff: 更新任务状态`,
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
role: "marketing",
|
|
59
|
+
taskType: "content_creation",
|
|
60
|
+
title: "内容创作与发布",
|
|
61
|
+
schedule: "weekly",
|
|
62
|
+
description: "基于行业热点创作一篇内容",
|
|
63
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」(${ctx.industry}行业)服务。
|
|
64
|
+
${ctx.staffSystemPrompt}
|
|
65
|
+
|
|
66
|
+
## 本周任务:内容创作(${ctx.date})
|
|
67
|
+
|
|
68
|
+
请完成以下工作:
|
|
69
|
+
1. 使用 opc_search 搜索「${ctx.industry} 热点话题」和「${ctx.industry} 行业趋势」
|
|
70
|
+
2. 选择一个与公司业务最相关的话题
|
|
71
|
+
3. 撰写一篇 800-1500 字的专业文章
|
|
72
|
+
4. 用 opc_media create_content 将文章写入系统:
|
|
73
|
+
- content_type: "article"
|
|
74
|
+
- status: "draft"
|
|
75
|
+
5. 调用 opc_staff update_task 更新任务状态
|
|
76
|
+
|
|
77
|
+
## 可用工具
|
|
78
|
+
- opc_search: 联网搜索
|
|
79
|
+
- opc_media: 内容管理
|
|
80
|
+
- opc_staff: 更新任务状态`,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// ── 财务顾问 ──────────────────────────────────────────────────
|
|
84
|
+
{
|
|
85
|
+
role: "finance",
|
|
86
|
+
taskType: "daily_finance_check",
|
|
87
|
+
title: "每日财务巡检",
|
|
88
|
+
schedule: "daily",
|
|
89
|
+
description: "检查未开票收入、到期发票、异常支出",
|
|
90
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」服务。
|
|
91
|
+
${ctx.staffSystemPrompt}
|
|
92
|
+
|
|
93
|
+
## 今日任务:财务巡检(${ctx.date})
|
|
94
|
+
|
|
95
|
+
请完成以下检查:
|
|
96
|
+
1. 调用 opc_finance finance_summary 查看整体财务状况
|
|
97
|
+
2. 调用 opc_finance list_invoices status=draft 查看草稿发票
|
|
98
|
+
3. 调用 opc_finance list_invoices status=sent 查看已发未收款发票
|
|
99
|
+
4. 分析是否有异常情况(超 30 天未收款、大额支出等)
|
|
100
|
+
5. 将巡检结果通过 opc_staff update_task 写回
|
|
101
|
+
|
|
102
|
+
## 可用工具
|
|
103
|
+
- opc_finance: 财务数据查询
|
|
104
|
+
- opc_staff: 更新任务状态`,
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// ── 法务助理 ──────────────────────────────────────────────────
|
|
108
|
+
{
|
|
109
|
+
role: "legal",
|
|
110
|
+
taskType: "contract_review",
|
|
111
|
+
title: "合同到期巡检",
|
|
112
|
+
schedule: "weekly",
|
|
113
|
+
description: "检查即将到期的合同,提醒续签",
|
|
114
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」服务。
|
|
115
|
+
${ctx.staffSystemPrompt}
|
|
116
|
+
|
|
117
|
+
## 本周任务:合同到期巡检(${ctx.date})
|
|
118
|
+
|
|
119
|
+
请完成以下检查:
|
|
120
|
+
1. 调用 opc_legal list_contracts 查看所有合同
|
|
121
|
+
2. 筛选出 30 天内即将到期的合同
|
|
122
|
+
3. 检查是否有需要续签或关闭的合同
|
|
123
|
+
4. 将巡检结果通过 opc_staff update_task 写回,列出需要关注的合同
|
|
124
|
+
|
|
125
|
+
## 可用工具
|
|
126
|
+
- opc_legal: 合同管理
|
|
127
|
+
- opc_staff: 更新任务状态`,
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// ── 运营经理 ──────────────────────────────────────────────────
|
|
131
|
+
{
|
|
132
|
+
role: "ops",
|
|
133
|
+
taskType: "daily_ops_report",
|
|
134
|
+
title: "每日运营报告",
|
|
135
|
+
schedule: "daily",
|
|
136
|
+
description: "汇总今日各部门工作,生成运营日报",
|
|
137
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」服务。
|
|
138
|
+
${ctx.staffSystemPrompt}
|
|
139
|
+
|
|
140
|
+
## 今日任务:运营日报(${ctx.date})
|
|
141
|
+
|
|
142
|
+
请完成以下工作:
|
|
143
|
+
1. 调用 opc_staff staff_standup 查看各员工工作状态
|
|
144
|
+
2. 调用 opc_finance finance_summary 查看今日财务动态
|
|
145
|
+
3. 调用 opc_project list_projects 查看项目进度
|
|
146
|
+
4. 综合以上信息,生成一份运营日报
|
|
147
|
+
5. 用 opc_media create_content 写入系统(content_type=report)
|
|
148
|
+
6. 调用 opc_staff update_task 更新任务状态
|
|
149
|
+
|
|
150
|
+
## 可用工具
|
|
151
|
+
- opc_staff: 员工状态查询
|
|
152
|
+
- opc_finance: 财务数据
|
|
153
|
+
- opc_project: 项目管理
|
|
154
|
+
- opc_media: 内容管理`,
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// ── HR 专员 ───────────────────────────────────────────────────
|
|
158
|
+
{
|
|
159
|
+
role: "hr",
|
|
160
|
+
taskType: "hr_monthly_check",
|
|
161
|
+
title: "月度人力资源巡检",
|
|
162
|
+
schedule: "weekly",
|
|
163
|
+
description: "检查社保缴纳、合同到期、薪酬异常",
|
|
164
|
+
buildPrompt: (ctx) => `你是「${ctx.staffRoleName}」,为「${ctx.companyName}」服务。
|
|
165
|
+
${ctx.staffSystemPrompt}
|
|
166
|
+
|
|
167
|
+
## 本周任务:人力资源巡检(${ctx.date})
|
|
168
|
+
|
|
169
|
+
请完成以下检查:
|
|
170
|
+
1. 调用 opc_hr list_employees 查看所有在职员工
|
|
171
|
+
2. 检查合同即将到期的员工(30天内)
|
|
172
|
+
3. 核查薪酬数据是否完整
|
|
173
|
+
4. 将巡检结果通过 opc_staff update_task 写回
|
|
174
|
+
|
|
175
|
+
## 可用工具
|
|
176
|
+
- opc_hr: 人力资源管理
|
|
177
|
+
- opc_staff: 更新任务状态`,
|
|
178
|
+
},
|
|
179
|
+
];
|