LangGraph 图初始化;FastAPI(Starlette)内置的通用容器;Skill 创建与维护

LangGraph 图初始化;FastAPI(Starlette)内置的通用容器;Skill 创建与维护

LangGraph 图初始化 & app.state

图初始化:两种写法

写法1:跟 lifespan/ctx 绑定

优点:启动即校验连接。
缺点:新增图要同步改 4 处(lifespan、路由、worker startup、handler)。

写法2:模块级异步单例

优点:随处 await get_adaptive_rag_graph() 调用。
缺点:首次调用才建连接,启动阶段发现不了问题。

取舍:图不多、不追求启动即校验 → 写法2;严格要求可靠性的大项目 → 写法1。

FastAPI(Starlette)内置的通用容器

app.state是FastAPI(Starlette)内置的通用容器,用来存放跨请求共享的数据(连接池、图对象等)。

app.state.redis = await create_pool(...)
# 路由里:request.app.state.redis

这个内置的通用容器app.state规模小时直接用没问题;东西多了可以考虑用 dataclass 集中管理字段。

Skill 创建与维护

结构

skill-name/
├── SKILL.md          # 必需:YAML frontmatter + Markdown 正文
├── scripts/          # 可选:可执行脚本
├── references/       # 可选:详细文档,正文超 500 行时拆出来,按需加载
└── assets/           # 可选:模板、素材

渐进式加载三层:name+description 常驻上下文(约100 token/个)→ 正文按需加载 → references/assets 按需加载。这意味着可以多装几个 skill 而不必担心占满上下文。

Frontmatter 写法

name: 技能名(简短、唯一)
description: 做什么 + 什么场景触发。这是唯一决定智能体会不会调用它的字段,正文里再详细也没用。写具体关键词、具体场景,宁可写得"push"一点,避免该触发时没触发。
argument-hint: 一句示例用法(可选)
user-invocable: true

新建一个 skill 的步骤

  1. 明确场景:这个 skill 解决"哪类反复出现的任务",不要泛泛而写。
  2. 收集事实来源
    • 优先在有代码库访问权限的环境(Claude Code / Cowork)里生成,让内容直接来自源码,天然可信。
    • 如果只能在网页对话里整理(没有仓库访问),要在文档里明确标注"待核实",并列出需要贴哪部分代码来核实。
  3. 正文写成可执行指令,不是背景介绍。反例:"X 是一个 Y 模型";正例:"调用 X 时必须用 A 方式,不要用 B 方式,否则会导致 C 问题"。
  4. 正文控制在 500 行以内,超了就拆到 references/,正文里写清楚"什么情况下该去读哪个 reference 文件"。

多个 skill 共存时的组织原则

  • 一个 skill 只管一件事,按项目/子系统边界拆分,不要塞成大杂烬。
  • 避免内容重叠:两个 skill 描述同一件事,会导致触发时互相抢,智能体可能读错那一个。
  • 标注可信度等级:核实过源码的 vs 对话整理待核实的,在文档开头写明,方便自己和协作者判断该信到什么程度。

排查"触发不准"

  • 该触发没触发 → 通常是 description 写得不够具体、关键词覆盖不够,补充用户可能的实际问法。
  • 触发了不该触发的场景 → description 和别的 skill 重叠,或者写得太宽泛,收窄场景描述。
  • skill 数量较多(5-6 个以上)时,值得用官方 skill-creator 流程做系统性的触发准确率评估,而不是靠感觉一条条改。

维护习惯

  • 每次相关代码/架构有重大改动后,顺手更新对应 skill,或者明确告知哪部分可能已过时。
  • 定期(如每月)粗过一遍所有 skill,处理"待核实"清单里堆积的条目。
  • 用 git 管理所有 skill 文件夹,网页端 Customize → Skills 里的上传只当作"部署",不当作版本记录。