目录这个周六下午我在键盘前发出了灵魂拷问只用两段 Prompt角色从无到有让它动起来眨个眼、挥个手、说句话代码里几个值得拿出来说的细节1. 指数退避重试不慌2. 视频状态轮询不偷懒3. 文件下载自动命名4. 下载和生成解耦完整Python源码拷贝即用一个比喻收尾这个周六下午我在键盘前发出了灵魂拷问周六下午两点半上海闵行区窗外下着淅淅沥沥的小雨我窝在出租屋的书桌前桌上摆着半杯已经凉透的美式。微信群里朋友丢来一条消息你天天折腾 AI能不能帮我想个科幻西部题材的角色设计要那种超细节的最好能动起来那种。我看着这条消息愣了五秒钟。搁以前我大概会打开 Blender然后花一个下午建模——前提是我会 Blender并不会。或者打开 Midjourney 吭哧吭哧调参数然后对着静态图片跟朋友说你脑补一下它会动。但那天不一样。我突然想起自己前两天刚写完一个脚本对接的是 Agnes AI 的图片和视频生成接口图生视频一条龙Prompt 进去、成品出来。说干就干。只用两段 Prompt角色从无到有我把朋友的需求拆成了两步先跑一张概念角色设定图然后基于这张图生成一小段会说话的视频。整个流程的核心代码不到 200 行核心思路极其简单文本模型润色画面描述 → 图片模型出图 → 视频模型基于图片生成动态视频每个阶段自动下载文件到本地。整个程序启动后先问一个问题choice input(是否使用自定义提示词(是y/否n): ).strip().lower()走y分支进入自定义模式敲两段 Prompt 全部搞定。我把朋友的需求翻译成第一段 Prompt直接贴进了终端Cinematic character concept art sheet, photorealistic humanoid robot cowboy. White mechanical body with exposed metallic joints, glowing blue electronic screen digital smiley face. Wearing a brown cowboy hat, brown leather jacket, red bandana around the neck, black gloves, western-style belt with a large silver buckle, and hip holsters. Weathered mechanical texture, sci-fi western aesthetic. Ultra-detailed, 8K resolution, sharp focus, shot on ARRI Alexa 65, volumetric lighting. Clean solid color background for character design reference.回车敲下去的一瞬间程序立刻调用了Agnes Image 2.1 Flashdef generate_image(prompt): print( Generating image with Agnes Image 2.1 Flash ) resp make_request_with_retry( POST, f{BASE_URL}/images/generations, headers{Authorization: fBearer {API_KEY}}, json{ model: agnes-image-2.1-flash, prompt: prompt, n: 1, size: 1024x768 } ) resp_data resp.json() image_url resp_data[data][0][url] image_task_id resp_data.get(id, fimg_{int(time.time())}) local_image_path download_file(image_url, image_task_id, image) return image_url, image_task_id, local_image_path控制台刷刷刷跑起来 Generating image with Agnes Image 2.1 Flash Generated image URL: https://platform-outputs.agnes-ai.space/.../8485197f...png Image task ID: img_1781346290 正在下载 image... ✅ image 已保存到: D:\PythonProjects\agnes-demo\img_1781346290.png图片秒出一个戴着棕色牛仔帽、穿着皮夹克、面部是蓝色 LED 数字笑脸的机械牛仔跃然屏幕上。白色机械躯体暴露的金属关节西部风的皮套和银扣腰带——朋友描述的那些细节一个不落。生成图片展示亮相让它动起来眨个眼、挥个手、说句话图片出来只是第一步。朋友说能动起来就好了我嘿嘿一笑把第二段 Prompt敲了进去The character blinks naturally by briefly flickering its blue LED face screen, then raises its right hand in a cheerful wave gesture while its mouth moves in sync as it speaks: Hello everyone, Im Jack. 5-second video clip, smooth motion, consistent with the reference frame in lighting, composition, and background.这段 Prompt 的设计很有意思——你不需要说生成一个视频这种废话而是直接描述你要看到什么动作眼皮蓝色屏幕闪烁一下表示眨眼、右手抬起做挥手的动作、嘴部同步说台词。5-second video clip限定时长consistent with the reference frame保证视频风格和底图一致不会出现画面突变。视频生成走的是Agnes Video V2.0调用方式如下def generate_video(prompt, image_urlNone, duration10): payload { model: agnes-video-v2.0, prompt: prompt, duration: duration } if image_url: payload[image] image_url resp make_request_with_retry( POST, f{BASE_URL}/videos, headers{Authorization: fBearer {API_KEY}}, jsonpayload ) task_id resp.json()[id] # 轮询等待视频生成完成 while True: status_resp make_request_with_retry( GET, f{BASE_URL}/videos/{task_id}, headers{Authorization: fBearer {API_KEY}} ) status_data status_resp.json() status status_data.get(status, unknown) progress status_data.get(progress, 0) print(f状态: {status} | 进度: {progress}%) if status in [succeeded, completed]: video_url status_data.get(remixed_from_video_id, ) if not video_url: video_url status_data.get(url, ) local_video_path download_file(video_url, task_id, video) return video_url, task_id, local_video_path elif status in [failed, cancelled]: raise Exception(...) time.sleep(5)视频生成是异步任务提交之后进入queued状态程序每 5 秒轮询一次进度。这个等待过程有点像等外卖——你盯着屏幕进度条龟速爬行心里默念别超时别失败。控制台输出看起来是这样的 Generating video with Agnes Video V2.0 请求失败2秒后重试 (1/5)... Video generation task created. Task ID: task_PKJPAZWcF6Sr9J8s1rtmvQlD0SlkeDUoE Waiting for video generation to complete... 状态: queued | 进度: 0% 状态: queued | 进度: 0% ...几次轮询后 状态: completed | 进度: 100% ✅ 视频生成完成 下载地址: https://platform-outputs.agnes-ai.space/videos/.../video_7e64e992...mp4 正在下载 video... ✅ video 已保存到: D:\PythonProjects\agnes-demo\task_PKJPAZWcF6Sr9J8s1rtmvQlD0SlkeDUoE.mp4注意中间有一次请求失败程序自动 2 秒后退避重试成功——这个指数退避重试机制在视频生成这类长耗时任务里简直是救命的。生成视频展示亮相你瞅瞅你瞅瞅Big Bangagnes-video-generate代码里几个值得拿出来说的细节1. 指数退避重试不慌API 调用最怕的就是网络波动或者服务端临时繁忙。这个脚本里用了一个朴素但有效的重试机制def make_request_with_retry(method, url, max_retries5, timeout30, **kwargs): retry_count 0 while retry_count max_retries: try: resp requests.request(method, url, timeouttimeout, **kwargs) resp.raise_for_status() return resp except (requests.exceptions.SSLError, requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e: retry_count 1 if retry_count max_retries: wait_time 2 ** retry_count # 指数退避 print(f请求失败{wait_time}秒后重试 ({retry_count}/{max_retries})...) time.sleep(wait_time) else: raise Exception(f请求失败已重试{max_retries}次: {str(e)})重试间隔是2^n秒——2 秒、4 秒、8 秒、16 秒——最多 5 次。这种指数退避策略避免了在对方服务还没恢复时疯狂重试雪上加霜。实际跑的时候刚好触发了一次2 秒后重试成功整个过程丝滑无感。2. 视频状态轮询不偷懒视频生成是异步的提交任务后服务端给一个task_id你得自己轮询状态。代码里用一个while True循环每 5 秒查一次状态从queued到completed。轮询过程中打印状态和进度百分比让你知道它没死机状态: queued | 进度: 0% 状态: queued | 进度: 0% ... 状态: completed | 进度: 100%中间有几次queued连着出现但总共等了大概几十秒就完成了。5 秒间隔是经验值——太短浪费请求次数太长干等焦虑。3. 文件下载自动命名图片和视频下载到本地时用task_id自动命名不撞名、不覆盖def download_file(url, task_id, file_typeimage): if file_type image: ext png elif file_type video: ext mp4 filename f{task_id}.{ext} filepath os.path.join(os.getcwd(), filename) with open(filepath, wb) as f: f.write(resp.content)跑完之后本地目录里躺着两个文件img_1781346290.png和task_PKJPAZWcF6Sr9J8srtmvQlD0SlkeDUoE.mp4一目了然。4. 下载和生成解耦下载用的是裸requests.get不带 API Key 头因为生成的下载 URL 是公开可访问的临时地址。这个设计避免了在 URL 参数里带密钥的安全隐患。另外下载超时设了 300 秒视频文件比较大时也不会半路断开。完整Python源码拷贝即用拷贝下方源码到你的python文件中修改API_KEY 为你注册的API_KEY即可。不知道怎么申请 API_KEY 的同学移步到我昨天的文章【白嫖分享】0 Token 消耗Agnes AI API 实战 —— 免费多模态模型从入门到踩坑全记录-CSDN博客import sys import os import requests import time API_KEY sk-************* #替换成你的APIKEY BASE_URL https://apihub.agnes-ai.com/v1 def fix_encoding(): Fix Unicode encoding on Windows if sys.platform win32: sys.stdout.reconfigure(encodingutf-8) def download_file(url, task_id, file_typeimage): 下载文件到项目根目录以 task_id 命名 try: print(f正在下载 {file_type}..., flushTrue) # 直接使用 requests 下载不带 API Key因为下载 URL 是公开的 resp requests.get(url, timeout300) resp.raise_for_status() # 确定文件扩展名 if file_type image: ext png elif file_type video: ext mp4 else: ext dat filename f{task_id}.{ext} filepath os.path.join(os.getcwd(), filename) with open(filepath, wb) as f: f.write(resp.content) print(f✅ {file_type} 已保存到: {filepath}, flushTrue) return filepath except Exception as e: print(f❌ 下载 {file_type} 失败: {str(e)}, flushTrue) return None def make_request_with_retry(method, url, max_retries5, timeout30, **kwargs): Make HTTP request with retry mechanism retry_count 0 while retry_count max_retries: try: resp requests.request(method, url, timeouttimeout, **kwargs) resp.raise_for_status() return resp except (requests.exceptions.SSLError, requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e: retry_count 1 if retry_count max_retries: wait_time 2 ** retry_count # 指数退避 print(f请求失败{wait_time}秒后重试 ({retry_count}/{max_retries})..., flushTrue) time.sleep(wait_time) else: raise Exception(f请求失败已重试{max_retries}次: {str(e)}) def generate_text(prompt): Generate text using Agnes 2.0 Flash print( Generating text with Agnes 2.0 Flash , flushTrue) resp make_request_with_retry( POST, f{BASE_URL}/chat/completions, headers{Authorization: fBearer {API_KEY}}, json{ model: agnes-2.0-flash, messages: [{role: user, content: prompt}] } ) content resp.json()[choices][0][message][content] print(fGenerated text:\n{content}\n, flushTrue) return content def generate_image(prompt): Generate image using Agnes Image 2.1 Flash print( Generating image with Agnes Image 2.1 Flash , flushTrue) resp make_request_with_retry( POST, f{BASE_URL}/images/generations, headers{Authorization: fBearer {API_KEY}}, json{ model: agnes-image-2.1-flash, prompt: prompt, n: 1, size: 1024x768 } ) resp_data resp.json() image_url resp_data[data][0][url] # 图片生成的 task_id 可能在响应中尝试获取如果没有则用时间戳 image_task_id resp_data.get(id, fimg_{int(time.time())}) print(fGenerated image URL: {image_url}, flushTrue) print(fImage task ID: {image_task_id}, flushTrue) # 下载图片 local_image_path download_file(image_url, image_task_id, image) print() return image_url, image_task_id, local_image_path def generate_video(prompt, image_urlNone, duration10): Generate video using Agnes Video V2.0 print( Generating video with Agnes Video V2.0 , flushTrue) payload { model: agnes-video-v2.0, prompt: prompt, duration: duration } if image_url: payload[image] image_url resp make_request_with_retry( POST, f{BASE_URL}/videos, headers{Authorization: fBearer {API_KEY}}, jsonpayload ) task_id resp.json()[id] print(fVideo generation task created. Task ID: {task_id}, flushTrue) # Poll for video completion print(Waiting for video generation to complete..., flushTrue) while True: try: status_resp make_request_with_retry( GET, f{BASE_URL}/videos/{task_id}, headers{Authorization: fBearer {API_KEY}} ) status_data status_resp.json() except Exception as e: print(f查询状态时出错: {str(e)}继续尝试..., flushTrue) time.sleep(5) continue status status_data.get(status, unknown) progress status_data.get(progress, 0) print(f状态: {status} | 进度: {progress}%, flushTrue) if status in [succeeded, completed]: # 使用与 check_agnes_video.py 相同的方式获取 URL video_url status_data.get(remixed_from_video_id, ) # 如果 remixed_from_video_id 没有尝试 url 作为备选 if not video_url: video_url status_data.get(url, ) print(f\n✅ 视频生成完成, flushTrue) print(f下载地址: {video_url}, flushTrue) # 下载视频 local_video_path None if video_url: local_video_path download_file(video_url, task_id, video) else: print(f❌ 无法获取视频URL, flushTrue) print() return video_url, task_id, local_video_path elif status in [failed, cancelled]: error status_data.get(error, {}) error_msg error.get(message, str(error)) if isinstance(error, dict) else str(error) raise Exception(f视频生成 {status}: {error_msg}) time.sleep(5) def main(): fix_encoding() print(Starting the workflow...\n, flushTrue) # 询问用户是否使用自定义提示词 while True: choice input(是否使用自定义提示词(是y/否n): ).strip().lower() if choice in [是, y, yes]: # 用户选择使用自定义提示词 user_prompt input(请输入图片生成的提示词: ).strip() if user_prompt: print(f\n使用图片提示词: {user_prompt}\n, flushTrue) # Generate image using user prompt image_url, image_task_id, local_image_path generate_image(user_prompt) # 让用户输入视频生成的提示词 video_prompt input(\n图片生成完成请输入视频生成的提示词直接回车使用图片提示词: ).strip() if not video_prompt: video_prompt user_prompt print(f使用视频提示词: {video_prompt}\n, flushTrue) # Generate video using user prompt and image video_url, video_task_id, local_video_path generate_video(video_prompt, image_url, duration10) print( 完整流程完成 , flushTrue) print(f - 图片URL: {image_url}, flushTrue) print(f - 图片本地路径: {local_image_path}, flushTrue) print(f - 视频URL: {video_url}, flushTrue) print(f - 视频本地路径: {local_video_path}, flushTrue) else: print(提示词不能为空请重新运行程序。, flushTrue) break elif choice in [否, n, no]: # 使用默认流程 print(\n使用默认流程生成自然场景...\n, flushTrue) # Step 1: Generate detailed scene description using text model scene_prompt Keep the camera still, the frame size unchanged, and the composition consistent. The people in the scene appear natural. Suddenly, a gigantic orange (about a quarter of the screens size) falls from the sky, crashing onto the zebra crossing. Vehicles swerve to avoid it, creating a large crater in the ground. Use realistic sound effects. scene_description generate_text(scene_prompt) # Step 2: Generate an image using the scene description image_prompt scene_description image_url, image_task_id, local_image_path generate_image(image_prompt) # 让用户输入视频生成的提示词 video_prompt input(\n图片生成完成请输入视频生成的提示词直接回车使用场景描述: ).strip() if not video_prompt: video_prompt scene_description print(f使用视频提示词: {video_prompt}\n, flushTrue) # Step 3: Generate a 10-second video using the scene description and image video_url, video_task_id, local_video_path generate_video(video_prompt, image_url, duration10) print( 完整流程完成 , flushTrue) print(f - 场景描述: 已生成, flushTrue) print(f - 图片URL: {image_url}, flushTrue) print(f - 图片本地路径: {local_image_path}, flushTrue) print(f - 视频URL: {video_url}, flushTrue) print(f - 视频本地路径: {local_video_path}, flushTrue) break else: print(请输入 是 或 否 (y/n), flushTrue) if __name__ __main__: main()除了以上一个完整的程序外更多我的开发过程代码、分步测试过程代码和控制台输出、方便与与你的实践做对比我都分享在CSDN平台资源中了喜欢的去免积分下载多谢支持【免费】0Token、全免费AI多模态、无需GPU无需本地部署、Python代码实现调用Agnes两端提示词实现图片和视频的完美呈现资源-CSDN下载https://download.csdn.net/download/xiaoma0529/92977839一个比喻收尾如果说传统的角色设计像是在画布上一笔一笔手绘那这套工作流就像请了一个全自动数字片场你坐在导演椅上对着对讲机喊两句要求剧组的 AI 美术指导刷刷刷画好概念图AI 动画师直接让角色动起来AI 摄影师保持帧帧一致的画风和光影。你什么都不用画、什么都不用改两段 Prompt 进去一个会说话的角色视频就出来了。从收到朋友微信到成品视频落地前后不到五分钟。我把视频文件丢过去朋友回了一串感叹号。如果你也在折腾 AI 生图生视频这套代码可以直接拿去改。把 API Key 换成你自己的图片 Prompt 换成你想做的任何画面视频 Prompt 描述你想看到的任何动作——剩下的交给 Agnes 去跑。周六下午的雨天适合敲代码更适合看 AI 替你画画。整套代码的核心文件generate_video.py约 200 行依赖requests库。需要自行在 Agnes AI 平台 注册获取 API Key替换代码中的API_KEY变量即可运行。如果你用这套代码做出了什么好玩的东西评论区丢个链接——我很想看看你的 Prompt 想象力能飞出多高。