Darbplūsmas straumēšanas darbības
Straumēšanas darbības ļauj darbplūsmai pakāpeniski ģenerēt izvadi. Katrs nodotais fragments tiek uzreiz ierakstīts datubāzē, tāpēc, ja darbība neizdodas straumēšanas laikā, jau saglabātie fragmenti tiek saglabāti un ir pieejami atkārtotā mēģinājumā.
Tas noder LLM straumēšanas atbildēm, lielu failu apstrādei un jebkurā situācijā, kad nedrīkst zaudēt daļēju progresu.
StreamingStep saskarne
Straumēšanas darbība implementē Queuety\Contracts\StreamingStep:
namespace Queuety\Contracts;
interface StreamingStep {
public function stream( array $state, array $existing_chunks = [] ): \Generator;
public function on_complete( array $chunks, array $state ): array;
public function config(): array;
}
| Metode | Apraksts |
|---|---|
stream( $state, $existing_chunks ) |
Ģenerators, kas nodod virknes fragmentus. Katra nodošana izraisa tūlītēju DB ierakstu. |
on_complete( $chunks, $state ) |
Tiek izsaukta pēc straumes pabeigšanas. Saņem visus fragmentus (iepriekšējos un jaunos) un atgriež datus, ko apvienot ar darbplūsmas stāvokli. |
config() |
Neobligāta konfigurācija. Atbalsta atslēgas max_attempts un backoff; backoff pieņem stratēģijas nosaukumu (exponential, linear, fixed), jo aizkavju saraksts pašlaik tiek atmests (#16). |
Kā tiek saglabāti fragmenti
Katru reizi, kad ģenerators nodod vērtību, Queuety izsauc ChunkStore::append_chunk(), lai ierakstītu to tabulā queuety_chunks. Netiek veikta pakešu apstrāde vai buferēšana. Viena nodošana atbilst vienam INSERT.
Fragmenta rindā tiek saglabātas vērtības job_id, workflow_id, step_index, chunk_index (sākot no nulles) un virkne content.
Izpildes atsākšana
Ja straumēšanas darbību mēģina atkārtoti pēc kļūmes, izpildītājs ielādē visus iepriekš saglabātos fragmentus un nodod tos kā parametru $existing_chunks. Jūs izvēlaties, kā atsākt darbu:
- Izlaist jau apstrādātos vienumus, pārbaudot esošo fragmentu skaitu
- Pāriet uz lappusēm sadalītā API nobīdi, izmantojot fragmentu skaitu kā lappuses nobīdi
- Sākt no jauna, pilnībā ignorējot
$existing_chunks(iepriekšējā mēģinājuma fragmenti joprojām ir DB un tiks pievienoti sākumā)
public function stream( array $state, array $existing_chunks = [] ): \Generator {
$offset = count( $existing_chunks );
$pages = $this->client->paginate( $state['query'], offset: $offset );
foreach ( $pages as $page ) {
yield json_encode( $page );
}
}
Sirdspuksti straumēšanas laikā
Ilgstošām straumēm jāsūta sirdspuksti, lai novērstu novecojušu darbu noteicēja mēģinājumus pārņemt darbu. Izsauciet Heartbeat::beat() ģeneratora ciklā:
use Queuety\Heartbeat;
public function stream( array $state, array $existing_chunks = [] ): \Generator {
foreach ( $this->client->stream( $state['prompt'] ) as $i => $chunk ) {
Heartbeat::beat( [ 'chunks' => $i + 1 ] );
yield $chunk;
}
}
Plašāku informāciju par Heartbeat API skatiet sadaļā Sirdspuksti.
Fragmentu tīrīšana
Kad straume tiek veiksmīgi pabeigta un on_complete() atgriežas, izpildītājs izsauc ChunkStore::clear_chunks(), lai dzēstu visus darba fragmentus. Apvienotais rezultāts no on_complete() ir pastāvīgais ieraksts. Fragmenti pēc konstrukcijas ir īslaicīgi.
Ja darbība neizdodas un tiek mēģināta atkārtoti, fragmenti netiek dzēsti. Nākamajā mēģinājumā tie tiek nodoti, izmantojot $existing_chunks.
Pilns piemērs: LLM straumēšana
use Queuety\Contracts\StreamingStep;
use Queuety\Heartbeat;
class StreamLLMResponse implements StreamingStep {
public function stream( array $state, array $existing_chunks = [] ): \Generator {
// If we have existing chunks from a previous attempt, we can skip
// re-requesting. The on_complete method will concatenate everything.
if ( ! empty( $existing_chunks ) ) {
return;
}
$response = OpenAI::chat()->createStreamed( [
'model' => 'gpt-4',
'messages' => $state['messages'],
] );
foreach ( $response as $i => $chunk ) {
$text = $chunk->choices[0]->delta->content ?? '';
if ( '' !== $text ) {
Heartbeat::beat( [ 'tokens' => $i ] );
yield $text;
}
}
}
public function on_complete( array $chunks, array $state ): array {
return [
'llm_response' => implode( '', $chunks ),
];
}
public function config(): array {
return [ 'max_attempts' => 5 ];
}
}
Izmantojiet to darbplūsmā tāpat kā jebkuru citu darbību:
use Queuety\Queuety;
Queuety::workflow( 'ai_chat' )
->then( BuildPromptHandler::class )
->then( StreamLLMResponse::class )
->then( SaveResponseHandler::class )
->dispatch( [ 'user_input' => $input ] );
Pilns piemērs: failu apstrāde
use Queuety\Contracts\StreamingStep;
use Queuety\Heartbeat;
class ProcessCSVRows implements StreamingStep {
public function stream( array $state, array $existing_chunks = [] ): \Generator {
$file = fopen( $state['csv_path'], 'r' );
$offset = count( $existing_chunks );
$row = 0;
// Skip the header.
fgetcsv( $file );
while ( $line = fgetcsv( $file ) ) {
$row++;
// Skip rows already processed in a previous attempt.
if ( $row <= $offset ) {
continue;
}
$result = $this->process_row( $line );
Heartbeat::beat( [ 'row' => $row ] );
yield json_encode( $result );
}
fclose( $file );
}
public function on_complete( array $chunks, array $state ): array {
$results = array_map( 'json_decode', $chunks );
return [
'processed_count' => count( $results ),
'results' => $results,
];
}
public function config(): array {
return [];
}
private function process_row( array $line ): array {
// Your row processing logic here.
return [ 'id' => $line[0], 'status' => 'ok' ];
}
}
ChunkStore API
Klase ChunkStore pārvalda fragmentu saglabāšanu. Tai var piekļūt, izmantojot Queuety::chunk_store().
$store = Queuety::chunk_store();
| Metode | Atgriež | Apraksts |
|---|---|---|
get_chunks( int $job_id ) |
string[] |
Iegūst visus darba fragmentus, sakārtotus pēc fragmenta indeksa |
append_chunk( int $job_id, int $chunk_index, string $content, ?int $workflow_id, ?int $step_index ) |
void |
Pievieno vienu fragmentu |
clear_chunks( int $job_id ) |
void |
Dzēš visus darba fragmentus |
chunk_count( int $job_id ) |
int |
Saskaita darba fragmentus |
get_accumulated( int $job_id ) |
string |
Savieno visus fragmentus vienā virknē |
Parasti ChunkStore nav jāizsauc tieši. Apstrādājot StreamingStep, izpildītājs automātiski parūpējas par saglabāšanu un tīrīšanu. API ir pieejams īpašiem lietojumiem, piemēram, apstrādes gaitā esošo fragmentu pārbaudei vai manuālai tīrīšanai.