这篇能做出什么
一场 45 分钟的产品周会结束后,能在几分钟内拿到这样一份结构化清单,并直接写进项目管理工具:
```json
[
{
"title": "补齐注册页的错误提示文案",
"owner": "李敏",
"due_date": "2025-04-18",
"priority": "P1",
"acceptance_criteria": "空手机号、格式错误、已注册三种情况各有独立提示,且通过文案评审",
"evidence": "李敏说这个她本周内补,周五前给到设计和前端",
"status": "confirmed",
"source_quote": "……"
},
{
"title": "确认三方登录是否纳入本次迭代",
"owner": null,
"due_date": null,
"priority": "P2",
"acceptance_criteria": "给出纳入或不纳入的结论与理由",
"evidence": "会上没定下来,说下次再聊",
"status": "tentative",
"source_quote": "……"
}
]
```
关键点在于:有主的任务直接建单,没主的任务进「待确认」区,不硬猜。 这比让模型自由发挥写一段纪要摘要有用得多——纪要没人看,任务会被人追。
整条链路是三段:会议内容接入 → 用固定模板提取 → 幂等同步到项目管理工具。下面按顺序拆开。
前置条件清单
开始前把这些准备好,后面每一步都依赖它们:
1. 一个 Claude 账号,能使用对话界面;如果要批量跑,还需要 API Key(在官方控制台生成,具体入口以官方文档为准)。
2. 会议录制与转写能力。大多数会议平台(Zoom、Teams、飞书会议、腾讯会议等)自带云录制和转写,能导出 VTT / SRT / TXT。没有的话,用手机录音 + 任意转写工具也可以,格式统一成纯文本就行。
3. 参会人名单。这是刚需,后面用来约束任务负责人,防止模型编造人名。
4. 一个项目管理工具和它的 API AI 词典:Token">Token。GitHub Issues、Jira、Linear、飞书多维表格、Notion 数据库都行,本文用 GitHub Issues 做完整示例,其它工具换接口地址和字段名即可。
5. 本地环境:Python 3、requests 库、curl。pip install requests 就够。
6. 一份任务模板。下一步会给,可以直接抄。
关于 Claude 侧的会议接入能力(连接器、会议助手入口、可用的集成列表),不同订阅计划的开放范围不一样,以官方文档当前版本为准。但下面的做法不依赖具体入口——只要能拿到转写文本,流程就能跑通。
第 1 步:把会议内容接进来
路线 A:会议平台导出转写(最稳)
会议结束后在会议平台的录制/转写列表里导出字幕文件。VTT 长这样:
```text
WEBVTT
00:00:12.000 --> 00:00:18.000
李敏: 注册页那个错误提示,我这周内补一下
00:00:18.500 --> 00:00:24.000
王强: 好,那三方登录这次还做吗
```
字幕带时间戳和说话人,信息量比纯文本大,尽量保留。但 VTT 有时间轴、样式标签,直接喂给模型是浪费 token,先清洗。
```python
clean_vtt.py
import re
import sys
def clean_vtt(path: str) -> str:
lines = open(path, encoding="utf-8").read().splitlines()
out, seen = [], set()
for line in lines:
if line.strip() == "WEBVTT" or "-->" in line:
continue
line = re.sub(r"<[^>]+>", "", line).strip() # 去掉样式标签
if not line or line.isdigit(): # 去掉纯序号行
continue
key = line
if key in seen: # 连续重复行去重
continue
seen.add(key)
out.append(line)
同一说话人的连续行合并,减少行数
merged = []
for line in out:
if merged and line.split(":")[0] == merged[-1].split(":")[0] and ":" in line:
merged[-1] += " " + line.split(":", 1)[1].strip()
else:
merged.append(line)
return "\n".join(merged)
if __name__ == "__main__":
print(clean_vtt(sys.argv[1]))
```
```bash
python clean_vtt.py meeting.vtt > meeting.txt
```
路线 B:直接在 Claude 对话里处理
不想写代码,就在对话里贴转写文本 + 模板(第 2 步那个),让它按 JSON 输出。适合单场会议、临时处理。
路线 C:桌面端连接器 / MCP
如果已经在用 Claude 桌面端,可以通过 MCP 把项目管理工具接进来,让它读会议文档、写任务单。配置一般放在 claude_desktop_config.json:
```json
{
"mcpServers": {
"project-tool": {
"command": "npx",
"args": ["-y", "你的-mcp-server-包名"],
"env": {
"BASE_URL": "https://your-project-tool.example.com",
"API_TOKEN": "替换为你的 token"
}
}
}
}
```
包名和参数以所选 MCP server 的官方 README 为准,不要照抄示例里的占位值。
第 2 步:写一个稳定出结构的提取模板
这一步决定成败。模板写不好,模型会给一堆"加强沟通""持续优化"这类没法执行的任务。核心是把规则写死。
系统提示(system)
```text
你是会议纪要转任务的解析器。你的唯一输出是合法 JSON 数组,不要输出解释、不要加代码块标记。
每条任务对象包含字段:
- title: 动词开头的任务标题,不超过 30 字
- owner: 负责人姓名,只能取自给定的参会人名单;无法确定时写 null
- due_date: YYYY-MM-DD;无法确定时写 null
- due_date_raw: 会上关于时间的原话,没有则写 null
- priority: P0 / P1 / P2 三选一,按会上明确的紧急程度判断,没有明说就填 P2
- acceptance_criteria: 怎样算做完,一句话,可验证
- evidence: 支持这条任务的会议原话摘录,不超过 50 字
- status: confirmed(有人认领且有明确动作)或 tentative(只是提到、没定人、没定时间)
- dependencies: 依赖的其他任务标题数组,没有则空数组
硬性规则:
1. 只提取有行动承诺的内容。纯信息同步、纯表态不产出任务。
2. owner 必须来自参会人名单,禁止编造人名。找不到就 null。
3. 一条任务对应一个交付物。会上说"顺便把 A 和 B 也做了",拆成两条。
4. 相对时间要结合会议日期换算成绝对日期。换算不了就 null,并在 due_date_raw 保留原话。
5. 每条任务必须有 evidence,evidence 必须是转写文本里真实出现的原话。
6. 讨论没有结论的话题,输出一条 status 为 tentative 的任务,title 用"确认……"开头。
7. 转写里说话人识别不清时,宁可 owner 写 null,也不要猜。
```
用户消息(user)
```text
会议日期:2025-04-15
会议主题:产品周会
参会人:李敏、王强、张倩、陈昊
以下是转写文本:
<transcript>
{这里放 clean_vtt 的输出}
</transcript>
```
几个规则值得单独说:
- 必须要求 evidence。有了原文摘录,人工复核时能三秒判断这条任务是不是模型瞎编的。这是整套流程里最值钱的一条约束。
- 允许输出 null。不给模型"必须填"的压力,它就不会编人名和日期。宁可多一条待确认,不要多十条假任务。
- 区分 confirmed / tentative。会上一半的讨论是没结论的,硬转成任务会让看板迅速变脏。
第 3 步:跑通一次 API 调用
先用 curl 确认请求能通。准备 payload.json:
```json
{
"model": "替换为官方文档中的当前模型 ID",
"max_tokens": 4000,
"system": "把第 2 步的系统提示粘进来",
"messages": [
{
"role": "user",
"content": "会议日期:2025-04-15\n参会人:李敏、王强、张倩、陈昊\n\n转写文本:\n(此处粘贴)"
}
]
}
```
```bash
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d @payload.json
```
模型 ID 不要写死,model 字段填官方文档里当前可用的型号,换模型时只改这一处。
跑通后换成脚本,一次处理整场会议:
```python
extract_tasks.py
import json
import os
import re
import sys
import requests
API_URL = "https://api.anthropic.com/v1/messages"
MODEL = os.environ.get("CLAUDE_MODEL", "替换为当前模型 ID")
API_KEY = os.environ["ANTHROPIC_API_KEY"]
SYSTEM = open("system_prompt.txt", encoding="utf-8").read()
def call_claude(transcript: str, meta: str) -> str:
resp = requests.post(
API_URL,
headers={
"x-api-key": API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
json={
"model": MODEL,
"max_tokens": 4000,
"system": SYSTEM,
"messages": [{
"role": "user",
"content": f"{meta}\n\n转写文本:\n<transcript>\n{transcript}\n</transcript>"
}],
},
timeout=120,
)
resp.raise_for_status()
return resp.json()["content"][0]["text"]
def parse_json(text: str):
模型偶尔会包一层代码块,兜底剥掉
text = re.sub(r"^``(?:json)?|``$", "", text.strip(), flags=re.MULTILINE).strip()
return json.loads(text)
if __name__ == "__main__":
transcript = open(sys.argv[1], encoding="utf-8").read()
meta = "会议日期:2025-04-15\n参会人:李敏、王强、张倩、陈昊"
tasks = parse_json(call_claude(transcript, meta))
json.dump(tasks, open("tasks.json", "w", encoding="utf-8"),
ensure_ascii=False, indent=2)
print(f"提取到 {len(tasks)} 条任务")
```
长会议会超出上下文窗口。处理办法是按时间分段:每 20 分钟切一段,各跑一次,最后按 title 合并去重。分段的坏处是跨段的依赖关系会丢,可以在合并后再跑一次"补依赖"的轻量调用。
第 4 步:本地校验,人工过一遍
模型输出不能直接进系统。加一层校验脚本,把明显有问题的拦下来:
```python
validate.py
import json, datetime, sys
tasks = json.load(open("tasks.json", encoding="utf-8"))
roster = {"李敏", "王强", "张倩", "陈昊"}
problems = []
for i, t in enumerate(tasks):
if not t.get("title"):
problems.append(f"[{i}] 缺 title")
if t.get("owner") and t["owner"] not in roster:
problems.append(f"[{i}] owner 不在名单:{t['owner']}")
t["owner"], t["status"] = None, "tentative"
d = t.get("due_date")
if d:
try:
datetime.date.fromisoformat(d)
except ValueError:
problems.append(f"[{i}] 日期格式不对:{d}")
t["due_date"] = None
if t.get("status") == "confirmed" and not t.get("owner"):
t["status"] = "tentative"
if not t.get("evidence"):
problems.append(f"[{i}] 缺 evidence,需人工确认")
标题近似去重
seen = {}
for t in tasks:
key = "".join(t["title"].split())[:12]
if key in seen:
problems.append(f"疑似重复:{t['title']} / {seen[key]}")
else:
seen[key] = t["title"]
json.dump(tasks, open("tasks.json", "w", encoding="utf-8"),
ensure_ascii=False, indent=2)
print("\n".join(problems) or "校验通过")
```
校验之后一定要人看一眼,重点看两类:status 为 tentative 的(要不要真的建单),以及 evidence 对不上的(可能是幻觉)。这个动作花两分钟,能省掉后面一周的看板噪音。
第 5 步:同步到项目管理工具
核心是幂等
会议重跑、脚本重试、下周一又跑了一次同一场会——这些都会导致重复建单。解决办法是给每条任务算一个稳定的外部 ID:
```python
import hashlib
def external_id(meeting_id: str, task: dict) -> str:
raw = f"{meeting_id}|{task['title']}|{task.get('owner') or ''}"
return "mtg-" + hashlib.sha1(raw.encode()).hexdigest()[:12]
```
建单前先按这个 ID 搜一遍,存在就跳过或更新,不存在才创建。也可以把它写进任务描述里当标记。
GitHub Issues 完整示例
```python
sync_github.py
import json, os, requests
TOKEN = os.environ["GITHUB_TOKEN"]
REPO = os.environ["GITHUB_REPO"] # 形如 your-org/your-repo
API = f"https://api.github.com/repos/{REPO}/issues"
HEAD = {
"Authorization": f"Bearer {TOKEN}",
"Accept": "application/vnd.github+json",
}
def build_body(t: dict, ext_id: str) -> str:
return (
f"验收标准:{t.get('acceptance_criteria') or '待补充'}\n\n"
f"会议原话:> {t.get('evidence') or ''}\n\n"
f"截止:{t.get('due_date') or '待定'}"
f"(原话:{t.get('due_date_raw') or '无'})\n\n"
f"<!-- {ext_id} -->"
)
def already_exists(ext_id: str) -> bool:
r = requests.get(API, headers=HEAD,
params={"state": "all", "per_page": 100}, timeout=30)
r.raise_for_status()
return any(ext_id in (i.get("body") or "") for i in r.json())
for t in json.load(open("tasks.json", encoding="utf-8")):
if t["status"] != "confirmed":
continue # 待确认的先不建单
ext = external_id("2025-04-15-产品周会", t)
if already_exists(ext):
print("跳过重复:", t["title"])
continue
payload = {
"title": t["title"],
"body": build_body(t, ext),
"labels": [t["priority"], "from-meeting"],
}
r = requests.post(API, headers=HEAD, json=payload, timeout=30)
r.raise_for_status()
print("已创建:", r.json()["html_url"])
```
换成 Jira 就是 POST /rest/api/3/issue,换成飞书多维表格就是写入记录接口——字段映射换一下,幂等逻辑照搬。负责人字段各平台要求不同(有的要用户 ID 而不是姓名),需要先做一次姓名到 ID 的映射表,别把中文名直接塞进 assignee。
待确认任务怎么办
两条路:一是写进一张「待确认」表(多维表格或单独的 Issue 池),下次会议开头花三分钟过一遍;二是直接留在 tasks.json 里,人工筛完再跑一次同步。推荐前者,因为没结论的讨论本身就是需要跟进的信息。
第 6 步:固化成日常流程
跑通一次之后把它变成不用想的动作:
```bash
会议结束后按顺序执行
python clean_vtt.py raw/2025-04-15.vtt > meeting.txt
python extract_tasks.py meeting.txt
python validate.py
python sync_github.py
```
包成脚本或 Makefile,会议结束点一下。想更进一步,用会议平台的录制完成 webhook 触发,或者用 cron 每天固定时间扫一遍当天的转写目录。
常见坑与排错
| 现象 | 原因 | 处理 |
|---|---|---|
| 任务全是"加强沟通""持续跟进" | 模板没约束动作和交付物 | 在系统提示里加一句:title 必须以动词开头且能对应一个具体交付物 |
| 出现会里没提过的人名 | 模型在补全 | 强制 owner 从名单取,配合第 4 步的名单校验兜底 |
| 截止日期全是会议当天 | 相对时间没换算 | 提示里明确给出会议日期,要求先换算再输出 |
| 同一场会重复建单 | 缺少幂等键 | 用 title + owner 计算哈希,建单前先搜 |
| 长会议解析到一半就断 | 超上下文 | 按时间分段,20 分钟一段,合并后补依赖 |
| 说话人识别错乱 | 转写质量差 | 让 owner 允许为 null,宁可待确认;重要会议用带说话人分离的转写 |
| API 报 429 | 并发过高 | 加指数退避重试,批处理串行跑 |
| 输出不是合法 JSON | 模型加了说明文字 | 提示里要求"只输出 JSON",脚本里再做一次代码块剥离兜底 |
还有一条容易被忽略:会议内容里可能有薪酬、人事、客户隐私信息。把整段转写发给外部接口之前,确认所在公司的数据合规要求;必要时先做脱敏,把具体金额、手机号、身份证号替换掉再发。
下一步建议
跑顺之后,可以往前推三步:
1. 加一块决策记录。同样的模板再输出一个 decisions 数组,记录"决定了什么、为什么、谁反对"。任务会过期,决策不会,日后回查价值更高。
2. 做闭环回写。每天把任务完成情况拉一遍,下次开会前自动生成"上次会后的任务进展"放进会议材料开头。这一步做完,会议本身会变短。
3. 让 Claude 直接读写工具。通过 MCP 把项目管理工具接进对话,会上一边讨论一边建单,会后不用再跑同步脚本。这条路对配置要求高一些,但省掉的是最枯燥的那一段。
先用一场真实的会跑一遍,把 tasks.json 导出来和人工整理的清单对一遍。差在哪里,就回去改模板里的那一条规则——模板是这套流程里唯一需要反复打磨的东西。
