157 lines
10 KiB
Markdown
157 lines
10 KiB
Markdown
# pipeline-opportunity — 商机产线能力包
|
||
|
||
把招标市场情报转化为软件研发商机:**热点软件推荐 → 每日AI/Agent招标 → 研发报告编写 → 人工确认 → 研发审批流程**。
|
||
|
||
## 平台边界(重要)
|
||
|
||
- **数据爬取平台**(ebpe.cc:/d/ymq/work/tender_opp,独立部署):采集中国招标网站数据、
|
||
持久存储、去重,仅通过内网 HTTP 只读接口对外(`192.168.16.2:9085`,X-API-Token 鉴权)。
|
||
- **本模块(产线平台侧)**:商机业务闭环。只消费爬虫数据,不爬取、不存招标原始数据;
|
||
报告/审批状态落产线库 `opp_reports` / `opp_approvals` 表,绝不回写爬虫库。
|
||
|
||
依赖方向:产线平台 ──HTTP──> 数据爬取平台(单向)。
|
||
|
||
## 产线闭环与门禁
|
||
|
||
```
|
||
opp_hot_software / opp_daily_ai_tenders(爬虫平台数据)
|
||
↓ 选定方向
|
||
opp_create_report(草稿)→ opp_update_report(补写,数据必须来自工具返回)
|
||
↓
|
||
opp_submit_report ──> 人工确认任务(门禁,pipeline_human_tasks: opp_report_confirm)
|
||
↓ 人工确认通过(confirm 回流)
|
||
initiate_approval ──> 研发审批任务(门禁,opp_dev_approval)
|
||
↓ 人工审批结论回流
|
||
approved / rejected(rejected 可退回 draft 重来)
|
||
```
|
||
|
||
状态机:`draft → confirmed → approval_initiated → approved/rejected`(非法迁移一律拒绝)。
|
||
agent 禁止自批自审:确认与审批结论只能由人工任务回流驱动。
|
||
|
||
## 工具清单(主 agent / 驾驶舱)
|
||
|
||
| 工具 | 用途 |
|
||
|------|------|
|
||
| opp_hot_software | 热点软件推荐(招标量+预算+地区排名) |
|
||
| opp_daily_ai_tenders | 每日 AI/Agent 招标展示数据 |
|
||
| opp_search_tenders | 关键词检索招标信息 |
|
||
| opp_tender_detail | 单条详情(含原文/预算/联系人/来源链接) |
|
||
| opp_crawler_stats | 爬虫平台采集健康度 |
|
||
| opp_create_report / opp_update_report | 报告草稿创建与补写 |
|
||
| opp_list_reports / opp_get_report | 报告列表与全文 |
|
||
| opp_submit_report | 提交人工确认(门禁) |
|
||
| opp_list_approvals | 审批单列表 |
|
||
| opp_diagnose | 产线诊断(报告分布/待办/爬虫连通性) |
|
||
| opp_start_mining | 启动需求挖掘批次(后台:拉取→向量化→聚类→命名;scope=top 全量TopX / targeted 指定类型) |
|
||
| opp_mining_status | 批次状态轮询(pulling/embedding/clustering/naming/done/failed+原因) |
|
||
| opp_list_clusters | 类别排名表(热度/需求数/占比/命名证据)= TopX 热门需求类别 |
|
||
| opp_cluster_detail | 类内需求明细样例(每条带来源 URL 可查证) |
|
||
|
||
角色:`agent.opp_analyst`(商机分析师)、`agent.opp_writer`(研发报告撰写工程师)。
|
||
slash 命令:`/hot` `/ai` `/reports` `/oppdiag`。
|
||
|
||
## 数据参考弹窗(三级下钻,2026-09-08 交互定稿)
|
||
|
||
菜单「📋 数据参考」→ `opp_references_popup.dspy`,三级结构全部**整行点击**(cursor:pointer,
|
||
无「查看分项」按钮):
|
||
|
||
1. **汇总层**(主题排名:招标/国内众包/海外三板块):点主题条目 → 弹出该主题分项列表
|
||
(`opp_references_items` / `opp_demand_items` / `opp_overseas_items.dspy`)。
|
||
2. **分项层**(明细条目):每条说明文字标注「来源:xxx」(`source_text()` 解析,
|
||
来源按钮已废弃删除);点条目 → 弹出具体内容(`opp_item_detail.dspy`:
|
||
爬虫平台 `/api/tenders/{id}` 全量字段 + 公告/需求全文 + 原文页/采集来源页按钮 + 附件列表)。
|
||
3. **附件层**:详情弹窗列出条目附件(爬虫平台 `/api/tenders/{id}/attachments`,
|
||
字段容错归一 `normalize_attachment`),「下载」按钮走服务端代理
|
||
`opp_attachment_dl.dspy`——先回查条目附件清单做归属校验(防 SSRF,前端传任意
|
||
att_url 不在清单内即拒绝),再按降级链取字节:记录 download_url →
|
||
`/api/attachments/{id}/download` → 附件原始 url;代理失败如实报错不静默。
|
||
|
||
附件端点未就绪(爬虫平台附件子系统部署中)时:分项列表不显示 📎 角标、详情弹窗
|
||
显示「附件服务暂不可用」,其余功能不受影响。
|
||
|
||
## 研发报告列表/详情/成果文件下载(2026-09-09 用户需求改造)
|
||
|
||
菜单「📊 研发报告」由原 CRUD 裸表页(`opp_reports/index.ui`,无过滤+可增删改)
|
||
改为只读列表页 `api/opp_reports_list.dspy`,三点改造:
|
||
|
||
1. **按项目 owner 过滤**(`list_visible_reports` 单一事实源):
|
||
- 挂项目的报告:当前用户是项目 owner(`sd_projects.created_by==uid`)才可见;
|
||
agent 创建的项目(`created_by LIKE 'agent.%'`,模式经 `${kw}$` 参数化——
|
||
SQL 字面量裸 `%` 会被驱动当格式化符炸 ValueError,实测踩过)降级为同机构成员可见
|
||
(对齐 `check_project_owner` 既有降级语义,不重复发明)
|
||
- 未挂项目的报告(平台级调研,存量数据大多是这类):登录用户可见
|
||
- 项目已删的孤儿报告:owner 无法验证 → 不可见(宁缺勿漏,LEFT JOIN 自然排除)
|
||
- 详情/下载端点复用 `check_report_visible`(与列表同一规则,语义不漂移)
|
||
2. **详情四节展示**(`opp_report_detail.dspy`,整行点击下钻,范式同 `opp_references_items`):
|
||
报告正文按四节模板解析(`parse_report_sections`)——`## 研发场景` / `## 输入` /
|
||
`## 输出` / `## 成果`,逐节 MdWidget 渲染;兼容带序号/冒号/加粗的标题变体,
|
||
三级标题不误判,重复同名节拼接不丢内容。**旧报告(无四节标题)全文归「输出」节**,
|
||
空节如实显示「本报告未填写此节」不编造。agent 写报告的四节模板硬要求已写进
|
||
OPP_PROMPT + opp_create/update_report 工具描述 + opp-workflow 技能。
|
||
3. **成果文件多个列出、逐个下载**(`list_report_files` + `opp_report_file_dl.dspy`):
|
||
文件范围 = 项目工作空间 `deliverables/` 全部文件 + 报告 PPT(`ppt_path`,
|
||
按 realpath 去重)。下载安全模型对齐 `opp_attachment_dl`:前端只传
|
||
`report_id + name`,服务端**回查清单命中才给文件**,文件名绝不参与路径拼接
|
||
(清单只来自目录列举+ppt_path,`../` 穿越名在清单里不可能出现)。
|
||
|
||
CRUD 页(`opp_reports/` `opp_approvals/`)保留供后台运维直接编辑,菜单入口不再暴露。
|
||
|
||
## 爬虫平台接入配置
|
||
|
||
系统级配置禁硬编码,走 appbase `params` 表(兜底默认值内网地址):
|
||
|
||
| params 键 | 值 |
|
||
|-----------|-----|
|
||
| `tender_api_base` | `http://192.168.16.2:9085`(默认) |
|
||
| `tender_api_token` | 爬虫平台 `state/api.env` 中的 TENDER_API_TOKEN |
|
||
|
||
## 部署(宿主 = pipeline-app)
|
||
|
||
1. `git clone` 到 `<APP_ROOT>/pkgs/pipeline-opportunity`
|
||
2. `bash build.sh`(建表 + 种子数据 + wwwroot 软链 + pip install)
|
||
3. app 入口已接线:`load_pipeline_opportunity()`(try/except 兜底)
|
||
4. RBAC:`cd <APP_ROOT> && py3/bin/python pkgs/pipeline-opportunity/scripts/load_path.py`
|
||
5. i18n:合并 `i18n/{zh,en}/msg.txt` 到宿主 wwwroot(merge_i18n.py),调 /i18n_getmsgs 刷新
|
||
6. 重启应用;浏览器验证驾驶舱 `/pipeline-opportunity/agent`
|
||
|
||
## 表
|
||
|
||
- `opp_reports`:研发报告(software/title/content/status/confirm_task_id/confirmed_by)
|
||
- `opp_approvals`:研发审批(report_id/status/note/resolved_by)
|
||
- `opp_mining_batches`:需求挖掘批次(org_id+project_id 隔离,project_id 空=平台级挖掘;
|
||
scope=top/targeted;状态机 pulling→embedding→clustering→naming→done/failed;
|
||
stats_json 进度统计;error_msg 可行动报错)
|
||
- `opp_demand_snap`:需求快照(batch_id+src_id 去重;cluster_id 聚类回填;embed_status)
|
||
- `opp_clusters`:需求类别(heat_rank 热度排名/share 占比/naming_evidence 命名证据/centroid_snap_id)
|
||
|
||
挖掘结果的项目可见性口径与 opp_reports 对齐(2026-09-12 用户确认挖掘也分项目):
|
||
挂项目的批次仅本项目会话可见;平台级批次(无项目会话发起)本机构登录可见;
|
||
无当前项目的会话只见平台级批次。`_check_batch_visible` 是单一规则源
|
||
(mining_status/list_clusters/cluster_detail 三层复用,E2E 11 项断言实测全过)。
|
||
|
||
## 需求挖掘架构(P1,2026-09-11)
|
||
|
||
众包需求(爬虫平台 record_type=demand)→ 语义聚类 → TopX/指定类型分析:
|
||
- **embedding 走模型调用**:rag `/rag/api/embed.dspy`(凭据单点 rag_engine_configs)
|
||
← pipeline_service.rag_client.rag_embed_texts(project_id)(Bearer,10条/批)。
|
||
- **向量与近邻压给 VDB(Milvus)**(upapp.rag-vdb):kNN=/v1/query+vector+pagerows;
|
||
标量过滤=expr;协议细节见技能 rag-module-operations。
|
||
- **两层 collection**:共享缓存 `opp_demand_emb_cache`(基础数据全机构共用,
|
||
一条需求只嵌一次)+ 批次工作集 `opp_mine_<batch16>`(分析结果按批次隔离,
|
||
每机构保留最近 opp_mine_keep_batches 个,旧批自动 drop)。
|
||
- **聚类算法**:kNN(top30) + τ阈值(默认0.75) + 纯 Python 并查集(零原生依赖,
|
||
确定性可复现);>150 条大簇质心二次细分(τ+0.10,防众包模板标题过度聚合——
|
||
2075条实测 τ=0.75 拐点:42簇 top1=8.2%);小组(<5)质心并入最近大簇或归其他。
|
||
- **LLM 只做簇命名**(llm_call purpose=utility 60s 短超时),失败规则兜底(高频词)。
|
||
- **机构隔离**(用户定夺):基础数据共享、分析结果隔离——批次/快照/类别挂 org_id,
|
||
查询强制过滤(E2E 伪 org 拒绝实测通过)。
|
||
- 算法参数全走 appbase params 表:opp_mine_tau/topk/min_cluster/big_split/keep_batches、
|
||
opp_embed_batch、opp_emb_cache_col(代码默认兜底)。
|
||
- 验证脚本:scripts/p0_probe_vdb_embed*.py(VDB/embed 协议探测)、
|
||
p1_cluster_analyze.py(多τ对比+缓存,调参用)、p1_create_tables.py(幂等建表)。
|
||
|
||
## 技能
|
||
|
||
产线技能在 pipeline-core 技能库:`skills_library/pipelines/opportunity_general/`
|
||
(common/opp-workflow 总纲 + roles/agent.opp_analyst、agent.opp_writer 角色技能)。
|