با Prompt API Chrome در Angular یک طبقهبندی احساسات بسازید

در این پست وبلاگ، نحوه ساخت یک برنامه طبقه بندی احساسات به صورت محلی با استفاده از Chrome's Built-In Prompt API و Angular را توضیح می دهم. برنامه Angular برای ایجاد یک مدل زبان، Prompt API را فراخوانی می کند و پرس و جوهایی را به Gemini Nano ارسال می کند تا احساسات مثبت یا منفی را طبقه بندی کند.
مزیت استفاده از هوش مصنوعی داخلی کروم هزینه صفر است زیرا برنامه از مدلهای محلی Chrome Canary استفاده میکند. زمانی که کاربران از Chrome Dev یا Chrome Canary استفاده می کنند، این مسیر خوشحال کننده است. اگر کاربران از مرورگرهای غیر کروم یا قدیمی کروم استفاده میکنند، یک پیادهسازی مجدد باید در دسترس باشد، مانند تماس با جما یا جمینی در Vertex AI برای بازگرداندن احساس صحیح.
Gemini Nano را روی کروم نصب کنید
Chrome Dev/Canary را به آخرین نسخه بهروزرسانی کنید. تا لحظه نگارش این مقاله، جدیدترین نسخه Chrome Canary 133 است.
لطفاً برای ثبت نام در برنامه پیش نمایش اولیه Chrome Built-in AI به این بخش مراجعه کنید.
https://developer.chrome.com/docs/ai/built-in#get_an_early_preview
لطفاً برای فعال کردن Gemini Nano در کروم و دانلود مدل به این بخش مراجعه کنید. https://developer.chrome.com/docs/ai/get-started#use_apis_on_localhost
غیرفعال کردن طبقهبندی ایمنی متن در کروم
- (توسعه محلی) به chrome://flags/#text-safety-classifier بروید.
- (توسعه محلی) Disabled را انتخاب کنید
- روی راه اندازی مجدد یا راه اندازی مجدد کروم کلیک کنید.
داربست یک برنامه زاویه ای
ng new prompt-api-demo
وابستگی ها را نصب کنید
npm i -save-exact -save-dev @types/dom-chromium-ai
این وابستگی تایپ TypeScript همه APIهای داخلی Chrome را فراهم می کند. بنابراین، توسعه دهندگان می توانند کدهای ظریفی برای ساخت برنامه های هوش مصنوعی در TypeScript بنویسند.
در main.ts، یک تگ مرجع برای اشاره به فایل تعریف تایپ بسته اضافه کنید.
// main.ts
///
مدل زبان را بوت استرپ کنید
import { InjectionToken } from '@angular/core';
export const AI_PROMPT_API_TOKEN = new InjectionToken<AILanguageModelFactory | undefined>('AI_PROMPT_API_TOKEN');
export function provideLanguageModel(): EnvironmentProviders {
return makeEnvironmentProviders([
{
provide: AI_PROMPT_API_TOKEN,
useFactory: () => {
const platformId = inject(PLATFORM_ID);
const objWindow = isPlatformBrowser(platformId) ? window : undefined;
return objWindow?.ai?.languageModel;
},
}
]);
}
من ارائه دهندگان محیط را برای بازگشت تعریف می کنم languageModel
در window.ai
فضای نام وقتی کدها را تزریق می کنند AI_LANGUAGE_PROMPT_API_TOKEN
آنها می توانند به Prompt API دسترسی داشته باشند تا متدهای آن را برای ارسال پرس و جوها به Gemini Nano فراخوانی کنند.
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideLanguageModel()
]
};
در پیکربندی برنامه، provideLanguageModel
به آرایه ارائه دهندگان وارد می شود.
نسخه مرورگر و در دسترس بودن API را تأیید کنید
هوش مصنوعی داخلی کروم در وضعیت آزمایشی است و Prompt API در نسخه 131 کروم و جدیدتر پشتیبانی میشود. بنابراین، من منطق اعتبارسنجی را برای اطمینان از در دسترس بودن API قبل از نمایش رابط کاربری اجرا کردم تا کاربران بتوانند متون را وارد کنند.
قوانین اعتبارسنجی عبارتند از:
- مرورگر کروم است
- نسخه مرورگر حداقل 131 است
- ai Object در فضای نام پنجره است
- وضعیت Prompt API به راحتی است
export async function checkChromeBuiltInAI(): Promise<string> {
if (!isChromeBrowser()) {
throw new Error(ERROR_CODES.NOT_CHROME_BROWSER);
}
if (getChromVersion() < CHROME_VERSION) {
throw new Error(ERROR_CODES.OLD_BROWSER);
}
if (!('ai' in globalThis)) {
throw new Error(ERROR_CODES.NO_PROMPT_API);
}
const assistant = inject(AI_PROMPT_API_TOKEN);
const status = (await assistant?.capabilities())?.available;
if (!status) {
throw new Error(ERROR_CODES.API_NOT_READY);
} else if (status === 'after-download') {
throw new Error(ERROR_CODES.AFTER_DOWNLOAD);
} else if (status === 'no') {
throw new Error(ERROR_CODES.NO_LARGE_LANGUAGE_MODEL);
}
return '';
}
را checkChromeBuiltInAI
تابع اطمینان می دهد که Prompt API تعریف شده و آماده استفاده است. اگر بررسی ناموفق باشد، تابع خطا می دهد. در غیر این صورت یک رشته خالی برمی گرداند.
export function isPromptAPISupported(): Observable<string> {
return from(checkChromeBuiltInAI()).pipe(
catchError(
(e) => {
console.error(e);
return of(e instanceof Error ? e.message : 'unknown');
}
)
);
}
را isPromptApiSupported
تابع خطا را می گیرد و پیغام خطای Observable of را برمی گرداند.
نمایش اجزای هوش مصنوعی
@Component({
selector: 'app-detect-ai',
imports: [PromptShowcaseComponent],
template: `
@let error = hasCapability();
@if (!error) {
} @else if (error !== 'unknown') {
{{ error }}
}
`
})
export class DetectAIComponent {
hasCapability = toSignal(isPromptAPISupported(), { initialValue: '' });
}
را DetectAIComponent
را ارائه می دهد PromptShowcaseComponent
جایی که هیچ خطایی وجود ندارد در غیر این صورت پیغام خطا را در سیگنال خطا نمایش می دهد.
// prompt-showcase.component.ts
@Component({
selector: 'app-prompt-showcase',
imports: [NgComponentOutlet],
template: `
@let outlet = componentOutlet();
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PromptShowcaseComponent {
promptService = inject(ZeroPromptService);
componentOutlet = computed(() => {
return {
component: NShotsPromptComponent,
inputs: {}
}
});
}
را PromptShowcaserComponent
را ارائه می دهد NShotsPromptComponent
به صورت پویا
N Shots Prompt Component
// n-shots-prompt.component.ts
@Component({
selector: 'app-n-shot-prompt',
imports: [FormsModule],
template: `
N-shots prompting
@let myState = state();
Prompt:
@if (error()) {
}
`,
styleUrl: './prompt.component.css',
providers: [
{
provide: AbstractPromptService,
useClass: NShotsPromptService,
}
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NShotsPromptComponent extends BasePromptComponent {
initialPrompts = signal<LanguageInitialPrompt>([
{ role: 'system', content: `You are an expert in determine the sentiment of a text.
If it is positive, say 'positive'. If it is negative, say 'negative'. If you are not sure, then say 'not sure'` },
{ role: 'user', content: "The food is affordable and delicious, and the venue is close to the train station." },
{ role: 'assistant', content: "positive" },
{ role: 'user', content: "The waiters are very rude, the food is salty, and the drinks are sour." },
{ role: 'assistant', content: "negative" },
{ role: 'user', content: "Google is a company" },
{ role: 'assistant', content: "not sure" },
{ role: 'user', content: "The weather is hot and sunny today." },
{ role: 'assistant', content: "postive" }
]);
constructor() {
super();
this.query.set('The toilet has no toilet papers again.');
this.promptService.setPromptOptions({ initialPrompts: this.initialPrompts() });
}
}
را NShotsPromptComponent
یک ناحیه متنی را برای کاربر نمایش می دهد تا درخواست را وارد کند و یک دکمه ارسال را برای ارسال آن به LLM برای ایجاد یک احساس نمایش می دهد. را initialPrompts
سیگنال نمونه هایی از احساسات مثبت و منفی را ذخیره می کند. اولین ورودی سیگنال یک اعلان سیستمی است که زمینه مشکل را توصیف می کند. در این دمو، از Gemini Nano خواسته می شود تا احساس یک جمله را تعیین کند. اگر احساسات مثبت باشد، نتیجه “مثبت” است. اگر احساسات منفی باشد، نتیجه “منفی” است. در غیر این صورت، نتیجه “مطمئن نیست”.
constructor() {
super();
this.query.set('The toilet has no toilet papers again.');
this.promptService.setPromptOptions({ initialPrompts: this.initialPrompts() });
}
سازنده کامپوننت مقدار اولیه پرس و جو را تنظیم کرده و آن را فراخوانی می کند NShotsPromptService
برای به روز رسانی درخواست های اولیه Prompt API.
جزء پایه
@Directive({
standalone: false
})
export abstract class BasePromptComponent {
promptService = inject(AbstractPromptService);
session = this.promptService.session;
isLoading = signal(false);
error = signal('');
query = signal('Tell me about the job responsibility of an A.I. engineer, maximum 500 words.');
response = signal('');
state = computed(() => {
const isLoading = this.isLoading();
const isUnavailableForCall = isLoading || this.query().trim() === '';
return {
status: isLoading ? 'Processing...' : 'Idle',
text: isLoading ? 'Progressing...' : 'Submit',
disabled: isLoading,
submitDisabled: isUnavailableForCall
}
});
async submitPrompt() {
try {
this.isLoading.set(true);
this.error.set('');
this.response.set('');
const answer = await this.promptService.prompt(this.query());
this.response.set(answer);
} catch(e) {
const errMsg = e instanceof Error ? (e as Error).message : 'Error in submitPrompt';
this.error.set(errMsg);
} finally {
this.isLoading.set(false);
}
}
}
را BasePromptComponent
عملکرد ارسال و سیگنال ها را برای نگهداری وضعیت های پرس و جو، پاسخ و مشاهده ارائه می کند.
متد submitPrompt پرس و جو را به Gemini Nano ارسال می کند تا متن ها را تولید کند و آنها را به سیگنال پاسخ اختصاص دهد. هنگامی که LLM اشغال می شود، سیگنال isLoading روی true تنظیم می شود و عناصر UI (منطقه متن و دکمه) غیرفعال می شوند. وقتی سیگنال روی نادرست تنظیم شود، عناصر رابط کاربری فعال می شوند.
یک لایه سرویس روی Prompt Detection API تعریف کنید
را NShotsPromptService
سرویس منطق Prompt API را کپسوله می کند.
را createPromptSession
یک جلسه با دستورات اولیه ایجاد می کند. هنگامی که سرویس از بین می رود، ngOnDestroy
روش برای جلوگیری از نشت حافظه، جلسه را از بین می برد.
@Injectable({
providedIn: 'root'
})
export class NShotsPromptService extends AbstractPromptService implements OnDestroy {
#controller = new AbortController();
override async createPromptSession(options?: PromptOptions): Promise<AILanguageModel | undefined> {
const { initialPrompts = undefined } = options || {};
return this.promptApi?.create({ initialPrompts, signal: this.#controller.signal });
}
ngOnDestroy(): void {
this.destroySession();
}
}
را AbtractPromptService
روشهای استانداردی را تعریف میکند که سایر سرویسهای سریع میتوانند ارث ببرند.
را createSessionIfNotExists
متد یک جلسه ایجاد می کند و آن را در آن نگه می دارد #session
سیگنال برای استفاده مجدد یک جلسه زمانی دوباره ایجاد می شود که توکن های قبلی کمتر از 500 توکن باقی مانده باشد.
export abstract class AbstractPromptService {
promptApi = inject(AI_PROMPT_API_TOKEN);
#session = signal<AILanguageModel | undefined>(undefined);
#tokenContext = signal<Tokenization | null>(null);
#options = signal<PromptOptions | undefined>(undefined);
resetSession(newSession: AILanguageModel | undefined) {
this.#session.set(newSession);
this.#tokenContext.set(null);
}
shouldCreateSession() {
const session = this.#session();
const context = this.#tokenContext();
return !session || (context && context.tokensLeft < 500);
}
setPromptOptions(options?: PromptOptions) {
this.#options.set(options);
}
async createSessionIfNotExists(): Promise<void> {
if (this.shouldCreateSession()) {
this.destroySession();
const newSession = await this.createPromptSession(this.#options());
if (!newSession) {
throw new Error('Prompt API failed to create a session.');
}
this.resetSession(newSession);
}
}
}
متد انتزاعی createPromptSession به خدمات بتن اجازه می دهد تا انواع مختلف جلسات را پیاده سازی کنند. یک جلسه نمی تواند اعلان، اعلان سیستم یا آرایه ای از اعلان های اولیه نداشته باشد.
abstract createPromptSession(options?: PromptOptions): Promise<AILanguageModel | undefined>;
متد prompt زمانی یک جلسه ایجاد می کند که وجود نداشته باشد. سپس، جلسه یک پرس و جو را برای تولید و بازگرداندن متون می پذیرد.
async prompt(query: string): Promise<string> {
if (!this.promptApi) {
throw new Error(ERROR_CODES.NO_PROMPT_API);
}
await this.createSessionIfNotExists();
const session = this.#session();
if (!session) {
throw new Error('Session does not exist.');
}
const answer = await session.prompt(query);
return answer;
}
را destroySession
متد جلسه را از بین می برد و سیگنال های موجود در سرویس را بازنشانی می کند.
destroySession() {
const session = this.session();
if (session) {
session.destroy();
console.log('Destroy the prompt session.');
this.resetSession(undefined);
}
}
در نتیجه، مهندسان نرمافزار میتوانند بدون راهاندازی سرور پشتیبان یا جمعآوری هزینههای LLM در فضای ابری، برنامههای هوش مصنوعی وب ایجاد کنند.
منابع: