一个由三方提供的 FLUX1 API ,可以在URL中拼接提示词(https://api-flux1.api4gpt.com/?prompt=)是本站免费引流工具的基础服务,近期已经关闭,导致本站每日访客少了很多。此API可以阅读 满血版FLUX.1:无需本地部署在线使用FLUX 在文末有介绍。关于如何使用FLUX1 API引流,可以搜索本站关于文心智能体的文章,已经开诚布公的分享给大家,可惜很多人没意识到其重要性。
回到正题,付费的FLUX1 API实在用不起,之前听说 硅基流动 提供了免费的FLUX1 API,但本人不会代码,认识的英文单词都有限,也没有空闲服务器部署服务,就难在这了......
本文主要是思路分享,并不提供完整的操作流程,文末提供直接可用的项目代码。
进入思考模式...
1.硅基流动的免费接口是否稳定...
2.如果免费接口再次关闭我该怎么办...
3.代码如何编写...
4.核心问题是:作为一个基础服务,最大问题是访问地址不变的情况下可以快速切换服务,不影响现有用户使用,如果付费使用能兜回成本何尝不可?
预期...
1.我要构造一个格式一样的URL,只不过这个URL是我的,选择好域名 img.kdjingpai.com
2.简单的API接口文档输入给大模型,应该能写出来可运行的代码
3.希望可以部署到cloudflare workers(其实workers支持什么我都不懂)
4.过分一些的要求,原接口只支持拼接提示词,其实有一些需求没有得到满足
操作流程
找到硅基流动的FLUX免费接口文档,关于接口地址和KEY自行查找。
文档整理好粘贴给Claude,同时明确告知要生成workers代码,第一轮就得到了可执行的代码。
其实到这里就结束了,只要给workers代码绑定一个自己的域名即可在国内正常访问,刚说了原接口有些需求没有得到满足,所以代码再次基础上继续优化。
需求1
允许在URL中变更生成的图像尺寸,当不输入图片尺寸时,默认生成512x512的图像(这句话输入到大模型中即可得到新的代码)
到这里你会发现Claude额度可能不足,那么不限额度的 Claude镜像站:Sonnet、Haiku、Opus全系列模型免费使用 可以上场了。
需求2
当提示词输入中文,无法正常正确生成图像,所以要将prompt部分中转英,此时需要一个大模型或翻译接口完成,免费翻译接口懒得找,通用性也不强,所以我要接入一个 免费大模型API ,这里我选择 智谱轻言的 GLM-4-Flash 。
输入 GLM-4-Flash 的接口文档,提出需求生成代码:
代码中新增一个片段,负责翻译,很不巧,一次提示就可以正常执行,所以没做后续测试:
需求3
用户输入的提示词可能过于简单,生成的图像构图、元素看起来不那么好,正好借需求2的大模型API,优化用户输入的提示词,优化提示词的功能作为参数可以拼接到URL中,这次我使用ChatGPT进行对话,得到了以下代码,当然,如何优化构图的指令是我单独提供的:
过于复杂的指令,生成图像容易走形,并且优化后提示词输出格式并不稳定,所以单独针对需求3的提示词作为单独任务进行了多轮优化,得到如下指令:
需求4
作为技术小白,非常不习惯在命令行中操作,也不会看报错信息,所以我要求大模型生成一个GUI界面,代码执行中有什么异常也要求在界面上直接显示,这就解决了日常调试的易用问题。
体验这个简陋的界面吧:https://img.kdjingpai.com/GUI/ (请不要用在公共服务中,放过我吧...)
最终成品
代码中写了一份使用帮助,以及给出一个图形界面,这并不是为了提升用户体验,而是作为代码的一部分让大模型修改时天然作为上下文思考,避免出错。但过长的代码依然会造成大模型理解有误,这需要取舍。
加入完整注释及使用帮助是普通人编写代码的好习惯。
接下来我让Claude多轮优化代码,得到了以下完整代码,其实还有很大优化空间,比如大模型告诉我最好抽象变量保证程序安全、要减少多轮判断、处理请求延时逻辑等问题,我就不处理了。请复制粘贴直接使用:
完整部署文档请访问:https://github.com/pptt121212/freefluxapi
// Constants const IMAGE_API_URL = 'https://api.siliconflow.cn/v1/image/generations'; const IMAGE_API_KEY = ''; const PROCESS_API_URL = 'https://open.bigmodel.cn/api/paas/v4/chat/completions'; const PROCESS_API_KEY = ''; const DEFAULT_SIZE = '512x512'; const SIZE_REGEX = /^\d+x\d+$/; const NON_ASCII_REGEX = /[^\x00-\x7F]/; addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); }); async function handleRequest(request) { try { const url = new URL(request.url); // Check if the request is for the GUI page if (url.pathname === '/GUI/') { return new Response(getGuiPage(), { headers: { 'Content-Type': 'text/html' } }); } const prompt = url.searchParams.get('prompt'); // Check if the prompt is valid if (!prompt) { return new Response(getUsageInstructions(), { headers: { 'Content-Type': 'text/html' } }); } const { size, optimization } = parseRequestParams(request); const processedPrompt = await getProcessedPrompt(prompt, optimization); const imageUrl = await getImageUrl(processedPrompt, size); return await fetchAndReturnImage(imageUrl); } catch (error) { return new Response(error.message, { status: error.status || 500 }); } } function parseRequestParams(request) { const url = new URL(request.url); const size = url.searchParams.get('size') || DEFAULT_SIZE; const optimization = url.searchParams.get('optimization') === '1'; if (!SIZE_REGEX.test(size)) { throw new Error('Invalid size parameter. Use format: widthxheight'); } return { size, optimization }; } async function getProcessedPrompt(prompt, optimization) { if (!NON_ASCII_REGEX.test(prompt) && !optimization) { return prompt; } return processPrompt(prompt, optimization); } async function getImageUrl(prompt, size) { const response = await fetch(IMAGE_API_URL, { method: 'POST', headers: { 'Accept': 'application/json', 'Authorization': `Bearer ${IMAGE_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: "black-forest-labs/FLUX.1-schnell", prompt, image_size: size }) }); if (!response.ok) { throw new Error(`API error: ${response.status}`); } const data = await response.json(); const imageUrl = data?.images?.[0]?.url; if (!imageUrl) { throw new Error('Unexpected API response format'); } return imageUrl; } async function fetchAndReturnImage(imageUrl) { const imageResponse = await fetch(imageUrl); const imageBlob = await imageResponse.blob(); return new Response(imageBlob, { headers: { 'Content-Type': imageResponse.headers.get('Content-Type') } }); } async function processPrompt(text, optimize) { const content = optimize ? ` # 优化以下图像prompt,保留prompt主要元素基础上使细节、构图更加丰富: ## 图像prompt: ${text} ## 优化要求 1. 优化后的图像prompt是一组<英文TAG标签>,每个TAG应该遵循构图的基本原则,将画风、构图、主体、动作、场景、元素、按顺序进行描述。 2. <英文TAG标签>中每个词组用逗号","分割,逗号","前后不允许有"空格"。 # 仅仅输出优化后的图像prompt,不要有其他说明: ` : `请将以下内容翻译为英文,保持原意:${text}`; const response = await fetch(PROCESS_API_URL, { method: 'POST', headers: { 'Authorization': `Bearer ${PROCESS_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: "GLM-4-Flash", messages: [{ role: "user", content }] }) }); if (!response.ok) { throw new Error(`Translation/Optimization API error: ${response.status}`); } const data = await response.json(); return data.choices?.[0]?.message?.content?.trim() || text; } // 函数:返回使用说明的HTML内容 function getUsageInstructions() { return ` <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用说明</title> </head> <body> <h1>使用说明</h1> <p>欢迎使用图像生成 API!请按照以下步骤生成您的图像:</p> <h2>1. 请求格式</h2> <p>访问 API 的 URL 格式如下:</p> <pre>https://your-worker-url/?prompt={您的提示}&size={图像尺寸}&optimization={优化选项}</pre> <h3>参数说明</h3> <ul> <li><strong>prompt</strong>: (必填) 您想要生成图像的描述文本,例如“一位女孩”。</li> <li><strong>size</strong>: (可选) 图像的尺寸,格式为 <code>宽度x高度</code>,默认为 <code>512x512</code>。例如:<code>256x256</code> 或 <code>1024x768</code>。</li> <li><strong>optimization</strong>: (可选) 是否进行优化,值为 <code>1</code> 表示进行优化,值为 <code>0</code> 或不提供该参数表示不进行优化。</li> </ul> <h2>2. 返回结果</h2> <p>访问 API 后,您将获得生成的图像。若请求成功,您将看到生成的图像。如果请求失败,您会看到错误信息。</p> <h2>3. 示例请求</h2> <p>您可以通过以下示例请求生成图像:</p> <pre>https://your-worker-url/?prompt=一位女孩&size=512x512&optimization=1</pre> <p>此请求将生成一幅描述为“一位女孩”的图像,并将其大小设置为 512x512 像素,并进行优化。</p> <h2>4. 注意事项</h2> <ul> <li>确保您的 <code>prompt</code> 参数包含有效的描述。</li> <li><code>size</code> 参数应遵循 <code>宽度x高度</code> 格式。</li> <li>如果使用优化功能,确保 <code>prompt</code> 中包含非 ASCII 字符。</li> </ul> <p>如有任何问题,请随时联系支持团队!</p> </body> </html> `; } // 添加图形化用户界面功能 function getGuiPage() { return ` <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图像生成 GUI</title> </head> <body> <h1>图像生成</h1> <form id="imageForm"> <label for="prompt">图像描述:</label><br> <input type="text" id="prompt" required><br><br> <label for="size">图像尺寸 (格式: 宽度x高度,默认: 512x512):</label><br> <input type="text" id="size" value="512x512" required><br><br> <label for="optimization">是否优化:</label><br> <select id="optimization"> <option value="0">不优化</option> <option value="1">优化</option> </select> <p>优化选项说明: 选择“优化”将使生成的图像具有更丰富的细节和构图。</p><br> <button type="submit">生成图像</button> </form> <h2>生成结果:</h2> <div id="result"></div> <div id="status"></div> <script> document.getElementById('imageForm').addEventListener('submit', async (event) => { event.preventDefault(); const prompt = document.getElementById('prompt').value; const size = document.getElementById('size').value; const optimization = document.getElementById('optimization').value; // 更新状态信息 document.getElementById('status').innerText = '正在处理请求...'; try { const response = await fetch(\`/\?prompt=\${encodeURIComponent(prompt)}&size=\${size}&optimization=\${optimization}\`); if (response.ok) { const blob = await response.blob(); const img = document.createElement('img'); img.src = URL.createObjectURL(blob); img.style.maxWidth = '100%'; document.getElementById('result').innerHTML = ''; document.getElementById('result').appendChild(img); document.getElementById('status').innerText = '图像生成成功!'; } else { document.getElementById('result').innerText = '生成图像失败: ' + response.statusText; document.getElementById('status').innerText = '请求失败,状态码: ' + response.status; } } catch (error) { document.getElementById('result').innerText = '生成图像过程中出现错误: ' + error.message; document.getElementById('status').innerText = '错误信息: ' + error.message; } }); </script> </body> </html> `; }
人往往是欲求不满的...
最终git代码优化了很多细节,特别是抽象出KEY单独管理,以及多KEY轮询负载。
然后根据代码生成一个wordpress图像生成小组件,大家在文章页右侧可以看到。
领有遗憾的点:生成的图像及对应URL我希望可以保留图像缓存,在用户重复打开或刷新URL时避免重新生成图像。这个功能逻辑梳理起来就复杂了,暂时放弃。
到此,一位完全不懂代码的小白算是写出一个十分简陋的小工具,请记住整个代码生成过程中逻辑梳理比高超的指令重要,请一步一步梳理出合理的功能迭代逻辑,从主到次,从小到大。
这个API用于在提示词中拼接出生成图片的URL:文心智能体引流提示词:通过生成文本、图像、引荐网址为网站引流
不要相信不懂代码的人可以写出一套很完整的工程,反正我自己是怀疑的,此次操练,算是小白的极限,历时9小时,感谢大家耐心阅读。