先日、記事に TianliGPT を組み込み、自動で記事要約(長すぎて読めない)モジュールを実現しました。TianliGPT はとてもシンプルで手軽な埋め込み方式を提供していますが、要約生成と関連記事推薦機能に特化しており、もし拡張したい場合はかなりの制限があります。そのため、最近は TianliGPT を諦めて Moonshot AI を使って記事要約の生成と追加機能の拡張を行っています。
要件の確定
まず、記事に要約を生成するだけでなく、記事の内容に対して関連する質問とその答えも提示したいと考えています。質問をクリックすると対応する答えが表示されるという効果で、下の画像のような感じです:

記事要約モジュールプレビュー画像
上記の要件に基づき、モデルに以下のような JSON 形式で返してもらい、フロントエンドで処理することにします:
そこで、以下のような prompt をモデルに渡すことにしました:
Noteprompt(提示词)指的是给模型提供的信息或指令,用以引导模型生成特定的响应或输出,它决定了模型如何理解和处理用户输入。一个有效的 prompt 通常包括以下几个要素:明确性、相关性、简洁性、上下文、指令性。
Kimi と Moonshot AI が提供するモデルは同一ソースなので、Kimi を使ってテストすることで、Moonshot API を使った際の結果をある程度予測できます (実は節約のため) 。この prompt が想定通りの効果を持つかどうかを確認するため、Kimi と会話してみた結果が下の画像です:

モデル会話結果
モデルとの会話開始
モデルと何を話すべきかという問題を解決した後、次はどうやってモデルと話すかという問題を解決する必要があります。Moonshot AI の公式ドキュメントでは Python と Node.js の 2 種類の実装方法が提供されていますが、ここでは PHP を使って対応する機能を実装します。
公式には Chat Completions の API:https://api.moonshot.cn/v1/chat/completions が提供されており、リクエストヘッダとリクエスト内容の例は以下の通りです:
modelはモデル名で、Moonshot-v1 には現在moonshot-v1-8k、moonshot-v1-32k、moonshot-v1-128kの 3 つのモデルがあります。messages配列は会話のメッセージリストで、role の値はsystem、user、およびassistanのいずれかです:system はシステムメッセージで、会話に文脈や指示を提供し、一般的に prompt を記載します;user はユーザーのメッセージで、ユーザーの質問や入力を意味します;assistant はモデルの返答を意味します。temperatureはサンプリング温度で、推奨値は0.3です。
この機能を実装するために MoonshotAPI クラスを構築できます:
[!NOTE] もしあなたがプロンプトで直接 Kimi 大模型に「JSON フォーマットで出力してください」と伝えた場合、Kimi 大模型はあなたの要望を理解し、要求通りに JSON ドキュメントを生成しますが、生成された内容には通常、JSON ドキュメントの外に追加の説明テキストが含まれるなどの些細な欠点があります。
そのため、リクエスト内容を構築する際に JSON Mode を有効にし、モデルに「要求通りに合法的で正しく解析可能な JSON ドキュメントを出力する」ようにさせる、つまり preparePayload メソッドの返す配列に 'response_format' => ["type" => "json_object"] を追加する必要があります。
明らかなことに、MoonshotAPI クラスが受け取る 3 つのパラメータのうち、$messages 会話メッセージリストだけが比較的複雑なので、getMessages 関数を作成して会話メッセージリスト配列を構築します。
ここで、最初に設計した prompt を system メッセージに記入し、記事内容を最初の user メッセージに記入します。実際の API リクエストでは、messages 配列は時系列順に並び、通常は最初に system メッセージ、次に user の質問、最後に assistant の回答となります。この構造は会話の文脈と一貫性を維持するのに役立ちます。
モデルと会話すると、モデルは以下のような JSON データを返します。ここで主に注目するのは choices 配列です。
この会話モードでは、choices 配列には通常 1 つのオブジェクトのみが含まれます(以下の Moonshot 関数がモデルの返信などを取得する際に $result['choices'][0] とハードコーディングしている理由です)。このオブジェクトはモデルが生成したテキスト返信を表します。オブジェクト内の finish_reason は生成テキストの完了理由を示し、モデルが完全な回答を出したと判断した場合、finish_reason の値は stop になります。これを利用してモデルが生成した内容が完全かどうかを判断できます。オブジェクト内の content がモデルからの返信です。
次に、Moonshot 関数を作成して MoonshotAPI クラスを呼び出します:
これで、以下のようなコードが得られます。予期せぬことがなければ、直接呼び出すとモデルの返信が得られます✌️。フロントエンドは返信を受け取ってページにレンダリングすればよく、ここでは詳細は割愛します。
番外:超長記事の処理
一部の記事では、上記のコードを直接呼び出すと以下のようなエラーが発生することがあります:
このエラーが発生する理由は、入出力の文脈トークンの合計がモデル(ここではデフォルトで使用している moonshot-v1-8k モデル)に設定されたトークン上限を超えたためです。文脈の長さに応じて、予想される出力トークン長を加えて適切なモデルを選択する必要があります。このような状況に対して、ドキュメントでも 如何根据上下文长度选择恰当的模型 のサンプルコードが提供されているので、それを PHP に変換して上記のコードに組み込むだけです。
Moonshot 関数内で、モデルエラーの返却タイプが invalid_request_error(つまり該当モデルの最大トークン制限を超えた)場合、selectModel 関数を呼び出して最適なモデルを選択した後、適切なモデルで再度会話を行います。