如何用抽象语法树(AST)解决LLM生成代码中的图像URL问题
近期,一家科技初创公司在开发一个让用户通过简单的提示构建网页应用的平台时,遇到了一系列问题,包括品牌风格不一致、语法错误等,特别是在前端代码生成过程中,出现了“幻觉化”图像URL的问题。这些由AI模型生成的图像URL往往指向互联网上的无效地址,导致生成的网页不能正常显示图片。为了解决这一问题,团队提出了一种基于抽象语法树(AST)的方法。 问题解析 幻觉化图像URL指的是在前端代码中,由于AI生成的原因,图像元素的源地址(src)并不指向有效的网络资源。这些图像URL可能出现在多种形式中,如HTML <img>标签、框架特定的 <Image> 标签、自定义组件 <ProfileImage> 以及引用JS对象或列表的属性。传统的正则表达式方法无法有效处理这些复杂情况,而使用大语言模型(LLM)则存在过度消耗资源、成本高昂、速度慢和非确定性等问题。 解决方案 使用AST 步骤1:找到所有图像节点 团队选择利用Babel提供的工具构建AST,解析JavaScript/Typescript代码。通过对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.key.name === 'src' || prop.key.name === 'url' || prop.key.name === 'image') { const nodeSourceCode = generator(path.node).code; candidateNodes.push(nodeSourceCode); } }); }, JSXElement(path) { const openingElement = path.node.openingElement; const tagName = 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 && existingSrcAttr.value.type === 'StringLiteral') { candidateNodes.push(nodeSourceCode); } } } }); return candidateNodes; } ``` 步骤4:替换图像URL 一旦找到所有潜在的图像节点,团队可以根据上下文生成相关的图说和图像,并将其替换到代码中。以下代码展示了如何更新图像节点的 src 和 alt 属性: ```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.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) { const altProperty = t.objectProperty(t.identifier('alt'), t.stringLiteral(resultMap.get(nodeSourceCode)['description'])); path.node.properties.push(altProperty); } else { existingAlt.value = t.stringLiteral(resultMap.get(nodeSourceCode)['description']); } } }); } }, 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) { openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('alt'), t.stringLiteral(resultMap.get(nodeSourceCode)['description']))); } else { existingAltAttr.value = t.stringLiteral(resultMap.get(nodeSourceCode)['description']); } } } } }); return generator(ast).code; } ``` 示例 输入代码包含多种形式的图像节点,经过处理后的输出代码不仅修复了无效的图像URL,还为每个图像自动生成了相关描述,如下所示: 输入代码示例 javascript export default function EcommerceTShirts() { 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="min-h-screen bg-gray-50"> <section className="py-16"> <div> <h2 className="text-center"> 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 key={shirt.id}> <CardHeader> <Image src={shirt.image} alt={shirt.name} /> </CardHeader> <CardContent> <CardTitle>{shirt.name}</CardTitle> <p>{shirt.price}</p> </CardContent> <CardFooter> <Button> Add to Cart </Button> </CardFooter> </Card> ))} </div> </div> </section> <section> <div> <img src="/placeholder.jpg" alt="现有描述" /> </div> </section> </div> </div> ); } 输出代码示例 javascript export default function EcommerceTShirts() { 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="min-h-screen bg-gray-50"> <section className="py-16"> <div> <h2 className="text-center"> 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 key={shirt.id}> <CardHeader> <Image src={shirt.image} alt={shirt.alt} /> </CardHeader> <CardContent> <CardTitle>{shirt.name}</CardTitle> <p>{shirt.price}</p> </CardContent> <CardFooter> <Button> Add to Cart </Button> </CardFooter> </Card> ))} </div> </div> </section> <section> <div> <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" /> </div> </section> </div> </div> ); } 业内评价与公司背景 这种方法得到了业内专业人士的好评。AST作为强大的代码解析工具,不仅能够解决当前的问题,还能在代码维护和大规模代码操作中发挥重要作用。例如,Aider在其博客中详细介绍了如何利用AST对LLM生成的代码进行lint检查。此外,在GritQL的优秀演讲中,也提到了如何通过AST实现大规模的代码维护和操作。 这家科技初创公司专注于借助LLM技术简化复杂的技术开发流程,旨在通过用户友好的界面让普通人也能构建高质量的网页应用。连接其LinkedIn账户并关注Zeniteq,可以获取最新的AI技术和应用动态。订阅公司的newsletter和YouTube频道,及时了解关于生成式AI的最新信息。让我们一起塑造AI的未来!
