From f53e1b264ec3fec2502e77b5d4b63e40380c97c1 Mon Sep 17 00:00:00 2001 From: hz4th_coder Date: Fri, 14 Aug 2026 12:55:15 +0800 Subject: [PATCH] =?UTF-8?q?V3.3.1=20=E4=BF=AE=E5=A4=8D=EF=BC=9AWBS=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=20'Extra=20data'=20=E6=8A=A5=E9=94=99=20+=20?= =?UTF-8?q?=E5=BC=80=E5=B7=A5=E6=8E=A5=E5=8F=A3=E7=A9=BA=20body=20?= =?UTF-8?q?=E5=AE=B9=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - parse_wbs 改用 raw_decode 提取第一个完整 JSON,忽略尾部杂质/多余 JSON(原按首{到尾}切片,模型输出带第二段JSON或解释文字时解析失败) - 兼容 subtasks/task_list/items/task 等任务键名 - /autostart 接口 get_json 改 silent 容错(无 body 时不再 400) --- app.py | 2 +- autostart.py | 48 +++++++++++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app.py b/app.py index 34c93e9..9492a87 100644 --- a/app.py +++ b/app.py @@ -1076,7 +1076,7 @@ def project_autostart(pid): return jsonify({'ok': False, 'error': '请先在编辑项目中设置 AI 主管 Worker'}), 400 if not db.q('SELECT 1 FROM project_team_workers WHERE project_id=?', (pid,)): return jsonify({'ok': False, 'error': '请先在编辑项目中勾选干活团队 Worker(≥1 个)'}), 400 - d = request.get_json(force=True) or {} + d = request.get_json(silent=True) or {} ok = autostart.launch(pid, review_required=1 if d.get('auto_review') else 0) if not ok: return jsonify({'ok': False, 'error': 'AI 主管正在工作中,请稍候'}), 400 diff --git a/autostart.py b/autostart.py index 5cf5c4b..50f93da 100644 --- a/autostart.py +++ b/autostart.py @@ -47,19 +47,39 @@ REVISE_PROMPT = ( ) -def parse_wbs(text): - """从 LLM 输出中稳健提取 JSON 任务列表""" +def _extract_json(text): + """用 raw_decode 稳健提取第一个完整 JSON 值(自动忽略尾部杂质/多余 JSON)""" t = text.strip() if t.startswith('```'): t = t.strip('`') if t.startswith('json'): t = t[4:] t = t.strip() - start = min([i for i in (t.find('{'), t.find('[')) if i >= 0] or [0]) - end = max(t.rfind('}'), t.rfind(']')) + 1 - data = json.loads(t[start:end]) - tasks = data['tasks'] if isinstance(data, dict) else data - assert isinstance(tasks, list) and tasks, '任务列表为空' + candidates = [i for i in (t.find('{'), t.find('[')) if i >= 0] + last_err = None + for i in sorted(candidates): + try: + obj, _ = json.JSONDecoder().raw_decode(t[i:]) + return obj + except Exception as e: + last_err = e + raise last_err or ValueError('未找到 JSON 内容') + + +def parse_wbs(text): + """从 LLM 输出中稳健提取 JSON 任务列表(兼容任务键名/多余内容)""" + try: + data = _extract_json(text) + except Exception: + raise + if isinstance(data, dict): + for key in ('tasks', 'subtasks', 'task_list', 'items', 'task'): + if key in data: + data = data[key] + break + tasks = data if isinstance(data, list) else [] + if not tasks: + raise ValueError('任务列表为空或格式不正确') return tasks @@ -100,20 +120,6 @@ def _chat_json(worker, messages, max_tokens=3000, temperature=0.3): raise last_err or ValueError('JSON 解析失败') -def _extract_json(text): - t = text.strip() - if t.startswith('```'): - t = t.strip('`') - if t.startswith('json'): - t = t[4:] - t = t.strip() - start = min([i for i in (t.find('{'), t.find('[')) if i >= 0] or [0]) - end = max(t.rfind('}'), t.rfind(']')) + 1 - if end <= start: - raise ValueError('未找到 JSON 内容') - return json.loads(t[start:end]) - - # --------------------------------------------------------------------------- # 团队读取 # ---------------------------------------------------------------------------