《Agentic Design Patterns》阅读记录

Chapter 1 Prompt Chaining

1
pip install -U langchain langchain-community langchain-deepseek langgraph python-dotenv

Hands-On Code Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import os
from langchain_deepseek import ChatDeepSeek
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

## 为了更好地安全性,从 .env 文件加载环境变量
from dotenv import load_dotenv
load_dotenv()
## 确保你的 DEEPSEEK_API_KEY 在 .env 文件中正确设置

## 初始化语言模型(此处使用ChatDeepSeek)
llm = ChatDeepSeek(
model="deepseek-chat",
temperature=0
)

## --- 提示词 1: 提取信息 ---
prompt_extract = ChatPromptTemplate.from_template(
"请从以下文本中提取技术规格:\n\n{text_input}"
)

## --- 提示词 2: 转换为 JSON ---
prompt_transform = ChatPromptTemplate.from_template(
"请将以下规格转换为 JSON 对象,使用'cpu'、'memory' 和 'storage' 作为键:\n\n{specifications}"
)

## --- 利用 LCEL 构建处理链
## StrOutputParser 用于将 LLM 的消息输出解析为简单字符串。
extraction_chain = prompt_extract | llm | StrOutputParser()

## 完整的链将提取链的输出传递到转换提示词的 'specifications' 参数中
full_chain = (
{ "specifications": extraction_chain }
| prompt_transform
| llm
| StrOutputParser()
)

## --- 运行链 ---
input_text = "新款笔记本电脑配备了3.5 GHz 八核处理器,16GB 内存和1TB NVMe SSD存储。"

## 使用输入文本字典执行链。
final_result = full_chain.invoke({ "text_input": input_text })

## 输出最终结果
print("\n--- 最终 JSON 输出---")
print(final_result)

Chapter 2 Routing

The mechanism of the Routing pattern:

  • LLM-based Routing
  • Embedding-based Routing
  • Rule-based Routing
  • Machine Learning Model-Based Routing

Practical Applications & Use Cases

  • Human-computer interaction
  • Automated data and document processing pipelines
  • Multiple Agents
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
# dependencies:
# pip install -U langchain langchain-deepseek python-dotenv

from langchain_deepseek import ChatDeepSeek
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableBranch

# ============================================================
# 1. Configuration
# ============================================================

# Ensure your API key environment variable is set (e.g., DEEPSEEK_API_KEY)
# 确保你的API密钥环境变量已设置(如DEEPSEEK_API_KEY)
from dotenv import load_dotenv
load_dotenv()

try:
llm = ChatDeepSeek(
model="deepseek-v4-flash",
temperature=0,
extra_body={
"thinking": {
"type": "disabled"
}
})
print(f"Language model initialized: {llm.model_name}")
except Exception as e:
print(f"Error initializing language model: {e}")
llm = None

# ============================================================
# 2. Simulated Sub-Agent Handlers
# ============================================================

def booking_handler(request: str) -> str:
"""Simulates the Booking Agent handing a request."""
print("\n--- DELEGATING TO BOOKING HANDLER ---")

return (
f"Booking Handler processed request: '{request}'."
f"confirmed for request: {request}"
)

def info_handler(request: str) -> str:
"""Simulates the Info Agent handling a request."""
print("\n--- DELEGATING TO INFO HANDLER ---")

return (
f"Info Handler processed request: '{request}'. "
f"Result: Simulated information retrieval."
)

def unclear_handler(request: str) -> str:
"""Handles requests that couldn't be delegated."""
print("\n--- HANDLING UNCLEAR REQUEST ---")

return (
f"Coordinator could not delegate request: '{request}'. "
f"Please clarify."
)

# ============================================================
# 3. Coordinator Router Prompt
# ============================================================
coordinator_router_prompt = ChatPromptTemplate.from_messages([
("system",
"""
Analyze the user's request and determine which specialist handler
should process it.

- If the request is related to booking flights or hotels,
output 'booker'.

- For all other general inforamtion questions,
output 'info'.

- If the request is unclear or doesn't fit either category,
output 'unclear'.

ONLY output one word:

booker
info
unclear
"""
),
("user", "{request}")
])

# ============================================================
# 4. Router Chain
# ============================================================

if llm:
coordinator_router_chain = (
coordinator_router_prompt | llm | StrOutputParser()
)

# ============================================================
# 5. Branch Handlers
# ============================================================

branches = {

"booker": RunnablePassthrough.assign(
output=lambda x: booking_handler(
x['request']['request']
)
),

"info": RunnablePassthrough.assign(
output=lambda x: info_handler(
x['request']['request']
)
),

"unclear": RunnablePassthrough.assign(
output=lambda x: unclear_handler(
x['request']['request']
)
),
}

# ============================================================
# 6. Delegation Logic
# ============================================================

delegation_branch = RunnableBranch(
(
lambda x:x["decision"].strip().lower() == "booker",
branches["booker"]
),
(
lambda x:x["decision"].strip().lower() == "info",
branches["info"]
),
# Default branch for unclear requests
branches["unclear"]
)

# ============================================================
# 7. Coordinator Agent
# ============================================================

def debug_router_result(x):
print(f"\nRouter Decision: {x['decision'].strip()}")
return x

coordinator_agent = {
# Run request through LLM router
"decision": coordinator_router_chain,
# Preserve original request
"request": RunnablePassthrough(),
} | debug_router_result | delegation_branch | (
lambda x: x["output"]
)

# ============================================================
# 8. Example Usage
# ============================================================

def main():
if not llm:
print("\nSkipping execution due to LLM initialization failure.")
return

print("--- DeepSeek Routing Demo ---")
print("Type 'exit' or 'quit' to stop.\n")

while True:
request = input("Please enter your request: ").strip()

# Exit the interactive Loop
if request.lower() in ["exit", "quit"]:
print("Exiting...")
break

# Ignore empty input
if not request:
print("Request cannot be empty.\n")
continue

try:
result = coordinator_agent.invoke({
"request": request
})
print(f"\nFinal Result: {result}\n")
except Exception as e:
print(f"\nError processing request: {e}\n")

# print("--- Running with a booking request ---")
# request_a = "Book me a flight to London."
# result_a = coordinator_agent.invoke({"request": request_a})
# print(f"Final Result A: {result_a}")
#
# print("\n--- Running with an info request ---")
# request_b = "What is the weather like in Paris?"
# result_b = coordinator_agent.invoke({"request": request_b})
# print(f"Final Result B: {result_b}")
#
# print("\n--- Running with an unclear request ---")
# request_c = "I need something."
# result_c = coordinator_agent.invoke({"request": request_c})
# print(f"Final Result C: {result_c}")

if __name__ == "__main__":
main()

Google ADK Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
# Copyright (c) 2025 Marco Fago
#
# This code is licensed under the MIT License.
# See the LICENSE file in the repository for the full license text.

import uuid
from typing import Dict, Any, Optional

from google.adk.agents import Agent
from google.adk.runners import InMemoryRunner
from google.adk.tools import FunctionTool
from google.genai import types
from google.adk.events import Event

# Colab 代码链接:https://colab.research.google.com/drive/10wxRlPyDJ70pPEtWWA3Aa3tjvfobrB3m

# 安装依赖
# pip install google-genai google-adk
# export GOOGLE_API_KEY=...

# --- Define Tool Functions ---
# These functions simulate the actions of the specialist agents.
# --- 定义工具函数 ---
# 这些函数模拟了专业智能体的具体行动。

def booking_handler(request: str) -> str:
"""
Handles booking requests for flights and hotels.
Args:
request: The user's request for a booking.
Returns:
A confirmation message that the booking was handled.
"""
print("-------------------------- Booking Handler Called ----------------------------")
return f"Booking action for '{request}' has been simulated."

def info_handler(request: str) -> str:
"""
Handles general information requests.
Args:
request: The user's question.
Returns:
A message indicating the information request was handled.
"""
print("-------------------------- Info Handler Called ----------------------------")
return f"Information request for '{request}'. Result: Simulated information retrieval."

def unclear_handler(request: str) -> str:
"""Handles requests that couldn't be delegated."""
return f"Coordinator could not delegate request: '{request}'. Please clarify."

# --- Create Tools from Functions ---
# --- 从函数创建工具 ---
booking_tool = FunctionTool(booking_handler)
info_tool = FunctionTool(info_handler)

# Define specialized sub-agents equipped with their respective tools
# --- 定义配备了各自工具的专业子智能体 ---
booking_agent = Agent(
name="Booker",
model="gemini-2.0-flash",
description="A specialized agent that handles all flight and hotel booking requests by calling the booking tool.",
tools=[booking_tool]
)

info_agent = Agent(
name="Info",
model="gemini-2.0-flash",
description="A specialized agent that provides general information and answers user questions by calling the info tool.",
tools=[info_tool]
)

# Define the parent agent with explicit delegation instructions
# --- 定义带有明确委派指令的父智能体 ---
coordinator = Agent(
name="Coordinator",
model="gemini-2.0-flash",
instruction=(
"You are the main coordinator. Your only task is to analyze incoming user requests "
"and delegate them to the appropriate specialist agent. Do not try to answer the user directly.\n"
"- For any requests related to booking flights or hotels, delegate to the 'Booker' agent.\n"
"- For all other general information questions, delegate to the 'Info' agent."
),
description="A coordinator that routes user requests to the correct specialist agent.",
# The presence of sub_agents enables LLM-driven delegation (Auto-Flow) by default.
# 定义了 sub_agents 默认就会启用由大语言模型驱动的委派。
sub_agents=[booking_agent, info_agent]
)

# --- Execution Logic ---
# --- 执行逻辑 ---
def run_coordinator(runner: InMemoryRunner, request: str):
"""Runs the coordinator agent with a given request and delegates."""
"""用给定的请求运行协调员智能体并进行委派。"""
print(f"\n--- Running Coordinator with request: '{request}' ---")
final_result = ""
try:
user_id = "user_123"
session_id = str(uuid.uuid4())
runner.session_service.create_session(
app_name=runner.app_name, user_id=user_id, session_id=session_id
)

for event in runner.run(
user_id=user_id,
session_id=session_id,
new_message=types.Content(
role='user',
parts=[types.Part(text=request)]
),
):
if event.is_final_response() and event.content:
# Try to get text directly from event.content to avoid iterating parts
if hasattr(event.content, 'text') and event.content.text:
final_result = event.content.text
elif event.content.parts:
# Fallback: Iterate through parts and extract text (might trigger warning)
text_parts = [part.text for part in event.content.parts if part.text]
final_result = "".join(text_parts)
# Assuming the loop should break after the final response
break

print(f"Coordinator Final Response: {final_result}")
return final_result
except Exception as e:
print(f"An error occurred while processing your request: {e}")
return f"An error occurred while processing your request: {e}"

def main():
"""Main function to run the ADK example."""
"""运行 ADK 示例的主函数。"""
print("--- Google ADK Routing Example (ADK Auto-Flow Style) ---")
print("Note: This requires Google ADK installed and authenticated.")

runner = InMemoryRunner(coordinator)
# Example Usage
# 使用示例
result_a = run_coordinator(runner, "Book me a hotel in Paris.")
print(f"Final Output A: {result_a}")
result_b = run_coordinator(runner, "What is the highest mountain in the world?")
print(f"Final Output B: {result_b}")
result_c = run_coordinator(runner, "Tell me a random fact.") # Should go to Info
print(f"Final Output C: {result_c}")
result_d = run_coordinator(runner, "Find flights to Tokyo next month.") # Should go to Booker
print(f"Final Output D: {result_d}")


if __name__ == "__main__":
main()