ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AI驱动需求管理:构建从模糊想法到结构化任务的工作空间

2026/8/27 6:43:16 拓冰建站 浏览量
AI驱动需求管理:构建从模糊想法到结构化任务的工作空间 在实际软件项目中需求管理从来不是把文字写进文档这么简单。需求入口混乱、描述粒度不一、团队对同一需求的认知产生偏差都会影响后续设计和开发。Documan 这类 AI Powered Requirement Management Workspace 要解决的问题正是把零散的产品想法转化为结构化、可跟踪、可协作的需求工作空间。这里不介绍某个商业产品而是围绕这类工具必须具备的能力设计最小实现包括需求结构化、AI 辅助拆解、状态流转和工程落地最终给出一个能在本地运行的需求管理工作空间。一个需求管理工具如果只提供“增删改查”其实并没有解决根本问题。真正有价值的是把需求从一句话变成可执行任务列表把负责人、验收标准、依赖关系都放进同一个上下文。AI 在这里不是生成花哨文案而是承担规范化、拆解和质量检查等工作。下面从 AI 在需求管理链路中的位置开始解释再逐步落地一个最小可运行示例。1. 先理解 AI 在需求管理链路中的位置1.1 传统需求管理低效的根源几乎所有团队都会遇到同一个问题需求“说过”和“写好”是两回事。产品经理在会议上口头描述了一个想法开发人员按自己的理解去做测试人员又按另一套标准验收最后用户拿到的功能和最初预期对不上。传统需求管理低效通常来自三个断点需求入口分散。需求来自即时消息、邮件、会议纪要、白板照片甚至一段语音没有一个统一接收入口。需求粒度不统一。有的人只写“优化登录体验”有的人写完整 PRD。开发拿到一句话无法排期拿到长文又抓不住重点。状态无法追踪。需求进入系统后是否评审、是否排期、是否完成往往依赖人追问而不是系统主动暴露。这三个断点不是靠一个表格工具就能解决的。表格可以记录“谁在什么时候提了什么需求”但无法自动把一句话扩展成可评审的条目也无法提醒你哪些需求存在重复或冲突。1.2 AI 能介入的具体环节在需求管理链路里AI 最合适的定位不是“自动写需求文档”而是做几类重复且确定的工作文本规范化。把口语化、碎片化的描述整理成结构清晰的句子不改变原意。需求拆解。把一个父级需求拆成子任务例如从“支持用户上传附件”拆出上传接口、文件类型校验、大小限制、存储策略等子项。重复检测。发现两条描述相近的需求提示可能重复。验收条件生成。根据需求描述生成可验证的验收标准减少“开发完不知道该怎么测”的情况。这些工作有一个共同点它们有相对稳定的输出格式又需要语言理解能力。把这类能力做成后台服务业务代码只需要调用一个函数就能获得结构化结果。1.3 工作空间的对象模型是地基AI 能力再强最终也要落在数据模型上。需求管理系统的核心不是大模型而是“需求对象”和“需求关系”。一个需求条目至少需要具备这些属性标题和描述类型Epic、Story、Task、Bug状态草稿、已细化、已批准、进行中、完成优先级上级需求或子任务验收条件来源和创建人当 AI 拆解完成、结构化结果写入这些字段后团队才能在工作空间里按状态过滤、按优先级排序、按父子关系追踪。所以第一步先把数据模型设计好再接入 AI。2. 搭建最小运行环境FastAPI 项目骨架2.1 技术选型为了在本地快速跑通这里选择 Python FastAPI SQLite SQLAlchemy 简单 HTML 页面。选型理由如下组件选择理由Web 框架FastAPI异步支持好天然适合对接大模型接口自带 API 文档数据库SQLite零配置文件适合学习生产再替换 PostgreSQLORMSQLAlchemy模型定义清晰便于后续迁移AI 接入HTTP 调用 OpenAI 兼容接口不绑定具体厂商通过环境变量切换服务商前端原生 HTML JavaScript减掉构建流程聚焦后端核心链路要提前说明下面的代码主要用于演示工作原理不代表完整生产实现。实际项目需要根据团队技术栈、模型服务商和部署方式调整。2.2 环境准备和项目结构本机需要安装 Python 3.10 及以上版本。创建虚拟环境后安装依赖python -m venv .venv source .venv/bin/activate # Windows 下使用 .venv\Scripts\activate pip install fastapi uvicorn sqlalchemy httpx pydantic python-dotenv项目结构如下documan/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── database.py # 数据库连接 │ ├── models.py # ORM 模型 │ ├── ai_service.py # 大模型调用封装 │ ├── requirement_service.py # 需求业务逻辑 │ └── templates/ │ └── index.html # 简单工作台页面 ├── requirements.txt ├── .env.example └── README.md.env.example里预留模型服务配置DATABASE_URLsqlite:///./documan.db LLM_API_URLhttps://api.example.com/v1/chat/completions LLM_API_KEYyour_key_here LLM_MODELyour_model_name LLM_TIMEOUT30这里的LLM_API_URL是 OpenAI 兼容的聊天补全接口地址。实际使用时要替换成自己团队的模型网关地址不要在生产配置里硬编码密钥。2.3 数据模型设计在models.py中定义 Requirement 模型from datetime import datetime from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, create_engine from sqlalchemy.orm import declarative_base, sessionmaker Base declarative_base() class Requirement(Base): __tablename__ requirements id Column(Integer, primary_keyTrue) title Column(String(200), nullableFalse) description Column(Text, default) requirement_type Column(String(30), defaultstory) status Column(String(30), defaultdraft) priority Column(String(20), defaultmedium) parent_id Column(Integer, ForeignKey(requirements.id), nullableTrue) acceptance_criteria Column(Text, default[]) subtasks Column(Text, default[]) source Column(String(50), defaultmanual) created_at Column(DateTime, defaultdatetime.utcnow) updated_at Column(DateTime, defaultdatetime.utcnow, onupdatedatetime.utcnow)关键点在于acceptance_criteria和subtasks用 JSON 字符串存储在应用层做序列化和反序列化。这样 AI 输出的结构不会受关系型表结构限制又能保留查询能力。如果后续需要按状态、负责人或优先级频繁筛选再根据瓶颈拆表或加索引。database.py中建立连接from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker import os DATABASE_URL os.getenv(DATABASE_URL, sqlite:///./documan.db) engine create_engine(DATABASE_URL, connect_args{check_same_thread: False}) SessionLocal sessionmaker(bindengine, autocommitFalse, autoflushFalse)SQLite 下加check_same_threadFalse是因为 FastAPI 异步接口可能跨线程访问数据库。换用 PostgreSQL 时这一项不需要。3. 核心实现从一段需求文本到结构化工作空间3.1 需求导入接收原始文本在main.py中先定义导入接口。这一层负责接收前端传来的需求文本调用 AI 服务做结构化处理最后落库。from fastapi import FastAPI, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.orm import Session from . import ai_service, requirement_service, models from .database import SessionLocal, engine app FastAPI(titleDocuman Minimal) models.Base.metadata.create_all(bindengine) def get_db(): db SessionLocal() try: yield db finally: db.close() class ImportRequest(BaseModel): text: str app.post(/api/requirements/import) async def import_requirement(payload: ImportRequest, db: Session Depends(get_db)): if not payload.text.strip(): raise HTTPException(status_code400, detailtext cannot be empty) result await ai_service.refine_requirement(payload.text.strip()) req requirement_service.create_from_ai_result(db, result) return req注意这里把“接收文本”和“结构化处理”分成两层。ai_service.refine_requirement只负责处理文本requirement_service.create_from_ai_result只负责写库。一旦 AI 返回格式变化或模型更换不会牵连数据库逻辑。3.2 AI 拆解把模糊描述拆成可执行条目ai_service.py中用系统提示词约束模型输出格式。这是整个链路里最需要打磨的部分提示词直接决定了后续解析能否成功。import json import os import httpx SYSTEM_PROMPT 你是一个需求分析师。用户会输入一段模糊的产品需求文本。 请把它拆解为结构化需求并只输出 JSON不要输出多余解释。 JSON 格式必须如下 { title: 需求标题, description: 清晰完整的需求描述, requirement_type: story, acceptance_criteria: [验收条件1, 验收条件2], subtasks: [ {title: 子任务标题, description: 子任务描述} ] } async def call_llm(prompt: str) - str: api_url os.getenv(LLM_API_URL, https://api.example.com/v1/chat/completions) api_key os.getenv(LLM_API_KEY, ) model os.getenv(LLM_MODEL, your_model_name) timeout float(os.getenv(LLM_TIMEOUT, 30)) headers {Authorization: fBearer {api_key}} payload { model: model, messages: [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: prompt} ], temperature: 0.2, } async with httpx.AsyncClient(timeouttimeout) as client: resp await client.post(api_url, headersheaders, jsonpayload) resp.raise_for_status() content resp.json()[choices][0][message][content] return content async def refine_requirement(text: str) - dict: content await call_llm(text) content content.strip() content _strip_code_fence(content) return json.loads(content) def _strip_code_fence(content: str) - str: if content.startswith(): lines content.splitlines() if lines and lines[0].startswith(): lines lines[1:] if lines and lines[-1].strip() : lines lines[:-1] content \n.join(lines) return content.strip()这里的json.loads非常关键。模型输出经常带 markdown 代码块标记所以先剥离json和再解析。temperature设为 0.2是为了让输出结果更稳定减少随机性导致需求拆解不一致。实际部署时这个函数还需要补充重试和降级逻辑。例如首次解析失败可以把原始输出原样返回让模型重新整理一次如果多次失败就不能让接口直接报 500而是要落库一条“未解析状态”的需求由人工介入。3.3 状态流转维护需求生命周期需求进入工作空间后团队要按状态推进。状态不能随意跳转例如“草稿”不能直接变成“已解决”。在requirement_service.py中定义合法转换表VALID_TRANSITIONS { draft: {refined, archived}, refined: {approved, draft}, approved: {in_progress, blocked}, in_progress: {done, blocked}, blocked: {in_progress, refined}, done: {approved}, archived: set(), } def can_transition(current: str, target: str) - bool: return target in VALID_TRANSITIONS.get(current, set())在更新接口里强制使用这个函数app.patch(/api/requirements/{req_id}/status) def update_status(req_id: int, payload: StatusRequest, db: Session Depends(get_db)): req db.get(models.Requirement, req_id) if not req: raise HTTPException(status_code404, detailrequirement not found) if not can_transition(req.status, payload.status): raise HTTPException(status_code400, detailfinvalid transition: {req.status} - {payload.status}) req.status payload.status db.commit() return {id: req.id, status: req.status}状态机看着简单但能防止大部分数据混乱。例如测试环境常见的问题后端没有校验状态前端可以任意修改最后看板数据对不上。集中在一个函数里做校验后续加权限、加审计也只需要改这一处。3.4 前端工作台一个可用的页面工作空间不能只有一个 API。用最小的 HTML 页面实现两个能力粘贴需求文本并提交查看需求列表和状态。!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleDocuman Minimal/title /head body h1Documan Minimal/h1 textarea idreqText rows4 placeholder粘贴一段模糊的产品需求/textarea button onclickimportRequirement()拆解需求/button div idresult/div script async function importRequirement() { const text document.getElementById(reqText).value; if (!text.trim()) return; const resp await fetch(/api/requirements/import, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({text: text}) }); const data await resp.json(); document.getElementById(result).innerText JSON.stringify(data, null, 2); } /script /body /html前端越简单越好。接口返回的 JSON 结构清晰前端只负责展示。真实工作空间还需要状态筛选、编辑表单、评论区域和看板拖拽这些属于交互层面的事不在最小实现范围内。4. 运行验证与常见问题排查4.1 启动服务并验证接口在项目根目录启动uvicorn app.main:app --reload启动后访问http://127.0.0.1:8000能看到工作台页面。用 curl 验证导入接口curl -X POST http://127.0.0.1:8000/api/requirements/import \ -H Content-Type: application/json \ -d {text: 用户可以在网页端上传产品需求文档系统能自动提取其中的功能点并按优先级拆分成多个迭代任务。}正常返回的 JSON 结构大致如下{ id: 1, title: 支持上传需求文档并自动提取功能点, description: 用户可以在网页端上传需求文档系统自动识别功能点并按优先级进行分解形成迭代任务列表。, requirement_type: story, status: refined, acceptance_criteria: [ 支持常见文档格式上传, 能自动提取文档中的功能点, 能按优先级生成迭代任务 ], subtasks: [ {title: 实现文档上传接口, description: 接收和校验文档文件}, {title: 实现功能点提取逻辑, description: 调用大模型识别功能点} ] }把这个 JSON 里的id拿到再验证状态流转curl -X PATCH http://127.0.0.1:8000/api/requirements/1/status \ -H Content-Type: application/json \ -d {status: approved}返回{id: 1, status: approved}说明状态机生效。如果尝试从approved直接跳回draft接口应该返回 400 错误。4.2 按现象拆排查链路AI 接入后的排错重点和普通 CRUD 项目很不一样。下面三条链路最常遇到。链路一接口超时或浏览器出现连接超时现象前端提交需求后长时间等待浏览器报net::ERR_CONNECTION_TIMED。排查顺序先确认是前端到后端超时还是后端到模型服务超时。检查uvicorn日志看请求是否已经进了import_requirement。如果进了接口再检查LLM_API_URL指向的服务能否连通。直接 curl 该地址做一次最小请求。检查LLM_TIMEOUT是否太短以及模型服务是否有排队。这类问题在本地环境容易被误判。浏览器报连接超时实际原因往往是后端在等待模型响应前端先等不住了。链路二AI 返回内容无法解析成 JSON现象接口返回 500日志里出现JSONDecodeError。排查顺序打印模型返回的原始content不要直接看解析后的结果。看内容是否带 markdown 代码块标记_strip_code_fence是否生效。看内容是否被截断例如choices[0].message.content只返回了前半段。看模型是否把提示词里的示例原样返回导致解析出非法结构。处理建议先记录原文再做人肉修复或二次调用。在提示词里减少示例数量也能降低“模型复述示例”的概率。链路三需求能写入但状态或字段不对现象需求成功入库但status是空值或者subtasks是字符串不是数组。排查顺序检查 AI 返回的字段名和create_from_ai_result里的映射是否一致。检查acceptance_criteria和subtasks是否做了json.dumps序列化后写入。检查读取时是否做了json.loads反序列化。这个问题的根因通常是模型把字段取名subtask而你程序里取subtasks。提示词里用英文给出的字段名是什么代码就必须严格对应。4.3 常见问题速查表问题现象常见原因检查方式处理建议浏览器报net::ERR_CONNECTION_TIMED后端等待模型服务超时看 uvicorn 日志curl 测试模型服务连通性调整超时时间增加重试和队列接口报 500日志有JSONDecodeError模型输出带代码块标记或内容损坏打印原始content剥离代码块后重试解析失败时保存原文需求标题为空AI 没有生成title字段查原始输出和数据库记录在创建函数里补默认值Schema 层做校验状态任意跳转更新接口没有走状态机检查是否直接 update status强制使用can_transition子任务查询出来是字符串写库时没有序列化查表字段内容写入前json.dumps读取后json.loads模型返回内容被截断上下文长度或 max_tokens 不足看choices[0].finish_reason调大输出上限或先压缩输入文本注意排错时不要只看异常信息还要确认“数据真正落库后的样子”。很多问题发生在模型输出到数据库字段的映射环节。5. 从演示到生产工程化补全和扩展方向5.1 学习环境与生产环境的差异最小实现里用 SQLite、单进程 uvicorn这些设计一旦进入多人协作场景就会暴露问题。维度学习环境生产环境数据库SQLitePostgreSQL配合迁移工具模型调用直接等待结果设置超时、重试、熔断、排队日志终端打印结构化日志记录请求 ID 和耗时权限不校验登录鉴权、RBAC、操作审计前端原生 HTML可选 React/Vue但同 API 契约部署单进程多 worker 反向代理 监控其中数据库升级是最容易忽略的一步。SQLite 在并发写入时容易出现锁问题团队一旦有几个人同时改需求状态就会开始遇到database is locked。所以生产环境要尽早切到 PostgreSQL并把模型建表交给 Alembic 之类的迁移工具。5.2 AI 输出质量控制AI 输出天然不稳定需求管理工具又要求结果能被评审和复用因此必须建立质量控制机制。推荐做法是设置一个 JSON Schema 校验层。模型返回的 JSON 先经过 Schema 校验再写入数据库。比如用 Pydantic 定义拆解结果结构from pydantic import BaseModel, Field class SubtaskOut(BaseModel): title: str Field(..., min_length1) description: str class RefinedRequirement(BaseModel): title: str Field(..., min_length1) description: str requirement_type: str story acceptance_criteria: list[str] [] subtasks: list[SubtaskOut] []调用model_validate(result)能拦截大部分脏数据。字段名不对、类型错误、必填为空都会在解析阶段抛出可读错误。这样就算模型不稳定也不会把脏数据直接写进需求库。这里还要处理好 AI 幻觉问题。模型可能生成看似合理但实际不存在的功能点或把用户原意推得过远。缓解办法有三点一是系统提示词里写明“只基于用户输入不要新增需求”二是把模型输出标记为“AI 草稿”必须有人确认后才进入approved状态三是保留原始文本和模型输出对比方便人工核查。5.3 可复用的发布前检查清单上线一个 AI 需求管理工作空间前至少检查下面这些项[ ] 环境变量是否全部外置密钥是否只存在于部署系统配置中[ ] 数据库连接是否指向生产实例create_all是否已替换为迁移工具管理[ ] 模型接口是否有超时、重试、失败降级是否会在异常时保存原始输出[ ] 模型返回是否经过 Schema 校验非法数据是否能拦截在写库前[ ] 需求状态流转是否集中在校验函数里前端是否无法直接改状态[ ] 是否记录操作人和更新时间状态变更是否有审计日志[ ] 对长文本是否做了分段或截断处理避免上下文超限[ ] 是否准备了一组回归测试用的标准需求文本和预期输出检查清单的价值在于发布时不用临时想“还有哪些忘了”。把排查过的坑变成固化规则团队每次发布都按同一套标准走。5.4 扩展方向从需求管理走向 AI 辅助研发链路需求管理工作空间只是一个起点。当需求变成了结构化数据后续工具链就可以围绕这些数据继续扩展。比较自然的扩展路径是需求摘要和站会汇报。每天自动生成“当前迭代里新增了哪些需求、哪些处于阻塞状态”。需求语义搜索。不再靠关键词拼匹配而是用向量检索找出“和这次改动相关的历史需求”。自动生成验收用例。在approved状态触发用例生成测试人员在此基础上确认。需求到任务再到代码。把子任务输出到项目管理工具甚至是 AI 编程助手的上下文让开发人员不用切换系统就能开始编码。这里最需要建议的是不要一开始就做一个“全流程 AI 平台”。先把需求结构化、状态流转、人工确认这三件事做扎实再逐步接入更多 AI 能力。需求数据质量越高后面的 AI 扩展才越稳。对新手来说最值得练习的是把refine_requirement的提示词和 JSON 解析逻辑反复打磨并用不同风格的需求文本做测试。你会很快发现模型输出的坑远比接口联调的坑多而处理好这些输出格式问题正是这类工具走向可用的关键。