粘贴一份会议纪要,用
response_format的 json_schema(strict) 拿到待办清单:谁、做什么、截止日期。程序核对负责人确实在纪要里出现过、日期合法,原文里写明的日期由程序补全。最后导出成.ics文件,可以直接导入手机或电脑日历。
先看清成品
- 测试: pytest 4 项通过。
- 本地 qwen2.5 1.5B 实测: 3 条待办都提取出来了,其中 2 条有截止日期(10-28 和 10-24,由程序从原文补全),刘洋那条标为“日期待定”。导出了 2 个日历事件。
- qwen3 0.6B: 漏掉了王敏的待办。
没有在 box 上跑的部分: box 上没有 Moonshot API Key,没有调用过 Kimi 官方 API。
- 用到的替身: 文中的
KIMI_BASE_URL指向两种替身。
- 本地 Ollama 的 OpenAI 兼容接口。
- A 篇自己写的
fake_moonshot.py。它是我按官方文档写的模拟服务器,只模拟了文件上传、抽取、删除这几个接口:用 pdftotext 抽取文字,对话转发给本地 Ollama。- 结论: 下面的成功和失败都是本地小模型的表现,不代表 Kimi 本身的效果。
合规提醒:Kimi API Key 只放在环境变量里。上传的文件会存到 Kimi 开放平台的账号下,含有个人信息、合同、病历等敏感内容的文件,要先确认公司的规定和对方是否同意。用完的文件及时删除(本文的代码会自动删除)。模型的回答只能作为参考,涉及报销、人事、法律的结论以原文件和负责人的确认为准。价格以官网“定价”页为准,本文不列价格。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
meeting_todo.py |
定义 schema → 获取 JSON → 核对负责人 → 补全原文里写明的日期 → 导出 .ics |
meeting.txt |
虚构的周会纪要 |
test_todo.py |
负责人核对、日期补全(日期写在 due 或 task 里都能识别)、生成 .ics 格式 |
需要准备哪些环境?
| 项目 | 要求 | 说明 |
|---|---|---|
| Python | 3.9 以上 | pip install openai pytest |
| 模型 | Kimi API,或本地 Ollama 模型 | Kimi 文档的 response_format 页介绍了 json_object 和 json_schema 两种方式 |
文件结构
42-kimi-C-src/
├── meeting_todo.py
├── meeting.txt
└── test_todo.py
第 1 步:代码
meeting_todo.py:
"""会议纪要 → 待办清单(Structured Output:response_format=json_schema),再导出成日历文件 .ics。
程序会核对:负责人必须在纪要里出现过;日期必须是合法的 YYYY-MM-DD;没有明确日期的写 null,不许模型猜。
环境变量:MOONSHOT_API_KEY、KIMI_BASE_URL(默认 https://api.moonshot.cn/v1)、KIMI_MODEL(默认 kimi-k2.6)
用法:python meeting_todo.py meeting.txt [todo.ics]"""
import json, os, sys, uuid
from datetime import date, datetime, timezone
from openai import OpenAI
client = OpenAI(base_url=os.environ.get("KIMI_BASE_URL", "https://api.moonshot.cn/v1"),
api_key=os.environ.get("MOONSHOT_API_KEY") or "missing-key")
MODEL = os.environ.get("KIMI_MODEL", "kimi-k2.6")
SCHEMA = {"type": "object", "additionalProperties": False, "required": ["todos"], "properties": {"todos": {"type": "array", "items": {
"type": "object", "additionalProperties": False, "required": ["owner", "task", "due"],
"properties": {"owner": {"type": "string"}, "task": {"type": "string"},
"due": {"type": ["string", "null"], "description": "YYYY-MM-DD;纪要里没有明确日期就填 null"}}}}}}
def extract(text, year):
r = client.chat.completions.create(model=MODEL, temperature=0, messages=[
{"role": "system", "content": f"从会议纪要中提取有明确负责人的待办事项,输出 json。今年是 {year} 年。"
"写明了几月几日的,due 填对应日期;“下周一”这类相对日期不要自己换算,原样写进 task,due 填 null。没有负责人的事项不要输出。"},
{"role": "user", "content": text}],
response_format={"type": "json_schema", "json_schema": {"name": "todos", "strict": True, "schema": SCHEMA}})
return json.loads(r.choices[0].message.content)["todos"]
def fill_explicit_dates(todos, year):
"""把日期统一成 YYYY-MM-DD:模型把 due 写成“10月28日”、或者没填但任务里写着明确日期,都由程序换算。
“下周一”这类相对日期不处理,保持待定。"""
import re
for t in todos:
try:
if t["due"]: date.fromisoformat(t["due"]); continue
except ValueError: pass
m = re.search(r"(\d{1,2})\s*月\s*(\d{1,2})\s*日", f"{t['due'] or ''} {t['task']}")
t["due"] = f"{year}-{int(m[1]):02d}-{int(m[2]):02d}" if m else None
if m: t["filled"] = True
return todos
def check(todos, text):
problems = []
for t in todos:
if t["owner"] not in text: problems.append(f"负责人“{t['owner']}”不在纪要里")
if t["due"]:
try: date.fromisoformat(t["due"])
except ValueError: problems.append(f"日期“{t['due']}”不是 YYYY-MM-DD"); t["due"] = None
return problems
def to_ics(todos):
now = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
ev = [f"BEGIN:VEVENT\r\nUID:{uuid.uuid4()}\r\nDTSTAMP:{now}\r\nDTSTART;VALUE=DATE:{t['due'].replace('-', '')}\r\n"
f"SUMMARY:{t['owner']}:{t['task']}\r\nEND:VEVENT" for t in todos if t["due"]]
return "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//meeting_todo//CN\r\n" + "\r\n".join(ev) + "\r\nEND:VCALENDAR\r\n"
if __name__ == "__main__":
text = open(sys.argv[1], encoding="utf-8").read()
todos = fill_explicit_dates(extract(text, date.today().year), date.today().year)
for p in check(todos, text): print("⚠", p)
for t in todos: print(f"- [{t['due'] or '日期待定'}] {t['owner']}:{t['task']}" + ("(日期由程序从原文补全)" if t.get("filled") else ""))
if len(sys.argv) > 2:
open(sys.argv[2], "w", encoding="utf-8", newline="").write(to_ics(todos))
print(f"已写入 {sys.argv[2]}({sum(1 for t in todos if t['due'])} 个有日期的待办,可导入手机或电脑日历)")
meeting.txt:
10 月 21 日 周会纪要(虚构示例)
参会:王敏、陈晨、刘洋
1. 官网改版:陈晨负责在 10 月 28 日前出首页设计稿,王敏周五(10 月 24 日)前确认文案。
2. 客户回访:刘洋下周一联系三家重点客户,整理反馈。
3. 年会场地:暂未定,下次周会再讨论。
test_todo.py:
import os; os.environ.setdefault("MOONSHOT_API_KEY", "test")
from meeting_todo import check, to_ics
def test_owner_and_date_checks():
todos = [{"owner": "赵六", "task": "x", "due": "2026-13-01"}]
p = check(todos, "参会:王敏")
assert len(p) == 2 and todos[0]["due"] is None
def test_ics_only_dated():
ics = to_ics([{"owner": "王敏", "task": "确认文案", "due": "2026-10-24"}, {"owner": "刘洋", "task": "回访", "due": None}])
assert ics.count("BEGIN:VEVENT") == 1 and "DTSTART;VALUE=DATE:20261024" in ics
def test_fill_explicit_dates():
from meeting_todo import fill_explicit_dates
t = fill_explicit_dates([{"owner": "陈晨", "task": "10 月 28 日前出设计稿", "due": None},
{"owner": "刘洋", "task": "下周一联系客户", "due": None}], 2026)
assert t[0]["due"] == "2026-10-28" and t[1]["due"] is None
def test_due_written_in_chinese_is_normalized():
from meeting_todo import fill_explicit_dates
assert fill_explicit_dates([{"owner": "王敏", "task": "确认文案", "due": "10月24日"}], 2026)[0]["due"] == "2026-10-24"
第 2 步:运行
$ KIMI_BASE_URL=http://127.0.0.1:11434/v1 KIMI_MODEL=qwen2.5-local:1.5b python meeting_todo.py meeting.txt todo.ics
- [2026-10-28] 陈晨:出首页设计稿(日期由程序从原文补全)
- [2026-10-24] 王敏:确认文案(日期由程序从原文补全)
- [日期待定] 刘洋:联系客户,整理反馈
已写入 todo.ics(2 个有日期的待办,可导入手机或电脑日历)
检查点:“下周一”没有被换算成具体日期,这是有意的:相对日期由人来确认,程序和模型都不去猜。“年会场地”在纪要里是“暂未定”,所以没有生成待办。
调试示例:日期写进了任务描述里
一开始,模型有时把“10 月 28 日前”写进 task 字段,due 字段却是 null,日历里就少了这个事件。fill_explicit_dates 现在同时在 due 和 task 里找“X月X日”格式的明确日期,按纪要的年份补全,并注明“由程序从原文补全”。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 负责人是纪要里没出现过的人 | 模型编造 | 程序核对后丢弃,并提示 |
| 导入日历后时间不对 | 全天事件的格式写错 | 本文使用 DTSTART;VALUE=DATE |
| 漏掉了待办 | 小模型能力有限 | 换大模型,并人工对照纪要 |
模板规则
- 用 schema 约束结构,用程序核对内容。
- 日期只取原文里写明的,相对日期留给人确认。
- 导出前列出全部待办,让人过目。
项目资源
| 用途 | 地址 |
|---|---|
| response_format(Structured Output) | https://platform.kimi.com/docs/guide/response_format |
| JSON Mode | https://platform.kimi.com/docs/guide/use-json-mode-feature-of-kimi-api |
| iCalendar 规范 RFC 5545 | https://www.rfc-editor.org/rfc/rfc5545 |
从纪要到日历只差一个 schema 和几道核对,省下的是会后手工整理待办的时间。