本篇展示可运行的离线规则原型。来源采集、客户订阅后台及定时邮件投递属于后续工程范围。示例数据不用于实际投标、政策申报或业务判断。
技术档案
| 档案编号 | YM-TECH-20261016-01 / v1.0 |
|---|---|
| 作者与发布主体 | 合肥域明科技有限责任公司 |
| 实现状态 | 设计文章 + 浏览器离线演示 + Python参考实现;不代表新增生产服务上线。 |
| 运行环境 | Python 3.10及以上,标准库;浏览器演示无构建依赖。 |
| 测试与范围 | 10项Python单元测试;演示页面使用5条固定样本。没有验证真实来源覆盖率或邮件送达率。 |
架构与流程

实现逻辑
问题定义:客户需要的不是更多链接
同一项目可能出现在多个站点,也可能先发布采购意向,再发布招标公告和更正公告。仅按关键词发送所有链接,会把重复、过期和不适用的信息交给客户。本方案把目标拆成三个可核查条件:业务是否相关、硬性条件是否满足、是否存在足够证据。输出不是一个神秘的相关度分数,而是命中原因、排除原因和待核验字段。
来源层:先定义覆盖范围,再谈及时性
生产系统应为每个来源配置公开接口、订阅源或允许访问的网页适配器,记录来源名称、栏目、检查间隔、最近成功时间和解析版本。列表发现新公告后,再抓取详情与必要附件。保存发布日期、抓取时间、原始地址和正文指纹;附件无法解析时进入人工复核队列,不能把空内容当成无相关信息。HTTP条件请求可以减少未变化内容的重复传输;采集失败应退避并记录,不等于来源当天没有更新。演示版不连接任何真实采购平台。
匹配层:硬过滤优先,缺失值单独处理
演示规则支持任一关键词或全部关键词、排除词、地区和最低预算。预算统一使用万元。正文命中关键词但地区不符,仍被排除;原文未写预算且客户设置预算门槛时,进入待复核,而不是填成零或猜测金额。已撤回和超过截止日期的项目不进入新商机列表。截止日按日期比较仅为演示简化,生产应保存带时区的精确截止时刻并考虑报名与投标两个不同窗口。
版本层:去重不应吞掉更正通知
用来源标识和公告编号定位记录,用内容指纹判断版本变化。跨站转载需要另设项目实体关联,不能仅靠相同标题自动合并。更正公告如果有独立公告编号,应通过关联项目编号指向原公告。示例代码演示同一编号多版本取最新的情况;生产中必须保留历史快照。如果已通知客户的项目被延期或撤回,应发送更新事件,即使它不再满足当前筛选条件。
交付层:邮件不是数据库的最终状态
将待发送邮件先写入发件队列表,以客户、公告版本、规则版本组成幂等键,防止同一版本反复入队。SMTP成功只代表邮件服务器接受,不代表客户已经阅读。生产需要记录发送尝试、退信和退订,遇到网络超时后的送达状态不明,应通过邮件服务商事件核对。页面只生成邮件预览,Python示例只输出EML文件;没有自动采集、没有实际邮件发送。
参考数据结构
下表是本专题的标准化字段,不是新发布的线上API响应契约。
| 字段 | 含义 | 处理要求 |
|---|---|---|
| source + id | 来源与公告唯一标识 | 定位记录,跨站项目另建关联 |
| updated_at / version | 更新时间与内容指纹 | 识别更新,保留旧版本 |
| budget / deadline | 万元 / 截止日期 | 缺失保留为空,不补猜 |
| source_url / text | 原文地址与证据片段 | 每条摘要可回查依据 |
查看完整样本记录
{
"id": "tender-1",
"kind": "tender",
"source": "fixture",
"title": "示例学校心理设备采购",
"text": "采购心理测评设备与配套服务,预算50万元。",
"region": "安徽",
"budget": 50,
"status": "published",
"deadline": "2026-10-30",
"updated_at": "2026-10-16T08:00:00+08:00",
"published_at": "2026-10-16T07:30:00+08:00",
"fetched_at": "2026-10-16T08:05:00+08:00",
"application_open": null,
"platform": "示例公开栏目",
"source_url": "https://example.invalid/tender/1"
}核心代码
离线参考代码:三态目标匹配。完整文件和测试见资料包。
def match(record, rule, as_of):
text = (record['title'] + ' ' + record['text']).casefold()
reasons = []
failures = []
pending = []
terms = [word for word in rule['keywords'] if word.casefold() in text]
if not rule['keywords']:
raise ValueError('At least one keyword is required')
required = len(rule['keywords']) if rule['mode'] == 'all' else 1
if len(terms) < required:
failures.append('keyword')
else:
reasons.append('keywords: ' + ', '.join(terms))
if any(word.casefold() in text for word in rule['exclude']):
failures.append('excluded')
if rule['region'] != 'all' and record['region'] != rule['region']:
failures.append('region')
if record['status'] in ('withdrawn', 'repealed'):
failures.append('inactive')
if record.get('deadline') and date.fromisoformat(record['deadline']) < as_of:
failures.append('expired')
minimum = rule.get('minimum_budget', 0)
if minimum > 0:
if record.get('budget') is None:
pending.append('budget_unknown')
elif record['budget'] < minimum:
failures.append('budget')
if record['kind'] == 'policy' and record.get('application_open') is not True:
pending.append('application_not_confirmed')
state = 'excluded' if failures else 'review' if pending else 'matched'
return {'id': record['id'], 'state': state, 'reasons': reasons,
'failures': failures, 'pending': pending}复现方式
python -m unittest -v
python monitor.py tender-monitoring.json --output output/tender-monitoring在资料包的 examples 目录运行,生成 analysis.json 与 digest.eml。没有真实采集或邮件发送。
页面展示

验收口径与实现边界
- 任一关键词和全部关键词产生不同结果
- 排除词、地区和截止日期优先于关键词命中
- 预算未知进入待复核,而非默认符合
- 同一来源同一编号取最新版本
- 重复发送键相同,客户或版本改变时发送键不同
生产化还需要来源适配器、权限与租户隔离、审核记录、调度、持久化和异常告警。建议记录抓取成功率、解析缺失率、人工复核通过率及投递失败率;不要用页面演示数据宣称覆盖率或准确率。
技术参考
配套发布文案
公众号导读
招标监测不是把搜索结果转发一遍。本文用一个可运行的离线原型,拆解来源登记、公告版本、目标筛选和邮件摘要四个环节。客户可以设置地区、关键词和预算门槛,每条信息都给出命中或排除原因;预算缺失进入待复核,更正与撤回必须单独追踪。附Python代码、数据结构、交互演示和测试用例。当前为技术方案与离线演示,真实采购来源和邮件服务需另行接入。
朋友圈摘要
从“搜到公告”到“找到适合客户的项目”,中间还缺规则判断、去重和证据。我们整理了一套招标监测技术方案:可配置目标、可解释命中、可预览邮件,并附代码与演示页面。
配图建议:架构图用于说明流程,交互页面图用于说明配置与结果。发布时保留“离线演示”与样本口径。