Skip to main content

Eurybia Frontend API(Ask + Plaza)· 供 didotxyz 团队

本文档是给 didotxyz 团队对接 Ask 圆桌对话 + Plaza 端点的唯一规范(2026-09-20 收口统一 API 平台)。 原 /api/frontend/* 免鉴权路径已迁移:/api/frontend/ask/* → /v1/ask/*,/api/frontend/plaza/* → /v1/data/frontend/plaza/*。 需要 X-API-Key 的单轮 KOL 问答端点见 ask-api-quickstart;统一鉴权/限流规则见 gateway。

Base URL​

https://api.eurybia.xyz

鉴权与跨域​

  • 鉴权:所有请求带 X-API-Key 请求头(didotxyz 的 key 不变,随平台统一鉴权)。缺失/无效 → 401,无路径权限 → 403,限流/日配额超限 → 429(详见 gateway)。
  • CORS 白名单:浏览器跨域调用时,仅白名单域名被放行。默认白名单 eurybia.xyz / di.xyz(含 www. 与 http 变体),配置在 app/core/config.py 的 cors_allow_origins(环境变量同名可覆盖,逗号分隔)。
  • 非浏览器调用(curl / 服务端)不受 CORS 限制,直接请求即可。
  • 模型固定:所有 Ask 端点服务端固定使用 deepseek-flash,请求体无 model 字段,客户端不可选模型。
  • 调用额度:Ask 端点无限流、不限额、不扣点(服务端不记账,扣点/积分由 DI 后台自管)。Plaza 另有每小时发帖限流(见注意事项)。

一、Ask —— KOL 圆桌讨论(拆解 + 三轮 + 思考模式)​

0. 问题拆解(含可选澄清)​

POST /v1/ask/decompose
Content-Type: application/json

请求体

{
"q": "BTC 现在还能上车吗?",
"idempotency_key": "可选,复用它作为 conversation_id(幂等)"
}

响应

{
"data": {
"standard_question": "发生美国降息事件时,BTC 将如何反应?",
"asset": "BTC",
"event": "美国降息",
"subject": "价格走势",
"period": null,
"clarify_question": "...",
"conversation_id": "a0dc3708..."
}
}
  • asset:用户问题中涉及的具体资产/市场/赛道(如 BTC、ETH、meme 币、DeFi)。🔴 保留用户明确提到的标的,不泛化不丢失;无明确标的时为 null。
  • clarify_question:非 null 时前端应展示输入框让用户补充说明,补充后带原始问题 + 说明重新调用本端点;null 表示无需澄清。
  • conversation_id:本轮对话唯一 ID。传 idempotency_key 则复用它(同一请求重试不会生成新记录)。字符串原样透传,不做任何规范化:带不带横线的 UUID 都原样存储、原样返回;不传 idempotency_key 时服务端生成 32 位无横线 hex(uuid4().hex)。
  • 本端点会写入 didotxyz.public.ask_conversations(含 asset 列,status=in_progress)。
  • 🔴 幂等:相同 idempotency_key 重复调用 → 复用同一 conversation_id,不重调模型、不重复计费,响应带 "cached": true。

1. 列出可用 KOL​

GET /v1/ask/kols[?embed_avatar=true]

响应

{
"data": [
{"handle": "SexyMichill", "display_name": "Michill💕", "avatar": "https://...", "profile": "..."},
{"handle": "cz_binance", "display_name": "CZ 🔶 BNB", "avatar": "https://...", "profile": "..."}
]
}
  • embed_avatar:可选布尔。默认 false → avatar 返回 pbs 直链(链接形式);true → avatar 返回 data URI(data:image/...;base64,...,图片内嵌,由服务端缓存兜底避免 pbs 404)。前端 <img src={avatar}> 两种形式都能直接渲染。
  • handle 规则:目录返回 X 上的真实 handle(原始大小写),如 PhyrexNi、100trillionUSD、saylor。所有端点接收 handle 参数时大小写不敏感(服务端内部按小写规范化查找);round1/round2/selected_kols 里回传的 handle 是前端传入的原值(通常即目录返回值)。

2. 搜索 KOL​

GET /v1/ask/kols/search?q=<关键词>
  • q:关键词,对 handle / display_name / profile 模糊匹配(大小写不敏感)
  • embed_avatar:可选布尔,同 /ask/kols(默认 pbs 直链,true 返回 data URI)。
  • 响应 {"data": [{"handle": ..., "display_name": ..., "avatar": ..., "profile": ...}]}(最多 20 条)

3. 自动挑选 KOL​

GET /v1/ask/match?q=<问题>[&exclude=handle1,handle2]
  • q:问题(1~2000 字符,需 URL 编码)
  • exclude:可选,逗号分隔的 handle,排除已选 KOL(换一组 / 替换时用)

响应

{"data": {"selected": [
{"handle": "PhyrexNi", "display_name": "Phyrex", "avatar": "https://...", "reason": "擅长链上数据,能提供 BTC 走势判断", "available": true}
]}}

selected 为 3 个对象,按相关性排序;reason 为推荐理由。

4. 第一轮:KOL 流式回答(含思考过程)​

GET /v1/ask/stream/{handle}?q=<问题>&conversation_id=<会话ID>
  • handle:KOL 真实 handle(原始大小写,如 PhyrexNi / saylor;大小写不敏感,服务端按小写规范化查找)
  • conversation_id:🔴 必填,decompose 返回的会话 ID。
  • 返回 text/plain 流式响应,先流式输出思考过程,再流式输出正文。协议见 §流式协议。
  • 幂等:该 KOL 第一轮已答过(round1 里有)→ 模拟打字机直接返回缓存(不重调模型),流式格式不变。

5. 第二轮:KOL 流式点评其他 KOL(含思考)​

POST /v1/ask/comment/{handle}
Content-Type: application/json

请求体

{
"q": "BTC 现在还能上车吗?",
"conversation_id": "a0dc3708...(decompose 返回的 id,必填)",
"others": [
{"handle": "cz_binance", "display_name": "CZ 🔶 BNB", "answer": "..."},
{"handle": "naval", "display_name": "Naval", "answer": "..."}
]
}
  • conversation_id:🔴 必填,decompose 返回的会话 ID。
  • others:其他 KOL 的第一轮回答列表(不含自己)
  • 返回 text/plain 流式响应(思考 + 评论正文)
  • 幂等:该 KOL 第二轮已评过(round2 里有)→ 模拟打字机直接返回缓存(不重调模型)。

6. 第三轮:主持人基于事实的结构化报告(含思考)​

POST /v1/ask/conclude/v2
Content-Type: application/json

请求体

{
"q": "BTC 现在还能上车吗?",
"conversation_id": "a0dc3708...(decompose 返回的 id,必填)",
"round1": [
{"handle": "saylor", "display_name": "Saylor", "answer": "..."},
{"handle": "cz_binance", "display_name": "CZ 🔶 BNB", "answer": "..."}
],
"round2": [
{"handle": "saylor", "display_name": "Saylor", "comment": "..."},
{"handle": "cz_binance", "display_name": "CZ 🔶 BNB", "comment": "..."}
]
}
  • 返回 text/plain 流式响应:思考过程 + JSON 报告(见 §流式协议)。
  • 主持人为 didotxyz 人设,总结时注入事实包:top 10 新闻(3 天)+ FRED 宏观数据(3 天发布,含最新值/上期值/环比)。
  • 🔴 证据搜集时机:事实包由服务端在 conclude/v2 阶段拉取,且不是按本次问题做定向检索——而是「最近 3 天通用 top 事实」(新闻按评分取 top 10、FRED 取 3 天发布的核心指标)。第一轮 stream 与第二轮 comment 不做任何检索,纯靠 KOL 人设 + 问题生成。
  • 流式结束后服务端后台写入 ask_conversations.conclusion(status=completed)。
  • 🔴 幂等:相同 conversation_id 重复调用 → 直接返回缓存的完整报告(不重调模型、不重复计费),流式响应为一条 {t:c} 完整 JSON + {t:done}。
  • 当前端点为 /ask/conclude/v2。

报告 JSON 结构(流式 content 拼合后解析,9 字段)

{
"long_conclusion": "综合结论长文…",
"stage_impacts": [{"stage": "短期", "impact": "...", "evidence": [{"text": "引用描述", "source": "Cointelegraph", "url": "https://...", "time": "2026-09-02"}]}],
"kol_summaries": [{"handle": "saylor", "position": "..."}],
"consensus": ["共识点1"],
"divergence": ["分歧点1"],
"facts": [{"fact": "...", "evidence": [{"text": "引用描述", "source": "FRED H.15", "url": "https://fred.stlouisfed.org/series/DGS10", "time": "2026-09-01"}]}],
"inferences": [{"inference": "...", "confidence": 65, "risk": "..."}],
"assumptions": ["假设1"],
"unknowns": ["未知点1"]
}
  • stage_impacts[].evidence 与 facts[].evidence 是对象数组 {text, source, url, time}:text 引用描述、source 来源名、url 原文/数据链接(可点击)、time 数据/发布日期(YYYY-MM-DD,新闻为发布时间,FRED 为数据日期)。url/time 无对应值时为 null。
  • 前端渲染建议:url 非空时把 text 包成 <a href=url target=_blank>,否则纯文本;time 可跟在链接后以小字展示。

6.5 按 conversation_id 查询对话状态(断网/关页面后找回)​

GET /v1/ask/history/{conversation_id}
  • 根据 decompose 返回的 conversation_id 查询一次对话的状态与最终结论。
  • 不存在返回 404({"detail":{"code":"NOT_FOUND","message":"..."}})。

响应

{
"data": {
"conversation_id": "a0dc3708...",
"status": "completed",
"raw_question": "BTC 现在还能上车吗?",
"standard_question": "...",
"asset": "BTC",
"event": "...",
"subject": "...",
"period": null,
"selected_kols": [{"handle": "saylor"}],
"round1": [{"handle": "...", "display_name": "...", "answer": "..."}],
"round2": [{"handle": "...", "display_name": "...", "comment": "..."}],
"conclusion": { ... 完整报告对象 ... },
"error_code": null,
"created_at": "2026-09-04T00:00:00",
"updated_at": "2026-09-04T00:01:30"
}
}
  • status:in_progress(生成中)| failed(已中断,带 error_code)| completed(已写入 conclusion)。
  • error_code:失败时的结构化原因(如 CONCLUDE_EMPTY / CONCLUDE_INVALID_JSON / CLIENT_ABORT),非 failed 时为 null。
  • 前端进入时按 conversation_id 查一次:completed 直接渲染 conclusion,failed 提示“已中断”并可选展示 error_code,in_progress 提示“生成中”,不存在提示重做。

响应 headers(200 / 404 均带)

content-type: application/json; charset=utf-8
X-Request-Id: <32 位 hex>

X-Request-Id 每次请求随机生成,仅用于日志关联,无幂等语义(幂等靠 conversation_id)。

三态样例

  1. completed(HTTP 200)—— 完整字段见上方响应示例,status="completed",round1/round2/conclusion 非空。

  2. in_progress(HTTP 200)—— decompose 已落库、尚未 conclude 的典型形态:

{
"data": {
"conversation_id": "0cd565ae1dc34d2ea059f08b2a5a6ea6",
"status": "in_progress",
"raw_question": "怎么看 meme 币的这波行情?",
"standard_question": "发生 meme 币这波行情时,投资者将如何反应?",
"asset": "meme 币",
"event": "这波行情",
"subject": "投资者",
"period": null,
"selected_kols": [],
"round1": [],
"round2": [],
"conclusion": null,
"error_code": null,
"created_at": "2026-09-09T06:38:19.787194",
"updated_at": "2026-09-09T06:38:19.787194"
}
}
  1. failed(HTTP 200)—— 中断/失败,error_code 说明原因:
{
"data": {
"conversation_id": "f42e5d61-660a-4645-9477-ea58e94c0df4",
"status": "failed",
"raw_question": "如果结构性机会浮现,会对加密市场产生什么影响?",
"error_code": "CLIENT_ABORT",
"round1": [ ... 已保存的部分发言 ... ],
"round2": [],
"conclusion": null
}
}
  1. 404 NOT_FOUND(HTTP 404)—— conversation_id 不存在:
{"detail": {"code": "NOT_FOUND", "message": "conversation not found: <id>"}}

conclusion 语义边界:conclusion 只含第三轮(最终)结构化报告(9 字段),不含第一/二轮正文。四阶段完整记录在同一次响应的其他字段,无需额外接口:

阶段字段格式
问题拆解standard_question / asset / event / subject / period字符串,可为 null
KOL 选择selected_kols[{"handle": "..."}]
第一轮回答round1[{"handle","display_name","answer"}]
第二轮评论round2[{"handle","display_name","comment"}]
第三轮总结conclusion9 字段报告对象,或 null
  • conclusion 内的 evidence 在 facts[].evidence 与 stage_impacts[].evidence,每条为 {text, source, url, time};inferences[].confidence 为 0~1 小数、inferences[].risk 为风险观察。
  • 主动标记中断:POST /v1/ask/abort,body {conversation_id, error_code?}(error_code 可选,默认 CLIENT_ABORT)。前端确认放弃时调用,把对话标记为 failed。
  • 客户端断开不影响生成:stream/comment/conclude/v2 的生成与转发已解耦,客户端断开只停止转发、生成会继续跑完并落库。用户断网/关浏览器后,回头查 history 能拿到完整结果(与是否在线无关)。
  • 仅当模型空输出/报告 JSON 解析失败时才标记 failed(CONCLUDE_EMPTY / CONCLUDE_INVALID_JSON);客户端断开不再是失败原因。
  • 时间格式:created_at/updated_at 为 ISO 字符串(YYYY-MM-DDTHH:MM:SS.microseconds,无时区后缀);evidence 里的 time 随源保留原样(新闻 YYYY-MM-DDTHH:MM:SS,FRED YYYY-MM-DD)。

7. 简化版:一键自动问答(非流式)​

GET /v1/ask/auto?q=<问题>[&handles=saylor,cz_binance]
  • 自动挑选 1~3 个 KOL 并一次性返回回答(非流式、无思考过程)
  • handles:可选,逗号分隔的 handle,传了则跳过自动匹配

响应

{
"data": {
"selected": ["saylor", "cz_binance"],
"answers": [
{"handle": "saylor", "display_name": "Michael Saylor", "in_scope": true, "answer": "..."},
{"handle": "cz_binance", "display_name": "CZ 🔶 BNB", "in_scope": true, "answer": "..."}
]
}
}

8. 简化版:单 KOL 问答(非流式)​

GET /v1/ask/{handle}?q=<问题>
  • 与指定 KOL 单轮对话,非流式、无思考过程
  • 响应:{"data": {"handle": "...", "display_name": "...", "in_scope": true|false, "answer": "..."}}
  • in_scope=false 时 answer 固定为「问题超出回答范围」

二、Plaza —— KOL 新闻评论 timeline​

帖子列表(分页)​

GET /v1/data/frontend/plaza/posts?limit=20&offset=0
参数说明
limit每页条数,默认 20,范围 1~200
offset偏移量(分页)

响应

{
"data": [
{
"id": 711,
"handle": "saylor",
"display_name": "Michael Saylor",
"avatar": "https://...",
"post_text": "Bitcoin is Digital Capital...",
"news_id": 600410539,
"news_title": "Multicoin Capital 近9小时将2170万美元HYPE转入Coinbase Prime",
"news_source": "PANews RSS",
"news_link": "https://...",
"news_time": "2026-09-02T01:14:00",
"created_at": "2026-09-02T01:14:00"
}
],
"count": 20,
"total": 715
}
  • total 用于判断是否还有更多(offset + count < total)
  • created_at 为 UTC 时间(无时区 ISO 字符串,前端需按 UTC 解析)

流式协议​

第一轮 / 第二轮 / 第三轮端点均返回 text/plain 流,每行一个 JSON 对象(\n 分隔):

{"t":"r","d":"思考增量"}
{"t":"c","d":"正文增量"}
{"t":"e","code":"EMPTY_CONTENT","d":"错误信息","retryable":true}
{"t":"done"}
t含义
r思考过程增量(reasoning_content)
c正文增量(content)。第一/二轮为纯文本;第三轮为 JSON 报告的片段
e错误(d 为错误描述,code 为稳定错误码,retryable 为是否可重试,出现后流即结束)
done正常结束标记(只出现在 c 正文非空时)
  • 思考过程先于正文到达;前端通常先流式展示思考(小字),思考结束后折叠,再展示正文。
  • 客户端需按行缓冲解析(最后一个 chunk 可能不含完整行,需保留到下一 chunk 拼接)。

错误码与重试​

错误行含 retryable 布尔字段,前端只看这个字段决定是否重试,无需理解每个 code:

code触发retryable前端动作
EMPTY_CONTENT模型空输出(不说话)✅同一 conversation_id 自动重试(空结果未落库,会重新调模型)
TIMEOUT / REQUEST_ERROR超时 / 断连✅自动重试
DEEPSEEK_HTTP_429 / DEEPSEEK_HTTP_5xx限流 / 服务端错✅退避后重试
CONTENT_RISK400 Content Exists Risk(前文含敏感词)❌停止,并短路整个 conversation
DEEPSEEK_HTTP_4xx(其他)格式 / 鉴权 / 余额❌显示错误
API_KEY_MISSING / INTERNAL_ERROR配置 / 未知❌显示错误

重试规则:retryable=true 时,用同一个 conversation_id 重试(幂等缓存只在"已成功落库"时命中,失败未落库会重新调模型)。建议最多重试 2 次,429 加 1s 退避。

⚠️ 幂等缓存返回的 JSON 键顺序可能不同:命中缓存时,后端返回的是 json.dumps 重新序列化的 JSON,键顺序可能与首次流式输出不同(字节不同、语义相同)。前端应 json.loads 后处理,不要对 content 字符串做精确比较。

🔴 CONTENT_RISK 是短路信号:它表示前文(round1 其他 KOL 的回答)含有敏感词,不是当前 KOL 偶发失败。重试会重复发送同一段被污染的前文,必继续 400。前端收到后应:

  1. 停止当前流;
  2. 标记该 conversation 为 risk,不再发起后续任何调用(其余 KOL 的 round2、conclude 都跳过);
  3. UI 提示"该话题涉及敏感内容,无法继续生成",引导用户换问题重开。

错误响应(非流式端点)​

非流式端点(decompose / match / kols/search / auto / 单KOL)的参数错误返回结构化 detail:

{"detail": {"code": "EMPTY_Q", "message": "q must not be empty"}}
code说明
EMPTY_Qq 缺失或为空
INVALID_BODY请求体字段类型非法(如 round1/exclude 非数组)
UNKNOWN_KOL未知 KOL handle

三轮编排流程(前端参考)​

decompose → match(可换一组,可删除/搜索添加)→ [第一轮:并发 stream 每位 KOL] → [第二轮:并发 comment 每位 KOL] → [第三轮:conclude/v2]
  1. decompose 得到 conversation_id + 拆解结果;若 clarify_question 非空则让用户补充后重拆
  2. match 得到 3 位 KOL(含 reason);前端可「换一组」(match + exclude)或「删除 + 搜索添加」
  3. stream 并发流式回答每位 KOL(第一轮)
  4. 若 selected.length > 1,进入第二轮:comment 并发点评(others = 其余 KOL 的第一轮回答)
  5. 第三轮 conclude/v2 输入 = 第一轮全部回答 + 第二轮全部评论 + conversation_id
  6. 思考模式:第一/二轮 reasoning_effort=low;第三轮(主持人)也为 low(控制耗时 < 30s)

注意事项​

  • 超时:完整三轮对话实测约 15~30s(主持人思考已降为 low);客户端超时建议 ≥ 120s,流式读取期间注意代理 read_timeout(服务端 nginx api.eurybia.xyz 已设 300s)。
  • 流式缓冲:nginx api.eurybia.xyz 已开 proxy_buffering off(2026-09-07),流式内容逐步输出(首字节 < 1s,思考→正文按真实生成节奏逐行到达),前端可据此驱动头像发言/文字/动效动画。
  • 并发:全部 LLM 端点(Ask 流式 + Ask 非流式 + Plaza 生成)均已改为异步实现(httpx,不占线程池),单实例可承受数百并发(实测 200 并发全成功,首字节延迟中位 1.5s);实际瓶颈在 DeepSeek 服务端(Flash 并发上限 2500)。
  • 语言跟随:回答语言与提问语言一致——中文提问用中文回答,英文提问用英文回答(覆盖第一/二/三轮,含思考过程与总结)。
  • Plaza 限流:KOL 池扩容后,Plaza 每个 KOL 每小时最多发 1 条,且只对「足够重要」的新闻发帖(宁缺毋滥);plaza/posts 每小时新增条数会相应减少。
  • 不缓存:每次调用独立生成。
  • 免责:KOL 回答与总结均为 AI 人设生成,不构成投资建议。

变更记录​

日期变更对接影响
2026-09-20🔴 统一 API 平台收口:base URL 换 https://api.eurybia.xyz;路径 /api/frontend/ask/* → /v1/ask/*、/api/frontend/plaza/* → /v1/data/frontend/plaza/*;鉴权从 keyless 改为统一 X-API-Key(key 已签发,didotxyz 侧不用换)🔴 破坏性(必须切新 base URL + 路径 + 加 key 头;旧 /api/frontend/* 路径保留过渡)
2026-09-02Ask 非流式端点(match/auto/单KOL)+ Plaza 生成异步化,全部 LLM 端点不再占用线程池无(接口契约不变)
2026-09-02新增语言跟随:中文提问→中文回答,英文提问→英文回答无(新增能力,不破坏现有调用)
2026-09-02移除 KOL 回答中的 DYOR/NFA 免责词要求轻微(回答不再强制带 DYOR/NFA,前端无需处理)
2026-09-03KOL 池扩容:9 → 45 个(新增 19 个人类 crypto/财经 KOL + 17 个中文 KOL)无(/ask/kols 返回更多,match 可选范围更大)
2026-09-03Plaza 限流:每个 KOL 每小时最多发 1 条,只对「足够重要」的新闻发帖轻微(Plaza 帖子密度下降)
2026-09-03Ask 流程重构:新增 decompose(含 clarify + idempotency_key)、kols/search、kols/reselect;match 返回对象数组(含 reason);conclude 替换为 conclude/v2(9 字段结构化报告)🔴 破坏性(match 响应结构变更、conclude 已移除)
2026-09-03Ask 全面 review 12 项修复:conclude 写库改 BackgroundTask、错误码结构化(EMPTY_Q/INVALID_BODY/UNKNOWN_KOL)、decompose 幂等、fred 查询优化(target_series 先筛 + popularity 入 GROUP BY)、建表去重、前端删除/添加 KOL + clarify 交互 + 重试不闪回无(接口契约不变,新增 idempotency_key 可选参数)
2026-09-03主持人思考 reasoning_effort 由 high 降回 low(控制单轮 < 30s);max_tokens 6000→12000;新增 EMPTY_CONTENT 空正文检测无(流式新增 code 字段,前端可据此重试)
2026-09-03decompose 新增 asset 字段(标的/市场),避免拆解丢失用户所问市场;对话表加 asset 列轻微(decompose 响应新增字段,老客户端忽略即可;不影响现有字段)
2026-09-04conclude/v2 的 evidence 从字符串改为对象数组 {text, source, url}(URL 可点击,来自新闻 link / FRED 系列页);新增 GET /ask/history/{conversation_id}(断网找回状态与结论)🔴 破坏性(evidence 结构变更,前端需适配新格式);新增端点不影响现有调用
2026-09-07evidence 增加 time 字段(YYYY-MM-DD,新闻发布时间/FRED 数据日期);nginx /api/frontend/ 开 proxy_buffering off 修复流式缓冲(此前首字节约 9.7s、内容挤在一起)轻微(evidence 新增字段,老客户端忽略即可);流式节奏修复,前端可跟随真实进度
2026-09-09文档补全:模型固定说明、Ask 无限流不限额、handle 原始大小写规则、conversation_id 原样透传、证据搜集时机(仅 conclude 阶段、非定向检索)、history 三态样例 + headers、conclusion 四阶段字段映射、无取消端点说明无(文档补全,接口契约不变)
2026-09-11conversation_id 贯穿四阶段 + 阶段级幂等:stream/comment/conclude 必填 conversation_id(stream 走 query 参数,comment/conclude 走 JSON body);重复请求命中缓存直接返回(stream/comment 模拟打字机,conclude 返回完整报告),不重调模型不重复计费🔴 破坏性(stream/comment/conclude 缺 conversation_id 返回 400 MISSING_CONVERSATION_ID)
2026-09-13流式错误行新增 retryable 字段(前端据此决定是否重试);新增 CONTENT_RISK 错误码(400 Content Exists Risk,前文含敏感词,前端应短路整个 conversation)轻微(错误行新增字段,老客户端忽略即可;建议前端按 retryable 自动重试、CONTENT_RISK 短路)
2026-09-13history 三态:新增 failed 状态 + error_code 字段;conclude 失败自动标 failed;新增 POST /ask/abort 主动标记中断轻微(history 响应新增字段;前端可区分「生成中」与「已中断」)
2026-09-13语言约束增强:四阶段(decompose/stream/comment/conclude)全部跟随用户语言,英文提问最高优先级禁汉字无(输出语言更稳定,英文任务不再偶发混入中文)
2026-09-13生成与转发解耦:stream/comment/conclude/v2 客户端断开后后台继续生成完并落库(断网/关浏览器不再导致任务中断,history 必有完整结果)无(行为增强;客户端断开不再是失败原因,DI 无需为断线做重试/补生成)