از Chrome's Prompt API برای ایجاد برنامهریز سفر در 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: SystemPromptsComponent,
inputs: {}
}
});
}
این PromptShowcaserComponent
را ارائه می دهد SystemPromptsComponent
به صورت پویا
کامپوننت واکنش سریع
@Component({
selector: 'app-prompt-response',
imports: [TokenizationComponent, FormsModule, LineBreakPipe, NgTemplateOutlet],
template: `
@let responseState = state();
Prompt:
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PromptResponseComponent {
state = input.required<PromptResponse>();
query = model.required<string>();
submitPrompt = output();
}
این PromptResponseComponent
یک ناحیه متنی را نشان می دهد که کاربران می توانند یک پرس و جو وارد کنند. سپس روی دکمه ارسال درخواست به Gemini Nano داخلی کلیک میکنند که یک پاسخ متنی ایجاد میکند. این submitPrompt
تابع خروجی به اطلاع می رساند SystemPromptComponent
جزء که یک درخواست کاربر ارسال شده است. در نهایت، لوله LineBreakPipe پاسخ را قبل از نمایش آن پاک می کند.
جزء درخواست های سیستم
// system-prompts.component.ts
@Component({
selector: 'app-system-prompt',
imports: [FormsModule, PromptResponseComponent],
template: `
System Prompts
System Prompt:
`,
styleUrl: './prompt.component.css',
providers: [
{
provide: AbstractPromptService,
useClass: SystemPromptService,
}
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SystemPromptsComponent extends BasePromptComponent {
systemPrompt = signal(`` );
responseState = computed<PromptResponse>(() => ({
...this.state(),
error: this.error(),
response: this.response(),
}));
constructor() {
super();
this.query.set(' ');
this.promptService.setPromptOptions({ systemPrompt: this.systemPrompt() });
}
}
این SystemPromptsComponent
یک ناحیه متنی را برای کاربران نمایش می دهد تا اعلان سیستم را برای توصیف زمینه مشکل به روز کنند. مولفه PromptResponseComponent به کاربران اجازه می دهد تا درخواست های خود را وارد کرده و نتایج را نمایش دهند. این systemPrompt
سیگنال اعلان سیستم را ذخیره می کند و به Gemini Nano آموزش می دهد که چگونه هنگام پاسخ دادن به درخواست کاربر رفتار کند.
constructor() {
super();
this.query.set(`);
this.promptService.setPromptOptions({ systemPrompt: this.systemPrompt() });
}
سازنده کامپوننت مقدار اولیه پرس و جو را تنظیم می کند و آن را فراخوانی می کند SystemPromptService
برای به روز رسانی اعلان سیستم Prompt API.
systemPrompt = signal(`You are a professional trip planner who helps travelers to plan a trip to a location. When a traveler specifies a country or city, you have to recommend how to apply for a travel visa, pack suitable clothes for the weather and essentials, and list the known attractions to visit daily. It is preferred to visit two to three attractions each day to maximize the value of the trip. If you don't know the answer, say, "I do not know the answer."`);
در این دمو، Gemini Nano یک برنامه ریز سفر حرفه ای است که به مسافران کمک می کند تا برای سفر به یک کشور خارجی برنامه ریزی کنند. اعلان سیستم به LLM دستور می دهد تا جزئیات مربوط به ویزای سفر، لباس پوشیدن و جاذبه های مختلف را برای بازدید در طول سفر ارائه دهد.
this.query.set('I will visit from Hong Kong to Taipei between Feb 13th to Feb 18th. Please help me plan the trip and assume I will arrive in the afternoon on day 1.');
کاربر در ماه فوریه به مدت 6 روز به تایپه سفر می کند و از Gemini Nano می خواهد که برای سفر برنامه ریزی کند.
جزء پایه
@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 ارسال می کند تا متن ها را تولید کرده و به آن اختصاص دهد response
سیگنال هنگامی که LLM اشغال می شود، isLoading
سیگنال روی true تنظیم می شود و عناصر رابط کاربری (منطقه متن و دکمه) غیرفعال می شوند. وقتی سیگنال روی نادرست تنظیم شود، عناصر رابط کاربری فعال می شوند.
یک لایه سرویس روی Prompt API تعریف کنید
این SystemPromptService
سرویس منطق Prompt API را کپسوله می کند.
این createPromptSession
یک جلسه با اعلان سیستم ایجاد می کند. هنگامی که سرویس از بین می رود، ngOnDestroy
روش برای جلوگیری از نشت حافظه، جلسه را از بین می برد.
@Injectable({
providedIn: 'root'
})
export class SystemPromptService extends AbstractPromptService implements OnDestroy {
#controller = new AbortController();
override async createPromptSession(options?: PromptOptions): Promise<AILanguageModel | undefined> {
const { systemPrompt = undefined } = options || {};
return this.promptApi?.create({ systemPrompt, 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 در فضای ابری، برنامههای هوش مصنوعی وب ایجاد کنند.
منابع: