skillfree 0.1.64 → 0.1.66

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/SKILL.md CHANGED
@@ -156,8 +156,11 @@ skillfree pilot --type pharma --target "IL-4" --status "III期临床"
156
156
  # 翻页(默认第1页)
157
157
  skillfree pilot --type pharma --target "STAT6" --page 2
158
158
 
159
- # 查询药物详情(需要先查管线获取 keyid)
160
- skillfree pilot --type pharma --model bcpm-drug-detail --keyid <keyid>
159
+ # 查询药物详情(自动查 keyid)
160
+ skillfree pilot --type pharma --model bcpm-drug-detail --drug "替雷利珠单抗"
161
+
162
+ # 查询全球临床试验结果(自动查 keyid)⭐ 新增
163
+ skillfree pilot --type pharma --model bcpm-clinical-result --drug "信迪利单抗"
161
164
 
162
165
  # 查询中国临床试验
163
166
  skillfree pilot --type pharma --model bcpm-cn-clinical --drug "度普利尤单抗"
@@ -175,29 +178,50 @@ skillfree pilot --type pharma --target "STAT6" --output result.json
175
178
  | model | 说明 | 主要参数 |
176
179
  |-------|------|---------|
177
180
  | `bcpm-drug-dev`(默认)| 全球药物研发管线 | --target / --drug / --indication / --company / --status |
178
- | `bcpm-drug-detail` | 药物详细信息(含销售额、适应症进展)| --keyid(必填)|
181
+ | `bcpm-drug-detail` | 药物详细信息(含销售额、适应症进展)| --drug(自动查 keyid)或 --keyid |
182
+ | `bcpm-clinical-result` | 全球临床试验结果 ⭐ 新增 | --drug(自动查 keyid)或 --keyid |
179
183
  | `bcpm-cn-clinical` | 中国临床试验注册 | --drug / --sponsor / --indication |
180
184
  | `bcpm-cn-evaluation` | 中国药品申报/评审 | --drug / --company |
181
185
 
182
- **典型两步工作流:**
186
+ **典型工作流(已简化):**
183
187
 
184
188
  ```bash
189
+ # 方式一:直接用药品名(推荐)⭐
190
+ skillfree pilot --type pharma --model bcpm-clinical-result --drug "信迪利单抗"
191
+
192
+ # 方式二:手动两步(如果需要精确控制)
185
193
  # 第一步:查管线,找到目标药物的 keyid
186
194
  skillfree pilot --type pharma --target "STAT6"
187
195
  # 输出中会显示每个药物的 keyid
188
196
 
189
- # 第二步:用 keyid 查详情(含全球销售额、各适应症研发现状)
197
+ # 第二步:用 keyid 查详情
190
198
  skillfree pilot --type pharma --model bcpm-drug-detail --keyid 0b0566619e92554c97b384c0e4c9ddfd
191
199
  ```
192
200
 
193
201
  ---
194
202
 
195
- ## 🌐 实时搜索(Tavily
203
+ ## 🌐 实时搜索(search
196
204
 
197
205
  ```bash
198
- skillfree pilot --type chat --prompt "搜索最新的 STAT6 抑制剂临床进展" --model tavily-search
206
+ # 基本搜索(默认返回 5 条结果 + AI 摘要)
207
+ skillfree pilot --type search --prompt "STAT6抑制剂最新临床进展"
208
+
209
+ # 指定返回结果数量
210
+ skillfree pilot --type search --prompt "2025年中国新能源汽车市场规模" --limit 10
211
+
212
+ # 保存完整 JSON 结果
213
+ skillfree pilot --type search --prompt "度普利尤单抗竞品分析" --output result.json
214
+
215
+ # 使用高级搜索(更深度,费用略高)
216
+ skillfree pilot --type search --prompt "PD-1抑制剂市场格局" --model tavily-search-advanced
199
217
  ```
200
218
 
219
+ **输出内容:** AI 摘要 + 各结果的标题、链接、摘要片段
220
+
221
+ **可用模型:**
222
+ - `tavily-search`(默认)— 标准搜索
223
+ - `tavily-search-advanced` — 深度搜索,结果更全面
224
+
201
225
  ---
202
226
 
203
227
  ## 🔧 其他常用命令
@@ -224,6 +248,7 @@ skillfree update # 更新到最新版本
224
248
  | 识别PDF/OCR扫描件 | `skillfree pilot --type ocr --file <路径>` |
225
249
  | 做PPT/演示文稿 | `skillfree ppt "..."` |
226
250
  | 查药物/查靶点/查临床 | `skillfree pilot --type pharma --target "..."` |
251
+ | 搜索网页/查新闻/实时信息 | `skillfree pilot --type search --prompt "..."` |
227
252
  | 查账户余额 | `skillfree balance` |
228
253
 
229
254
  ---
package/bin/skillfree.js CHANGED
@@ -37,7 +37,7 @@ auth
37
37
  program
38
38
  .command('pilot')
39
39
  .description('AI 智能调度:chat | image | video | ocr | presentation | pharma')
40
- .option('--type <type>', '调用类型: chat | image | video | ocr | presentation | pharma', 'chat')
40
+ .option('--type <type>', '调用类型: chat | image | video | ocr | presentation | pharma | search', 'chat')
41
41
  .option('--prompt <text>', '输入提示词')
42
42
  .option('--file <path>', '输入文件路径(ocr 用)')
43
43
  .option('--output <path>', '输出文件路径')
@@ -53,6 +53,7 @@ program
53
53
  .option('--sponsor <sponsor>', '医药查询:申办单位(临床试验用)')
54
54
  .option('--keyid <keyid>', '医药查询:药物 keyid(drug-detail 用)')
55
55
  .option('--page <page>', '医药查询:页码', '1')
56
+ .option('--limit <n>', '搜索结果数量(search 用,默认 5)')
56
57
  .action(async (flags) => {
57
58
  const { pilot } = require('../scripts/commands/pilot')
58
59
  await pilot(flags).catch(e => { console.error('❌', e.message); process.exit(1) })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfree",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "🦞 一个 API,满足所有龙虾技能需求",
5
5
  "main": "bin/skillfree.js",
6
6
  "bin": {
@@ -156,6 +156,53 @@ async function pilot(flags) {
156
156
  return
157
157
  }
158
158
 
159
+ // ── SEARCH(Tavily 实时网页搜索)────────────────────────────────────────────
160
+ if (type === 'search') {
161
+ if (!prompt) throw new Error('--prompt 是必需的(搜索关键词)')
162
+ const searchModel = model || 'tavily-search'
163
+ const apiKey = getApiKey()
164
+
165
+ console.log(`🔍 搜索中:${prompt}\n`)
166
+
167
+ const res = await fetch(`${BASE_URL}/v1/search`, {
168
+ method: 'POST',
169
+ headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
170
+ body: JSON.stringify({
171
+ model: searchModel,
172
+ query: prompt,
173
+ include_answer: true,
174
+ include_images: false,
175
+ max_results: flags.limit ? Number(flags.limit) : 5,
176
+ }),
177
+ })
178
+
179
+ const data = await res.json()
180
+ if (!res.ok || data.error) throw new Error(data.error?.message || `HTTP ${res.status}`)
181
+
182
+ // 格式化输出
183
+ if (data.answer) {
184
+ console.log(`💡 AI 摘要\n${'─'.repeat(50)}`)
185
+ console.log(data.answer)
186
+ console.log()
187
+ }
188
+
189
+ if (data.results?.length) {
190
+ console.log(`📄 搜索结果(共 ${data.results.length} 条)\n${'─'.repeat(50)}`)
191
+ data.results.forEach((r, i) => {
192
+ console.log(`${i + 1}. ${r.title}`)
193
+ console.log(` 🔗 ${r.url}`)
194
+ if (r.content) console.log(` ${r.content.slice(0, 200).replace(/\n/g, ' ')}...`)
195
+ console.log()
196
+ })
197
+ }
198
+
199
+ if (output) {
200
+ fs.writeFileSync(output, JSON.stringify(data, null, 2))
201
+ console.log(`✅ 完整结果已保存到 ${output}`)
202
+ }
203
+ return
204
+ }
205
+
159
206
  // ── PRESENTATION(AI 演示文稿,异步轮询)────────────────────────────────────
160
207
  if (type === 'presentation') {
161
208
  const { createPresentation } = require('./presentation')
@@ -177,9 +224,38 @@ async function pilot(flags) {
177
224
  if (flags.title) search.title = flags.title
178
225
  if (flags.sponsor) search.sponsor = flags.sponsor
179
226
 
180
- const inputs = pharmaModel === 'bcpm-drug-detail'
181
- ? { keyid: flags.keyid ?? prompt } // detail 模式需要 keyid
182
- : { search, page: Number(flags.page ?? 1) }
227
+ let inputs
228
+ let keyid = flags.keyid ?? prompt
229
+
230
+ // bcpm-clinical-result / bcpm-drug-detail 特殊处理:如果没有 keyid 但有 drug,自动查询获取 keyid
231
+ if ((pharmaModel === 'bcpm-clinical-result' || pharmaModel === 'bcpm-drug-detail') && !keyid && search.drug_name) {
232
+ console.log(`🔍 自动查询 "${search.drug_name}" 的 keyid...`)
233
+ const drugRes = await post('/pharma', {
234
+ model: 'bcpm-drug-dev',
235
+ inputs: { search: { drug_name: search.drug_name }, page: 1 }
236
+ })
237
+ if (drugRes.error) {
238
+ console.error(`❌ 查询药物失败: ${drugRes.error.message}`)
239
+ return
240
+ }
241
+ const list = drugRes.data?.list || []
242
+ if (list.length === 0) {
243
+ console.error(`❌ 未找到药物: ${search.drug_name}`)
244
+ return
245
+ }
246
+ keyid = list[0].keyid
247
+ console.log(`✅ 找到药物: ${list[0].drug_name_cn || list[0].drug_name} (keyid: ${keyid})\n`)
248
+ }
249
+
250
+ if (pharmaModel === 'bcpm-drug-detail' || pharmaModel === 'bcpm-clinical-result') {
251
+ if (!keyid) {
252
+ console.error(`❌ ${pharmaModel} 需要 --keyid 或 --drug 参数`)
253
+ return
254
+ }
255
+ inputs = { keyid, page: Number(flags.page ?? 1) }
256
+ } else {
257
+ inputs = { search, page: Number(flags.page ?? 1) }
258
+ }
183
259
 
184
260
  const res = await post('/pharma', { model: pharmaModel, inputs })
185
261
 
@@ -220,6 +296,20 @@ async function pilot(flags) {
220
296
  console.log(` • ${s.year} ${s.company}: ${s.sales}`)
221
297
  })
222
298
  }
299
+ } else if (pharmaModel === 'bcpm-clinical-result') {
300
+ // 临床试验结果
301
+ const list = data.list || []
302
+ const total = data.total || list.length
303
+ console.log(`共 ${total} 条结果,当前显示 ${list.length} 条\n`)
304
+ list.slice(0, 10).forEach((item, i) => {
305
+ console.log(`${i + 1}. [${item.registration_number}] ${item.title}`)
306
+ console.log(` 适应症: ${(item.indication || []).join(', ')}`)
307
+ console.log(` 分期: ${item.phase}`)
308
+ if (item.representative_data) console.log(` 代表性数据: ${item.representative_data}`)
309
+ if (item.result_tendency) console.log(` 结果趋势: ${item.result_tendency}`)
310
+ if (item.abstract?.length) console.log(` 摘要: ${item.abstract[0].slice(0, 150)}...`)
311
+ console.log('')
312
+ })
223
313
  } else {
224
314
  const list = data.list || []
225
315
  const total = data.total || list.length
@@ -260,7 +350,7 @@ async function pilot(flags) {
260
350
  return
261
351
  }
262
352
 
263
- throw new Error(`不支持的类型: ${type}\n可选: chat | image | video | ocr | presentation | pharma`)
353
+ throw new Error(`不支持的类型: ${type}\n可选: chat | image | video | ocr | presentation | pharma | search`)
264
354
  }
265
355
 
266
356
  module.exports = { pilot }