UpgradeAITerm.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Http\Resources\AiModelResource;
  4. use App\Services\AIAssistant\AITermService;
  5. use App\Services\AIModelService;
  6. use App\Services\AuthService;
  7. use App\Services\TermService;
  8. use Illuminate\Console\Command;
  9. use Illuminate\Support\Facades\Cache;
  10. class UpgradeAITerm extends Command
  11. {
  12. // 断点缓存键:记录 channel 模式下最后一个已完成的术语游标 ['index'=>int,'word'=>string],
  13. // 使命令可重入——中断后重跑自动跳过已完成的术语
  14. private const CURSOR_KEY = 'upgrade:ai.term:cursor';
  15. /**
  16. * The name and signature of the console command.
  17. * php artisan upgrade:ai.term {model} --id=e07bf5b8-bd81-4f0a-9d2c-8e0128b954d7
  18. * php artisan upgrade:ai.term {model} --channel={channelId}
  19. *
  20. * @var string
  21. */
  22. protected $signature = 'upgrade:ai.term
  23. {model : LLM 模型 id}
  24. {--word= : 保留参数}
  25. {--channel= : 遍历术语表,逐词生成/更新到该 channel}
  26. {--id= : 仅处理该术语 guid}
  27. {--thinking= : 开启/关闭 deepseek thinking,true | false}
  28. {--fresh : 清除断点缓存,从头开始}';
  29. /**
  30. * The console command description.
  31. *
  32. * @var string
  33. */
  34. protected $description = 'Command description';
  35. protected AiModelResource $model;
  36. protected $modelToken;
  37. protected $workChannel;
  38. protected $accessToken;
  39. protected ?bool $thinking = null;
  40. /**
  41. * Create a new command instance.
  42. *
  43. * @return void
  44. */
  45. public function __construct(
  46. protected AIModelService $modelService,
  47. protected TermService $termService,
  48. protected AITermService $aiTermService
  49. ) {
  50. parent::__construct();
  51. }
  52. /**
  53. * Execute the console command.
  54. *
  55. * @return int
  56. */
  57. public function handle()
  58. {
  59. $modelId = $this->argument('model');
  60. $this->aiTermService->setModel($modelId);
  61. $this->model = $this->modelService->getModelById($modelId);
  62. $this->info("model:{$this->model['model']}");
  63. $this->modelToken = AuthService::getUserToken($modelId);
  64. if ($this->option('thinking') !== null) {
  65. $this->thinking = $this->option('thinking') === 'true';
  66. }
  67. $this->aiTermService->setThinking($this->thinking);
  68. if ($this->option('fresh')) {
  69. Cache::forget(self::CURSOR_KEY);
  70. $this->info('cleared checkpoint');
  71. }
  72. if ($this->option('id')) {
  73. $this->info($this->option('id').' running');
  74. $result = $this->aiTermService->update($this->option('id'));
  75. $this->info('done content '.substr($result, 0, 30));
  76. } elseif ($this->option('channel')) {
  77. $this->runChannel($this->option('channel'));
  78. } else {
  79. $this->error('id or channel is request');
  80. return 1;
  81. }
  82. return 0;
  83. }
  84. /**
  85. * 遍历术语表,逐词生成/更新到指定 channel;逐词写入断点以支持重入。
  86. *
  87. * 断点游标记录最后一个已完成术语的 word 与 index;重跑时优先按 word 在当前术语表中
  88. * 定位续跑位置(术语表尾部新增也能正确接续),找不到则回退到 index。
  89. */
  90. private function runChannel(string $channelId): void
  91. {
  92. $terms = $this->termService->getGlossary();
  93. $total = count($terms);
  94. $this->info("{$total} terms");
  95. $startIndex = 0;
  96. $cursor = Cache::get(self::CURSOR_KEY, []);
  97. if (! empty($cursor)) {
  98. $pos = array_search($cursor['word'] ?? null, $terms, true);
  99. $startIndex = $pos !== false ? (int) $pos + 1 : (int) ($cursor['index'] ?? -1) + 1;
  100. $this->info("resume from index {$startIndex} (after word [{$cursor['word']}])");
  101. }
  102. for ($key = $startIndex; $key < $total; $key++) {
  103. $term = $terms[$key];
  104. $this->info("[{$key}/{$total}][{$term}] running");
  105. $result = $this->aiTermService->updateOrCreate($channelId, $term);
  106. $this->info("[{$term}]content ".substr($result, 0, 30));
  107. // 该术语完成后写入断点(中途中断时不会误标记未完成的术语)
  108. Cache::put(self::CURSOR_KEY, ['index' => $key, 'word' => $term], now()->addHours(48));
  109. }
  110. // 全部完成,清空断点
  111. Cache::forget(self::CURSOR_KEY);
  112. $this->info('all terms completed');
  113. }
  114. }