Claude Prompt 香港场景:公文里「今日」落在 6 个日历日上,「翌日」锚错了 5 天

文章目录

    • [1. 先说结论](#1. 先说结论)
    • [2. 一个绝对时间,一堆相对时间](#2. 一个绝对时间,一堆相对时间)
    • [3. 发现一:日期以汉字为主,朴素正则会漏 92.6%](#3. 发现一:日期以汉字为主,朴素正则会漏 92.6%)
    • [4. 发现二:「今日」在这批数据里有 6 种所指](#4. 发现二:「今日」在这批数据里有 6 种所指)
    • [5. 发现三:锚点不统一,2.3% 的样本会翻车](#5. 发现三:锚点不统一,2.3% 的样本会翻车)
    • [6. 发现四:汉字时刻的边界比日期更绕](#6. 发现四:汉字时刻的边界比日期更绕)
    • [7. Prompt 契约:把锚点变成可校验的后置条件](#7. Prompt 契约:把锚点变成可校验的后置条件)
    • [8. 踩坑清单与结论](#8. 踩坑清单与结论)
    • [9. 参考链接](#9. 参考链接)

1. 先说结论

香港政府新闻公报每天发布,正文里到处是「今日」「明日」「翌日」这种相对时间。我把最新 100 条抓下来逐条扫了一遍:

问题 实测结果
「今日」能被正确解释吗? 不能一次解释完 。100 条里有 73 条 含「今日」,它们落在 6 个不同的日历日上------同一个词,6 种所指
日期都是阿拉伯数字吧? 反了 。汉字日期出现 189 次9月16日 这种写法只有 15 次 。用 \d{1,2}月\d{1,2}日 干活,会静默漏掉 92.6% 的日期
把相对词锚定到发布时刻就行了吗? 97.7% 行,剩下 2.3% 会错 。而错的那 2 条是「翌日」------它指的是文本里前一个日期的次日 ,锚到发布日会差 5 天

这篇不讲怎么让模型「更聪明」,讲的是喂给模型之前先把时间锚定住。而锚点这件事,比看上去多一个坑。

2. 一个绝对时间,一堆相对时间

政府新闻公报的 RSS 里,每条都有一个 pubDate,这是绝对时间戳;而正文里全是汉字写的相对时间。两者在同一个 item 里,却是两种东西:

复制代码
pubDate:  Mon, 14 Sep 2026 19:40:00 +0800      ← 绝对,机器可读
正文:     他於九月九日出現...,翌日(九月十日)到衞生署...就醫   ← 汉字 + 相对

所以真正要做的事只有一件:把正文里的相对时间,用 pubDate 折算成绝对时间。先把汉字数字和汉字时间解析出来:

python 复制代码
import re

ZH = {"一": 1, "二": 2, "兩": 2, "三": 3, "四": 4, "五": 5,
      "六": 6, "七": 7, "八": 8, "九": 9, "十": 10}

def zh_num(s):
    """中文数字 → 整数。覆盖 一...十、十一...十九、二十...三十一。"""
    s = s.strip()
    if not s or not all(c in ZH for c in s):
        return None
    if s == "十":
        return 10
    if "十" in s:
        a, _, b = s.partition("十")
        return (ZH.get(a, 1) if a else 1) * 10 + (ZH.get(b, 0) if b else 0)
    return ZH[s]

def parse_zh_md(s):
    """「九月十六日」→ (9, 16)。阿拉伯写法不走这个函数。"""
    m = re.fullmatch(r"([一二三四五六七八九十]{1,3})月([一二三四五六七八九十]{1,3})日", s)
    if not m:
        return None
    mo, dy = zh_num(m.group(1)), zh_num(m.group(2))
    return (mo, dy) if mo and dy and 1 <= mo <= 12 and 1 <= dy <= 31 else None

这段解析可以直接拿走用,建议收藏备用------港澳台和内地公文的中文数字写法基本通用,改一下正则字符集就能复用。

3. 发现一:日期以汉字为主,朴素正则会漏 92.6%

100 条公报全文扫下来,五类时间表述的出现次数是这样的:

形态 例子 次数
汉字月日 九月十六日 189
阿拉伯日期 9月16日 15
汉字时刻 下午三時三十分 43
相对偏移词 今日 / 明日 / 翌日 141
时段词 上午 / 下午 / 凌晨 61

日期写法里汉字占 92.6%。 而最顺手的那条正则------\d{1,2}月\d{1,2}日------只能命中 15 个,剩下 189 个日期它一个都看不见

麻烦的地方在于它是静默漏掉:脚本不报错、不告警,只是结果的「日期」字段大部分是空的。你如果拿它统计「这些公报涉及多少个日期」,会得到一个只有真实值 7% 的数字,而且看起来完全正常。

189 个汉字日期里有 45 个不同值 ,不是同一句套话在重复------最高频的 九月十四日 出现 25 次,最低的一批只出现 1 次。所以这是真实的日期分布,不是模板。

4. 发现二:「今日」在这批数据里有 6 种所指

100 条公报跨 6 个发布日(09-11 到 09-16)。含「今日」的 73 条,逐日分布是:

复制代码
09-11 → 19 条    09-12 → 14 条    09-13 → 4 条
09-14 → 20 条    09-15 → 12 条    09-16 → 4 条

同一个词,6 个含义。 如果你把这 100 条一次性丢给模型,问「哪些事发生在今日」,模型手上只有一个「今日」------它必须猜,或者编一个。

正确的做法不是让模型更努力,而是在数据进入上下文之前,把每一条的 pubDate 和它绑在一起 。这也是 Prompt 契约里 published_at 必须逐条传、不能只传一个「当前时间」的原因。

5. 发现三:锚点不统一,2.3% 的样本会翻车

香港公文有个很好的习惯:在相对时间后面用括号再把绝对日期写一遍

复制代码
明日(九月十六日)
翌日(九月十日)

这批数据里一共有 86 条 这样的写法。它等于文本自己给出的标注------可以直接拿来验证「锚点选得对不对」,而且是免费的回归测试集。

我把每条都按「锚到发布日」算了一遍:

偏移词 样本数 锚到发布日命中
今日 72 100.0%
明日 6 100.0%
昨日 6 100.0%
翌日 2 0.0%

今日 / 明日 / 昨日 全部命中------因为公报基本是当天事当天发。但「翌日」两条全错,而且错法一模一样:

复制代码
①  个案涉及一名 31 岁男子,他于九月九日出现...皮疹,
    翌日(九月十日)到衞生署...求医
    发布日 09-14 → 按发布日算得 09-15,实际 09-10,差 5 天

②  他于九月十一日开始出现发烧...,
    翌日(九月十二日)到伊利沙伯医院急症室求诊
    发布日 09-14 → 按发布日算得 09-15,实际 09-12,差 3 天

「翌日」的锚点不是发布日,而是文本里最近出现的那个日期。 这是句内指代,不是文档级偏移。把它当成「发布日 +1 天」,得到的是一个合法日期------格式对、范围合理、不报错,只有对着原文看才知道错了。

python 复制代码
OFFSET = {"今日": 0, "本日": 0, "是日": 0, "即日": 0, "當日": 0, "同日": 0,
          "明日": 1, "翌日": 1, "後日": 2, "昨日": -1, "前日": -1}

def anchor(pubdate, word):
    """把相对词锚定到绝对日期------只对文档级偏移词成立。"""
    import datetime as dt
    return (pubdate + dt.timedelta(days=OFFSET[word])).date()

def pair_offset_absolute(text):
    """抽「翌日(九月十日)」这类自带绝对日期的写法。

    这是文本自己给出的标注,可以拿来当回归测试集。
    """
    pat = re.compile(r"(" + "|".join(OFFSET) + r")[((]"
                     r"([一二三四五六七八九十]{1,3}月[一二三四五六七八九十]{1,3}日)[))]")
    return [(m.group(1), m.group(2), m.group(0)) for m in pat.finditer(text)]

这两段建议收藏 :先用 pair_offset_absolute() 在真实语料上把所有自带对照抽出来,你会免费得到一批标注样本。用它们跑一遍 anchor(),哪些词能用文档级偏移、哪些词不能,一次就看清了------比手写测试用例可靠得多。

6. 发现四:汉字时刻的边界比日期更绕

43 处汉字时刻全用中文数字写,而「几点」这件事有三个必须写死的规则:

python 复制代码
PERIOD = {"上午": 0, "早上": 0, "凌晨": 0, "中午": 12,
          "下午": 12, "傍晚": 12, "黃昏": 12, "晚上": 12, "深夜": 0}

def parse_zh_hm(period, hh, mm):
    """「下午 十二時 三十分」→ (12, 30)。"""
    h = zh_num(hh)
    if h is None or not (1 <= h <= 12):
        return None
    m = zh_num(mm) if mm else 0
    if m is None or not (0 <= m <= 59):
        return None
    if period in ("上午", "早上", "凌晨", "深夜") and h == 12:
        h = 0                      # 🔴 上午十二時 = 00:xx
    elif period in ("下午", "中午", "傍晚", "黃昏", "晚上") and h != 12:
        h += 12                    # 🔴 下午一時 = 13:xx,但下午十二時 保持 12
    return h, m

三个容易写错的点:

  • 「下午十二時三十分」= 12:30,不是 00:30。中文的「下午十二時」指中午,不是午夜;
  • 「上午十二時」= 00:xx,同一时刻换个时段词,结果差 12 小时;
  • 「下午十一時二十四分」= 23:24,加 12 是对的------但前提是「十二」这一格已经被特殊处理掉。

语料里真实出现过 上午十一時至晚上七時及 这种跨时段范围表述。范围比单点更麻烦,因为两个时刻各自要按各自的时段词换算。

7. Prompt 契约:把锚点变成可校验的后置条件

上面四条发现合起来,指向同一个设计:别指望模型自己搞对时间,把这活儿拆成「模型抽取 + 代码校验」两段。

Prompt 里要传进去的(逐条,不是全局):

python 复制代码
PROMPT_CONTRACT = {
    "role": "user",
    "content": (
        "下面每一条新闻公报都带有 published_at。\n"
        "请抽取其中的时间信息,输出严格的 JSON 数组,每个元素包含:\n"
        "  event_date ------ 事件发生的日期,ISO 8601(YYYY-MM-DD)\n"
        "  anchor     ------ 你是根据哪个锚点推算出来的,"
        "只能填 published_at / in_text / unknown 三者之一\n"
        "  quote      ------ 原文中支撑这个日期的最短片段(原样抄写,不要改写)\n"
        "规则:\n"
        "  1) 文中的「今日」以 published_at 为准;\n"
        "  2) 若某个相对词指向的是文内前文提到的日期,anchor 必须填 in_text;\n"
        "  3) 无法确定日期时,event_date 填 null、anchor 填 unknown,"
        "不允许猜测;\n"
        "  4) 汉字日期必须规范化成阿拉伯数字的 ISO 格式。"
    ),
}

然后在本地把模型说得出处的东西验一遍:

python 复制代码
def validate_times(items, rows):
    """逐条校验模型输出的时间抽取结果。"""
    problems = []
    for r in rows:
        it = items[r["source_id"]]
        d, anchor_kind, quote = r["event_date"], r["anchor"], r["quote"]
        # ① 出处必须在原文里逐字存在
        if quote and quote not in (it["title"] + " " + it["body"]):
            problems.append(("quote_not_found", r["source_id"], quote))
        # ② 声明用发布日锚定,就必须落在发布日 ±2 天内
        if anchor_kind == "published_at" and d:
            gap = abs((dt.date.fromisoformat(d) - it["pubdate"].date()).days)
            if gap > 2:
                problems.append(("anchor_out_of_range", r["source_id"], gap))
        # ③ 说 unknown 就不许同时给出日期
        if anchor_kind == "unknown" and d is not None:
            problems.append(("guessed_date", r["source_id"], d))
    return problems

三条规则各自拦一类错:

  • quote 必须逐字命中:挡住模型改写原文、或者把「看起来像时间」的东西编进去;
  • anchor=published_at 时日期必须落在发布日附近 :这正是那两条「翌日」会触发的地方------它算出来的 09-15 距发布日只有 1 天,这条规则拦不住它。所以还有第三条;
  • anchor=unknown 不许带日期:把「我不确定」和「我猜的」在结构上分开。

而「翌日」这种真正需要句内指代的,校验器拦不住------只能靠 anchor 字段主动申报。 这也是为什么强制模型填 anchor 比让它填日期更重要:日期是结果,锚点才是推理依据,二者不一致时你只看得见前者。

8. 踩坑清单与结论

现象 处理
只用阿拉伯日期正则 189 个汉字日期一个都抓不到,静默漏 92.6% 先统计写法分布,再写正则
把「今日」当成一个时间 100 条里它落在 6 个日历日上 逐条绑定 published_at
假定所有相对词都以发布日为锚 「翌日」2 条全错,差 3--5 天 强制输出 anchor 字段
只校验日期格式 错锚点算出来的是合法日期 校验必须带原文出处
「下午十二時」按 +12 处理 变成 00:30,差 12 小时 十二点单列规则
让模型输出改写过的引文 无法回溯验证 quote 必须逐字命中原文
不确定就让模型给个日期 幻觉日期看起来和真的一样 anchor=unknown 时日期必须为 null

三条:

  1. 在数据进上下文之前锚定时间,比让模型「注意一下时间」有效得多。 模型没有能力知道你手上那份数据是几号发的------那不在它的输入里。
  2. 锚点会因词而异。 今日/明日/昨日 用文档级偏移就够(本语料里 84/86 全对),翌日 要用句内指代。差异只有 2.3%,抽样根本抽不到。
  3. 免费标注样本藏在文本里。 香港公文「相对词 + 括号绝对日期」的写法给出了 86 个自带答案的样本------用它当回归集,比拍脑袋写测试用例靠谱。

这套方法不挑语料:换任何一个官方中文文本源,先跑一遍 pair_offset_absolute() 看有多少自带对照,再用它验 anchor() ------如果这篇对你有用,收藏 + 点赞,下次处理中文公文时间时可以直接套。

香港公开数据的实测会继续更新,关注不迷路。

9. 参考链接

  1. https://www.info.gov.hk/gia/rss/general_zh.xml
  2. https://www.info.gov.hk/gia/general/today.htm
  3. https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType=fnd\&lang=tc

原创声明:本文为原创技术实践。全部数字来自 2026-09-16 对香港特区政府新闻公报 RSS 最新 100 条的真实采集(跨 6 个发布日,09-11 至 09-16)。文中「锚到发布日的命中率」以文本自带的括号绝对日期为对照标准;「翌日」样本仅 2 条,属于小样本观察,结论限于本批语料,请以自测数据为准。接口字段与发布节奏可能随官方调整而变化。

相关推荐
YsyaaabB1 小时前
Python 数值分析
python
阿洛学长1 小时前
计算机二级 Python 基本操作题(15 分)真题笔记(0101 ~ 1903 全套)
python·pycharm
weixin199701080162 小时前
[特殊字符]️《二手ERP对接电商平台的总体方案:统一数据模型 + 事件驱动 + 灰度上线6原则》(附Python源码)
大数据·python
滚雪球~2 小时前
量化交易 防止Windows电脑自动更新并重启
python·量化
L@ncor3 小时前
第二章可能出现的问题
人工智能·python
y = xⁿ4 小时前
大模型基础概念
python
Behavior4 小时前
每天用 Claude Code 的人,常用指令都在这 100 条里
aigc·claude·vibecoding
ctlover4 小时前
排序与查找算法详解
开发语言·python
用户8356290780514 小时前
如何使用 Python 给 Word 文档添加水印
后端·python