技术博客
精品长文一键写作:任务写完了没有?看 task_status 三种状态

精品长文一键写作:任务写完了没有?看 task_status 三种状态

作者: 万维易源
2026-09-15
精品长文一键写作task_status异步任务轮询
# 精品长文一键写作:任务写完了没有?看 task_status 三种状态 > 接口:精品长文一键写作(apiCode=3206)· 接入点:3206-2 查询任务列表 / 3206-3 查询文章详情 > 计费:计次收费(3206-2 与 3206-3 均为 0 厘/次)· 请求方式:POST/GET · 返回格式:JSON > 适用人群:接入中的开发者 · 阅读时间:约 6 分钟 · **最后实测核对:2026-09-15** ## 核心要点 - `task_status` 只有三个值:`preparing`(准备执行)、`writing`(执行中)、`success`(执行完成)。 - 想拿正文只能用 `3206-3`,并且它在文章未完成时返回 **HTTP 450**;`3206-2` 只给列表概览。450 的响应体随 `need_md` 变化,且 `ret_code` 会返回 **-1**。 - 长文生成是长耗时任务。2026-09-15 实测两个任务,一个约 7 分钟完成,另一个提交后 87 分钟仍是 `writing`、未出稿。 ## task_status 的三种取值 精品长文一键写作(apiCode=3206)用 `task_status` 字段表达任务进度,`3206-1`、`3206-2`、`3206-3` 三个接入点返回的取值集合一致。 | 取值 | 含义 | 这个阶段能拿到什么 | |------|------|------------------| | `preparing` | 准备执行 | 只有 `task_id` 和 `topic`,正文与标题都还没有 | | `writing` | 执行中 | `3206-2` 里 `title` 为空字符串、`content_length` 为 0;`3206-3` 返回 HTTP 450,`ret_code` 为 -1 | | `success` | 执行完成 | `3206-3` 返回 `title`、`content`、`keywords`、`outline`、`imgs` | 状态是单向推进的,不会从 `writing` 退回 `preparing`。文档没有给出第四种终态,也没有「失败」这个状态值;调用层面失败走的是 `ret_code`(`3206-1`、`3206-2` 为 0 成功 / -1 失败),任务层面没写完就是一直在 `writing`。 2026-09-15 实测两个任务:一个英文主题约 7 分钟内变为 `success`;另一个中文主题提交后 87 分钟仍是 `writing`,`content_length` 一直为 0,始终没出稿。样本只有两次,无法据此推断耗时分布,也不能判断这属于偶发还是常态。结论是:客户端必须按业务容忍度设等待上限,不要写死一个偏大的固定秒数——超时后用 `3206-4` 清掉任务再重提,比无限等下去更可控。 ## 两个查询入口怎么分工 `3206-2` 和 `3206-3` 都能看到状态,用途不一样。 | 对比项 | 3206-2 查询任务列表 | 3206-3 查询文章详情 | |------|------|------| | 入参 | `page`(页码,可选,默认 1) | `task_id`(必填)、`need_md`(可选) | | 返回范围 | 近 30 天内的任务,一页最多 20 条 | 单个任务的完整内容 | | 是否带正文 | 不带,只有 `content_length` 这个长度值 | 带,`content` 是全文 | | 响应封装 | 有 `showapi_res_body` 一层 | 透传模式,字段在顶层 | | 未完成时的表现 | 正常返回 JSON,状态为 `writing` | 返回 HTTP 450;`need_md=2` 时为 JSON(`ret_code=-1`),`need_md=1` 时为纯文本 | | 适合什么时候调 | 做任务总览、批量看进度、翻历史记录 | 盯单个任务直到出稿 | 后台管理界面用 `3206-2` 拉列表,具体取稿的那一步用 `3206-3`,这是最省的组合:列表页一次拿 20 条状态,只有需要正文的那几条才去调详情。 `3206-2` 的列表里 `topic` 会截断。2026-09-15 实测,提交时传入 25 个字符的 `topic`,列表里返回的是前 20 个字符加 6 个点: ```json "topic": "写一篇介绍昆明气候特点的短文,300字左......" ``` 在列表页展示时按这个长度预留版面,需要完整 `topic` 就去调 `3206-3`。 ## 轮询怎么写 最省事的做法是按固定间隔查,查到就停。生成中的响应不是 JSON,所以判断顺序是「先看状态码,再看字段」。 ```python import time import requests def poll_article(task_id, appkey, interval=15, max_wait=900): """轮询 3206-3 直到出稿。返回详情 dict,超时抛异常。""" base = "https://route.showapi.com" deadline = time.time() + max_wait while time.time() < deadline: time.sleep(interval) r = requests.post( f"{base}/3206-3", params={"appKey": appkey}, data={"task_id": task_id, "need_md": "2"}, # 2 返回 JSON;传 1 会直接返回 Markdown 原文 timeout=60, ) if r.status_code == 450: continue # 生成中,等下一轮 if r.status_code != 200: r.raise_for_status() # 其它异常交给上层 detail = r.json() if detail.get("task_status") == "success": return detail if detail.get("ret_code") not in (0, None): raise RuntimeError(f"取稿失败:{detail.get('remark')}") raise TimeoutError(f"任务 {task_id} 超时未完成") ``` `interval` 别设太小。接口并发量是 2 次/秒,单个任务 15 秒一查已经足够密;一篇长文生成按分钟计,每秒都在查只是白占配额。 任务多的时候,把轮询改成「先问列表,再问详情」更划算: ```python def poll_batch(task_ids, appkey, interval=20): """先用 3206-2 扫全量状态,只对写好的任务调 3206-3 取稿。""" base = "https://route.showapi.com" pending = set(task_ids) done = {} while pending: time.sleep(interval) page, pages = 1, 1 while page <= pages: r = requests.post(f"{base}/3206-2", params={"appKey": appkey}, data={"page": page}, timeout=60).json()["showapi_res_body"] pages = r.get("allPages", 1) or 1 for item in r["contentlist"]: if item["task_status"] == "success" and item["task_id"] in pending: pending.discard(item["task_id"]) done[item["task_id"]] = item page += 1 return done ``` 注意空列表时 `allPages` 会是 0,上面用 `or 1` 兜住,否则分页循环会直接跳过。 ## 超时与重试 `3206-3` 的读写超时按 OpenAPI 文档声明是 60 秒。生成阶段返回的 450 属于中间状态,不属于超时,重试是正常路径而不是异常处理。 真正需要重试的是网络层错误和 5xx。给这类错误加指数退避,别把 450 也当成失败去重试,那样会堆无意义的请求。 任务提交本身不需要幂等保护:`3206-1` 每次调用都会新建一个任务,返回新的 `task_id`。重复提交的后果是产生两篇稿子和两次计费,所以客户端要自己防重复点击。 ## 计费与调用方式 `3206-2` 与 `3206-3` 的单次费率都是 0 厘,也就是轮询这两个接入点不额外产生费用;真正计费的是 `3206-1`(300 厘/次)和 `3206-5`(260 厘/次)。调用成功才计费,失败不扣费。档位与其余费率见接口详情页的「产品价格」标签。 ## FAQ **Q1:多久轮询一次比较合适?** 15 到 30 秒一次。2026-09-15 实测两个任务:一个英文主题约 7 分钟完成,另一个中文主题提交后 87 分钟仍是 `writing`。间隔太短拿到的都是同一个状态,还占用 2 次/秒的并发额度。 **Q2:`3206-3` 返回 HTTP 450 是出错了吗?** 不是出错,是任务还在生成中。2026-09-15 实测:`need_md=2` 时响应体是 `{"ret_code":-1,"task_status":"writing","remark":"文章正在生成中,请稍后!"}`,`need_md=1` 时是一行纯文本。注意生成中的 `ret_code` 就是 -1,所以判断逻辑要先看状态码 450,再看 `task_status`,不能只看 `ret_code`。 **Q3:为什么 `3206-2` 里 `title` 是空的?** 任务还在 `writing` 阶段,标题和正文都还没生成,所以 `title` 为空字符串、`content_length` 为 0。等状态变 `success` 再读这两个字段。 **Q4:任务一直停在 `writing` 怎么办?** 先确认提交后已经过了多久,长文生成按分钟计。若超过业务容忍时间仍未完成,用 `3206-4` 删除失败任务,再重新提交一次;删除接口需要传 `task_id`。 **Q5:有回调推送吗?** 文档没有提供回调地址参数,任务完成不会主动通知。取稿靠主动轮询,这也是 `3206-2` 和 `3206-3` 存在的意义。 ## 下一步阅读 - 返回字段逐个说明:[精品长文一键写作返回字段与 ret_code 说明](https://www.showapi.com/guides/longform-writing-response-fields-3206) - 批量任务的调度设计:[把批量写作任务排成队列的几种做法](https://www.showapi.com/guides/longform-writing-batch-queue-3206) - 最小可用流程:[用 Python 提交第一个写作任务并取回成稿](https://www.showapi.com/guides/longform-writing-quickstart-3206) - **本系列共 9 篇**:查看[精品长文一键写作指南总目录](https://www.showapi.com/guides/longform-writing-guides-3206)