@zax360/openapi-skills 1.0.2 → 1.0.5
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/.codex-plugin/plugin.json +3 -3
- package/bin/check.js +3 -0
- package/bin/install.js +98 -11
- package/lib/skill-manifest.js +161 -0
- package/package.json +13 -3
- package/skills/zhianxin-openapi-hro-labor/SKILL.md +62 -0
- package/skills/zhianxin-openapi-hro-workflow/SKILL.md +109 -0
- package/skills/zhianxin-openapi-leads/SKILL.md +25 -42
- package/skills/zhianxin-openapi-ops/SKILL.md +6 -4
- package/skills/zhianxin-openapi-readonly/SKILL.md +68 -2
- package/skills/zhianxin-openapi-shared/SKILL.md +3 -3
- package/skills/zhianxin-openapi-sync/SKILL.md +3 -3
- package/skills-manifest.json +47 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zhianxin-openapi",
|
|
3
3
|
"version": "0.1.1",
|
|
4
|
-
"description": "面向 AI Agent 的职安心开放能力说明:HTTP
|
|
5
|
-
"keywords": ["zhianxin", "openapi", "职安心", "leads", "signing", "sync", "webhook"],
|
|
4
|
+
"description": "面向 AI Agent 的职安心开放能力说明:HTTP 签名校验、线索、岗位查询、同步、HRO 劳务、合作方暂存、事件订阅等 Skills(每能力一份 SKILL.md)。",
|
|
5
|
+
"keywords": ["zhianxin", "openapi", "职安心", "leads", "signing", "sync", "webhook", "hro", "labor"],
|
|
6
6
|
"skills": "./skills/",
|
|
7
7
|
"interface": {
|
|
8
8
|
"displayName": "职安心开放平台",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
"developerName": "职安心",
|
|
12
12
|
"category": "Developer Tools",
|
|
13
13
|
"capabilities": ["Interactive", "Write"],
|
|
14
|
-
"defaultPrompt": ["
|
|
14
|
+
"defaultPrompt": ["先使用 semantic_intent / knowledge://zhianxin/routing/semantic-intent-system-prompt 判断用户语义和能力边界;只有语义明确属于职安心开放平台时,才根据开放平台文档构造签名并调用 API。"]
|
|
15
15
|
}
|
|
16
16
|
}
|
package/bin/check.js
ADDED
package/bin/install.js
CHANGED
|
@@ -1,16 +1,103 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
const
|
|
2
|
+
const { execFileSync } = require('node:child_process')
|
|
3
|
+
const { readFileSync } = require('node:fs')
|
|
4
|
+
const { homedir } = require('node:os')
|
|
5
|
+
const { join, resolve } = require('node:path')
|
|
6
|
+
const {
|
|
7
|
+
agentAssignmentReport,
|
|
8
|
+
buildManifest,
|
|
9
|
+
compareTarget,
|
|
10
|
+
discoverManagedTargets,
|
|
11
|
+
installCommandArgs,
|
|
12
|
+
validateLockedManifest,
|
|
13
|
+
} = require('../lib/skill-manifest')
|
|
7
14
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
15
|
+
const pluginRoot = resolve(__dirname, '..')
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(join(pluginRoot, 'package.json'), 'utf8'))
|
|
17
|
+
|
|
18
|
+
function parseArgs(argv) {
|
|
19
|
+
const options = {
|
|
20
|
+
check: false,
|
|
21
|
+
json: false,
|
|
22
|
+
target: undefined,
|
|
23
|
+
}
|
|
24
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
25
|
+
const arg = argv[index]
|
|
26
|
+
if (arg === '--check') options.check = true
|
|
27
|
+
else if (arg === '--json') options.json = true
|
|
28
|
+
else if (arg === '--target') {
|
|
29
|
+
const target = argv[index + 1]
|
|
30
|
+
if (!target || target.startsWith('--')) throw new Error('missing_target_path')
|
|
31
|
+
options.target = resolve(target)
|
|
32
|
+
index += 1
|
|
33
|
+
} else {
|
|
34
|
+
throw new Error(`unknown_option:${arg}`)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return options
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function loadManifest() {
|
|
41
|
+
const locked = JSON.parse(readFileSync(join(pluginRoot, 'skills-manifest.json'), 'utf8'))
|
|
42
|
+
validateLockedManifest(locked)
|
|
43
|
+
const current = buildManifest(pluginRoot)
|
|
44
|
+
if (JSON.stringify(locked) !== JSON.stringify(current)) {
|
|
45
|
+
throw new Error('skills_manifest_stale')
|
|
46
|
+
}
|
|
47
|
+
return locked
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function report(manifest, explicitTarget, agentListJson) {
|
|
51
|
+
const targets = explicitTarget
|
|
52
|
+
? [explicitTarget]
|
|
53
|
+
: discoverManagedTargets(homedir(), manifest)
|
|
54
|
+
const result = {
|
|
55
|
+
source: packageJson.name,
|
|
56
|
+
version: packageJson.version,
|
|
57
|
+
targets: targets.map((target) => compareTarget(manifest, target)),
|
|
58
|
+
}
|
|
59
|
+
if (!explicitTarget) {
|
|
60
|
+
result.agents = agentAssignmentReport(manifest, agentListJson)
|
|
61
|
+
}
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function printResult(result, json) {
|
|
66
|
+
console.log(JSON.stringify(result, null, json ? 0 : 2))
|
|
67
|
+
}
|
|
11
68
|
|
|
12
69
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
70
|
+
const options = parseArgs(process.argv.slice(2))
|
|
71
|
+
const manifest = loadManifest()
|
|
72
|
+
if (!options.check) {
|
|
73
|
+
execFileSync('npx', installCommandArgs(pluginRoot), {
|
|
74
|
+
stdio: options.json ? ['ignore', 'ignore', 'inherit'] : 'inherit',
|
|
75
|
+
env: process.env,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
const agentListJson = options.target
|
|
79
|
+
? undefined
|
|
80
|
+
: execFileSync('npx', ['skills', 'list', '-g', '--json'], {
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
env: process.env,
|
|
83
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
84
|
+
})
|
|
85
|
+
const result = report(manifest, options.target, agentListJson)
|
|
86
|
+
printResult(result, options.json)
|
|
87
|
+
if (
|
|
88
|
+
result.targets.some(
|
|
89
|
+
(target) => target.missing.length > 0 || target.stale.length > 0,
|
|
90
|
+
) || (result.agents && result.agents.missing.length > 0)
|
|
91
|
+
) {
|
|
92
|
+
process.exitCode = 2
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(
|
|
96
|
+
JSON.stringify({
|
|
97
|
+
ok: false,
|
|
98
|
+
code: 'skills_install_error',
|
|
99
|
+
message: error instanceof Error ? error.message : String(error),
|
|
100
|
+
}),
|
|
101
|
+
)
|
|
102
|
+
process.exitCode = 1
|
|
16
103
|
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const { createHash } = require('node:crypto')
|
|
2
|
+
const { existsSync, readFileSync, readdirSync } = require('node:fs')
|
|
3
|
+
const { join } = require('node:path')
|
|
4
|
+
|
|
5
|
+
const REQUIRED_AGENT_NAMES = ['Claude Code', 'Codex', 'Cursor']
|
|
6
|
+
|
|
7
|
+
function parseFrontmatter(text) {
|
|
8
|
+
const match = text.match(/^---\n([\s\S]*?)\n---\n/)
|
|
9
|
+
if (!match) throw new Error('skill_frontmatter_missing')
|
|
10
|
+
const values = {}
|
|
11
|
+
for (const line of match[1].split('\n')) {
|
|
12
|
+
const field = line.match(/^(name|version):\s*["']?([^"']+?)["']?\s*$/)
|
|
13
|
+
if (field) values[field[1]] = field[2]
|
|
14
|
+
}
|
|
15
|
+
if (!values.name || !/^\d+\.\d+\.\d+$/.test(values.version || '')) {
|
|
16
|
+
throw new Error('skill_frontmatter_invalid')
|
|
17
|
+
}
|
|
18
|
+
return values
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex')
|
|
22
|
+
|
|
23
|
+
function validateLockedManifest(manifest) {
|
|
24
|
+
if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.skills)) {
|
|
25
|
+
throw new Error('skills_manifest_invalid')
|
|
26
|
+
}
|
|
27
|
+
if (manifest.skills.length === 0) throw new Error('skills_manifest_empty')
|
|
28
|
+
const names = new Set()
|
|
29
|
+
for (const item of manifest.skills) {
|
|
30
|
+
if (
|
|
31
|
+
!item ||
|
|
32
|
+
!/^[a-z0-9-]+$/.test(item.name || '') ||
|
|
33
|
+
item.path !== `skills/${item.name}/SKILL.md`
|
|
34
|
+
) {
|
|
35
|
+
throw new Error('invalid_skill_manifest_path')
|
|
36
|
+
}
|
|
37
|
+
if (
|
|
38
|
+
!/^\d+\.\d+\.\d+$/.test(item.version || '') ||
|
|
39
|
+
!/^[a-f0-9]{64}$/.test(item.sha256 || '')
|
|
40
|
+
) {
|
|
41
|
+
throw new Error('skills_manifest_invalid')
|
|
42
|
+
}
|
|
43
|
+
if (names.has(item.name)) throw new Error('duplicate_skill_name')
|
|
44
|
+
names.add(item.name)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildManifest(pluginRoot) {
|
|
49
|
+
const skillsRoot = join(pluginRoot, 'skills')
|
|
50
|
+
const skills = readdirSync(skillsRoot, { withFileTypes: true })
|
|
51
|
+
.filter((entry) => entry.isDirectory())
|
|
52
|
+
.map((entry) => {
|
|
53
|
+
const bytes = readFileSync(join(skillsRoot, entry.name, 'SKILL.md'))
|
|
54
|
+
const meta = parseFrontmatter(bytes.toString('utf8'))
|
|
55
|
+
if (meta.name !== entry.name) {
|
|
56
|
+
throw new Error(`skill_name_mismatch:${entry.name}:${meta.name}`)
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
name: meta.name,
|
|
60
|
+
version: meta.version,
|
|
61
|
+
path: `skills/${entry.name}/SKILL.md`,
|
|
62
|
+
sha256: sha256(bytes),
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
66
|
+
if (new Set(skills.map((item) => item.name)).size !== skills.length) {
|
|
67
|
+
throw new Error('duplicate_skill_name')
|
|
68
|
+
}
|
|
69
|
+
return { schemaVersion: 1, skills }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function compareTarget(manifest, targetRoot) {
|
|
73
|
+
const result = {
|
|
74
|
+
target: targetRoot,
|
|
75
|
+
current: [],
|
|
76
|
+
missing: [],
|
|
77
|
+
stale: [],
|
|
78
|
+
unmanaged: [],
|
|
79
|
+
}
|
|
80
|
+
const managed = new Set(manifest.skills.map((item) => item.name))
|
|
81
|
+
for (const item of manifest.skills) {
|
|
82
|
+
const file = join(targetRoot, item.name, 'SKILL.md')
|
|
83
|
+
if (!existsSync(file)) result.missing.push(item.name)
|
|
84
|
+
else if (sha256(readFileSync(file)) === item.sha256) result.current.push(item.name)
|
|
85
|
+
else result.stale.push(item.name)
|
|
86
|
+
}
|
|
87
|
+
if (existsSync(targetRoot)) {
|
|
88
|
+
for (const entry of readdirSync(targetRoot, { withFileTypes: true })) {
|
|
89
|
+
if (
|
|
90
|
+
entry.isDirectory() &&
|
|
91
|
+
!managed.has(entry.name) &&
|
|
92
|
+
entry.name.startsWith('zhianxin-')
|
|
93
|
+
) {
|
|
94
|
+
result.unmanaged.push(entry.name)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const key of ['current', 'missing', 'stale', 'unmanaged']) result[key].sort()
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function discoverTargets(home) {
|
|
103
|
+
return ['.agents/skills', '.claude/skills', '.codex/skills', '.cursor/skills'].map(
|
|
104
|
+
(relative) => join(home, relative),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function discoverManagedTargets(home, manifest) {
|
|
109
|
+
return discoverTargets(home).filter((target, index) => {
|
|
110
|
+
if (!existsSync(target)) return false
|
|
111
|
+
if (index === 0) return true
|
|
112
|
+
return manifest.skills.some((item) =>
|
|
113
|
+
existsSync(join(target, item.name, 'SKILL.md')),
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function installCommandArgs(pluginRoot) {
|
|
119
|
+
return [
|
|
120
|
+
'skills',
|
|
121
|
+
'add',
|
|
122
|
+
pluginRoot,
|
|
123
|
+
'-g',
|
|
124
|
+
'--agent',
|
|
125
|
+
'claude-code',
|
|
126
|
+
'codex',
|
|
127
|
+
'cursor',
|
|
128
|
+
'--skill',
|
|
129
|
+
'*',
|
|
130
|
+
'-y',
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function agentAssignmentReport(manifest, listJson) {
|
|
135
|
+
const parsed = JSON.parse(listJson)
|
|
136
|
+
const rows = Array.isArray(parsed) ? parsed : parsed.skills
|
|
137
|
+
if (!Array.isArray(rows)) throw new Error('skills_list_invalid')
|
|
138
|
+
const byName = new Map(rows.map((row) => [row.name, row]))
|
|
139
|
+
const missing = []
|
|
140
|
+
for (const item of manifest.skills) {
|
|
141
|
+
const installed = byName.get(item.name)
|
|
142
|
+
const agents = Array.isArray(installed && installed.agents)
|
|
143
|
+
? installed.agents
|
|
144
|
+
: []
|
|
145
|
+
const absent = REQUIRED_AGENT_NAMES.filter((name) => !agents.includes(name))
|
|
146
|
+
if (absent.length > 0) missing.push({ name: item.name, agents: absent })
|
|
147
|
+
}
|
|
148
|
+
return { required: REQUIRED_AGENT_NAMES.slice(), missing }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
agentAssignmentReport,
|
|
153
|
+
buildManifest,
|
|
154
|
+
compareTarget,
|
|
155
|
+
discoverManagedTargets,
|
|
156
|
+
discoverTargets,
|
|
157
|
+
installCommandArgs,
|
|
158
|
+
parseFrontmatter,
|
|
159
|
+
sha256,
|
|
160
|
+
validateLockedManifest,
|
|
161
|
+
}
|
package/package.json
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zax360/openapi-skills",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "职安心开放平台 Agent Skills
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "职安心开放平台 Agent Skills(签名、只读查询、线索、同步、HRO 劳务、合作方与事件接口),供 npx skills / Cursor / Codex 等加载",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"zhianxin",
|
|
8
8
|
"zax360",
|
|
9
9
|
"openapi",
|
|
10
|
+
"hro",
|
|
11
|
+
"labor",
|
|
10
12
|
"skills",
|
|
11
13
|
"职安心",
|
|
12
14
|
"cursor",
|
|
13
15
|
"codex"
|
|
14
16
|
],
|
|
15
17
|
"bin": {
|
|
16
|
-
"zax360-openapi-skills": "bin/install.js"
|
|
18
|
+
"zax360-openapi-skills": "bin/install.js",
|
|
19
|
+
"zax360-openapi-skills-check": "bin/check.js"
|
|
17
20
|
},
|
|
18
21
|
"files": [
|
|
19
22
|
"bin",
|
|
23
|
+
"lib",
|
|
20
24
|
"skills",
|
|
25
|
+
"skills-manifest.json",
|
|
21
26
|
".codex-plugin"
|
|
22
27
|
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "node --test test/*.test.js",
|
|
30
|
+
"build:manifest": "node scripts/build-manifest.js",
|
|
31
|
+
"prepack": "npm run build:manifest && npm test"
|
|
32
|
+
},
|
|
23
33
|
"publishConfig": {
|
|
24
34
|
"access": "public"
|
|
25
35
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zhianxin-openapi-hro-labor
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "职安心 HRO 劳务 P0:项目报人、驻场履约事件、考勤事实、结算草表。适用于 CLI zax360 hro labor-*、Skill 调用和 MCP 工具声明。"
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: ["node", "npx"]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# HRO 劳务 P0 开放能力
|
|
11
|
+
|
|
12
|
+
> 前置条件:先阅读 `../zhianxin-openapi-shared/SKILL.md`。本 Skill 面向劳务 SaaS P0 最小闭环,所有写动作都应在用户确认项目、供应商、人员与周期后执行。
|
|
13
|
+
|
|
14
|
+
## 默认 CLI
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @zax360/openapi-cli hro labor-delivery-create \
|
|
18
|
+
--project-id project-1 \
|
|
19
|
+
--supplier-id supplier-1 \
|
|
20
|
+
--worker-name 张三 \
|
|
21
|
+
--worker-mobile 13812345678
|
|
22
|
+
|
|
23
|
+
npx @zax360/openapi-cli hro labor-event-record \
|
|
24
|
+
--project-id project-1 \
|
|
25
|
+
--delivery-id delivery-1 \
|
|
26
|
+
--event-type ONBOARD \
|
|
27
|
+
--source skill
|
|
28
|
+
|
|
29
|
+
npx @zax360/openapi-cli hro labor-attendance-upsert \
|
|
30
|
+
--project-id project-1 \
|
|
31
|
+
--delivery-id delivery-1 \
|
|
32
|
+
--period 2026-07 \
|
|
33
|
+
--present-days 22 \
|
|
34
|
+
--work-hours 176
|
|
35
|
+
|
|
36
|
+
npx @zax360/openapi-cli hro labor-settlement-generate \
|
|
37
|
+
--project-id project-1 \
|
|
38
|
+
--period 2026-07
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
如果 Java OpenAPI 在部署层挂载到 `/openapi` 前缀,追加:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
--path-prefix /openapi
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 能力边界
|
|
48
|
+
|
|
49
|
+
| 能力 | 默认路径 | 说明 |
|
|
50
|
+
|------|----------|------|
|
|
51
|
+
| 报人 | `POST /hro/labor/delivery/create` | 创建 `REPORTED` 报人记录,手机号/证件号由服务端摘要接口脱敏 |
|
|
52
|
+
| 履约事件 | `POST /hro/labor/fulfillment/event` | 记录驻场到岗、入职、离场、异常等事件 |
|
|
53
|
+
| 考勤事实 | `POST /hro/labor/attendance/upsert` | 按 `projectId + deliveryId + period` upsert 出勤天数/工时 |
|
|
54
|
+
| 结算草表 | `POST /hro/labor/settlement/generate` | 按考勤、结算规则、借支生成草表;缺考勤/规则生成异常任务 |
|
|
55
|
+
| 正式工甲方名单预览 | `POST /hro/labor/settlement/formal-worker/statement/preview` | 按身份证、手机号、姓名匹配正式工送人记录,生成甲方名单匹配快照和待复核提成草表 |
|
|
56
|
+
|
|
57
|
+
## 安全要求
|
|
58
|
+
|
|
59
|
+
- 身份证号、手机号、原始履约 payload 都属于敏感信息;不要写入日志、PR 或公开回复。
|
|
60
|
+
- 报人、考勤、结算都是业务写动作;Agent 必须复述关键参数并取得确认。
|
|
61
|
+
- 不直接处理真实付款、开票、电子签或线下分账;P0 只到结算草表和异常任务。
|
|
62
|
+
- 当用户只想查看样例时,优先构造 `zax360 request post ... --data` 模板,不代替用户发送真实请求。
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zhianxin-openapi-hro-workflow
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "职安心 HRO 业务流程中心:流程定义、校验、模拟、发布、审批任务和实例查询。适用于 CLI zax360 hro workflow 及对应 MCP 工具。"
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: ["node", "npx"]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# HRO 业务流程中心
|
|
11
|
+
|
|
12
|
+
> 本 Skill 处理商户的请假、发单、接单、履约、结算和权限审批。它不处理 `/internal/agent/*` 的 AI WorkItem,也不得使用内部 workflow token。
|
|
13
|
+
|
|
14
|
+
## 身份与范围
|
|
15
|
+
|
|
16
|
+
1. 先执行 `zax360 auth login`,使用企业成员 Token。HRO 流程接口不接受 AK/SK 代替成员身份。
|
|
17
|
+
2. Token 必须包含目标操作的 workflow scope,并保存有效的企业成员授权引用。
|
|
18
|
+
3. 同一人有多个角色或范围时,所有命令都提交 `--hro-assignment-id <授权行ID>`;只有一个授权时服务端可自动解析。
|
|
19
|
+
4. 当前工作空间只能缩小查询范围。不要通过修改商户、项目、厂区、班组或授权行 ID 尝试扩大权限。
|
|
20
|
+
|
|
21
|
+
## 查询命令
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
zax360 hro workflow definition list --hro-assignment-id ASSIGNMENT_ID
|
|
25
|
+
zax360 hro workflow definition get DEF_ID --hro-assignment-id ASSIGNMENT_ID
|
|
26
|
+
zax360 hro workflow task list --status PENDING --hro-assignment-id ASSIGNMENT_ID
|
|
27
|
+
zax360 hro workflow instance list --biz-type LEAVE --hro-assignment-id ASSIGNMENT_ID
|
|
28
|
+
zax360 hro workflow instance get INSTANCE_ID --hro-assignment-id ASSIGNMENT_ID
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
部署层存在 `/openapi` 前缀时追加 `--path-prefix /openapi`。
|
|
32
|
+
|
|
33
|
+
## 定义、预检与发布
|
|
34
|
+
|
|
35
|
+
创建或更新前,先生成完整的 `definition + nodes + transitions` JSON,并保存到受控文件或通过 `--args-json` 提交:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
zax360 hro workflow definition create \
|
|
39
|
+
--hro-assignment-id ASSIGNMENT_ID \
|
|
40
|
+
--idempotency-key workflow-create-001 \
|
|
41
|
+
--args-json '{"definition":{},"nodes":[],"transitions":[]}'
|
|
42
|
+
|
|
43
|
+
zax360 hro workflow definition update DEF_ID \
|
|
44
|
+
--etag CHECKSUM \
|
|
45
|
+
--idempotency-key workflow-update-001 \
|
|
46
|
+
--hro-assignment-id ASSIGNMENT_ID \
|
|
47
|
+
--args-json '{"definition":{},"nodes":[],"transitions":[]}'
|
|
48
|
+
|
|
49
|
+
zax360 hro workflow validate DEF_ID --hro-assignment-id ASSIGNMENT_ID
|
|
50
|
+
zax360 hro workflow simulate DEF_ID --hro-assignment-id ASSIGNMENT_ID \
|
|
51
|
+
--args-json '{"bizType":"DEMAND_PUBLISH","projectId":"PROJECT_ID","amount":50000}'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
发布必须在校验和模拟通过后由具备独立发布权限的人明确确认:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
zax360 hro workflow publish DEF_ID \
|
|
58
|
+
--hro-assignment-id ASSIGNMENT_ID \
|
|
59
|
+
--idempotency-key workflow-publish-001
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
AI 可以生成草稿、解释路径和提示风险,但不得自动发布流程。
|
|
63
|
+
|
|
64
|
+
## 审批动作
|
|
65
|
+
|
|
66
|
+
先读取任务,使用响应中的最新 `taskVersion`。每次状态变更使用新的、可追踪的幂等键:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
zax360 hro workflow task approve TASK_ID --task-version 0 \
|
|
70
|
+
--comment "资料完整" --hro-assignment-id ASSIGNMENT_ID \
|
|
71
|
+
--idempotency-key task-approve-001
|
|
72
|
+
|
|
73
|
+
zax360 hro workflow task reject TASK_ID --task-version 0 \
|
|
74
|
+
--reject-to-node submit --comment "请补充凭证" \
|
|
75
|
+
--hro-assignment-id ASSIGNMENT_ID --idempotency-key task-reject-001
|
|
76
|
+
|
|
77
|
+
zax360 hro workflow task transfer TASK_ID --task-version 0 \
|
|
78
|
+
--target-user USER_ID --hro-assignment-id ASSIGNMENT_ID \
|
|
79
|
+
--idempotency-key task-transfer-001
|
|
80
|
+
|
|
81
|
+
zax360 hro workflow task add-sign TASK_ID --task-version 0 \
|
|
82
|
+
--target-user USER_ID --hro-assignment-id ASSIGNMENT_ID \
|
|
83
|
+
--idempotency-key task-add-sign-001
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
撤回实例也必须先读取最新 `rowVersion`:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
zax360 hro workflow instance cancel INSTANCE_ID --row-version 0 \
|
|
90
|
+
--comment "业务单据已撤回" --hro-assignment-id ASSIGNMENT_ID \
|
|
91
|
+
--idempotency-key instance-cancel-001
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
资金、工资、结算、付款和高权限角色变更必须由有权人员人工判断并确认。Agent 不得依据模拟结果自动同意、驳回或发布。
|
|
95
|
+
|
|
96
|
+
## 并发与错误处理
|
|
97
|
+
|
|
98
|
+
| 状态 | 处理 |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `401` | 重新执行成员登录;不要改用商户 AK/SK 绕过成员身份。 |
|
|
101
|
+
| `403` | 检查 workflow scope、成员状态、授权有效期和 `hroAssignmentId`,不要扩大权限。 |
|
|
102
|
+
| `409` | 重新读取定义摘要或任务/实例版本;不要盲目重试旧版本。 |
|
|
103
|
+
| 无审批人/死节点 | 修正角色范围或流程图,重新校验和模拟,禁止发布。 |
|
|
104
|
+
| 业务回写失败 | 保留流程终态,通过 V1 补偿队列查询和重试;不要直接改业务表。 |
|
|
105
|
+
| 重复提交 | 相同业务动作复用原幂等键;不同动作必须使用不同幂等键。 |
|
|
106
|
+
|
|
107
|
+
## MCP 边界
|
|
108
|
+
|
|
109
|
+
MCP 的定义查询、保存、校验、模拟、发布、任务处理和实例工具与上述 scope、工作空间、幂等和版本规则完全一致。调用写工具前必须展示业务摘要和影响,发布以及资金、结算、付款、权限审批必须等待人工确认。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zhianxin-openapi-leads
|
|
3
|
-
version: 1.
|
|
4
|
-
description: "
|
|
3
|
+
version: 1.2.2
|
|
4
|
+
description: "职安心线索:写入暂存 POST /v1/leads/ingest、查暂存 GET /v1/leads/staging/:id;CRM 跟进名单用 GET /v1/leads / zax360 lead list / MCP recruit.lead.search(CW_JOB_SEEKER)。仅在 semantic_intent 判定属于职安心线索提交/暂存或本商户求职者跟进名单后使用。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
7
7
|
bins: ["curl", "openssl"]
|
|
@@ -9,59 +9,42 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
# 线索 API(Agent)
|
|
11
11
|
|
|
12
|
-
> **前置条件:**
|
|
12
|
+
> **前置条件:** 先通过 `semantic_intent` 判定,再读 [`../zhianxin-openapi-shared/SKILL.md`](../zhianxin-openapi-shared/SKILL.md)。
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## 口径分流(必读)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
| 问题类型 | 正确入口 | 数据 |
|
|
17
|
+
|----------|----------|------|
|
|
18
|
+
| 开放平台写入/单条暂存状态 | `lead push` / `lead status` → ingest、`/v1/leads/staging/:id` | `OPENAPI_LEAD_STAGING` |
|
|
19
|
+
| 本商户「未联系/已标注」名单 | `GET /v1/leads` / `zax360 lead list` / MCP `recruit.lead.search` | `CW_JOB_SEEKER`(可按 `status`/`seekerStatus` 过滤) |
|
|
20
|
+
| 运营按商户名跨户明细 | toolbox `toolbox.recruit.leads_detail_by_merchant_name` | `CW_JOB_SEEKER` |
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-H "Content-Type: application/json; charset=utf-8" \
|
|
26
|
-
-H "Cw-Access-Key: $ACCESS_KEY" \
|
|
27
|
-
-H "Cw-Timestamp: $TS" \
|
|
28
|
-
-H "Cw-Signature: $SIG" \
|
|
29
|
-
--data-binary "$BODY"
|
|
30
|
-
```
|
|
22
|
+
- `STATUS`(跟进:未联系/已沟通/…)≠ `SEEKER_STATUS`(标注:0 未标注;>0 已标注)。
|
|
23
|
+
- **禁止**:用暂存表 `OPENAPI_LEAD_STAGING`(ingest / staging/:id)冒充 CRM 跟进明细。
|
|
24
|
+
- **不要禁止** `recruit.lead.search`——它绑 `GET /v1/leads` / `CW_JOB_SEEKER`,是商户侧正确 MCP 入口。
|
|
25
|
+
- 跨户按商户名查询不要指望 OpenAPI MCP(仅本商户凭证范围)。
|
|
26
|
+
- `GET /v1/jobseekers` 同源但现网可能 500,不要再作为 CRM 名单入口。
|
|
27
|
+
|
|
28
|
+
## 写入暂存 · POST /v1/leads/ingest
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
CLI:`zax360 lead push --content "..."`。
|
|
33
31
|
|
|
34
32
|
## 查询暂存 · GET /v1/leads/staging/:id
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
TS=$(date +%s)
|
|
40
|
-
SIGN_STRING="${ACCESS_KEY}"$'\n'"${SECRET_KEY}"$'\n'"${TS}"
|
|
41
|
-
SIG=$(printf '%s' "$SIGN_STRING" | openssl dgst -sha1 -hmac "$SECRET_KEY" -binary | openssl base64)
|
|
42
|
-
curl -sS "$BASE/v1/leads/staging/线索记录ID" \
|
|
43
|
-
-H "Cw-Access-Key: $ACCESS_KEY" \
|
|
44
|
-
-H "Cw-Timestamp: $TS" \
|
|
45
|
-
-H "Cw-Signature: $SIG"
|
|
46
|
-
```
|
|
34
|
+
CLI:`zax360 lead status --id <id>`。仅此路径读 `OPENAPI_LEAD_STAGING`。
|
|
47
35
|
|
|
48
|
-
##
|
|
36
|
+
## CRM 跟进名单 · GET /v1/leads
|
|
49
37
|
|
|
50
38
|
```bash
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
SIG=$(printf '%s' "$SIGN_STRING" | openssl dgst -sha1 -hmac "$SECRET_KEY" -binary | openssl base64)
|
|
54
|
-
curl -sS "$BASE/v1/leads?page=1&page_size=100" \
|
|
55
|
-
-H "Cw-Access-Key: $ACCESS_KEY" \
|
|
56
|
-
-H "Cw-Timestamp: $TS" \
|
|
57
|
-
-H "Cw-Signature: $SIG"
|
|
39
|
+
zax360 lead list --status 未联系 --page 1 --page-size 50
|
|
40
|
+
zax360 lead list --seeker-status 0 --from 2026-07-30 --to 2026-08-06
|
|
58
41
|
```
|
|
59
42
|
|
|
60
|
-
|
|
43
|
+
HTTP 日期参数为 `from`/`to`(YYYY-MM-DD),亦接受 `startDate`/`endDate`。
|
|
61
44
|
|
|
62
|
-
|
|
45
|
+
MCP:`recruit.lead.search`(参数 `status` / `seekerStatus` / `from` / `to` / `page` / `pageSize`)。
|
|
63
46
|
|
|
64
47
|
## 原则
|
|
65
48
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
49
|
+
- 写入前向用户确认内容与合规;密钥仅环境变量/本机配置注入。
|
|
50
|
+
- 暂存与 CRM 不得混用;catalog `leads_read` 只是发现名,不等于已授权。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zhianxin-openapi-ops
|
|
3
|
-
version: 1.
|
|
4
|
-
description: "职安心开放平台通用开放能力:合作方 payroll/labor/insurance 暂存、事件订阅与投递日志、闲鱼经营接口、触达回写、AI 报告、access token、静态 token、scope
|
|
3
|
+
version: 1.2.0
|
|
4
|
+
description: "职安心开放平台通用开放能力:合作方 payroll/labor/insurance 暂存、事件订阅与投递日志、闲鱼经营接口、触达回写、AI 报告、access token、静态 token、scope 申请。仅在 semantic_intent / knowledge://zhianxin/routing/semantic-intent-system-prompt 已判定用户语义明确属于这些职安心 OpenAPI 扩展能力后使用;不要用 payroll/token/scope 等字面词直接触发。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
7
7
|
bins: ["curl", "openssl"]
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
# 通用开放能力(Agent)
|
|
11
11
|
|
|
12
|
-
>
|
|
12
|
+
> 前置条件:先通过 `semantic_intent` 与知识库/system prompt 确认语义边界,再阅读 `../zhianxin-openapi-shared/SKILL.md`。本 Skill 只给已选中 OpenAPI 扩展能力后的 scope 与调用边界;请求/响应字段以开放控制台、Apifox 和仓库 README 为准。
|
|
13
13
|
|
|
14
14
|
## 建议调用方式
|
|
15
15
|
|
|
@@ -29,13 +29,15 @@ metadata:
|
|
|
29
29
|
| 事件日志 | `GET /v1/event/logs`、`GET /v1/event/deliveries` | `event.log:read` |
|
|
30
30
|
| 投递重试 | `POST /v1/event/deliveries/:id/retry` | `event.subscription:manage` |
|
|
31
31
|
| 闲鱼写入 | `POST /v1/xianyu/publish/precheck`、`/address/standardize`、`/fission`、`/content/risk-check`、`/post/result-callback`、`/metrics/account`、`/metrics/job`、`/penalty/callback` | `xianyu:write` |
|
|
32
|
-
| 闲鱼查询 | `GET /v1/xianyu/dashboard
|
|
32
|
+
| 闲鱼查询 | MCP `recruit.metrics.xianyuDashboard` + HTTP `GET /v1/xianyu/dashboard`(Admin only;返回 promotion/publishing/accounts/leads;可传 `start_date`/`end_date`);`GET /v1/xianyu/reconciliation` 仍为 stub | `xianyu:read` |
|
|
33
33
|
| 触达 | `POST /v1/lead/touch/callback`、`GET /v1/lead/touch/route` | `touch.callback:write` / `touch.route:read` |
|
|
34
34
|
| AI 报告 | `POST /v1/ai/report/generate`、`GET /v1/ai/report/:task_no`、`POST /v1/ai/report/:task_no/refresh` | `ai.report:write` / `ai.report:read` |
|
|
35
35
|
| 短期 access token | `POST /v1/auth/token` | AK/SK 签名 |
|
|
36
36
|
| 静态 token | `POST /v1/account/static-tokens`、`POST /v1/account/static-tokens/:id/revoke` | 账号管理权限 |
|
|
37
37
|
| scope 申请 | `POST /v1/account/scope-applications` | 商户账号 |
|
|
38
38
|
|
|
39
|
+
Agent 渐进发现:capability-manifest → skill_bindings(readonly)→ `list_tools`;闲鱼 MCP/HTTP dashboard 仅 Admin(`all_merchants`);具体只读覆盖口径以当前签名工具描述为准。
|
|
40
|
+
|
|
39
41
|
合作方暂存写入建议带 `Idempotency-Key`,长度不超过 128 字符。
|
|
40
42
|
|
|
41
43
|
## POST 模板
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zhianxin-openapi-readonly
|
|
3
|
-
version: 1.
|
|
4
|
-
description: "
|
|
3
|
+
version: 1.6.8
|
|
4
|
+
description: "如意开放平台与内部授权能力包的只读业务语义。覆盖企业、岗位、顾问、招聘经营指标、达人/抖音账号到期口径,以及用工企业、注册商户、直播招聘报名与直播来源商户合作申请的独立口径。只在结构化语义决策确认需要当前业务事实,且签名 capability grant 绑定本 Skill 后按需加载;不要用字面关键词直接触发。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
7
7
|
bins: ["curl", "openssl"]
|
|
@@ -11,10 +11,20 @@ metadata:
|
|
|
11
11
|
|
|
12
12
|
> **前置条件:** [`../zhianxin-openapi-shared/SKILL.md`](../zhianxin-openapi-shared/SKILL.md)。
|
|
13
13
|
|
|
14
|
+
## 语义路由边界
|
|
15
|
+
|
|
16
|
+
- 由当前主 Agent 根据完整会话、签名 capability grant 和工具语义决定是否加载本 Skill;不要再调用第二个 LLM/路由器做语义预分类,也不要只靠关键词命中。
|
|
17
|
+
- 本 Skill 只回答职安心 OpenAPI 与招聘业务只读数据问题。`/v1/account/info` 只能用于明确询问“当前职安心/OpenAPI 凭证是谁、有什么 scope、账号/配额/用量”等问题。
|
|
18
|
+
- 如果用户问的是“机器人是否进群、能否用 bot 身份发消息、能否把某人拉进群、飞书/IM 群聊成员”等,语义上属于 IM/机器人能力,不属于职安心 OpenAPI 账号查询;不要用当前职安心登录用户信息替代回答。
|
|
19
|
+
- 如果语义分类不确定,先追问要验证的系统和目标会话/群,而不是直接调用账号查询。
|
|
20
|
+
- 候选人与岗位的匹配评估必须同时具备一个明确的目标岗位(岗位 JD,或工具明确返回且足以确认职责与任职要求的岗位信息)和至少一名候选人的可评估材料(简历、经历与技能,或工具明确返回的等价字段)。候选人 ID、姓名或手机号只能用于定位记录,不能替代可评估材料;现有只读能力若只返回招聘基础字段,不得承诺能拉取完整履历或直接给出可靠评分。缺少任一侧时,如果可用工具中存在 `request_human_input`,必须实际调用它,而不是只在普通回答正文里列出问题:`toolType=decide`、`placement=inline`、`templateId=zhianxin.intake.inline_ask`、每次生成唯一 `resumeToken`,`payload.question` 写一个合并后的最小追问,`payload.missing` 至少逐项声明缺少的 `target_job` / `candidate_profile`;评分维度或权重没有给出时一并询问其是否有指定口径。在用户补充前不得调用知识搜索、Skill 发现、能力枚举、数据库或招聘数据工具,也不得先给出虚构评分。该规则按完整会话语义判断,不得在客户端实现关键词匹配。
|
|
21
|
+
|
|
14
22
|
## CLI `zax360 openapi` 与 HTTP 一一对照
|
|
15
23
|
|
|
16
24
|
Base URL = 开放平台 **`/v1`** 所用网关根(与 `auth login` 一致)。签名头:`Cw-Access-Key`、`Cw-Timestamp`、`Cw-Signature`。
|
|
17
25
|
|
|
26
|
+
声明 `cli.credentialMode=short_token` 的 canonical `zax360 capability call`(本期为闲鱼求职者画像)在已保存 AK/SK 且没有可用 token 时,自动签名调用 `POST /v1/auth/token` 换取并缓存 `zat_*`,业务请求走 Bearer。其他能力在 manifest 明确 token 兼容前继续使用 AK/SK 签名。AK/SK 是根凭证,不代表可跨商户;租户和 scope 以网关返回的 token 上下文为准。短 token 不能再次换票、创建/吊销静态 token、申请 scope 或进入商户账号自助面。
|
|
27
|
+
|
|
18
28
|
| 能力说明 | CLI | 实际请求 |
|
|
19
29
|
|----------|-----|----------|
|
|
20
30
|
| **企业列表** | `zax360 openapi company-list` | `GET /v1/companies?page=1&page_size=100` |
|
|
@@ -23,6 +33,10 @@ Base URL = 开放平台 **`/v1`** 所用网关根(与 `auth login` 一致)
|
|
|
23
33
|
| **快照:企业 + 在招岗位 + 能力目录** | `zax360 openapi info` | **并行** `GET /v1/companies?page=1&page_size=50`、`GET /v1/jobs?page=1&page_size=20`、`GET /v1/catalog/services` |
|
|
24
34
|
| **工种标签**(首屏去重,非字典表) | `zax360 openapi job-types` | `GET /v1/jobs?page=1&page_size=100`,对 `job_type` 去重输出 |
|
|
25
35
|
| **顾问列表** | `zax360 openapi consultant-list` | `GET /v1/consultants?page=1&page_size=100` |
|
|
36
|
+
| **求职者/CRM 线索** | `zax360 lead list` 或 MCP `recruit.lead.search` | `GET /v1/leads`(status/seekerStatus/consultantId/unassigned) |
|
|
37
|
+
| **直播场次/直播来源商户合作申请** | `zax360 openapi live-summary` / `live-lead-summary` | `GET /v1/live/summary` / `lead-summary`;后者不是求职报名 |
|
|
38
|
+
| **闲鱼求职者画像** | `zax360 capability call recruit.metrics.xianyuJobseekerProfile --args-json '{"topN":10}'` | `GET /v1/xianyu/jobseeker-profile?top_n=10` |
|
|
39
|
+
| **指标聚合** | **无 CLI**;Agent/OpenAPI MCP `recruit.metrics.aggregate` | `group_by`: job/company/status/consultant |
|
|
26
40
|
| **学历字典** | `zax360 openapi dict-degree` | **不请求 `/v1`**;需 `GET /openapi/dict/degree` 或 Apifox |
|
|
27
41
|
|
|
28
42
|
**汇总(非 openapi 子命令)**:`zax360 account info` 并行调用 `GET /v1/account/quota`、`/v1/account/usage`、`/v1/catalog/services`、`/v1/companies?page=1&page_size=10`、`/v1/jobs?page=1&page_size=10`。
|
|
@@ -31,6 +45,58 @@ Base URL = 开放平台 **`/v1`** 所用网关根(与 `auth login` 一致)
|
|
|
31
45
|
|
|
32
46
|
---
|
|
33
47
|
|
|
48
|
+
## 指标口径约定
|
|
49
|
+
|
|
50
|
+
- `/v1/companies` 和 MCP 对象 `recruit.customer.*` 表示 **用工企业/客户公司**(`CW_COMPANY`),不是注册商户。
|
|
51
|
+
- 经营总览 MCP `recruit.metrics.overview` 返回 `datasets.kpis` 和 `datasets.metricDefinitions`;回答统计问题时必须同时读取 `metricDefinitions` 里的 `businessMeaning`、`sourceTables`、`calculation`、`scopeRule`。
|
|
52
|
+
- `recruit.metrics.overview` 已直接提供招聘主线索的近窗趋势;查询招聘线索趋势时不再追加 `get-platform-overview`。后者是平台综合经营概览,不提供招聘主线索的日期趋势,不能作为该问题的第二个必做步骤。
|
|
53
|
+
- 招聘指标默认只能表述为“当前授权可见范围”。只有工具结果本身明确返回管理员或 `platform_all_merchants` 等全平台 scope 时,才可写“全平台”;不能从 Skill、能力授权或账号身份自行推断。
|
|
54
|
+
- 招聘时间窗工具的参数以各自签名 schema 为准,不能在工具之间类推名称:`recruit.metrics.overview` 与 `recruit.metrics.aggregate` 使用 `from`/`to`(含首尾,`YYYY-MM-DD`),不是 `start_date`/`end_date`。直播 `recruit.liveBroadcast.search` / `recruit.liveLead.search` 的 `from`/`to` 为 `YYYYMMDD` 或 `YYYY-MM-DD`。`overview` 可同时省略两者以使用近 7 天默认窗;`aggregate` 必须同时提供两者及 `group_by`。
|
|
55
|
+
- 按岗位汇总本周招聘进展时,优先用 `recruit.metrics.aggregate`,设置业务时区的周一为 `from`、当前日为 `to`、`group_by=job`。返回的 `new_cnt/contacted/interview/hired/wechat_added/unassigned` 是 `CW_JOB_SEEKER` 聚合;其中 `interview=SEEKER_STATUS=1` 条数,不是 `interviewStage` 阶段分布。该结果没有阻塞原因、下周风险或预测字段;用户同时要求这些内容时,只交付有证据的聚合并明确缺口,不得补写或用更新时间替代。
|
|
56
|
+
- 问“注册商户/商户数量/平台开户商户”时优先使用 `merchants` 或 `active_merchants`;不要用 `customers`。
|
|
57
|
+
- 问“用工企业/客户公司/企业数量”时使用 `customers`。
|
|
58
|
+
- `leads` 是线索记录条数;若用户要求去重求职人数,不要直接把 `leads` 当作去重人数。
|
|
59
|
+
- `recruit.liveLead.search` / `GET /v1/live/lead-summary` 只统计独立合作申请收件箱 `CW_LEADS` 中的直播来源记录,业务含义是商户合作申请/平台招商商机,不是求职者岗位报名;不得用于报名率、T+1招聘线索或求职转化漏斗。`lead_count` 是合作申请记录条数;`unread_count` 只表示 `IS_READ` 为 0 或 NULL,即记录尚未打开,不能推导为未处理、未联系、未跟进或未分配;`lead_count - unread_count` 也只能得到非未读记录数,不能改称已处理数。判断跟进状态必须读取 `CW_LEADS.STATUS`(如 `NoContact` / `Contacted`)或处理流水。
|
|
60
|
+
- `recruit.metrics.overview.datasets.liveLeadT1` 统计 `CW_JOB_SEEKER` 经 `CW_CELEBRITY` 归因的直播招聘报名/求职线索;它才用于 T+1 招聘线索和报名转化分析,不得与 `CW_LEADS` 合并、相减、计算倍率或串成同一漏斗。用户只说“直播线索”且上下文不能确定对象时,应先澄清是“直播招聘报名”还是“直播来源商户合作申请”;若已有两个口径,必须分别命名、分别解释,不能用大小关系推导转化。
|
|
61
|
+
- `GET /v1/live/summary` / `recruit.liveBroadcast.search` 的 `live_count`、UV/PV 来自分析层;`room_count`、房间时长/账号来自房间层(仅 `START_TIME` 非空且 `IS_HIDDEN` 为空或 0)。分析层 `freshness.analysis.empty_in_window=true` 且 `room_count>0` 时,写「分析表当日未出数、房间层已有开播」,禁止写「今日无直播」。不得用 `room_count` 填 `live_count`,也不得用房间层合成 UV。平均时长分母必须是 `rooms_with_duration_count`。
|
|
62
|
+
- 展示建议(由主 Agent 按会话语义选用,不进客户端规则、不作关键词表):意图仅为确认开播事实时优先 `recruit.liveBroadcast.search`,开口用房间层 `room_count`(开播房间数)和 `freshness.rooms`;分析层空则说明观看人数次日出数。意图为直播经营分析时建议分块:开播记录(房间层)|观看与招聘报名转化(分析层与 `CW_JOB_SEEKER`,T+1)|直播来源商户合作申请(`recruit.liveLead.search`,`CW_LEADS` 独立收件箱)。`liveLead` 的 `live_room_count` / `celebrity_count` 是产生过合作申请的间/达人,不得标成与房间层相同的「直播间数/达人数」,也不得写成会被理解成开播记录的「关联直播间/关联达人」。用户同时问招聘线索、私信或转化时才追加 `recruit.lead.search`,且必须标明它是招聘主线索,不是商户合作申请。
|
|
63
|
+
- 直播聚合快照只支持描述所选时间窗内的规模、字段值和可复核计算。没有历史同期、目标值或基准时,不评价规模高低、活跃度、转化好坏或异常程度;没有用户级路径和归因明细时,不把多个聚合计数串成已证实的转化漏斗或因果链路。
|
|
64
|
+
- **闲鱼经营快照** MCP `recruit.metrics.xianyuDashboard`(Admin:`all_merchants` + `xianyu:read`;HTTP `GET /v1/xianyu/dashboard`):一次返回 `promotion` / `publishing` / `accounts` / `leads` 四块,不是仅推广四字段。`scope=platform_all_merchants`;`publishing`/`leads` 的 `snapshotScope=platform_all_merchants`;`accounts` 为当前全平台快照(`snapshotScope=platform_current`),勿当成窗口累计。
|
|
65
|
+
- `promotion` 来自 `CW_XIANYU_PROMOTION_METRICS`;近窗全 0 时必须先看 `promotion.sourceMaxRecordDate`。若该日期早于 `startDate`,文案写「推广指标断更」,禁止写「确认无投放 / 暂无经营数据」。
|
|
66
|
+
- 问「闲鱼数据如何 / 怎么样」时,至少同时汇报:推广新鲜度(含 `sourceMaxRecordDate`)+ 线索(`leads.total`)或发帖(`publishing.postsPublished`)之一;不得只拿 `promotion` 零值收工。
|
|
67
|
+
- 在生产网关仍返回扁平四字段时,不得假装已有嵌套块;应如实说明工具形态,并可用 `data_analytics` 补查线索/发帖。
|
|
68
|
+
- **闲鱼求职者画像** canonical capability `recruit.metrics.xianyuJobseekerProfile` 走 HTTP/CLI,不开放 MCP。普通凭证由网关绑定当前商户,接口不接受 `merchantId`、`merchantName` 等租户选择器;只有返回 `scope.kind=platform_all_merchants` 时才可称全平台。
|
|
69
|
+
- `followStatus` 是跟进状态,`seekerStatus` 是业务标注状态,二者不得混写;`uniqueCandidateCount` 是非空手机号去重后的纯计数,不能下钻或反推个人。
|
|
70
|
+
- `jobTargets` 来自线索关联岗位,是目标岗位代理画像;`targetWorkArea` 是岗位工作地,不是候选人居住地。不得据此声称掌握求职者简历、经验、技能、年龄或性别。
|
|
71
|
+
- 回答“闲鱼求职者画像分析”时必须同时读取 `dataQuality.fields` 和 `unavailableDimensions`;字段高缺失或维度不可用时,将缺口写进结论,不得由模型补全。
|
|
72
|
+
- `from/to` 必须同时提供且最长 366 天;均省略时默认近 30 天。`jobTargets.truncated=true` 时不得把返回的 Top N 写成全量岗位分布。
|
|
73
|
+
|
|
74
|
+
### 内部 toolbox 能力包
|
|
75
|
+
|
|
76
|
+
- `toolbox.recruit.customer_lead_summary` 接受业务主体自然名称与可选时间窗;未提供时间窗时由服务端统一使用当前业务日向前 7 天至当前业务日。它一次返回用工企业招聘线索、注册商户招聘线索和直播来源商户合作申请三个独立口径。适合用户询问某一主体的整体数据情况;不要先用无名称筛选能力的线索列表替代它。
|
|
77
|
+
- 用工企业(company)统计岗位所属企业下的 `CW_JOB_SEEKER` 求职报名;注册商户(merchant)统计商户名下的 `CW_JOB_SEEKER` 求职报名;直播(live)统计商户名下达人产生的 `CW_LEADS` 商户合作申请。三个数字来源、业务对象和责任团队不同,不得互相替代、由一个外推另一个或合并为同一漏斗。
|
|
78
|
+
- 自然名称匹配由工具的结构化返回确认。多个匹配候选时使用 Ask 澄清;零值是成功事实,但只能说明已查询名称、时间窗与授权范围内为零,不能断言实体不存在。
|
|
79
|
+
- 先依据 capability pack 的 purpose 与本 Skill 理解业务目标;需要具体参数和返回 schema 时再调用 `list_tools(capability_id)`,执行必须通过统一 Rust 网关。Skill 不授予权限,也不替代 manifest、PlanBinding、PII 投影或 ToolFact 验收。
|
|
80
|
+
- **明细下钻**:汇总工具只给条数。要「未联系/已标注是谁」:运营用 `toolbox.recruit.leads_detail_by_merchant_name`;商户用 `zax360 lead list` / MCP `recruit.lead.search`(可 `status`/`seekerStatus`/`consultantId`/`unassigned`)。`STATUS`=跟进,`SEEKER_STATUS`=标注。入口是 `GET /v1/leads`;暂存仅 ingest / staging/:id。
|
|
81
|
+
- **运营 toolbox 路由(Admin grant;仅 MCP)**:`get-platform-overview` / `get-user-statistics-by-date` / `get-interview-statistics-by-date` / `leads_by_company_name` / `get-job-statistics-by-merchant` / `get-hot-urgent-jobs` / `toolbox.live.summary`/`leads` / `search-users-by-consultant` / `search-interviews-by-mobile`。
|
|
82
|
+
- **刻意不暴露**:`toolbox.live.overview`(无商户限定);`toolbox.recruit.leads_by_merchant_name`(与 customer_lead_summary 商户口径重复);`get-celebrity-performance` / `get-account-operation-audit`(Local 但未 curated)。
|
|
83
|
+
- Toolbox 共享 key 不携带租户身份,只能作为内部 Admin grant 的传输凭证;不得下发给商户,也不得把它描述成客户级权限隔离。服务端 curated facade 与客户端 allowlist 都必须过滤未暴露工具,客户画像走租户绑定的 OpenAPI/CLI。
|
|
84
|
+
- **顾问负载**:MCP `recruit.metrics.aggregate`(`group_by=consultant`);未分配名单 `recruit.lead.search`(`unassigned=true`)或 `zax360 lead list --unassigned`。
|
|
85
|
+
- **Admin 问数**:仅当 capability-manifest 含 `zhianxin.internal.data_analytics`(purpose=`internal_database_readonly`)时,用该包实际授予的只读库工具(常见为 `database.schema.list` / `database.table.describe` / `database.query.readonly`,及若已授予的 `metric.query`;以 manifest 为准,未授予不得假装)。表级计数、到期窗口、按列过滤等 SQL 口径选该包,不要用 toolbox(purpose=`internal_business_analytics`)的 curated 汇总冒充。
|
|
86
|
+
- **达人/抖音账号到期(`cw_celebrity`)**:
|
|
87
|
+
- 声称「抖音」时 SQL 必须 `PLATFORM IN ('抖音','douyin')`;这是本 Skill 已确认的生产值域,已加载本节时不要再做 knowledge 搜索、schema 广搜或 `SELECT DISTINCT PLATFORM`。无平台过滤则不得以「抖音」作主语。
|
|
88
|
+
- 默认「快过期」:`STATUS = 'Normal'` AND `EXPIRY_TYPE = 'LIMITED'` AND `EXPIRY_TIME > NOW()` AND `EXPIRY_TIME <= DATE_ADD(NOW(), INTERVAL 7 DAY)`;时间基准与库会话时区一致,回答时注明查询时点。
|
|
89
|
+
- 用户明确指定自然月并问“哪些要过期”时,该月窗口优先于默认 7 天:使用 `EXPIRY_TIME > NOW()` AND `EXPIRY_TIME >= '<月初>'` AND `EXPIRY_TIME < '<下月月初>'`,先做总数和按 `EXPIRY_TIME ASC` 的同口径明细;若用户问的是历史“到期记录”而非未来“要过期”,才去掉 `> NOW()`。不要调用 Bash 或第二套知识工具处理结果。
|
|
90
|
+
- 明细超过模型可见行数时,回答总数和当前证据实际覆盖的最早到期账号,并明确名单尚未完整覆盖;不得为了补齐名单重复做相同查询,也不得把部分明细声称为全部。
|
|
91
|
+
- 用户后续要求 Excel/xlsx 完整名单,且当前主 Agent 提供 `create_excel_file` 时,先用同口径 `COUNT(*)` 确认总数。总数为 0 时直接说明没有匹配账号,不生成空工作簿;仅当总数为 1 至 200、未超过 `database.query.readonly` 的签名上限时,才直接使用 `source` 模式:`toolName=database.query.readonly`,`expectedRows=先前总数`;`args.sql` 复用上述同口径、带稳定排序且**不自行追加 `LIMIT`** 的完整明细 SQL,`args.limit=200`,统一网关传输信封的明细路径为 `result.structuredContent.rows`。`create_excel_file` 会在发布前校验未截断且实际行数等于 `expectedRows`;工具成功后还应确认返回的 `rows` 等于先前总数,才可称为完整名单。若总数超过 200、返回截断或数量不一致,当前能力不能完成完整导出:明确说明缺口,不生成貌似完整的文件,等待 OpenAPI 提供签名分页或专用导出能力。不要先把同一批明细拉入模型再用 `rows` 拼表,也不要用 Bash 或本地脚本生成文件。
|
|
92
|
+
- 同时报 7/30 天:用独立 `SUM(CASE)`,使 `expiring_30d` **含** 7 天;禁止互斥 `CASE` 桶数字却写「含上述」。
|
|
93
|
+
- 用户只问默认“快过期”时,主答报 7 天主数字和口径;用户明确问“哪些”或指定月份时,必须按上两条返回有证据的明细,不能套用只报数字的默认模板。
|
|
94
|
+
- 推荐模板:`SELECT SUM(CASE WHEN EXPIRY_TIME > NOW() AND EXPIRY_TIME <= DATE_ADD(NOW(), INTERVAL 7 DAY) THEN 1 ELSE 0 END) AS expiring_7d, SUM(CASE WHEN EXPIRY_TIME > NOW() AND EXPIRY_TIME <= DATE_ADD(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) AS expiring_30d FROM cw_celebrity WHERE PLATFORM IN ('抖音','douyin') AND STATUS = 'Normal' AND EXPIRY_TYPE = 'LIMITED'`。
|
|
95
|
+
- **Admin 问数不得回退**:商户无 data_analytics grant。勿回退本地 db_query / zax360 CLI;勿用 knowledge_search / 桌面搜索代替权威库读取。
|
|
96
|
+
- **直播聚合字段边界**:`toolbox.live.dashboard_snapshot` 中 `live_room_count/room_count`、`live_count` 是不同字段;`room_duration_seconds/room_duration_hours` 与 `duration_seconds` 也是不同来源口径。字段定义未在结果中对齐时,保留原字段标签并说明差异,不能都改称“直播场次”或“直播总时长”。`rooms_with_duration_count` 是有时长记录数;计算有时长记录的平均值时以它为分母,不能声称现有证据无法计算。
|
|
97
|
+
- **缺口**:HRO 多数只读契约未闭合;`finance_metrics`/`ops_metrics` 无正式 MCP binding。
|
|
98
|
+
- **写路径未暴露**:线索推进/派单无真 HTTP;勿假装已有。
|
|
99
|
+
|
|
34
100
|
## REST 路径速查(自建客户端 / cURL)
|
|
35
101
|
|
|
36
102
|
以下为 **GET**,签名字符串均为三行:`accessKey\nsecretKey\ntimestamp`。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zhianxin-openapi-shared
|
|
3
|
-
version: 1.
|
|
4
|
-
description: "职安心开放平台通用约定:签名字符串、cURL
|
|
3
|
+
version: 1.1.0
|
|
4
|
+
description: "职安心开放平台通用约定:签名字符串、cURL 模板、环境与安全。仅在 semantic_intent / knowledge://zhianxin/routing/semantic-intent-system-prompt 已判定用户语义明确属于职安心 OpenAPI 调用或签名任务后使用;不要用关键词命中直接触发。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
7
7
|
bins: ["curl", "openssl"]
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
# 职安心开放平台 · 共享约定(Agent)
|
|
11
11
|
|
|
12
|
-
> 每个能力对应一份 `SKILL.md
|
|
12
|
+
> 每个能力对应一份 `SKILL.md`。触发前必须先使用能力清单里的 `semantic_intent` 和 `knowledge://zhianxin/routing/semantic-intent-system-prompt` 做语义判断;本文件只提供已选中 OpenAPI 能力后的签名与环境约定。具体 HTTP 路径与 body 模型以 **Apifox** 及仓库根目录 **README /docs** 为准。
|
|
13
13
|
|
|
14
14
|
## 前置条件
|
|
15
15
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zhianxin-openapi-sync
|
|
3
|
-
version: 1.
|
|
4
|
-
description: "职安心开放平台同步写入:CLI zax360 company/job sync 与 POST /v1/sync/companies、/v1/sync/jobs、/v1/sync/jobs/recruit-status
|
|
3
|
+
version: 1.1.0
|
|
4
|
+
description: "职安心开放平台同步写入:CLI zax360 company/job sync 与 POST /v1/sync/companies、/v1/sync/jobs、/v1/sync/jobs/recruit-status。仅在 semantic_intent / knowledge://zhianxin/routing/semantic-intent-system-prompt 已判定用户语义明确属于职安心企业、岗位或招聘状态同步后使用;不要用“企业/岗位/同步”等关键词直接触发。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
7
7
|
bins: ["curl", "openssl"]
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
# 企业与岗位同步(Agent)
|
|
11
11
|
|
|
12
|
-
>
|
|
12
|
+
> 前置条件:先通过 `semantic_intent` 与知识库/system prompt 确认用户确实要执行职安心 OpenAPI 同步写入,再阅读 `../zhianxin-openapi-shared/SKILL.md`。写接口必须确认用户拥有对应 AK/SK 与写入 scope。
|
|
13
13
|
|
|
14
14
|
## 优先用 CLI
|
|
15
15
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"skills": [
|
|
4
|
+
{
|
|
5
|
+
"name": "zhianxin-openapi-hro-labor",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"path": "skills/zhianxin-openapi-hro-labor/SKILL.md",
|
|
8
|
+
"sha256": "4eb95e433ef8ffa606baec432811ce186d94ac5c5cdb44c2aaa565a3246ae903"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "zhianxin-openapi-hro-workflow",
|
|
12
|
+
"version": "1.0.0",
|
|
13
|
+
"path": "skills/zhianxin-openapi-hro-workflow/SKILL.md",
|
|
14
|
+
"sha256": "5a13ab6207bf8781db1d9f47246457909c824842daef29f796ef7865d0944d3f"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "zhianxin-openapi-leads",
|
|
18
|
+
"version": "1.2.2",
|
|
19
|
+
"path": "skills/zhianxin-openapi-leads/SKILL.md",
|
|
20
|
+
"sha256": "a64889f8549108e5c9f5c4ca055acd3e7e92edb3a176040fc6794adfbbb71109"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "zhianxin-openapi-ops",
|
|
24
|
+
"version": "1.2.0",
|
|
25
|
+
"path": "skills/zhianxin-openapi-ops/SKILL.md",
|
|
26
|
+
"sha256": "d26cfea0d0a86413d3206a7f591ef77376d148acca7d440341688bc5b8290f01"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "zhianxin-openapi-readonly",
|
|
30
|
+
"version": "1.6.8",
|
|
31
|
+
"path": "skills/zhianxin-openapi-readonly/SKILL.md",
|
|
32
|
+
"sha256": "6f6a6202579bc68c3a5b69f1e4003869b3af30753948ffe5d44257a365a85454"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "zhianxin-openapi-shared",
|
|
36
|
+
"version": "1.1.0",
|
|
37
|
+
"path": "skills/zhianxin-openapi-shared/SKILL.md",
|
|
38
|
+
"sha256": "730da8a8342cef620fee1c3a0dd59f867a2d2302645e0c24e83ab22e2f9e7ee6"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "zhianxin-openapi-sync",
|
|
42
|
+
"version": "1.1.0",
|
|
43
|
+
"path": "skills/zhianxin-openapi-sync/SKILL.md",
|
|
44
|
+
"sha256": "2042665519d58dac51c33f13df54efb80a0f7b0f68ef665547c076b949bf43a8"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|