HyperAI超神経
Back to Headlines

「ASTを活用してLLM生成コードの画像URLを修正:フロントエンド開発の新ソリューション」 この記事では、フロントエンド開発においてLLM(大規模言語モデル)が生成したコード内の幻覚的な画像URLを検出して正当化する方法を紹介します。AST(抽象構文木)を使用することで、画像要素を効率的に特定し、そのURLを適切なものに置き換えることができます。具体的には、コード内の画像要素を抽出し、テキストから画像を生成するモデルを利用して新しいURLを設定するプロセスを詳しく解説します。

18日前

ASTを活用したLLMによるソースコード生成の改善 最近、ユーザーがシンプルなプロンプトを使用してウェブアプリを構築できるプラットフォームを開発していました。このプラットフォームでは、多くのテックスタートアップと同様に大規模言語モデル(LLM)を用いてソースコードを自動生成しましたが、ブランドやスタイルの一貫性の不足、文法エラーなど、様々な問題に直面しました。特にフロントエンドのコードでは、生成された画像URLが無効な場合があり、これを解決するためにAST(抽象構文木)を利用しています。 問題の概要 ホロージネーションによる画像URLの問題 フロントエンドのコードでは、ウェブページに表示される画像要素には有効かつ適切なURLが必要です。しかし、LLMによって生成されたコードでは、画像URLが実際には存在しないものとなることが frecuent です。この問題に対処するためには以下の手順を踏む必要があります: 1. コード内のすべての画像要素を見つける。 2. それらの画像要素の周辺の文脈に基づいて、適切なキャプションを生成する。 3. 文字列から画像を生成する。 4. 無効な画像URLを新しいものに置換する。 解決策の選択肢 正規表現(Regex) 正規表現を使用することで、画像要素を簡単に抽出できると思われます。しかし、JSX内の画像はさまざまな形式で表されるため、単純な正規表現では不十分です。画像はHTML <img> タグだけでなく、フレームワーク固有の <Image> タグやカスタムコンポーネント <ProfileImage>、そしてJavaScriptでのオブジェクト定義にも含まれることがあります。 大規模言語モデル(LLM) LLMには、複数の形式で表される画像要素を把握する能力がありますが、以下のような課題があります: - 費用: 単純なコード抽出にLLMを利用するコストが長期的にかけて蓄積します。 - 遅さ: LLMへのAPI呼び出しに時間がかかるため、プロセスが遅くなります。 - 非決定性: LLMは非決定的な特性を持ち、特定のシナリオでは失敗する可能性があります。 ASTを用いた解決策 ASTの説明 ASTはソースコードの抽象的な「木」表現であり、ソースコードをJSX要素、関数、JavaScriptオブジェクトなどの文法的コンポーネントに分解・階層化します。Babelというツールを使えば、JavaScript/TypeScriptのASTを簡単に構築できます。 画像ノードの抽出 まず、ソースコードからASTを生成し、画像ノードを見つけ出すコード例を示します: ```javascript const babelParser = require('@babel/parser'); const traverse = require('@babel/traverse').default; const generator = require('@babel/generator').default; const t = require('@babel/types'); function findImageNodes(code) { const ast = babelParser.parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'], }); const candidateNodes = []; // 画像をオブジェクトリテラルとして定義した場合の処理 traverse(ast, { ObjectExpression(path) { const properties = path.node.properties; properties.forEach((prop) => { if ( prop.type === 'ObjectProperty' && (prop.key.name === 'src' || prop.key.name === 'url' || prop.key.name === 'image') ) { const nodeSourceCode = generator(path.node).code; candidateNodes.push(nodeSourceCode); } }); }, // JSX要素として定義した場合の処理 JSXElement(path) { const openingElement = path.node.openingElement; const tagName = openingElement.name && openingElement.name.name; if (tagName === 'img' || tagName === 'Image') { const nodeSourceCode = generator(openingElement).code; const existingSrcAttr = openingElement.attributes.find(attr => attr.name.name === 'src'); if (existingSrcAttr) { if (existingSrcAttr.value.type === 'StringLiteral') { candidateNodes.push(nodeSourceCode); } } else { candidateNodes.push(nodeSourceCode); } } } }); return candidateNodes; } ``` 上記のコードでは、ASTを生成し、その後、対応する画像ノードを探します。ObjectExpression パスではオブジェクトリテラルとして定義された画像を探し、JSXElement パスではJSX要素として定義された画像を探します。 画像ノードの更新 次に、見つけた候補ノードにメタデータ(新的な画像URLとキャプション)を追加するためのコード例を示します: ```javascript function updateImageNodesWithMetaData(code, nodeResults) { const ast = babelParser.parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'], }); const resultMap = new Map(nodeResults); traverse(ast, { ObjectExpression(path) { const properties = path.node.properties; const nodeSourceCode = generator(path.node).code; if (resultMap.has(nodeSourceCode)) { properties.forEach((prop) => { if ( prop.type === 'ObjectProperty' && (prop.key.name === 'src' || prop.key.name === 'url' || prop.key.name === 'image') ) { prop.value = t.stringLiteral(resultMap.get(nodeSourceCode)['image_url']); const existingAlt = properties.find((p) => p.key.name === 'alt'); if (existingAlt) { existingAlt.value = t.stringLiteral(resultMap.get(nodeSourceCode)['description']); } else { const altProperty = t.objectProperty( t.identifier('alt'), t.stringLiteral(resultMap.get(nodeSourceCode)['description']) ); path.node.properties.push(altProperty); } } }); } }, JSXElement(path) { const openingElement = path.node.openingElement; const nodeSourceCode = generator(openingElement).code; if (resultMap.has(nodeSourceCode)) { const srcAttr = openingElement.attributes.find( (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'src' ); if (srcAttr) { srcAttr.value = t.stringLiteral(resultMap.get(nodeSourceCode)['image_url']); const existingAltAttr = openingElement.attributes.find( (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'alt' ); if (existingAltAttr) { existingAltAttr.value = t.stringLiteral(resultMap.get(nodeSourceCode)['description']); } else { openingElement.attributes.push( t.jsxAttribute( t.jsxIdentifier('alt'), t.stringLiteral(resultMap.get(nodeSourceCode)['description']) ) ); } } } } }); return generator(ast).code; } ``` この関数では、ASTを再度生成し、nodeResults に基づいて各ノードの src 属性と alt 属性を更新します。これにより、生成されたコードの構造的整合性と文法は維持され、必要な情報を置換することができます。 例 入力ソースコード javascript export default function EcommerceTShirts() { const [menuOpen, setMenuOpen] = useState(false) const [searchOpen, setSearchOpen] = useState(false) const hats = [ { id: 1, name: "Simple Hat", price: "$24.99", image: "/hat.jpg", alt: "existing" }, { id: 2, name: "New Hat", price: "$19.99", image: "/hat-new.jpg" }, ] return ( <div className="flex flex-col min-h-screen bg-gray-50"> <main className="flex-grow mt-20"> <section> <h2>Featured T-Shirts</h2> <div> {[{ id: 1, name: "Graphic Tee", price: "$24.99", image: "/tshirt1.jpg" }, { id: 2, name: "Pocket Tee", price: "$19.99", image: "/tshirt2.jpg" }].map((shirt) => ( <Card> <Image src={shirt.image} alt={shirt.name} /> </Card> ))} </div> </section> <section> <img src="/placeholder.jpg" alt="A featured hat displayed in a stylish setting" /> </section> <section> <Image src="/placeholder.png" alt="A placeholder image for a t-shirt" /> </section> <section> {hats.map((hat) => ( <img key={hat.id} src={hat.image} alt={hat.name} /> ))} </section> </main> </div> ) } 出力ソースコード javascript export default function EcommerceTShirts() { const [menuOpen, setMenuOpen] = useState(false) const [searchOpen, setSearchOpen] = useState(false) const hats = [ { id: 1, name: "Simple Hat", price: "$24.99", image: "s3.productx.hat.jpg", alt: "A simple classic hat in a neutral tone displayed on a flat surface" }, { id: 2, name: "New Hat", price: "$19.99", image: "s3.productx.hat-new.jpg", alt: "A modern-style hat with a minimalist logo on the front panel" }, ] return ( <div className="flex flex-col min-h-screen bg-gray-50"> <main className="flex-grow mt-20"> <section> <h2>Featured T-Shirts</h2> <div> {[{ id: 1, name: "Graphic Tee", price: "$24.99", image: "s3.productx.tshirt1.jpg", alt: "A white graphic t-shirt featuring bold abstract art" }, { id: 2, name: "Pocket Tee", price: "$19.99", image: "s3.productx.tshirt2.jpg", alt: "A soft grey t-shirt with a front chest pocket" }].map((shirt) => ( <Card> <Image src={shirt.image} alt={shirt.alt} /> </Card> ))} </div> </section> <section> <img src="s3.productx.placeholder.jpg" alt="A featured hat displayed in a stylish setting with a vibrant background that fades from blue to indigo, enhancing visual appeal" /> </section> <section> <Image src="s3.productx.placeholder.png" alt="A placeholder image representing a featured t-shirt on a blank background" /> </section> <section> {hats.map((hat) => ( <img key={hat.id} src={hat.image} alt={hat.alt} /> ))} </section> </main> </div> ) } このように、ASTを用いることで、無効な画像URLを適切なものに置き換えつつ、コードの構造的整合性と文法エラーを回避することができます。 追加の使用例 ASTは他の多くのユースケースでも便利です。例えば: 1. Aider のブログでは、LLM向けのコードLintingを行う方法を紹介しています。 2. GritQL は、ASTを使用して大規模なコードメンテナンスと操作を行い、効率的な開発を支援しています。 これらの解決策は、生成AIの領域で重要な技術となっています。詳しくはGenerative AIの公開記事をご覧いただくか、TwitterやLinkedInで弊社をフォローし、最新のAIストーリーをチェックしてください。 Zeniteqは生成AIの未来に取り組んでいます。ニュースレターに登録して最新情報を得り、YouTubeチャンネルもチェックしてください。AIの future とともに成長しましょう!

Related Links