Blame view

docs/Skills实现方案-LangChain1.0.md 10.1 KB
e7f2b240   tangwang   first commit
1
2
3
4
5
6
7
8
9
  # Skills 渐进式展开实现方案(LangChain 1.0+)
  
  ## 一、需求概述
  
  **Skills** 替代零散的工具调用,实现**渐进式展开**(Progressive Disclosure):  
  Agent 在 system prompt 中只看到技能摘要,按需加载详细技能内容,减少 token 消耗、提升扩展性。
  
  | 技能 | 英文标识 | 职责 |
  |------|----------|------|
8810a6fa   tangwang   重构
10
  | 查找相关商品 | lookup_related | 基于文本/图片查找相似或相关商品(图片需先分析风格) |
e7f2b240   tangwang   first commit
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  | 搜索商品 | search_products | 按自然语言描述搜索商品 |
  | 检验商品 | check_product | 检验商品是否符合用户要求 |
  | 结果包装 | result_packaging | 格式化、排序、筛选并呈现结果 |
  | 售后相关 | after_sales | 退换货、物流、保修等售后问题 |
  
  ---
  
  ## 二、LangChain 1.0 中的 Skills 实现方式
  
  ### 2.1 两种实现路线
  
  | 方式 | 适用场景 | 依赖 |
  |------|----------|------|
  | **方式 A:create_agent + 自定义 Skill 中间件** | 购物导购等业务 Agent | `langchain>=1.0`、`langgraph>=1.0` |
  | **方式 B:Deep Agents + SKILL.md** | 依赖文件系统、多技能目录 | `deepagents` |
  
8810a6fa   tangwang   重构
27
  购物导购场景推荐**方式 A**,更易与现有 Search API 等服务集成。
e7f2b240   tangwang   first commit
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  
  ### 2.2 核心思路:Progressive Disclosure
  
  ```
  用户请求 → Agent 看轻量描述 → 判断需要的技能 → load_skill → 拿到完整说明 → 执行工具 → 回复
  ```
  
  - **启动时**:只注入技能名称 + 简短描述(1–2 句)
  - **按需加载**:Agent 调用 `load_skill(skill_name)` 获取完整指令
  - **执行**:按技能说明调用对应工具
  
  ---
  
  ## 三、实现架构
  
  ### 3.1 技能定义结构
  
  ```python
  from typing import TypedDict
  
  class Skill(TypedDict):
      """可渐进式展开的技能"""
      name: str          # 唯一标识
      description: str   # 1-2 句,展示在 system prompt
      content: str      # 完整指令,仅在 load_skill 时返回
  ```
  
  ### 3.2 五个技能定义示例
  
  ```python
  SKILLS: list[Skill] = [
      {
          "name": "lookup_related",
8810a6fa   tangwang   重构
61
          "description": "查找与某商品相关的其他商品,支持文本相似、同品类推荐。",
e7f2b240   tangwang   first commit
62
63
64
65
66
67
68
69
          "content": """# 查找相关商品
  
  ## 适用场景
  - 用户上传图片要求「找类似的」
  - 用户说「和这个差不多」「搭配的裤子」
  - 用户已有一件商品,想找相关款
  
  ## 操作步骤
8810a6fa   tangwang   重构
70
  1. **有图片**:先调用 `analyze_image_style` 理解风格,再调用 `search_products` 用描述搜索
e7f2b240   tangwang   first commit
71
72
73
74
  2. **无图片**:用 `search_products` 描述品类+风格+颜色
  3. 可结合上下文中的商品 ID、品类做同品类推荐
  
  ## 可用工具
e7f2b240   tangwang   first commit
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  - `search_products(query, limit)`:文本搜索
  - `analyze_image_style(image_path)`:分析图片风格""",
      },
      {
          "name": "search_products",
          "description": "按自然语言描述搜索商品,如「红色连衣裙」「运动鞋」等。",
          "content": """# 搜索商品
  
  ## 适用场景
  - 用户用文字描述想要什么
  - 如「冬天穿的外套」「正装衬衫」「跑步鞋」
  
  ## 操作步骤
  1. 将用户描述整理成结构化 query(品类+颜色+风格+场景)
  2. 调用 `search_products(query, limit)`,limit 默认 5–10
  3. 如有图片,可先 `analyze_image_style` 提炼关键词再搜索
  
  ## 可用工具
  - `search_products(query, limit)`:自然语言搜索""",
      },
      {
          "name": "check_product",
          "description": "检验商品是否符合用户要求,如尺寸、材质、场合、价格区间等。",
          "content": """# 检验商品是否符合要求
  
  ## 适用场景
  - 用户问「这款适合我吗」「有没有 XX 材质的」
  - 用户提出约束:尺寸、价格、场合、材质
  
  ## 操作步骤
  1. 从对话中提取约束条件(尺寸、材质、场合、价格等)
  2. 对已召回商品做筛选或二次搜索
  3. 调用 `search_products` 时在 query 中带上约束
  4. 回复时明确说明哪些符合、哪些不符合
  
  ## 注意
  - 无专门工具时,用 search_products 的 query 表达约束
  - 可结合商品元数据(baseColour, season, usage 等)做简单筛选""",
      },
      {
          "name": "result_packaging",
          "description": "对搜索结果进行格式化、排序、筛选并呈现给用户。",
          "content": """# 结果包装
  
  ## 适用场景
  - 工具返回多条商品后需要整理呈现
  - 用户要求「按价格排序」「只要前 3 个」
  
  ## 操作步骤
  1. 按相关性/相似度排序
  2. 限制展示数量(通常 3–5 个)
  3. **必须使用以下格式**呈现每个商品:
  
  ```
  1. [Product Name]
     ID: [Product ID Number]
     Category: [Category]
     Color: [Color]
     Gender: [Gender]
     Season: [Season]
     Usage: [Usage]
     Relevance: [XX%]
  ```
  
  4. ID 字段不可省略,用于前端展示图片""",
      },
      {
          "name": "after_sales",
          "description": "处理退换货、物流、保修、尺码建议等售后问题。",
          "content": """# 售后相关
  
  ## 适用场景
  - 退换货政策、运费、签收时间
  - 尺码建议、洗涤说明
  - 保修、客服联系方式
  
  ## 操作步骤
  1. 此类问题无需调用商品搜索工具
  2. 按平台统一售后政策回答
  3. 涉及具体商品时,可结合商品 ID 查询详情后再回答
  4. 复杂问题引导用户联系客服""",
      },
  ]
  ```
  
  ---
  
  ## 四、核心代码实现
  
  ### 4.1 load_skill 工具
  
  ```python
  from langchain.tools import tool
  
  @tool
  def load_skill(skill_name: str) -> str:
      """加载技能的完整内容到 Agent 上下文中。
  
      当需要处理特定类型请求时,调用此工具获取该技能的详细说明和操作步骤。
  
      Args:
          skill_name: 技能名称,可选值:lookup_related, search_products, check_product, result_packaging, after_sales
      """
      for skill in SKILLS:
          if skill["name"] == skill_name:
              return f"Loaded skill: {skill_name}\n\n{skill['content']}"
  
      available = ", ".join(s["name"] for s in SKILLS)
      return f"Skill '{skill_name}' not found. Available: {available}"
  ```
  
  ### 4.2 SkillMiddleware(注入技能描述)
  
  ```python
  from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
  from langchain.messages import SystemMessage
  from typing import Callable
  
  class ShoppingSkillMiddleware(AgentMiddleware):
      """将技能描述注入 system prompt,使 Agent 能发现并按需加载技能"""
  
      tools = [load_skill]
  
      def __init__(self):
          skills_list = []
          for skill in SKILLS:
              skills_list.append(f"- **{skill['name']}**: {skill['description']}")
          self.skills_prompt = "\n".join(skills_list)
  
      def wrap_model_call(
          self,
          request: ModelRequest,
          handler: Callable[[ModelRequest], ModelResponse],
      ) -> ModelResponse:
          skills_addendum = (
              f"\n\n## 可用技能(按需加载)\n\n{self.skills_prompt}\n\n"
              "在需要详细说明时,使用 load_skill 工具加载对应技能。"
          )
          new_content = list(request.system_message.content_blocks) + [
              {"type": "text", "text": skills_addendum}
          ]
          new_system_message = SystemMessage(content=new_content)
          modified_request = request.override(system_message=new_system_message)
          return handler(modified_request)
  ```
  
  ### 4.3 创建带 Skills 的 Agent
  
  ```python
  from langchain.agents import create_agent
  from langgraph.checkpoint.memory import MemorySaver
  
8810a6fa   tangwang   重构
227
228
  # 基础工具(搜索、风格分析等)
  from app.tools.search_tools import search_products, analyze_image_style
e7f2b240   tangwang   first commit
229
230
231
232
233
234
  
  agent = create_agent(
      model="gpt-4o-mini",
      tools=[
          load_skill,           # 技能加载
          search_products,
e7f2b240   tangwang   first commit
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
          analyze_image_style,
      ],
      system_prompt="""你是智能时尚购物助手。根据用户需求,先判断使用哪个技能,必要时用 load_skill 加载技能详情。
  
  处理商品结果时,必须遵守 result_packaging 技能中的格式要求。""",
      middleware=[ShoppingSkillMiddleware()],
      checkpointer=MemorySaver(),
  )
  ```
  
  ---
  
  ## 五、与工具的关系
  
  | 能力 | 技能 | 工具 |
  |------|------|------|
8810a6fa   tangwang   重构
251
  | 查找相关 | lookup_related | search_products, analyze_image_style |
e7f2b240   tangwang   first commit
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  | 搜索商品 | search_products | search_products |
  | 检验商品 | check_product | search_products(用 query 表达约束) |
  | 结果包装 | result_packaging | 无(纯 prompt 约束) |
  | 售后 | after_sales | 无(或对接客服 API) |
  
  - **技能**:提供「何时用、怎么用」的说明,支持渐进式加载。
  - **工具**:实际执行搜索、分析等操作。
  
  ---
  
  ## 六、可选:技能约束(进阶)
  
  若希望「先加载技能再使用工具」,可结合 `ToolRuntime` 和 state 做约束:
  
  ```python
  from langchain.tools import tool, ToolRuntime
  from langgraph.types import Command
  from langchain.messages import ToolMessage
  from typing_extensions import NotRequired
  
  class CustomState(AgentState):
      skills_loaded: NotRequired[list[str]]
  
  @tool
  def load_skill(skill_name: str, runtime: ToolRuntime) -> Command:
      """..."""
      for skill in SKILLS:
          if skill["name"] == skill_name:
              content = f"Loaded skill: {skill_name}\n\n{skill['content']}"
              return Command(update={
                  "messages": [ToolMessage(content=content, tool_call_id=runtime.tool_call_id)],
                  "skills_loaded": [skill_name],
              })
      # ...
  
  # 在 check_product 等工具中检查 skills_loaded
  ```
  
  ---
  
  ## 七、依赖与版本
  
  ```text
  # requirements.txt
  langchain>=1.0.0
  langchain-openai>=0.2.0
  langchain-core>=0.3.0
  langgraph>=1.0.0
  ```
  
  - Python 3.10+
  - 若使用 Deep Agents 的 SKILL.md,需额外安装 `deepagents`
  
  ---
  
  ## 八、总结
  
  | 项目 | 说明 |
  |------|------|
  | **效果** | 系统 prompt 只放简短技能描述,按需加载完整内容,减少 token、便于扩展 |
  | **流程** | 轻量描述 → load_skill → 完整说明 → 调用工具 → 回复 |
  | **实现** | `SkillMiddleware` + `load_skill` + `create_agent` |
  | **技能** | lookup_related, search_products, check_product, result_packaging, after_sales |
  
  完整示例可参考官方教程:[Build a SQL assistant with on-demand skills](https://docs.langchain.com/oss/python/langchain/multi-agent/skills-sql-assistant)